Pinvoke DeviceIoControl parameters

本文关键字:parameters DeviceIoControl Pinvoke | 更新日期: 2023-09-27 18:19:33

我正在使用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);

当我叫它的时候,它至少没有任何例外。这真是太丑陋了,但也让人感到非常痛苦。整理程序不能像我希望的那样处理将数据复制到我的本地结构中吗?

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

附加:

这个问题一开始看起来很有用,但最终却变成了一个不正确的常数。

我并不反对使用unsafe构造。(fixed大小的struct成员需要此功能。)

Pinvoke DeviceIoControl parameters

DeviceIoControl非常不友好。但你可以让它不那么痛苦,你不必自己整理结构。你可以利用两件事:C#支持方法重载,即使你在声明上撒谎,pinvoke-mashaller也会相信你。这对于结构来说是完美的,因为它们已经被封送为一个字节块。这正是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
);

假设您对它返回STORAGE_DEVICE_DESCRIPTOR:感兴趣,那么您可以添加一个非常适合IOCTL_STORAGE_QUERY_PROPERTY的重载

[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");

它应该看起来比你现有的更漂亮,更容易诊断。未测试,应该接近。