Pinvoke DeviceIoControl参数

Jon*_*art 12 c# pinvoke deviceiocontrol

我正在使用C#项目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
    );
Run Code Online (Sandbox Code Playgroud)

我从来没有见过object和前,但MSDN文档听起来前途:[MarshalAs(UnmanagedType.AsAny)]

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

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

例如,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), ...);
Run Code Online (Sandbox Code Playgroud)

但是,我刚刚System.ExecutionEngineException做了这个,所以我改为:

int cb = Marshal.SizeOf(typeof(...));
IntPtr query = Marshal.AllocHGlobal(cb);
...
Marshal.PtrToStructure(...);
Marshal.FreeHGlobal(query);
Run Code Online (Sandbox Code Playgroud)

当我打电话时,它至少没有抛出任何异常.这只是非常丑陋,虽然屁股上有巨大的痛苦.编组器是否能像我希望的那样处理复制数据到我/我的本地结构?

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

额外:

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

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

Han*_*ant 20

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
);
Run Code Online (Sandbox Code Playgroud)

并且你会添加一个非常适合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
);
Run Code Online (Sandbox Code Playgroud)

你会这样称呼它:

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");
Run Code Online (Sandbox Code Playgroud)

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