November 20, 2014
Hot Topics:

Visual C++ Orcas Marshaling Library

  • August 2, 2007
  • By Nick Wienholt
  • Send Email »
  • More Articles »

One of the frequent criticisms of C++ is that it offers too many options to perform the same task. Consider the number of different options that exist for writing a text file that ship out-of-the-box with Visual C++—the C Runtime Libraries, the Standard C++ libraries, the Windows SDK I/O APIs, the .NET file APIs, MFC, and even the FileSystemObjects available through COM. All of these libraries can write an identical text file, yet each has their own subtle advantages and pitfalls.

In the same way, the C++ developer has a wide array of options for converting between types in the .NET and COM world. Although most developers may resort to hand-coded conversion that involves boiling the data of a type down to blittable primitives (blittable data types are those that have the same in-memory format in the managed and native world, such as four-byte signed integers or Unicode characters), there is also the option of using the .NET type Marshal, which has various static methods for converting from one data type to another. Another option available to a C++ code compiled with /clr:pure or /clr:safe is the use of properties on the DllImport attribute to specify how a managed type is converted to a native type. With Visual C++ Orcas, there is a new technique for conversion: the C++ Marshaling Library.

The Marshaling Library provides a template-based approach to converting between native and managed data types. For simple conversions that have no memory management complexities, the syntax for converting from one data type to another is very simple:

#include <msclr\marshal.h>

TCHAR* c_style_string = _T("My C style string");
System::String^ dotNetString =
   msclr::interop::marshal_as<System::String^>(c_style_string);

When the returned object requires explicit memory clean-up, context-based marshaling needs to be used. With context-based marshaling, a managed marshal_context object needs to be created and passed to the marshaling method, and the results of the marshaling call are only valid for the lifetime of the marshal_context object. To convert the managed .NET string back to a C-style string, the following code would be required.

//declare new marshal_context
marshal_context^ mc = gcnew marshal_context();

//convert string from .NET to C-style
const TCHAR* new_c_style_string =
   mc->marshal_as<const TCHAR*>(dotNetString);

//get the length of the convert string
int strLen = (int)_tcslen(new_c_style_string) + 1;

//allocate a new character array to hold the string
TCHAR* copy_of_new_c_style_string = new TCHAR(strLen);

//copy to the new array
_tcscpy_s(copy_of_new_c_style_string, strLen, new_c_style_string);

//delete the marshaling context
delete mc;

When the marshal_context is deleted, any memory that has been allocated during marshaling calls is deleted, and this means a copy of the marshaled data needs to be made if the converted object will be accessed once the marshal_context object has been deleted.

A wide range of conversions is currently supported in the Beta 1 release of Visual C++ Orcas. The main header file (marshal.h) defines the base marshal type and string conversions between System::String and C-style strings (char* and wchar_t*). Marshal_atl.h provides conversion from COM strings (CComBSTR) and MFC strings(CStringA and CStringW) to managed strings, and marshal_cppstd.h goes between the standard C++ strings (std::wstring and std::string) to .NET. The final marshaling header file (marshal_windows.h) provides conversion between the managed IntPtr type and the native HANDLE type, and also supports the conversion to and from System::String for another two COM string types (_bstr_t and BSTR).

Extending the Marshaling Library

Extending the Marshaling Library is a relatively simple undertaking. A new marshal function simply needs to be added to a header file, and the code that wants to use the new marshaler uses the marshal_as context in exactly the same manner as the in-built conversion functions. The marshal functions that ship with Beta 1 do not support conversions between the SecureString class in .NET and the common native types, and this is a good candidate for a sample extension. A conversion to and from wchar_t* will be covered because the pattern for the other native string types will be very similar.

The first point to note is that there is no requirement to support a bi-directional conversion, but most users will probably expect one. For the conversion to a secure string, the marshal method is quite simple:

template <>
inline System::Security::SecureString^ msclr::interop::marshal_as
   (wchar_t* const & _from_object)
{
   if (_from_object == NULL)
   {
      return nullptr;
   }
   return gcnew System::Security::SecureString(_from_object,
      (int)wcslen(_from_object));
}

The code to convert to a SecureString using a template specialization through the Marshaling Library is no different or no more complex than the code that would need to be written for any other helper function. The input parameter is checked against NULL, and if a valid parameter has been passed, a new SecureString is created using one of its overloaded constructors.





Page 1 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Rocket Fuel