簡體   English   中英

將C#類對象傳入和傳出C ++ DLL類

[英]Passing a C# class object in and out of a C++ DLL class

我一直在研究在C#中運行的原型代碼應用程序,並使用舊C ++代碼中的類和函數(以導入的DLL的形式)。 代碼要求是將類對象傳遞給非托管C ++ DLL(來自C#),並將其存儲/修改以便以后由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);
}

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#對象)。 我相信我錯過了類編組或內存/指針問題。 我錯過了什么?

編輯:我改變了上面的代碼,以反映邦德建議的C#/ C ++中方法/函數定義的變化。 傳入的值(1234)現在由C#代碼正確檢索。 這暴露了C ++ DLL中的另一個問題。 1234值不可用於C ++代碼。 相反,該對象在DLL內部的值為0。 我想使用預定義的C ++函數來編輯DLL中的對象。 非常感謝任何更多的幫助。 謝謝!

Bond是正確的,我無法在托管代碼和非托管代碼之間傳遞對象,但仍保留其存儲的信息。

我最后只是調用C ++函數來創建一個對象並將指針傳遞回C#的IntPtr類型。 然后我可以將指針傳遞給我需要的任何C ++函數(假設它是extern)來自C#。 這並不是我們想做的事情,但它會在我們需要的范圍內達到目的。

這是C#我正在使用的包裝器/示例/參考。 (注意:我正在使用StringBuilder而不是上面示例中的'int intTest'。這就是我們原型所需的。為了簡單起見,我只是在類對象中使用了一個整數。):

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

希望這可以幫助其他人節省比我試圖解決的更多時間!

我相信你應該像這樣聲明你的返回方法

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

分別是C#原型

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

入站方法也應該使用指向CClass的指針,C#原型是可以的。

如果你只使用C#中的類,那么你應該使用GCHandle來實現這個目的http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.gchandle%28v=vs.110%29.aspx

編輯:

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);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM