Passing unmanaged method as callback to managed C++/CLI class - c#

I want to pass as callback a C++ member function to a C# project. I have other project in C++/CLI and I want to do it through it.
So, in unmanaged C++ of my C++/CLI project I have a function object:std::function<void(int)>callback;
This function is coming from my C++ project and it works fine, I save it there as example to avoid the previous step. Now, I would like to pass this callback function to my C# project. For this, I create a method in unmanaged C++, pass it to managed C++ and from this point pass it finally to C#. I'd like something like this:
// Unmanaged class
public class Wrapper
{
public:
std::function<void(int)>callback;
void ReturnToCallback(int data)
{
callback(data);
}
void PassCallback()
{
NETWrapper netWrapper;
netWrapper.TestCallback(ReturnToCallback);
}
};
//Managed class
public ref class NETWrapper
{
public:
void TestCallback(Action<int>^ callback)
{
StartGenerator^ startGen = gcnew StartGenerator(callback);
}
};
// C#
public class StartGenerator
{
private Communication comm;
public StartGenerator(Action<int> callback)
{
comm = Communication.Instance;
comm.callback = callback;
}
}
This solution, of course, gives me back an error when compiling:
Error 3 error C3867: 'IfaceXXX::Wrapper::ReturnToCallback': function call missing argument list; use '&IfaceXXX::Wrapper::ReturnToCallback' to create a pointer to member d:\XXX.h
I have tried other ways such as Get the delegate for the function pointer so I can work on Managed C++ and pass it to C# but I am not able to implement it right. What do you think is the best way to try this?

Make Wrapper::callback a pointer to the std::function.
Change Wrapper to a ref class.
That's it.
public ref class Wrapper
{
public:
std::function<void(int)>* callback;
void ReturnToCallback(int data)
{
(*callback)(data);
}
void PassCallback()
{
NETWrapper netWrapper;
netWrapper.TestCallback(gcnew Action<int>(this, &Wrapper::ReturnToCallback));
}
};
You do then need to manage the lifetime of the std::function now, perhaps my clr_scoped_ptr could show you how to do that.

Related

Send status update to C# from C++ using delegate?

I have a C++ function which performs a number of tasks "PerformJob()". I have a C# wrapper which calls PerformJob(). The job takes a while and I would like the C++ method to send "status updates" back up to the calling C# class. The C++ code is not exposed to the C# class. Adding the C# project as a reference would cause a circular dependency.
I've attempted to pass a delegate through as a parameter but I'm not familiar enough with C++ syntax to make this work (or if it is even possible?). Is there an appropriate way to pass a delegate into C++ as a parameter? Is there a better method to facilitate this communication? I'd like to avoid a dllimport, as I only need to receive updates from this one class.
CSharpClass.cs:
public delegate void CallbackDelegate(ref string status);
public CallbackDelegate jobStatusDelegate;
public void UpdateJobStatus(ref string status)
{
Job.JobStatus = status;
}
public void StartJob()
{
jobStatusDelegate = new CallbackDelegate(UpdateJobStatus);
CPlusClass jobHelper = new CPlusClass();
jobHelper.PerformJob(jobStatusDelegate);
}
CPlusClass.h:
public ref class CPlusClass
{
public:
void PerformJob(delegate del); // is there c++ delegate type?
};
CPlusClass.cpp:
void CPlusClass::PerformJob(delegate del)
{
// ....
}
C++ doesn't have delegates, but C++/CLI does, and based on ref class your code already is C++/CLI.
Delegates work much like any other reference type, they get stored as a tracking handle with the ^.
In order to not create a dependency on the C#, which would be in the wrong direction, I suggest you use a System::Action<System::String^>^ instead of defining your own delegate type.
public ref class CPlusClass
{
public:
void PerformJob(System::Action<System::String^>^ del)
{
del->Invoke(gcnew String("Hello World"));
}
};

How to specify a callback from C# to C++/CLI

