//
you're reading...
Programming Issues/Tips

The Meaning of Marshal.SizeOf().

1. Introduction.

1.1 Recently, someone from the MSDN forum complained of a problem he faced while passing a structure from managed code to an unmanged API. The following is a link to the forum post : memory corruption pinvoke.

1.2 My advise to him was that, along with other possible problems, there was something wrong with the way he used the Marshal.SizeOf() method.

1.3 This made me recall the many times some of the junior developers in my company has approached me with memory corruption exception issues which were connected with calls to the Marshal.SizeOf() method.

1.4 In this blog, I shall attempt to dispel some myths which are connected with this method.

2. The Spirit and Intent of the Marshal Class.

2.1 First and foremost, the Marshal class itself. The MSDN documentation describes this class as follows :

Provides a collection of methods for allocating unmanaged memory, copying unmanaged memory blocks, and converting managed to unmanaged types, as well as other miscellaneous methods used when interacting with unmanaged code.

2.2 The Marshal class is really meant for working with unmanaged code (COM or PInvoke). Its methods are not meant for consumption with pure managed coding.

3. The Marshal.SizeOf() Method.

3.1 The Marshal.SizeOf() method is really meant to determine how much unmanaged memory (in bytes) a data type would occupy when it is marshaled to unmanaged code.

3.2 When calculating the size of a data type, the presence of MarshalAsAttributes provides additional good information.

3.3 Marshal.SizeOf() is often used in conjunction with Marshal.AllocHGlobal() and Marshal.AllocCoTaskMem() to allocate unmanaged memory.

3.4 The Marshal.SizeOf() certainly cannot be used to determine the size of a managed structure at runtime. Or rather, such use often returns deceptive results.

3.5 Let’s take the following example :

struct MyStruct01
{
  public int[] m_intArray;
}

...
...
...

int iSizeOfStructure = Marshal.SizeOf(typeof(MyStruct01));

3.6 What would be the value of iSizeOfStructure ? It is 4. This is because, in the absence of MarshalAsAttributes, if MyStruct01 were to be marshaled to unmanaged memory, the interop marshaler will assume that the m_intArray field is a reference to an array integer. References are generally synonymous with memory pointers and so in a 32-bit OS, the size of m_intArray is 4.

3.7 However, note how things change when we apply proper attributes to the structure :

[StructLayout(LayoutKind.Sequential)]
struct MyStruct01
{
  [MarshalAs(UnmanagedType.ByValArray, SizeConst=10)]
  public int[] m_intArray;
}

In this case, assuming a 32-bit OS, Marshal.SizeOf(typeof(MyStruct01)) will return 40. This is because the size of an integer is 4 bytes, and the m_intArray field has been declared to be marshaled as an inline array of 10 integers. This works out to 40 (i.e. 4 x 10) bytes.

Note the meaning of the MarshalAsAttribute. Its use implies the following :

  • In the unmanaged world, there is a counterpart data which is the target for marshaling a managed data.
  • The MarshalAsAttribute provides specification as to the format of this counterpart data.

To say “the m_intArray field is to be marshaled as an inline array of 10 integers” is to say the following :

  • There is an unmanaged structure that is the counterpart of MyStruct01.
  • This unmanaged structure naturally contains a field which is the counterpart of the m_intArray field.
  • Note that the managed m_intArray field is simply an integer array of no specific size.
  • However, the unmanaged m_intArray field is an inline array of (specifically) 10 integers.
  • When a managed MyStruct01 is marshaled to its counterpart, the integer array elements of m_intArray will be copied element by element to this inline array of 10 integers.

3.8 Take another example :

struct MyStruct02
{
  public string m_str01;
}

Devoid of any MarshalAsAttribute, Marshal.SizeOf(typeof(MyStruct02)) returns 4 – the size of a reference.

3.9 Now, note the difference when proper attributes are applied :

[StructLayout(LayoutKind.Sequential)]
struct MyStruct02
{
  [MarshalAs(UnmanagedType.ByValTStr, SizeConst=100)]
  public string m_str01;
}

This time, via the MarshalAsAttribute, the field m_str01 is indicated to be marshaled as an inline array of 100 ANSI characters. As such, Marshal.SizeOf(typeof(MyStruct02)) will return 100 (each ANSI character is 1 byte in size).

3.10 To marshal an inline array of Unicode characters, the CharSet argument for the StructLayoutAttribute must be applied :

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
struct MyStruct02
{
  [MarshalAs(UnmanagedType.ByValTStr, SizeConst=100)]
  public string m_str01;
}

The above indicates that m_str01 is to be marshaled as an inline array of 100 Unicode characters. Marshal.SizeOf(typeof(MyStruct02)) will return 200.

3.11 Let’s examine a final example :

[StructLayout(LayoutKind.Sequential)]
struct MyStruct03
{
  [MarshalAs(UnmanagedType.BStr)]
  public string m_str01;
}

This time, the m_str01 field is declared to be marshaled as a BSTR. Marshal.SizeOf(typeof(MyStruct02)) will return value 4. A BSTR is simply a pointer to a wide character string. Hence the size of a BSTR in a 32-bit OS is 4.

4. In Conclusion.

4.1 In summary, Marshal.SizeOf() is meant to be used where interop marshaling is involved.

4.2 It is very often used together with memory allocation functions like Marshal.AllocHGlobal() and Marshal.AllocCoTaskMem().

4.3 To be effective, the target data type used with Marshal.SizeOf() should be specified further, if applicable, with MarshalAsAttributes and the StructLayoutAttribute.

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.

Discussion

No comments yet.

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: