Managed C++ to form a bridge between c# and C++ - c#

I'm a bit rusty, actually really rusty with my C++. Haven't touched it since Freshman year of college so it's been a while.
Anyway, I'm doing the reverse of what most people do. Calling C# code from C++. I've done some research online and it seems like I need to create some managed C++ to form a bridge. Use __declspec(dllexport) and then create a dll from that and use the whole thing as a wrapper.
But my problem is - I'm really having a hard time finding examples. I found some basic stuff where someone wanted to use the C# version to String.ToUpper() but that was VERY basic and was only a small code snippet.
Anyone have any ideas of where I can look for something a bit more concrete? Note, I do NOT want to use COM. The goal is to not touch the C# code at all.

Create a new C++/CLI project in visual studio and add a reference to your C# dll. Assume we have a C# dll called DotNetLib.dll with this class in:
namespace DotNetLib
{
public class Calc
{
public int Add(int a, int b)
{
return a + b;
}
}
}
Now add a CLR C++ class to your C++/CLI project:
// TestCPlusPlus.h
#pragma once
using namespace System;
using namespace DotNetLib;
namespace TestCPlusPlus {
public ref class ManagedCPlusPlus
{
public:
int Add(int a, int b)
{
Calc^ c = gcnew Calc();
int result = c->Add(a, b);
return result;
}
};
}
This will call C# from C++.
Now if needed you can add a native C++ class to your C++/CLI project which can talk to the CLR C++ class:
// Native.h
#pragma once
class Native
{
public:
Native(void);
int Add(int a, int b);
~Native(void);
};
and:
// Native.cpp
#include "StdAfx.h"
#include "Native.h"
#include "TestCPlusPlus.h"
Native::Native(void)
{
}
Native::~Native(void)
{
}
int Native::Add(int a, int b)
{
TestCPlusPlus::ManagedCPlusPlus^ c = gcnew TestCPlusPlus::ManagedCPlusPlus();
return c->Add(a, b);
}
You should be able to call the Native class from any other native C++ dll's as normal.
Note also that Managed C++ is different to and was superceeded by C++/CLI. Wikipedia explains it best:
http://en.wikipedia.org/wiki/C%2B%2B/CLI

While lain beat me to writing an example, I'll post it anyhow just in case...
The process of writing a wrapper to access your own library is the same as accessing one of the standard .Net libraries.
Example C# class code in a project called CsharpProject:
using System;
namespace CsharpProject {
public class CsharpClass {
public string Name { get; set; }
public int Value { get; set; }
public string GetDisplayString() {
return string.Format("{0}: {1}", this.Name, this.Value);
}
}
}
You would create a managed C++ class library project (example is CsharpWrapper) and add your C# project as a reference to it. In order to use the same header file for internal use and in the referencing project, you need a way to use the right declspec. This can be done by defining a preprocessor directive (CSHARPWRAPPER_EXPORTS in this case) and using a #ifdef to set the export macro in your C/C++ interface in a header file. The unmanaged interface header file must contain unmanaged stuff (or have it filtered out by the preprocessor).
Unmanaged C++ Interface Header file (CppInterface.h):
#pragma once
#include <string>
// Sets the interface function's decoration as export or import
#ifdef CSHARPWRAPPER_EXPORTS
#define EXPORT_SPEC __declspec( dllexport )
#else
#define EXPORT_SPEC __declspec( dllimport )
#endif
// Unmanaged interface functions must use all unmanaged types
EXPORT_SPEC std::string GetDisplayString(const char * pName, int iValue);
Then you can create an internal header file to be able to include in your managed library files. This will add the using namespace statements and can include helper functions that you need.
Managed C++ Interface Header file (CsharpInterface.h):
#pragma once
#include <string>
// .Net System Namespaces
using namespace System;
using namespace System::Runtime::InteropServices;
// C# Projects
using namespace CsharpProject;
//////////////////////////////////////////////////
// String Conversion Functions
inline
String ^ ToManagedString(const char * pString) {
return Marshal::PtrToStringAnsi(IntPtr((char *) pString));
}
inline
const std::string ToStdString(String ^ strString) {
IntPtr ptrString = IntPtr::Zero;
std::string strStdString;
try {
ptrString = Marshal::StringToHGlobalAnsi(strString);
strStdString = (char *) ptrString.ToPointer();
}
finally {
if (ptrString != IntPtr::Zero) {
Marshal::FreeHGlobal(ptrString);
}
}
return strStdString;
}
Then you just write your interface code that does the wrapping.
Managed C++ Interface Source file (CppInterface.cpp):
#include "CppInterface.h"
#include "CsharpInterface.h"
std::string GetDisplayString(const char * pName, int iValue) {
CsharpClass ^ oCsharpObject = gcnew CsharpClass();
oCsharpObject->Name = ToManagedString(pName);
oCsharpObject->Value = iValue;
return ToStdString(oCsharpObject->GetDisplayString());
}
Then just include the unmanaged header in your unmanaged project, tell the linker to use the generated .lib file when linking, and make sure the .Net and wrapper DLLs are in the same folder as your unmanaged application.
#include <stdlib.h>
// Include the wrapper header
#include "CppInterface.h"
void main() {
// Call the unmanaged wrapper function
std::string strDisplayString = GetDisplayString("Test", 123);
// Do something with it
printf("%s\n", strDisplayString.c_str());
}

