这篇讲关于结构体和结构体指针的P-INVOKE,关键有4个P-INVOKE类型,结构体作为输入输出参数。结构体指针作为输入输出参数。还有结构体内的成员类型分为:数组,指针,指针数组,结构体,结构体指针,结构体数组,结构体指针数组。当然还有类继承(这里只介绍了单继承)。
其中有一个比较费解的是结构体作为返回值的P-INVOKE的奇怪现象,下一篇结合反汇编讲解。

即时通讯软件

第一:C++结构体和C#结构体对应关系,看下面。这里提到一点C# 声明结构体中的成员是数组的必须像下面那样声明:使用[MarshalAs(UnmanagedType.ByValArray, SizeConst = N)]

C++代码不多,全部贴到这里:

1 struct Base
2 {
3 int BaseInt;
4 };
5
6  struct Test : Base
7 {
8 int TestIntArray[2];
9 //
10   int * TestIntPointer;
11 int * TestIntPointerArray[2];
12 //
13   Base TestBase;
14 Base * TestBasePoint;
15 Base TestBaseArray[2];
16 Base * TestBasePointerArray[2];
17 };

再来看C#的结构体声明:

1 [StructLayout(LayoutKind.Sequential)]
2 public struct Base
3 {
4 public int BaseInt;
5 }
6
7 [StructLayout(LayoutKind.Sequential)]
8 public struct Test
9 {
10 public Base _base;//把继承的基类放在第一个元素的位置。
11 //
12   [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
13 public int[] TestIntArray;
14 //
15   public IntPtr TestIntPointer;
16 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
17 public IntPtr[] TestIntPointerArray;
18 //
19   public Base TestBase;
20 public IntPtr TestBasePoint;
21 //
22   [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
23 public Base[] TestBaseArray;
24 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
25 public IntPtr[] TestBasePointerArray;
26 }
27

第二。C++导出函数和C# P-INVOKE函数的对应。
C++:

1 static Test _test;
2 void SetTest(Test test)
3 {
4 _test = test;
5 PrintTest();
6 }
7
8 void SetTestPointer(Test * lptest)
9 {
10 _test = * lptest;
11 PrintTest();
12 }
13
14 Test GetTest()
15 {
16 return _test;
17 }
18
19 Test * GetTestPointer()
20 {
21 return &_test;
22 }

C#:   注意结构体作为返回值的P-INVOKE声明是不是很奇怪。不过运行很正常。下一篇结合反汇编说。

1 [DllImport("TestDll")]
2 public static extern void SetTest(Test test);
3
4 [DllImport("TestDll")]
5 public static extern void SetTestPointer(IntPtr lptest);
6
7 [DllImport("TestDll")]
8 public static extern void GetTest(IntPtr lptest); //注意声明。
9
10 [DllImport("TestDll")]
11 public static extern IntPtr GetTestPointer();

第三:看下C#如何调用,这里用到了Marshal.AllocHGlobal 方法,和Alloc功能基本一样,会造成内存泄露,使用完了记住使用Marshal.FreeHGlobal函数释放申请的内存。

1 private Test _test = new Test();
2
3 public void Run()
4 {
5 InitTest();
6 //#########################
7 SetTest(_test);
8 Console.WriteLine("-------------------------------------------------------------/n");
9 //#########################
10 _test._base.BaseInt = 9999;
11 //Marshal.AllocHGlobal 和WIN32 API, Alloc功能基本一样,
12 //这个方法不要多用,可能造成内存泄露。
13 //记住使用Marshal.FreeHGlobal函数释放申请的内存
14 IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Test)));
15 Marshal.StructureToPtr(_test,p,false);
16 SetTestPointer(p);
17 Console.WriteLine("-------------------------------------------------------------/n");
18 //#########################
19 IntPtr pp = GetTestPointer();
20 Test temp = (Test)Marshal.PtrToStructure(pp, typeof(Test));
21 PrintTest(temp);
22 Console.WriteLine("-------------------------------------------------------------/n");
23
24 //#########################
25 IntPtr pp2 = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Test)));
26 GetTest(pp2);
27 Test temp2 = (Test)Marshal.PtrToStructure(pp2, typeof(Test));
28 PrintTest(temp2);
29
30 }

总结一下:Marshal.StructureToPtr从托管类复制数据到未托管的内存中,Marshal.PtrToStructure恰好相反。Marshal.AllocHGlobal申请非托管内存,Marshal.FreeHGlobal函数释放非托管内存。使用 Marshal.Read系列读写指针,使用Marshal.ReadIntPtr来读写二级指针。

关注技术文章飞秋:http://www.freeeim.com/,24小时专业转载。

