Unknown error when using .NET 4.0 with dll - c#

c++ code is
MSIPC_SDK LONG __stdcall Ms_IpcClient_CaptureImage(LONG nUserId, char *sFilePath,
int nPathLen, const char *sDiskPath = NULL);//sDiskPath example: "C: \\".
Affect: Take a snapshoot
Parameters remark:
LONG nUserId: Ms_Ipc_Login()//Return value after login successfully
char *sFilePath: //destination for saving the recording files
int nPathLen: //the length of the path
const char *sDiskPath = NULL: //which disk to be saved
my c# code is:
[DllImport("MsIpcSDK", CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern int Ms_IpcClient_CaptureImage(
int lUserID,
[MarshalAs(UnmanagedType.LPStr)]
string sFilePath,
int nPathLen,
[MarshalAs(UnmanagedType.LPStr)]
string sDiskPath
);
and using is method:
var ret = Ms_IpcClient_CaptureImage(loginID, "C:\\a.bmp", 10000, "C:\\");
It is working in .Net Framework 2 but does not work in .Net Framework 4.
How can I fix it in .Net Framework 4?

sFilePath is used to pass a string from callee to caller. That's why the type is char* rather than const char*, and that's why there is a parameter for buffer length. That means you need to use StringBuilder rather than string. The p/invoke should be:
[DllImport("MsIpcSDK", CharSet = CharSet.Ansi,
CallingConvention = CallingConvention.StdCall)]
public static extern int Ms_IpcClient_CaptureImage(
int lUserID,
StringBuilder sFilePath,
int nPathLen,
string sDiskPath
);
And the call should be:
var filePath = new StringBuilder(260);
var ret = Ms_IpcClient_CaptureImage(loginID, filePath, filePath.Capacity, "C:\\");
Your code has always been wrong and you've just been getting away with it up until now. The fact that you passed a made up buffer length value of 10000 should have set the alarm bells ringing!

Related

Use XGBoost DLL from c# via p/invoke

I'm trying to use XGBoost's dll (libxgboost.dll) to create a DMatrix (which is like a 2D array) and get how many columns it has. It runs fine until it throws a System.AccessViolationException at the int cols = ... line in the code below:
using System;
using System.Runtime.InteropServices;
namespace basicXgboost
{
class Program
{
[DllImport("../../libs/libxgboost.dll", CharSet = CharSet.Auto)]
public static extern int XGDMatrixCreateFromFile([MarshalAs(UnmanagedType.LPStr)] string file, int silent, IntPtr outputPtr);
[DllImport("../../libs/libxgboost.dll", CharSet = CharSet.Auto)]
public static extern int XGDMatrixNumCol(IntPtr dmatrixPtr, IntPtr dmatrixColumnsPtr);
static void Main(string[] args)
{
IntPtr dmatrixPtr = Marshal.AllocHGlobal(1000000);
IntPtr dmatrixColumnsPtr = Marshal.AllocHGlobal(10);
int result = XGDMatrixCreateFromFile("../../libs/test.txt", 0, dmatrixPtr);
int cols = XGDMatrixNumCol(dmatrixPtr, dmatrixColumnsPtr);
Marshal.FreeHGlobal(dmatrixPtr);
Marshal.FreeHGlobal(dmatrixColumnsPtr);
}
}
}
Why does accessing unmanaged memory allocated with XGDMatrixNumCol(dmatrixPtr, dmatrixColumnsPtr) cause a System.AccessViolationException?
One possibility might be that I'm using pinvoke incorrectly for these functions. Below are the definitions for each dll function I use:
XGDMatrixCreateFromFile()
/*!
* \brief load a data matrix
* \param fname the name of the file
* \param silent whether print messages during loading
* \param out a loaded data matrix
* \return 0 when success, -1 when failure happens
*/
XGB_DLL int XGDMatrixCreateFromFile(const char *fname,
int silent,
DMatrixHandle *out);
XGDMatrixNumCol()
/*!
* \brief get number of columns
* \param handle the handle to the DMatrix
* \param out The output of number of columns
* \return 0 when success, -1 when failure happens
*/
XGB_DLL int XGDMatrixNumCol(DMatrixHandle handle,
bst_ulong *out);
Here is the repo for my project. I'm using Visual Studio Enterprise 2015 . It's built in "Debug" mode (targeting x64) on Windows 10 Pro (64-bit). x64 binaries for libxgboost.dll can be found here. Although the linked repo does contain a copy of libxgboost.dll.
Try to use the calling convention Cdecl which seems to be used by the DLL.
Also, the signature of the XGDMatrixCreateFromFile function is wrong. The parameter expected is not a pointer to some memory allocated by you, but the function will allocate memory itself and then return the pointer as an output parameter.
Try the following code. Note the use of the the out keyword on the outputPtr parameter in the XGDMatrixCreateFromFile function.
[DllImport("C:\\dev\\libs\\xgboost\\build\\Release\\libxgboost.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int XGDMatrixCreateFromFile([MarshalAs(UnmanagedType.LPStr)] string file, int silent, out IntPtr outputPtr);
[DllImport("C:\\dev\\libs\\xgboost\\build\\Release\\libxgboost.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int XGDMatrixNumCol(IntPtr dmatrixPtr, IntPtr dmatrixColumnsPtr);
static void Main(string[] args)
{
IntPtr dmatrixPtr;
IntPtr dmatrixColumnsPtr = Marshal.AllocHGlobal(10);
int result = XGDMatrixCreateFromFile("C:\\dev\\libs\\xgboost\\demo\\data\\agaricus.txt.test", 0, out dmatrixPtr);
int cols = XGDMatrixNumCol(dmatrixPtr, dmatrixColumnsPtr);
Marshal.FreeHGlobal(dmatrixColumnsPtr);
}
When this works, you can then also simplify the call to get the number of columns by using the ulong datatype:
[DllImport("C:\\dev\\libs\\xgboost\\build\\Release\\libxgboost.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int XGDMatrixCreateFromFile([MarshalAs(UnmanagedType.LPStr)] string file, int silent, out IntPtr outputPtr);
[DllImport("C:\\dev\\libs\\xgboost\\build\\Release\\libxgboost.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int XGDMatrixNumCol(IntPtr dmatrixPtr, out ulong dmatrixColumnsPtr);
static void Main(string[] args)
{
IntPtr dmatrixPtr;
ulong dmatrixColumns;
int result = XGDMatrixCreateFromFile("C:\\dev\\libs\\xgboost\\demo\\data\\agaricus.txt.test", 0, out dmatrixPtr);
int cols = XGDMatrixNumCol(dmatrixPtr, out dmatrixColumns);
}

Access Violation Exception accessing C DLL function by the second access

I have the following code:
Generic.cs
class Generic
{
[DllImport("Generic.dll", EntryPoint = "Consult", CallingConvention = CallingConvention.Winapi)]
public static extern String Consulta(int NumeroSecao);
}
And
main.cs
private void Button_Click(object sender, RoutedEventArgs e)
{
Random rnd = new Random();
int random = rnd.Next(9999);
Stopwatch sw = new Stopwatch();
sw.Start();
String teste = Generic.Consult(random);
sw.Stop();
TextoLog = "Tempo de Execução:"+sw.Elapsed.Milliseconds+"ms | ConsultarSAT";
tempoEntreComandos();
}
The code of DLL is:
Generic.c
__declspec(dllexport) char* __stdcall Consult(int numeroSessao)
{
memset(pcReturn,0,sizeof(char) * BUFFER_SIZE);
HANDLE fileHandle;
Communicate(&fileHandle, pcReturn,10);
return pcReturn;
}
The problem occurs when I call function Consult the second time, the message received is an Access Violation Exception. What is happening here?
The solution is the type for the return in the declaration of function, instead of use String is necessary use of IntPtr.
Native strings have to be marshaled to be used in c#. Your native code is not actually returning a string object, but a char pointer. You have to tell the marshaler what the return type is, as so:
[DllImport("Generic.dll", EntryPoint = "Consult", CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern String Consulta(int NumeroSecao);
Using LPStr for ASCII strings and LPWStr for unicode.
Actually it might be a better idea to marshal the string manually as the marshaler deallocates the native string right after conversion. The declaration would be then
[DllImport("Generic.dll", EntryPoint = "Consult", CallingConvention = CallingConvention.Winapi)]
public static extern IntPtr Consulta(int NumeroSecao);
And you manually marshal the IntPtr to string like so
String str = Marshal.PtrToStringAnsi(strptr); // for ansi
String str = Marshal.PtrToStringUni(strptr); // for unicode

.NET Interop: How to get returned string (not null terminated) from unmanaged DLL in C#

I defined a function in C DLL library.
__declspec(dllexport) void* GetText();
It will return a string which is dynamically allocated from heap memory (And GlobalAlloc is used here for allocating memory). Note that the returned string is not null-terminated.
Then at C# side I tried two methods to declare the function
[DllImport("D:\\ca\\TextAccessLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
static extern String GetText();
When calling above method, the application will crash without any exception thrown.
[DllImport("D:\\ca\\TextAccessLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr GetText();
ptr = GetText();
string text = Marshal.PtrToStringAuto(ptr, 1000);
And calling this method will return incorrect string. Checked the real bytes by using Marshal.Copy, I found the bytes value is not same as the value in DLL library. (I think it's caused by Virtual Memory, C# process cannot access memory space of the DLL directly)
(Don't mind the string length, I hard coded it to 1000 for ease)
This is the C++ code and the memory value of the string when debugging (It's a Console Application but not the original DLL, because Console Application is easy to debug. But the DLL code is same as this one except the logging part).
Following is the original DLL code
__declspec(dllexport) char* GetText(){
VTHDOC hDoc = NULL;
VTHTEXT hText = VTHDOC_INVALID;
DAERR da_err = NULL;
DAERR ta_err = NULL;
DAERR read_err = NULL;
char *buf = (char*)GlobalAlloc(GMEM_FIXED, 1000);
DWORD real_size;
DAInitEx(SCCOPT_INIT_NOTHREADS, OI_INIT_DEFAULT);
da_err = DAOpenDocument(&hDoc, 2, "D:\\1TB.doc", 0);
ta_err = TAOpenText(hDoc, &hText);
read_err = TAReadFirst(hText, (VTLPBYTE)buf, 1000, &real_size);
return buf;
}
But at C# side the bytes are not same as C++ side
You can see the first byte in C++ is 0, but it's 200 for C# (decimal)
Another thing to note: if I return a const string(e.g. "AASSDD") directly in DLL code, C# side will get the correct string
You can't do it that way. Marshaling of string works only for null-terminated strings (or for BSTR, if you specify some options). You can:
[DllImport("D:\\ca\\TextAccessLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr GetText();
But from there, it isn't clear how the C# program should know the length of the string.
The various Marshal methods of C# handle BSTR (that have internally their length) or NUL terminated strings.
As already stated, it works for null-terminated strings only, in the following way:
C# part, declaration:
[DllImport("myDll.dll", EntryPoint = "myString", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
extern private static string myString(out int size);
C# part, usage:
int size;
string s = myString(out size);
C++ part:
char* myString(int* size)
{
*size = 20;
char* strg = (char*)::GlobalAlloc(GMEM_FIXED, *size);
memset(strg, 0x3f, *size); //preset with a questionmark
for (int i=0; i < 9; i++)
strg[i] = 0x40 + i;
strg[*size -1] = 0; //limit the maximum string length
return strg;
}
And the obtained C# string:
"#ABCDEFGH??????????", value of size: 20
A treatment of the issue may be found here

system.access.violation exception while calling c++ function from a thread in c#

i am importing c++ function from a dll to my winform c# app:
[DllImport(#"eyeWhere.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern int Eye_GetPositionS(string filename, [MarshalAs(UnmanagedType.LPArray, SizeConst = 9)] double[] sensors);
when i call the function from the constructor its working fine.
the problem is when i am calling it from a new thread opened within the
"fleck websocket server" Fleck, "onMessage" Action,
then it throws the "system.access.violation" exception.
i managed to narrow down the problem to the double array that i am passing,
it seems like the pointer to it is corrupted.
i cant find the source of the problem one thing is sure the function from the dll is working as i tested it.
function call(two stages):
open new thread within "fleck":
socket.OnMessage = message =>
{Thread locationThread = new Thread( unused => processLocation(fileName,socket,sensorsList,sensors) );
locationThread.Start();}
the actual function:
private void processLocation(string fileName, IWebSocketConnection sock, List<Sensor> sensorsList, double[] sensors)
{
int map_position = Eye_GetPositionS(fileName,sensors);
string locationString = "floor:1,mx:" + (map_position / 10000) + ",my:" + (map_position % 10000);
// send location string to user
sock.Send(LOCATION_CODE + "-" + locationString);}
the interface is:
extern "C" __declspec(dllexport) int Eye_GetPositionS(const wchar_t *fname_mob, double sensors[9], int &map_x, int &map_y)
i am not passing the two last arguments (int&) as agreed with the man who wrote the dll.
I am not passing the two last arguments (int&) as agreed with the man who wrote the DLL.
Well, there's the problem. You cannot omit parameters. It should be:
[DllImport(#"eyeWhere.dll", CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Unicode)]
public static extern int Eye_GetPositionS(
string filename,
[In, MarshalAs(UnmanagedType.LPArray, SizeConst = 9)]
double[] sensors,
ref int map_x,
ref int map_y
);
You simply cannot omit these parameters, no matter what the guy who wrote the DLL says. Perhaps he means that the parameters are really int* and you can pass nullptr.
I've used ref for these parameters, but perhaps they should be out. You presumably know which is appropriate.
Likewise I'm guessing at the intent of the sensors parameter. If the data flows out rather than in, then use Out. If the data flows in both directions, use [In, Out, ...].

Interop sending string from C# to C++

I want to send a string from C# to a function in a native C++ DLL.
Here is my code:
The C# side:
[DllImport(#"Native3DHandler.dll", EntryPoint = "#22",
CharSet = CharSet.Unicode)]
private static extern void func1(byte[] path);
public void func2(string path)
{
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] arr = encoding.GetBytes(path);
func1(this.something, arr);
}
The C++ side:
void func1(char *path)
{
//...
}
What I get in the C++ side is an empty string, every time, no matter what I send.
Help?
Thanks.
It looks like you have 2 issues. The first is your native C++ uses an ANSI string but you are specifying unicode. Secondly, it's easiest to just marshal a string as a string.
Try changing the DllImport to the following
[DllImport(
#"Native3DHandler.dll",
EntryPoint = "#22",
CharSet = CharSet.Ansi)]
private static extern void func1(void* something, [In] string path);
Works fine for me with no extra marshalling instructions in VS2008:
C# side:
[DllImport("Test.dll")]
public static extern void getString(StringBuilder theString, int bufferSize);
func()
{
StringBuilder tstStr = new StringBuilder(BufSize);
getString(tstStr, BufSize);
}
C++ side:
extern "C" __declspec(dllexport) void getString(char* str, int bufferSize)
{
strcpy_s(str, bufferSize, "FOOBAR");
}
Your declaration is wrong. The parameter should be of type string, and you should set the character set encoding to Ansi, like so:
[DllImport(#"Native3DHandler.dll", EntryPoint = "#22",
CharSet = CharSet.Ansi)]
private static extern void func1(string path);
This assumes that you are not modifying the contents of the path variable in your C++ code. Then, you pass the string parameter directly (no need for the wrapper).
If you just want to send a string, just declare func1's parameter as a string. If you want to receive a string, declare it as a StringBuilder and allocate enough buffer space for what you want to receive.
Default Marshaling for Strings
http://msdn.microsoft.com/en-us/library/s9ts558h.aspx

Categories

Resources