Calling managed C++ lib from C# passing values by reference - c#

I'm trying to set up a test project to figure out how to correctly wrap a native C++ lib in a managed C++ DLL, to then be called by a C# application.
For my test case I have 3 application:
CPP lib
I compile a library with a class containing four functions.
1) return a double value
2) return a string
3) take a double by reference, modify it.
4) take a string by reference, modify it.
cpp_lib.h
namespace cpp_lib
{
class CPPLibFunctions{
public:
CPPLibFunctions();
public:
static double getValue();
static void incrementValue(double& val);
static std::string getText();
static void changeText(std::string& txt);
};
}
cpp_lib.cpp
#include "cpp_lib.h"
#include <stdexcept>
namespace cpp_lib{
CPPLibFunctions::CPPLibFunctions(){};
double CPPLibFunctions::getValue(){
return 5.5;
}
void CPPLibFunctions::incrementValue(double& val){
val += 1.0;
}
std::string CPPLibFunctions::getText(){
return "success";
}
void CPPLibFunctions::changeText(std::string& txt){
txt += "_123";
}
}
Managed CPP lib
This is a managed C++ project compiled to a DLL, which wraps the functions from cpp_lib.
managed_cpp_lib.h
#include "cpp_lib.h"
#pragma once
#include <msclr\marshal_cppstd.h>
using namespace System;
namespace managed_cpp_lib {
System::String^ stdToSysString(std::string str){
return gcnew System::String(str.c_str());
}
public ref class ManagedCppFunctions
{
private:
cpp_lib::CPPLibFunctions * cppLibFunctions;
public:
ManagedCppFunctions(){
cppLibFunctions = new cpp_lib::CPPLibFunctions();
}
double getValue(){
return cppLibFunctions->getValue();
}
void incrementValue(double& val){
cppLibFunctions->incrementValue(val);
}
System::String^ getText(){
return stdToSysString(cppLibFunctions->getText());
}
void changeText(System::String^ txt){
//this does not work:
std::string txtstd = msclr::interop::marshal_as<std::string>(txt);
cppLibFunctions->changeText(txtstd);
txt = stdToSysString(txtstd);
}
};
}
csharp_app
Lastly, cssharp_app is a C# console application, which references managed_cpp_lib, and calls the functions.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using managed_cpp_lib;
namespace csharp_app
{
class Program
{
static void Main(string[] args)
{
managed_cpp_lib.ManagedCppFunctions managedCppFunctions = new managed_cpp_lib.ManagedCppFunctions();
//works:
double val = managedCppFunctions.getValue();
Console.WriteLine("\managedCppFunctions.getValue() = {0}", val);
//works:
string txt = managedCppFunctions.getText();
Console.WriteLine("\managedCppFunctions.getText() = {0}", txt);
//does not work:
/*managedCppFunctions.incrementValue(val);
Console.WriteLine("\managedCppFunctions.incrementValue(val) = {0}", val);*/
//the string is not modified:
managedCppFunctions.changeText(txt);
Console.WriteLine("\managedCppFunctions.changeText(txt) = {0}", txt);
Console.WriteLine("...");
}
}
}
The output of csharp_app is:
managedCppFunctions.getValue() = 5.5
managedCppFunctions.getText() = success
managedCppFunctions.changeText(txt) = success
So managedCppFunctions.getValue() and managedCppFunctions.getText() works.
managedCppFunctions.changeText(txt) does not modify the content of the string.
I'm not sure how to implement managedCppFunctions.incrementValue(val).
What is the correct way to pass a double and a string by reference, to then change their values using managed C++ and C#?

Related

Swig : How to get string in a callback

I achieved interoperability between C# & C++ using SWIG.
However, the C ++ to C # callback does not work properly.
If you pass "std::wstring" to the callback on the C++ side, it will be treated as a "string" on the C# side, but only the first character of the string passed on the C++ side will be stored.
My goal is using "std::string" in argument of callback.
Creating a base class with virtual function in C++ and derive a class in C# which implement the virtual method.
I referred to this link.
Callback from C++ to C# using SWIG
My code what callback from C++ to C# using SWIG is following.
C++ : Header.h
#include <string>
#include <vector>
class CallbackBase
{
public:
virtual void Callback(const std::wstring& str) = 0;
};
class CppClass
{
public:
void SetString(const std::wstring &str);
std::wstring GetString();
void SetCallback(CallbackBase& callbackObject);
void ExeCallback();
private:
std::wstring string;
CallbackBase* pCallbackObj = NULL;
void InvokeCallback();
};
typedef CppClass* (*GetInstanceFuncPointer)(void);
extern "C" __declspec(dllexport) CppClass * CreateInstance(void);
C++ : Impl.cpp
#include <thread>
#include "Header.h"
void CppClass::SetString(const std::wstring& str)
{
string = str;
}
std::wstring CppClass::GetString()
{
return string;
}
void CppClass::SetCallback(CallbackBase &callbackObj)
{
pCallbackObj = &callbackObj;
}
void CppClass::ExeCallback()
{
std::thread *th = new std::thread(&CppClass::InvokeCallback, this);
th->join();
}
void CppClass::InvokeCallback()
{
if (pCallbackObj != NULL)
{
pCallbackObj->Callback(string);
}
}
__declspec(dllexport) CppClass* CreateInstance(void)
{
return new CppClass;
}
Swig : Interface file
%include <windows.i>
%include <wchar.i>
%include <std_wstring.i>
%module (directors="1") MyDLL
%{
#include "Header.h"
%}
%feature("director") CallbackBase;
%include "Header.h"
C# : main.cs
using System.Diagnostics;
namespace CsharpApp
{
public class MyCallback : CallbackBase
{
public override void Callback(string str)
{
Debug.WriteLine($"str = {str}"); // --> str = H
}
}
class Program
{
static void Main(string[] args)
{
MyCallback myCallbackObj = new MyCallback();
CppClass instance = MyDLL.CreateInstance();
string str = "Hello World!";
instance.SetString(str);
Debug.WriteLine($"str = {instance.GetString()}"); // --> str = Hello World!
instance.SetCallback(myCallbackObj);
instance.ExeCallback();
}
}
}
I'm getting the string twice on the C# side.
The first is "GetString()" in the main function.
In this case, "Hello World!" Is stored.
The second time I get it as an argument of the callback function.
In this case, only "H" is stored.
How can I correctly store the string in the callback function argument?

c++ native code DLL using an abstract class into a wrapper to be used in C# module

