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.
Related
Is it possible to store member functions in C++ the way delegates are used in C#?
How do Delegates accomplish the object-instance, memberFuncPtr pairs and calls without knowing anything about the instance class?
(note: this questions arose while I was trying to understand events/delegates in publisher/subscriber code design)
I read that:
C++ requires knowledge of a class type such as ClassType::*FuncPtr
C# delegates allow someDelegate += classInstance.memberFunc
There is type called std::function,but if you are using older version of C++ (older than C++11) you will need to use raw function pointers.Check function pointers in C,they are the same for C++.
Not getting exactly what you are asking.
But you can store member functions in form of function pointers in c++.
void yourFunc(int x);
int main()
{
void (*funcPtr)(int)=&yourFunc;
(*funcPtr)(10);
}
Or
A normal function with an int parameter
// and void return type
void fun(int a)
{
printf("Value of a is %d\n", a);
}
int main()
{
// fun_ptr is a pointer to function fun()
void (*fun_ptr)(int) = &fun;
/* The above line is equivalent of following two
void (*fun_ptr)(int);
fun_ptr = &fun;
*/
// Invoking fun() using fun_ptr
(*fun_ptr)(10);
return 0;
}
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.
The problem is that i've got a C++ DLL that I want to use in my C# project.
The problematic function has the prototype:
int MRK3LINK_Open(void (*pfLog)(const char* s),void (*pfErrorOut)(const char* s));
The documentation states that:
pfLog is a pointer to log handler function of type const char*.
pfErrorOut is a pointer to error out handler function of type const char*.
And an example of how to call the functions of the DLL from C++:
static void _LogHandler(const char* sLog) {
printf(sLog);
}
static void _ErrorOutHandler(const char* sError) {
MessageBox(NULL, sError, "2-Link", MB_OK);
}
MRK3LINK_Open(_LogHandler, _ErrorOutHandler);
I'm stuck on this for 2 days now. Could you provide me some tips ?
Thanks.
The two parameters are function pointers. They will map to delegates in C++. Like this:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void LogHandlerDelegate(string str);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ErrorOutHandlerDelegate(string error);
Then the function you import is:
[DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
public static extern int MRK3LINK_Open(
LogHandlerDelegate LogHandler,
ErrorOutHandlerDelegate ErrorOutHandler
);
Then you create delegates in the usual way and pass them to MRK3LINK_Open. Make sure that you store references to the delegates if the unmanaged code takes a reference to them and calls them after MRK3LINK_Open returns. Otherwise the garbage collector is liable to collect them.
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.
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