I would like to pass a function pointer (or similar) as a callback function to the constructor of a C# class, called from C++/CLI. The C# class is a sub-module; the C++ side is the main program. I'm getting errors reported by Visual Studio 2017, and I can't work out the correct syntax to use. (I'm a C++ programmer, but have close to zero experience with CLI and C#.) I find plenty of examples on how to set up callbacks the other way around, but from C# to C++/CLI I find little information.
Can somebody tell me what the correct syntax is, or show a different approach to achieve the same goal if this one is fundamentally flawed?
C# code (seems fine):
namespace MyNamespace
{
public class MyCSharpClass
{
private Action<string> m_logger;
public MyCSharpClass(Action<string> logger) => m_logger = logger;
public void logSomething()
{
m_logger("Hello world!");
}
}
}
C++/CLI code (errors are in the second gcnew line with the System::Action):
#pragma once
#pragma managed
#include <vcclr.h>
class ILBridge_MyCSharpClass
{
public:
ILBridge_MyCSharpClass(ManagedDll_MyCSharpClass* pManagedDll_MyCSharpClass)
: m_pManagedDll_MyCSharpClass(pManagedDll_MyCSharpClass)
{
m_pImpl = gcnew MyCSharpClass::MyCSharpClass(
gcnew System::Action<System::String^>^(this, &ILBridge_MyCSharpClass::log)
);
}
void log(System::String^ message) const
{
// ...
}
}
The errors reported:
error C3698: 'System::Action<System::String ^> ^': cannot use this type as argument of 'gcnew'
note: did you mean 'System::Action<System::String ^>' (without the top-level '^')?
error C3364: 'System::Action<System::String ^>': invalid argument for delegate constructor; delegate target needs to be a pointer to a member function
If I remove the "^" as suggested, the C3698 error disappears but the C3364 error remains.
I'm following the design pattern suggested here, though not using code generation: http://blogs.microsoft.co.il/sasha/2008/02/16/net-to-c-bridge/
Edit: essential solution
An Action in C++ CLI, can be created from a function (not a member function but free or static) or from the member function of a managed ref class.
In order to call a native member function from an Action, the native member call needs to be wrapped in a managed member function.
class NativeClassType;
ref class ManagedWrapper
{
typedef void(NativeClassType::*MemberFunc)(System::String^);
NativeClassType* nativeObject;
MemberFunc memberFunction;
public:
ManagedWrapper(NativeClassType* obj, MemberFunc wrappedFunction)
: nativeObject(obj), memberFunction(wrappedFunction)
{
// Action that can be used in other managed classes to effectively invoke the member function from NativeClassType
auto actionObject = gcnew System::Action<System::String^>(this, &ManagedWrapper::CallWrapped);
}
void CallWrapped(System::String^ msg)
{
// forward the call
(nativeObject->*memberFunction)(msg);
}
};
Original answer and full example
I played around a little and as far as I can tell, you will need to use native member function pointer handling at some point in order to callback to native member functions...
The following example code provides a managed (ref) class for static function callback and another one for member function callback. The native class NativeManaged is using both bridge classes to demonstrate different callbacks.
ref class ILBridge_Logger
{
private:
System::Action<System::String^>^ loggerCallback;
public:
ILBridge_Logger(void (*logFn)(System::String^))
{
loggerCallback = gcnew System::Action<System::String^>(logFn);
}
ILBridge_Logger(System::Action<System::String^>^ logFn)
{
loggerCallback = logFn;
}
void Test(System::String^ msgIn)
{
log(msgIn);
}
void log(System::String^ message)
{
loggerCallback(message);
}
};
template<typename CallbackObject>
ref class ILBridge_MemberLogger : public ILBridge_Logger
{
CallbackObject* o;
void (CallbackObject::*logFn)(System::String^);
public:
ILBridge_MemberLogger(CallbackObject* o, void (CallbackObject::*logFn)(System::String^))
: ILBridge_Logger(gcnew System::Action<System::String^>(this, &ILBridge_MemberLogger::logMember)), o(o), logFn(logFn)
{
}
// translate from native member function call to managed
void logMember(System::String^ message)
{
(o->*logFn)(message);
}
};
class NativeManaged
{
gcroot<ILBridge_Logger^> Impl1;
gcroot<ILBridge_Logger^> Impl2;
public:
NativeManaged()
{
Impl1 = gcnew ILBridge_Logger(gcnew System::Action<System::String^>(log1));
Impl2 = gcnew ILBridge_MemberLogger<NativeManaged>(this, &NativeManaged::log2);
}
void Test(System::String^ msgIn)
{
Impl1->Test(msgIn);
Impl2->Test(msgIn);
}
// static logger callback
static void log1(System::String^ message)
{
System::Console::WriteLine(L"Static Log: {0}", message);
}
// member logger callback
void log2(System::String^ message)
{
System::Console::WriteLine(L"Member Log: {0}", message);
}
};
int main(array<System::String ^> ^args)
{
NativeManaged c;
c.Test(L"Hello World");
return 0;
}
Note: there might be more elegant ways of handling member function pointers with the C++11/14/17 features that I'm not aware of.
You can't use c# delegates like function pointer. But you can make unsafe c++ cli method which call c# methods.
For reference, here is the solution I ended up with. The C# code is the same as in the OP. A managed (ref) class was needed, as suggested by grek40 in his answer. In order to use the managed class by my managed DLL, the original IL Bridge class was still needed.
#pragma once
#pragma managed
#include <vcclr.h>
class ManagedDll_MyCSharpClass;
class ILBridge_MyCSharpClass;
ref class Managed_MyCSharpClass
{
ILBridge_MyCSharpClass* m_pILBridge_MyCSharpClass;
void (ILBridge_MyCSharpClass::*m_logger)(System::String^);
MyCSharpClass::MyCSharpClass^ m_pImpl;
public:
Managed_MyCSharpClass(ILBridge_MyCSharpClass* pILBridge_MyCSharpClass, void (ILBridge_MyCSharpClass::*logger)(System::String^))
: m_pILBridge_MyCSharpClass(pILBridge_MyCSharpClass)
, m_logger(logger)
{
m_pImpl = gcnew MyNamespace::MyCSharpClass(
gcnew System::Action<System::String^>(this, &Managed_MyCSharpClass::log)
);
}
void log(System::String^ message)
{
(m_pILBridge_MyCSharpClass->*m_logger)(message);
}
};
class ILBridge_MyCSharpClass
{
public:
ILBridge_MyCSharpClass(ManagedDll_MyCSharpClass* pManagedDll_MyCSharpClass)
: m_pManagedDll_MyCSharpClass(pManagedDll_MyCSharpClass)
{
m_pManaged_MyCSharpClass = gcnew Managed_MyCSharpClass(this, &ILBridge_MyCSharpClass::log);
}
void log(System::String^ message)
{
// ...
}
private:
ManagedDll_MyCSharpClass* m_pManagedDll_MyCSharpClass;
gcroot<Managed_MyCSharpClass^> m_pManaged_MyCSharpClass;
};

