//
you're reading...
Interop Marshaling, Managed Structures

Passing a Pointer to a Structure from C# to C++ Part 1.

1. Introduction.

1.1 Unmanaged APIs, especially those written in C++, sometimes require pointers to structures to be passed as parameters.

1.2 This may be a breeze for C++ developers but for C# programmers, careful attention must be paid to the contents of the structures.

1.3 This is due mainly to the fact that a managed structure needs to be marshaled to an unmanaged version before it can be passed to an unmanaged API.

1.4 When the parameter is passed by value, the contents of the structure is completely managed by the interop marshaler and, as long as the StructLayoutAttribute has been properly applied to the definition of the structure and MarshalAsAttributes have been judiciously attached to the fields, reference type data (e.g. strings, arrays) are usually handled properly with no memory leakage.

1.5 When the parameter is passed as a pointer, it needs to be passed as an IntPtr type. This is where potential problems (memory leakage) may set in if care is not taken concerning certain contents of the structure.

1.6 This multipart series of blogs aims to demonstrate this and provide approriate advise. In part 1, I shall attempt to explain in general how structures are marshaled (by value and by pointer) from managed code to unmanaged code.

1.7 The principles expounded here will serve as a base for understanding more complex marshaling that is demonstrated in later parts.

2. A Simple Structure with a Blittable Type.

2.1 I will begin with a simple structure defined in C# as follows :

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct TestStructSimple
{
  public Int32 m_int01;
}

Note that the single member field m_int01 is of type Int32 which is a blittable type and so it is embedded as part of the memory block of an instance of such a structure in managed memory.

Blittable types have a common memory representation in managed and unmanaged memory and so they can be directly transferred from managed to unmanaged memory (and vice versa) without being decorated with any MarshalAsAttributes.

For more information on this, please refer to : Passing Structures between Managed and Unmanaged Code.

2.2 Such a structure would have the following C++ counterpart :

#pragma pack(1)

struct TestStructSimple
{
  int m_int01;
};

2.3 Let’s say we have the following API written in C++ :

void __stdcall DisplayTestStructSimple(/*[in]*/ TestStructSimple test_struct_simple)
{
  printf
  (
    "test_struct_simple.m_int01 : [%d].\r\n",
    test_struct_simple.m_int01
  );
}

The TestStructSimple parameter test_struct_simple is passed by value into the API.

2.4 The following is how it would be declared in C# :

[DllImport("TestDLL01.dll", CallingConvention = CallingConvention.StdCall)]
private static extern void DisplayTestStructSimple([In] TestStructSimple test_struct_simple);

2.5 And the following is a sample call to this API :

static void DisplayTestStructSimple()
{
  TestStructSimple test_struct_simple = new TestStructSimple();

  test_struct_simple.m_int01 = 100;

  DisplayTestStructSimple(test_struct_simple);
}

2.6 When DisplayTestStructSimple() is to be called, the interop marshaler will build an unmanaged version of TestStructSimple and then pass it to the API.

2.7 Now because the TestStructSimple parameter is to be passed by value, the unmanaged version of the structure will be built on the call stack.

2.8 When DisplayTestStructSimple() completes, the memory occuppied by test_struct_simple (on the stack) is automatically recovered.

2.9 As mentioned in point 2.1 above, the single member of test_struct_simple (i.e. m_int01 of type Int32) is a blittable type data and so it is embedded inline as part of the memory block for test_struct_simple itself. When the memory for test_struct_simple is recovered, memory for the m_int01 field is recovered automatically. All is simple so far.

3. Simple Structure Passed by Pointer.

3.1 What happens if TestStructSimple is to be passed as a pointer to an API ?

3.2 As far as the structure itself is concerned, the rules are the same : an unmanaged representation of the structure must be constructed in unmanaged memory. After such an unmanaged structure has been built up, a pointer to can be passed to the API.

3.3 Let’s say we have the following C++ API :

void __stdcall DisplayTestStructSimple_ByPointer
(
  /*[in]*/ TestStructSimple* ptest_struct_simple
)
{
  printf
  (
    "ptest_struct_simple -> m_int01 : [%d].\r\n",
    ptest_struct_simple -> m_int01
  );
}

