结构示例
更新:2007 年 11 月
该示例说明如何传递指向另一个结构的结构,如何传递具有嵌入结构的结构,以及如何传递具有嵌入数组的结构。
下面的代码示例的源代码由 .NET Framework 平台调用技术示例提供。
Structs 示例使用以下非托管函数(这里同时显示其原始函数声明):
从 PinvokeLib.dll 导出的 TestStructInStruct。
int TestStructInStruct(MYPERSON2* pPerson2);
从 PinvokeLib.dll 导出的 TestStructInStruct3。
void TestStructInStruct3(MYPERSON3 person3);
从 PinvokeLib.dll 导出的 TestArrayInStruct。
void TestArrayInStruct( MYARRAYSTRUCT* pStruct );
PinvokeLib.dll 是一个自定义非托管库,它包含前面列出的函数的实现和以下四种结构:MYPERSON、MYPERSON2、MYPERSON3 和 MYARRAYSTRUCT。这些结构包含以下元素:
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;
托管的 MyPerson、MyPerson2、MyPerson3 和 MyArrayStruct 结构具有以下特征:
MyPerson 只包含字符串成员。CharSet 字段在传递给非托管函数时将这些字符串设置为 ANSI 格式。
MyPerson2 包含一个指向 MyPerson 结构的 IntPtr。IntPtr 类型将替换指向非托管结构的原始指针,原因是除非代码被标记为 unsafe,否则 .NET Framework 应用程序不使用指针。
MyPerson3 包含作为嵌入结构的 MyPerson。嵌入到另一个结构中的结构可以通过将嵌入结构的元素直接放置在主结构中而单一化;该结构也可保留为嵌入结构,此示例中即如此处理。
MyArrayStruct 包含一个整数数组。MarshalAsAttribute 属性将 UnmanagedType 枚举值设置为 ByValArray,后者用于指示数组中的元素数目。
对于此示例中的所有结构,都将应用 StructLayoutAttribute 属性,以确保成员在内存中按照它们的出现顺序依次排列。
LibWrap 类包含由 App 类调用的 TestStructInStruct、TestStructInStruct3 和 TestArrayInStruct 方法的托管原型。每个原型都声明单个参数,如下所示:
TestStructInStruct 将对 MyPerson2 类型的引用声明为其参数。
TestStructInStruct3 将 MyPerson3 类型声明为其参数并通过值传递该参数。
TestArrayInStruct 将对 MyArrayStruct 类型的引用声明为其参数。
作为方法参数的结构将通过值传递,除非该参数包含 ref(在 Visual Basic 中为 ByRef)关键字。例如,TestStructInStruct 方法将对 MyPerson2 类型的对象的引用(一个地址值)传递给非托管代码。为操作 MyPerson2 所指向的结构,该示例通过组合使用 Marshal.AllocCoTaskMem 和 Marshal.SizeOf 方法创建一个指定大小的缓冲区并返回其地址。接着,该示例将该托管结构的内容复制到该非托管缓冲区。最后,该示例使用 Marshal.PtrToStructure 方法从该非托管缓冲区向托管对象封送数据,并使用 Marshal.FreeCoTaskMem 方法释放非托管内存块。
声明原型
' 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
// 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 );
}
调用函数
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
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 ] );
}
}