13

を使用して 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
    );

私は見たことがありませんでしobjectたが、MSDNのドキュメントは有望に聞こえました:[MarshalAs(UnmanagedType.AsAny)]

実行時にオブジェクトの型を決定し、オブジェクトをその型としてマーシャリングする動的な型。このメンバーは、プラットフォーム呼び出しメソッドに対してのみ有効です。

私の質問は次のとおりです。この署名を使用する「最良の」および/または「適切な」方法は何ですか?

たとえば、構造体であることがIOCTL_STORAGE_QUERY_PROPERTY期待InBufferされますSTORAGE_PROPERTY_QUERYnewその構造体を定義し、インスタンスを作成して、それを 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 は非常に不親切です。しかし、構造を自分でマーシャリングする必要はありません。利用できる 2 つのこと: 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
);

そして、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");

これは、あなたが持っているものよりもきれいで、はるかに診断しやすいはずです. テストされていない、近いはずです。

于 2013-06-27T23:35:53.603 に答える