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;
};
Related
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 have the following setup in C#:
public delegate void CallbackDelegate(string message);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public static extern void setCallback(CallbackDelegate aCallback);
public void testCallbacks()
{
System.Console.Write("Registering C# callback...\n");
setCallback(callback01);
}
public void callback01(string message)
{
System.Console.Write("callback 01 called: " + message + "\n");
}
And this in C++ (the function is registered correctly via mono_add_internal_call ):
typedef void (*CallbackFunction)(const char*);
void setCallback(MonoDelegate* delegate)
{
// How to convert the MonoDelegate to a proper function pointer?
// So that I can call it like func("test");
}
The C++-function is called and something is passed to the delegate variable.
But what now?
I looked around and found the function "mono_delegate_to_ftnptr" mentioned a few times, and from those examples it seems to be exactly what I need.
However, this function simply does not seem to exist in my distribution of mono (4.6), so I can only guess it does not exist any more.
I also found a few examples of how to do this with PInvoke. Which is something I do not want to use - since InternalCall is much faster for my purpose.
Of course, if PInvoke would be the only way, so be it, but I doubt that.
In the end, I am really at a loss at how to proceed from here.
After some more hours of digging I finally found a (the?) solution.
Basically, what works for the PInvoke approach works here as well, you can pass a function pointer instead of a delegate from C# to C(++).
I'd prefer a solution where you can pass a delegate directly, but you can always add some wrapper code in C# to at least make it look like that.
Solution:
C#:
public delegate void CallbackDelegate(string message);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public static extern void setCallback(IntPtr aCallback);
private CallbackDelegate del;
public void testCallbacks()
{
System.Console.Write("Registering C# callback...\n");
del = new CallbackDelegate(callback01);
setCallback(Marshal.GetFunctionPointerForDelegate(del));
System.Console.Write("Calling passed C++ callback...\n");
}
public void callback01(string message)
{
System.Console.Write("callback 01 called. Message: " + message + "\n");
}
C++:
typedef void (*CallbackFunction)(MonoString*);
void setCallback(CallbackFunction delegate)
{
std::cout << &delegate << std::endl;
delegate(mono_string_new(mono_domain_get(), "Test string set in C++"));
}
Watch out, though: You need to keep the delegate around in C# somehow (which is why I assigned it to "del") or it will be caught by the GC and your callback will become invalid.
It makes sense, of course, but I feel this is easy to forget in this case.
You can pass a function pointer as a parameter in C++ to C# using intptr_t.
MSDN isn't accurate, below code works:
In C++:
static void func(int param)
{
//...
}
void other_func()
{
ptr->SetCallback( reinterpret_cast<intptr_t>(func));
}
In C#:
public static mydelegatetype somefunc = null;
public void SetCallback(IntPtr function_pointer)
{
somefunc = (mydelegatetype)
Marshal.GetDelegateForFunctionPointer(function_pointer, typeof(mydelegatetype));
}
Calling C++/Qt classes through C++/CLI wrapper is a like a walk in the park.
But I'm stuck mapping C++/Qt signals to C# events.
I tried to combine some available how-tos/answers but did not get any working result:
How to map Qt Signal to Event in Managed C++ (C++/CLI)
Calling managed code from unmanaged code and vice-versa
and some other not so directly related...
The problem here is, that these how-tos/answers are quite old. I am currently working with Qt5.5 (soon 5.6) and .NET 4.6. I tried to adapt everything to current state of the art but may have failed.
It may be, that I can't see the forest because of too much trees, so I would like to ask for a working example which accomplishes the task with current framework versions, so that I can spot the differences and learn from the mistakes.
[edit]
You can checkout this github repo, for this source. The parts for QtSignal to C#Event are commented out to have this code in a working state.
Github repo: https://github.com/qwc/QtSignalToCSharpEvent
For those who still want to read everything without playing around... read on...
Here my current non-working code:
So I have a class in pure Qt5
#ifndef PUREQT_H
#define PUREQT_H
#include <QObject>
#include <QString>
#include "PureQt_global.h"
class PUREQTSHARED_EXPORT PureQt : public QObject {
Q_OBJECT
public:
PureQt(QString name);
~PureQt(){}
QString getSomeVar();
void someFunc(const QString & string);
public slots:
signals:
void someFuncWasCalled(const QString &string);
private:
QString someVar;
};
#endif // PUREQT_H
With the following rather simple implementation:
#include "PureQt.h"
PureQt::PureQt(QString name) {
this->someVar = "ctor("+name+")";
}
QString PureQt::getSomeVar() {
return this->someVar;
}
void PureQt::someFunc(const QString &string) {
this->someVar += "someFunc("+string+")";
emit someFuncWasCalled(this->someVar);
}
Then I've implemented a managed wrapper with C++/CLI to be able to call the unmanaged code from C#. Be aware that I've already tried to add code to get signal to event management.
#pragma once
#include "conversion.h"
#include "../pureqt/PureQt.h"
#include "SignalProxy.h"
using namespace System;
using namespace System::Collections::Generic;
using namespace System::Runtime::InteropServices;
namespace ManagedCppQtSpace {
// different variants... from tinkering around.
delegate void someFuncWasCalled(String^);
delegate void someFuncWasCalledU(QString str);
[StructLayoutAttribute(LayoutKind::Sequential)]
public ref struct DelegateWrapper {
[MarshalAsAttribute(UnmanagedType::FunctionPtr)]
someFuncWasCalledU^ delegate;
};
public ref class ManagedCppQt
{
public:
ManagedCppQt(String^ name){
pureQtObject = new PureQt(StringToQString(name));
proxy = new SignalProxy(pureQtObject);
wrapper = gcnew DelegateWrapper();
wrapper->delegate = gcnew someFuncWasCalledU(this, ManagedCppQt::signalCallback);
signalCallbackProxy callbackproxy;
Marshal::StructureToPtr(wrapper, callbackproxy, false); // currently im stuck here with a compile error, but the problem may lie somewhere else...
proxy->setCallback(callbackproxy);
};
~ManagedCppQt(){
delete pureQtObject;
};
event someFuncWasCalled ^ someFuncCalled;
void someFunc(String^ string){
pureQtObject->someFunc(StringToQString(string));
};
String^ getSomeString() {
return QStringToString(pureQtObject->getSomeVar());
}
void signalCallback(QString str) {
someFuncCalled(QStringToString(str));
}
DelegateWrapper ^ wrapper;
private:
PureQt * pureQtObject;
SignalProxy * proxy;
};
}
So to link signal and slot handling from Qt with a callback which is able to raise an event in managed code some will need a proxy class when there's no option to change the basis code (because it's also used in other unmanaged C++ projects).
#ifndef SIGNALPROXY_H
#define SIGNALPROXY_H
#include <QObject>
#include "../pureqt/PureQt.h"
typedef void (*signalCallbackProxy) (QString str);
class SignalProxy : public QObject
{
Q_OBJECT
public:
explicit SignalProxy(PureQt* pqt);
~SignalProxy();
void setCallback(signalCallbackProxy callback);
signals:
public slots:
void someFuncSlot(QString str);
private:
PureQt* pureQt;
signalCallbackProxy scallback;
};
#endif // SIGNALPROXY_H
With implementation:
#include "SignalProxy.h"
SignalProxy::SignalProxy(PureQt* pqt){
pureQt = pqt;
this->connect(pureQt, SIGNAL(PureQt::someFuncWasCalled(QString)), this, SLOT(someFuncSlot(QString)));
}
SignalProxy::~SignalProxy()
{}
void SignalProxy::setCallback(signalCallbackProxy callback){
this->scallback = callback;
}
void SignalProxy::someFuncSlot(QString str){
if(this->scallback != NULL)
this->scallback(str);
}
So. Now, how to correctly link these two worlds, Qt signals -> managed .NET events?
I've also tried some simple approaches, which lead to compile errors, like:
QObject::connect(pureQtObject, &PureQt::someFuncWasCalled, &MangagedCppQtSpace::ManagedCppQt::signalCallback);
instead of the proxy class, or with a lambda function:
QObject::connect(pureQtObject, &PureQt::someFuncWasCalled, [] (QString str) {
signalCallback(str);// or ManagedCppQt::signalCallback, but for this the method has to be static, and it isn't possible to raise events from static methods...
}
Problem here is mixing Qt with C++ CLI. To have functional signal and slots you Qt needs process header files to generate own meta data. Problem is that tool will be unable to understand C++CLI features.
To overcome this problem first you have to do fallback to C++ interfaces and there perform safely C++ CLI operations.
So you need extra class like this which doesn't know .net and creates bridge to standard C++:
class PureQtReceiverDelegate { // this should go to separate header file
virtual void NotifySomeFunc(const char *utf8) = 0;
};
class PureQtReceiver : public QObject {
Q_OBJECT
public:
PureQtReceiver(PureQtReceiverDelegate *delegate, PureQt *parent)
: QObject(parent)
, mDelegate(delegate)
{
bool ok = connect(parent, SIGNAL(PureQt::someFuncWasCalled(QString)),
this, SLOT(someFuncReceiver(QString)));
Q_ASSERT(ok);
}
public slots:
void someFuncReceiver(const QString & string)
{
delegate->NotifySomeFunc(string.toUtf8().data());
}
private:
PureQtReceiverDelegate *delegate;
};
Now your C++CLI class should implement this PureQtReceiverDelegate and there convert string to .net version and post notification.
Note you can/should forward declare Qt specific classes in C++CLI header file.
Above solution is good if you are using Qt4 od don't to use C++11.
If you are using Qt 5 and have C++11 available than there is more handy solution: you can use lambda expression in when making a connection to a QObject. So your ManagedCppQt can look like this:
header:
#pragma once
#include "conversion.h"
using namespace System;
using namespace System::Collections::Generic;
using namespace System::Runtime::InteropServices;
// forward declaration
class PureQt;
namespace ManagedCppQtSpace {
delegate void someFuncWasCalled(String^);
public ref class ManagedCppQt
{
public:
ManagedCppQt(String^ name);
~ManagedCppQt();
event someFuncWasCalled ^ someFuncCalled;
void someFunc(String^ string);
String^ getSomeString();
void signalCallback(QString str);
private:
PureQt * pureQtObject;
};
}
in cpp:
#include "../pureqt/PureQt.h"
using namespace ManagedCppQtSpace;
ManagedCppQt:ManagedCppQt(String^ name) {
pureQtObject = new PureQt(QStringFromString(name));
QObject::connect(pureQtObject, &PureQt::someFuncWasCalled,
[this](const QString &string){
if (this->someFuncCalled) {
String^ s = StringFromQString(string);
this->someFuncCalled(s);
}
});
}
ManagedCppQt::~ManagedCppQt(){
delete pureQtObject;
}
This is much easier, faster and easier to maintain.
I've been trying to create a c# wrapper for a c++ class I have created. I've looked around on how to do this but none of the examples seem to use classes and objects. I have the following code in c++:
#ifndef PORTAUDIOMANAGER_H
#define PORTAUDIOMANAGER_H
#include "portaudio.h"
#include "pa_asio.h"
class PortAudioManager
{
public:
PortAudioManager();
virtual ~PortAudioManager();
static PortAudioManager* createObject();
void openStream();
void dispose(PortAudioManager* obj);
void stopStream();
typedef struct
{
float left_phase;
float right_phase;
}
paTestData;
private:
void* stream;
paTestData data;
static PortAudioManager* audioManager;
};
#endif
The createObject method creates a new object of PortAudioManager and registers it to the audioManager pointer. The dispose method acts as the destructor (since I thought you can't use the constructor and destructor in C#).
So how it should be used is simply like this:
PortAudioManager manager = PortAudioManager.createObject();
manager.openStream();
How would I go about creating a system that this can be used in c#?
If you need more information, let me know.
Create a new class library project and compile with the /clr flag. Given the native C++ class presented above, add the following C++/CLI class to wrap your native class:
public ref class PortAudioManagerManaged
{
private:
PortAudioManagerManaged(PortAudioManager* native)
: m_native(native) { }
public:
PortAudioManagerManaged()
: m_native(new PortAudioManager) { }
// = IDisposable.Dispose
virtual ~PortAudioManagerManaged() {
this->!PortAudioManagerManaged();
}
// = Object.Finalize
!PortAudioManagerManaged() {
delete m_native;
m_native = nullptr;
}
static PortAudioManagerManaged^ CreateObject()
{
return gcnew PortAudioManagerManaged(PortAudioManager::createObject());
}
void OpenStream()
{
if (!m_native)
throw gcnew System::ObjectDisposedException(GetType()->FullName);
m_native->openStream();
}
void StopStream()
{
if (!m_native)
throw gcnew System::ObjectDisposedException(GetType()->FullName);
m_native->stopStream();
}
private:
PortAudioManager* m_native;
};
In your C# project, add a reference to your C++/CLI class library.
using (PortAudioManagerManaged manager = PortAudioManagerManaged.CreateObject())
{
manager.OpenStream();
}
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! =)