从C ++库调用C ++ / CLI函数

时间:2010-01-21 10:51:12

标签: c# c++-cli

我需要将本机C ++库集成到C#项目中。现在在这个C ++库中,我需要在C ++ / CLI中继承具有虚函数的类。 所以在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 个答案:

答案 0 :(得分:2)

确保代表在仍然需要时不进行垃圾回收。 在类C ++ CliClass中,您可以添加CallbackDelegate类型的成员并将其设置为d。 如果在执行SomeFunction期间只存在C ++ CliClass的实例..函数末尾的GC.KeepAlive(d)可能就足够了。

或许更简单:在C ++中,CliClass定义了类型为gcroot&lt; GCClass ^&gt;的memeber。然后在VirtualFunction中直接调用此memeber上的GetData函数,而无需委托。

答案 1 :(得分: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();