- PR -

doubleとstringをフィールドにもつ構造体のマーシャリングが正しく動作しない

1
投稿者投稿内容
momomo
会議室デビュー日: 2008/06/27
投稿数: 5
投稿日時: 2008-06-27 02:30
C#とDLL(VC++)間の構造体マーシャリングについて教えてください。

構造体のメンバーがdoubleとstring(char*)の場合に
C#からC++側に構造体をマーシャリングすると、正しく値が渡せません。
string以降のフィールドが参照しているアドレスがずれているように見えます。
何か間違っているのでしょうか?
また、doubleの項目をintにするか、char*をchar[](固定長)にすると
正しく値が渡せます。

C++側
typedef struct Sample
{
double x;
char* y;
double z; // doubleをintにすると正しく動作する
}

C#側
[StructLayout(LayoutKind.Sequential)]
public struct Sample
{
double x;
string y;
double z;
}

[DLLImport("sample.dll")]
private static extern int SampleMethod([In, Out] Sample[] samples);

private void StructureTest()
{
Sample[] samples = new Sample[2];
Sample sample1 = new Sample();
sample1.x = 1;
sample1.y = "test";
sample1.z = 2;
samples[0] = sample1;
sample2.x = 5;
sample2.y = "test";
sample2.z = 10;
samples[1] = sample2;

// DLL呼び出し
SampleMethod(samples); // DLL内部をデバッグすると、zが変な値になる。
}

Stringの部分をIntPtrにしてみたりもしましたが
どうしても上手くいきません。
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2008-06-27 10:13
.NET のマーシャリングはデフォルトでパックサイズ 8 bytes なので、ポインタが 4 バイトの環境だと次に double が来た場合 4 バイトのパディングが入ってしまいます。
VC++ のデフォルトは確かパックサイズ 4 bytes だったはずなので、DLL 側の構造体にはパディングが入りません。
StructLayout 属性で Pack フィールドを適切に設定してください。

あと StructLayout で CharSet も明示した方がいいかも。デフォルトで Ansi だから問題ないっちゃ問題ないけど。
momomo
会議室デビュー日: 2008/06/27
投稿数: 5
投稿日時: 2008-06-27 12:00
Hongliang さま

DLL作成者に確認したところ、パックサイズが1になっていました。
パックサイズをお互い合わせたところ、正常にデータの受け渡しができるようになりました。
ありがとうございました!
momomo
会議室デビュー日: 2008/06/27
投稿数: 5
投稿日時: 2008-06-27 21:02
サンプルの小さい構造体で成功したので解決したと思ったのですが、
フィールドがもっと多い構造体(int, float, char*)だとやはり失敗しました。

自分なりにパックについて調べてみて、
C#側を Pack = 4 にし、
VC++側にも #pragma pack(4) を指定してもらって解決したと思っていました。
他に何か方法がないでしょうか?(それとも私が勘違いしている?)

charを固定長にしないとだめなのか?とも思い始めています。
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2008-06-27 22:50
前提条件がそれだけなら問題なくマーシャリングできるはずです。
おそらく何かほかの要因が絡んでいます。
// ていうか「失敗」って何?

固定長にできるなら固定長の方が楽です。
特に DLL 側でメモリ確保とかやりだすと面倒です。
momomo
会議室デビュー日: 2008/06/27
投稿数: 5
投稿日時: 2008-06-28 11:32
Hongliang さま

遅くなってすみません。
おっしゃるとおり、他の要因が絡んでいるようです。
上手く行かなかった構造体をサンプルコードのように手動で設定したところ、
正常に値が渡りました。
他の要因を調べます(なんとなく凡ミスの気がします…)

> // ていうか「失敗」って何?
すみません。横着してしまいました。
VisualStudioのデバッグでVC++の引数に渡ってきた構造体の中を見たところ、
構造体の先頭付近のフィールドには期待した値が渡っていましたが
後半のフィールドの値が、想定外の変な値またはエラーになっていました。
教えていただいた、パックサイズの違いのように
読み取る箇所がずれているのだと考えています。

問題をきちんと切り分けて調べてみます。
momomo
会議室デビュー日: 2008/06/27
投稿数: 5
投稿日時: 2008-06-29 13:25
一応結果報告です。

パックサイズの指定だけで上手くいきました。
正常に動かないと思っていた箇所は、?(Nullabl型)にしていました…。

ありがとうございました。
1

スキルアップ/キャリアアップ(JOB@IT)