我正在使用DeviceIoControl
处理C#项目。我已经为我的签名咨询了相关的Pinvoke.net page:
[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 documentation听起来很有希望:
一种动态类型,它在运行时确定对象的类型,并将对象编组为该类型。该成员仅对平台调用方法有效。
我的问题是:什么是最好的"和/或"适当的"使用此签名的方式?
例如,IOCTL_STORAGE_QUERY_PROPERTY
期望InBuffer
为STORAGE_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);
当我打电话时,它至少没有抛出任何异常。这只是非常丑陋,虽然屁股上有巨大的痛苦。编组人员能不能像我希望的那样将数据复制到我的本地结构中?
输出数据有时可能很棘手,因为它们不是固定大小的结构。我理解marshaller可以自动处理这个问题,而且我可以在HGlobal和我需要的地方复制业务。
其他:
This question一开始看起来很有帮助,但最终只是一个不正确的常数。
我不反对使用unsafe
构造。 (fixed
- 尺寸struct
成员需要此功能。)
答案 0 :(得分:19)
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");
哪个应该看起来比你得到的更漂亮,更可诊断。未经测试,应该接近。