Feeds:
Posts
Comments

Archive for April, 2007

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.

Read Full Post »

Follow

Get every new post delivered to your Inbox.