Marshal structure with array element in C #

I am using C # with P / Invoke to access a DLL method. The definition of the method is as follows:

[DllImport("userManager.dll")] static extern int GetUsers(out IntPtr userList); 

Original structures:

 typedef struct user_list { unsigned short NumUsers; USER_LIST_ITEM List[VARLEN]; } USER_LIST typedef struct user_list_item { char name[260]; unsigned char address[256]; } USER_LIST_ITEM 

And the layout of the structure I made is as follows:

 [StructLayout(LayoutKind.Sequential)] public class USER_LIST { public uint NumUsers; [MarshalAs(UnmanagedType.ByValArray)] public USER_LIST_ITEM [] List; } [StructLayout(LayoutKind.Sequential)] public class USER_LIST_ITEM { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string name; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string address; }; 

But I get an error when I try to undo it:

 USER_LIST userList = new USER_LIST(); // Prepare pointer IntPtr uList = Marshal.AllocHGlobal(Marshal.SizeOf(userList)); Marshal.StructureToPtr(userList, uList, false); result = GetUsers(out uList); Marshal.PtrToStructure(uList, userList); <-- 

The runtime has encountered a fatal error. The error address was on 0x79f82af6, on stream 0x464. Error Code: 0xc0000005. This error may be an error in the CLR or in unsafe or unverifiable portions of user code. Common sources of this error include user marshaling errors for COM-interop or PInvoke, which can damage the stack.

I get the NumUsers property to the right, but it seems like an error occurs with an unmarshalling array. Any thoughts?

+4
source share
3 answers

If you specify an array in the structure used as the out parameter, you need to tell the marshaller how long the array will be. With your code, the marshaler probably allocates an array of zero length or just uses null , which leads to a crash. Unfortunately, there seems to be no way to specify an array of variable length out as a member of the structure, because MarshalAs.SizeParamIndex only works for methods. You can get away with specifying a large array with a constant size using MarshalAs.SizeConst , but usually you will need to MarshalAs.SizeConst response buffer (supposedly called by the user) as follows:

 var count = Marshal.ReadInt32 (uList) ; var users = new List<USER_LIST_ITEM> () ; var ptr = (long)uList + 4 ; for (int i = 0 ; i < count ; ++i) { users.Add (Marshal.PtrToStructure (typeof (USER_LIST_ITEM), new IntPtr (ptr))) ; ptr += Marshal.SizeOf (typeof (USER_LIST_ITEM)) ; } 

You will need to pay extra attention to alignment and addition and 32/64 bit.

+4
source

This is because List has not yet been allocated.

You will need to initialize all the fields.

Another problem that I see is the following:

 IntPtr uList = Marshal.AllocHGlobal(Marshal.SizeOf(userList)); ... result = GetUsers(out uList); 

Are you sure that out should not be ref ? Otherwise, it makes no sense (not sure if ref is correct either).

Update. Once again looking at your code, you should do this (and avoid memory leaks, causing your attention).

 IntPtr uList; var result = GetUsers(out uList); var userlist = (USER_LIST) Marshal.PtrToStructure(ulist, typeof(USER_LIST)); Marshal.FreeHGlobal(ulist); // pray here or shoot the author of the C function 

Refresh again:

Your p / invoke signature is most likely incorrect or you misinterpret it.

I can probably guess something like:

 int GetUsers(USER_LIST* ulist); 

And this is what you have - it is not the same thing.

If so, the decision is easy.

Change USER_LIST to a class (but keep a consistent layout) and use

 // pinvoke sig int GetUsers(USER_LIST ulist); var ulist = new USER_LIST(); // initialize fields var r = GetUsers(ulist); 

- or -

Name it ref .

 // pinvoke sig int GetUsers(ref USER_LIST ulist); var ulist = new USER_LIST(); // initialize fields var r = GetUsers(ref ulist); 

This way, you don’t have to bother with the manual sorter, and I don’t see the possibility of a memory leak.

Final update:

Given the signature you sent, it looks like this: GetUsers returns a pointer to the USER_LIST list with the return value, which is the counter. Good memory leak there.

In any case, I would probably experiment with an unsafe approach here and just go through the result and make sure everything is freed. (I still think you should shoot the author).

+3
source

I think your source code is probably not so wrong. You probably just used the wrong Marshal.PtrToStructure overload .

Have you tried this?

 [DllImport("userManager.dll")] static extern int GetUsers(out IntPtr userList); [DllImport("userManager.dll")] static extern void UMFree(IntPtr userList); static void Main() { IntPtr userList; // no need to allocate memory in managed code; GetUsers(out userList); // memory is allocated by native function USER_LIST u = (USER_LIST)Marshal.PtrToStructure(userList, typeof(USER_LIST)); UMFree(userList); } 

Using unsafe code:

 public unsafe struct USER_LIST { public uint numUsers; public USER_LIST_ITEM* list; } public unsafe struct USER_LIST_ITEM { public fixed byte name[260]; public fixed byte address[256]; } class Program { [DllImport("userManager.dll")] static unsafe extern int GetUsers(USER_LIST** userList); [DllImport("userManager.dll")] static unsafe extern int UMFree(USER_LIST* userList); private static unsafe void Main() { USER_LIST* list; GetUsers(&list); UMFree(list); } } 
+1
source

All Articles