14

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

シンプルな 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);
}

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

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 内からオブジェクトを編集したいと考えています。これ以上の助けをいただければ幸いです。ありがとう!

4

3 に答える 3

12

ボンドは正しかった。マネージドコードとアンマネージドコードの間でオブジェクトを渡すことはできず、保存されている情報を保持することはできません。

結局、C ++関数を呼び出してオブジェクトを作成し、ポインターをC#のIntPtr型に戻しました。次に、C#から必要なC ++関数(externの場合)にポインターを渡すことができます。これは私たちがやりたかったことではありませんでしたが、必要な範囲でその目的を果たします。

これが、example /referenceで使用しているラッパーのC#です。(注:上記の例の「intintTest」の代わりに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-aC-Class

うまくいけば、これは私がそれを理解しようとしたよりも多くの時間を節約するのに役立つでしょう!

于 2012-07-10T13:20:09.933 に答える
5

このように返すメソッドを宣言する必要があると思います

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

およびそれぞれ C# プロトタイプ

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

インバウンドメソッドもCClass へのポインターを取る必要があります。C# プロトタイプは問題ありません。

于 2012-06-20T16:51:58.827 に答える
0

C# からクラスを使用する場合は、その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);
    }
}
于 2012-06-20T16:50:51.947 に答える