This time, the TestStructSimple structure is to be passed as a pointer.

3.4 The following is how DisplayTestStructSimple_ByPointer() is declared in C# :

[DllImport("TestDLL01.dll", CallingConvention = CallingConvention.StdCall)]
private static extern void DisplayTestStructSimple_ByPointer([In] IntPtr ptest_struct_simple);

3.5 This time however, because the parameter is declared simply as an IntPtr, we cannot completely rely on the interop marshaler to help us as before. An IntPtr after all, can point to just about anything and so the interop marshaler will not know what exactly to do with it concerning marshaling. We must do the marshaling manually.

3.6 However, the Marshal class provides a rich set of methods that help us perform the marshaling ourselves. They are not very intuitive to use but they are certainly the standard ones to use.

3.7 The following sample C# code demonstrates how a pointer to a TestStructSimple structure is to be prepared and how the API is called :

static void DisplayTestStructSimple_ByPointer()
{
  TestStructSimple test_struct_simple = new TestStructSimple();

  test_struct_simple.m_int01 = 100;

  int iSizeOfTestStructSimple = Marshal.SizeOf(typeof(TestStructSimple));
  IntPtr ptest_struct_simple = Marshal.AllocHGlobal(iSizeOfTestStructSimple);
  Marshal.StructureToPtr(test_struct_simple, ptest_struct_simple, false);

  DisplayTestStructSimple_ByPointer(ptest_struct_simple);

  Marshal.FreeHGlobal(ptest_struct_simple);
  ptest_struct_simple = IntPtr.Zero;
}

The above is typical code for transforming a structure into an IntPtr. The following are the pertinent points :

  • A new TestStructSimple structure is allocated and referenced by test_struct_simple.
  • Its field member m_int01 is assigned a value (100).
  • Next, using Marshal.SizeOf(), we calculate the size of the TestStructSimple as it would be in unmanaged memory. That is, we want the size of an unmanaged version of TestStructSimple.
  • We then use Marshal.AllocHGlobal() to allocate in unmanaged memory a block of memory of this size.
  • The return value is an IntPtr (ptest_struct_simple). We shall later pass this ptest_struct_simple as the IntPtr parameter to the API. But before that, the memory block has to contain field values that mirrors the managed structure.
  • This important step is done by calling Marshal.StructureToPtr(). What it does is to copy all field data from the managed TestStructSimple to the unmanaged version of TestStructSimple.
  • This is done with the help of the StructLayoutAttribute (applied to the structure) and any MarshalAsAttributes applied to the fields.
  • The DisplayTestStructSimple_ByPointer() API may then be called.
  • After DisplayTestStructSimple_ByPointer() is called, all is not done yet. We need to free up the memory occuppied by the unmanaged version of TestStructSimple.
  • This is done by calling Marshal.FreeHGlobal() which is the conjugal of Marshal.AllocHGlobal().

3.8 The steps taken in the C# code above are certainly elaborate but note that, with the exclusion of one additional important call to a Marshal class method, it is quite standard. After frequent usage, it can usually be committed to memory.

3.9 Remember that in general, a managed structure cannot be passed directly to unmanaged code. An unmanaged representation of that structure must be created in unmanaged memory and then passed to unmanaged code.

4. In Conclusion.

4.1 We have seen how a simple managed structure with a blittable type field may be passed to unmanaged code by value and by pointer. The idea is to demonstrate, in general, the marshaling of managed structures with blittable types.

4.2 All is simple so far. In part 2, we shall use a more complex structure which includes non-blittable type fields. I shall then introduce a very important and useful Marshal class method that would make the code in point 3.7 complete.

Advertisements

About Lim Bio Liong

I've been in software development for nearly 20 years specializing in C , COM and C#. It's truly an exicting time we live in, with so much resources at our disposal to gain and share knowledge. I hope my blog will serve a small part in this global knowledge sharing network. For many years now I've been deeply involved with C development work. However since circa 2010, my current work has required me to use more and more on C# with a particular focus on COM interop. I've also written several articles for CodeProject. However, in recent years I've concentrated my time more on helping others in the MSDN forums. Please feel free to leave a comment whenever you have any constructive criticism over any of my blog posts.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: