Calling Unity C# delegate from unmanaged code in a different thread (Android) - c#

I passed a delegate (logging function) to unmanaged code and it works fine, unless it's called from another thread, which is internal to the C++ library.
I read this, but I can't find any information on what shall I expect when C++ threads are involved. It's definitely threading problem, since delegate works when I call it from the same thread which called unmanaged function from Unity.
C#
public class SomeClass : MonoBehaviour
{
public delegate void LibLogHandler(int a);
private LibLogHandler handlerDelegate;
[DllImport ("mylibrary")]
private static extern bool library_init (string arg1, string arg2, LibLogHandler logHandler);
public void goNow()
{
this.handlerDelegate = new LibLogHandler (this.logHandler);
bool res = library_init ("someArg1", "someArg2", this.handlerDelegate);
Debug.Log ("library init: " + res);
}
void logHandler(int a)
{
Debug.Log ("[library] " + a);
}
}
C++ header
extern "C" {
typedef void (*LibLog) (int a);
bool library_init(const char* arg1, const char* arg3, LibLog libLog);
}
C++ source
static boost::asio::io_service IoService;
static boost::thread aThread;
static boost::shared_ptr<boost::asio::io_service::work> ThreadWork;
bool library_init(const char* arg1, const char* arg2, LibLog libLog)
{
#ifdef THREAD_ENABLED
ThreadWork.reset(new boost::asio::io_service::work(IoService));
aThread = boost::thread([](){
IoService.run();
});
IoService.dispatch([libLog]{
libLog(100);
});
#else
libLog(10);
#endif
return true;
}
So, when THREAD_ENABLED is defined, application silently crashes with no additional information.

