System.AccessViolationException 执行存储的回调时出错

System.AccessViolationException error when stored callback is executed

我已经通过 C++/CLI 包装器将 C++ 成员函数 作为回调传递给 C# 项目(这很好用)。当从另一个 .exe 进程接收数据时,C# 项目将调用此委托:将引发一个事件,一个方法将调用此回调。因此,我需要使用已创建的 C# class 静态实例 "save" 此 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 进程接收到数据时能够在之后调用它。当此数据到达时,将引发 事件并从事件方法 调用回调。正是在这一点上,我得到以下异常:

Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt. at Iface.Wrapper.ReturnToCallback(Int32 data)

我想我需要管理 std::function 的生命周期,我不知道托管 class 指向的函数对象的生命周期。该对象似乎已被删除,托管 class 留下一个悬挂指针。

I think I need to manage the lifetime of the std::function

是的,当我告诉您在托管包装器中存储一个指针时,我也告诉过您很多,

I don't know about the lifetime of the function object being pointed to by the managed class.

std::function 是本机对象并遵循本机 C++ 规则。将指针放在托管包装器中不会使其成为垃圾收集器。

The object seems to be deleted and the managed class is left holding a dangling pointer.

是的,您的术语不准确,但您已正确诊断出问题。看看:

void dispatchEvent(std::function<void(int)> processEvent)
{
    Iface::Wrapper wrapper;
    wrapper.callback = &processEvent;
    wrapper.PassCallback();
}

processEvent是函数参数,一个std::function对象按值传递。创建并存储在参数中的副本一直存在到范围结束。它具有自动存储期限。当函数returns时,所有局部变量,包括函数参数,都被销毁(不是"deleted")。

您需要动态分配 std::function 对象(的副本),例如:

typedef std::function<void(int)> cbfn;
wrapper.callback = new cbfn(processEvent);

现在你有内存泄漏,但至少在它被销毁后你没有使用该对象。如果您只制作少数这些物体,泄漏甚至可能是可以接受的。通常,您应该在包装器上实现 IDisposable 并让 Dispose 方法执行 delete callback;。在 C++/CLI 中,您使用析构函数语法来完成此操作。

~Wrapper()
{
    delete callback;
    callback = nullptr;
}