Related

How to write wrapper classes for mapping Qt Signals to C# events (through C++/CLI)

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.

Inheriting from native C++ in C# and passing the inherit class backward to c++

I've an engine in a native C++ dll and I need to use it in a C# project.
I'm totally new on doing that, but I've been googling for hours and now I know more or less how to achieve it. I know I've to write a C# or C++/CLI wrapper class.
But I haven't found how to wrap some aspects of the dll C++ class.
The engine has class CEntity whith this important part:
class CEntity
{
void SendMsg(CEntity *receiver);
virtual void ReceiveMsg(MSG msg);
}
That works as follows:
A inherit class from CEntity overrides the ReceiveMsg function implementing what it whants, and Inheriting objects comunicate sending messages.
What I need is to use this functionality in C#: "Inheriting" from CEntity, overriding ReceiveMsg on a way that the C++ code can call it, and been able to send messages to other C# "Inheriting" CEntity objets throw the SendMsg C++ implementation.
Or in other words I need that the C++ unmanaged code calls a C# managed code. The C++ code is calling ReceiveMessage() and I need to "override" it or redirect that call into C# code.
Is a way of doing that whithout changing the dll? I can't acces directly the C++ code, but if needed I can ask for dll modifications. If not, what'll be the minimum dll modifications?
Thanks a lot
It is tricky:
ILogger.h
#pragma once
using namespace System;
namespace AlPDFGenV4
{
public interface class ILogger
{
public:
virtual void Log( String^ ltxt ) = 0;
};
}
Then LRLog.h
#pragma once
#include "CMSysString.h"
#include "CLRILogger.h"
#include <vcclr.h>
using namespace System;
class CNtvLogger;
namespace AlPDFGenV4
{
public ref class Logger
{
public:
Logger(void);
virtual ~Logger(void);
ILogger^ extlogger;
CNtvLogger *ntv;
void Log( String^ txt )
{
extlogger->Log( txt );
}
};
}
And LRLog.cpp
#include "StdAfx.h"
#include "LRLog.h"
using namespace AlPDFGenV4;
Logger::Logger(void)
{
ntv = new CNtvLogger;
ntv->clrlogger = this;
}
Logger::~Logger(void)
{
delete ntv;
}
class CNtvLogger : CMSysLogger
{
public:
gcroot<AlPDFGenV4::Logger ^> clrlogger;
protected:
void _InternalLog( LPCTSTR txt)
{
String ^str = gcnew String( txt );
clrlogger->Log( str );
}
public:
bool Init(void * obj)
{
return true;
}
};
Hope this helps.
In this example, the class Logger is a bridge tha allows to keep the link between the native logger (used by the native code) and the interface ILogger (used by managed code to pass the class that receives the log output).
You'll need a class like this in your C++/CLI, no need to change the dll with this approach.

Passing a C# string to C++/CLI does not show the string value in the CPP program

