2012-06-20 15 views
8

私はC#で動作するプロトタイプコードアプリケーションを開発していましたが、古いC++コードのクラスと関数を(インポートされたDLLの形で)使用しています。コード要件は、クラスオブジェクトをアンマネージC++ DLL(C#から)に渡して、後でC#アプリケーションで取得できるように格納/変更することです。ここで私はこれまで...C#クラスオブジェクトをC++ DLLクラスの内外に渡す

シンプルなC++ DLLクラスを持っているコードは次のとおりです。

class CClass : public CObject 
{ 
public: 
    int intTest1 
}; 

C++ DLL関数:

CClass *Holder = new CClass; 

extern "C" 
{ 
    // obj always comes in with a 0 value. 
    __declspec(dllexport) void SetDLLObj(CClass* obj) 
    { 
     Holder = obj; 
    } 

    // obj should leave with value of Holder (from SetDLLObj). 
    __declspec(dllexport) void GetDLLObj(__out CClass* &obj) 
    { 
     obj = Holder; 
    } 
} 

C#クラスおよびラッパー:

[StructureLayout(LayoutKind.Sequential)] 
public class CSObject 
{ 
    public int intTest2; 
} 

class LibWrapper 
{ 
    [DLLImport("CPPDLL.dll")] 
    public static extern void SetDLLObj([MarshalAs(UnmanagedType.LPStruct)] 
     CSObject csObj); 
    public static extern void GetDLLObj([MarshalAs(UnmanagedType.LPStruct)] 
     ref CSObject csObj); 
} 

のC# DLLへの関数呼び出し:

class TestCall 
{ 
    public static void CallDLL() 
    { 
     ... 
     CSObject objIn = new CSObject(); 
     objIn.intTest2 = 1234; // Just so it contains something. 
     LibWrapper.SetDLLObj(objIn); 
     CSObject objOut = new CSObject(); 
     LibWrapper.GetDLLObj(ref objOut); 
     MessageBox.Show(objOut.intTest2.ToString()); // This only outputs "0". 
     ... 
    } 
} 

