トップ回答者
DeviceIoControlでの、構造体サイズの動的変更

質問
-
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;
回答
-
API のリファレンスに付けられている名前を C# で定義する際に .NET 風の名前に変更するのは、私自身普通にやってますが、掲示板で出すときにはせめて元の名前を併記してください。調べるのが面倒です。リファレンスに載っている C 言語用の定義も書いていただければ、調べる必要もなくなることも多いです。
でまあ C でどう定義されているのか分からないので推測で書くと、HostControllerDriverKeyName.Name は WCHAR wszName[1]; とかそんなんでしょうか。
C# では可変長構造体のマーシャリングはサポートできません。UnmanagedType.LPWStr は名前どおり LPWSTR であって、そこにはポインタが格納されていなければいけませんから。構造体内の WCHAR 配列では直に文字列として格納されますからね。
一番手っ取り早いのは、DeviceIOControl に渡したポインタを uint だけずらして、それに対して Marshal.PtrToStringUni をかけることかな。
ま、所詮は推測ですけど。unsafe 版とやらも不明ですし。
-
Hongliang さんのおっしゃるとおり、可変長メンバ変数を持つ構造体のマーシャリングは出来ません。
とりあえず、類似スレッドがあったのでそちらを参考にしてみてください。http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=32627&forum=7
すべての返信
-
API のリファレンスに付けられている名前を C# で定義する際に .NET 風の名前に変更するのは、私自身普通にやってますが、掲示板で出すときにはせめて元の名前を併記してください。調べるのが面倒です。リファレンスに載っている C 言語用の定義も書いていただければ、調べる必要もなくなることも多いです。
でまあ C でどう定義されているのか分からないので推測で書くと、HostControllerDriverKeyName.Name は WCHAR wszName[1]; とかそんなんでしょうか。
C# では可変長構造体のマーシャリングはサポートできません。UnmanagedType.LPWStr は名前どおり LPWSTR であって、そこにはポインタが格納されていなければいけませんから。構造体内の WCHAR 配列では直に文字列として格納されますからね。
一番手っ取り早いのは、DeviceIOControl に渡したポインタを uint だけずらして、それに対して Marshal.PtrToStringUni をかけることかな。
ま、所詮は推測ですけど。unsafe 版とやらも不明ですし。
-
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 */
} -
Hongliang さんのおっしゃるとおり、可変長メンバ変数を持つ構造体のマーシャリングは出来ません。
とりあえず、類似スレッドがあったのでそちらを参考にしてみてください。http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=32627&forum=7