Encapsulating C Callback function within a C# function - c#

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.

Related

Pointer of a C# object for unmanaged interop

I am currently writing a wrapper for the PhysFS library, and I stumbled across a bit of troubles regarding the marshalling of managed objects. Take for example the PHYSFS_enumerateFilesCallback method, which takes a function pointer and a user-defined pointer as its arguments. How can I pass managed objects to this method? This is what I am currently doing:
// This is the delegate signature
public delegate void EnumFilesCallback(IntPtr data, string origdir, string fname);
// This is the method signature
[DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
public static extern void PHYSFS_enumerateFilesCallback(string dir, EnumFilesCallback c, IntPtr d);
Finally, this is what I'm doing to pass an arbitrary object to the method:
// I use the unsafe keyword because the whole Interop class is declared so.
// This code was taken from https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.gchandle(VS.71).aspx
public static void EnumerateFilesCallback(string dir, EnumFilesCallback c, object data)
{
unsafe
{
GCHandle objHandle = GCHandle.Alloc(data);
Interop.PHYSFS_enumerateFilesCallback(dir, c, (IntPtr)objHandle);
objHandle.Free();
}
}
When I run this code:
static void Enum(IntPtr d, string origdir, string fname )
{
System.Runtime.InteropServices.GCHandle handle = (System.Runtime.InteropServices.GCHandle)d;
TestClass c = (TestClass)handle.Target;
Console.WriteLine("{0} {1}", origdir, fname);
}
static void Main(string[] args)
{
PhysFS.Init("");
PhysFS.Mount("D:\\", "/hello", true);
TestClass x = new TestClass() { a = 3, b = 4 }; // This can be any gibberish object
PhysFS.EnumerateFilesCallback("/hello/", Enum, x);
}
The delegate gets called 4 times with legit data, the fifth time it contains garbage data and then it throws an AccessViolationException
I suspect this is because the object gets GCed in between the calls to the delegate. Can anyone shed light on this?
UPDATE: Changing the mounted directory eliminates the rubbish data, yet the exception is still thrown, and still before all the data can be consumed
Have you tried to create the callback and store it as a class static field?
private static EnumFilesCallback callback = new EnumFilesCallback(Enum);
And in your main method:
PhysFS.EnumerateFilesCallback("/hello/", callback, x);
This should probably avoid the GC to collect the local variable holding the delegate object.
Thanks to everyone who invested their time trying to provide an answer! I've finally found the source of the problem and solved it!
The problem was... I am a bit ashamed of it... calling convention.
All the PInvoked methods were declared as cdecl while I forgot to declare the delegates as such, so it created unbalanced stacks and mayhem and whatnot...

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.

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

Is there any possibility to invoke managed methods from C in Xamarin.IOS

On the Windows Platform there's the possibility to create a COM wrapper around a managed object that might be used from unmanaged code.
Since I'm just dealing with a problem where I would like to pass a managed System.IO.Stream reference from managed code to a legacy C library function (its not even Objective-C), I'm curious if there's any chance at all to get this to work?
No, you can't pass a managed reference like this to C code in iOS.
But you can do reverse P/Invoke calls: you give native code a delegate, and you can call that delegate from C as a function pointer.
Here is some (untested) sample code that should get you on the right track:
delegate long GetLengthCallback (IntPtr handle);
// Xamarin.iOS needs to the MonoPInvokeCallback attribute
// so that the AOT compiler can emit a method
// that can be called directly from native code.
[MonoPInvokeCallback (typeof (GetLengthCallback)]
static long GetLengthFromStream (IntPtr handle)
{
var stream = (Stream) GCHandle.FromIntPtr (handle).Target;
return stream.Length;
}
static List<object> delegates = new List<object> ();
static void SetCallbacks (Stream stream)
{
NativeMethods.SetStreamObject (new GCHandle (stream).ToIntPtr ());
var delGetLength = new GetLengthCallback (GetLengthFromStream);
// This is required so that the GC doesn't free the delegate
delegates.Add (delGetLength);
NativeMethods.SetStreamGetLengthCallback (delGetLength);
// ...
}

C++/CLI wrapper for native C++ dll

I have written an C++/Cli wrapper for a native C++ dll, but when I call some method from C# I get an System.AccessViolationException error in my C++/Cli Wrapper dll! It's necessary to marshal the unmanaged types or something else?!
// Wrapper.h
typedef UnmanagedClass* (*Instance)(void);
private:
UnmanagedClass *m_object; // unmanaged object
// Wrapper.cpp
Wrapper:Wrapper()
{
HINSTANCE unmanagedLib;
unmangedLib = LoadLibrary(SystemStringToLPCSTR(dllPath+dllName));
// load instance
Instance _createInstance = (Instance)GetProcAddress(unmangedLib, "GetInstance");
m_object = (_createInstance)();
}
Wrapper::~Wrapper()
{
m_object->~UnmanagedClass();
}
Uint32 Wrapper::SomeMethod(Uint8 *bytRecvBuffer, int &iRecvLen)
{
return m_object->SomeMethod(bytRecvBuffer, iRecvLen);
}
// Unmanaged Class
class UnmanagedClass
{
public:
/**
* Default constructor.
*/
UnmanagedClass(void);
/**
* Default Destructor
*/
~UnmanagedClass(void);
virtual Uint32 Wrapper::SomeMethod(Uint8 *bytRecvBuffer, int &iRecvLen);
};
// export the UnmanagedClass object
extern "C" _declspec(dllexport) UnmanagedClass* GetInstance();
// UnamangedClass.cpp
UnamangedClass::~UnamangedClass(void)
{
if (UnamangedClassDLL != NULL)
FreeLibrary(UnamangedClassDLL);
UnamangedClassDLL = NULL;
}
extern "C" _declspec(dllexport) UnmanagedClass* GetInstance()
{
return new UnmanagedClass();
}
When I call at example SomeMethod from C# I get the error in C++/Cli dll!
(I included the C++/cli dll with add reference in C sharp project and create the Wrapper object)
Thank you for your help!
greets
It is inappropriate to directly call the destructor of an object that was allocated with (non-placement) new. Try changing
m_object->~UnmanagedClass();
to
delete m_object;
m_object = 0;
(m_object = 0; is necessary because unlike a native C++ type's destructor, which may only be called once, an managed type's Dispose implementation may be called repeatedly, and doing so must have defined behavior.)
Or, better yet, in addition to exposing a GetInstance function, also expose a DestroyInstance function and call that instead of using delete so that consuming code does not need to depend on the implementation details of GetInstance (i.e., that it allocates its instance using operator new).
I have found the error (System.AccessViolationException):
I'm using an other object in the unmanaged code without initialization (null object -> only declared)!
Init the object with new() and all should run properly!

Categories