C# 1 - PInvoke - Pinning structures via GCHandle

G

Guest

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
 
M

Mattias Sjögren

// Call succeeds but DiskGeometry is not updated. WHY???

If DISK_GEOMETRY is declared as a struct, you'll end up pinning a
boxed copy in the GCHandle.Alloc call. That copy will get modified,
not the original DiskGeometry instance.


Mattias
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top