I have a C (not C++, as is most common) function in a DLL with the following signature:
unsigned long MyFunc(mystruct *arr, unsigned long arrLen);
where arr
is an array of mystruct
and arrLen
is the length of that array
The structure is like so:
typedef struct mystruct {
unsigned long type;
void* val;
unsigned long valLen;
} mystruct;
Where type
is a code that lets me know what is the actual pointer type of val
, and valLen
is the size in bytes of the value pointed to by val
.
I'm trying to call this function from C#, and am getting the following error:
Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
this is how I declared the structure in C#:
[StructLayout(LayoutKind.Sequential)]
public struct mystruct{
public uint type;
public IntPtr val;
public uint valLen;
}
this is my P/Invoke declaration:
[DllImport("mydll.dll")]
public static extern uint MyFunc(mystruct[] arr, uint arrLen);
Let's say I'm trying to pass 3 elements in the array. One is an uint
(so val
should correspond to an unsigned long*
in C), one is a byte
(so val
should correspond to an unsigned char
in C) and another an array of bytes (so val
should correspond to a char *
in C).
I have also tried to only pass a single element array (the element being the uint
one) and I got the same error. This tells me that the problem is not necessarily to do with passing the array of bytes but also with the simplest types.
I have checked the DLL and commented out the lines that dereference val
in MyFunc
and with those lines and only those lines commented out, there is no error. Thus, I believe the problem lies with how I pass the pointers. Another reason is because if I pass a null
array there is no error, even without the code commented out.
I have tried to do this in 2 different ways.
0- without allocating memory manually
mystruct[] arr = new mystruct[3];
arr[0].type = 0;
uint my_uint = 10;
GCHandle handle_uint = GCHandle.Alloc(my_uint, GCHandleType.Pinned);
arr[0].val = handle_uint.AddrOfPinnedObject();
arr[0].valLen = sizeof(uint);
arr[1].type = 1;
byte my_byte= 1;
GCHandle handle_byte = GCHandle.Alloc(my_byte, GCHandleType.Pinned);
arr[1].val= handle_byte.AddrOfPinnedObject();
arr[1].valLen= sizeof(byte);
arr[2].type = 2;
byte[] my_byte_arr = new byte[32];
for (int i = 0; i < 32; i++) {
my_byte_arr[i] = 255;
}
GCHandle handle_byte_arr = GCHandle.Alloc(my_byte_arr, GCHandleType.Pinned);
arr[2].val = handle_byte_arr.AddrOfPinnedObject();
arr[2].valLen = (uint) my_byte_arr.Length;
MyFunc(arr, (uint) arr.Length);
handle_uint.Free();
handle_byte.Free();
handle_byte_arr.Free();
1- allocating memory manually - for brevity sake, I only show how I did it for an uint
. Another reason is because I only tried this one with a single uint
argument
mystruct[] arr = new mystruct[1];
arr[0].valLen = sizeof(uint);
arr[0].type = 0;
arr[0].val = Marshal.AllocCoTaskMem((int) arr[0].valLen);
Marshal.WriteInt32(arr[0].val, 10);
MyFunc(arr, (uint) arr.Length);
Marshal.FreeCoTaskMem(arr[0].val);
These questions are related:
0- How to pass an object pointer from and to a DLL? However, the solution presented is not correct for my case, because I can call all other functions from the DLL, just not this one because, as I explained, I am most likely passing the pointers incorrectly in this case. I also have successfully called other functions from this DLL, and even this function as long as that void*
is never dereferenced by the DLL
1- C# call C++ DLL passing pointer-to-pointer argument