PInvoke - marshal - an array of structures from the pointer

I am trying to complete the answer to this question

My structure looks like this: C

typedef struct drive_info_t { unsigned char drive_alias[32]; } drive_info_t; 

My function looks like this: C

 unsigned int get_drive_info_list(drive_info_t **list, unsigned int *item_count) { //fill list in native C //print out in native C printf("list.alias - %s\r\n",list[i]->drive_alias); } 

My C # structure is as follows

 [StructLayout(LayoutKind.Sequential)] public struct drive_info_t { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] public byte[] drive_alias; } 

The declaration of my C # function is as follows

 [DllImport("mydll.dll", EntryPoint = "get_drive_info_list", CallingConvention = CallingConvention.Cdecl)] public static extern uint GetDriveInfoList(out System.IntPtr ptr_list_info, out System.IntPtr ptr_count); 

I call the C # function as follows

 IntPtr ptr_list_info = IntPtr.Zero; IntPtr ptr_cnt = IntPtr.Zero; ret = api.GetDriveInfoList(out ptr_list_info, out ptr_cnt); 

I'm marshaling returned pointers like

 nAlloc = ptr_cnt.ToInt32(); int szStruct = Marshal.SizeOf(typeof(api.drive_info_t)); api.drive_info_t[] localStructs = new api.drive_info_t[nAlloc]; for (int i = 0; i < nAlloc; i++) { localStructs[i] = (api.drive_info_t)Marshal.PtrToStructure(ptr_list_info, typeof(api.drive_info_t)); ptr_list_info = new IntPtr(ptr_list_info.ToInt32() + (szStruct)); } 

Print the structure alias as follows

 for (uint i = 0; i < localStructs.Length; i++) { Console.WriteLine("list.alias - {0}", System.Text.Encoding.Default.GetString(localStructs[i].drive_alias)); } 

Thank you for staying with me ..

This is what my output looks like in a console application in C #. You can see the original C dll print on the console, which it means, but my marshaling messed up somewhere:

 ======================== C values ============================ list.alias - drv1 list.alias - drv2 list.alias - drv3 list.alias - drv4 ======================== C# values ============================ list.alias - drv1 list.alias - o£Q95drv2 list.alias - o£Q95drv3 list.alias - o£Q95drv4 

I don’t know where this text and garbage offset come from.

I am responsible for the .Net side, other team members can change their native C if necessary, but, if necessary, changes to C should be cross-platform OSX / Windows / Linux.

Thanks in advance.

+4
c pointers c # pinvoke
source share
1 answer

This is a great example of why argument types do not define an interface. Consider

 drive_info_t **list 

It can be a pointer to an array of structures. In this case, the array is supposedly allocated by the called. This is why an array pointer is used in contrast to an array.

Or it could be an array of pointers to a struct. Here the array will be allocated by the caller, and structures can be allocated either by the called or the caller. We can not say.

We may notice that your interface accepts an array of pointers to a struct. Take a look at this code:

 list[i]->drive_alias 

It is clear that list is an array of pointers to a struct.

This indicates that your code is incorrect. You followed the wrong template because you misidentified the semantics. The next step is to return to the documentation for the interface and any example that calls the code to know exactly what semantics are. Who isolates and liberates that.

You will almost certainly use IntPtr[] to marshal the array, but beyond that we cannot say what is happening. I assume that the caller allocates structures, but does not rely on guesswork. Read the calling code and example code.

In addition, the second argument should be ref uint or possibly out uint depending on the semantics of the function. Again, this is something we cannot decide.

+1
source share

All Articles