Hi i am having the same problem with abstract class.i am trying to wrap my cpp Dll calls to use in the C# module.
Dll is using the Factory pattern.which contain a class Factory,MiddileWare ,And their costrong textrresponding child class.Could any one help me to start the wrapper. i am stuck in between this.Your help will be appreciated. i am giving the flow here:
MiddleWareFactory.h
#pragma once
#include "MiddleWareConnection.h"
#include "std afx.h"
#ifdef MIDDLEWAREFACTORY_EXPORTS
#define MIDDLEWAREFACTORY_API __declspec(dllexport)
#else
#define MIDDLEWAREFACTORY_API __declspec(dllimport)
#endif
MIDDLEWAREFACTORY_API enum eMiddleWareEngine
{
eRabitMQ = 0,
eZeroMQ,
eActiveMQ
};
// This class is exported from the MiddleWareFactory.dll
class MIDDLEWAREFACTORY_API CMiddleWareFactory
{
public:
CMiddleWareFactory(void);
~CMiddleWareFactory();
// TODO: add your methods here.
//Function to Create the object of Broker module:implemnted the Factory concept.
BOOL CreateInstance(CMiddleWareConnection** pMObj);
int m_eMomEngine;//To define which MOM need to enable.
};
extern MIDDLEWAREFACTORY_API int nMiddleWareFactory;
MIDDLEWAREFACTORY_API int fnMiddleWareFactory(void);
MiddleWareConnection.h
#pragma once
class CMiddleWareConnection
{
public:
virtual ~CMiddleWareConnection(void)
{
}
//Pure virtual fuctions for interfacing
virtual BOOL Connect(int nServerType)=0;
virtual BOOL CreateSessionExchange() = 0;
virtual BOOL CreateQueue(LPCTSTR lpszQueueName) = 0;
virtual BOOL Disconnect() = 0;
virtual BOOL Send(void *MomItem,LPCTSTR lpszKey, int &nSendCount)=0;
virtual BOOL Receive() = 0;
virtual void StopReceiver() = 0;
virtual void GetData(void* pMsg, int &nMsgLen,int nMsgType,int &nReceiveCount)=0;
};
RabbitMQ.h
#pragma once
#include "MiddleWareConnection.h"
#include "Amqp.h"
#pragma comment(lib, "rabbitmq.4.lib")
#define GET_DATA(a){memcpy(&a, pDataPtr, sizeof(a));pDataPtr+=sizeof(a);}
#define GET_DATA_EX(s,n){memcpy(s, pDataPtr, n);pDataPtr+=n;}
typedef struct _ROUTINE_KEY
{
CString RoutingKey;
}ROUTEKEY, *LPROUTEKEY;
class CRabbitMQ :
public CMiddleWareConnection
{
public:
CRabbitMQ(CAppConfig &rConfig);
~CRabbitMQ();
void InitializeRBMQ(CAppConfig &rConfig);//Initialize RBMQ Config;
BOOL Connect(int nServerType);
BOOL Disconnect(void);
BOOL Send(void *MomItem, LPCTSTR lpszKey, int &nSendCount);
BOOL Receive(void);
BOOL CreateQueue(LPCTSTR lpszQueueName);
BOOL CreateSessionExchange();
BOOL BindQueue(LPCTSTR lpszQueue, LPCTSTR lpszExchangeName, LPCTSTR lpszKey);
bool IsConnected(){return m_bConnected;}
void SetKeyQueueCombination( TCHAR *pszQueueName, TCHAR *pszRoutingKey);
void StopReceiver();
bool ReEstablishRMQMWConnection();
void GetData(LPBYTE &pMsg, int &nMsgLen,int &nReceiveCount);
void GetData(void* pMsg, int &nMsgLen,int nMsgType,int &nReceiveCount);
BOOL GetNext_JasonListItem(LPJASON_ITEM pItem);
LPRABBIT_MQ_ITEM GetNextItem();
};
Here i want to expose the rabbitMq class functions[Connect,send,Recieve ete from MiddleWareConnection.h to C# module.
Thanks
below I posted a small code example of a wrapping C++/CLI class. You could create a new C++/CLI project which links your native library and create a class following the example below. This will produce a managed assembly that you could reference from C# side. Hope it helps!
#include <Windows.h>
#include <vcclr.h>
using namespace System;
using namespace System::Text;
using namespace System::Collections::Generic;
using namespace System::Runtime::InteropServices;
public ref class CMiddleWareConnection_Net
{
public:
CMiddleWareConnection_Net()
{
pMiddleWareConnection = NULL;
}
~CMiddleWareConnection_Net()
{
//Release pMiddleWareConnection
if (pMiddleWareConnection)
delete pMiddleWareConnection;
}
void InitializeRBMQ(CAppConfig_Net config)
{
// CAppConfig_Net is another ref class
// which provides a toNative method
// returning a plain C++ CAppConfig object
CAppConfig rConfig = config.toNative();
pMiddleWareConnection = CRabbitMQ(rConfig);
}
bool Connect(int nServerType)
{
return (pMiddleWareConnection->Connect(nServerType) == TRUE);
}
bool Disconnect(void)
{
return (pMiddleWareConnection->Disconnect() == TRUE);
}
bool Send(array<Byte>^ MomItem, String^ lpszKey, int% nSendCount)
{
//Assuming MomItem is a byte array...
unsigned char *pData = new unsigned char[MomItem->Length];
Marshal::Copy(MomItem, 0, IntPtr(pData), MomItem->Length);
//Marshal String^ to LPCSTR
IntPtr ip = Marshal::StringToHGlobalAnsi(lpszKey);
LPCSTR str = static_cast<LPCSTR>(ip.ToPointer());
bool bRet = (pMiddleWareConnection->Send(pData, str, nSendCount) == TRUE);
//Freeing memory
delete[] pData;
Marshal::FreeHGlobal(ip);
return bRet;
}
bool Receive(void)
{
return (pMiddleWareConnection->Receive() == TRUE);
}
/* Other methods */
...
private:
CMiddleWareConnection *pMiddleWareConnection;
};
****MiddleWareFactory.h****
BOOL CreateInstance(CMiddleWareConnection** pMObj)
{
//if( some config value)
*pMObj = new RabbitMq();
// esle
/***other MOm like OBCS,OSCS etec...**/
}
****MiddlewareConnection.h****
have the basic abstract functions....
**Rabbitmq.h**
it is one of the child class.
****Wrapper****
CLRWrapper::CppMathWrapper::CppMathWrapper()
{
cppMath = new cCppMath();
oFact->createInstance(&cppMath);'// here i want to pass the reference & wanna get back the child class type:now i am stuck here..how can we pass/marshal the object:im getting the error
}
BOOL CLRWrapper::CppMathWrapper::connect(type)
{
return (cppMath->Connect(nServerType) == TRUE);
}
..........And so On............
CLR Wrapper.cpp(11) : error C2664: 'Factory::createInstance' : cannot convert parameter 1 from 'cli::interior_ptr' to 'Base **'
1> with
1> [
1> Type=cCppMath *
1> ]
1> Cannot convert a managed type to an unmanaged type
}
instead of this we are using createinstance()[InitializeRBMQ() is inside connect(),so it will be handled inside i think]
void InitializeRBMQ(CAppConfig_Net config)
{
// CAppConfig_Net is another ref class
// which provides a toNative method
// returning a plain C++ CAppConfig object
CAppConfig rConfig = config.toNative();
pMiddleWareConnection = CRabbitMQ(rConfig);
}
C# side
i want to expose :
private static void Main(string[] args)
{
CppMathWrapper wrapper = new CppMathWrapper();
wrapper.connect(0);
}
i tried one example with CPPMath class.In real i am attaching the code below:
CLRWrapper::CppMathWrapper::CppMathWrapper()
{
middlewareConnection *oConn;// MiddleConnection.h pointer
middleWareFactory oFact= new middleWareFactory ();// MiddleWareFactory.h object
oFact->createInstance(&oConn);'// here i want to pass the reference & wanna get back the child class type:now i am stuck here..how can we pass/marshal the object:im getting the error.How could i wrap the object....the same like
}
BOOL CLRWrapper::CppMathWrapper::connect(type)
{
return (oConn->Connect(nServerType) == TRUE);
}
..........And so On............

