我需要将本机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的调用约定。我尝试了所有选项但没有结果:我总是在异常中结束或者应用程序永远挂起。有人能给我一些可能出错的提示吗?
答案 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();