Crash in when calling a C# callback function from C DLL - c#

I am writing a C# app on Windows CE 6 to monitor a 3G modem. The app will call functions in a C DLL to access the modem.
In startup, C# app will call this function to create a new connection:
[DllImport("swmodem.dll", CallingConvention = CallingConvention.Winapi)]
public static extern int CreateDataConnection(EVENT_CALLBACK callback);
The EVENT_CALLBACK is defined as:
public delegate void EVENT_CALLBACK(int e, IntPtr data);
A data structure is also defined:
[StructLayout(LayoutKind.Sequential)]
public struct ECIO_INFO
{
public UInt32 ecio1; /*!< Primary scramble code */
public UInt32 ecio2; /*!< Received signal code power */
public UInt32 ecio3; /*!< Energy per chip per power density */
}
In C DLL, a function pointer is passed in CreateDataConnection() for modem status update.
int CreateDataConnection(EVENT_CALLBACK ecb)
{
.
.
fEventCallback = ecb;
// Create a connection
.
.
}
After a connection is created, the DLL will invoke the callback function to update the modem status, for example EC/IO (The ratio of received pilot energy).
Basically, when ECIO changes, the callback function is called to pass the ECIO data to C# app:
In C DLL:
void ProcessNotification(EVENT_CALLBACK fEventCallback)
{
ECIO_INFO ecio_info;
ecio_info.ecio1 = ecio_info.ecio2 = ecio_info.ecio3 = 0;
if(data.nNumOfCells>0)
ecio_info.ecio1 = data.arCellInfo[0].nEcIo;
if(data.nNumOfCells>1)
ecio_info.ecio2 = data.arCellInfo[1].nEcIo;
if(data.nNumOfCells>2)
ecio_info.ecio3 = data.arCellInfo[2].nEcIo;
if(data.nNumOfCells>0)
fEventCallback(ME_RSCP_ECIO, &ecio_info);
}
In C# app, the callback function is defined as:
private void ModemEventCallback(int e, IntPtr data)
{
.
.
Modem.ECIO_INFO new_reinfo = new Modem.ECIO_INFO();
new_reinfo = (Modem.ECIO_INFO)Marshal.PtrToStructure(
data, typeof(Modem.ECIO_INFO));
.
.
}
Now, the problem comes. When the program starts, everything is fine, the connection is created ok and EC/IO is being updated. but after running for several hours, the EC/IO update is stopped. After a test, I found it is stopped when the callback is invoke:
fEventCallback(ME_RSCP_ECIO, &ecio_info);
I don't know what has gone wrong here Probably passing a function pointer in a C# DLL invoke just not right way to do it, or some fault is buried in the code?

Since callback function is just pointer for C/C++, callback parameter must be declared as IntPtr. Create EVENT_CALLBACK instance end ensure that it remains alive all time your program runs. Use Marshal.GetFunctionPointerForDelegate Method to convert delecate instance to IntPtr, and pass resulting IntPtr to CreateDataConnection function.
[DllImport("swmodem.dll", CallingConvention = CallingConvention.Winapi)]
public static extern int CreateDataConnection(IntPtr callback);
...
EVENT_CALLBACK c;
c = new EVENT_CALLBACK( ... ); // keep this alive !
...
CreateDataConnection(Marshal.GetFunctionPointerForDelegate(c));

Try this
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void EVENT_CALLBACK(int e, IntPtr data);
It solved my problem.

I think you must use GCHandl.Alloc with GCHandleType.Pinned, by this you will tell gc that this object must remain in memory even though
there might be no "roots" in the application that refer to this object and the memory for this object cannot be compacted

Related

C# app crashes on exit after using C++ function with delegates

