is it possible at all to make C/C++ function callback into Unity scripts, provided that you can create a new thread from the scripts? I tried but Unity crashes as soon as the scripts get executed.
I googled about it and found this thread which says
Unity is not callback nor threadsafe, any access to classes that
extend UnityEngine.Object is only allowed from within the same thread
as unity scripts are running in, not asyncronous from other threads
nor from asyncrnous callbacks from COM / OS async operations
If thats the case for you there are two possible ways:
(1) Write a wrapper that gets these callbacks and queues the stuff and
then expose a function that allows unity to request the next event /
dataobject or whatever in a blocking, unthreaded form (2) Make the
callbacks call into a static function on something extending from
System.Object and write the same kind of logic as above to request the
information on classes extending UnityEngine.Object
But I think if I create a thread and callback into that thread, it will be okay right? I am thinking like this because I've read threads like this one that introduces how to make C functions calling back C# functions. So I reasoned that if I create a new thread, it's no longer Unity, it will just be mono and C#.
Here is my code that crashes Unity:
The C++ code:
#include <iostream>
// #include "stdafx.h"
typedef int (__stdcall * Callback)(const char* text);
Callback Handler = 0;
extern "C" __declspec(dllexport)
void __stdcall SetCallback(Callback handler) {
Handler = handler;
}
extern "C" __declspec(dllexport)
void __stdcall TestCallback() {
int retval = Handler("hello world");
}
The C# code:
using UnityEngine;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
class UnManagedInterop : MonoBehaviour {
private delegate int Callback(string text);
private Callback mInstance; // Ensure it doesn't get garbage collected
public void Test() {
mInstance = new Callback(Handler);
SetCallback(mInstance);
TestCallback();
}
private int Handler(string text) {
// Do something...
print(text);
return 42;
}
[DllImport("test0")]
private static extern void SetCallback(Callback fn);
[DllImport("test0")]
private static extern void TestCallback();
void Start()
{
Thread oThread = new Thread(new ThreadStart(Test));
// Start the thread
oThread.Start();
}
}
The answer is Yes!
I tested again on August 8, 2012, with Unity 3.5.2f2, Pro license. Thanks for #hatboyzero's comment I found this example.
Although the code in my question doesn't work, the following code works:
// C#
using System.Runtime.InteropServices;
class Demo {
delegate int MyCallback1 (int a, int b);
[DllImport ("MyRuntime")]
extern static void RegisterCallback (MyCallback1 callback1);
static int Add (int a, int b) { return a + b; }
static int Sub (int a, int b) { return a - b; }
void Init ()
{
// This one registers the method "Add" to be invoked back by C code
RegisterCallback (Add);
}
}
// C
typedef int (*callback_t) (int a, int b);
static callback_t my_callback;
void RegisterCallback (callback_t cb)
{
my_callback = cb;
}
int InvokeManagedCode (int a, int b)
{
if (my_callback == NULL){
printf ("Managed code has not initialized this library yet");
abort ();
}
return (*my_callback) (a, b);
}
I didn't have to embed MonoRuntime as the tutorial suggests. Just the above two pieces of code solved my problem.
Related
I'm working on a .NET 5 project to bind the AWS-Encryption-SDK from C and C++ to C# using PInvoke, type marshalling, and the DllImportAttribute. When attempting to call this C++ function
aws_cryptosdk_keyring *Build(const Aws::String &generator_key_id, const Aws::Vector<Aws::String> &additional_key_ids = {}) const;
using
[DllImport("aws-encryption-sdk-cpp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern aws_cryptosdk_keyring Build(string generator_key_id, string[] additional_key_ids = null);
I get this error
System.EntryPointNotFoundException : Unable to find an entry point named 'Build' in DLL 'aws-encryption-sdk-cpp.dll'.
The dll signature for this function is
?Build#Builder#KmsKeyring#Cryptosdk#Aws##QEBAPEAUaws_cryptosdk_keyring##AEBV?$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std##AEBV?$vector#V?$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std##V?$allocator#V?$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std###2##7##Z
So far nothing that I have tried as the EntryPoint has been successful. Does anyone have any ideas?
Expanding on #jwezorek's comment, C# does not know how to call C++ (class) functions, only C functions. There are several reasons why this is so, but I won't get into them here.
You need to create (in C++) a wrapper that converts each C++ call to a 'flattened' C call. For example:
extern "C" void *createClass() {
return (void*) new myClass();
}
extern "C" void destroyClass(void* self) {
delete (myClass*) self;
}
extern "C" int callFunction1(void* self, int x, int y) {
return ((myClass*) self)->function1(x, y);
}
You'll need to write a function for each class function (plus the constructor and destructor). Then compile, linking it to that original C++ DLL's library. Then in C#, write a wrapper to call your flattened class:
public class myClass : IDisposable {
[DllImport("myFlattenedDll", EntryPoint="createClass")]
public static extern IntPtr createClass();
[DllImport("myFlattenedDll", EntryPoint="destroyClass")]
public static extern void destroyClass(IntPtr self);
[DllImport("myFlattenedDll", EntryPoint="callFunction1")]
public static extern int callFunction1(IntPtr self, int x, int y);
private IntPtr self;
public myClass() {
self = createClass();
}
~myClass() {
Dispose();
}
public virtual void Dispose() {
lock (this) {
destroyClass(self);
GC.SuppressFinalize(this);
}
}
public int callFunction1(int x, int y) {
return callFunction1(self, x, y);
}
}
You should also look into a tool called SWIG that can automate a good deal of the wrapper coding for you.
I have setup a c# console mode program which calls a clr/dll to access a MFC c++ dll and that works for accessing functions in the MFC c++ dll. But I want to pass back a delegate function from c# so that when a function in the MFC c++ dll needs to call the c# program it has the callback to do that. But I can't get it setup right...this is my attempt:
program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using dd_clr; // this is my clr.dll
namespace dd_gui
{
public delegate int GUI_CallbackDelegate(string fn);
class Program
{
int GUI_callback(string fn)
{
Console.WriteLine("Begin GUI_callback()");
Console.WriteLine("GUI_callback fn=" + fn);
Console.WriteLine("End GUI_callback()");
return (1);
}
static GCHandle gch;
static void Main(string[] args)
{
Console.WriteLine("begin GUI");
dd_clr.DDManaged instance = new dd_clr.DDManaged();
GUI_CallbackDelegate ^ fp = gcnew GUI_CallbackDelegate(GUI_Callback); // this does not compile for some reason ; expected after gcnew ???
gch = GCHandle.Alloc(fp);
instance.Set_GUICallback(fp); // this I am trying to get to work.
instance.batch_run("test.dap"); // this call works.
Console.WriteLine("end GUI");
gch.Free();
}
}
}
From your code it's not obvious what you are trying to do, but you can pass a delegate to C++ this way:
server.h:
extern "C"
{
typedef int(__stdcall * ComputeCallback)(int);
__declspec(dllexport) int Sum(ComputeCallback computeCallback);
}
server.cpp:
__declspec(dllexport) int Sum(ComputeCallback computeCallback)
{
int sum = 0;
for (int i = 0; i < 4; i++)
{
int x = computeCallback(i);
sum += x;
}
return sum;
}
client.cs:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate int ComputeCallback(int value);
class Program
{
[DllImport("server.dll")]
public static extern int Sum(ComputeCallback callback);
static void Main(string[] args)
{
ComputeCallback callback = x =>
{
Console.WriteLine("Computing. X = {0}", x);
return x*x;
};
Console.WriteLine("Result: {0}", Sum(callback));
}
}
I want to hook a C# method to a C++ event written in the C++ DLL
C++ side
#include
extern "C"
{
typedef void (__stdcall *PFN_MYCALLBACK)();
int __stdcall MyUnmanagedApi(PFN_MYCALLBACK callback);
}
C# side
public delegate void MyCallback();
[DllImport("data_acquisition_sys.dll")]
public static extern void MyUnmanagedApi(MyCallback callback);
static void Main(string[] args) {
MyUnmanagedApi(
delegate()
{
Console.WriteLine("Called back by unmanaged side");
}
);
}
}
I followed the http://blogs.msdn.com/b/davidnotario/archive/2006/01/13/512436.aspx
Error
Unhandled Exception: System.EntryPointNotFoundException: Unable to find an entry point named 'MyUnmanagedApi' in DLL 'data_acquisition_sys.dll'. at affect_detection_sys.Program.MyUnmanagedApi(MyCallback callback) at affect_detection_sys.Program.Main(String[] args) in C:\Users\Public\Docume
For all interested parties, here is a working solution to the problem.
C++ side
extern "C"
{
typedef void (*callback_function)();
callback_function gCBF;
__declspec(dllexport) void StartAcquisition(callback_function callback) {
gCBF = callback;
cout << "Acquisition started" << endl;
}
void DoWork() {
gCBF()
}
}
C# side
[DllImport("data_acquisition_sys.dll", EntryPoint = "StartAcquisition")]
public static extern void StartAcquisition(MyCallback callback);
StartAcquisition(delegate()
{
Console.WriteLine("Called back by unmanaged side ");
}
);
Note that the callback_function is an empty method (), since returning and accepting back ANY data results in a runtime crash. This has been reported in other threads, but the answer hasn't been given.
MyUnmanagedApi returns int and you have declared void. Try this:
public delegate void MyCallback();
[DllImport("data_acquisition_sys.dll")]
public static extern int MyUnmanagedApi(MyCallback callback);
static void Main(string[] args) {
MyUnmanagedApi(
delegate()
{
Console.WriteLine("Called back by unmanaged side");
}
);
}
}
I have this code in a c++ file, with compiles to a dll.
#include "stdafx.h"
#include "WHUU.h"
#include "stdafx.h"
typedef int (__stdcall * Callback)(const int text);
static int x , y= 0;
Callback Handler = 0;
int getX()
{
return x;
}
void setX(int i)
{
x = i;
}
void setY(int i)
{
y = i;
}
int getY()
{
return y;
}
extern "C" __declspec(dllexport)
void __stdcall SetCallback(Callback handler) {
Handler = handler;
}
extern "C" __declspec(dllexport)
void __stdcall addX(int x) {
setX(x);
}
extern "C" __declspec(dllexport)
void __stdcall addY(int y) {
setY(y);
}
extern "C" __declspec(dllexport)
void __stdcall TestCallback() {
int z = getX() + getY();
int retval = Handler(z);
}
My c# application now has to load this dll on runtime. Add to the callback and call the functions. I dont want to use a class. I could load the class and with
Type[] types = moduleAssembly.GetTypes();
But this overkill! Also c++ is not managed.
I mean its so tiny! (and yes this is an example , but the "real" is just as big as this example).
How do i do that?
Thanks you for your help!
add:
i dont like frameworks (like pinvoke / assembly)
the function names / types are fixed and will never change (think of a driver.dll read write)
this dll is written by customers so it should be as easy as possible!
You could also do it over p/invoke, as example:
[DllImport("unmanaged.dll", CharSet = CharSet.Ansi)]
private extern static int yourFunction(int var1, int var2);
i dont like frameworks (like pinvoke / assembly)
I'll suggest P/Invoke anyway. I don't think there are any reasonable alternatives. Perhaps writing a managed wrapper in managed C++, but really?
When you use P/Invoke the .NET runtime will dynamically load the DLL's that you have specified (and this is done when you first call the function). There is no reason to use P/Invoke to call LoadLibrary first.
You can either use P/Invoke to call the dll directly, or you can create a C++/CLI wrapper for it.
Here's how you can do it using P/Invoke:
Add the compiled dll to your C# project, set its 'Copy to Output Directory' property to 'Copy Always'. and call it like this:
class Program
{
[DllImport("cppdll.dll")]
private static extern void addX(int x);
[DllImport("cppdll.dll")]
private static extern void addY(int y);
static void Main(string[] args)
{
addX(5);
addY(7);
}
}
One way to do this would be through API. Find below an example for the setX and getX method from your sample above.
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr LoadLibraryEx(string libraryPath, IntPtr fileHandle, int actionFlag);
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern bool FreeLibrary(IntPtr libraryHandle);
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr GetProcAddress(IntPtr dllPointer, string functionName);
IntPtr ptr = IntPtr.Zero;
private delegate void setX (int i);
private delegate int getX();
private setX setXDel;
private getX getXDel;
public Constructor()
{
loadLib();
}
public void YourMethod()
{
setXDel(100);
int y = getXDel();
Console.WriteLine(y.ToString());
}
private void loadLib()
{
string path = "your dll path";
ptr = LoadLibraryEx(path, IntPtr.Zero, 0);
if (ptr == IntPtr.Zero)
throw new Exception("Cannot load dll.");
IntPtr addressPtr = GetProcAddress(ptr, "setX");
setXDel = (setX)Marshal.GetDelegateForFunctionPointer(addressPtr, typeof(setX));
addressPtr = GetProcAddress(unrarPtr, "getX");
getXDel = (getX)Marshal.GetDelegateForFunctionPointer(addressPtr, typeof(getX));
}
public void Dispose()
{
if (ptr != IntPtr.Zero)
{
FreeLibrary(ptr);
}
}
I used P/Invoke, post my own cause the callback was not included by the others. But they were good answers! The C# Applicationhas use this GUI.
The Code for the Application is this : (mind it is a prototype to answer a technical question)
public partial class Form1 : Form
{
private delegate int Callback(int text);
private Callback mInstance; // Ensure it doesn't get garbage collected
[DllImport(#"C:\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void SetCallback(Callback fn);
[DllImport(#"C:\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void addX(int x);
[DllImport(#"C:\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void addY(int y);
[DllImport(#"C:\\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void TestCallback();
private int Handler(int text)
{
textBox3.Text = text.ToString();
return 42;
}
private void button1_Click(object sender, System.EventArgs e)
{
mInstance = new Callback(Handler); // to set the callback in lib
SetCallback(mInstance); // could also be withhin constructor!
addX(int.Parse(textBox1.Text)); // other people in this thread posted this correct answer
addY(int.Parse(textBox2.Text));
TestCallback();
}
public Form1()
{
InitializeComponent();
}
So if you want to use a c++ lib with functions and callbacks (like in the question posted) you can use this.
But how to change the lib?
The Path to the lib is hardcoded (see [dllImport ......] ). But you
can swop the lib in filesystem. This can happen AFTER you build this
application.
If the lib is not on the given path an exception is thrown. So if
your program wants to use this lib first check the lib is present on the filesystem.
(not included in this simple prototype)
So to switch functionality (swop a driver lib ect) you copy a different lib with the
same name into the given path. (Copy paste - maybe change name)
This solution has the least overhead and is good if you never change the interface of an dll after the application was build!
developers!
I have very strange problem. My project has DLL writen in C++ and a GUI writen in C#. And I have implemented callback for some interoperability. I planed that C++ dll will call C# code in some circumstances. It works... but not long and I cant understand why. The problem marked in comment in C# part
Here the complete code of simplified sample:
C++ DLL:
#include <SDKDDKVer.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
extern "C"
{
typedef void (*WriteSymbolCallback) (char Symbol);
WriteSymbolCallback Test;
_declspec(dllexport) void InitializeLib()
{
Test = NULL;
}
_declspec(dllexport) void SetDelegate(WriteSymbolCallback Callback)
{
Test = Callback;
}
_declspec(dllexport) void TestCall(const char* Text,int Length)
{
if(Test != NULL)
{
for(int i=0;i<Length;i++)
{
Test(Text[i]);
}
}
}
};
C# part:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace CallBackClient
{
class Program
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void WriteToConsoleCallback(char Symbol);
[DllImport("CallbackSketch.dll",CharSet=CharSet.Ansi,CallingConvention=CallingConvention.Cdecl)]
private static extern void InitializeLib();
[DllImport("CallbackSketch.dll",CharSet=CharSet.Ansi,CallingConvention=CallingConvention.Cdecl)]
private static extern void SetDelegate(WriteToConsoleCallback Callback);
[DllImport("CallbackSketch.dll",CharSet=CharSet.Ansi,CallingConvention=CallingConvention.Cdecl)]
private static extern void TestCall(string Text,int Length);
private static void PrintSymbol(char Symbol)
{
Console.Write(Symbol.ToString());
}
static void Main(string[] args)
{
InitializeLib();
SetDelegate(new WriteToConsoleCallback(PrintSymbol));
string test = "Hello world!";
for (int i = 0; i < 15000; i++)
{
TestCall(test, test.Length);// It crashes when i == 6860!!!! Debugger told me about System.NullReferenceException
}
}
}
}
The problem is that it crashes in 6860th iteration! I believe that the problem is lack of my knowlege in the subject. Could sombody help me?
SetDelegate(new WriteToConsoleCallback(PrintSymbol));
Yes, this cannot work properly. The native code is storing a function pointer for the delegate object but the garbage collector cannot see this reference. As far as it is concerned, there are no references to the object. And the next collection destroys it. Kaboom.
You have to store a reference to the object yourself. Add a field in the class to store it:
private static WriteToConsoleCallback callback;
static void Main(string[] args)
{
InitializeLib();
callback = new WriteToConsoleCallback(PrintSymbol);
SetDelegate(callback);
// etc...
}
The rule is that the class that stores the object must have a lifetime at least as long as native code's opportunity to make the callback. It must be static in this particular case, that's solid.