A while ago, I posted an entry about marshalling a managed System.String into an unmanaged C string, specifically a plain char*. The solution I suggested, back in 2007, involved calling Marshal::StringToHGlobalAuto method to allocate memory and copy the string data into it, and then cast the HGlobal pointer into a char*.
It seems that in Visual Studio 2008 a new way of doing it was added, as part of the new Marshalling Library. This library provides a whole set of conversions between System.String and popular unmanaged string representations, like char*, wchar_t*, BSTR, CStringT<wchar_t> and others I am even less familiar with.
The smarter string representations, like std::string, have their own destructors so I can carelessly let them drop out of scope without worrying about leaks. The more primitive ones, like char*, need to be explicitly released, so that’s why the Marshalling Library contains a new (managed) class called marshal_context which gives me exactly this explicit release.
Let’s compare my old code with the new:
1: const char* unmanagedString = NULL;
2: try
3: {
4: String^ managedString = gcnew String("managed string");
5: // Note the double cast.
6: unmanagedString = (char*)(void*)Marshal::StringToHGlobalAnsi(managedString);
7: }
8: finally
9: {
10: // Don't forget to release. Note the ugly casts again.
11: Marshal::FreeHGlobal((IntPtr)(void*)unmanagedString);
12: }
And the new:
1: marshal_context^ context = gcnew marshal_context();
2: String^ managedString = gcnew String("managed string");
3: const char* unmanagedString = context->marshal_as<const char*>( managedString );
Much shorter, I’m sure you’ll agree. And neater – no need for all the icky, icky casting between different pointer types. And most importantly, I don’t have to explicitly release the char* – the marshal_context class keeps a reference to all the strings that were marshalled through it, and when it goes out of scope its destructor makes sure to release them all. Very efficient, all in all.