2012-06-20 22 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中的另一個問題。 C++代碼無法使用1234值。相反,該對象在DLL中的值爲0。我想使用預定義的C++函數從DLL中編輯對象。任何更多的幫助,不勝感激。謝謝!

+0

__declspec(dllexport)的無效SetDLLObj(CClass OBJ) { 持有人= &obj; } 將本地CClass的疼痛指針,以便你可能會得到那些垃圾淡然。 – user629926

+0

持有者確實似乎正在存儲進入C++代碼以供稍後檢索的值。我可以看看它,但我現在主要關心的是從傳入DLL的對象中提取一個值。 – notsodev

回答

5

債券是正確的,我不能傳遞託管和非託管代碼之間的對象,並仍然保留其存儲的信息。我最終只是調用C++函數來創建一個對象,並將指針傳遞迴C#的IntPtr類型。然後,我可以將指針傳遞給C#所需的任何C++函數(提供它是extern)。這並不是我們想要做的事情,但它將在我們需要的範圍內達到其目的。

這裏是我用於例子/參考的包裝器。 (注意:我在上面的例子中使用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-a-C-Class

希望這可以幫助一些人節省比我試圖弄明白更多的時間!

0

如果使用類剛剛從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); 
    } 
} 
+0

這很有趣,因爲我有一種感覺,這是與兩者之間的內存訪問有關的,我沒有跑過GCHandle。但是使用這個我需要使用一個IntPtr類型來代替傳入我的CSObject。有沒有辦法將CSObject類轉換爲IntPtr?否則,我無法傳遞課堂內容。謝謝! – notsodev

+0

您只需將CClass替換爲CClass *或void *,並且可以將它用作C#端的IntPtr。你有沒有在C++端改變它的值? C#類的CClass也會自動作爲CClass *編組。你嘗試將你的類聲明爲struct,並使用 – 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); 

inbound方法也應該帶一個指向CClass的指針,C#原型是好的。

+0

當試圖編譯DLL時,出現「指向非法的指針」錯誤。同時將C++函數定義爲引用給公共類提供了「類中的connot訪問私有成員」錯誤。如果我將返回函數定義爲指向該類的指針,它會更改C#應用程序中的輸出,但是我提到的傳遞到DL的類垃圾郵件更改爲0值。這可能與另一個問題有關嗎? – notsodev

+0

我的錯誤,它必須是對指針或指向指針的引用 - 我修正了我的示例代碼。您需要在getDLLObj中使用對指針的引用,並在SetDLLObj中使用指針,並通過在參數上添加ref關鍵字來修復getDLLObj的C#原型。 – Bond

+0

謝謝邦德,你的建議幫助刪除存儲在DLL中的垃圾值。然而,我仍然有一個0值被傳入的問題,應該是1234.相信在@ user629926提出的無人值守和受管理的內存之間仍然存在問題。我感謝你的幫助,任何其他想法都會很棒! – notsodev