[Mono-dev] libmtp bindings

Jonathan Pryor jonpryor at vt.edu
Thu Jan 18 08:07:30 EST 2007

On Thu, 2007-01-18 at 03:15 -0700, Ted Bullock wrote:
> Here is the problem; I am trying to write a binding for the following c
> function call:
> void Get_Devices_List(device_entry_t ** const, int * const);
> Notice the location of const.  The target is constant, but the pointer
> isn't.

You have that backwards: it's a const pointer to pointer to
device_entry_t (you read from right to left).  Regardless, you don't
need to worry about the `const' in this case.

> [DllImport ("libmtp")]
> internal static extern void LIBMTP_Get_Supported_Devices_List(
> 	out IntPtr devicelist,
> 	out int NumDevices);
> The function I am trying to write is as follows which simply returns an
> array of the devices pointed to by device_entry_t:  Note that NumDevices
> is correctly filled out with a value 44.
> public static DeviceEntry[] SupportedDeviceList()
> {
> IntPtr DeviceList = IntPtr(0);
> int NumDevices = 0;

These initializations aren't necessary as you later use `out'.

> Get_Devices_List(out DeviceList, out NumDevices);
> if (NumDevices <= 0)
>   return new DeviceEntry[]{};
> DeviceEntry[] Devices = new DeviceEntry[NumDevices];
> for (int i = 0; i < NumDevices; ++i)
> {	
> IntPtr p = Marshal.ReadIntPtr(DeviceList, i * IntPtr.Size);
> //*************** Crash on next line ********************
> Devices[i] =(DeviceEntry)Marshal.PtrToStructure(p,typeof(DeviceEntry));
> }
> return Devices;
> }
> Hope somebody can help!

Your memory use is wrong.  I assume that
LIBMTP_Get_Supported_Devices_List()'s deviceList parameter will contain
a directly accessible array; that is, C use is:

	device_entry_t *list; int n;
	LIBMTP_Get_Supported_Devices_List (&list, &n);
	for (int i = 0; i < n; ++n) { /* list [i] contains devices */ }

This is NOT what your code does.  Your code is equivalent to:

	device_entry_t *list; int n;
	LIBMTP_Get_Supported_Devices_List (&list, &n);
	for (int i = 0; i < n; ++n) {
		device_entry_t e = (device_entry_t)
			(((char*) list) + n*sizeof(void*));

You'll notice that this tries to read device_entry_t partially offset
from the _actual_ device_entry_t returned, due to using sizeof(void*)
(i.e. 4) instead of sizeof(device_entry_t) (i.e. not 4).

No wonder things don't work. :-)

The solution is to change your loop to use:

	IntPtr p = Marshal.ReadIntPtr(DeviceList, 
		i * Marshal.SizeOf(typeof(DeviceEntry)));

which is equivalent to the C loop body:

	device_entry_t e = (device_entry)
		(((char*) list) + n*sizeof(device_entry_t));

which is what `list[n]' effectively compiles to.

HOWEVER, this requires that the memory layout of your C# DeviceEntry and
the C device_entry_t structures match _identically_, so that
Marshal.SizeOf() returns the size of the unmanaged device_entry_t
structure.  Otherwise, you'll still be improperly accessing the
device_entry_t array.  You may need to introduce an intermediate
structure to make sure your managed & unamanged memory structure layouts
match, and then manually convert between your managed helper structure
and DeviceEntry.  (This would be necessary if e.g. DeviceEntry contains
an inline `char' array, and you're unable to use the
[MarshalAs(UnmanagedType.ByValArray, SizeConst=N)] attribute on the
managed string counterpart.  See

 - Jon

More information about the Mono-devel-list mailing list