- - PR -
C# プラットフォーム呼び出しのためのstruct定義
1
投稿者 | 投稿内容 | ||||
---|---|---|---|---|---|
|
投稿日時: 2005-06-07 11:33
以下のようなDLL(アンマネージド)の関数を
C#からプラットフォーム呼び出ししたいと考えています。 ■メソッド BOOL SampleMethod( LPVOID SAMPLEDATA); ■sampleData構造体 typedef struct{ char hi[2]; char hinmei[3][20]; char suuryou[3][4]; }SAMPLEDATA; 以下のような書き方になると思うのですが、 配列のstruct定義の方法がよくわかりません。 [StructLayout(LayoutKind.Sequential)] struct SAMPLEDATA { [MarshalAs(UnmanagedType.ByValTStr,SizeConst=2)] public string hi; [MarshalAs(UnmanagedType.ByValTStr,SizeConst=60)] public string hinmei; [MarshalAs(UnmanagedType.ByValTStr,SizeConst=12)] public string suryou; } [DllImport("IwLnpApi.dll")] private extern static bool sampleMethod( SAMPLEDATA data ); 上記を試してもやはりうまくいきません。 多次元配列のところが間違っていると思います。 (その他もおかしい?) どのように定義すればよいのでしょうか? よろしくお願いいたします。 以上 | ||||
|
投稿日時: 2005-06-07 13:11
SampleMethodの仮引数・LPVOIDは*voidのことですよね。
つまりなんらかの値のポインタを指すことになります。 C#において、structは値型です。 値型の場合、代入したりメソッド引数に渡したりした場合、値のコピーが渡されます。 つまりSampleMethod(new SAMPLEDATA())とした場合、74バイトのデータがそのままコピーされ引数として渡されます。 これは明らかに望んでいることとは違うでしょう。 渡すのは飽くまでポインタ、Win32なら4バイトだけです。 ポインタを要求する関数に渡すのは、.NETでは参照渡しと言うことになります。 //厳密には違うはずですがその辺はマーシャラが巧くやってくれます。 参照を渡すには、
DllImportで一般的なのは後者の手法でしょう。 ところでSAMPLEDATA構造体ですが、UnmanagedType.ByValTStrを使うときはStructLayout属性にCharSetも欲しいところです。 まあデフォルトではAnsiのはずなので問題はないでしょうけど。 それにしてもchar[C/C++]/string[C#]で宣言するには微妙なメンバ名ですが。 ちなみにUnmanagedType.ByValTStrを宣言した場合、データはインラインのバイト列で表現されるようになりますから、例えばhinmeiを
などとしても問題ありません。 | ||||
|
投稿日時: 2005-06-10 14:14
Hongliangさん
ご指導ありがとうございました。 返信遅れましてすみません。 実はいろいろ試しているのですが、まだうまくいかないところがあります。 ご指摘のとおり以下の様にしてみました。 ■Struct定義 [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi )] struct BIHIN { [MarshalAs(UnmanagedType.ByValTStr,SizeConst=20)] public string hinmei1; [MarshalAs(UnmanagedType.ByValTStr,SizeConst=20)] public string hinmei2; [MarshalAs(UnmanagedType.ByValTStr,SizeConst=20)] public string hinmei3; } ■メソッド BOOL SampleMethod( ref SAMPLEDATA data); ■呼び出し [DllImport("IwLnpApi.dll")] private extern static bool sampleMethod( ref SAMPLEDATA data ); すると、結果がどれも20byteで渡せているはずが、 20byte目がマネージドのC++PGMで欠落してしまいます。 C#のほうでhinmei1を表示させてもしっかり20byte入っています。 何が問題なのでしょうか。。。 お手数ですがご指導よろしくお願いいたします。 以上 | ||||
|
投稿日時: 2005-06-10 15:24
C/C++において、文字列は終端文字\0(0x00のバイトデータ)をもって終了とされ、
その文字列を格納するchar配列の長さは終端文字を含みます。 つまりchar name[10]と宣言されている場合、 格納できる文字数は最大でASCIIで9文字+終端文字と言うことになります。 C#においては、文字列はstringクラスで表され、長さなどのメタデータをクラス自身が持つため、 文字列には終端文字は存在しません。不必要ですから。 よって、終端文字を持たないC#のstringから持つC/C++のchar[]へマーシャリングする際に、 終端文字をどこかで付加する必要があります。 問題はどこに付加するかです。 char[]の長さが十分に大きい場合は悩まずstringの一番後ろにつければ済む話です。 しかし確保されるchar[]よりもstringの方が長い場合、 途中で文字列を打ち切って、終端文字をつけねばなりません。 //そうでないとバッファオーバーフローしちゃいます。 さて、例えばstringがASCII10文字、char[]で確保する必要があるのが10バイト(つまりSizeConst=10)だった場合。 一見問題ないように見えますが、ここまで述べたとおり終端文字が必要です。 char[]が10バイト確保されている場合表現できるのは必ずASCII9文字分までです。 ですからこのstringの最後1文字は切り捨てられ、代わりに\0文字が付け足されます。 もしこのような処理が不要で、単純に10バイトのデータが欲しいだけなら、 C#の構造体宣言でstringを使用することはできません。 代わりにMarshalAs(UnmanagedType.ByValArray, SizeConst=10)で修飾されたbyte配列を使用する必要があります。 [ メッセージ編集済み 編集者: Hongliang 編集日時 2005-06-10 15:26 ] |
1