none
DeviceIoControlでの、構造体サイズの動的変更 RRS feed

  • 質問

  • unsafeな構造体をマーシャリングを使ってunsafeでない構造体にし、DeviceIoControl関数にて使用しようとしていますが2度目のアクセス時に文字列データが文字化けしてしまいます。どうしたらいいでしょうか?(unsafe版は正常に動作しています。)

    <構造体の宣言>
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public class HostControllerDriverKeyName
    {
        public uint ActualLength;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string Name;
    }

    <コード>
    HostControllerDriverKeyName valKeyName = new HostControllerDriverKeyName();
    int nReturned = Marshal.SizeOf(valKeyName);
    IntPtr ptrKeyName = Marshal.AllocCoTaskMem(nReturned);

    if (!DevIOCtl.DeviceIoControl(hHostController,
                                  DevIOCtl.ControlCode.USBHcdGetDriverKeyName,
                                  IntPtr.Zero,
                                  0,
                                  ptrKeyName,
                                  nReturned,
                                  ref nReturned,
                                  IntPtr.Zero))
    {
        Marshal.FreeCoTaskMem(ptrKeyName);
        return null;
    }

    nReturned = Marshal.ReadInt32(ptrKeyName);
    if (nReturned <= Marshal.SizeOf(valKeyName))
    {
        Marshal.FreeCoTaskMem(ptrKeyName);
        return null;
    }

    ptrKeyName = Marshal.ReAllocCoTaskMem(ptrKeyName, nReturned);
    if (ptrKeyName == IntPtr.Zero)
    {
        Marshal.FreeCoTaskMem(ptrKeyName);
        return null;
    }

    if (!DevIOCtl.DeviceIoControl(hHostController,
                                  DevIOCtl.ControlCode.USBHcdGetDriverKeyName,
                                  IntPtr.Zero,
                                  0,
                                  ptrKeyName,
                                  nReturned,
                                  ref nReturned,
                                  IntPtr.Zero))
    {
        Marshal.FreeCoTaskMem(ptrKeyName);
        return null;
    }

    Marshal.PtrToStructure(ptrKeyName, valKeyName);
    Marshal.FreeCoTaskMem(ptrKeyName);

    return valKeyName.Name;

    2007年1月10日 2:27

回答

  • API のリファレンスに付けられている名前を C# で定義する際に .NET 風の名前に変更するのは、私自身普通にやってますが、掲示板で出すときにはせめて元の名前を併記してください。調べるのが面倒です。リファレンスに載っている C 言語用の定義も書いていただければ、調べる必要もなくなることも多いです。

    でまあ C でどう定義されているのか分からないので推測で書くと、HostControllerDriverKeyName.Name は WCHAR wszName[1]; とかそんなんでしょうか。

    C# では可変長構造体のマーシャリングはサポートできません。UnmanagedType.LPWStr は名前どおり LPWSTR であって、そこにはポインタが格納されていなければいけませんから。構造体内の WCHAR 配列では直に文字列として格納されますからね。

    一番手っ取り早いのは、DeviceIOControl に渡したポインタを uint だけずらして、それに対して Marshal.PtrToStringUni をかけることかな。

    ま、所詮は推測ですけど。unsafe 版とやらも不明ですし。

    2007年1月10日 4:10
  • Hongliang さんのおっしゃるとおり、可変長メンバ変数を持つ構造体のマーシャリングは出来ません。
    とりあえず、類似スレッドがあったのでそちらを参考にしてみてください。

    http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=32627&forum=7

    2007年1月10日 5:13

すべての返信

  • API のリファレンスに付けられている名前を C# で定義する際に .NET 風の名前に変更するのは、私自身普通にやってますが、掲示板で出すときにはせめて元の名前を併記してください。調べるのが面倒です。リファレンスに載っている C 言語用の定義も書いていただければ、調べる必要もなくなることも多いです。

    でまあ C でどう定義されているのか分からないので推測で書くと、HostControllerDriverKeyName.Name は WCHAR wszName[1]; とかそんなんでしょうか。

    C# では可変長構造体のマーシャリングはサポートできません。UnmanagedType.LPWStr は名前どおり LPWSTR であって、そこにはポインタが格納されていなければいけませんから。構造体内の WCHAR 配列では直に文字列として格納されますからね。

    一番手っ取り早いのは、DeviceIOControl に渡したポインタを uint だけずらして、それに対して Marshal.PtrToStringUni をかけることかな。

    ま、所詮は推測ですけど。unsafe 版とやらも不明ですし。

    2007年1月10日 4:10
  • Hongliangさんへご指摘&アドバイスありがとうございます。

    遅れましたが、オリジナルのソースです。(WindowsDDKのusbioctl.hより)

    typedef struct _USB_HCD_DRIVERKEY_NAME {
        ULONG ActualLength;     /* OUTPUT */
        /* NULL terminated unicode driverkeyname for hcd */
        WCHAR DriverKeyName[1];   /* OUTPUT */
    } USB_HCD_DRIVERKEY_NAME, *PUSB_HCD_DRIVERKEY_NAME;

    ちなみにunsafeで記述して動作した構造体のソースです。

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    struct USB_HCD_DRIVERKEY_NAME
    {
         public uint ActualLength;     /* OUTPUT */
         /* NULL terminated unicode driverkeyname for hcd */
         public unsafe fixed char DriverKeyName[1];   /* OUTPUT */
    }

     

     

     

    2007年1月10日 4:53
  • Hongliang さんのおっしゃるとおり、可変長メンバ変数を持つ構造体のマーシャリングは出来ません。
    とりあえず、類似スレッドがあったのでそちらを参考にしてみてください。

    http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=32627&forum=7

    2007年1月10日 5:13
  • Hongliangさん、蒼の洞窟さん アドバイスありがとうございました。

    無事、解決いたしました。

     

    2007年1月10日 8:58