Can't access public methods of C++/CLI wrapper class in C#

I have written a Wrapper assembly in C++/CLI. Now I want to call into C++/CLI from C#,however I get the error:
Error CS0122 'USBWrapper.setPortDisable(int)' is inaccessible due to its protection level
So my .h and .cpp file are as follows(which is compiled with the /clr switch):
The .h file:
// USBFunctionsWrapper.h
#pragma once
#include "C:\Users\admin\Downloads\brainstem_dev_kit_win32_i386_17\brainstem_dev_kit_win32_i386\development\lib\BrainStem2\BrainStem-all.h"
#include "C:\Users\admin\Downloads\brainstem_dev_kit_win32_i386_17\brainstem_dev_kit_win32_i386\development\lib\BrainStem2\aUSBHub2x4.h"
using namespace System;
namespace USBFunctionsWrapper {
public ref class USBWrapper
{
public:
USBWrapper();
~USBWrapper();
aErr setPortDisable(int portNumber);
aErr setPortEnable(int portNumber);
aErr usbSleep(int time);
private:
};
}
The .cpp file:
// This is the main DLL file.
#include "stdafx.h"
#include "USBFunctionsWrapper.h"
#include "C:\Users\admin\Downloads\brainstem_dev_kit_win32_i386_17\brainstem_dev_kit_win32_i386\development\lib\BrainStem2\BrainStem-all.h"
#include "C:\Users\admin\Downloads\brainstem_dev_kit_win32_i386_17\brainstem_dev_kit_win32_i386\development\lib\BrainStem2\aUSBHub2x4.h"
aUSBHub2x4 stem;
aErr err = aErrNone;
USBFunctionsWrapper::USBWrapper::USBWrapper() {
err = stem.linkDiscoverAndConnect(USB);
}
USBFunctionsWrapper::USBWrapper::~USBWrapper() {
err = stem.linkDisconnect();
}
aErr USBFunctionsWrapper::USBWrapper::setPortDisable(int portNumber) {
return stem.usb.setPortDisable(portNumber);
}
aErr USBFunctionsWrapper::USBWrapper::setPortEnable(int portNumber) {
return stem.usb.setPortEnable(portNumber);
}
aErr USBFunctionsWrapper::USBWrapper::usbSleep(int time) {
return aTime_MSSleep(time);
}
The C# file I am calling these functions from:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using USBFunctionsWrapper;
namespace USBPortWrapper
{
public class WrapperClass
{
USBWrapper usb;
public WrapperClass()
{
usb = new USBWrapper();
}
public void setPortEnable(int portNumber)
{
usb.setPortEnable(portNumber);
}
}
}
I get the error for the usb.setPortEnable(portNumber) function in the c# code.
Do you know what could be a possible reason for that?

