Structs Sample

This sample demonstrates how to pass a structure that points to a second structure, pass a structure with an embedded structure, and pass a structure with an embedded array. The Microsoft .NET Framework SDK includes the complete Visual Basic .NET and C# versions of this sample in Samples\Technologies\Interop\Platform-Invoke.

The Structs sample uses the following unmanaged functions, shown with their original function declaration:

  • TestStructInStruct exported from PinvokeLib.dll.

    int TestStructInStruct(MYPERSON2* pPerson2);
    
  • TestStructInStruct3 exported from PinvokeLib.dll.

    void TestStructInStruct3(MYPERSON3 person3);
    
  • TestArrayInStruct exported from PinvokeLib.dll.

    void TestArrayInStruct( MYARRAYSTRUCT* pStruct );
    

PinvokeLib.dll is a custom unmanaged library that contains implementations for the previously listed functions and four structures: MYPERSON, MYPERSON2, MYPERSON3, and MYARRAYSTRUCT. These structures contain the following elements:

typedef struct _MYPERSON
{
   char* first; 
   char* last; 
} MYPERSON, *LP_MYPERSON;

typedef struct _MYPERSON2
{
   MYPERSON* person;
   int age; 
} MYPERSON2, *LP_MYPERSON2;

typedef struct _MYPERSON3
{
   MYPERSON person;
   int age; 
} MYPERSON3;

typedef struct _MYARRAYSTRUCT
{
   bool flag;
   int vals[ 3 ]; 
} MYARRAYSTRUCT;

The managed MyPerson, MyPerson2, MyPerson3, and MyArrayStruct structures have the following characteristic:

  • MyPerson contains only string members. The CharSet field sets the strings to ANSI format when passed to the unmanaged function.
  • MyPerson2 contains an IntPtr to the MyPerson structure. The IntPtr type replaces the original pointer to the unmanaged structure because .NET Framework applications do not use pointers unless the code is marked unsafe.
  • MyPerson3 contains MyPerson as an embedded structure. A structure embedded within another structure can be flattened by placing the elements of the embedded structure directly into the main structure, or it can be left as an embedded structure, as is done in this sample.
  • MyArrayStruct contains an array of integers. The MarshalAsAttribute attribute sets the UnmanagedType enumeration value to ByValArray, which is used to indicate the number of elements in the array.

For all structures in this sample, the StructLayoutAttribute attribute is applied to ensure that the members are arranged in memory sequentially, in the order in which they appear.

The LibWrap class contains managed prototypes for the TestStructInStruct, TestStructInStruct3, and TestArrayInStruct methods called by the App class. Each prototype declares a single parameter, as follows:

  • TestStructInStruct declares a reference to type MyPerson2 as its parameter.
  • TestStructInStruct3 declares type MyPerson3 as its parameter and passes the parameter by value.
  • TestArrayInStruct declares a reference to type MyArrayStruct as its parameter.

Structures as arguments to methods are passed by value unless the parameter contains the ref (ByRef in Visual Basic) keyword. For example, the TestStructInStruct method passes a reference (the value of an address) to an object of type MyPerson2 to unmanaged code. To manipulate the structure that MyPerson2 points to, the sample creates a buffer of a specified size and returns its address by combining the Marshal.AllocCoTaskMem and Marshal.SizeOf methods. Next, the sample copies the content of the managed structure to the unmanaged buffer. Finally, the sample uses the Marshal.PtrToStructure method to marshal data from the unmanaged buffer to a managed object and the Marshal.FreeCoTaskMem method to free the unmanaged block of memory.

Declaring Prototypes

' Declares a managed structure for each unmanaged structure.
< StructLayout( LayoutKind.Sequential, CharSet := CharSet.Ansi )> _
Public Structure MyPerson
   Public first As String
   Public last As String
End Structure 'MyPerson

< StructLayout( LayoutKind.Sequential )> _
Public Structure MyPerson2
   Public person As IntPtr
   Public age As Integer
End Structure 'MyPerson2

< StructLayout( LayoutKind.Sequential )> _
Public Structure MyPerson3
   Public person As MyPerson
   Public age As Integer
End Structure 'MyPerson3

< StructLayout( LayoutKind.Sequential )> _
Public Structure MyArrayStruct 
   Public flag As Boolean
   < MarshalAs( UnmanagedType.ByValArray, SizeConst:=3 )> _
   Public vals As Integer()
End Structure 'MyArrayStruct

Public Class LibWrap
   ' Declares managed prototypes for unmanaged functions.
   Declare Function TestStructInStruct Lib "..\LIB\PinvokeLib.dll" ( _
      ByRef person2 As MyPerson2 ) As Integer
   Declare Function TestStructInStruct3 Lib "..\LIB\PinvokeLib.dll" ( _
      ByVal person3 As MyPerson3 ) As Integer
   Declare Function TestArrayInStruct Lib "..\LIB\PinvokeLib.dll" ( _
      ByRef myStruct As MyArrayStruct ) As Integer   
End Class 'LibWrap
[C#]// Declares a managed structure for each unmanaged structure.
[ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Ansi )]
public struct MyPerson 
{
   public String first; 
   public String last;
}
[ StructLayout( LayoutKind.Sequential )]
public struct MyPerson2 
{
   public IntPtr person;
   public int age;
}
[ StructLayout( LayoutKind.Sequential )]
public struct MyPerson3 
{
   public MyPerson person;
   public int age;
}
[ StructLayout( LayoutKind.Sequential )]
public struct MyArrayStruct 
{
   public bool flag;
   [ MarshalAs( UnmanagedType.ByValArray, SizeConst=3 )] 
   public int[] vals;
}

