Pinvoke DeviceIoControl参数

我正在使用DeviceIoControl C#项目。 我已经为我的签名咨询了相关的Pinvoke.net页面 :

 [DllImport("Kernel32.dll", SetLastError = false, CharSet = CharSet.Auto)] public static extern bool DeviceIoControl( SafeFileHandle hDevice, EIOControlCode IoControlCode, [MarshalAs(UnmanagedType.AsAny)] [In] object InBuffer, uint nInBufferSize, [MarshalAs(UnmanagedType.AsAny)] [Out] object OutBuffer, uint nOutBufferSize, out uint pBytesReturned, [In] IntPtr Overlapped ); 

我之前从未见过object[MarshalAs( UnmanagedType.AsAny )] ,但MSDN文档听起来很有希望:

一种动态类型,它在运行时确定对象的类型,并将对象编组为该类型。 该成员仅对平台调用方法有效。

我的问题是:使用此签名的“最佳”和/或“正确”方式是什么?

例如, IOCTL_STORAGE_QUERY_PROPERTY期望InBufferSTORAGE_PROPERTY_QUERY结构。 看起来我应该能够定义该结构,创建一个new实例,并将其传递给我的Pinvoke签名:

 var query = new STORAGE_PROPERTY_QUERY { PropertyId = 0, QueryType = 0 }; DeviceIoControl(..., query, Marshal.SizeOf(query), ...); 

但是,我刚刚得到一个System.ExecutionEngineException ,所以我改为:

 int cb = Marshal.SizeOf(typeof(...)); IntPtr query = Marshal.AllocHGlobal(cb); ... Marshal.PtrToStructure(...); Marshal.FreeHGlobal(query); 

当我打电话时,它至少没有抛出任何exception。 这只是非常丑陋,虽然屁股上有巨大的痛苦。 编组人员难道不能像我希望的那样处理从我的本地结构复制数据吗?

输出数据有时可能很棘手,因为它们不是固定大小的结构。 我理解marshaller不可能自动处理,我可以做HGlobal并在我需要的地方复制业务。

额外:

这个问题起初看起来很有帮助,但最终只是一个不正确的常数。

我并不反对使用unsafe结构。 ( fixed -size struct成员需要这个。)

DeviceIoControl非常不友好。 但是你可以减少痛苦,你不必自己编组。 你可以利用两件事:C#支持方法重载,pinvoke marshaller会相信你,即使你对你的声明撒谎。 这对于结构来说是完美的,它们已经被整理成一堆字节。 正是DeviceIoControl()需要的东西。

所以一般声明如下:

 [DllImport("Kernel32.dll", SetLastError = true)] public static extern bool DeviceIoControl( SafeFileHandle hDevice, int IoControlCode, byte[] InBuffer, int nInBufferSize, byte[] OutBuffer, int nOutBufferSize, out int pBytesReturned, IntPtr Overlapped ); 

并且你会添加一个非常适合IOCTL_STORAGE_QUERY_PROPERTY的重载,假设你对它返回STORAGE_DEVICE_DESCRIPTOR感兴趣:

 [DllImport("Kernel32.dll", SetLastError = true)] public static extern bool DeviceIoControl( SafeFileHandle hDevice, EIOControlCode IoControlCode, ref STORAGE_PROPERTY_QUERY InBuffer, int nInBufferSize, out STORAGE_DEVICE_DESCRIPTOR OutBuffer, int nOutBufferSize, out int pBytesReturned, IntPtr Overlapped ); 

你会这样称呼它:

 var query = new STORAGE_PROPERTY_QUERY { PropertyId = 0, QueryType = 0 }; var qsize = Marshal.SizeOf(query); STORAGE_DEVICE_DESCRIPTOR result; var rsize = Marshal.SizeOf(result); int written; bool ok = DeviceIoControl(handle, EIOControlCode.QueryProperty, ref query, qsize, out result, rsize, out written, IntPtr.Zero); if (!ok) throw new Win32Exception(); if (written != rsize) throw new InvalidOperationException("Bad structure declaration"); 

哪个应该看起来比你得到的更漂亮,更可诊断。 未经测试,应该接近。