C#でパックされた構造体のアンマネージバッファをマーシャリングする方法
-
21-08-2019 - |
質問
次の pinvoke シグネチャを使用して、C# で Windows FilterSendMessage 関数を (正常に) 呼び出しています。
[DllImport("fltlib.dll")]
public static extern IntPtr FilterSendMessage(
IntPtr hPort,
IntPtr inBuffer,
UInt32 inBufferSize,
IntPtr outBuffer,
UInt32 outBufferSize,
out UInt32 bytesReturned);
の アウトバッファ パラメータには、C で次のように定義される、任意の数の構造体 (次々にパックされる) が設定されます。
typedef struct _BAH_RECORD {
int evt
int len;
WCHAR name[1];
} BAH_RECORD, *PBAH_RECORD;
の 名前 フィールドには、可変長の NULL で終わる Unicode 文字列が割り当てられます。の レン フィールドには、構造体の合計サイズ (名前文字列を含む) がバイト単位で示されます。アンマネージ側での構造体の処理方法には何の問題もないと確信しています。
C# で次のように定義されている BAH_RECORD 構造体のインスタンスに outBuffer をマーシャリングしようとすると、問題が発生します。
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct BAH_RECORD
{
public UInt32 evt;
public UInt32 len;
public string name;
}
IntPtr outBuffer = Marshal.AllocHGlobal(OUT_BUFFER_SIZE);
hResult = Win32.FilterSendMessage(hPortHandle, inBuffer, IN_BUFFER_SIZE, outBuffer, OUT_BUFFER_SIZE, out bytesReturned);
BAH_RECORD bah = (BAH_RECORD)Marshal.PtrToStructure(outBuffer, typeof(BAH_RECORD));
<snip>
bah.name を印刷/表示/表示しようとすると、ガベージが表示されます...
outBuffer に実際に有効なデータが含まれていることを確認するために、C# で粗雑なポインター ハッキングを実行して、Marshal.ReadInt32 を 2 回呼び出し (最初の 2 つの構造体フィールドをカバーするため)、次に Marshal.ReadByte を数回呼び出してバイトを設定しました。 [] 次に、Encoding.Unicode.GetString() への引数として使用します...文字列は正常に出力されるので、間違いなくそこにありますが、マーシャラーにそれを正しく処理させることができないようです(たとえそれがあったとしても)できる?)
助けていただければ幸いです
スティーブ
解決
問題は、C# BAH_RECORD 構造体の 'name' 文字列が文字列 (WCHAR*) へのポインターとしてマーシャリングされているにもかかわらず、C 側ではインライン WCHAR バッファーであることです。したがって、構造体をマーシャリングすると、ランタイムはバッファーの最初の 4 バイトをポインターとして読み取り、それが指す文字列を読み取ろうとします。
残念ながら、ランタイムが構造体の内部で可変サイズのバッファーを自動的にマーシャリングする方法はないため、手動でマーシャリング (または「ポインターハッカリー」と呼ばれる) を使用する必要があります。ただし、バッファーを指すようにポインターを進めるときは、バイトを個別に読み込んで文字列に変換する必要はありません。Marshal.PtrToStringUni を呼び出すだけです。