1

ここで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 に設定すると機能し、生のメモリの割り当て/割り当て解除とマーシャリングの代わりに、上記で定義した構造を使用できます。

4

1 に答える 1

0

この回答からコードをコピーしたため、この問題にも陥りました: https://stackoverflow.com/a/2937588/1070906 構造体定義にエラーがあります。多分あなたは同じ間違いをしました。

http://msdn.microsoft.com/en-us/library/windows/hardware/ff552344(v=vs.85).aspxで SP_DEVINFO_DATA の定義を見ると、最後のパラメーターはポインターであり、のように uint ではありません。別の投稿。

私は構造体定義を次のように変更したので:

[StructLayout(LayoutKind.Sequential)]
private struct SP_DEVINFO_DATA
{
    /// <summary>Size of the structure, in bytes.</summary>
    public uint cbSize;
    /// <summary>GUID of the device interface class.</summary>
    public Guid ClassGuid;
    /// <summary>Handle to this device instance.</summary>
    public uint DevInst;
    /// <summary>Reserved; do not use.</summary>
    public IntPtr Reserved;
}

できます!

Marshall.SizeOf(new SP_DEVINFO_DATA ()) は 28 ではなく 32 を返し、シリアル ポートが表示されます。

于 2014-03-06T16:25:36.603 に答える