Pass a object** between a c++/cli wrapper method and c#

So I have a managed c++ wrapper for my native c++ code that looks like this:
namespace AmbitCLRWrapper {
public ref class Render
{
AmbitRender::Render* nativeClass;
public:
Render()
{
nativeClass = new AmbitRender::Render();
}
~Render()
{
this->!Render();
}
!Render()
{
delete nativeClass;
}
void getD3DSurface(IDirect3DSurface9** surface)
{
nativeClass->getD3DSurface(surface);
}
void** getD3DSurfaceR()
{
IDirect3DSurface9** surface;
nativeClass->getD3DSurface(surface);
return (void**)surface;
}
};
}
I can import the dll into c# just fine, and create a Render without issue doing:
AmbitCLRWrapper.Render render = new AmbitCLRWrapper.Render();
but I cannot figure out how to use either of my bottom two methods or even if they are the right way to do it - but I'm trying to follow along using http://msdn.microsoft.com/en-us/library/cc656716(v=vs.110).aspx as a template (but I do want a wrapper, not just to pass straight from unmanaged to c#
Anyone know how to do this correctly?
IntPtr pSurface = render.getD3DSurfaceR(); // this line does not compile
It needs to be consumed like so:
d3dimg.SetBackBuffer(D3DResourceType.IDirect3DSurface9, pSurface);

Delegate in c++

