Pass C# string to C++ and pass C++ result (string, char*.. whatever) to C#

Solution 1:

What I've found to work best is to be more explicit about what's going on here. Having a string as return type is probably not recommended in this situation.

A common approach is to have the C++ side be passed the buffer and buffer size. If it's not big enough for what GetString has to put in it, the bufferSize variable is modified to indicate what an appropriate size would be. The calling program (C#) would then increase the size of the buffer to the appropriate size.

If this is your exported dll function (C++):

extern "C" __declspec void GetString( char* buffer, int* bufferSize );

Matching C# would be the following:

void GetString( StringBuilder buffer, ref int bufferSize );

So to use this in C# you would then do something like the following:

int bufferSize = 512;
StringBuilder buffer = new StringBuilder( bufferSize );
GetString( buffer, ref bufferSize );

Solution 2:

The only good way that I know of doing this is to write a .NET C++ wrapper class using Managed C++ Extensions, and within the .NET C++ object call your native C++ code. There are functions in the managed extensions to convert a System.String to a char* or any other type of unmanaged string.

Basically you create a .NET class using C++ and expose it from an assembly, and internally to that assembly you can call your native C++ code. The other way is to add a pure C function to your C++ code using P/Invoke and then call your C code from C# and have your C function call your C++ code. This will work, but I tend to try to use managed code as much as possible.

Solution 3:

The biggest problem with passing strings from C++ back to C# is the memory allocation. The GC should be able to know how to cleanup the memory allocated for this string. Since C# has extensive COm interop support, it does know about COM BSTRs and how to allocate and deallocate these. Thus the easiest way to do this would be to use BSTR on the C++ side and string on the C# side.

Note, using BSTRs does not imply that your function has to be expose through COM.

Solution 4:

The "string" return value is the problem. The P/Invoke marshaller is going to call CoTaskMemFree() on the pointer you return. That's not going to work well unless you used CoTaskMemAlloc() in your C/C++ code to allocate the string buffer. Which is a fairly unusual thing to do.

The best solution is to allow the caller of your code to pass a pointer to a buffer and the buffer length to you as arguments. That way all memory allocation happens on one side. Scott showed you how to do this.