2010-01-21 87 views
2

我需要將本機C++庫集成到C#項目中。現在在這個C++庫中,有一些類需要在C++/CLI中繼承的虛函數。 所以在C++/CLI我寫成才喜歡從C++庫調用C++/CLI函數

class C++CliClass : public C++Class 
{ 
    C++CliClass(Callback callback) { iCallback = callback; } 
    virtual VirualFunctionCallFromC++(int x, int y, int z, SomeClass *p) 
    { 
    // I need to call C++/CLI here 
    iCallback(x, y, z, p); 
    } 

private: 
    Callback iCallback; 
} 

I defined the callback function as: 
typedef int (__cdecl *Callback)(int x, int y, int z, SomeClass *p); 

The idea is now that C++ library calls the virtual function of the C++Cli 
    class which on his turn calls the call back which gets me hopefully into C#. 

// This delegate definition is needed to setup the callback for the C++ class 
delegate int CallbackDelegate(int x, int y, int z, SomeClass *p); 

So now I defined a managed C++/CLI class 
public ref class GCClass 
{ 
    public: 
    delegate <Byte>^ GetDataDelegate(); 
    GCClass(GetData^ getDataDelegate) { iGetDataDelegate = getDataDelegate }; 

    private: 
    GetDataDelegate ^iGetDataDelegate; 
    int GetData(int x, int y, int z, SomeClass *p) 
    { 
     // call delegate into C# 
     <Byte>^ data = iGetDataDelegate->Invoke(); 
    } 
    public: 
    void SomeFunctionWhichEventuallyCallsC++Libary 
    { 
     // create a delegate for the method that will call the C# delegate 
     CallbackDelegate ^d = gcnew CallbackDelegate(this, &GCClass::GetData); 
     IntPtr del = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(d); 

     // Install in the C++ class 
     C++CliClass(del.ToPointer()); 

     // Setup the C++ library and install my C++ class into the library 
     SomeObjectOfTheLibrary->Install(&C++CliClass); 
     SomeObjectOfTheLibrary->DoSometing() // will call the C++ virtual function and end up in C# 

     // The delegate is no longer needed anymore 
    } 

直到這裏的代碼。所以我希望能夠實現的是有人可以調用我的託管C++/CLI類的方法,該類使用本機C++庫來完成他的工作。 C++庫調用C++/CLI回調,最後調用C#委託。現在最後的問題是:在調試模式下一切都很順利。然而,在發佈模式下,有時會引發AccesException,或者有時應用程序只是掛起。我懷疑它與C++/CLI和C++的不同調用約定有關。例如,我觀察到第二次調用回調時,iCallback的值與第一次調用時不同。然而,對於所有下一次調用,iCallback的值不再改變。我期望iCallback的值應該始終相同,但我不確定,因爲我不知道框架內部如何工作以便能夠從C++調用委託。我還嘗試用[UnmanagedFunctionPointer(Cdecl)]定義CallbackDelegate的調用約定。我嘗試了所有選項,但沒有結果:我總是以異常結束,或者應用程序永遠掛起。有人能給我一些什麼可能是錯誤的暗示嗎?

回答

2

確保代理在仍然需要時不被垃圾收集。 在類C++ CliClass中,您可以添加CallbackDelegate類型的成員並將其設置爲d。 如果C++ CliClass的實例僅在執行SomeFunction期間存在.GC.KeepAlive(d)在函數的末尾可能就足夠了。

也許更簡單:在C++ CliClass定義類型gcroot < GCClass^>然後直接調用這個memeber在虛函數中的GetData功能,而不需要針對受委託的memeber。

2

上述代碼的問題之一是d是託管引用,這意味着它可以在運行時移動。這反過來將使您的回調指針失效。

爲了委託傳遞給本地代碼,你需要告訴垃圾收集不要動它,使用GCHandle::Alloc

CallbackDelegate^ d = gcnew CallbackDelegate(this, &GCClass::GetData); 

    // As long as this handle is alive, the GC will not move or collect the delegate 
    // This is important, because moving or collecting invalidate the pointer 
    // that is passed to the native function below 
    GCHandle delegate_handle = GCHandle::Alloc(d); 

    IntPtr del = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(d); 

    C++CliClass native(del.ToPointer()); 

    SomeObjectOfTheLibrary->Install(&native); 
    SomeObjectOfTheLibrary->DoSometing() // will call the C++ virtual function and end up in C# 

    // Release the handle 
    delegate_handle.Free(); 
+0

Henrik的回答是好多了,委託對象並不需要是固定。此外,你可能*真的*不想使用Invoke()方法,它啓動一個新的線程。 –

+0

我試過Bojan的建議,它的工作。 Invoke在C++/CLI中調用C#委託。我可以採取另一種方法來做到這一點嗎? – kvd

+0

@nobugz'Invoke'不會啓動一個新的線程。在委託上調用'Invoke'與執行'del()'完全相同。 –