17.3 Marshaling Classes and Structs

Passing a class or struct to a C function requires marking the struct or class with the StructLayout attribute:

// InteropFun.cs - compile with /nowarn:649 or add "=0;" default 
//   initializers to the fields in SystemTime to avoid warnings
using System;
using System.Runtime.InteropServices;
class SystemTime {
   public ushort wYear;
   public ushort wMonth;
   public ushort wDayOfWeek;
   public ushort wDay;
   public ushort wHour;
   public ushort wMinute;
   public ushort wSecond;
   public ushort wMilliseconds;
class InteropFun {
   static extern void GetSystemTime(SystemTime t);
   static void Main( ) {
      SystemTime t = new SystemTime( );

In both C and C#, fields in an object are located at n number of bytes from the address of that object. The difference is that in a C# program, the CLR finds this offset by looking it up using the field name; C field names are compiled directly into offsets. For instance, in C, wDay is just a token to represent whatever is at the address of a SystemTime instance plus 24 bytes.

For access speed and future widening of a data type, these offsets are usually in multiples of a minimum width, called the pack size. For .NET types, the pack size is usually set at the discretion of the runtime, but by using the StructLayout attribute, field offsets can be controlled. When using this attribute, the default pack size is 8 bytes, but it can be set to 1, 2, 4, 8, or 16 bytes (pass Pack=packsize to the StructLayout constructor). There are also explicit options to control individual field offsets (see Section 17.6 later in this chapter). This lets a .NET type pass to a C function.

    Part II: Programming with the .NET Framework
    Part IV: API Quick Reference