My C# app needs to talk with a DLL written in C++. I don't have the code of this DLL, but I have the code for a demo app (also in C++) that uses the DLL and works.
Here's the interesting code from the C++ demo app. It has two functions, one of which accepts a callback set of 4 delegates.
typedef BOOL (CBAPI* OPENSCANNERSDK)(HWND hwnd, TCallBackSet* callbackSet, wchar_t* configPath);
typedef void (CBAPI* CLOSESCANNERSDK)();
typedef struct TCallBackSet
{
TOnScannerStatusEvent scannerStatusEvent;
TOnScannerNotifyEvent scannerNotifyEvent;
TOnRFIDStatusEvent rfidStatusEvent;
TOnRFIDNotifyEvent rfidNotifyEvent;
} TCallBackSet;
typedef void (cdecl* TOnScannerStatusEvent ) (int scannerStatus);
typedef void (cdecl* TOnScannerNotifyEvent) (int scannerNotify, int lParam);
typedef void (cdecl* TOnRFIDStatusEvent ) (int rfidStatus);
typedef void (cdecl* TOnRFIDNotifyEvent ) (int rfidnotify);
So, I have to call OpenScannerSDK, pass a callback set with pointers to some functions, do some stuff, and finally call CloseScannerSDK.
I have declared this in my C# app like so:
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "_OpenScannerSDK")]
extern public static bool OpenScannerSDK(IntPtr hwnd, TCallBackSet callbackSet,
[MarshalAs(UnmanagedType.LPWStr)]string configPath);
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "_CloseScannerSDK")]
extern public static void CloseScannerSDK();
[StructLayout(LayoutKind.Sequential)]
public class TCallBackSet
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void TOnScannerStatusEvent(int scannerStatus);
[MarshalAs(UnmanagedType.FunctionPtr)]
public TOnScannerStatusEvent ScannerStatusEvent;
(I have removed the other 3 callbacks for brevity)
}
Finally I use the library like so:
var callback = new TCallBackSet() { ... set the delegates ... }
OpenScannerSDK(IntPtr.Zero, callback, ".");
... do other stuff...
CloseScannerSDK();
All this seems to work - both OpenScannerSDK and CloseScannerSDK and all the others I use between them work correctly. The problem is that as soon as the application tries to exit, I get an APPCRASH in KERNELBASE.dll. I don't see any relevant information in the crash report file. I have noticed that if I do not call OpenScannerSDK, but just some other functions of the DLL that are not related to delegates, the APPCRASH doesn't happen.
I also tried GC.KeepAlive for the delegates, no effect.
I had a similar issue porting other C DLL to C# some years ago.
Function pointers in C# are represented as delegates. Internally, delegates are class instances and they're collected by GC the same way other objects are collected.
Probably in your DLL there's some method that "stops" the API, making it stopping invoking the C++ function pointers. You must call this before you close the application.
Probably your app collects the delegate objects and when the C++ DLL tries to invoke from unmanaged code, finds an invalid object reference.
It's interesting to keep the references in C# of these delegates in private fields, to avoid them to be collected when the application is running. The sympton of this issue is that the application crashes intermitently.
Hope this helps.
To anyone interested, here is the final working solution.
First, CallBackSet is defined like so:
[StructLayout(LayoutKind.Sequential)]
public class CallBackSet
{
public IntPtr ScannerStatusEvent;
public IntPtr ScannerNotifyEvent;
public IntPtr RfidStatusEvent;
public IntPtr RfidNotifyEvent;
}
And then:
OnScannerStatusEvent onScannerStatus = (ScannerStatus status) => {...};
OnScannerNotifyEvent onScannerNotify = (ScannerNotify notify, short lparam) => {...};
OnRfidStatusEvent onRfidStatus = (RfidStatus status) => {...};
OnRfidNotifyEvent onRfidNotify = (RfidNotify notify) => {...};
GCHandle.Alloc(onScannerStatus);
GCHandle.Alloc(onScannerNotify);
GCHandle.Alloc(onRfidStatus);
GCHandle.Alloc(onRfidNotify);
callbackSet = new CallBackSet()
{
RfidNotifyEvent = Marshal.GetFunctionPointerForDelegate(onRfidNotify),
RfidStatusEvent = Marshal.GetFunctionPointerForDelegate(onRfidStatus),
ScannerNotifyEvent = Marshal.GetFunctionPointerForDelegate(onScannerNotify),
ScannerStatusEvent = Marshal.GetFunctionPointerForDelegate(onScannerStatus)
};
callbackPtr = Marshal.AllocHGlobal(Marshal.SizeOf(callbackSet));
Marshal.StructureToPtr(callbackSet, callbackPtr, false);
OpenScannerSDK(IntPtr.Zero, callbackPtr, ".");
Upon further testing I found that actually OpenScannerSDK always returns True, which made me believe it was working correctly, but it may not be the case. I did several tests, including passing a TCallBackSet struct that does not have any members and OpenScannerSDK still returned True.
So it seems the problem is with TCallBackSet and these delegates. Something's wrong there. I tried passing the CallBackSet w and w/o ref, no difference. Noticed int in C++ is not the same as int in C#, so changed functions signatures to use short instead of int. None of these made any difference.

Destructor execution when wrapping a C++ object for using with C# PInvoke