【飞秋】关于结构体和结构体指针的P-INVOKE相关推荐

  1. C语言结构体篇 结构体

    在描述一个物体的属性的时候,单一的变量类型是无法完全描述完全的.所以有了灵活的结构体变量. 结构体变量从意义上来讲是不同数据类型的组合,从内存上来讲是在一个空间内进行不同的大小划分. 1.1 结构体类 ...

  2. Go 学习笔记(33)— Go 自定义类型 type(自定义结构体、结构体初始化、结构体内嵌、自定义接口)

    1. 自定义类型格式 用户自定义类型使用关键字 type ,其语法格式是: type newType oldType oldType 可以是自定义类型.预声明类型.未命名类型中的任意一种. newTy ...

  3. Go 学习笔记(14)— 结构体定义、实例化、初始化、匿名结构体、结构体访问、结构体作为形参、结构体指针

    Go 语言中没有 "类" 的概念,也不支持 "类" 的继承等面向对象的概念.Go 语言不仅认为结构体能拥有方法,且每种自定义类型也可以拥有自己的方法. 1. 结 ...

  4. 结构体中定义函数指针

    结构体指针变量的定义,定义结构体变量的一般形式如下: 形式1:先定义结构体类型,再定义变量 struct结构体标识符 { 成员变量列表;- }; struct 结构体标识符 *指针变量名; 变量初始化 ...

  5. 对C语言 结构体 和 结构变量

    一.结构体/结构的引入 在学生的登录记录表中,姓名应该为字符型.学号可以为整形或字符型,年龄应该为整形,性别应该为字符型,成绩可以整形或实型.显然不能用一个数组来存放这一组数据,因为数组各元素的类型和 ...

  6. 【Linux 内核 内存管理】Linux 内核堆内存管理 ① ( 堆内存管理 | 内存描述符 mm_struct 结构体 | mm_struct 结构体中的 start_brk、brk 成员 )

    文章目录 一.堆内存管理 二.内存描述符 mm_struct 结构体 三.mm_struct 结构体中的 start_brk.brk 成员 一.堆内存管理 Linux 操作系统中的 " 堆内 ...

  7. 【C 语言】结构体 ( 结构体中嵌套二级指针 | 为 结构体内的二级指针成员 分配内存 | 释放 结构体内的二级指针成员 内存 )

    文章目录 一.结构体中嵌套二级指针 1.结构体中嵌套二级指针 类型声明 2.为 结构体内的二级指针成员 分配内存 3.释放 结构体内的二级指针成员 内存 二.完整代码示例 一.结构体中嵌套二级指针 1 ...

  8. 【C 语言】结构体 ( 结构体中嵌套一级指针 | 分配内存时先 为结构体分配内存 然后再为指针分配内存 | 释放内存时先释放 指针成员内存 然后再释放结构头内存 )

    文章目录 一.结构体中嵌套一级指针 1.声明 结构体类型 2.为 结构体 变量分配内存 ( 分配内存时先 为结构体分配内存 然后再为指针分配内存 ) 3.释放结构体内存 ( 释放内存时先释放 指针成员 ...

  9. 【DBMS 数据库管理系统】OLAP 核心技术 : 数据方体 ( 数据方体 | 数据方体格结构 | 数据单元 )

    文章目录 一.数据方体 二.数据方体 格结构 ( 参考 ) 三.数据单元 一.数据方体 数据方体 简介 : "数据方体" 概念 : 多维数据模型 构成的 多维数据空间 称为 &qu ...

最新文章

  1. windows性能计数器搜集方法
  2. 获取网页各种宽高的值
  3. 【栈】【232. 用栈实现队列】【简单】
  4. 【2006-1】【字符统计】
  5. mysql内表和外表_Hive内表和外表的区别
  6. 管道实现父子进程的信息传递(二)【标准流和其文件描述符、fwrite函数、perror函数】
  7. android java 调试快捷键_Android Studio 代码页跳界面 /java和XML快速切换技巧
  8. 如何修改动态库符号表
  9. 支付宝:“答答星球”小程序上线20天累计参与人数超2亿
  10. Python编程常见出错信息及原因分析(1)
  11. [Unity3d][NGUI]打包NGUI预制件成Assetbundle 两种思路.
  12. php 二位数组排序
  13. 软件测试周刊(第33期):当夏季的光阴已然流逝
  14. 微信公众平台 自动回复消息
  15. java 文件目录操作_Java目录文件的操作 -解道Jdon
  16. 应用调优常用技巧-線程池
  17. Python基础——魔法方法(二)
  18. 做每个人的互联网中心
  19. 盘点Sui生态20个值得关注的项目,其中8个已进入测试阶段
  20. 宇视NVR录像机,录像下载/回放提示“回放下载能力已达上线”如何解决

热门文章

  1. 互联网晚报 | 2月18日 星期五 | 高途宣布停止高中学科辅导服务;小红书启动最严医美专项治理;FF 91量产版2月23日发布...
  2. zabbix api java_zabbix的Java API(一)
  3. 消息长度_【消息】听说咱安阳的第一条封闭外环即将全线通车了?是的,长度相当于北京五环...
  4. python创建数据库表空间_Python 操作 mysql
  5. python一般用什么软件写_python用什么软件写代码
  6. N皇后问题的解(洛谷P1219题题解,Java语言描述)
  7. 使用Spring框架实现数据库事务处理
  8. Docker 6岁啦!多场线下 Party 即将来袭!
  9. 计算机视觉方向简介 | 三维深度学习中的目标分类与语义分割
  10. GUI编程tkinter模块常用参数(python3)