6

I'm attempting to follow the answer at this question

My struct looks like this in C

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

My function looks like this in 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# struct looks like this

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

My C# function declaration looks like this

[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'm calling C# function like this

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

I'm marshaling the returned pointers like this

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)); } 

Printing the struct alias like this

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

Thanks for staying with me..

This is what my output looks like on a console application in C#. You can see the native C dll printing to the console it's values, but my marshaling is messing 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 have no clue where this garbage text and offset is coming from.

I'm responsible for the .Net side, other team members can change the native C as needed if required, but native C changes need to be cross-platform OSX/Windows/Linux.

Thanks in advance.

8
  • Out of curiosity, what happens if you change [StructLayout(LayoutKind.Sequential)] to [StructLayout(LayoutKind.Sequential, Pack=5)]? Commented Jul 28, 2016 at 23:21
  • Pack = 5 is not a valid value, also I've tried going through 1, 2, 4, 8 values with no change. Commented Jul 28, 2016 at 23:22
  • I was wondering... If you change the IntPtr to UIntPtr and all references of ToInt32 to UInt32. I know sometimes it can seems unimportant but... Commented Jul 28, 2016 at 23:34
  • When I switch to UIntPtr, PtrToStructure yells at me. I tried to use .ToPointer but it still errored. Commented Jul 29, 2016 at 0:14
  • @DavidHeffernan the struct on the managed size is 32, the struct on unmanaged size is 32. How are you determining 36? Commented Jul 29, 2016 at 20:42

1 Answer 1

2

This is a perfect example of why the types of the arguments do not define the interface. Consider

drive_info_t **list 

This could be a pointer to an array of structs. In which case presumably the array is allocated by the callee. Which is why a pointer to array is used as opposed to an array.

Or it could be an array of pointers to struct. Here the array would be allocated by caller and the structs could be allocated by either callee or caller. No way for us to tell.

We can discern that your interface accepts an array of pointers to struct. Look at this code:

list[i]->drive_alias 

Quite clearly list is an array of pointers to struct.

This tells you that your code is wrong. You have followed the wrong template because you mis-identified the semantics. Your next step is to return to the documentation for the interface and any example calling code to learn precisely what the semantics are. Who allocates and deallocated what.

Almost certainly you will use IntPtr[] to marshal the array but beyond that it is impossible for us to say what happens. I would guess that the caller allocates the structs but don't rely on guesswork. Read the documentation and example calling code.

Beyond that the second argument should be ref uint or perhaps out uint depending on the semantics of the function. Again, that's something that we cannot work out.

Sign up to request clarification or add additional context in comments.

1 Comment

Almost certainly you will use IntPtr[] to marshal the array, yes, I ended up changing the declaration to [In,Out] System.IntPtr[] ptr_list_info and also the 2nd parameter to uint. Thanks!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.