平台调用P-INVOKE(三)--(封送结构体)

  平时,我们接触的平台调用,对于简单的类型,一般很容易学会。因为简单类型有直观的类型对应。而结构体,是一种自定义类型,结构体成员可能会很复杂。所以,封送结构体变量,是平台调用的一个重点,也是个难点。本节篇幅较多,将阐述如下几个内容:

(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);
int TestStructInStruct3(MYPERSON1* pPerson);
void TestArrayInStruct( MYARRAYSTRUCT* pStruct );
C#:

[ 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++:

  • typedef struct Student  
  • {  
  •     char name[20];  
  •     int age;  
  •     double scores[32];  
  • }Student;  
  •  
  • typedef struct Class  
  • {  
  •     int number;  
  •     Student students[126];  
  • }Class;  
C#:

  
  
  1. [StructLayout(LayoutKind.Sequential)]      
         public   struct Student  
  2.       {  
  3.            [MarshalAs(UnmanagedType.ByValTStr,SizeConst=20)]  
  4.            public string name;  
  5.            public int age;  
  6.            [MarshalAs(UnmanagedType.ByValArray,SizeConst=32)]  
  7.            public double[] scores;  
  8.        }  
  9.        [StructLayout(LayoutKind.Sequential)]  
  10.        struct Class  
  11.        {  
  12.           public int number;  
  13.            [MarshalAs(UnmanagedType.ByValArray,SizeConst=126)]  
  14.           public Student[] students;  
  15.  
  16.        }  


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将不会回传修改后的内存值。







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值