Let's say I have a timer object in C#:
public class Timer {
private int currentTime;
private int lastTime;
public int GetTimePassed() {
}
}
Is it possible to use this object as a parameter in an external function? Like so:
public class Program {
[DllImport("example.dll"), CallingConvention = CallingConvention.Cdecl]
extern void ExampleFunction(Timer arg);
}
#include <stdio.h>
extern "C" {
__declspec(dllexport) void ExampleFunction(Timer arg) {
}
}
Related
We have a C# programmer that wants a .NET object to do all the underlying work. It should basically be a black box with functions and events.
I had written all this with C++ Builder, using non-visual VCL classes and now it seems like I have to make a .NET object out of it.
I need a simple example of how to create a .NET "box" with one function and one event handler and from there I should be able to implement the rest of it. Should I do this in a COM object? What technology should I use?
Example C++ side.
typedef void __fastcall (__closure *TIntEvent)(int Status);
typedef void __fastcall (__closure *TVoidEvent)(void);
typedef void __fastcall (__closure *TResultEvent)(String cmd, int code);
typedef void __fastcall (__closure *TModeEvent)(int mode, int reason);
class TDevice : public TObject {
private:
// properties
String FPortName;
String FDevice;
String FComment;
String FID;
double FBootware;
double FFirmware;
protected:
public:
// properties
__property String PortName = { read=FPortName };
__property String Device = { read=FDevice };
__property String Comment = { read=FComment };
__property String ID = { read=FID };
__property double Bootware = { read=FBootware };
__property double Firmware = { read=FFirmware };
// event function pointers
TModeEvent OnMode;
TIntEvent OnStatus;
TIntEvent OnSensors;
TVoidEvent OnInfo;
TResultEvent OnResult;
// public interface
bool Connect(void);
void Disconnect(void);
void Reset(void);
void Boot(void);
void GetInfo(void);
void GetTag(void);
};
I have removed all the internal stuff and only left exposed functions, events and properties that should be possible to reach from C#.
From this class I need to create a .NET object like this:
MyLib.IDevice.Connect();
MyLib.IDevice.Disconnect();
MyLib.IDevice.Reset();
MyLib.IDevice.Boot();
MyLib.IDevice.GetInfo();
MyLib.IDevice.GetTag();
I also need C# to connect a function to the Event handlers in the C++ class.
MyLib.IDevice.OnMode = CSharpEventHandler1;
MyLib.IDevice.OnStatus = CSharpEventHandler2;
MyLib.IDevice.OnSensors = CSharpEventHandler3;
MyLib.IDevice.OnInfo = CSharpEventHandler4;
MyLib.IDevice.OnResult = CSharpEventHandler5;
These event handlers are called inside the C++ class to fire the events like this:
if(OnMode != NULL)
{
OnMode(FMode,FReason);
}
There are also a few properties, but these are easy to impalement in a COM interface (if this is what we need)...
Since this is written in C++ Builder and C++ builder can write components (for C++ Builder and Delphi, using ActiveX technology), maybe it is possible to convert a C++ Builder component library into a .Net object/component?
EDIT:
To make it even more clear...
The MyLib.IDevice.Connect() is what I want C# to see... The list of functions are the C++ functions as in a .Net object MyLib with an interface IDevice.
So assuming I have created an instance of MyLib.IDevice as Device, I can call Device.Connect(); from C#.
It is hard... And ugly... The simplest solution is probably to create a C interface:
extern "C"
{
__declspec(dllexport) __stdcall TDevice* NewDevice()
{
return new TDevice();
}
__declspec(dllexport) void __stdcall DeleteDevice(TDevice *pDevice)
{
delete pDevice;
}
__declspec(dllexport) bool __stdcall ConnectDevice(TDevice *pDevice)
{
return pDevice->Connect();
}
.. and so on
}
In C#:
[DllImport("YourDll.dll", CallingConvention = CallingConvention.Stdcall)]
public static extern IntPtr NewDevice();
[DllImport("YourDll.dll", CallingConvention = CallingConvention.Stdcall)]
public static extern void DeleteDevice(IntPtr pDevice);
[DllImport("YourDll.dll", CallingConvention = CallingConvention.Stdcall)]
public static extern bool ConnectDevice(IntPtr pDevice);
... and so on
If you are ok with this, we can begin talking about passing delegates... and it will be a pain, trust me :-)
Uff... It was quite long... C++ side it is better if you create a wrapper for your class. This because you are using __fastcall __closure for your events. Both of these modifiers are incompatible with C#, so you "proxy" them in the wrapper.
// __fastcall not handled by C#
typedef void __stdcall (*TIntEventFunc)(int Status);
typedef void __stdcall (*TVoidEventFunc)(void);
typedef void __stdcall (*TResultEventFunc)(const wchar_t *cmd, int code);
typedef void __stdcall (*TModeEventFunc)(int mode, int reason);
class TDeviceWrapper {
public:
// You could even use directly a TDevice Device, depending on how your program works.
// By using a TDevice *, you can attach the wrapper to a preexisting TDevice.
TDevice *PDevice;
TModeEventFunc OnModeFunc;
TIntEventFunc OnStatusFunc;
TIntEventFunc OnSensorsFunc;
TVoidEventFunc OnInfoFunc;
TResultEventFunc OnResultFunc;
void __fastcall OnStatus(int status) {
OnStatusFunc(status);
}
void __fastcall OnResult(String cmd, int code)
{
OnResultFunc(cmd.c_str(), code);
}
};
extern "C" {
__declspec(dllexport) TDeviceWrapper* __stdcall NewDevice()
{
auto pWrapper = new TDeviceWrapper();
pWrapper->PDevice = new TDevice();
return pWrapper;
}
__declspec(dllexport) void __stdcall DeleteDevice(TDeviceWrapper *pWrapper)
{
delete pWrapper->PDevice;
delete pWrapper;
}
__declspec(dllexport) const wchar_t* __stdcall GetPortName(TDeviceWrapper *pWrapper)
{
return pWrapper->PDevice->PortName.c_str();
}
__declspec(dllexport) bool __stdcall Connect(TDeviceWrapper *pWrapper)
{
return pWrapper->PDevice->Connect();
}
__declspec(dllexport) void __stdcall SetStatus(TDeviceWrapper *pWrapper, TIntEventFunc statusFunc) {
pWrapper->OnStatusFunc = statusFunc;
if (statusFunc) {
pWrapper->PDevice->OnStatus = pWrapper->OnStatus;
} else {
pWrapper->PDevice->OnStatus = nullptr;
}
}
__declspec(dllexport) void __stdcall SetResult(TDeviceWrapper *pWrapper, TResultEventFunc resultFunc) {
pWrapper->OnResultFunc = resultFunc;
if (resultFunc) {
pWrapper->PDevice->OnResult = pWrapper->OnResult;
} else {
pWrapper->PDevice->OnResult = nullptr;
}
}
}
Then C#-side you have to create another wrapper :-) This time because when you pass a delegate C#->C++, the .NET creates a "thunk", but if you don't save the delegate somewhere, this "thunk" gets garbage collected. So the easiest solution is normally to create a wrapper class where you can save the used delegates. You can even incapsulate the Dispose() pattern in this wrapper :-)
public class TDeviceWrapper : IDisposable
{
// Fastcall not handled by C#
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void TIntEventFunc(int Status);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void TVoidEventFunc();
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
public delegate void TResultEventFunc(string cmd, int code);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void TModeEventFunc(int mode, int reason);
IntPtr ptr;
[DllImport("TDevice.dll")]
static extern IntPtr NewDevice();
[DllImport("TDevice.dll")]
static extern void DeleteDevice(IntPtr pWrapper);
[DllImport("TDevice.dll")]
static extern IntPtr GetPortName(IntPtr pWrapper);
[DllImport("TDevice.dll")]
static extern void Connect(IntPtr pWrapper);
[DllImport("TDevice.dll")]
static extern void SetStatus(IntPtr pWrapper, TIntEventFunc statusFunc);
[DllImport("TDevice.dll")]
static extern void SetResult(IntPtr pWrapper, TResultEventFunc resultFunc);
// To prevent the GC from collecting the managed-tounmanaged thunks, we save the delegates
TModeEventFunc modeFunc;
TIntEventFunc statusFunc;
TIntEventFunc sensorsFunc;
TVoidEventFunc infoFunc;
TResultEventFunc resultFunc;
public void Init()
{
ptr = NewDevice();
}
public string PortName
{
get
{
// Important! .NET will try to free the returned
// string if GetPortName returns directly a string.
// See for example https://limbioliong.wordpress.com/2011/06/16/returning-strings-from-a-c-api/
IntPtr ptr2 = GetPortName(ptr);
return Marshal.PtrToStringUni(ptr2);
}
}
public void Connect()
{
Connect(ptr);
}
public void SetStatus(TIntEventFunc statusFunc)
{
this.statusFunc = statusFunc;
SetStatus(ptr, statusFunc);
}
public void SetResult(TResultEventFunc resultFunc)
{
this.resultFunc = resultFunc;
SetResult(ptr, resultFunc);
}
~TDeviceWrapper()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (ptr != IntPtr.Zero)
{
DeleteDevice(ptr);
ptr = IntPtr.Zero;
}
if (disposing)
{
modeFunc = null;
statusFunc = null;
sensorsFunc = null;
infoFunc = null;
resultFunc = null;
}
}
}
Then you can, for example:
public class MyClass
{
public void StatusEvent(int status)
{
Console.WriteLine("Status: {0}", status);
}
public void ResultEvent(string cmd, int code)
{
Console.WriteLine("Resukt: {0}, {1}", cmd, code);
}
}
and
var mc = new MyClass();
using (var wrapper = new TDeviceWrapper())
{
wrapper.Init();
wrapper.SetStatus(mc.StatusEvent);
wrapper.SetResult(mc.ResultEvent);
wrapper.Connect();
}
dll C++
extern "C"
{
__declspec(dllexport) int mainfun()
{
return x;
}
}
In C#
[DllImport("example.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mainfun();
I only know how to return and call one variable from C++ to C#. I am writing a program where i need to call two different varables in C# from c++ dll
(like return x,y;). Please i need help.
EDIT1:
In C++
struct Point
{
int count_err;
int statement;
} obj;
extern "C"
{
__declspec(dllexport) Point mainfun()
{
return obj;
}
}
In C#
[StructLayout(LayoutKind.Sequential)]
public struct Point
{
public int errsize;
public int statmnt;
}
[DllImport("example.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Point mainfun();
errsize = mainfun();
statmnt = mainfun();
Here errsize is giving an error-"the name 'errsize' does not exist in the current context".. What to do?
EDIT2:
In C#
total_errors.Text = p.errsize.ToString();
giving same error-"the name 'p' does not exist in the current." context"
Define new struct or array of data. Something like this:
C++:
struct Point
{
int count_err;
int statement;
} obj;
extern "C"
{
__declspec(dllexport) Point mainfun()
{
return obj;
}
}
C#:
[StructLayout(LayoutKind.Sequential)]
public struct Point
{
public int errsize;
public int statmnt;
}
[DllImport("example.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Point mainfun();
Point p = mainfun();
var errsize = p.errsize;
var statmnt = p.statmnt;
First understand that if you want to return more than one value from any function, then you will need an object which can hold multiple values like struct, class object, list etc. But in your case you cannot use LIST or KeyValuePairList of C# because you have direct dependency with C++ code.
So use structure which is same in both the platforms. Now first you need to create a suitable data structure and change the return type of mainfun() when you are calling it as follows..
public struct abc {
public int a;
public int b;
}
[DllImport("example.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern abc mainfun();
Now in your C++ library, add the data structure and change the function definition.
typedef struct {
int a;
int b;
} abc;
extern "C"
{
__declspec(dllexport) abc mainfun()
{
abc obj;
obj.x = 1;
obj.y = 2;
return obj;
}
}
The idea is to create and export a wrapper for the C++ class and then use it from C# like following:
First, let us create the C++ class itself:
File : MyClass.cpp
class myclass
{
public:
int funct(int val)
{
return val + 1;
}
~myclass(){}
};
Then, we create a wrapper:
File wrapper.cpp
extern "C" __declspec(dllexport) myclass* expConst()
{
return new myclass();
}
extern "C" __declspec(dllexport) void expDispose(myclass * obj)
{
delete obj;
}
extern "C" __declspec(dllexport) int expfunct(myclass* obj, int val)
{
return obj->funct(val);
}
Now, we come to c#:
public class CsClass : IDisposable
{
//Import the functions from dll
[DllImport("ExportedLib.dll")]
public static extern IntPtr expDispose(IntPtr obj);
[DllImport("ExportedLib.dll")]
public static extern IntPtr expConst();
[DllImport("ExportedLib.dll")]
public static extern int expfunct(IntPtr obj, int val);
IntPtr objPtr;
public CsClass()
{
objPtr = expConst();
}
public int funct(int q)
{
return expfunct(objPtr, q);
}
public void Dispose()
{
expDispose(objPtr);
}
}
Finally, we ecxecute this by
File:Program.cs
class Program
{
static void Main(string[] args)
{
CsClass v = new CsClass();
Console.WriteLine(v.Func(1));
}
}
I tested this simple things and the program printed 2 as it was expected.
The question is, whether the garbage collector of C# will move the created C++ object and, thus, making the objPtr to point to some wrong place in memory?
Are there any other principal obstacles here? I mean some unsolvable problems which make such an approach impossible.
Thanks in advance.
No, the garbage collector only goes to work for managed memory. Your class created in C++ is not managed memory and will not be touched, for good or for bad. You will need to manage it yourself as you already did.
I have a C++ native dll where a structure is defined as follows:
typedef struct
{
int x;
int y;
unsigned short* orange;
}SET_PROFILE;
And a function in the C++ dll as:
extern "C" _declspec(dllexport) void modifyClass(SET_PROFILE** input)
{
*input = &globPro;
}
where globPro is a global object of type SET_PROFILE and its member orange is "ABC".
I have redefined this structure on the C# side as a class:
[StructLayout(LayoutKind.Sequential)]
public class SET_PROFILE
{
public int x;
public int y;
public String orange;
}
and pinvoking the function:
[DllImport("Project2.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void modifyClass(out IntPtr input);
When I call this function and then marhal it back to the structure:
IntPtr classPtr = IntPtr.Zero;
classPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SET_PROFILE)));
modifyClass(out classPtr);
profile1 = (SET_PROFILE)Marshal.PtrToStructure(classPtr, typeof(SET_PROFILE));
Now, profile1's orange meber just has "A" instead of "ABC". It has something to do with how it's copied using pointers. I can't modify the C++ function however.
Is it because I used a string as the member of the C# class instead of unsigned short[]. I tried that too but failed.
Try using the following:
[StructLayout(LayoutKind.Sequential)]
public class SET_PROFILE
{
public int x;
public int y;
public System.IntPtr orange;
public string OrangeString { get { return Marshal.PtrToStringAnsi(orange); } }
}
I work on my program, which I need for my bachelor work(C#/C++ interoperability) and I have problem with missing entry point in my code... I try create simply number generator, which will be genarated number in C++ class calling from C# ... At first I don't know how I pass a class, but then I found way how to do it on this page... Please help me fix it...
I added my code:
[C++]
#include <iostream>
#include <cstdlib>
#include <time.h>
using namespace std;
__declspec(dllexport) class Generator
{
private:
int zaciatok;
int koniec;
int pocetprvkov;
int *pole;
public:
Generator(){}
void Vytvor (int zaciatok, int koniec, int pocetprvkov)
{
srand((unsigned)time(0));
pole= new int [pocetprvkov];
}
void Napln()
{
for(int a=0; a<pocetprvkov; a++)
{
pole[a] = rand() % (koniec - zaciatok +1) + zaciatok;
}
}
void Vypis()
{
for(int a=0; a<pocetprvkov; a++)
cout << pole[a] << endl;
}
~Generator()
{
delete[] pole;
pole= 0;
}
};
extern "C"
{
__declspec(dllexport) Generator* Vytvor_Triedu() { return new Generator(); }
__declspec(dllexport) void Vytvor(Generator* prva) {prva->Vytvor(5,25,4); }
__declspec(dllexport) void Napln(Generator* prva) {prva->Napln(); }
__declspec(dllexport) void Vypis(Generator* prva) {prva->Vypis(); }
__declspec(dllexport) void Vymaz(Generator* prva) { delete prva; }
}
[C#]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace GeneratorCsharp
{
class Program
{
[DllImport("DllTriedaGenerator.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr Vytvor_Triedu();
[DllImport("DllTriedaGenerator.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Vytvor(IntPtr value);
[DllImport("DllTriedaGenerator.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Napln(IntPtr value);
[DllImport("DllTriedaGenerator.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Vypis(IntPtr value);
[DllImport("DllTriedaGenerator.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Vymaz(IntPtr value);
static void Main(string[] args)
{
IntPtr trieda = Vytvor_Triedu();
Vytvor(trieda);
Napln(trieda);
Vypis(trieda);
Vymaz(trieda);
}
};
}
Thanks a lot!
Well, you should use dumpbin, find the mangled name of your function then add EntryPoint="yourMangledName" in your DllImport attribute.