2011-02-13 96 views
7

我使用C++/CLI封裝了一個C函數庫。 C庫設計用於從非託管C++類中使用。這意味着庫函數接受一個C++對象指針,然後在回調中提供該指針。這使回調代碼可以將請求重定向到調用C++對象中的適當事件函數。c庫的C++/CLI類封裝器 - 回調函數

的實際功能是相當複雜的,所以我簡化了問題空間只有幾個基本項目:

// C library function signature 
void CLibFunc(CLIBCALLBACK *callback, void *caller); 

// C callback signature 
// Second parameter is meant to point to the calling C++ object 
typedef int (__stdcall CLIBCALLBACK) (int param1, void *caller); 

// C callback implementation 
int CallBackImpl(int param1, void* caller) 
{ 
    // Need to call the ManagedCaller's EventFunction from here 
    // ??? 
} 

// C++/CLI caller class 
public ref class ManagedCaller 
{ 
    public: 
     void CallerFunction(void) 
     { 
      // Call the C library function 
      // Need to pass some kind of this class pointer that refers to this object 
      CLibFunc(CallBackImpl, ????); 
     } 

     void EventFunction(param1) 
     { 
     } 
} 

現在的C庫函數需要從一個託管C++類調用。在C++/CLI下,垃圾收集器在內存中移動對象,因此傳遞一個簡單的固定指針到類不再工作。我可以通過固定對象來解決問題,但不建議這樣做,因爲這會導致內存碎片。看起來,另一種選擇是使用auto_gcroot指針,但我對託管C++來說是相當新的,我不知道如何使其工作。

有誰知道如何使這項工作?什麼樣的指針應該傳遞給C函數?回調實現應該如何重定向到調用對象的事件函數?

回答

3

這恰好類似於我現在正在處理中的事情。

這裏是用C++類提供本地回調的博客文章:http://blogs.microsoft.co.il/blogs/alon/archive/2007/05/29/Native-Callback.aspx

我不熟悉的C調用C++成員函數,但我做了一個接口(抽象基)類到另一個C++類回調(類似於文章)。以下是我提供一個橋樑的一個基本的例子:

// Interface (abstract base) class providing the callback 
class IProvider { 
public: 
    virtual ~IProvider() {} 
    virtual void Callback() = 0; 
}; 

// User class of the callback 
class CUser { 
    IProvider * m_pProvider; 
public: 
    CUser(IProvider * pProvider) { 
     m_pProvider = pProvider; 
    } 
    void DoSomething() { 
     m_pProvider->Callback(); 
    } 
}; 

// Implementation of the interface class 
class CHelloWorldProvider : public IProvider { 
    void Callback() { 
     printf("Hello World!"); 
    } 
}; 

// Usage of the callback provider in a pure native setting 
void PureNativeUsage() { 
    CHelloWorldProvider oProvider; 
    CUser oUser(&oProvider); 
    oUser.DoSomething(); 
} 

現在爲了使這個可用於供應商的管理的實現,我們必須創造了一系列提供的橋樑課程。

// Where gcroot is defined 
#include <vcclr.h> 

// Managed provider interface class 
public interface class IManagedProvider { 
    void Callback(); 
}; 

// Native bridge class that can be passed to the user 
class CProviderBridge : public IProvider { 
    // Give the managed class full access 
    friend ref class ManagedProviderBase; 

    // Store a reference to the managed object for callback redirects 
    gcroot<IManagedProvider ^> m_rManaged; 

public: 
    void Callback(){ 
     m_rManaged->Callback(); 
    } 
}; 

// Managed provider base class, this provides a managed base class for extending 
public ref class ManagedProviderBase abstract : public IManagedProvider { 
    // Pointer to the native bridge object 
    CProviderBridge * m_pNative; 

protected: 
    ManagedProviderBase() { 
     // Create the native bridge object and set the managed reference 
     m_pNative = new CProviderBridge(); 
     m_pNative->m_rManaged = this; 
    } 

public: 
    ~ManagedProviderBase() { 
     delete m_pNative; 
    } 

    // Returns a pointer to the native provider object 
    IProvider * GetProvider() { 
     return m_pNative; 
    } 

    // Makes the deriving class implement the function 
    virtual void Callback() = 0; 
}; 

// Pure managed provider implementation (this could also be declared in another library and/or in C#/VB.net) 
public ref class ManagedHelloWorldProvider : public ManagedProviderBase { 
public: 
    virtual void Callback() override { 
     Console::Write("Hello World"); 
    } 
}; 

// Usage of the managed provider from the native user 
void MixedUsage() { 
    ManagedHelloWorldProvider^rManagedProvider = gcnew ManagedHelloWorldProvider; 
    CUser oUser(rManagedProvider->GetProvider()); 
    oUser.DoSomething(); 
} 

編輯:添加代碼來顯示W/O的管理接口類的例子,我使用。

這裏是我的例子的一個修改版本,可以使用上面給出的CLibFunc。這是假設C函數如何執行回調是準確的。

此外,這可能會減少一點,這取決於您的回調類是如何參與以及您需要多少擴展的自由度。

// Where gcroot is defined 
#include <vcclr.h> 

// C callback signature 
// Second parameter is meant to point to the calling C++ object 
typedef int (__stdcall CLIBCALLBACK) (int param1, void *caller); 

// C library function 
void CLibFunc(CLIBCALLBACK *callback, void *caller) { 
    // Do some work 
    (*callback)(1234, caller); 
    // Do more work 
} 

// Managed caller interface class 
public interface class IManagedCaller { 
    void EventFunction(int param1); 
}; 

// C++ native bridge struct 
struct CCallerBridge { 
    // Give the managed class full access 
    friend ref class ManagedCaller; 

    // Store a reference to the managed object for callback redirects 
    gcroot<IManagedCaller ^> m_rManaged; 

public: 
    // Cast the caller to the native bridge and call managed event function 
    // Note: This must be __stdcall to prevent function call stack corruption 
    static int __stdcall CallBackImpl(int param1, void * caller) { 
     CCallerBridge * pCaller = (CCallerBridge *) caller; 
     pCaller->m_rManaged->EventFunction(param1); 
     return 0; 
    } 
}; 

// C++/CLI caller class 
public ref class ManagedCaller : public IManagedCaller { 
    // Pointer to the native bridge object 
    CCallerBridge * m_pNative; 

public: 
    ManagedCaller() { 
     // Create the native bridge object and set the managed reference 
     m_pNative = new CCallerBridge(); 
     m_pNative->m_rManaged = this; 
    } 
    ~ManagedCaller() { 
     delete m_pNative; 
    } 

    // Calls the C library function 
    void CallerFunction() { 
     CLibFunc(CCallerBridge::CallBackImpl, m_pNative); 
    } 

    // Managed callback function 
    virtual void EventFunction(int param1) { 
     Console::WriteLine(param1); 
    } 
}; 

// Usage 
int main(array<System::String ^> ^args) { 
    ManagedCaller^oCaller = gcnew ManagedCaller(); 
    oCaller->CallerFunction(); 
    return 0; 
} 
+0

這只是與我的問題相切。這顯示瞭如何在託管C++類中包裝非託管C++類並使用回調。我遇到過很多這方面的例子,包括http://tweakbits.com/UnmanagedToManagedCallback.cpp等示例。我正在尋找的是如何在C++/CLI類中包裝C庫並使用回調的示例。在這一天結束時,我面臨的問題與這些樣本中解決的問題有所不同。 – Theo 2011-02-13 18:26:27