public class LibWrap
{
   // Declares a managed prototype for unmanaged function.
   [ DllImport( "..\\LIB\\PinvokeLib.dll" )]
   public static extern int TestStructInStruct( ref MyPerson2 person2 );
   [ DllImport( "..\\LIB\\PinvokeLib.dll" )]
   public static extern int TestStructInStruct3( MyPerson3 person3 );
   [ DllImport( "..\\LIB\\PinvokeLib.dll" )]
   public static extern int TestArrayInStruct( ref MyArrayStruct 
   myStruct );
}

Calling Functions

Public Class App
   Public Shared Sub Main()
      ' Structure with a pointer to another structure. 
      Dim personName As MyPerson
      personName.first = "Mark"
      personName.last = "Lee"
      
      Dim personAll As MyPerson2
      personAll.age = 30
      
      Dim buffer As IntPtr = Marshal.AllocCoTaskMem( Marshal.SizeOf( _
      personName ))
      Marshal.StructureToPtr( personName, buffer, False )
      
      personAll.person = buffer
      
      Console.WriteLine( ControlChars.CrLf & "Person before call:" )
      Console.WriteLine( "first = {0}, last = {1}, age = {2}", _
      personName.first, personName.last, personAll.age )
      
      Dim res As Integer = LibWrap.TestStructInStruct( personAll )
      
      Dim personRes As MyPerson = _
         CType( Marshal.PtrToStructure( personAll.person, _
         GetType( MyPerson )), MyPerson )
      
      Marshal.FreeCoTaskMem( buffer )
      
      Console.WriteLine( "Person after call:" )
      Console.WriteLine( "first = {0}, last = {1}, age = {2}", _
      personRes.first, _
         personRes.last, personAll.age )
      
      ' Structure with an embedded structure.
      Dim person3 As New MyPerson3()
      person3.person.first = "John"
      person3.person.last = "Evens"
      person3.age = 27
      LibWrap.TestStructInStruct3( person3 )
      
      ' Structure with an embedded array.
      Dim myStruct As New MyArrayStruct()
      
      myStruct.flag = False
      Dim array( 2 ) As Integer
      myStruct.vals = array
      myStruct.vals( 0 ) = 1
      myStruct.vals( 1 ) = 4
      myStruct.vals( 2 ) = 9
      
      Console.WriteLine( ControlChars.CrLf & "Structure with array _
      before call:" )
      Console.WriteLine( myStruct.flag )
      Console.WriteLine( "{0} {1} {2}", myStruct.vals( 0 ), _
         myStruct.vals( 1 ), myStruct.vals( 2 ) )
      
      LibWrap.TestArrayInStruct( myStruct )
      Console.WriteLine( ControlChars.CrLf & "Structure with array _
      after call:" )
      Console.WriteLine( myStruct.flag )
      Console.WriteLine( "{0} {1} {2}", myStruct.vals( 0 ), _
         myStruct.vals( 1 ), myStruct.vals( 2 ) )
   End Sub 'Main
End Class 'App
[C#]public class App
{
   public static void Main()
   {
      // Structure with a pointer to another structure. 
      MyPerson personName;
      personName.first = "Mark";
      personName.last = "Lee";
      
      MyPerson2 personAll;
      personAll.age = 30;
      
      IntPtr buffer = Marshal.AllocCoTaskMem( Marshal.SizeOf( 
      personName ));
      Marshal.StructureToPtr( personName, buffer, false );
      
      personAll.person = buffer;
      
      Console.WriteLine( "\nPerson before call:" );
      Console.WriteLine( "first = {0}, last = {1}, age = {2}", 
         personName.first, personName.last, personAll.age ); 
      
      int res = LibWrap.TestStructInStruct( ref personAll );
      
      MyPerson personRes = 
         (MyPerson)Marshal.PtrToStructure( personAll.person, 
         typeof( MyPerson ));
      
      Marshal.FreeCoTaskMem( buffer );
      
      Console.WriteLine( "Person after call:" );
      Console.WriteLine( "first = {0}, last = {1}, age = {2}", 
         personRes.first, personRes.last, personAll.age );
      
      // Structure with an embedded structure.
      MyPerson3 person3 = new MyPerson3();
      person3.person.first = "John";
      person3.person.last = "Evens";
      person3.age = 27;
      LibWrap.TestStructInStruct3( person3 );
      
      // Structure with an embedded array.
      MyArrayStruct myStruct = new MyArrayStruct();
      
      myStruct.flag = false;
      myStruct.vals = new int[ 3 ];
      myStruct.vals[ 0 ] = 1;
      myStruct.vals[ 1 ] = 4;
      myStruct.vals[ 2 ] = 9;
      
      Console.WriteLine( "\nStructure with array before call:" );
      Console.WriteLine( myStruct.flag );
      Console.WriteLine( "{0} {1} {2}", myStruct.vals[ 0 ], 
         myStruct.vals[ 1 ], myStruct.vals[ 2 ] );
      
      LibWrap.TestArrayInStruct( ref myStruct );
      Console.WriteLine( "\nStructure with array after call:" );
      Console.WriteLine( myStruct.flag );
      Console.WriteLine( "{0} {1} {2}", myStruct.vals[ 0 ], 
         myStruct.vals[ 1 ], myStruct.vals[ 2 ] );
   }
}

See Also

Marshaling Classes, Structures, and Unions | Platform Invoke Data Types | Creating Prototypes in Managed Code