13

我正在使用DeviceIoControl. 我已经查阅了相关的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,但MSDN 文档听起来很有希望:[MarshalAs(UnmanagedType.AsAny)]

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

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

例如,IOCTL_STORAGE_QUERY_PROPERTYexpectedInBuffer是一个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);

至少当我调用它时它没有抛出任何异常。那只是非常丑陋,而且是一个巨大的痛苦。编组器不能像我希望的那样处理向/从我的本地结构复制数据吗?

输出数据有时可能很棘手,因为它们不是固定大小的结构。我知道编组器不可能自动处理这个问题,而且我可以在需要的地方进行 HGlobal 和复制业务。

额外的:

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

我不反对使用unsafe构造。(fixed-sizestruct成员需要这个。)

4

1 回答 1

24

DeviceIoControl 非常不友好。但是您可以减轻痛苦,您不必自己编组结构。您可以利用两件事:C# 支持方法重载,并且 pinvoke 编组器会相信您,即使您对声明撒谎。这对于结构来说是完美的,它们已经被编组为一个字节块。正是 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");

这应该比你所拥有的看起来更漂亮,更容易诊断。未经测试,应该接近。

于 2013-06-27T23:35:53.603 回答