Hi all,
I want to understand more about how the pinvoke pinning process works.
I'm writing some code that calls DeviceIoControl.
DeviceIoControl provides a generic interface to device drivers.
Its signature is deliberately open-ended so that it can be highly generic.
Refer SDK for more.
I want to access the drive geommetry of an SD-Card via
IOCTL_DISK_GET_DRIVE_GEOMETRY.
I've tried three different methods for calling DeviceIoControl.
Two work, but the third doesn't. I want to know why?
The three methods are presented below,
And are delimited by "//////////////////"
Cheers Steve
//////////////////
//
// Passing an appropriately defined DISK_GEOMETRY structure by ref works..
ie.
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true,
CharSet = CharSet.Auto)]
private static extern int DeviceIoControl(
IntPtr hDevice, uint dwIoControlCode,
IntPtr lpInBuffer, uint nInBufferSize,
ref DISK_GEOMETRY lpOutBuffer, uint nOutBufferSize,
out uint lpBytesReturned, IntPtr lpOverlapped);
....
DISK_GEOMETRY DiskGeometry = new DISK_GEOMETRY();
....
// This works
DeviceIoControl(
...,
ref DiskGeometry, // StructLayout sequential, packing = 1
(uint) Marshal.SizeOf(typeof(DISK_GEOMETRY), // 24 bytes
...);
//////////////////
//
// Pinning an allocated byte[] and passing via an IntPtr through lpOutBuffer
works
// yes i know you can just pass a byte array by ref
ie.
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true,
CharSet = CharSet.Auto)]
private static extern int DeviceIoControl(
IntPtr hDevice, uint dwIoControlCode,
IntPtr lpInBuffer, uint nInBufferSize,
IntPtr lpOutBuffer, uint nOutBufferSize,
out uint lpBytesReturned, IntPtr lpOverlapped);
....
byte[] DiskGeometry = new byte[24];
GCHandle PinnedDiskGeometry = GCHandle.Alloc(DiskGeometry,
GCHandleType.Pinned);
IntPtr pDiskGeometry = PinnedDiskGeometry.AddrOfPinnedObject();
....
// This works
DeviceIoControl(
...,
pDiskGeometry,
(uint) DiskGeometry.Length,
...);
....
PinnedDiskGeometry.Free();
//////////////////
//
// Pinning an allocated DISK_GEOMETRY structure
// and passing via an IntPtr through lpOutBuffer DOES NOT WORK
//
// Why? Is it because structs are allocated on the stack?
//
ie.
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true,
CharSet = CharSet.Auto)]
private static extern int DeviceIoControl(
IntPtr hDevice, uint dwIoControlCode,
IntPtr lpInBuffer, uint nInBufferSize,
IntPtr lpOutBuffer, uint nOutBufferSize,
out uint lpBytesReturned, IntPtr lpOverlapped);
....
DISK_GEOMETRY DiskGeometry = new DISK_GEOMETRY();
GCHandle PinnedDiskGeometry = GCHandle.Alloc(DiskGeometry,
GCHandleType.Pinned);
IntPtr pDiskGeometry = PinnedDiskGeometry.AddrOfPinnedObject();
....
DeviceIoControl(
...,
pDiskGeometry, // StructLayout sequential, packing = 1
(uint) Marshal.SizeOf(typeof(DISK_GEOMETRY), // 24 bytes
...);
// Call succeeds but DiskGeometry is not updated. WHY???
....
PinnedDiskGeometry.Free();
Cheers,
Steve