执行存储的回调时出现 System.AccessViolationException 错误 [英] System.AccessViolationException error when stored callback is executed

查看:33
本文介绍了执行存储的回调时出现 System.AccessViolationException 错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已通过 C++/CLI 包装器将 C++ 成员函数 作为回调传递给 C# 项目(这很好用).C# 项目将在从另一个 .exe 进程接收数据时调用此委托:将引发一个事件并且一个方法将调用此回调.因此,我需要使用已经创建的 C# 类的静态实例来保存"这个 Action 委托.我得到以下代码:

//C++ 非托管函数WRAPPER_API void dispatchEvent(std::function processEvent){Iface::Wrapper 包装器;wrapper.callback = &processEvent;wrapper.PassCallback();}//C++ 托管公共引用类包装器{民众:std::function* 回调;void ReturnToCallback(int 数据){(*回调)(数据);}void PassCallback(){StartGenerator^ startGen = gcnew StartGenerator(gcnew Action(this, &Wrapper::ReturnToCallback));}};//C#公共类 StartGenerator{私人通讯;public StartGenerator(Action回调){comm = Communication.Instance;comm.callback = 回调;}}

如果我在 StartGenerator 方法中调用 Action 委托,C++ 函数就会正确执行.但是,我的目标是保存委托,以便在从另一个 .exe 进程接收数据时能够调用它.当此数据到达时,将引发一个事件并从事件方法中调用回调.正是在这一点上,我收到以下异常:

<块引用>

未处理的异常:System.AccessViolationException:试图读或写受保护的内存.这通常表明其他内存损坏.在Iface.Wrapper.ReturnToCallback(Int32 数据)

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

解决方案

我想我需要管理std::function

的生命周期

是的,当我告诉您将指针存储在托管包装器中时,我也告诉您了,此处<块引用>

我不知道托管类指向的函数对象的生命周期.

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

<块引用>

该对象似乎已被删除,托管类保留了一个悬空指针.

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

void dispatchEvent(std::function processEvent){Iface::Wrapper 包装器;wrapper.callback = &processEvent;wrapper.PassCallback();}

processEvent 是函数参数,一个通过值传递的 std::function 对象.创建并存储在参数中的副本一直存在到作用域结束.它具有自动存储期限.当函数返回时,所有局部变量,包括函数参数,都会被销毁(不是删除").

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

typedef std::functioncbfn;wrapper.callback = new cbfn(processEvent);

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

~Wrapper(){删除回调;回调 = nullptr;}

I have passed as callback a C++ member function to a C# project through a C++/CLI wrapper (this works fine). The C# project is going to call this delegate when receiving data from another .exe process: an event will be raised and a method will call this callback. So, I needed to "save" this Action delegate using an static instance of a C# class already created. I got the following code:

// 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;
    }
}

If I call the Action delegate in StartGenerator method, the C++ function is properly executed. However, my goal was saving the delegate to be able to call it afterwards, when data is received from another .exe process. When this data arrives, an event is raised and callback is called from the event method. It is at this point when I get the following exception:

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)

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. The object seems to be deleted and the managed class is left holding a dangling pointer.

解决方案

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

Yes, I told you as much when I told you to store a pointer in the managed wrapper, here

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

The std::function is a native object and follows the native C++ rules. Putting a pointer in a managed wrapper won't make it garbage-collected.

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

Yes, your terminology isn't exact but you've correctly diagnosed the problem. Take a look:

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

processEvent is function argument, a std::function object passed by value. The copy made and stored in the argument lives until the end of scope. It has automatic storage duration. When the function returns, all the local variables, function arguments included, are destroyed (not "deleted").

You will need to dynamically allocate (a copy of) the std::function object, like:

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

Now you have a memory leak, but at least you aren't using the object after it's destroyed. If you only make a handful of these objects the leak might even be acceptable. In general, you should implement IDisposable on your wrapper and have the Dispose method do delete callback;. In C++/CLI you use destructor syntax to accomplish that.

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

这篇关于执行存储的回调时出现 System.AccessViolationException 错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