Passing strings from C# to C++ DLL and back -- minimal example

asutherland picture asutherland · Dec 23, 2013 · Viewed 62.5k times · Source

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.

Answer

David Heffernan picture David Heffernan · Dec 23, 2013

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);