Passing managed function to unmanaged function that uses std::function() - c#

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.

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);

Related

How safe is ref when used with unsafe code?

Using Microsoft Visual C# 2010, I recently noticed that you can pass objects by ref to unmanaged code. So I tasked myself with attempting to write some unmanaged code that converts a C++ char* to a a C# string using a callback to managed code. I made two attempts.
Attempt 1: Call unmanaged function that stores a ref parameter. Then, once that function has returned to managed code, call a another unmanaged function that calls a callback function that converts the char* to a managed string.
C++
typedef void (_stdcall* CallbackFunc)(void* ManagedString, char* UnmanagedString);
CallbackFunc UnmanagedToManaged = 0;
void* ManagedString = 0;
extern "C" __declspec(dllexport) void __stdcall StoreCallback(CallbackFunc X) {
UnmanagedToManaged = X;
}
extern "C" __declspec(dllexport) void __stdcall StoreManagedStringRef(void* X) {
ManagedString = X;
}
extern "C" __declspec(dllexport) void __stdcall CallCallback() {
UnmanagedToManaged(ManagedString, "This is an unmanaged string produced by unmanaged code");
}
C#
[DllImport("Name.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void StoreCallback(CallbackFunc X);
[DllImport("Name.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void StoreManagedStringRef(ref string X);
[DllImport("Name.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void CallCallback();
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void CallbackFunc(ref string Managed, IntPtr Native);
static void Main(string[] args) {
string a = "This string should be replaced";
StoreCallback(UnmanagedToManaged);
StoreManagedStringRef(ref a);
CallCallback();
}
static void UnmanagedToManaged(ref string Managed, IntPtr Unmanaged) {
Managed = Marshal.PtrToStringAnsi(Unmanaged);
}
Attempt 2: Pass string ref to unmanaged function that passes the string ref to the managed callback.
C++
typedef void (_stdcall* CallbackFunc)(void* ManagedString, char* UnmanagedString);
CallbackFunc UnmanagedToManaged = 0;
extern "C" __declspec(dllexport) void __stdcall StoreCallback(CallbackFunc X) {
UnmanagedToManaged = X;
}
extern "C" __declspec(dllexport) void __stdcall DoEverything(void* X) {
UnmanagedToManaged(X, "This is an unmanaged string produced by unmanaged code");
}
C#
[DllImport("Name.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void StoreCallback(CallbackFunc X);
[DllImport("Name.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void DoEverything(ref string X);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void CallbackFunc(ref string Managed, IntPtr Unmanaged);
static void Main(string[] args) {
string a = "This string should be replaced";
StoreCallback(UnmanagedToManaged);
DoEverything(ref a);
}
static void UnmanagedToManaged(ref string Managed, IntPtr Unmanaged) {
Managed = Marshal.PtrToStringAnsi(Unmanaged);
}
Attempt 1 doesn't work but attempt 2 does. In attempt 1 it seems that as soon as the unmanaged code returns after storing the ref, the ref becomes invalid. Why is this happening?
Given the outcomes of attempt 1, I have doubts that attempt 2 will work reliably. So, how safe is ref on the unmanaged side of code when used with unmanaged code? Or in other words, what won't work in unmanaged code when using ref?
Things I'd like to know are are:
What exactly happens when objects are passed using ref to unmanaged code?
Does it guarantee that the objects will stay at their current position in memory while the ref is being used in unmanaged code?
What are the limitations of ref (what can't I do with a ref) in unmanaged code?
A complete discussion of how p/invoke works is beyond the proper scope of a Stack Overflow Q&A. But briefly:
In neither of your examples are you really passing the address of your managed variable to the unmanaged code. The p/invoke layer includes marshaling logic that translates your managed data to something usable by the unmanaged code, and then translates back when the unmanaged code returns.
In both examples, the p/invoke layer has to create an intermediate object for the purpose of marshaling. In the first example, this object is gone by the time you call the unmanaged code again. Of course in the second example, it's not, since all of the work happens all at once.
I believe that your second example should be safe to use. That is, the p/invoke layer is smart enough to handle ref correctly in that case. The first example is unreliable because p/invoke is being misused, not because of any fundamental limitation of ref parameters.
A couple of additional points:
I wouldn't use the word "unsafe" here. Yes, calling out to unmanaged code is in some ways unsafe, but in C# "unsafe" has a very specific meaning, related to the use of the unsafe keyword. I don't see anything in your code example that actually uses unsafe.
In both examples, you have a bug related to your use of the delegate passed to unmanaged code. In particular, while the p/invoke layer can translate your managed delegate reference to a function pointer that unmanaged code can use, it doesn't know anything about the lifetime of the delegate object. It will keep the object alive long enough for the p/invoked method call to complete, but if you need it to live longer than that (as would be the case here), you need to do that yourself. For example, use GC.KeepAlive() on a variable in which you've stored the reference. (You likely can reproduce a crash by inserting a call to GC.Collect() between the call to StoreCallback() and the later call to unmanaged code where the function pointer would be used).

Encapsulating C Callback function within a C# function

I need to make a DLL written in C accessible to C# programs. I wrote a wrapper DLL in C# that wraps every C interface function.
I came out with a code which is functional but eventually crashes with the following message:
Managed Debugging Assistant 'CallbackOnCollectedDelegate' has detected a problem in 'D:\WrapperTest\bin\x64\Debug\WrapperTest.exe'.
Additional information: A callback was made on a garbage collected delegate of type 'vJoyInterfaceWrap!vJoyInterfaceWrap.vJoy+WrapFfbCbFunc::Invoke'.
The part that is responsible to the crash is related to a callback function that the C# code registers with the DLL. The crash occurs when the DLL calls (or returns) from the registered callback function.
C DLL Interface:
// Definition of callback function prototype
// Parameter 1: Pointer to a data structure to be used inside the CB function
// Parameter 2: Pointer to user-defined data passed during registration
typedef void (CALLBACK *FfbGenCB)(PVOID, PVOID);
// Registration of CB function.
// Parameter cb is a pointer to the callback function.
// Parameter data is a pointer to user-defined data
VOID __cdecl FfbRegisterGenCB(FfbGenCB cb, PVOID data);
C# Interface:
I'd like the user-defined data to be an object (rather than an IntPtr).
To do that I encapsulate the user's callback function with a predefined callback function that converts IntPtr to object.
Earlier during registration, I register the predefined callback function and convert the user-defined data (a C# object) to IntPtr.
// The user-defined CB function is of type FfbCbFunc
// FfbCbFunc is defined:
public delegate void FfbCbFunc(IntPtr data, object userData);
// The registration of the predefined callback function _WrapFfbCbFunc is done by calling
// function FfbRegisterGenCB:
public void FfbRegisterGenCB(FfbCbFunc cb, object data)
{
// Convert object to pointer
GCHandle handle1 = GCHandle.Alloc(data);
// Apply the user-defined CB function
_g_FfbCbFunc = new FfbCbFunc(cb);
WrapFfbCbFunc wf = new WrapFfbCbFunc(_WrapFfbCbFunc);
_FfbRegisterGenCB(wf, (IntPtr)handle1);
}
Here are additional definitions and declaration:
// Placeholder for user defined callback function
private static FfbCbFunc _g_FfbCbFunc;
// predefined callback function that encapsulates the user-defined callback function
public delegate void WrapFfbCbFunc(IntPtr data, IntPtr userData);
public void _WrapFfbCbFunc(IntPtr data, IntPtr userData)
{
// Convert userData from pointer to object
GCHandle handle2 = (GCHandle)userData;
object obj = handle2.Target as object;
// Call user-defined CB function
_g_FfbCbFunc(data, obj);
}
The wrapper callback function and its reference had to be static:
// Make wf global and static
private static WrapFfbCbFunc wf;
// Make the wrapping callback function static
public static void _WrapFfbCbFunc(IntPtr data, IntPtr userData)
{
object obj = null;
if (userData != IntPtr.Zero)
{
// Convert userData from pointer to object
GCHandle handle2 = (GCHandle)userData;
obj = handle2.Target as object;
}
// Call user-defined CB function
_g_FfbCbFunc(data, obj);
}
P.S.
I don't think this was such a bad question to be rewarded by a -1 vote.
Personally, I just ignore questions that are not interesting.

C# pInvoke to a C function

I would be grateful for help with a problem I have been stuck on for a couple of days.
I have a native C++ function type declared so:
typedef STATUS (T_TED_AcppBoxDYN_RegisterEventCallback) (
PEventCallback function, // pointer to the customer callback
PVOID param // custom data (returned in callback)
);
where PEventCallback and PEVENT are declared like so:
typedef int (*PEventCallback) (PEVENT event, PVOID param);
typedef struct
{
int nEventId;
void* pParam;
} EVENT,*PEVENT;
The C++ code provides a pointer to a function of that type as a global variable:
T_TED_AcppBoxDYN_RegisterEventCallback* TED_AcppBoxDYN_RegisterEventCallback
= NULL;
which is initialized later, via this code:
#ifdef LOAD_PROC_ADDRESS
#undef LOAD_PROC_ADDRESS
#endif
#define LOAD_PROC_ADDRESS(handle,func) \
if((func=(T_##func*)GetProcAddress(handle,#func))==NULL) \
{\
sMsg.Format( "Error occurs while loading entry point\n'%s'\n"\
"from detector DLL '%s'\n", GetName (), #func );\
MessageBox(NULL, sMsg.GetBuffer(), "Load Proc Error", MB_OK | MB_ICONSTOP);\
return (false);\
}
bool P5100EDllManager::LoadProc ()
{
CString sMsg;
HMODULE hDllHandle = GetHandle();
if (hDllHandle == NULL)
{
return false; // cannot load the proc if the dll has not been loaded
}
LOAD_PROC_ADDRESS(hDllHandle, TED_AcppBoxDYN_RegisterEventCallback);
return true;
}
I want to call the pointed-to function from C#. For that purpose, I have defined a C# wrapper:
public delegate void TDICallBack(IntPtr callbackEvent, IntPtr pParam);
[DllImport(DLL, EntryPoint = "TED_AcppBoxDYN_RegisterEventCallback", CallingConvention = CallingConvention.Cdecl)]
private static extern int TED_AcppBoxDYN_RegisterEventCallback(TDICallBack callBack, IntPtr param);
public void RegisterEventCallback(TDICallBack callBack, IntPtr param)
{
TED_AcppBoxDYN_RegisterEventCallback(callBack, param);
}
I am using it like this:
TdiapiFacade.RegisterEventCallback(OnTdiCallBack, IntPtr.Zero);
public void OnTdiCallBack(IntPtr ptr, IntPtr param)
{
}
The RegisterEventCallback() seems to work successfully, but at the point where the callback function is supposed to be invoked, the app crashes. As you can see, at this stage I am not even unwrapping the parameters provided to the callback function.
What do I need to do to make this work?
P/invoke doesn't allow access to exported data (variables) such as your function pointer. You'll need a helper inside the DLL, either an exported function which wraps the function pointer, or one that returns it (the return type will be seen as a delegate inside C#).
Thanks for your input
I've resolved the issue, by modifying the registration of my callback from:
void RegisterCB()
{
var ret = TdiapiFacade.RegisterEventCallback(OnTdiCallBack, new IntPtr());
HandleError(ret);
}
to this:
private TDIAPI.TDICallBack callback;
void RegisterCB()
{
callback = new TDIAPI.TDICallBack(OnTdiCallBack);
var ret = TdiapiFacade.RegisterEventCallback(callback , new IntPtr());
HandleError(ret);
}
This fixes the issue, now my callback is properly invoked.

What does GetFunctionPointerForDelegate convert a String^ parameter in a delegate into?

I'm trying to convert a C# delegate to a C++ function pointer, using Managed C++. Here's the method we were previously using:
// Define a delegate
public delegate void ADelegate(Int32);
ADelegate^ delegateInstance;
// Define a function pointer
typedef void (__stdcall *AFuntionPointer)(int);
AFuntionPointer functionPointerInstance;
// Create an instance of a delegate, using GetFunctionPointerForDelegate
delegateInstance = gcnew ADelegate(this, &AClass::AFunction);
// Convert the delegate to a pointer
IntPtr anIntPtr = Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(delegateInstance);
// Cast the pointer into a function pointer
functionPointerInstance = static_cast<AFuntionPointer>(anIntPtr.ToPointer());
If I turn the ADelegate's parameter from an Int32 to a String^, to what type should I change the AFunctionPointer's parameter to? In another words, if I changed the first two lines in the above code to:
public delegate void ADelegate(String ^);
ADelegate^ delegateInstance;
How should I change the next two lines?
// To what type does GetFunctionPointerForDelegate translate String^ to?
typedef void (__stdcall *AFuntionPointer)( /* char*? std::string? */ );
AFuntionPointer functionPointerInstance;
Marshal::GetFunctionPointerForDelegate() on that delegate would generate a function pointer that's compatible with
typedef void (__stdcall *AFuntionPointer)( const char* );
String^ marshals to const char* unless a [MarshalAs] attribute is applied to the delegate argument. Marshaling directly to std::string is not possible, the pinvoke marshaller doesn't know anything about C++ object layout for classes that are declared in a header file.

How do I call a function defined in a C++ DLL that has a parameter of type int *, from inside C# code?

I have a native regular C++ Dll which I want to call from C# code, so i created C++/CLI class (as described here and here) which will include managed C++ code and which can be called by any C# code directly and which can make calls inturn to native unmanaged C++.
One of function in native C++ dll has parameter of type int *. How do I declare in wrapper function and how can i convert it into int *?
It is the C/C++ way of passing a value by reference. You should use the ref or out keyword:
[DllImport("something.dll")]
private static extern void Foo(ref int arg);
In C++/CLI that would look roughly like this:
public ref class Wrapper {
private:
Unmanaged* impl;
public:
void Foo(int% arg) { impl->Foo(&arg); }
// etc..
};
[DllImport("some.dll")]
static extern void SomeCPlusPlusFunction(IntPtr arg);
IntPtr is a type that is roughly equivalent to void *.
From your comment, you'd be best off doing something like this (C#):
int size = 3;
fixed (int *p = &size) {
IntPtr data = Marshal.AllocHGlobal(new IntPtr(p));
// do some work with data
Marshal.FreeHGlobal(data); // have to free it
}
but since AllocHGlobal can take an int, I don't know why you wouldn't do this:
IntPtr data = Marshal.AllocHGlobal(size);

Categories