I figured out the problem - this article was useful to understand the peculiarities of callbacks invoked from native plugins. In short, in managed code (C#) one just need to make sure the delegate object is persistent (referenced) for the period while it can be invoked from unmanaged code. No need to worry about pinning the memory using GCHandle, since managed runtime environment automatically creates an unmanaged stub handle for the delegate object, which maintains its' address regardless of garbage collector operations. This stub handle is passed to the unmanaged function behind the scenes.

Unity doesn't support threading and you can't use it's API in another threads.
Try using
bool res;
try{
res = library_init ("someArg1", "someArg2", this.handlerDelegate);
}catch (System.Exception e){
Debug.LogError(e.Message);
throw;
}
to get whats wrong with your thread.
EDIT
I wonder if my first statement is true in your case. I think Debug.Log() should be safe. If that's the case you should debug your C++ thread and look why it's crashing.

Related

How to adapt a C++ std::future return value to a C# System.Threading.Tasks.Task?

I am wrapping a C++ library to be used from .NET. There are functions in the C++ API that return std::future. I want to have the .NET proxy classes return System.Threading.Tasks.Task.
I thought of adding a replacement method on the C++ side that would take in a function pointer in addition to the methods parameters. I could then start up a new thread (using std::async for example) and have that wait on std::future::get. Once std::future::get returned I could call the passed in function pointer. On the C# side I would pass the a pointer to a function that would complete the returned Task. Something like:
Task CallCpp(int a, int b) {
TaskCompletionSource<int> tcs;
Action callback = () => tcs.SetResult(0);
IntPtr callbackPtr = Marshal.GetFunctionPointerForDelegate(callback);
CppMethod(callbackPtr, a, b);
return tcs.Task;
}
[DllExport]
external void CppMethod(IntPtr callback, int a, int b);
void CppMethod(void (*callback)(), int a, int b) {
std::future f = realmethod(a, b);
std::async([&]{ f.get; callback(); });
}
std::future realmethod(int a, int b);
(Yes, the code has memory management issues. It should be enough to get the idea across though)
Marshalling asynchronous completion or failure is relatively easy part. I would personally use a COM interface implemented in C#, marked [InterfaceType( ComInterfaceType.InterfaceIsIUnknown )], with 2 methods like setCompleted() and setFailed( HRESULT status ), and pass it when I start the task, but that’s a matter of taste.
The problem is that C++ multithreading implementation is lacking.
Boost is better in that regard, it’s future class has then method that can be used to propagate the status without blocking the thread.
Currently, standard C++ futures don’t offer anything comparable. I think you're out of luck.
If you can modify that C++ library, change the API to something that makes more sense, i.e. can notify about the completion in non-blocking manner.
Without COM, lifetime management is complicated. After you’ve launched the async task but before it completes, someone on C# side needs to retain that function pointer. Here’s a possible solution, note how it uses GCHandle to make callback outlive the task:
[UnmanagedFunctionPointer( CallingConvention.Cdecl )]
delegate void pfnTaskCallback( int hResult );
[DllImport( "my.dll" )]
static extern void launch( [MarshalAs( UnmanagedType.FunctionPtr )] pfnTaskCallback completed );
public static async Task runAsync()
{
var tcs = new TaskCompletionSource<bool>();
pfnTaskCallback pfn = delegate ( int hr )
{
if( hr >= 0 ) // SUCEEDED
tcs.SetResult( true );
else
tcs.SetException( Marshal.GetExceptionForHR( hr ) );
};
var gch = GCHandle.Alloc( pfn, GCHandleType.Normal );
try
{
launch( pfn );
await tcs.Task;
}
finally
{
gch.Free();
}
}

Marshal C# Method to Unity3D with C++

I would like to call a specific method in my Unity3D project within an external plugin. Here is my C++ code:
static void UNITY_INTERFACE_API OnRenderEventThreadSafe(int eventID)
{
[...] Some C++ code
//Call C# Method here
}
My C++ code does already work on its own but now I want to call a specific C# method. How can I manage to do this? I tried creating delegates like this
#if UNITY_EDITOR
[DllImport("lib")]
#else
[DllImport ("lib")]
#endif
private static extern void Method(Delegate callback);
private delegate void Delegate(int number);
[MonoPInvokeCallback(typeof(Delegate))]
private static void DelegateMessageReceived(int number)
{
Debug.Log("MessageReceived " + number);
}
public static void Method2()
{
Method(DelegateMessageReceived);
}
But I don't think this is the right way...
Should have mentioned it earlier, but I'm coding on Linux-Fedora25
I ran into the-same issue in the past when making rendering plugin but the solution is not really hard.
You need to create a global static function pointer to hold the C# function you want to call from C++. The C++ function pointer must be static. Initialize this variable from the Start or Awake function in C#. After this, you can freely call that function anytime from C++.
C++ (YourCppPlugin.h)
#define DLLExport __declspec(dllexport)
extern "C"
{
//Create a function pointer and give it a callback alias to shorten it
typedef void(*FuncCallBack)(int eventID);
//Create a callback delegate from the alias to hold the function from C#
static FuncCallBack callbackInstance = nullptr;
//Create the function that will be called from C# to store the function
DLLExport void RegisterRenderCallback(FuncCallBack cb);
}
C++ (YourCppPlugin.cpp):
#include "YourCPP.h"
//Save the function we received from C# to the global variable for future use:
void RegisterRenderCallback(FuncCallBack cb) {
callbackInstance = cb;
}
Done. You can now use the callbackInstance variable to call the C# function anytime from the Unity's UNITY_INTERFACE_API OnRenderEventThreadSafe C++ API function. Just make sure it's not null before calling it like below:
static void UNITY_INTERFACE_API OnRenderEventThreadSafe(int eventID)
{
[...] Some C++ code
//Call C# Method here
if (callbackInstance != nullptr)
callbackInstance(eventID);
}
Register a function to it first from C# with the RegisterRenderCallback function first before using the callbackInstance variable. See example in the Start function below.
C# side of the code:
// Use this for initialization
void Start()
{
//Register the OnRenderCallback function to our C++ plugin for a callback
RegisterRenderCallback(OnRenderCallback);
}
//Function that registers a local C# function to a delegate in C# for later use
[DllImport("YourCppPlugin", CallingConvention = CallingConvention.Cdecl)]
static extern void RegisterRenderCallback(renderCallback cb);
//int param callback delegate that links to the C# function we want to call from C++
delegate void renderCallback(int eventID);
//Function we want to call from C#. Registered in the Start function
[MonoPInvokeCallback(typeof(renderCallback))]
static void OnRenderCallback(int eventID)
{
Debug.Log("Called from C# with ID: " + eventID);
}
Finally, if you have more than one function to register, you have to use List, array or dictionary to hold them on both C# and C++ side then iterate and call each one when necessary.

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

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

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

Categories

Resources