I have this define in C++ header file
extern "C" __declspec(dllexport) const char* __stdcall GetId(const My_Polyhedron *obj);
and has this in C#
[DllImport("polyhedra.dll", CallingConvention = CallingConvention.Cdecl)]
static private extern string GetId(IntPtr obj);
It returns me gobbledygook
îþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþîþ2§{€ûW#_‹p
in .net 4.0, but it works well in .net 3.5.
Note:
I have tried charset=...(all possible settings, e.g. Charset.Unicode) in DllImport.
I have tried to let it returns IntPtr instead of string, then use Marshal.PtrToStringAnsi convert it back to string.
I have tried StringBuilder instead of string.
All the solutions above do not work.
If you are the owner of the C++ code, you can modify the function so that it returns the string as a out parameter:
extern "C" __declspec(dllexport) void __stdcall GetId(
const My_Polyhedron *obj, char* result);
Then use this import in C#:
[DllImport("polyhedra.dll", CallingConvention = CallingConvention.Cdecl)]
static private extern void GetId(IntPtr obj, StringBuilder result);
Please note that result is a StringBuilder instead of a string.
You have to allocate enough space first by making the StringBuilder big enough.
Call it like this:
StringBuilder result = new StringBuilder(1000);
GetId(obj, result);
Use result.ToString() to get the string.
There's the return attribute (it isn't really a attribute) that specifies how marshal the returned value:
[return: MarshalAs(UnmanagedType.Bool)]
But I didn't tried it on your code.
Maybe this helps...
Related
I have a problem, I'm doing some C#<->C++ interop via a C interface and I have a function that I import from a dll like this
[DllImport("pixi_api", EntryPoint = "pixi_sprite_asm_file", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PixiStringMarshaler))]
private static extern pixi_string _pixi_sprite_asm_file(IntPtr sprite);
and it gets called, for example, like this
var cstr = _pixi_sprite_asm_file(data_pointer);
the actual function in the DLL looks like this
EXPORT pixi_string pixi_sprite_asm_file(pixi_sprite_t pixi_sprite_ptr) {
const char* const ptr = pixi_sprite_ptr->asm_file;
pixi_string str = {ptr, static_cast<int>(strlen(ptr))};
return str;
}
however there's a problem:
if I print the data_pointer passed from the C# code, before the call to _pixi_sprite_asm_file and then I print it while inside the c native function call (the pixi_sprite_ptr), I get 2 completely different pointers and as such, everything explodes.
e.g:
data_pointer => 191BF4A0300
pixi_sprite_ptr => 0x00007ffc0e1ebb92
I have other methods that return simple-to-marshal types such as ints, that don't have this problem and the pointer gets passed unchanged.
e.g this one works, the pointers are the same and it returns the expected value:
[DllImport("pixi_api", EntryPoint = "pixi_sprite_line", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I4)]
private static extern int _pixi_sprite_line(IntPtr sprite);
I have no clue what could be going wrong.
An example of what it looks like when called
I want to call in C# a function from unmanaged library with following signature:
DLL_EXPORT int xli_open(char *, int , struct t_info *);
In legacy code on Windows 7 the function is improrted as:
[DllImport(DRIVER_FILENAME, EntryPoint = "xli_open", CallingConvention = CallingConvention.Cdecl)]
public static extern int xli_open(string device, int hndl, ref t_info tInfo);
On Windows 10 I get an AccessViolationException for calling the function and I import the function as:
[DllImport(DRIVER_FILENAME, EntryPoint = "xli_open", CallingConvention = CallingConvention.Cdecl)]
public static extern int xli_open(ref string device, int hndl, ref t_info tInfo);
I don't get AccessViolationException anymore, but it seems that the function gets an empty string. Is the declaration right? And why does the pass of the ref parameter work (would string not be passed by reference anyway?)?
Assuming that you are passing the text to the function, then plain by value string is correct. The access violation is likely because of some other error. Perhaps the structure definition does not match, perhaps the calling convention is wrong. Or perhaps some other mistake, but the string argument appears to be correct.
I am trying to make the absolute simplest minimal example of how to pass strings to and from a C++ DLL in C#.
My C++ looks like this:
using std::string;
extern "C" {
string concat(string a, string b){
return a + b;
}
}
With a header like
using std::string;
extern "C" {
// Returns a + b
__declspec(dllexport) string concat(string a, string b);
}
My C# is
[DllImport("*****.dll", CallingConvention = CallingConvention.Cdecl)]
static extern string concat(string a, string b);
}
And I am calling it with:
Console.WriteLine(concat("a", "b"));
But this gives a System.AccessViolationException. This seems like it out to be the most trivial thing to deal with, but I am completely stuck on it. When I tried to do a similar experiment with a function "Add" that took two doubles and returned a double I had no problems.
You cannot pass a C++ std::string across an interop boundary. You cannot create one of those in your C# code. So your code can never work.
You need to use interop friendly types at the interop boundary. For instance, null-terminated arrays of characters. That works well when you allocate and deallocate the memory in the same module. So, it's simple enough when passing data from C# to C++.
C++
void foo(const char *str)
{
// do something with str
}
C#
[DllImport("...", CallingConvention = CallingConvention.Cdecl)
static extern void foo(string str);
....
foo("bar");
In the other direction you would typically expect the caller to allocate the buffer, into which the callee can write:
C++
void foo(char *str, int len)
{
// write no more than len characters into str
}
C#
[DllImport("...", CallingConvention = CallingConvention.Cdecl)
static extern void foo(StringBuilder str, int len);
....
StringBuilder sb = new StringBuilder(10);
foo(sb, sb.Capacity);
This is the simplest way I like - pass a string in, and use a lambda to get the response
C#
public delegate void ResponseDelegate(string s);
[DllImport(#"MyDLL.dll", EntryPoint ="Foo", CallingConvention = CallingConvention.StdCall)]
public static extern void Foo(string str, ResponseDelegate response);
...
Foo("Input", s =>
{
// response is returned in s - do what you want with it
});
C++
typedef void(_stdcall *LPEXTFUNCRESPOND) (LPCSTR s);
extern "C"
{
__declspec(dllexport) void __stdcall Foo(const char *str, LPEXTFUNCRESPOND respond)
{
// Input is in str
// Put your response in respond()
respond("HELLO");
}
}
I have a dll which contains this function:
int __stdcall PrnText(char *printtext);
In Windows Forms i have this code to invoke the dll:
[DllImport("Printing.dll", EntryPoint = "PrnText", CharSet = CharSet.Ansi)]
public static extern int PrnText(char *printtext);
When i call the function in C# code i get an error like this : " cannot cast string to char*
PrnText("Hello World");
What parameter should i give to PrnText() to make it work?
Later edit:
Parameter: printtext
pointer to string containing text to be printed
The CLR knows how to convert a string to an unmanaged char* at runtime. You should use a signature which accepts a string, as such:
public static extern int PrnText(string printtext);
Note that this will work only if the parameter is input only.
I want to send a string from C# to a function in a native C++ DLL.
Here is my code:
The C# side:
[DllImport(#"Native3DHandler.dll", EntryPoint = "#22",
CharSet = CharSet.Unicode)]
private static extern void func1(byte[] path);
public void func2(string path)
{
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] arr = encoding.GetBytes(path);
func1(this.something, arr);
}
The C++ side:
void func1(char *path)
{
//...
}
What I get in the C++ side is an empty string, every time, no matter what I send.
Help?
Thanks.
It looks like you have 2 issues. The first is your native C++ uses an ANSI string but you are specifying unicode. Secondly, it's easiest to just marshal a string as a string.
Try changing the DllImport to the following
[DllImport(
#"Native3DHandler.dll",
EntryPoint = "#22",
CharSet = CharSet.Ansi)]
private static extern void func1(void* something, [In] string path);
Works fine for me with no extra marshalling instructions in VS2008:
C# side:
[DllImport("Test.dll")]
public static extern void getString(StringBuilder theString, int bufferSize);
func()
{
StringBuilder tstStr = new StringBuilder(BufSize);
getString(tstStr, BufSize);
}
C++ side:
extern "C" __declspec(dllexport) void getString(char* str, int bufferSize)
{
strcpy_s(str, bufferSize, "FOOBAR");
}
Your declaration is wrong. The parameter should be of type string, and you should set the character set encoding to Ansi, like so:
[DllImport(#"Native3DHandler.dll", EntryPoint = "#22",
CharSet = CharSet.Ansi)]
private static extern void func1(string path);
This assumes that you are not modifying the contents of the path variable in your C++ code. Then, you pass the string parameter directly (no need for the wrapper).
If you just want to send a string, just declare func1's parameter as a string. If you want to receive a string, declare it as a StringBuilder and allocate enough buffer space for what you want to receive.
Default Marshaling for Strings
http://msdn.microsoft.com/en-us/library/s9ts558h.aspx