I have a C++ class that I want to use in C#. For doing so I am trying to write an other C++ dll to wrap this class (which is part of an other library) with invokable functions (using "extern C and __declspec(dllexport)").
My idea is to keep a pointer to my object and send it to functions in wrapper dll and then call methods of that object from there. This seems fine but problems happen when object has deconstructor.
Here is my C++ wrapper code: (Device is my C++ class/object)
__declspec(dllexport) Status Device_open(Device* di, const char* uri)
{
Device dl;
Status status = dl.open(uri);
di = &dl;
return status;
}
__declspec(dllexport) void Device_Close(Device* di)
{
di->close();
}
Here is my C# wrapper code:
[DllImport("Wrapper.dll")]
static extern Status Device_open(ref IntPtr objectHandler, IntPtr uri);
public static Device Open(string uri)
{
IntPtr handle = IntPtr.Zero;
Device_open(ref handle, Marshal.StringToHGlobalAnsi(uri));
return new Device(handle);
}
[DllImport("Wrapper.dll")]
static extern void Device_Close(IntPtr objectHandler);
public void Close()
{
Device_Close(this.Handle);
}
Here is testing code in C# application:
Device d = Device.Open(di.URI);
d.Close();
Every thing is good. Problem is here that when I request opening a new device, Deconstructor of main C++ object will be executed so my close request always return exception (becouse it is already closed or destructed);
What can I do to prevent this?!
The Device is being destructed as it is out of scope at the endof the Device_open() function. To resolve, dynamically allocate the Device instance using new, this gives control of the lifetime of dl to you. Then delete dl; in the Device_Close() function.
Note that the C++ function is assigning the address to a Device* that is local to the function, it will not be seen by the caller. To fix this, on the C++ side, you can pass the pointer by reference:
__declspec(dllexport) Status Device_open(Device*& di, const char* uri)
or you can pass a Device**.
__declspec(dllexport) Status Device_open(Device** di, const char* uri)
I am, however, unsure how this would affect the c# side.
To prevent any memory leaks ensure that the new instance of Device is deleted if the call to dl.open(url) fails:
__declspec(dllexport) Status Device_open(Device*& di, const char* uri)
{
Status status;
try
{
Device* dl = new Device();
status = dl->open(uri);
if (status != OK)
{
delete dl;
}
else
{
di = dl;
}
}
catch (std::bad_alloc const&)
{
status = BAD_ALLOC; // Or equivalent failure reason.
}
return status;
}
__declspec(dllexport) void Device_Close(Device* di)
{
// di->close(); Uncomment if destructor does not close().
delete di;
}

LoadLibrary and define callback to C DLL (function pointer)

I have a code like this one in a DLL:
int (*callback)(int a, int b);
void mainfunc()
{
callback(1, 2);
callback(3, 4);
}
To access this DLL from a C program, I used to do this:
#include <windows.h>
int callback(int a, int b) {return a+b;}
int main()
{
HANDLE dll = LoadLibrary("test.dll");
*(void**)GetProcAddress(dll, "callback") = (void*)callback;
((void(*))GetProcAddress(dll, "mainfunc"))();
FreeLibrary(dll);
}
The DLL is still C code, but the main program have switched to C#. How to deal with function pointer? How would that code be in C#?
callback is a global variable in the dll, so you can't[1] set it from c#.
You shouldn't be setting it from C either. What you should have, if callback really is shared, is a function like SetCallback( MyCallbackType callback ) to set it.
But probably, you want to pass the function to mainfunc.
In either case, this is easy to do in C#:
// C#
public delegate Int32 Callback( Int32 a, Int32 b );
[DllImport("test.dll")]
static extern void mainfunc( Callback callback );
// Or if you're setting the callback globally,
[DllImport("test.dll")]
static extern void SetCallback(Callback callback);
[DllImport("test.dll")]
static extern void mainfunc();
The take home message is that DllImport is smart enough to convert a delegate with a reasonably close signature to a C function pointer.
[1] Ok, so you can...dll injection sometimes uses one of a number of hacks along those lines, but it's all kinds of the wrong way to do it in any other situation.

calling C++ functions containing callbacks in C#

