C ++ / CLI class wrapper for c library - callbacks

I am wrapping a C library using C ++ / CLI. The C library was designed to be used from an unmanaged C ++ class. This means that library functions take a pointer to a C ++ object, and then provide that pointer back in callbacks. This allows the callback code to redirect requests to the appropriate event function in the C ++ calling object.

Actual functions are pretty active, so I simplified the problem space with just a few basic elements:

// 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 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) { } } 

Now the C library functions must be called from the C ++ managed class. In C ++ / CLI, the garbage collector moves objects in memory, so passing a simple fixed pointer to a class no longer works. I can solve the problem by binding an object, but this is not recommended because it leads to memory fragmentation. It seems like another option would be to use auto_gcroot pointers, but I'm pretty new to C ++ management, and I'm not sure how to do this.

Does anyone know how to make this work? Which pointer should be passed to the C function? How is the callback implementation redirected to the caller's event function?

+7
source share
1 answer

It just looks like what I'm working on right now.

Here's a blog post about providing your own callbacks using C ++ classes: http://blogs.microsoft.co.il/blogs/alon/archive/2007/05/29/Native-Callback.aspx

I am not familiar with calls to C ++ member functions from C, but I made an interface class (abstract base) for another C ++ class for callbacks (similar to the article). Here is a basic example of what I provide for the bridge:

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

Now, in order to make this available for managed provider implementations, we need to create a series of classes that provide a bridge.

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

Edit: Added code to display without using the managed interface class class that I am using.

Here is a modified version of my example that can be used to fit your CLibFunc above. This assumes that the C function is making a callback.

It can also be slightly reduced depending on how your callback classes are involved and how much freedom you need to expand.

 // 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; } 
+3
source

All Articles