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
Related
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
I'm making messaging system with C library.
To send messages from C library (DLL), I made this DLL file and tried to callback from C to C#
here is C DLL Code
logging.h
#pragma once
#include <stdio.h>
struct loggingMessage {
void (*fp)(char *, int, int);
};
struct loggingMessage messageConfig;
__declspec(dllexport) void loggingInitialize();
__declspec(dllexport) void print();
__declspec(dllexport) void setEventCallBack(void(*fp)(char *, int, int));
logging.c
void loggingInitialize()
{
messageConfig.fp = NULL;
}
void print(char *str, int length)
{
char buf[1024];
memset(buf, 0, sizeof(char) * 1024);
sprintf(buf, "%s", str);
if (messageConfig.fp != NULL)
{
messageConfig.fp(buf, length, 0);
}
}
void setEventCallBack(void (*fp)(char *buf, int length, int debugLevel))
{
messageConfig.fp = fp;
char str[512] = "stringTmp";
fp(str, 1, 0);
fp(str, 1, 1);
fp(str, 1, 2);
fp(str, 1, 3);
}
Program.cs in C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
delegate void EventCallback(string _input, int length, int level);
namespace console
{
class Program
{
[DllImport("logging.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void print();
[DllImport("logging.dll")]
public static extern void setEventCallBack(EventCallback fp);
[DllImport("logging.dll")]
public static extern void loggingInitialize();
public static void update(string _input, int length, int level)
{
Console.WriteLine("Message : {0} Length {1} Level {2}", _input, length , level);
}
static void Main(string[] args)
{
loggingInitialize();
setEventCallBack(update);
}
}
}
And in console I could see the message, but there was a error about function pointer.
I didn't understand what error was and I wonder how to debug and how to set parameters, pointers between C# and C.
You have declared your DllImport statements as CallingConvention = CallingConvention.StdCall however in your C code you declared them as __declspec, you need to use CallingConvention = CallingConvention.Cdecl instead on all of the imports (the default if you don't specify is CallingConvention.Winapi which maps to StdCall on most platforms).
Apologies if there is a duplicate - I have struggled to find an answer (I have found a few questions around C++ functions that use Function callbacks, and some answers that use Classes as callbacks when called from C/C++ but..
I am in C#.
I am calling a C++ function
I can not change the signature of the C++ function.
Also, I am using the dynamic method of p/invoke rather then the static binding (my example below);
I can handle the situation where a function takes a simple value, or a struct containing simple values, and returns simple values, but in this instance I have a C function which is taking a call-back object.
Following some ideas online, I tried to make a class that had the same signature, then pinned that class, and passed it in., but I get the C# error of 'Object is non-Blittable' (which it doesn't have any variables in it!).
Header file:
again apologies if there are any mistakes in my example, I've tried to strip all non relevant code and explode the macros, but I hope you understand the essence of what is going on
struct someData_t
{
int length; /**< JSON data length */
char* pData; /*< JSON data */
};
namespace FOO {
class ICallback
{
public: virtual ~ICallback() {}
virtual void Callback(const someData_t &response) = 0;
};
}
extern "C" __declspec(dllexport) void process(const someData_t *inData, FOO::ICallback *listener);
My C# file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace Scratchpad {
class Program {
static void Main(string[] args) {
Console.Out.WriteLine("I'm in Managed C#...");
IntPtr user32 = NativeMethods.LoadLibrary(#"somelongpath\my_c.dll");
IntPtr pAddressOfFunctionToCall = NativeMethods.GetProcAddress(user32, "process");
process proc = (process)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(process));
String someJson = "{ \"type\":\"someTestJson\"}";
byte[] rawdata = Encoding.UTF8.GetBytes(someJson);
someData myData = new someData();
int dataLength = rawdata.Length * Marshal.SizeOf(typeof(byte)); // I realise byte is size 1 but..
myData.length = rawdata.Length;
myData.pData = Marshal.AllocHGlobal(dataLength);
Marshal.Copy(rawdata, 0, myData.pData, dataLength);
Console.Out.WriteLine("Size of mydata: " + Marshal.SizeOf(myData));
IntPtr unmanagedADdr = Marshal.AllocHGlobal(Marshal.SizeOf(myData));
Marshal.StructureToPtr(myData, unmanagedADdr, true);
// ################################################################
// FIXME: This area still working
Callbacker myCallback = new Callbacker();
GCHandle gch = GCHandle.Alloc(myCallback, GCHandleType.Pinned);
IntPtr mycallbackPtr = gch.AddrOfPinnedObject();
// FIXME: close of working area.
// ################################################################
// CALL THE FUNCTION!
proc(unmanagedADdr, mycallbackPtr);
myData = (someData) Marshal.PtrToStructure(unmanagedADdr, typeof(someData));
Marshal.FreeHGlobal(unmanagedADdr);
Marshal.FreeHGlobal(myData.pData);
gch.Free();
unmanagedADdr = IntPtr.Zero;
bool result = NativeMethods.FreeLibrary(user32);
Console.Out.WriteLine("Fini!)");
}
private delegate void process(IntPtr data, IntPtr callback);
[StructLayout(LayoutKind.Sequential)]
private struct someData {
public int length;
public IntPtr pData;
}
private class Callbacker {
public void Callback(someData response) {
Console.WriteLine("callback Worked!!!");
}
}
}
static class NativeMethods {
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
}
}
Any suggestions welcome
You could create some un-managed wrapper for Your managed Callbacker class which implements ICallback interface.
Something like this:
typedef void (*PointerToManagedFunctionToInvoke)(const someData_t&);
class UnmanagedDelegate : public FOO::ICallback {
private:
PointerToManagedFunctionToInvoke managedCallback;
public:
UnmanagedDelegate(PointerToManagedFunctionToInvoke inManagedCallback)
: managedCallback(inManagedCallback) {}
virtual void Callback(const someData_t &response)
{
managedCallback(response);
}
};
// Export this to managed part
UnmanagedDelegate* CreateUnmanagedDelegate(PointerToManagedFunctionToInvoke inManagedCallback)
{
return new UnmanagedDelegate(inManagedCallback);
}
Then at C# part You could create a delegate to marshal as PointerToManagedFunctionToInvoke, pass it to CreateUnmanagedDelegate receive unmanaged implementation of ICallback and use that to pass to your process
Please be aware that managedCallback should stay allocated at C# side while UnmanagedDelegate class object is alive. And You should delete the UnmanagedDelegate object when it is not used anymore.
OR You could use thin C++/CLI to implement this wrapper.
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 am trying to do 2 things: get a return value from a C dll function, and have that same function modify 1 of the members of a structure that is passed to it. After much experimentation I was able to get the function to return a value, but I am still unable to get it to return a modified value to the C# code; the value remains (unmodified) as 0.
I've tried lots of variations (ref, [In,Out], etc) to no avail
using System;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
namespace Vexing.Problem{
public class myIdx : VexingObject {
public myIdx(object _ctx) : base(_ctx) { }
private IPlotObject plot1;
[StructLayout(LayoutKind.Sequential)]
public class PLEX { public int yowser; }
[DllImport("my.dll", CharSet = CharSet.Unicode)]
public static extern int cFunction(
[MarshalAs(UnmanagedType.LPStruct)] PLEX mPlex);
PLEX a;
protected override void Create() { a = new PLEX(); }
protected override void CalcBar() {
int mf = cFunction(a);
plot1.Set(a.yowser); }
}}
// pertinent c dll code
typedef struct s_plex { int yowser;} cplex;
extern "C" __declspec( dllexport )
int cFunction(cplex *Cplex){ Cplex->yowser = 44; return 1;}
Your import declaration is wrong.
Setting the CharSet in your case doesn't make any sense (there are no string parameters in the native function declaration).
If you want to pass class instance, ref/out also must be thrown away (classes being passed by reference).
And the main point: extern "C" __declspec( dllexport ) means CallingConvention.Cdecl.
UPDATE. Here's complete working code sample:
C++ (header):
struct CStruct
{
int myField;
};
extern "C" __declspec( dllexport ) int MyFunction(CStruct *pStruct);
C++ (code):
int MyFunction(CStruct *pStruct)
{
pStruct->myField = 100;
return 1;
}
C#:
[StructLayout(LayoutKind.Sequential)]
class MyStruct
{
public int myField;
};
class Program
{
MyStruct myStruct = new MyStruct();
[DllImport("MyLib.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int MyFunction(MyStruct pStruct);
static void Main(string[] args)
{
var p = new Program();
var result = MyFunction(p.myStruct);
Console.WriteLine("Result: {0}, MyStruct.myField = {1}", result, p.myStruct.myField);
}
}
Prints:
Result: 1, MyStruct.myField = 100