有一些大型类是用 C++ 实现的。我需要在 C# 项目中使用它。为了节省时间并且不重写代码,我选择了用 C++/CLI 编写此类的包装器,并将其全部放入类库中。但是,我在使用 1 种方法时遇到了困难,该方法将回调函数的指针作为参数。
情况如下:本机类有一个方法,该方法接受一个指向某个函数的指针,该函数在某些事件发生时被调用。任务是在包装类中提供类似的方法,但将分别传递委托而不是指针。
我简化了代码来演示该问题:
class CFoo
{
public:
bool FooMember;
CFoo()
{
FooMember = true;
}
void Call(void(*callback)(CFoo))
{
CFoo foo = CFoo();
std::thread thr([=]() { callback(foo); });
thr.join();
}
};
ref class FooWrapper
{
public:
delegate void ManagedCallback(FooWrapper^ foo);
private:
delegate void UnmanagedCallback(CFoo foo);
ManagedCallback^ Callback;
void FooProc(CFoo foo)
{
std::cout << "FooProc: " << (foo.FooMember ? "true" : "false") << std::endl;
Callback(gcnew FooWrapper(&foo));
}
public:
CFoo* foo;
FooWrapper()
{
foo = new CFoo();
}
FooWrapper(CFoo* foo)
{
this->foo = foo;
}
void Call(ManagedCallback^ callback)
{
Callback = callback;
UnmanagedCallback^ unmanagedCallback = gcnew UnmanagedCallback(this, &FooWrapper::FooProc);
IntPtr^ methodPtr = Marshal::GetFunctionPointerForDelegate(unmanagedCallback);
foo->Call(static_cast<void(*)(CFoo)>(methodPtr->ToPointer()));
}
};
ref class Program
{
public:
FooWrapper^ Foo;
void Callback(FooWrapper^ foo)
{
Foo = foo;
std::cout << "Callback: " << (foo->foo->FooMember ? "true" : "false") << std::endl;
}
void Start()
{
FooWrapper^ w = gcnew FooWrapper();
w->Call(gcnew FooWrapper::ManagedCallback(this, &Program::Callback));
}
};
int main(array<System::String ^> ^args)
{
Program^ c = gcnew Program();
c->Start();
CFoo foo = *c->Foo->foo; // <- AccessViolationException
return 0;
}
控制台输出:
FooProc:正确
回调:true
一旦CFoo::Call
方法中创建的线程完成执行,类成员FooWrapper::foo
就会被销毁。我尝试查看对析构函数的调用FooWrapper
并CFoo
确保该对象没有被破坏。您还可以在尝试取消引用之前查看指针本身的数值,并确保指针有效。
例外:
System.AccessViolationException:“尝试读取或写入受保护的内存。这通常表明其他内存已损坏。
也许有一种更好的方法来实现包装器,或者您可以以某种方式修复现有的方法。
您在 FooProc 方法中保存一个指向在堆栈上创建的 CFoo 对象的指针,并在它被销毁后尝试访问它。
一个不太明显的错误是,通过创建托管委托,您可以获得对其的非托管引用,而无需将委托固定在内存中。