9

SP_DEVICE_INTERFACE_DETAIL_DATA構造:

typedef struct _SP_DEVICE_INTERFACE_DETAIL_DATA {
  DWORD cbSize;
  TCHAR DevicePath[ANYSIZE_ARRAY];
} SP_DEVICE_INTERFACE_DETAIL_DATA, *PSP_DEVICE_INTERFACE_DETAIL_DATA;

Marshal.SizeOfC# で正しく動作するように宣言するにはどうすればよいですか?

動的バッファーの割り当てに問題はありません。ハードコードされていない適切な方法で計算したいだけです。cbSize

PInvoke.netの定義は間違っています。
PInvoke.net の説明も間違っています。

SP_DEVICE_INTERFACE_DETAIL_DATA didd = new SP_DEVICE_INTERFACE_DETAIL_DATA();
didd.cbSize = 4 + Marshal.SystemDefaultCharSize; // trust me :)

彼を信用しないでください。
4 + Marshal.SystemDefaultCharSizex86 でのみ有効です。についても同じですsizeof(int) + Marshal.SystemDefaultCharSize。x64 では、惨めに失敗します。

これは、アンマネージ C++ が提供するものです。

x86
構造体サイズ A:5
デバイス パスのオフセット A:4
構造体サイズ W:6
デバイス パスのオフセット W:4

x64
構造体サイズ A:8
デバイス パスのオフセット A:4
構造体サイズ W:8
デバイス パスのオフセット W:4

StructLayoutとパラメータの可能な限りの組み合わせを試しMarshalAsましたが、上記の値を返すことができませんでした。

正しい申告は?

4

5 に答える 5

9

構造の重要なポイントは、それがどのくらいの大きさであるべきかわからないということです。SetupDiGetDeviceInterfaceDetail() を 2 回呼び出す必要があります。最初の呼び出しでは、DeviceInterfaceDetailSize 引数に意図的に 0 を渡します。これはもちろん失敗しますが、RequiredSize 引数は、構造体に必要な大きさを教えてくれます。次に、適切なサイズの構造体を割り当てて、再度呼び出します。

構造体の動的なサイズ変更は、pinvoke マーシャラーまたは C# 言語では直接サポートされていません。したがって、構造を宣言してもまったく役に立ちません。試してはいけません。Marshal.AllocHGlobal() を使用する必要があります。これにより、DeviceInterfaceDetailData 引数として渡すことができるポインターが取得されます。Marshal.WriteInt32 で cbSize を設定します。今すぐ電話をかけてください。そして返された文字列を Marshal.PtrToStringUni() で取得します。Marshal.FreeHGlobal をクリーンアップします。メソッド名からこれを行うコードをグーグル検索するのに問題はないはずです。


cbSize メンバーが問題です。SetupApi.h SDK ヘッダー ファイルには次の内容が含まれています。

#ifdef _WIN64
#include <pshpack8.h>   // Assume 8-byte (64-bit) packing throughout
#else
#include <pshpack1.h>   // Assume byte packing throughout (32-bit processor)
#endif

これは厄介です。C コンパイラは、配列の後に 2 バイトのパディングがあると認識しますが、実際にはありません。C# コードでは、StructLayoutAttribute.Pack の値は、32 ビット コードと 64 ビット コードで異なる必要があります。2 つの構造体を宣言せずにこれをきれいに行う方法はありません。そして、IntPtr.Size の値に基づいてそれらの中から選択します。または、構造体宣言は役に立たないので、ハードコーディングするだけです。32 ビット モードでは 6、64 ビット モードでは 8 です。どちらの場合も、文字列はオフセット 4 から始まります。もちろん、Unicode 文字列を想定すると、ansi 文字列を使用しても意味がありません。

于 2012-05-24T00:02:25.757 に答える
0

Mike Danesは、JamieSeeが提供したリンクで正しいマーシャリングを行っています:http ://social.msdn.microsoft.com/Forums/en/clr/thread/1b7be634-2c8f-4fc6-892e-ece97bcf3f0e

ただし、彼はポインタ演算を誤って実行しました。

正しい

detail = (IntPtr)(detail.ToInt64() + 4); //skip the cbSize field

正しくありません(x64で正しい値を生成できない可能性があります)

detail = (IntPtr)(detail.ToInt32() + 4); //skip the cbSize field

取得したサイズ値が表示される理由は、パディングが原因です。パディングは、呼び出されている関数とは関係ありません。重要なのは、最初の呼び出しでcbSize> = 4であるということです(必要な実際のサイズを取得するため)。

于 2012-05-23T23:21:57.660 に答える
0

実行時に何かをしなければなりません。

コード:

didd.cbSize = Marshal.SizeOf(typeof(Native.SP_DEVICE_INTERFACE_DETAIL_DATA));
if (IntPtr.Size == 4)
{
    didd.cbSize = 4 + Marshal.SystemDefaultCharSize;
 }
于 2013-04-10T07:26:18.633 に答える
0

XPでのみこれを確認しました:

DWORD get_ascii_detail_size(void)
{
   DWORD detail[2], n;

   for(n=5;n<=8;n+=3)
   {
      detail[0]=n;
      SetupDiGetDeviceInterfaceDetailA(NULL, NULL, detail, n, NULL, NULL);
      if (GetLastError()!=ERROR_INVALID_USER_BUFFER) return(n);
   }
   return(0);
}

SetupApi.dll 内のコードを調べたところ、ASCII バージョンが最初に行うことは、詳細の NULL をチェックし、次にハードコードされた値に対して正しい cbSize をチェックすることです。これは、ASCII バージョンが Widechar バージョンにフィードされるためです。

最初の 2 つのパラメーターが無効な Widechar API を使用してこれを行うことはできません。Widechar API を使用している場合は、この関数のサイズを WORD で揃えます。

誰かが他のシステムでこれを確認できれば幸いです。

于 2013-11-20T20:15:25.583 に答える