[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
http://www.mono-project.com/dllimport#Arrays_Embedded_Within_Structures.)
- Jon
More information about the Mono-devel-list
mailing list