2016-06-10 87 views
4

回調函數在我的本地代碼,我有以下幾點:編組C/C++函數參數到C#

我需要從C#代碼調用

SetProgressHandlerCallBack (HandlerCallBack CallBack) 

,所以我做的以下:

[UnmanagedFunctionPointer(CallingConvention.StdCall)] 
public delegate void HandlerCallBack([MarshalAs(UnmanagedType.R4)]float percent); 

[DllImport("my.dll")] 
public static extern void SetProgressHandlerCallBack ([MarshalAs(UnmanagedType.FunctionPtr)] 
                 HandlerCallBack callBack); 

public void AssimpCallBack ([MarshalAs(UnmanagedType.R4)]float percent) { 
    Debug.Log (percent); 
} 



void SomeFunction() { 
    HandlerCallBack cb = new HandlerCallBack (AssimpCallBack); 
    SetProgressHandlerCallBack (cb); 
} 

,但我發現了異常,

SetProgressHandlerCallBack

在(包裝託管到本機) ModellInstantiator:SetProgressHandlerCallBack (ModellInstantiator/HandlerCallBack)

,我無法弄清楚我失去了什麼。

+1

什麼異常,你所看到的? –

+0

你認爲C++ \ CLI包裝是否可行?這是一種非常乾淨自然的方式來規避所有聲明式編組的東西。 –

+0

_SetProgressHandlerCallBack 在(包裝託管到本機)ModellInstantiator:SetProgressHandlerCallBack(ModellInstantiator/HandlerCallBack)_ 這是我所看到的。 – gev29

回答

2

在你的代碼,你上面顯示SetProgressHandlerCallback功能如下:

void Importer::SetProgressHandlerCallBack(...) { ... } 

有2個問題,這一點,這兩者都可以用一個簡單的最小完全可驗證的例子來說明,我創建一個新的Visual Studio解決方案,包含一個C#(WinForms)項目和一個C++ Dll(非託管而不是C++/CLI)。

在DLL(dllmain.cpp)我把下面的代碼:

typedef void (__stdcall *HandlerCallBack)(float); 

extern "C" { 

    // extern "C" ignored for C++ classes 
    class __declspec(dllexport) TestClass 
    { 
    public: 
     static void CallMeBack(HandlerCallBack callback) 
     { 
      callback(1.0f); 
     } 
    }; 


    void __declspec(dllexport) CallRightBack(HandlerCallBack callback) 
    { 
     TestClass::CallMeBack(callback); 
    } 
} 

你可以看到回調簽名匹配您正在使用的一個,和靜態類方法和導出函數。建立這個,啓動Visual Studio命令提示符並運行

dumpbin /exports <dllname>.dll 

這使您從DLL,它看起來像這樣導出的函數:

(Trimmed to just show what we care about) 
    ordinal hint RVA  name 

      1 0 00011082 [email protected]@[email protected][email protected]@Z = @ILT+125([email protected]@[email protected][email protected]@Z) 
      2 1 000110E1 [email protected]@[email protected]@@Z = @ILT+220([email protected]@[email protected]@@Z) 
      3 2 00011069 [email protected]@@[email protected]@Z = @ILT+100([email protected]@@[email protected]@Z) 
      4 3 00011014 CallRightBack = @ILT+15(CallRightBack) 

你會發現,經常全局函數與其未裝飾名稱一起導出,但靜態方法導出,裝飾(已損壞)名稱爲

現在讓我們向C#項目添加一些代碼,看看爲什麼這會成爲問題。添加2個按鈕(按鈕1和按鈕2 )。到形式,我添加以下代碼:

[UnmanagedFunctionPointer(CallingConvention.StdCall)] 
delegate void HandlerCallbackDelegate(float value); 

[DllImport("CppDllTestProj.dll")] 
static extern void CallRightBack([MarshalAs(UnmanagedType.FunctionPtr)]HandlerCallbackDelegate callback); 

[DllImport("CppDllTestProj.dll")] 
static extern void CallMeBack([MarshalAs(UnmanagedType.FunctionPtr)]HandlerCallbackDelegate callback); 

HandlerCallbackDelegate callbackDel; 

// in the designer associated with button1 
private void button1_Click(object sender, EventArgs e) 
{ 
    if (this.callbackDel == null) 
    { 
     this.callbackDel = new HandlerCallbackDelegate(this.Callback); 
    } 
    CallRightBack(callbackDel); 
} 

// in the designer associated with button2 
private void button2_Click(object sender, EventArgs e) 
{ 
    if (this.callbackDel == null) 
    { 
     this.callbackDel = new HandlerCallbackDelegate(this.Callback); 
    } 
    CallMeBack(callbackDel); 
} 

void Callback(float value) 
{ 
    MessageBox.Show(value.ToString()); 
} 

編譯,將dll文件拷貝到C#項目的構建目錄,並運行。單擊button1,調用回調,並顯示消息框。單擊按鈕2和程序崩潰,但例外:

無法在DLL'CppDllTestProj.dll'中找到名爲'CallMeBack'的入口點。 at ScratchApp.Form1.CallMeBack(HandlerCallbackDelegate callback) at Form1中的ScratchApp.Form1.button2_Click(Object sender,EventArgs e)。cs:第49行

無法找到名爲「CallMeBack」的入口點,表示它無法在該DLL中找到具有該名稱的函數。如果我現在在C#測試應用程序改變CallMeBack定義:

[DllImport("CppDllTestProj.dll", EntryPoint="[email protected]@@[email protected]@Z")] 
static extern void CallMeBack([MarshalAs(UnmanagedType.FunctionPtr)]HandlerCallbackDelegate callback); 

重新編譯,並重新啓動,現在兩個按鈕正常工作。 (您錯位/裝飾名稱可能有所不同。用你的dumpbin得到任何價值。)

這個問題的根源在於extern "c"存儲類說明是爲C++類和類成員忽略。

,你會盡快解決你上面有另一個問題,是這樣的代碼:

void SomeFunction() { 
    HandlerCallBack cb = new HandlerCallBack (AssimpCallBack); 
    SetProgressHandlerCallBack (cb); 
} 

你會注意到,cb垃圾收集可用從呼叫控制返回瞬間SetProgressHandlerCallBack。一旦該代表被垃圾收集,任何回調都將失敗。壯觀。


技術上同樣的項目,我使用所有的測試,但每次我清除代碼。
創意的名字,我知道。
來源:Is it possible to export a C++ member method with C linkage in a DLL?