Hey i want to implement delegate in c++ i know how to do in c# i posting my code but don't know how i convert this to c++
public class Events {
public delegate void Action();
public Action OnPrintTheText = delegate{};
}
public class ABC {
private Event evt;
public ABC() {
evt = new Event();
}
public void printText() {
evt.OnPrintTheText();
}
public Event getEventHandler() {
return evt;
}
}
public class Drived {
private ABC abc;
public Drived() {
abc = new ABC();
abc.getEventHandle().OnPrintTheText += OnPrint;
}
~Drived() {
abc.getEventHandle().OnPrintTheText -= OnPrint;
}
public void OnPrint() {
Debug.Log("ABC");
}
}
now whenever i call printText it will automatically call the OnPrint() of Drived class so is there anyway to implement this to c++?
C# does a lot of management behind the scenes. Broadly, a delegate in C# is a container of function references.
In C++, you can create a similar delegate by using a functor template that wraps an object instance and a member function for class member functions, and also one that just wraps a function (for non members). Then you can use a standard container to maintain the list/array/map/etc of the instances of the functors, and this will provide you with the functionality of the C# delegate and also allow adding and removing 'actions' (C#: += and -=).
Please see my answer here on how you can create such a templated functor for class members. The non-member case is simpler since it does not wrap an object instance.
Take a look at function pointers:
http://www.newty.de/fpt/index.html
A function pointer is a pointer that points to a specific function, basically what a delegate is.
Look at this:
What is the difference between delegate in c# and function pointer in c++?

How to pass a JNI C# class into Java or handle this situation?

