Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AccessViolation

I keep getting an AccessViolationException when calling the following from an external C DLL:

short get_device_list(char ***device_list, int *number_of_devices);

I set up a DLLImport declaration as such:

[DLLImport("mydll.dll")]
static public extern short get_device_list([MarshalAs(UnmanagedType.LPArray)] ref string[] devices, ref int number_of_devices);

My C# application code:

{
string[] devices = new string[20];
int i = 0;
short ret = 0;
ret = get_device_list(ref devices, ref i); // I receive the AccessViolation Exception here
// devices[0] = "2255f796e958f7f31a7d2e6b833d2d426c634621" which is correct.
}

Although I receive the exception, the device array gets filled correctly with the 2 UUIDs of the devices connected (and also gets resized to size = 2; i is also 2;).

What is wrong?

PS: After a long research I also tried:

[DLLImport("mydll.dll")]
static public extern short get_device_list(ref IntPtr devices, ref int number_of_devices);

and

{
IntPtr devices = new IntPtr();
int i = 0;
short ret = 0;
ret = get_device_list(ref devices, ref i); // No AccessViolation Exception here
string b = Marshal.PtrToStringAuto(devices); // b = "歀ׄ", which is incorrect
}

but that did not help me.

Thanks in advance!

like image 767
manuel w Avatar asked Sep 04 '12 14:09

manuel w


1 Answers

[DLLImport("mydll.dll")]
static public extern short get_device_list(out IntPtr devices, 
    out int number_of_devices);

Is the best way to tackle this. The memory is allocated and owned on the native side of the interface. The trick is how to get at it. Something like this should work.

static public string[] getDevices()
{
    IntPtr devices;
    int deviceCount;
    short ret = get_device_list(out devices, out deviceCount);
    //need to test ret in case of error

    string[] result = new string[deviceCount];
    for (int i=0; i<deviceCount; i++)
    {
        IntPtr ptr = (IntPtr)Marshal.PtrToStructure(devices, typeof(IntPtr));
        result[i] = Marshal.PtrToStringAnsi(ptr);
        devices += IntPtr.Size;//move to next element of array
    }
    return result;
}

Your code was using PtrToStringAuto but that's going to interpret the data as UTF-16 encoded. But your C++ code uses char* which is 8 bit ANSI. So you need PtrToStringAnsi. OK, there's an assumption here that the encoding is not UTF-8, but that's a detail I cannot provide. It's easy enough to adapt this to UTF-8.

You should also double check that the native code uses the stdcall calling convention and isn't using cdecl.

like image 142
David Heffernan Avatar answered Sep 24 '22 00:09

David Heffernan