- - PR -
C# クラスの配列を含むクラスをマーシャリングする方法
1
投稿者 | 投稿内容 | ||||
---|---|---|---|---|---|
|
投稿日時: 2008-01-18 09:56
Visual C# 2005でVC++6.0で作成されたDLLを使用する処理を作成しています。
このDLL内の"クラスの配列を含むクラス"を引数とする関数呼出を行うと、 DLL内の関数で、配列にしたクラスの値が正常に読めません。 また、DLL内の関数で、配列にしたクラスの値を変更してもC#側で受け取れません。 御存知の方がいらっしゃいましたら教えて下さい。 [環境] VisualStudio.NET2005、Windows2000 [詳細] ・DLLのclass宣言+関数宣言 class Dllapi { ////////////////////DLLの構造体宣言 [StructLayout(LayoutKind.Sequential, Pack=4, CharSet=CharSet.Auto)] public class DLLDATA_SUB { public int dummy2; } [StructLayout(LayoutKind.Sequential, Pack=4, CharSet=CharSet.Auto)] public class DLLDATA { public int dummy1; public DLLDATA_SUB[] arry = new DLLDATA_SUB[2]; public DLLDATA(){ for (int cnt = arry.GetLowerBound(0); cnt <= arry.GetUpperBound(0); cnt++) arry[cnt] = new DLLDATA_SUB(); } } //////////////////// ////////////////////DLLの関数宣言 [DllImport("DllProc.dll")] public extern static int fnDllProc([In, Out] DLLDATA data); } ・処理 Dllapi.DLLDATA dat = new Dllapi.DLLDATA(); dat.dummy1 = 10; dat.arry[0].dummy2 = 20; Dllapi.fnDllProc(dat); ←この関数内でdummy2が正常に受け取れない。(dummy1は正常に受け取れる) もともと、DLLでは 以下の様な構造体を受け取る様に設計されているのですが、 ボクシング時に値のコピーになる等あるため、 構造体を使わずclassとして作成しています。 typedef struct { int dummy2; } DLLDATA_SUB; typedef struct { int dummy1; DLLDATA_SUB arry[2]; } DLLDATA; DLLに渡ったときに、 dummy1 arry[0].dummy2 arry[1].dummy2 の様なメモリ配置になっていない様です。 (DLLDATA_SUBを配列にしなければ正常に動いている様です。) 以上、よろしくお願いします。 | ||||
|
投稿日時: 2008-01-18 10:32
上の投稿ですが、
構造体ではなくclassにしたのは、 ボクシング時の値コピーを避けるためではなく、 class Dllapi { ////////////////////DLLの構造体宣言 [StructLayout(LayoutKind.Sequential, Pack=4, CharSet=CharSet.Auto)] public struct DLLDATA_SUB { public int dummy2; } [StructLayout(LayoutKind.Sequential, Pack=4, CharSet=CharSet.Auto)] public struct DLLDATA { public int dummy1; [MarshalAs(UnmanagedType.ByValArray, SizeConst=2)] public DLLDATA_SUB[] arry; public void Initialize(){arry = new DLLDATA_SUB[2];} } ////////////////////DLLの関数宣言 [DllImport("DllProc.dll")] public extern static int fnDllProc(ref DLLDATA data); } となり、 C#側で使用するときに、 Dllapi.DLLDATA dat = new Dllapi.DLLDATA(); dat.Initialize(); ←このInitial関数を呼ばなくてはならない。 dat.dummy1 = 10; dat.arry[0].dummy2 = 20; Dllapi.fnDllProc(ref dat); の様に、Initial用の関数を呼ぶのを省略したかった為です。 以上、訂正させて頂きます。 よろしくお願いします。 | ||||
|
投稿日時: 2008-01-18 11:53
諦めて C# 側のを struct で定義して下さい。
class で定義した場合は何をどうやっても値として扱うことはできません。常にポインタとして扱われます。 で、引数なしコンストラクタが struct で定義できないのが不満なら、代わりに static な New メソッドでも用意すればいいのではないでしょうか。
[ メッセージ編集済み 編集者: Hongliang 編集日時 2008-01-18 11:54 ] | ||||
|
投稿日時: 2008-01-18 20:14
Hongliang様
早速のアドバイスありがとうございました。 大変参考になりました。 例えば以下のような場合、 もっときれいに書けるのでしょうか?
ちなみに、HOGE_SUB_AやHOGE_SUB_Bも、 DllApi.HOGE_SUB_B tmp = DllApi.HOGE_SUB_B.New(); の様にHOGEとは別に単独で使用したいので、 HOGEと同じ形にしています。 是非、アドバイス頂けないでしょうか。 よろしくお願いします。 | ||||
|
投稿日時: 2008-01-18 23:07
いいんじゃないですかそれで。
// 私はガチで C/C++ とマーシャリングするならさっさと C++/CLI に行きます。 | ||||
|
投稿日時: 2008-01-20 07:46
Hongliangさま
アドバイスありがとうございました。 大変参考になりました。 解決とさせていただきます。 本当にありがとうございました。 |
1