hey all im trying to get my head around calling this c++ function in c#:
BOOL __stdcall CodecStart(int hRadio,void __stdcall (*CallbackFunc)(void *),void *CallbackTarget);
this is from a WinRadio api found here http://www.winradio.com/home/g305_sdk.htm.
i did find that other people asked about calling this specific function on the net and they had:
public delegate void CallbackFunc( IntPtr p);
[DllImport("WRG305API.dll")]
public static extern bool CodecStart(int hRadio, CallbackFunc func, IntPtr CallbackTarget);
but i cant figure out how to implement this further.
any thoughts or guidance as to how to call this?
many thanks
Here's a simple implementation that will put it all together.
class WinRadioWrapper
{
public delegate void CallbackFunc( IntPtr pData );
[DllImport( "WRG305API.dll" )]
public static extern bool CodecStart( int hRadio, CallbackFunc func, IntPtr CallbackTarget );
public bool CodecStartTest(int hRadio)
{
bool bStarted = CodecStart( hRadio, MyCallbackFunc, IntPtr.Zero );
return bStarted;
}
// Note: this method will be called from a different thread!
static void MyCallbackFunc( IntPtr pData )
{
// Sophisticated work goes here...
}
}
Note that because MyCallbackFunc will be executed on a different thread, I chose to make is static. This way you won't be tempted to access WinRadioWrapper data members.
For simplicity I passed an IntPtr.Zero parameter to the callback, but this can point to any data that you'd like to use in the callback.[Please ignore this paragraph] Look into Marshal.StructureToPtr if you'd like to pass data to the callback, but make sure to also pin the data that you're passing in order to make sure it's not garbage-collected (see GCHandle for more details).
EDIT:
With the interesting words by svick (thanks!), I realize I was mixing a copied object with a pinned one.
So, to sort things out:
Marshal.StructureToPtr should be used if you want to copy an existing data structure and then pass it to the callback function.
If, on the other hand, you'd like to use and existing data structure (e.g. for modifying its content), the you should use GCHandle in order to pin it in memory and prevent it from being garbage-collected.This, however, will add some maintenance overhead for the GCHandle.
A callback function is a code that is called by a dll (you're importing in this case) that performs some functions. You also need to learn how to work with delegates in c#. You can implement the code like this:
public void MyCallback(IntPtr p)
{
//do something
}
and then your dll call will be like this:
[DllImport("WRG305API.dll")]
public static extern bool CodecStart(int hRadio, func, IntPtr CallbackTarget);
If you need more guidance, post the C++ version of the code you want to convert and we can help you with the C# version.
All you need to do is to create a c# function that matches the signature of the delegate you declared. Create a delegate, hold on to a reference to this delegate so it doesn't get garbage collected, and call the dll import with the delegate as the callback.
so you would having something like this:
public void MyCallback(IntPtr P)
{
//do something
}
// somewhere else in your code
var cb = new CallbackFunc(MyCallback);
CodecStart(..., cb, ...);

C#. How to pass message from unsafe callback to managed code?

Is there a simple example of how to pass messages from unsafe callback to managed code?
I have a proprietary dll which receives some messages packed in structs and all is coming to a callback function.
The example of usage is as follows but it calls unsafe code too. I want to pass the messages into my application which is all managed code.
*P.S. I have no experience in interop or unsafe code. I used to develop in C++ 8 yrs ago but remember very little from that nightmarish times :)
P.P.S. The application is loaded as hell, the original devs claim it processes 2mil messages per sec.. I need a most efficient solution.*
static unsafe int OnCoreCallback(IntPtr pSys, IntPtr pMsg)
{
// Alias structure pointers to the pointers passed in.
CoreSystem* pCoreSys = (CoreSystem*)pSys;
CoreMessage* pCoreMsg = (CoreMessage*)pMsg;
// message handler function.
if (pCoreMsg->MessageType == Core.MSG_STATUS)
OnCoreStatus(pCoreSys, pCoreMsg);
// Continue running
return (int)Core.CALLBACKRETURN_CONTINUE;
}
Thank you.
You can use Marshal class to deal with interop code.
example:
C:
void someFunction(int msgId, void* funcCallback)
{
//do something
funcCallback(msgId); //assuming that function signature is "void func(int)"
}
C#
[DllImport("yourDllname.dll")]
static extern someFunction(int msgId, IntPtr funcCallbackPtr);
public delegate FunctionCallback(int msgId);
public FunctionCallback functionCallback;
public void SomeFunction(int msgId, out FunctionCallback functionCallback)
{
IntPtr callbackPtr;
someFunction(msgId, callbackPtr);
functionCallback = Marshal.DelegateToPointer(callbackPtr);
}
you can call as:
SomeFunction(0, (msgIdx) => Console.WriteLine("messageProcessed"));
I hope I did it right. I did not try to compile it :)

Categories

Resources