ジャンク値はDLL内で使用できるように見えます(渡されたC#オブジェクトからのもの)。クラスマーシャリングやメモリ/ポインタの問題で何かが欠けていると思います。私は何が欠けていますか?

編集: Bondが提案したC#/ C++のメソッド/関数定義の変更を反映するように上記のコードを変更しました。渡される値(1234)は、C#コードによって正しく取得されます。これはC++ DLLに別の問題を公開しています。 1234の値はC++コードでは使用できません。代わりに、オブジェクトはDLL内で値0を持ちます。私は定義済みのC++関数を使用してDLL内からオブジェクトを編集したいと思います。これ以上の助けをいただければ幸いです。ありがとう!あなただけのC#のクラスを使用している場合

+0

__declspec(のdllexport){&obj; ホルダー=} あなたはそのジャンククーゼをローカルCClassの痛みポインタを無効SetDLLObj(CClassのOBJ) を得ることができますので。 – user629926

+0

ホルダーは、後で取り出すためにC++コードに入ってくる値を格納しているように見えます。私はそれを見ることができますが、今私の主な懸念は、DLLに渡されたオブジェクトからの価値を引き出すことです。 – notsodev

答えて

5

ボンドが正しく、私は管理コードとアンマネージコードの間でオブジェクトを渡すことはできませんし、依然としてその格納された情報を保持しています。

私はオブジェクトを作成し、ポインタをC#のIntPtr型に戻すためにC++関数を呼び出すだけで終わった。私は、C#から必要なC++関数(externを提供)にポインタを渡すことができます。これは私たちがやりたいことは醜悪ではありませんでしたが、それが必要な範囲で目的を果たします。

ここはC#の例です。参考用に使っています。 (注:上記の例の 'int intTest'の代わりにStringBuilderを使用していますが、これは私たちのプロトタイプのためのものです。):

もちろん
class LibWrapper 
{ 
    [DllImport("CPPDLL.dll")] 
    public static extern IntPtr CreateObject(); 
    [DllImport("CPPDLL.dll")] 
    public static extern void SetObjectData(IntPtr ptrObj, StringBuilder strInput); 
    [DllImport("CPPDLL.dll")] 
    public static extern StringBuilder GetObjectData(IntPtr ptrObj); 
    [DllImport("CPPDLL.dll")] 
    public static extern void DisposeObject(IntPtr ptrObj); 
} 

public static void CallDLL() 
{ 
    try 
    { 
     IntPtr ptrObj = Marshal.AllocHGlobal(4); 
     ptrObj = LibWrapper.CreateObject(); 
     StringBuilder strInput = new StringBuilder(); 
     strInput.Append("DLL Test"); 
     MessageBox.Show("Before DLL Call: " + strInput.ToString()); 
     LibWrapper.SetObjectData(ptrObj, strInput); 
     StringBuilder strOutput = new StringBuilder(); 
     strOutput = LibWrapper.GetObjectData(ptrObj); 
     MessageBox.Show("After DLL Call: " + strOutput.ToString()); 
     LibWrapper.DisposeObject(ptrObj); 
    } 
    ... 
} 

C++が必要なすべての変更を実行し、C#内容にアクセスするための唯一の方法は、C++を介して所望のコンテンツを要求することによって、多かれ少なかれあります。この方法では、C#コードでは非マージンクラスのコンテンツにアクセスすることができず、両端でコード化するのに少し時間がかかります。しかし、それは私のために働く。

これは私が私の解決策の基礎を考え出すために使用される参照です。うまくいけば、これはいくつか他の人が、私はそれを理解しようとしたより多くの時間を節約することができます http://www.codeproject.com/Articles/18032/How-to-Marshal-a-C-Class

0

、あなたはそのhttp://msdn.microsoft.com/en-us/library/system.runtime.interopservices.gchandle%28v=vs.110%29.aspx

編集のためのGCHandleを使用する必要があります。

CClass *Holder; 

extern "C" 
{ 
    // obj always comes in with a 0 value. 
    __declspec(dllexport) void SetDLLObj(CClass* obj) 
    { 
     (*Holder) = (*obj); 
    } 

    // obj should leave with value of Holder (from SetDLLObj). 
    __declspec(dllexport) void GetDLLObj(__out CClass** &obj) 
    { 
     (**obj) = (*Holder); 
    } 
} 
+0

これは、2人の間のメモリアクセスに関連するもので、私はGCHandleを横断していないと感じたので興味深いです。しかし、これを使用して、私はCSObjectを渡す代わりにIntPtr型を使用する必要があります。 CSObjectクラスをIntPtrに変換する方法はありますか?それ以外の場合、私はクラスの内容を渡すことはできません。ありがとう! – notsodev

+0

CClassをCClass *またはvoid *に置き換えるだけで、C#側でIntPtrとして使用できます。 C++側で値を変更していますか?また、C#クラスCClassは自動的にCClass *としてマーシャリングします。クラスを構造体として宣言してみてください。 – user629926

+0

public static extern void GetDLLObj( out CSObject csObj); – user629926

4

を私はあなたがこの

__declspec(dllexport) void getDLLObj(__out CClass* &obj) 

のようなあなたを返すメソッドを宣言する必要があると考えていますC#プロトタイプ

public static extern void GetDLLObj([MarshalAs(UnmanagedType.LPStruct)] 
     ref CSObject csObj); 

インバウンドメソッドもCClassへのポインタを取る必要があります.C#プロトタイプはokです。

+0

DLLをコンパイルしようとしたときに、 "参照のポインタが不正です"というエラーが表示されます。また、C++関数を参照として定義すると、publicクラスの "クラス内のconnotアクセスprivateメンバー"エラーが発生します。返す関数をクラスへのポインタとして定義すると、C#アプリケーションの出力が変更されますが、私が言及したクラスジャンクがDLに渡され、代わりに0の値に変更されます。これは別の問題に関連していますか? – notsodev

+0

私の間違いは、ポインタへの参照かポインタへのポインタでなければなりません。サンプルコードを修正しました。 getDLLObjのポインタへの参照とSetDLLObjのポインタだけを使用し、パラメータにrefキーワードを追加してgetDLLObjのC#プロトタイプを修正する必要があります。 – Bond

+0

ありがとうございましたBond、あなたの提案は、DLL内に格納されているジャンク値を削除するのに役立ちました。しかし、私はまだ0の値が渡されているという私の問題を1234にする必要があります。まだunmangedとmanaged memoryの間に@ user629926という問題があります。私はあなたの助けに感謝し、他の考えは素晴らしいだろう! – notsodev

関連する問題