Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Custom Marshalling


Custom Marshalling

In the last newsletter, I talked about .NET custom marshalling and explained that it is used to allow you to determine how data is marshalled between the managed and unmanaged worlds. I also explained that Managed C++ has the IJW (It Just Works!) technology to allow you to call unmanaged code from managed code, which allows you to call unmanaged code as if you were writing native code. Although it appears that custom marshalling is not needed by Managed C++, I described two situations where it is needed: marshalling function pointers (which can only be achieved with .NET marshalling) and using custom marshalling. Custom marshalling is useful because you can get the marshaler to convert the data into a format that is more convenient for you to use. This is what I’ll describe in this newsletter. I’ll use C++, but the principles are the same for other languages.

A custom marshaler implements a .NET interface called ICustomMarshaler. This is one of those unusual .NET interfaces that requires that you implement a static method as well as the interface members. Since you cannot have a static method as part of an interface, you only learn about this requirement by reading the documentation. I would have preferred an attribute on the interface to specify that another member is required because the compiler would then have enough information to decide exactly what the marshaler object should implement. The static method is called GetInstance, and it is used to create an instance of the custom marshaler:

static ICustomMarshaler* GetInstance(String* pstrCookie);

The parameter of this method is used to pass information to the constructor of the marshaler class. To specify that a custom marshaler is used, you apply the [MarshalAs] attribute on the parameter that you want to marshal. The unnamed parameter is passed UnmanagedType::CustomMarshaler and the MarshalType named parameter is used to specify the name of the type of the marshaler. The runtime locates this type and calls the GetInstance method. The MarshalCookie named parameter of [MarshalAs] is the string that is passed to the GetInstance method.

ICustomMarshaler allows you to create objects when data is marshalled to the managed world, and it allows you to clean up when the objects are no longer needed. For data passing in the other direction—to the native world—it allows you to allocate native memory and free that memory when it is no longer needed.

In the sample code, I provide a marshaler for a string array. In C++, a string array is an array of string pointers, and each of those pointers will point to the actual character buffer that holds the string. The strings may or may not be in contiguous memory. This is a problem when passing data between processes because a pointer in one process is meaningless in another. The simplest solution is to copy all the strings into a contiguous buffer and rely on the fact that strings are NUL (zero) terminated. Such a buffer will have a NUL character at the end of each string and a double NUL character to mark the end of the array. Many Win32 APIs use this scheme; one example is GetLogicalDriveStrings, another is GetEnvironmentStrings.

If I was to call either of these through IJW, I would still have to manipulate the buffer passed to me by the API to extract the individual strings. Instead, I can use a custom marshaler to do the work. Here is the declaration of the GetEnvironmentStrings:

DllImport("kernel32.dll")]
[returnvalue:MarshalAs(
 UnmanagedType::CustomMarshaler,
 MarshalType="StringArrayMarshaler")]
extern String* GetEnvironmentStrings() [];

The Win32 function GetEnvironmentStrings actually returns a LPVOID pointing to the character array, and the StringArrayMarshaler class is used to convert this into a managed array of strings. The key method of this class is:

Object* MarshalNativeToManaged(IntPtr pNativeData)
{
   // pNativeData is a pointer to the unmanaged data
   // we know that it is an array of characters, the end of the buffer
   // has a double null \0\0
   char* p = static_cast<char*>(static_cast<void*>(pNativeData));
   if (p == 0) return 0;
   ArrayList* array = new ArrayList();
   while (*p != 0)
   {
      String * s = new String(p);
      array->Add(s);
      p += s->Length + 1;
   }
   return array->ToArray(__typeof(String));
}

The sample code calls the native function like this:

String* p[] = GetEnvironmentStrings();

As you can see, Managed C++ treats the method as if it returns a managed array.


Richard Grimes speaks at conferences and writes extensively on .NET, COM, and COM+. He is the author of Developing Applications with Visual Studio .NET (Addison-Wesley, 2002). If you have comments about this topic, Richard can be reached at [email protected].


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.