我已经通过C ++ / CLI包装器将C ++成员函数作为回调传递给C#项目(这很好)。当从另一个.exe进程接收数据时,C#项目将调用此委托:将引发一个事件,一个方法将调用此回调。因此,我需要使用已经创建的C#类的静态实例“保存”此Action委托。我得到以下代码:
// C++ unmanaged function
WRAPPER_API void dispatchEvent(std::function<void(int)> processEvent)
{
Iface::Wrapper wrapper;
wrapper.callback = &processEvent;
wrapper.PassCallback();
}
//C++ Managed
public ref class Wrapper
{
public:
std::function<void(int)>* callback;
void ReturnToCallback(int data)
{
(*callback)(data);
}
void PassCallback()
{
StartGenerator^ startGen = gcnew StartGenerator(gcnew Action<int>(this, &Wrapper::ReturnToCallback));
}
};
// C#
public class StartGenerator
{
private Communication comm;
public StartGenerator(Action<int> callback)
{
comm = Communication.Instance;
comm.callback = callback;
}
}
如果我在StartGenerator方法中调用Action委托,则C ++函数将正确执行。但是,我的目标是保存委托以便以后从另一个.exe进程接收到数据时可以调用它。当此数据到达时,事件被引发,并从事件方法中调用回调。此时,我收到以下异常:
未处理的异常:System.AccessViolationException:尝试执行以下操作:读取或写入受保护的内存。这通常表明其他内存已损坏。在Iface.Wrapper.ReturnToCallback(Int32数据)
我认为我需要管理std :: function的生存期,我不知道托管类所指向的函数对象的生存期。该对象似乎已删除,托管类保留了悬空的指针。
我认为我需要管理
std::function
的生命周期>是的,当我告诉您将指针存储在托管包装中时,我也告诉过您很多,here
我不知道托管类所指向的功能对象的寿命。
std::function
是本机对象,并遵循本机C ++规则。将指针放在托管包装器中不会使其被垃圾回收。
该对象似乎已删除,托管类保持悬空指针。
是的,您的术语不正确,但是您已经正确诊断了问题。看看:
void dispatchEvent(std::function<void(int)> processEvent) { Iface::Wrapper wrapper; wrapper.callback = &processEvent; wrapper.PassCallback(); }
processEvent
是函数参数,是通过值传递的std::function
对象。在参数中创建并存储的副本将一直保留到作用域结尾。它具有自动存储期限。当函数返回时,所有局部变量(包括函数参数)均被销毁(不是“删除”)。
您将需要动态分配std::function
对象(的副本),例如:
typedef std::function<void(int)> cbfn; wrapper.callback = new cbfn(processEvent);
现在您发生了内存泄漏,但是至少在对象被销毁后,您没有使用它。如果仅使这些对象很少,则泄漏甚至可以接受。通常,应该在包装器上实现
IDisposable
并让Dispose
方法执行delete callback;
。在C ++ / CLI中,您可以使用析构函数语法来完成此任务。
~Wrapper()
{
delete callback;
callback = nullptr;
}