passing a delegate function back to a clr dll which calls a mfc c++ dll in c#

I have setup a c# console mode program which calls a clr/dll to access a MFC c++ dll and that works for accessing functions in the MFC c++ dll. But I want to pass back a delegate function from c# so that when a function in the MFC c++ dll needs to call the c# program it has the callback to do that. But I can't get it setup right...this is my attempt:
program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using dd_clr; // this is my clr.dll
namespace dd_gui
{
public delegate int GUI_CallbackDelegate(string fn);
class Program
{
int GUI_callback(string fn)
{
Console.WriteLine("Begin GUI_callback()");
Console.WriteLine("GUI_callback fn=" + fn);
Console.WriteLine("End GUI_callback()");
return (1);
}
static GCHandle gch;
static void Main(string[] args)
{
Console.WriteLine("begin GUI");
dd_clr.DDManaged instance = new dd_clr.DDManaged();
GUI_CallbackDelegate ^ fp = gcnew GUI_CallbackDelegate(GUI_Callback); // this does not compile for some reason ; expected after gcnew ???
gch = GCHandle.Alloc(fp);
instance.Set_GUICallback(fp); // this I am trying to get to work.
instance.batch_run("test.dap"); // this call works.
Console.WriteLine("end GUI");
gch.Free();
}
}
}
From your code it's not obvious what you are trying to do, but you can pass a delegate to C++ this way:
server.h:
extern "C"
{
typedef int(__stdcall * ComputeCallback)(int);
__declspec(dllexport) int Sum(ComputeCallback computeCallback);
}
server.cpp:
__declspec(dllexport) int Sum(ComputeCallback computeCallback)
{
int sum = 0;
for (int i = 0; i < 4; i++)
{
int x = computeCallback(i);
sum += x;
}
return sum;
}
client.cs:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate int ComputeCallback(int value);
class Program
{
[DllImport("server.dll")]
public static extern int Sum(ComputeCallback callback);
static void Main(string[] args)
{
ComputeCallback callback = x =>
{
Console.WriteLine("Computing. X = {0}", x);
return x*x;
};
Console.WriteLine("Result: {0}", Sum(callback));
}
}

Callbacks from C++ back to C#

