cks2k2
cks2k2

Reputation: 231

Passing managed function to unmanaged function that uses std::function()

in my C++ code, a callback function is represented as a std::function() obj, not as the more common function pointer construct.

typedef std::function<void()> MYCALLBACK;

The callback function in C++ is set via:

MYCALLBACK myCBFunc; // some member variable
void SetCallbackFunction(MYCALLBACK cbFunc)
{
  myCBFunc = cbFunc;
}

in C#:

delegate void MyCallbackFunc(); // delegate matching the c++ callback sig

// method matching the call back sig
void foo()
{ }

[DllImport("mydll.dll")]
static extern SetCallbackFunction(MyCallbackFunc cbfunc);

// set the callback
MyCallbackFunc cb = foo;
SetCallbackFunction(cb); // crash here

Compiles OK, but crashes with AccessViolationException when run. I initially thought this is because MYCALLBACK obj is on the stack, and need to pass by reference and changed the signature to match but it still crashes i.e.

MYCALLBACK myCBFunc; // some member variable
void SetCallbackFunction(MYCALLBACK& cbFunc)
{
  myCBFunc = cbFunc;
}

[DllImport("mydll.dll")]
static extern SetCallbackFunction(ref MyCallbackFunc cbfunc);

// set the callback
MyCallbackFunc cb = foo;
SetCallbackFunction(ref cb); // crash here

how do i get this to work with std::function()? No problems using plain old function pointers.

Upvotes: 3

Views: 2202

Answers (1)

David Heffernan
David Heffernan

Reputation: 612964

std::function<void()> is a class template. It looks like a function because the class overloads operator(). So, std::function<void()> being a class means that it is simply not binary compatible with an unmanaged function pointer.

You'll need to use:

typedef void (*MYCALLBACK)();

Note that this function is cdecl. Either switch it to stdcall in your C++, or declare your C# delegate as being cdecl. I expect you know how to do the former. Here's an example of the latter:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void MyCallbackFunc();

Your pinvoke is also wrong in that it should not pass the callback as a ref parameter. It should be:

[DllImport("mydll.dll")]
static extern SetCallbackFunction(MyCallbackFunc cbfunc);

Upvotes: 2

Related Questions