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.
I'm trying to pass a simple C++ class, created in C#, trough a method to my c++ WinRT component, but I can't figure out how to do that and if its even possible.
I created this custom class in c++
(from https://msdn.microsoft.com/en-us/library/hh441569.aspx)
namespace CppComponent
{
// Custom struct
public value struct PlayerData
{
Platform::String^ Name;
int Number;
double ScoringAverage;
};
public ref class Player sealed
{
private:
PlayerData m_player;
public:
property PlayerData PlayerStats
{
PlayerData get(){ return m_player; }
void set(PlayerData data) {m_player = data;}
}
};
}
I can create it in c# and play with it, so that works.
I can also use other methods that return int or Platform::String.
But how can I use it in methods to c++ like? (and as return type)
in .cpp file:
Platform::String^ CppComponent::DoSomething(Platform::String^ input, Player myCustomClass)
{
in .h file:
Platform::String^ DoSomething(Platform::String^ input, Player myCustomClass);
Any idea how to get "Player myCustomClass" right?
PS: working on https://github.com/cmusphinx/pocketsphinx-wp-demo
Turns out I was close already.
I put de struct and class in my .h file and used Player^ instead of Player like:
Platform::String^ CppComponent::DoSomething(Platform::String^ input, Player^ myCustomClass)
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?
I wrote simple C++ Dll ( win32 ) and i change the properties to be 'Common Language Runtime Support (/clr)' -
I created new Simple winform project ( C# 4.0 ) and i created reference to the C++ project ( the C++ DLL ).
Now i can't see the C++ dll in the C# project - i cant use it and i dont know why.
If you have, for example, this unmanaged function:
bool fooFunction(int firstParameter, int secondParameter);
If you want to make it visible to managed code you have to wrap (as first, simple, step) it into a managed class:
public ref class MyClass abstract sealed
{
public:
static bool Foo(int firstParameter, int secondParameter)
{
return fooFunction(firstParameter, secondParameter);
}
};
This is a simple exam, if you have to interop with complex type you may need to wrap them all. For example if you have to interop with a function that accepts a string you have to manage this. Usually I use a class like this:
ref class UnmanagedString sealed
{
public:
UnmanagedString(String^ content) : _content(content)
{
_unicodePtr = _ansiPtr = IntPtr::Zero;
}
~UnmanagedString()
{
Free();
}
operator LPWSTR()
{
if (_content == nullptr)
return NULL;
Free();
_unicodePtr = Marshal::StringToHGlobalUni(_content);
return reinterpret_cast<LPWSTR>(_unicodePtr.ToPointer());
}
operator LPCSTR()
{
if (_content == nullptr)
return NULL;
Free();
_ansiPtr = Marshal::StringToHGlobalAnsi(_content);
return reinterpret_cast<LPCSTR>(_ansiPtr.ToPointer());
}
virtual System::String^ ToString() override
{
return _content;
}
virtual int GetHashCode() override
{
return _content->GetHashCode();
}
virtual bool Equals(Object^ obj) override
{
return _content->Equals(obj);
}
private:
IntPtr _unicodePtr, _ansiPtr;
String^ _content;
void Free()
{
if (_unicodePtr != IntPtr::Zero)
{
Marshal::FreeHGlobal(_unicodePtr);
_unicodePtr = IntPtr::Zero;
}
if (_ansiPtr != ntPtr::Zero)
{
Marshal::FreeHGlobal(_ansiPtr);
_ansiPtr = IntPtr::Zero;
}
}
};
Using this class you can call a function like void foo(LPCSTR pszText) with foo(UnamangedString(myManagedString)). More complex are the calls you have to do and more code you have to write to interop between them.
Note: simply exposing a 1:1 managed interface to unmanaged functions will make your C# code harder to read, I suggest you write a true OO interface to hide underlying implementation.
The types you created in your C++ dll are still native. You need to explicitly declare them as managed types. For example:
ref class SomeType { }
declares a managed class (note the ref keyword). It's not quite that easy though. Your native code won't get magically converted to managed code (a few basic data types like int are, but not things like std::string). If you really want to take full advantage of C++/CLI for interop, you should take the time to learn about the syntax differences.
I am trying to make a callback interface between C# and Java using JNA.
C# <--CLI--> Visual C++ 2010 <--JNA--> Java
Between Java and C++ I am using unmanaged structures to get callback functionality. In C++ I am trying to wrap structure, that has callback pointers, into managed object.
Between Java and C++ everything works until I'm trying to use gcroot for managed object generation in unmanaged code.
UPDATE it even fails without gcroot. Just with "Logger^ logger = gcnew Logger(logStruct);"
My current solution is as follows:
Java
LoggerStruct.java
package jnatest;
import com.sun.jna.Callback;
import com.sun.jna.Structure;
import java.util.logging.Logger;
public class LoggerStruct extends Structure {
private Logger logger;
public interface GetLevelCallback extends Callback {
int callback();
}
public GetLevelCallback getLevel;
public LoggerStruct(Logger log) {
super();
this.log = log;
getLevel = new GetLevelCallback() {
public int callback() {
return logger.getLevel().intValue();
}
}
setFieldOrder(new String[] {"getLevel"});
}
}
ITestLib.java
package jnatest;
import com.sun.jna.Library;
import com.sun.jna.Native;
public interface ITestLib extends Library {
ITestLib INSTANCE = (ITestLib) Native.loadLibrary("JNATestC", ITestLib.class);
int callbackTest(LoggerStruct logStruct);
}
Main.java
package jnatest;
import com.sun.jna.NativeLibrary;
import java.util.logging.Logger;
import java.util.logging.FileHandler;
public class MainClass {
public static void main(String[] args) throws Exception {
NativeLibrary.addSearchPath("JNATestC", "C:\\JNATest");
Logger log = Logger.getLogger("Test");
FileHandler fileTxt = new FileHandler("Logging.txt");
log.addHandler(fileTxt);
LoggerStruct logStruct = new LoggerStruct(log);
ITestLib.INSTANCE.callbackTest(logStruct);
}
}
C++
JNATestC.h
#pragma once
extern "C" {
struct LoggerStruct {
int (*getLevel)();
}
__declspec(dllexport) void callbackTest(LoggerStruct * logStruct);
}
namespace JnaWrapperTypes {
public ref class Logger { // "public ref" because I have to use it in C# as well
private:
LoggerStruct * logStruct;
public:
Logger(LoggerStruct * logStruct);
~Logger() {}
int getLevel();
};
}
JNATestC.cpp
#include "stdafx.h"
#include <vcclr.h>
#include "JNATestC.h"
namespace JnaWrapperTypes {
Logger::Logger(LoggerStruct * logStruct) {
this->logStruct = logStruct;
}
Logger::getLevel() {
return logStruct->getLevel();
}
}
using namespace JnaWrapperTypes;
using namespace StaticCSharpNamespace; // Just an example. Not existing C# lib.
extern "C" {
__declspec(dllexport) void callbackTest(LoggerStruct * logStruct) {
int level = logStruct->getLevel();
gcroot<Logger^> logger = gcnew Logger(logStruct); // IF I ADD "gcroot" FOR "Logger" THEN WHOLE INVOKE FAILS
level = logger->getLevel();
StaticCSharpClass::staticMethod(logger); // I want to pass Managed object to C# later
gcroot<System::String^> str = gcnew System::String(""); // This doesn't generate error
}
}
I wrote these on the fly. I hope these validate as well.
What am I doing wrong? For example if I use...
gcroot<System::String^> str = gcnew System::String("");
...everything works just fine.
Is there another way to pass managed object to C#?
Log for this error LOG
UPDATE
It seems that anykind of my own Class usage will head me to failure.
UPDATE
Anykind of my own Managed object or function use heads me to failure.
UPDATE
StaticCSharpClass::staticMethod(); fails as well. Looks like all operations related to Managed objects fail.
UPDATE
If I invoke the same method from .NET, everything works fine.
just to make this problem more findable
Internal Error (0xe0434352)
Should have googled for error "Internal Error (0xe0434352)".
http://jira.talendforge.org/browse/TDI-19427
It leads to point that I have to register the dll for GAC (Global Assembly Cache), because Java searches only GAC and Application Base directories for dll. And because Java.exe paths aren't configurable.
== Solution ==
Use post build event to register assembly for GAC:
gacutil /i "$(TargetPath)"
Great monologue! =)