[ACCEPTED]-Pass C# string to C++ and pass C++ result (string, char*.. whatever) to C#-interop

Accepted answer
Score: 19

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

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

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

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

Matching C# would be the following:

void GetString( StringBuilder buffer, ref int bufferSize );

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

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

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

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

Score: 2

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

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

Score: 2

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

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

Score: 2

I had to convert a unicode C# string to 5 a multibyte representation in order to convert 4 to char* in c++ (this is partial one way 3 solution)

I found the following very useful 2

string st; 
IntPtr stPtr = Marshal.StringToHGlobalAnsi(st); 
// Do your thing in C++ 

Marshal.FreeHGlobal(stPtr); 

This may be inefficient and not in C# manner, I'm 1 new to C#.

More Related questions