2011-02-13 15 views
7

Estoy envolviendo una biblioteca C utilizando C++/CLI. La biblioteca C se diseñó para ser utilizada desde una clase C++ no administrada. Esto significa que las funciones de la biblioteca aceptan un puntero de objeto C++ y luego devuelven ese puntero a las devoluciones de llamada. Esto permite que el código de devolución de llamada redirija las solicitudes a una función de evento apropiada en el objeto C++ llamante.Contenedor de clase C++/CLI para biblioteca c - devoluciones de llamada

Las funciones reales son un poco complicado, así que han simplificado el espacio del problema a sólo unos pocos elementos básicos:

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

Ahora las funciones de biblioteca C necesita ser llamado desde una clase administrada C++. En C++/CLI, el recolector de basura mueve objetos en la memoria, por lo que ya no funciona pasar un puntero fijo a la clase. Puedo resolver el problema fijando el objeto, pero no se recomienda porque conduce a la fragmentación de la memoria. Parece que otra opción sería usar punteros auto_gcroot, pero soy bastante nuevo en C++ administrado y no estoy seguro de cómo hacer que esto funcione.

¿Alguien sabe cómo hacer esto? ¿Qué tipo de puntero debería pasar a la función C? ¿Cómo debería la implementación de devolución de llamada redirigir a la función de evento del objeto llamante?

Respuesta

3

Esto pasa a ser similar a algo en lo que estoy trabajando ahora mismo.

Aquí es una entrada de blog en la prestación de servicios repetidos nativas usando C++ clases: http://blogs.microsoft.co.il/blogs/alon/archive/2007/05/29/Native-Callback.aspx

No estoy familiarizado con llamar a C funciones miembro ++ de C, pero he hecho una interfaz (base abstracta) clase a otra clase de C++ para devoluciones de llamada (similar al artículo). Aquí está un ejemplo básico de lo que estoy proporcionando un puente para:

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

Ahora con el fin de hacer que este disponible para implementaciones administradas del proveedor, tenemos que crear una serie de clases que proporcionan el puente.

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

Editar: Añadido código para mostrar w/o el ejemplo de la clase interfaz administrada utilizo.

Aquí hay una versión modificada de mi ejemplo que se puede usar dada su CLibFunc anterior. Esto supone que la forma en que la función C realiza la devolución de llamada es precisa.

También esto podría reducirse un poco dependiendo de cuán involucradas estén sus clases de devolución de llamada y cuánta libertad de extensión necesita.

// 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

Esto solo es tangencialmente relevante para mi pregunta. Esto muestra cómo ajustar una clase de C++ no administrada dentro de una clase de C++ administrada y usar devoluciones de llamada. He encontrado muchos ejemplos de esto, incluidas muestras como http://tweakbits.com/UnmanagedToManagedCallback.cpp. Lo que estoy buscando es un ejemplo de cómo envolver una biblioteca C dentro de una clase C++/CLI y utilizar devoluciones de llamada. Al final del día, los problemas que estoy enfrentando son algo diferentes a los problemas resueltos en estas muestras. – Theo

+0

Sí. Este ejemplo directo es tangencialmente relevante como es, pero se entendía como un puntero de dirección. Como no he hecho exactamente lo que me está preguntando, asumo que hacerlo es lo mismo que mi ejemplo. Aún crearías una clase bridge nativa y la incluirías en la clase administrada; luego, en lugar de pasar la clase al por mayor nativa (como en mi 'IProvider') podrías pasar una función estática de miembro que hace la llamada del objeto administrado. – CuppM

+0

Agregué una versión de mi ejemplo usando su función C. – CuppM

Cuestiones relacionadas