2011-02-13 2 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(); 
} 


편집 : 관리 인터페이스 클래스의 예 O를 내가 사용/w를 보여 추가 된 코드입니다.

여기는 귀하의 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

+0

예. 이 직접적인 예는 접선으로는 의미가 있지만 방향 지시자로 사용되었습니다. 저는 여러분이 묻고있는 것을 정확하게하지 않았기 때문에 그것을하는 법을 제 예제와 같은 줄로 생각합니다. 네이티브 브릿지 클래스를 생성하여 관리되는 클래스에 랩핑하면 네이티브 클래스를 전달하는 대신 ('IProvider'처럼) 관리 객체를 호출하는 정적 멤버 함수를 전달할 수 있습니다. – CuppM

+0

C 함수를 사용하여 예제를 추가했습니다. – CuppM