I'm looking for exchange a string from my c++ code with my c# form.
Here is my code in my C# program:
[DllImport("libDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern string valeurExpr(IntPtr pImg);
public unsafe string objetLibValeurCarteExpr()
{
return valeurExpr(ClPtr);
}
And my code in my C++ program :
Class IHM
class IHM {
private:
std::string card_number_str;
std::string card_expr_str;
std::string card_porteur_str;
My extern
extern "C" _declspec(dllexport) std::string valeurExpr(IHM* pImg) { return pImg->lireExpr(); }
Function lireExpr()
_declspec(dllexport) std::string lireExpr() const {
return card_expr_str;
}
When i execute Visual said that i try to access to a protected part of memory.
First of all you cannot have std::string nor std::wstring in dll that you want to be accessible from other languages. So these fellows have to be changed to char * or wchar_t * <--- this is the real string - array of chars.
So, how to get string from C++ to C#?
C++
void foo(char *str, int len)
{
//write here content of string
}
C#
[DllImport("...", CallingConvention = CallingConvention.Cdecl)
static extern void foo(StringBuilder str, int len);
and then you have to call it somehow:
void callFoo()
{
StringBuilder sb = new StringBuilder(10); //allocate memory for string
foo(sb, sb.Capacity);
}
Notice that in C# you have to use StringBuilder to get string from c++.
If you want to pass a string in the other way - from C# to C++ is simpler:
C++
void foo(const char *str)
{
//do something with this str
}
C#
[DllImport("...", CallingConvention = CallingConvention.Cdecl)
static extern void foo(string str);
and then just:
void callFoo(string str)
{
foo(str);
}
You have to remember about codepage. So if you're using unicodes, you will have to give additional attribute to DllImport: CharSet=CharSet.Unicode
Now classes. There is NO simple way to pass a class defined in C++ to C#. The simples way is to do some magic. So for every member function in C++ create non member function that will be exported to dll. Something like that:
//class in C++
class Foo
{
public:
int Bar();
};
//now you will have to define non member function to create an instance of this class:
Foo* Foo_Create()
{
return new Foo();
}
//and now you will have to create non member function that will call Bar() method from a object:
int Foo_Bar(Foo* pFoo)
{
return pFoo->Bar();
}
//in the end you will have to create a non member function to delete your object:
void Foo_Delete(Foo* pFoo)
{
delete pFoo;
}
And then you can use it in C#:
[DllImport("Foo.dll")]
public static extern IntPtr Foo_Create();
[DllImport("Foo.dll")]
public static extern int Foo_Bar(IntPtr value);
[DllImport("Foo.dll")]
public static extern void Foo_Delete(IntPtr value);
You can also use a C# class in C++, but it's a little more complicated and requires use of C++/CLI
Related
I'm working on a .NET 5 project to bind the AWS-Encryption-SDK from C and C++ to C# using PInvoke, type marshalling, and the DllImportAttribute. When attempting to call this C++ function
aws_cryptosdk_keyring *Build(const Aws::String &generator_key_id, const Aws::Vector<Aws::String> &additional_key_ids = {}) const;
using
[DllImport("aws-encryption-sdk-cpp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern aws_cryptosdk_keyring Build(string generator_key_id, string[] additional_key_ids = null);
I get this error
System.EntryPointNotFoundException : Unable to find an entry point named 'Build' in DLL 'aws-encryption-sdk-cpp.dll'.
The dll signature for this function is
?Build#Builder#KmsKeyring#Cryptosdk#Aws##QEBAPEAUaws_cryptosdk_keyring##AEBV?$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std##AEBV?$vector#V?$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std##V?$allocator#V?$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std###2##7##Z
So far nothing that I have tried as the EntryPoint has been successful. Does anyone have any ideas?
Expanding on #jwezorek's comment, C# does not know how to call C++ (class) functions, only C functions. There are several reasons why this is so, but I won't get into them here.
You need to create (in C++) a wrapper that converts each C++ call to a 'flattened' C call. For example:
extern "C" void *createClass() {
return (void*) new myClass();
}
extern "C" void destroyClass(void* self) {
delete (myClass*) self;
}
extern "C" int callFunction1(void* self, int x, int y) {
return ((myClass*) self)->function1(x, y);
}
You'll need to write a function for each class function (plus the constructor and destructor). Then compile, linking it to that original C++ DLL's library. Then in C#, write a wrapper to call your flattened class:
public class myClass : IDisposable {
[DllImport("myFlattenedDll", EntryPoint="createClass")]
public static extern IntPtr createClass();
[DllImport("myFlattenedDll", EntryPoint="destroyClass")]
public static extern void destroyClass(IntPtr self);
[DllImport("myFlattenedDll", EntryPoint="callFunction1")]
public static extern int callFunction1(IntPtr self, int x, int y);
private IntPtr self;
public myClass() {
self = createClass();
}
~myClass() {
Dispose();
}
public virtual void Dispose() {
lock (this) {
destroyClass(self);
GC.SuppressFinalize(this);
}
}
public int callFunction1(int x, int y) {
return callFunction1(self, x, y);
}
}
You should also look into a tool called SWIG that can automate a good deal of the wrapper coding for you.
I need to return the necessary information about an object as a struct with callbacks and other data.
This is what it looks like on the C# side:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ChartAddDataCallback(IntPtr data, int size);
[StructLayout(LayoutKind.Sequential)]
public struct ChartAccessors
{
public IntPtr HWnd;
public ChartAddDataCallback addDataCallback;
}
[DllImport("CppPart.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "GetAccessors")]
public static extern ChartAccessors GetAccessors();
The C++ "mirrored" version looks like this:
typedef void(__cdecl *AddDataCallback) (int * data, int size);
struct ChartAccessors
{
HWND m_chartHWnd;
AddDataCallback m_addDataCallback;
};
extern "C" __declspec(dllexport) ChartAccessors GetAccessors();
Usage:
static void Main(string[] args)
{
ChartAccessors accessors = GetAccessors();
}
However, when i start up the program i get the exception "Method's type signature is not PInvoke compatible."
It works if i use any other return type (like int or float) instead of the struct.
Marshal.PtrToStructure was actually solving this issue, as Pavel pointed out.
void RegisterCallbacks(IntPtr callbackPtr)
{
ChartAccessors accessors = (ChartAccessors)Marshal.PtrToStructure(callbackPtr, typeof(ChartAccessors));
// do stuff with the struct
}
Below is a toy example to better understand marshaling of string types between c# and c++/dll.
What's the best way to marshal the "MyStrCopy" Function below into c#?
(Preferably without using the unsafe keyword and by marshaling the type into and out of c# as a string type.)
File: MyStrCopy.cs
using System.Runtime.InteropServices;
namespace MySpace {
class MyDll {
[DllImport (#"MyStrCopy")]
public static extern void MyStrCopy(
string dest????, string source????, int dest_length????);
}
}
FILE: MyStrCopy.h:
extern "C" {
void __declspec(dllexport) MyStrCopy(
char* dest, const char* source, int dest_length);
}
FILE: MyStrCopy.cpp
#include <cstring>
#include "MyStrCopy.h"
void MyStrCopy(char* dest, const char* source, int dest_len) {
strncpy(dest, source, dest_len);
dest[dest_len-1] = 0; // zero terminated when source > dest
}
I compile the above file "MyStrCopy.cpp" into a dll called: MyStrCopy.dll
I'm also a little bit curious what it would look like if you return char* as well under same preference of not using unsafe and marshaling type to string. example, if dll exported function looks like this instead:
char* MyStrCopy(char* dest, const char* source, int dest_len) {
return strncpy(dest, source, dest_len);
}
using System.Text;
using System.Runtime.InteropServices;
namespace MySpace
{
class MyDll {
[DllImport("MyStrCopy.dll", CharSet = CharSet.Ansi)]
public static extern void MyStrCopy(
StringBuilder dst_str,
string src_str,
int dst_len);
static void ExampleUsage() {
int dest_len = 100;
StringBuilder dest_str = new StringBuilder(dest_len);
string source = "this is a string";
MyStrCopy(dest_str, source, dest_len);
return;
}
} //class
} //namespace
I have the following function in C++ that I want to call from C#:
std::string getString();
How do I best do this (using Pinvoke)?
Am I correct in assuming that return allocated memory from C++ to C# is problematic and that it would be better (easier) to allocate a large string in C# and then pass it on to C++ for writing?
In this case I guess I should wrap the C++ function with C ?:
extern "C"
{
__declspec(dllexport) void get_string(int size, char *buffer)
{
call getString and strncpy to buffer
}
}
The classical way to do it is:
__declspec(dllexport) void get_string(int size, char *buffer)
{
std::string str = getString();
strncpy(buffer, str.c_str(), size);
buffer[size - 1] = '\0';
}
C# side:
[DllImport("NativeLibrary", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern void get_string(int len, [Out] StringBuilder sb);
and then (very important! You must pre-size the StringBuilder)
var sb = new StringBuilder(100);
get_string(sb.Capacity, sb);
string str = sb.ToString();
You can use a char[], but it is more complex, because then you have to manually "trim" the \0.
There is a more complex way to do it with one less copy of memory... But it is a little more complex:
C++ side:
__declspec(dllexport) void get_string(void (*func)(const char*))
{
std::string str = getString();
func(str.c_str());
}
C# side:
[DllImport("NativeLibrary", CallingConvention = CallingConvention.Cdecl)]
public static extern void get_string(StringFromIntPtr.FromIntPtrDelegate func);
public class StringFromIntPtr
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void FromIntPtrDelegate(IntPtr ptr);
public string Value { get; protected set; }
public void FromIntPtr(IntPtr ptr)
{
Value = Marshal.PtrToStringAnsi(ptr);
}
}
Then you use like this:
var sfip = new StringFromIntPtr();
get_string(sfip.FromIntPtr);
string str = sfip.Value;
The point is that you pass to the C++ code a delegate to a C# method that knows how to handle a raw pointer to a string (through the use of Marshal.PtrToStringAnsi for example), and the C++ code uses it. Note that it would be much easier to do this in C++/CLI (because you wouldn't need the delegate, the C++/CLI can use both std::string and System.String^)
If you are using C++/CLI, you can return System::String too. You can construct the System::String from getString().c_str()
I have this code in a c++ file, with compiles to a dll.
#include "stdafx.h"
#include "WHUU.h"
#include "stdafx.h"
typedef int (__stdcall * Callback)(const int text);
static int x , y= 0;
Callback Handler = 0;
int getX()
{
return x;
}
void setX(int i)
{
x = i;
}
void setY(int i)
{
y = i;
}
int getY()
{
return y;
}
extern "C" __declspec(dllexport)
void __stdcall SetCallback(Callback handler) {
Handler = handler;
}
extern "C" __declspec(dllexport)
void __stdcall addX(int x) {
setX(x);
}
extern "C" __declspec(dllexport)
void __stdcall addY(int y) {
setY(y);
}
extern "C" __declspec(dllexport)
void __stdcall TestCallback() {
int z = getX() + getY();
int retval = Handler(z);
}
My c# application now has to load this dll on runtime. Add to the callback and call the functions. I dont want to use a class. I could load the class and with
Type[] types = moduleAssembly.GetTypes();
But this overkill! Also c++ is not managed.
I mean its so tiny! (and yes this is an example , but the "real" is just as big as this example).
How do i do that?
Thanks you for your help!
add:
i dont like frameworks (like pinvoke / assembly)
the function names / types are fixed and will never change (think of a driver.dll read write)
this dll is written by customers so it should be as easy as possible!
You could also do it over p/invoke, as example:
[DllImport("unmanaged.dll", CharSet = CharSet.Ansi)]
private extern static int yourFunction(int var1, int var2);
i dont like frameworks (like pinvoke / assembly)
I'll suggest P/Invoke anyway. I don't think there are any reasonable alternatives. Perhaps writing a managed wrapper in managed C++, but really?
When you use P/Invoke the .NET runtime will dynamically load the DLL's that you have specified (and this is done when you first call the function). There is no reason to use P/Invoke to call LoadLibrary first.
You can either use P/Invoke to call the dll directly, or you can create a C++/CLI wrapper for it.
Here's how you can do it using P/Invoke:
Add the compiled dll to your C# project, set its 'Copy to Output Directory' property to 'Copy Always'. and call it like this:
class Program
{
[DllImport("cppdll.dll")]
private static extern void addX(int x);
[DllImport("cppdll.dll")]
private static extern void addY(int y);
static void Main(string[] args)
{
addX(5);
addY(7);
}
}
One way to do this would be through API. Find below an example for the setX and getX method from your sample above.
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr LoadLibraryEx(string libraryPath, IntPtr fileHandle, int actionFlag);
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern bool FreeLibrary(IntPtr libraryHandle);
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr GetProcAddress(IntPtr dllPointer, string functionName);
IntPtr ptr = IntPtr.Zero;
private delegate void setX (int i);
private delegate int getX();
private setX setXDel;
private getX getXDel;
public Constructor()
{
loadLib();
}
public void YourMethod()
{
setXDel(100);
int y = getXDel();
Console.WriteLine(y.ToString());
}
private void loadLib()
{
string path = "your dll path";
ptr = LoadLibraryEx(path, IntPtr.Zero, 0);
if (ptr == IntPtr.Zero)
throw new Exception("Cannot load dll.");
IntPtr addressPtr = GetProcAddress(ptr, "setX");
setXDel = (setX)Marshal.GetDelegateForFunctionPointer(addressPtr, typeof(setX));
addressPtr = GetProcAddress(unrarPtr, "getX");
getXDel = (getX)Marshal.GetDelegateForFunctionPointer(addressPtr, typeof(getX));
}
public void Dispose()
{
if (ptr != IntPtr.Zero)
{
FreeLibrary(ptr);
}
}
I used P/Invoke, post my own cause the callback was not included by the others. But they were good answers! The C# Applicationhas use this GUI.
The Code for the Application is this : (mind it is a prototype to answer a technical question)
public partial class Form1 : Form
{
private delegate int Callback(int text);
private Callback mInstance; // Ensure it doesn't get garbage collected
[DllImport(#"C:\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void SetCallback(Callback fn);
[DllImport(#"C:\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void addX(int x);
[DllImport(#"C:\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void addY(int y);
[DllImport(#"C:\\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void TestCallback();
private int Handler(int text)
{
textBox3.Text = text.ToString();
return 42;
}
private void button1_Click(object sender, System.EventArgs e)
{
mInstance = new Callback(Handler); // to set the callback in lib
SetCallback(mInstance); // could also be withhin constructor!
addX(int.Parse(textBox1.Text)); // other people in this thread posted this correct answer
addY(int.Parse(textBox2.Text));
TestCallback();
}
public Form1()
{
InitializeComponent();
}
So if you want to use a c++ lib with functions and callbacks (like in the question posted) you can use this.
But how to change the lib?
The Path to the lib is hardcoded (see [dllImport ......] ). But you
can swop the lib in filesystem. This can happen AFTER you build this
application.
If the lib is not on the given path an exception is thrown. So if
your program wants to use this lib first check the lib is present on the filesystem.
(not included in this simple prototype)
So to switch functionality (swop a driver lib ect) you copy a different lib with the
same name into the given path. (Copy paste - maybe change name)
This solution has the least overhead and is good if you never change the interface of an dll after the application was build!