(1)、结构体(指针)作为输入输出参数。
(2)、结构体(指针)作为函数返回值。
(3)、结构体中值类型数组。
(4)、结构体中的字符指针和字符数组
(5)、嵌套结构体
(6)、结构体数组
1、作为输入输出参数
C++:
typedef struct _MYPERSON{
char* first; //字符指针
char* last;
} MYPERSON, *LP_MYPERSON;
typedef struct _MYPERSON1{
char first[20]; //字符数组
char last[20];
} MYPERSON1, *LP_MYPERSON1;
typedef struct _MYARRAYSTRUCT{
bool flag;
int vals[ 3 ]; //值类型数组
} MYARRAYSTRUCT;
int TestStructInStruct1(MYPERSON pPerson);
int TestStructInStruct2(MYPERSON* pPerson);
C#:int TestStructInStruct3(MYPERSON1* pPerson);
void TestArrayInStruct( MYARRAYSTRUCT* pStruct );[ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Ansi )]
public struct MyPerson {
public String first;
public String last;
}
[ StructLayout( LayoutKind.Sequential, CharSet=CharSet.Ansi )]
public struct MyPerson1 {
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public String first;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public String last;
}
public struct MyArrayStruct {public bool flag;
[ MarshalAs( UnmanagedType.ByValArray, SizeConst=3 )]
public int[] vals;
}
[ DllImport( "test.dll" ,CharSet = CharSet.Ansi)]
public static extern int TestStructInStruct( MyPerson person);
[ DllImport( "test.dll" ,CharSet = CharSet.Ansi)]
public static extern int TestStructInStruct1(ref MyPerson person);
[ DllImport( "test.dll" ,CharSet = CharSet.Ansi)]
public static extern int TestStructInStruct2(ref MyPerson1 person);[ DllImport( "test.dll" ,CharSet = CharSet.Ansi)]
public static extern int TestArrayInStruct(refMYARRAYSTRUCT person);
总结:
(1)、结构体声明必须保证:字段声明顺序、字段类型、字段在内存中的大小原来的一致!结构体名称,其成员名称可以不同。
(2)、结构体中,char*与char[]在C#声明区别很大,前者直接对应string,后者(字符数组)很容易被初学者误用char[]来对应,它还是要用string来对应,但还需要用[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]来指明该字段的封送行为。
(3)、 其他值类型的数组,直接用数组方式对应,但也需要用 [ MarshalAs( UnmanagedType.ByValArray, SizeConst=3 )] 指明封送行为。
(4)、有直接结构体对应的结构体指针,建议直接用ref + 具体类型,而不采用IntPtr,省去一些不必要的转换操作,
TestArrayInStruct、TestStructInStruct2、TestStructInStruct3都是如此。
2、作为函数返回值:
C++:
MYPERSON* TestReturnStruct();
void FreeStruct(MYPERSON* pStruct);
C#:
[ DllImport( "test.dll" ,CharSet = CharSet.Ansi)]
public static externIntPtrTestReturnStruct(); //注意对应的是IntPtr指针
[ DllImport( "test.dll" ,CharSet = CharSet.Ansi)]
public static externvoid FreeStruct(IntPtrpStruct);
使用:
IntPtrpStruct=TestReturnStruct();
MYPERSON person=(MYPERSON)Marshal.PtrToStructure(pStruct,typeof(MYPERSON));
//在非托管代码,大多用new/malloc分配内存,net无法正确释放,
//需要对应的调用释放内存的方法释放非托管内存
FreeStruct(pStruct);
3、结构体嵌套结构体:
C++:
typedef struct _MYPERSON2{
MYPERSON* person;
int age;
} MYPERSON2, *LP_MYPERSON2;
typedef struct _MYPERSON3{
MYPERSON person;
int age;
} MYPERSON3;
int TestStructInStruct(MYPERSON2* pPerson2);
void TestStructInStruct3(MYPERSON3 person3);
C#:[ StructLayout( LayoutKind.Sequential )]
public struct MyPerson2 {
public IntPtr person;
public int age;
}
[ StructLayout( LayoutKind.Sequential )]
public struct MyPerson3 {
public MyPerson person;
public int age;
}
[ DllImport( "test.dll" )]
public static extern int TestStructInStruct( ref MyPerson2 person2 );
[ DllImport( "test.dll" )]
public static extern int TestStructInStruct3( MyPerson3 person3 );
使用:
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;
int res = TestStructInStruct( ref personAll );
MyPerson personRes = (MyPerson)Marshal.PtrToStructure( personAll.person, typeof( MyPerson ));
Marshal.FreeCoTaskMem( buffer );
MyPerson3 person3 = new MyPerson3();
person3.person.first = "John";
person3.person.last = "Evens";
person3.age = 27;
TestStructInStruct3( person3 );
总结:
(1)、结构体嵌套,如果是实体成员,直接用结构体类型对应,如上面的MyPerson3;
(2)、如果是指针变量,则用IntPtr对应,如上面的MYPERSON2;
(3)、如果嵌套的是结构体数组,那么,出来办法以值类型数组方式对应,如MYARRAYSTRUCT,只不过,类型为具体的结构体类型。这里不另外在举例。(还是给个例子)
C++:
C#:
- typedef struct Student
- {
- char name[20];
- int age;
- double scores[32];
- }Student;
- typedef struct Class
- {
- int number;
- Student students[126];
- }Class;
- [StructLayout(LayoutKind.Sequential)]
public struct Student- {
- [MarshalAs(UnmanagedType.ByValTStr,SizeConst=20)]
- public string name;
- public int age;
- [MarshalAs(UnmanagedType.ByValArray,SizeConst=32)]
- public double[] scores;
- }
- [StructLayout(LayoutKind.Sequential)]
- struct Class
- {
- public int number;
- [MarshalAs(UnmanagedType.ByValArray,SizeConst=126)]
- public Student[] students;
- }
4、结构体数组作为输入输出参数:
C++:
int TestArrayOfStructs2 (MYPERSON* pPersonArray, int size);
C#:[ DllImport( "test.dll" )]
public static extern int TestArrayOfStructs2( [In, Out] MyPerson[] personArray, int size );
使用:
MyPerson[] persons = { new MyPerson( "Kim", "Akers" ), new MyPerson( "Adam", "Barr" )};
int namesSum = TestArrayOfStructs2( persons, persons.Length );
总结:
(1)、一般我们数组作为输入输出参数,需要显式加上[In,Out]属性,标识为输入输入参数。如果不写,默认为In方向,CLR将不会回传修改后的内存值。