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();
}
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'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.
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! =)
I have created a C++ Dll project which contains a class "myCppClass" and tried to Dll export it using the following code as described by:
http://msdn.microsoft.com/en-us/library/a90k134d(v=vs.80).aspx
class __declspec(dllexport) CExampleExport : //public CObject
{ ... class definition ... };
I have omitted the "public CObject" as that requires afx.h and implies it is an MFC Dll. I am not sure if this is a good thing or not but it differed from the DLL project default settings.
From the above linked documentation I am led to believe that all "public functions and member variables" are available for import. How do I accomplish this in C#? Can simply instantiate the class?
Edit: I just realized that the Title of the post may be misleading. The emphasis should be on DllImport-ing from C# and ensuring that I followed the documentation properly in C++
C# cannot directly import C++ classes (which are effectively name-mangled C interfaces).
Your options are exposing the class via COM, creating a managed wrapper using C++/CLI or exposing a C-style interface. I would recommend the managed wrapper, since this is easiest and will give the best type safety.
A C-style interface would look something like this (warning: untested code):
extern "C" __declspec(dllexport)
void* CExampleExport_New(int param1, double param2)
{
return new CExampleExport(param1, param2);
}
extern "C" __declspec(dllexport)
int CExampleExport_ReadValue(void* this, int param)
{
return ((CExampleExport*)this)->ReadValue(param)
}
A C++/CLI-style wrapper would look like this (warning: untested code):
ref class ExampleExport
{
private:
CExampleExport* impl;
public:
ExampleExport(int param1, double param2)
{
impl = new CExampleExport(param1, param2);
}
int ReadValue(int param)
{
return impl->ReadValue(param);
}
~ExampleExport()
{
delete impl;
}
};
As far as I know, C# can only interop with COM interfaces. Lucky enough it doesn't need to be a full blown COM object with registry, it can be any plain old C++ class implementing IUnknown.
So do something like this in C++:
#include <Windows.h>
// Generate with from VisualStudio Tools/Create Guid menu
static const GUID IID_MyInterface =
{ 0xefbf7d84, 0x3efe, 0x41e0, { 0x95, 0x2e, 0x68, 0xa4, 0x4a, 0x3e, 0x72, 0xca } };
struct MyInterface: public IUnknown
{
// add your own functions here
// they should be virtual and __stdcall
STDMETHOD_(double, GetValue)() = 0;
STDMETHOD(ThrowError)() = 0;
};
class MyClass: public MyInterface
{
volatile long refcount_;
public:
MyClass(): refcount_(1) { }
STDMETHODIMP QueryInterface(REFIID guid, void **pObj) {
if(pObj == NULL) {
return E_POINTER;
} else if(guid == IID_IUnknown) {
*pObj = this;
AddRef();
return S_OK;
} else if(guid == IID_MyInterface) {
*pObj = this;
AddRef();
return S_OK;
} else {
// always set [out] parameter
*pObj = NULL;
return E_NOINTERFACE;
}
}
STDMETHODIMP_(ULONG) AddRef() {
return InterlockedIncrement(&refcount_);
}
STDMETHODIMP_(ULONG) Release() {
ULONG result = InterlockedDecrement(&refcount_);
if(result == 0) delete this;
return result;
}
STDMETHODIMP_(DOUBLE) GetValue() {
return 42.0;
}
STDMETHODIMP ThrowError() {
return E_FAIL;
}
};
extern "C" __declspec(dllexport) LPUNKNOWN WINAPI CreateInstance()
{
return new MyClass();
}
And on the C# side you do something like this:
[ComImport]
[Guid("EFBF7D84-3EFE-41E0-952E-68A44A3E72CA")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface MyInterface
{
[PreserveSig] double GetValue();
void ThrowError();
}
class Program
{
[DllImport("mylib.dll")]
static extern MyInterface CreateInstance();
static void Main(string[] args)
{
MyInterface iface = CreateInstance();
Console.WriteLine(iface.GetValue());
try { iface.ThrowError(); }
catch(Exception ex) { Console.WriteLine(ex); }
Console.ReadKey(true);
}
}
You can do pretty much anything you want this way, as long as the communication between C++ and C# goes through the virtual interface.
You cannot create a C++ class instance through pinvoke from C#. This is a troublesome implementation detail, only the C++ compiler knows how much memory needs to be allocated and when and how to properly call the constructor and destructor. The object size is by far the hardest nut to crack, there is no way whatsoever to make that reliable.
If you cannot flatten the C++ class out into static methods then you need to write a managed wrapper. That's done with the C++/CLI language, you'd write a "ref class" that has the unmanaged class object stored as a pointer, created in the constructor and deleted in the destructor and finalizer.
Actually, you can refer to the mangled names directly, using the EntryPoint property of the DllImport attribute. See this answer for more details.
C# and C++ are NOT ABI compatible like C++ and Delphi, so you cannot export virtual class members (methods) and declare them purely virtual on the calling side an invoke them, because C# cannot handle vtbl's of C++ objects.
I would suggest you to wrap your C++ classes by COM, so you have another positive side effect, that other COM compatible languages can use your classes too.