I want to call a C# function from C++ , via CLI/C++.
C# code
private string _text = " ";
public void setText(string text)
{
// _text = text;
_text = "HI World";
}
Ideally setText shall have the commented line only. The _text = "HI World" is an example.
public string getText()
{
return _text;
}
C++/CLI code
Header :
gcroot<Bridge> _managedObject;
virtual void setText(std::string text);
virtual std::string getText();
CPP file
std::string CStringBridge::getText()
{
// _managedObject = gcnew Bridge();
return (marshal_as(_managedObject->getText()));
}
void CStringBridge::setText(std::string text)
{
// _managedObject = gcnew Bridge();
_managedObject->setText(gcnew System::String(text.c_str()));
}
IStringBridgeWrapper* IStringBridgeWrapper::CreateInstance(void)
{
return ((IStringBridgeWrapper *)new CStringBridge());
}
Note : When I use the following code
virtual void setText(System::String^ text);
virtual System::String^ getText();
I get the following error 3395
*__declspec(dllexport) cannot be applied to a function with the __clrcall calling convention*
, and so I stuck to std::string
When I use the library from the C++/CLI code, and call from my C++ program, "Hi World" should be printed ; instead nothing gets printed
C++ console application
IStringBridgeWrapper *pBridge = IStringBridgeWrapper::CreateInstance();
pBridge->setText(std::string("I am here"));
pBridge->getText();
I think the string is not being properly passed .
Any ideas to solve it shall be appreciated.
EDIT
I have updated the code after the comments , yet nothing shows up.
gcroot creates a handle, but does not allocate memory for it. But as Bridge has no memory allocated , the application does not work.My code is in the same lines at the article here - http://www.codeproject.com/Articles/10020/Using-managed-code-in-an-unmanaged-application .
COM is your friend:
Create a an interface in C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace CsharpLibrary
{
// Since the .NET Framework interface and coclass have to behave as
// COM objects, we have to give them guids.
[Guid("56A868B1-0AD4-11CE-B03A-0020AF0BA770"),
InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IStringHolder
{
String GetText();
void SetText(String s);
}
}
Implement the C# interface:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace CsharpLibrary
{
[Guid("C6659361-1625-4746-931C-36014B146679")]
public class MyStringHolder : IStringHolder
{
String _text;
public String GetText()
{
return this._text;
}
public void SetText(String value)
{
_text = value;
}
}
}
Create and call your C# object from C++
#include <windows.h>
#include <stdio.h>
#pragma warning (disable: 4278)
// To use managed-code servers like the C# server,
// we have to import the common language runtime:
#import <mscorlib.tlb> raw_interfaces_only
#pragma warning (disable: 4278)
// To use managed-code servers like the C# server,
// we have to import the common language runtime:
#import <mscorlib.tlb> raw_interfaces_only
#import "..\CsharpLibrary\bin\Debug\CsharpLibrary.tlb" no_namespace named_guids
int main(int argc, char* argv[])
{
HRESULT hr = S_OK;
IStringHolder *pStringHolder = NULL;
//
// Initialize COM and create an instance of the InterfaceImplementation class:
//
CoInitialize(NULL);
hr = CoCreateInstance( __uuidof(MyStringHolder),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IStringHolder),
reinterpret_cast<void**>(&pStringHolder));
if(SUCCEEDED(hr))
{
_bstr_t sHelloWorld = SysAllocString( L"Hello, World" );
hr = pStringHolder->SetText(sHelloWorld);
SysFreeString(sHelloWorld);
}
//
// Be a good citizen and clean up COM
//
CoUninitialize();
return hr;
}
On the C# side you have to generate the type library and register the class by a post-build event:
generate the type library:
"$(FrameworkSDKDir)bin\NETFX 4.0 Tools\tlbexp.exe" "$(TargetPath)" /out:"$(TargetDir)$(TargetName).tlb"
register the class:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe "$(TargetPath)"
Enjoy!
I want to call a C# function from C++ , via CLI/C++.
Wait... you want to call a C++ function from C#, right? That's what C++/CLI is good for. Wrapping C++ code to be accessible in managed environments. If you really want to call C# code from C++, you should look into COM registering your C# code. If you use C++/CLI for this, your whole C++ program will be dragged into the .NET world and you could have used C# from the start.
In C++/CLI, your whole public class interface of ref (.NET) classes should consist of only managed types. That would be System::String^ instead of std::string.

Wrap a C struct for access in WPF application

for a project of mine, I'd like to wrap a C struct in order to use it in C# code.
My C++ algorithm returns a struct object and I'd like to see contained informations from C#/WPF code.
When I try "my way", I get an error message while compiling :
"'Wrapper.WrapperAlgo.createMediumMap()' is inaccessible due to its protection level"
my C# piece of code:
public partial class MainWindow : Window
{
unsafe public MainWindow()
{
WrapperAlgo algo = new WrapperAlgo();
square** map = (square**)algo.createMediumMap();
}
}
My wrapper
#ifndef __WRAPPER__
#define __WRAPPER__
#include "../CivilizationAlgo/mapalgo.h"
#pragma comment(lib, "../Debug/CivilizationAlgo.lib")
using namespace System;
namespace Wrapper {
public ref class WrapperAlgo {
private:
Algo* algo;
public:
WrapperAlgo(){ algo = Algo_new(); }
~WrapperAlgo(){ Algo_delete(algo); }
square** createSmallMap() { return algo->createSmallMap(); }
square** createMediumMap() { return algo->createMediumMap(); }
int computeFoo() { return algo->computeFoo(); }
};
}
#endif
My C++ algo.h with the struct I'd like to use in C#
#ifdef WANTDLLEXP
#define DLL _declspec(dllexport)
#define EXTERNC extern "C"
#else
#define DLL
#define EXTERNC
#endif
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <vector>
using namespace std;
struct square {
// 0: Mountain, 1: Plain, 2: Desert
int type;
// 0: No additionnal ressource, 1: Additionnal Iron, 2: Additionnal Food
int bonus;
// 0: free, 1: checked, 2: frozen
int state;
};
class DLL Algo {
public:
Algo() {}
~Algo() {}
square** createSmallMap();
square** createMediumMap();
int computeFoo();
};
Do you have any idea where i'm wrong ?
If I understand you well you are looking for a way to use non-managed library (like cpp/c library) in your C# code. If yes, then you should read about marshaling, for ex. here, here, or here.
Basics about including unmanaged code in C# app can be found on MSDN, or MSDN Magazine.
Hope it helps.

How to create managed object in unmanaged C++ while wrapping it for JNA between Java and C#?

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! =)

Categories