say I have a C++ library function for computing PI:
// pi.h:
#ifdef BUILDING_DLL
#define DLL_MACRO __declspec(dllexport)
#else
#define DLL_MACRO __declspec(dllimport)
#endif
namespace Cpp {
class PI {
public:
static double DLL_MACRO compute();
};
};
// pi.cpp:
#include "pi.h"
#include <cmath>
double
Cpp::PI::compute() {
// Leibnitz summation formulae:
double sum = 0.0;
for(long n = 0; n < 100*1000*1000; n++)
sum += 4.0*pow(-1.0, n)/(2*n + 1.0);
return sum;
}
I need to call this function from C#, and I want to use C++/CLI as a "bridge". However this C++ function is somewhat slow. Therefore the C# code calling this function need to get callbacks telling it how far the function has come in %.
The C# code might need some state, e.g. a progress bar, to deal with this info.
So the callbacks from C++ must enter into a member function on the C# side.
So I introduce:
// piCLI.h: The C++/CLI "bridge" between C# and C++
#include "pi.h"
#pragma once
namespace CLI {
public ref class PI abstract {
public:
double compute() {
return Cpp::PI::compute();
}
virtual void progress(int percentCompleted) = 0;
};
};
and
namespace CSharp
{
public class PI : CLI.PI
{
public override void progress(int percentCompleted)
{
System.Console.WriteLine(percentCompleted + "% completed.");
}
}
}
Now invoking CSharp.PI.compute() works fine :-). It forwards the call to Cpp::PI::compute() as intended.
But how do I get the C++ library to forward progress updates to CSharp.PI.progress() whilst Cpp::PI::compute() is running?
Thanks in advance for any answers!
I would take a function pointer/delegate approach as well:
// pi.h:
#pragma once
#ifndef DLL_MACRO
#ifdef BUILDING_DLL
#define DLL_MACRO __declspec(dllexport)
#else
#define DLL_MACRO __declspec(dllimport)
#endif
#endif
namespace Cpp {
typedef void (__stdcall *ComputeProgressCallback)(int);
class PI {
public:
static double DLL_MACRO compute(ComputeProgressCallback callback);
};
}
// pi.cpp:
#include "pi.h"
#include <cmath>
double Cpp::PI::compute(Cpp::ComputeProgressCallback callback) {
double sum = 0.;
for (long n = 0L; n != 100000000L; ++n) {
sum += 4. * std::pow(-1., n) / (2L * n + 1.);
callback(/*impl*/);
}
return sum;
}
// piCLI.h: The C++/CLI "bridge" between C# and C++
#pragma once
#include "pi.h"
namespace CLI {
public delegate void ComputeProgressDelegate(int percentCompleted);
public ref class PI abstract sealed {
public:
static double compute(ComputeProgressDelegate^ callback) {
using System::IntPtr;
using System::Runtime::InteropServices::Marshal;
IntPtr cbPtr = Marshal::GetFunctionPointerForDelegate(callback);
return Cpp::PI::compute(
static_cast<Cpp::ComputeProgressCallback>(cbPtr.ToPointer())
);
}
};
}
namespace CSharp {
public static class PI {
public static double compute() {
CLI.PI.compute(
percentCompleted => System.Console.WriteLine(
percentCompleted.ToString() + "% completed."
)
);
}
}
}
Or, to override an abstract progress method rather than creating a delegate on the C# side:
// piCLI.h: The C++/CLI "bridge" between C# and C++
#pragma once
#include "pi.h"
namespace CLI {
public ref class PI abstract {
delegate void ComputeProgressDelegate(int percentCompleted);
public:
double compute() {
using System::IntPtr;
using System::Runtime::InteropServices::Marshal;
ComputeProgressDelegate^ callback = gcnew ComputeProgressDelegate(
this,
&PI::progress
);
IntPtr cbPtr = Marshal::GetFunctionPointerForDelegate(callback);
return Cpp::PI::compute(
static_cast<Cpp::ComputeProgressCallback>(cbPtr.ToPointer())
);
}
protected:
virtual void progress(int percentCompleted) abstract;
};
}
namespace CSharp {
public sealed class PI : CLI.PI {
protected override void progress(int percentCompleted) {
System.Console.WriteLine(
percentCompleted.ToString() + "% completed."
);
}
}
}
Pass a C++/cli defined Native function to your native C++ as a callback and upon callback use a gcroot to call your managed C# function.

Categories

Resources