.NET Framework - Best way to interop C# System.String with C++ std::string&

Asked By Edson Manoel
08-May-08 08:17 PM
I have some C++ unmanaged code that takes std::string& arguments (as
reference), and fills them (possibly growing the string).

I want to call this code through PInvoke (DllImport), possibly using
wrapper layers in unmanaged C++ and C#.

I've thought about two approaches:

1) To pass a StringBuilder, this is converted to a char* in C++, the
wrapper code converts the char* to a std::string (copy), and in the
end, copies the std::string content back to the char*. The problem is
that the StringBuilder cannot grow in C++ code (or maybe it can? with
callbacks?), it must be initialized in C# with a size that is not
known beforehand; this also seems insecure.

2) To pass a "ref String", this is converted to a "char** arg". The
wrapper code converts *arg to a string, and in the end, should set
*arg to a new value. I don't know if the *arg pointer can really be
changed... if I create a new char[] block in the C++ heap and pass it
back (setting it to *arg), it results in a heap error (although the
string returned is correct).

All of these approaches involves copying the data (sometimes more than
twice), but, by now, I just want to make it work. Are there better
ways to accomplish this?

Thanks,
Edson
WideCharToMultiByte
(1)
StringToHGlobalAnsi
(1)
PtrToStringAnsi
(1)
StringBuilder
(1)
ConvertSS2MS
(1)
DllImport
(1)
PtrToStringChars
(1)
CoTaskMemAlloc
(1)
  Pixel.to.life replied...
08-May-08 08:17 PM
Not sure if this would help, but here it is:

System::Void ManagedToBasicString(String^ iSourceStr,
std::string &oTargetStr)
{
oTargetStr =3D std::string((char*)
(void*)Marshal::StringToHGlobalAnsi(iSourceStr));
return;
}

Good luck:-)
  Pixel.to.life replied...
08-May-08 08:17 PM
How about

- you write a mixed (managed/native) wrapper
- you pass a System::String by reference to the wrapper
- in the wrapper scope, you fetch the std::string from unmanaged code,
and assign to the referenced System::String?

I dont see why that wont work.
  Jeff Louie replied...
06-May-08 03:03 PM
Edson... Is it possible that your code could take a managed string as an
in
parameter, convert the managed string to char*, call the C++ code, and
then
RETURN a new managed string on method exit? The following code was a
quick hack taking a std::string and returning a managed string. I
readily
admit I only dabble in STL/CLI.

using namespace System;
using namespace cliext;  // STL/CLR
using namespace System::Runtime::InteropServices;   // Marshal

class Util {
public:
/////////////////////////////////////////////////////////
//  ** ConvertSS2MS  **                                //
// Converts std::string to managed String^             //
// Parameters constant std::string in by ref           //
// Returns String^ out                                 //
// Static Public Class method                          //
// Internally converts to most common denominator      //
// char* on heap using new and delete                  //
// std::string ref in cannot be null, but may be empty //
// Returns empty string on exception                   //
// JAL 12/04/08                                        //
/////////////////////////////////////////////////////////

static String^ ConvertSS2MS(const std::string& in) {
String^ out= L""; // L wchar_t typedef unsigned short
char* str=0;
size_t size= strlen(in.c_str()) +1; // +1 for null terminator

try {
//const char *p= in.c_str();  // use p immediately
//out= Marshal::PtrToStringAnsi(static_cast<IntPtr>(p)); //
error on const char *
str= new char[size]; // null terminated
strcpy_s(str,size,in.c_str());  // create longer lived copy in
non const char array
// strcpy_s has new security enhancements over strcpy,
size includes null char
out= Marshal::PtrToStringAnsi(static_cast<IntPtr>(str));  //
or safe_cast?
}
catch(...) { // eat the exception
out= L"";  // returns empty string on exception
}
finally {  // clean up memory
if(str) {
delete [] str;  // release char[] on unmanaged heap,
delete [] calls destructors
}
}
return out;
} // end ConvertSS2MS
}; // end class Util

