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

当我调用它时,它至少没有抛出任何异常。那真是太难看了,不过屁股疼得厉害。不能像我希望的那样,将数据复制到本地结构中吗?
输出数据有时会很棘手,因为它们不是固定大小的结构。我知道马歇尔不可能自动处理这个问题,我可以做我需要的HGORD和复印业务。
附加:
This question起初看起来很有帮助,但最终只是一个不正确的常数。
我并不反对使用unsafe结构。(fixed-sizestruct成员需要此功能。)

最佳答案

设备控制非常不友好。但你可以让它不那么痛苦,你不必自己整理结构。你可以利用两件事:C.*支持方法重载,pPoECK封送器会相信你,即使你在声明中撒谎。这对于结构来说是完美的,它们已经被封送为一个字节块。正是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_属性的重载:
[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");

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

关于c# - Pinvoke DeviceIoControl参数,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/17268889/

10-10 00:52