I've been reading multiple examples from here and other sources on how to accomplish this. Recently I followed this specific example which had been linked to in multiple other similar questions.
https://dorodnic.com/blog/2014/12/10/calling-cpp-by-example/
However even when directly importing this project from github it still presents an error in the C# project where it cannot reference the the C++ CLI code. "The type or namespace could not be found(are you missing a using directive or an assembly reference)". This is the same error I find while following other examples myself. Could anyone please explain to me why this fails and/or suggest steps to fix it?
Edit: adding code here to save time.
//Logic.h
#pragma once
namespace MeaningOfLife
{
namespace Cpp
{
// This is our native implementation
// It's marked with __declspec(dllexport)
// to be visible from outside the DLL boundaries
class __declspec(dllexport) Logic
{
public:
int Get() const; // That's where our code goes
};
}
}
//Logic.cpp
#include "Logic.h"
int MeaningOfLife::Cpp::Logic::Get() const
{
return 42; // Really, what else did you expect?
}
//Logic.h CLI
#pragma once
namespace MeaningOfLife
{
namespace Cpp
{
// First a Forward Declaration to Cpp::Logic class:
class Logic; // This allows us to mention it in this header file
// without actually including the native version of Logic.h
namespace CLI
{
// Next is the managed wrapper of Logic:
public ref class Logic
{
public:
// Managed wrappers are generally less concerned
// with copy constructors and operators, since .NET will
// not call them most of the time.
// The methods that do actually matter are:
// The constructor, the "destructor" and the finalizer
Logic();
~Logic();
!Logic();
int Get();
void Destroy();
static void InitializeLibrary(System::String^ path);
private:
// Pointer to our implementation
Cpp::Logic* _impl;
};
}
}
}
//Logic.cpp CLI
#include "Logic.h"
#include "..\MeaningOfLife.Cpp\Logic.h"
#include <string>
#include <Windows.h>
using namespace std;
MeaningOfLife::Cpp::CLI::Logic::Logic()
: _impl(new Cpp::Logic())
// Allocate some memory for the native implementation
{
}
int MeaningOfLife::Cpp::CLI::Logic::Get()
{
return _impl->Get(); // Call native Get
}
void MeaningOfLife::Cpp::CLI::Logic::Destroy()
{
if (_impl != nullptr)
{
delete _impl;
_impl = nullptr;
}
}
MeaningOfLife::Cpp::CLI::Logic::~Logic()
{
// C++ CLI compiler will automaticly make all ref classes implement IDisposable.
// The default implementation will invoke this method + call GC.SuspendFinalize.
Destroy(); // Clean-up any native resources
}
MeaningOfLife::Cpp::CLI::Logic::!Logic()
{
// This is the finalizer
// It's essentially a fail-safe, and will get called
// in case Logic was not used inside a using block.
Destroy(); // Clean-up any native resources
}
string ManagedStringToStdString(System::String^ str)
{
cli::array<unsigned char>^ bytes = System::Text::Encoding::ASCII->GetBytes(str);
pin_ptr<unsigned char> pinned = &bytes[0];
std::string nativeString((char*)pinned, bytes->Length);
return nativeString;
}
void MeaningOfLife::Cpp::CLI::Logic::InitializeLibrary(System::String^ path)
{
string nativePath = ManagedStringToStdString(path);
LoadLibrary(nativePath.c_str()); // Actually load the delayed library from specific location
}
//C#
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace MeaningOfLife.WPF
{
using Cpp.CLI;
using Microsoft.Win32;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var fileOpenDialog = new OpenFileDialog
{
CheckFileExists = true,
Filter = "Native Library|MeaningOfLife.Cpp.dll",
InitialDirectory = Environment.CurrentDirectory
};
var result = fileOpenDialog.ShowDialog(this);
if (result.HasValue && result.Value)
{
Logic.InitializeLibrary(fileOpenDialog.FileName);
using (var wrapper = new Logic())
{
MessageBox.Show("The answer is " + wrapper.Get());
}
}
}
}
}
The error occurs at the using Cpp.CLI and then at the calls to Logic because of that.
Just as a follow up I also tried this example and received the same error in the comparable place in the code.
https://www.red-gate.com/simple-talk/dotnet/net-development/creating-ccli-wrapper/
This was solved by:
A)removing and adding back the WPF portion of the application
B)setting the "Copy Local" property of the CLI reference to true.
The reason I'm following these examples is to learn more about this process due to lack of knowledge. I do not think this is the real answer but it did solve my immediate issue.
Related
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;
};
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 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.
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! =)
C# methods cannot be called directly in Java using JNI due to different reasons. So first we have to write a wrapper for C# using C++ then create the dll and use it through JNI in Java.
I have problem in calling C# code in C++. I'm adding C# .netmodule file to a C++ project. Code is pasted below. Please guide me if i'm doing anything wrong.
This is my managed C++ class UsbSerialNum.h:
#using <mscorlib.dll>
#include <iostream>
#using "UsbSerialNumberCSharp.netmodule"
using namespace std;
using namespace System;
public __gc class UsbSerialNum
{
public:
UsbSerialNumberCSharp::UsbSerialNumberCSharp __gc *t;
UsbSerialNum() {
cout<<"Hello from C++";
t = new UsbSerialNumberCSharp::UsbSerialNumberCSharp();
}
void CallUsbSerialNumberCSharpHello() {
t->hello();
}
};
C# UsbSerialNumberCSharp.cs file from which i've created the .netmodule file:
using System.Collections.Generic;
using System.Text;
namespace UsbSerialNumberCSharp
{
public class UsbSerialNumberCSharp
{
public UsbSerialNumberCSharp(){
Console.WriteLine("hello");
}
public static void hello()
{
Console.WriteLine("hello");
}
public void helloCSharp ()
{
Console.WriteLine("helloCSharp");
}
}
}
Here is my main makeDLL.cpp file from which makeDLL.dll is created:
#include "jni.h"
#include <iostream>
// This is the java header created using the javah -jni command.
#include "testDLL.h"
// This is the Managed C++ header that contains the call to the C#
#include "UsbSerialNum.h"
using namespace std;
JNIEXPORT void JNICALL Java_testDLL_hello
(JNIEnv *, jobject) {
// Instantiate the MC++ class.
UsbSerialNum* serial = new UsbSerialNum();
serial->CallUsbSerialNumberCSharpHello();
}
Here is my java class:
public class testDLL {
static {
System.loadLibrary("makeDLL");
}
/**
* #param args
*/
public static void main (String[] args) {
// new testDLL().GetUSBDevices("SCR3", 100);
new testDLL().hello();
}
public native void hello();
}
EDIT:
If i simply ignore the call to UsbSerial.h in my main file i.e. use simple C++ then my code is working fine in Java. Basically C++ managed class is not working properly.
Please guide me. Thanks.
It would be useful to know what you need this interoperability for exactly. In any case, you should look into IKVM; alternatively you can (as has been suggested for a similar problem) use COM as a bridge: expose the C#/CLR as a COM interface and then use com4j in Java.
You can avoid C# and still can query WMI using C++ only. See Using WMI to call method on objects