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 theMyPerson
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
containsMyPerson
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 typeMyPerson2
as its parameter.TestStructInStruct3
declares typeMyPerson3
as its parameter and passes the parameter by value.TestArrayInStruct
declares a reference to typeMyArrayStruct
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