ここでSetupDiGetDeviceInterfaceDetail() を呼び出していますが、SP_DEVICE_INTERFACE_DETAIL_DATA 構造が正しくマーシャリングされていません。構造体の定義はここにあります。PInvoke.net のこの構造の定義を使用しようとしましたが、ここでは役に立ちませんでした。
これまでのところ、関数の呼び出しが成功した場合 (つまり、マーシャラーがエラーをスローしなかった場合)、戻り値は 1784 (INVALID_USER_BUFFER) です。キッカーは、このコードが私のボックスの 32 ビット プロセスから実行される場合、これらすべてが正常に機能することです。64 ビット プロセスで実行すると、この問題が発生します。
私の現在の SetupDiGetInterfaceDetailData() 署名は次のようになります。
[DllImport(@"c:\Windows\System32\SetupApi.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return : MarshalAs(UnmanagedType.Bool)]
public static extern bool SetupDiGetDeviceInterfaceDetail(
SafeHandleZeroOrMinusOneIsInvalid deviceInfoSet,
ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
IntPtr deviceInterfaceDetailData,
uint deviceInterfaceDetailDataSize,
IntPtr requiredSize,
IntPtr deviceInfoData);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SP_DEVICE_INTERFACE_DETAIL_DATA
{
public UInt32 cbSize;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string DevicePath;
}
現在、私は Marshal.AllocHGlobal() でメモリを割り当て、Marshal.* 関数ファミリーを使用してそのバッファからデータを読み書きしています。
参考までに、これは私がやっていることです:
public string GetPathToDevice(SafeHandleZeroOrMinusOneIsInvalid hDevList,
SP_DEVICE_INTERFACE_DATA devIntfData)
{
uint sizeNeeded = 0;
// get's the size needed
SetupApi.SetupDiGetDeviceInterfaceDetailData(hDevList,
ref devIntfData,
IntPtr.Zero,
0,
ref sizeNeeded,
IntPtr.Zero);
IntPtr pBuffer = Marshal.AllocHGlobal((int)(sizeNeeded + 4)); // +4 for cbSize
SetupApi.SetupDiGetDeviceInterfaceDetailData(hDevList,
ref devIntfData,
pBuffer,
sizeNeeded,
IntPtr.Zero,
IntPtr.Zero);
// copy bytes from unmanaged space in pBuffer to a manged byte array
// free unmanaged memory
return theStringParsedFromByteArray;
}
前述したように、SP_DEVICE_INTERFACE_DETAIL_DATA (上記のリンクを参照) の PInvoke.net で概説されている構造を定義して、それを処理する新しい PInvoke メソッド シグネチャを作成してみました。64 ビット システムから実行すると、同じ問題が発生します。つまり、関数が 1784 を返します。その理由は、64 ビット ランタイムで実行する場合、C# での参照が 8 バイト アラインされているためと思われます (別のStackOverflow の記事)。その構造体に対してさまざまなレイアウトを試し、(明示的なフィールド オフセットを使用して) レイアウトを 4 バイト アラインされた構造体に強制しようとしましたが、それもうまくいきませんでした。コンパイル時間の問題がありました。
PInvoke メソッドの署名パラメーターにさまざまな装飾を使用してみました。MarshalAs(UnmanagedType.LPStruct) など、不適切にペアリングし続けています。私は今、これについて助けが必要なところまで来ています。
私が本当に理解していないのは、なぜそれが起こっているのかということです。私のボックスで 32 ビット ランタイムで動作する場合でも、64 ビット ランタイムはセットアップ API の正しい 64 ビット バージョンに接続するだけではないでしょうか? どうしたの?
助けてくれてありがとう、アンディ
問題は解決しました
良いことは、それが解決されたことです。イライラすることは、ここに投稿してから 1、2 時間以内に問題を修正するのが好きではないことです。したがって、問題は確かに64ビットの問題でした。Marshal.GetLastWin32Error() からのエラー コードが問題を教えてくれました。cbSize 値が正しくありませんでした。8 に変更したところ、すべてが機能するようになりました。
誰か、サイズが 64 ビットで 8 になった理由を説明してください。構造は上記のとおりです (コメント投稿者から、それを含めるように依頼されました)。この構造は、単一の DWORD と TCHAR[ANYSIZE_ARRAY] の 2 つのメンバーで構成されます。ANYSIZE_ARRAY は 1 と評価されます。TCHAR は、Unicode の場合は常に WCHAR であり、それ以外の場合は char です。DWORD は常に 32 ビット (4 バイト) であり、Unicode の 1 つの TCHAR は 2 バイトです。では、4 + 2 = 6 です。なぜ 8 なのですか? これは、64 ビットでのその構造のバイト アラインメントによるものですか? 私は本当にこれを理解したいです。
とにかく、cbSize メンバーを 64 ビットの場合は 8、32 ビットの場合は 6 に設定すると機能し、生のメモリの割り当て/割り当て解除とマーシャリングの代わりに、上記で定義した構造を使用できます。