Regards,
Jeff

*** Sent via Developersdex http://www.developersdex.com ***
  Chris Nahr replied...
07-May-08 02:40 AM
On Tue, 6 May 2008 04:52:30 -0700 (PDT), Edson Manoel


You always will have to copy the data because .NET strings are
internally encoded in Unicode UTF-16 which uses two bytes per
character, whereas C++ std::string uses an implementation-specific
ASCII superset with one byte per character.  So there's no way to just
share a common buffer if you were thinking of that.
--
http://www.kynosarges.de
  Edson Manoel replied...
08-May-08 08:19 PM
Thanks for all the answers!

I solved it passing a ref string; this is marshaled to a char**.
Dereferencing it in C++, I get a char*, then I build a std::string
from it and pass it to my C++ method. After the call to the method, I
use CoTaskMemFree to free the original char*, and then I allocate a
new char* buffer with CoTaskMemAlloc, with the returned std::string
size().

So, in the end, to just pass one string and get it returned, there are
at least 4 copies being made (C# string to char* conversion; char* to
std::string / input and output), 3 allocations (char*, std::string and
my own CoTaskMemAlloc) and 3 deallocations. Well, at least, It Just
Works=AE :p. I wish that it were faster, but it seems that C# (and CLR
in general) weren't made to speak with *unmanaged* C++ code... (only
C).

I guess this does not leak memory, am I right?

I've learned that .Net uses CoTaskMemAlloc/CoTaskMemFree through some
websites (Msdn, etc.). I couldn't debug inside the Microsoft code that
does the marshaling (/interop)... is there any way to do this, or am I
not allowed to see the magic trickery that is going on besides the
curtain?

Thanks,
Edson
  Ben Voigt [C++ MVP] replied...
08-May-08 10:53 AM
Well, this is only partly true.  There is no support in the CLR for C++
interop other than what is needed to make the C++/CLI compiler work.  You
are expected to use C++/CLI for tasks like this.

Here is how you would avoid the extra copies (still need Unicode conversion
of course):

void LetsNativeCodeOperateOnManagedString(System::String^% managedString)
{
int len = managedString->Length;
std::string nativeString;
nativeString.reserve(len + 1);
WideCharToMultiByte(..., PtrToStringChars(managedString), len,
&(nativeString[0]), len, ...);

callNativeFunction(nativeString);

managedString = gcnew String^(nativeString, nativeString.length());
}
  Ben Voigt [C++ MVP] replied...
08-May-08 10:57 AM
oops, should be gcnew String, not gcnew String^
  Ben Voigt [C++ MVP] replied...
