The need to marshal between native and managed types is a very frequent scenario in mixed-mode programming. This is specially true when it comes to strings – when you’ve got MFC strings, COM strings, standard C++ strings and CLR strings and need to convert between those types. In fact that’s what prompted me to write the StringConvertor class for managed-unmanaged string conversions. While I did realize that I had misspelled converter as convertor I decided to leave it like that so I got a unique class-name and that way I could avoid Google dilution (there are dozens of other StringConverter classes, specially Java based ones).
Anyway, in the Orcas release of Visual C++, the VC++ team have added a mixed-mode marshalling library which primarily consists of the marshal_as template function. While marshal_as is technically not limited to string conversions, that is its most important role as of now. Using it is very similar to using one of the C++ cast operators such as static_cast – though be aware that you are not really casting here, you are doing a conversion – which may or may not be a different thing from a mere cast.
Here’s a code snippet that shows how to convert various native strings to System::String
String^ clrString;
const char* pcszHello = "hello world";
clrString = marshal_as<String^>(pcszHello);
wchar_t* pwszHello = L"hello wide world";
clrString = marshal_as<String^>(pwszHello);
bstr_t bstrtHello("hello bstr_t world");
clrString = marshal_as<String^>(bstrtHello);
std::string stdHello = "hello from std::string";
clrString = marshal_as<String^>(stdHello);
CString mfcString("hello from CString");
clrString = marshal_as<String^>(mfcString);
CComBSTR atrBSTR(L"hello from CComBSTR");
clrString = marshal_as<String^>(atrBSTR);
The reverse conversion is also similar.
String^ clrString = "Original System::String"; std::string stdHello = marshal_as<std::string>(clrString); CString mfcString = marshal_as<CString>(clrString); CComBSTR atrBSTR = marshal_as<CComBSTR>(clrString);
You cannot directly use marshal_as for converting a String^ to a const char*, a const wchar_t* or a BSTR – because those conversions require the unmanaged resources to be freed after use. For those, you need to use a context object as shown below.
marshal_context context; const char* pcszHello = context.marshal_as<const char*>(clrString); const wchar_t* pcwszHello = context.marshal_as<const wchar_t*>(clrString); BSTR bstrString = context.marshal_as<BSTR>(clrString); Console::WriteLine(context._clean_up_list.Count);
The context object keeps track of the allocated objects and frees them in its destructor. Internally it maintains a linked list of objects that are allocated, and the output of the Console::WriteLine in the above code will be 3 (as we have allocated three objects using the context object). Initially I found this a little annoying to do, but I couldn’t think of any alternate solution that was more elegant and where we could avoid using the context object. In fact, in my StringConvertor class I had internally used an std::vector to store all allocated objects so I could free them in the destructor.