问题
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.
回答1:
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;
}
来源:https://stackoverflow.com/questions/61170340/system-accessviolationexception-error-when-stored-callback-is-executed