08-May-08 03:34 PM
Of course 2008 gives you all of this in the Microsoft-provided marshal_as
function.
help
NET Framework StringBuilder much much faster and better than String for concatenation !!! StringBuilder better and faster than string for adding many strings. Look at the below. It's amazing how much faster StringBuilder is than string. The last loop below is telling: for adding 200000 strings of 8 char each, string took over 25 minutes while StringBuilder took 40 milliseconds! Can anybody explain such a radical difference? The hardware running this program was a Pentium IV with 2 GB RAM. RL / / stringbuilder much faster than string in concatenation / / / / / / / / / / / / / / using System; using System.Collections.Generic; using System.Linq are: {0}, {1}", myUpdateTime.txtConcatTime, myUpdateTime.txtStringBTime); } } } / * * OUTPUT * results: * for 1000 iterations: string = 10.01ms; stringbuilder = 0 * for 5000 iterations: string = 410.6ms; stringbuilder = 0 * for 50k iterations: sring = 79013 ms; stringbuilder = 0; * for 10k iterations : string = 1772.5 ms; stringbuilder = 0; * for 75k iterations : string = 186237
seems to indicate that regex is slower. String Replace : 15 -> 12 x 6666666 : 6, 6875 StringBuilder Replace : 15 -> 12 x 6666666 : 6, 546875 Regex Replace : 15 -> 12 x 6666666 : 27, 1875 Optimized : 15 -> 12 x 6666666 : 15, 828125 String Replace : 960 -> 768 x 104166 : 3, 3125 StringBuilder Replace : 960 -> 768 x 104166 : 2, 03125 Regex Replace : 960 -> 768 x 104166 : 17, 421875 Optimized : 960 -> 768 x 104166 : 13, 4375 String Replace : 1000 -> 1000 x 100000 : 1, 15625 StringBuilder Replace : 1000 -> 1000 x 100000 : 2, 4375 Regex Replace : 1000 -> 1000 x 100000 : 3, 78125 n, (dt2 - dt1).TotalSeconds)); } private static void TestStringBuilderReplace(string s) { int n = N / s.Length; StringBuilder sb = new StringBuilder(s); string s2 = null; DateTime dt1 = DateTime.Now; for(int i = 0; i < n; i Replace(" \ r", "").Replace(" \ n", "").Replace(" \ t", } DateTime dt2 = DateTime.Now; Console.WriteLine(String.Format(FMT, "StringBuilder Replace", s.Length, s2.Length, n, (dt2 - dt1).TotalSeconds)); } private static void TestRegexReplace(string s And I was right. The thing that scares me though, is that for String manipulation & Stringbuilder, the impact of a larger amount to remove has 'little' impact. In fact a stringbuilder the best option if you have a lot to remove. The Regular Expressions get much
pointer + = mfTransactionType 'etc etc There appears no native VB.Net equivalent to this method. The StringBuilder Class does not have this method either. It should be noted that the Replace method for either String or StringBuilder would not be appropriate for the above. The above algorithm that I use will actually True" and "False" in the boxes. So, you will have to stick either with the StringBuilder or the & operator for concatenation. If I understood correctly, you have to build a new content of a previous one, right? To make this considerably faster, you can use one Stringbuilder object with maximum capacity (120 in this case), and set it is Length property to probably much slower. Instead something like: Module whatever Sub AddWithPadding(ByVal sb As System.Text.StringBuilder, _ ByVal Text As String, ByVal TotalLength As Integer) sb.Append(Text) sb.Append(New the time with a little test: Shared Sub test() Dim sb As New System.Text.StringBuilder(10) Dim watch = Stopwatch.StartNew For i = 1 To 30000000 sb.Length = 0 sb.AddWithPadding it as you say returns a new string. It basically does some checks, uses a StringBuilder to build the new string, and then set your string reference to the StringBuilder instances .ToString - - Tom Shelton it is not clear from your original post, but the Mid
NET Framework about the string Class and the StringBuilder class Hello! I read in a book and here is a question and the answer that I'm not satisfied with. When should you use the StringBuilder class instead of the String class. 1.When building a string from shorter strings. 2 3 because string object is immutable so here is it an advantage to use the StringBuilder class but according to the book is the right answer 1. But if you build a string from shorter strings you don't have to use a StringBuilder class as string s = "This" + "is" + "a" + "Test"; I can accept numer 1 if they Tony Johansson" <johansson.anders. . .@telia.com> ble The book must have been referring to concatentation. StringBuilder will be more efficient in that scenario because it uses techniques that require fewer copy See http: / / pobox.com / ~skeet / csharp / str = ingbuilder.html for more information. . table ng is stringbuilder.html Oh, by the way, I should point out that you're example is unique will occur at compile time since the individual strings are constant. In that respect the StringBuilder would not perform any better. On Sep 22, 1:25 pm, "Tony Johansson" <johansson.anders Test"; Example 2: string s; s + = "This"; s + = "is"; s + = "a"; s + = "Test"; Example 3: StringBuilder s = new StringBuilder(); s.Append("This"); s.Append("is"); s.Append("a"); s.Append("Test