I'm trying to call a Java method from C#, it's called like this from java:
EgamePay.pay(thisActivity, payAlias, new EgamePayListener() {
#Override
public void paySuccess(String alias) {
}
#Override
public void payFailed(String alias, int errorInt) {
}
#Override
public void payCancel(String alias) {
}
});
The first two parameters are ok, but how do I pass the EgamePayListner through in C#? Simply creating a C# class with the same functions won't work...
Here's what I'm doing currently:
using (AndroidJavaClass jc = new AndroidJavaClass("cn.egame.terminal.smspay.EgamePay"))
{
System.IntPtr cls_Activity = AndroidJNI.FindClass("com/unity3d/player/UnityPlayer");
System.IntPtr fid_Activity = AndroidJNI.GetStaticFieldID(cls_Activity, "currentActivity", "Landroid/app/Activity;");
object[] p = { fid_Activity, "payAlias", new EgamePayListener() };
jc.CallStatic("pay", p);
}
..
class EgamePayListener
{
public void paySucess(string alias)
{
}
public void payFailed(string alians, int errorInt)
{
}
public void payCancel(string alias)
{
}
}
Obviously that's wrong, how can I handle this situation so that I can get notified back in C# land when those functions are fired?
You can link these projects together in VS201X via references. From here, you should be able to fill in the other layers(JNI/Java), and then start passing your pointers(as a long) around the system and invoking your functions.
C# Layer
Program.cs
namespace CSharpLayer
{
class Program : CLILayer.CLIObject
{
static void Main(string[] args)
{
Program p = new Program();
p.invokeJava();
}
public void invokeJava()
{
//Call into CLI layer function to loadJVM, call Java code, etc
loadJava();
}
public override void callback(string data)
{
//This will be called from the CLI Layer.
}
}
}
C++/CLI Layer - DLL C++ project w/ CLR support(/clr)
CLIObject.h
#pragma once
namespace CLILayer
{
public ref class CLIObject
{
public:
CLIObject();
~CLIObject();
void loadJava(System::String^ jvm, System::String^ classpath);
virtual void callback(System::String^ data) = 0;
};
}
CLIObject.cpp
#include "CLIObject.h"
#include <string>
#include <msclr/marshal_cppstd.h>
#include <msclr/marshal.h>
using namespace msclr::interop;
using namespace CLILayer;
CLIObject::CLIObject()
{
}
CLIObject::~CLIObject()
{
}
CLIObject::loadJava(System::String^ jvmLocaion, System::String^ classpath)
{
std::string _jvmLoc = marshal_as<std::string>(jvmLocation);
std::string _classpath = marshal_as<std::string>(classpath);
}
if you are in a context of Unity3D, a better way to interact from Java native code to Unity3D C# script would be the calling the UnitySendMessage method. You can call this method in you Java code, and a message will be sent to C#, so you can get a specified method executed in C#.
You could add a gameObject in your Unity scene and create a MonoBehavior script which contains these three methods (paySucess(string message), payFailed(string message) and payCancel(message)). Then attach the new created script to the gameObject (let us assume the name of this gameObject to be "PayListener") and make sure the gameObject existing in your scene when the Java code be executed (you can call DontDestroyOnLoad on the gameObject in Awake, for example).
Then, in your Java code, just write like these:
EgamePay.pay(thisActivity, payAlias, new EgamePayListener() {
#Override
public void paySuccess(String alias) {
com.unity3d.player.UnityPlayer.UnitySendMessage("PayListener","paySuccess", alias);
}
#Override
public void payFailed(String alias, int errorInt) {
com.unity3d.player.UnityPlayer.UnitySendMessage("PayListener","payFailed", alias + "#" + errorInt);
}
#Override
public void payCancel(String alias) {
com.unity3d.player.UnityPlayer.UnitySendMessage("PayListener","payCancel", alias);
}
});
It would prevent the java file to build successfully without the com.unity3d.player.UnityPlayer class. You could find the /Applications/Unity/Unity.app/Contents/PlaybackEngines/AndroidPlayer/bin/classes.jar file and add it as a dependency library for your java project. Then you can build, generate and use the new jar file without error.
Because of the UnitySendMessage method can only take 1 parameter, so you might have to connect the payFailed result alias and errorInt by a strategy, and parse it back in Unity C# side.
By using the new built jar file and load it as a AndroidJavaClass or AndroidJavaObject, the corresponding method in the PayListener script should be called when the one in Java is get called.
For documentation of the Android plugin with UnitySendMessage, you can visit the official guide of how to implement a Android JNI plugin here, in the example 3.
Here is JNI Field Descriptor
JavaLanguage Type
--------------------------------
Z boolean
B byte
C char
S short
I int
J long
F float
D double
Ljava/lang/String; string
[Ljava/lang/Object; object[]
Method descriptors make use of the fields descriptors and describe the structure of a Java method. There are no spaces between the field descriptors in a method descriptor.
Apart from the void return type which is denoted by V, all other return types use the field descriptor. The table below describes the Java method declaration and the corresponding JNI descriptor. The JNI method descriptor is used when calling a Java method from C# via JNI.
Java Method Declaration JNI Method Descriptor
----------------------------------------------------
String foo(); "()Ljava/lang/String;"
Void bar(int I, bool b); (IZ)V
The first thing that needs to be done is to create a dictionary object that will contain all of the parameters to pass to the Java Virtual Machine. In the example below, I am doing the minimum of setting the class path that will tell the JVM where to look for the classes and packages.
private Dictionary<string, string> jvmParameters = new Dictionary<string, string>();
jvmParameters.Add("-Djava.class.path", Location of the java class);
Once the JVM parameters have been assigned to the dictionary object, an instance of JavaNativeInterface can be created. Once created, the method LoadJVM needs to be called with the JVM parameters, and this will then load up the Java Virtual Machine. Once loaded, the user calls the method to instantiate the Java object (note that the use of the method InstantiateJavaObject is optional as the user may just want to call a static method, in which case, this method does not need to be called; however, it will not case any harm).
Java = new JavaNativeInterface();
Java.LoadVM(jvmParameters, false);
Java.InstantiateJavaObject(Name of the java class excluding the extension);
Once the JVM has been loaded and a class instantiated, the user may call any method they wish. First create an object list that will contain all of the parameters to pass into the Java method. As it is an object list, it can hold parameters of different types as everything inherits from an object.
List<object> olParameters = new List<object>();
olParameters.Add(Value of the parameter to be passed to the java method);
Next, simply call the generic method CallMethod passing in the return type of the Java method as the template type. In the example below, I am calling CallMethod which means that the return type of my Java method that I want to call is a string.
Next, pass in the name of the Java method to call and the method descriptor (see above); finally, pass in the list of all of the parameters. (Note: If no parameters are required, then pass in an empty list.)
Java.CallMethod<string>("AddTwoNumbers", "(IILjava/lang/String;)I", olParameters);
Well, I guess that wraps it all up, but remember that there is so much more you can do with JNI. The test application was just a quick and dirty way to demonstrate the basics of the JNI component. For a full understanding of JNI.
Either you can get more on this link
Have a look at http://jni4net.sourceforge.net/. I have successfully used it to communicate between CLR and JVM. Java application example that calls .NET classes can be found here. Events (bound to java listener interfaces) are supported too.
I ended up solving this problem myself, the other answers posted here while good unfortunately did not address the fact the problem I was facing was in Unity.
The way I solved it was by writing a custom listener class in Java with a 'GetResult' function that returned a string explaining what the outcome was (i.e. which function was called and with what result) and a get function for the result which C# called through AndroidJNI.
After making the purchase call it was a matter of calling the get function from C# until I had a result.
Sounds like a mess. What are you trying to accomplish exactly and why? Why not just run the JVM separately and CLR separately, interface over REST calls or something similar?

Categories