Passing strings from C# to C++ DLL and back -- minimal example
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");
}
}