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

C#调用C++ DLL实战:P/Invoke结构体对齐那些坑(附完整解决方案) 本文深入探讨了C#通过P/Invoke调用C++ DLL时,因结构体内存对齐规则差异导致的常见问题与崩溃。文章提供了完整的诊断方法和解决方案,包括使用StructLayout、Pack参数及FieldOffset进行精确内存布局控制,并附有实战案例,帮助开发者彻底规避跨语言交互中的结构体对齐陷阱。 阅读详情

这篇讲关于结构体和结构体指针的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调用:如何让C#和Windows API“谈场恋爱”? 《C#调用Windows API的P/Invoke炼丹术》摘要 本文以幽默风格解析C#通过P/Invoke调用Windows API的核心技巧: DllImport三要素:需指定DLL名称、字符集和调用约定,如[DllImport("user32.dll", CharSet=CharSet.Unicode)] 数据类型映射:IntPtr对应HANDLE,string需匹配char*,结构体需[StructLayout(LayoutKind.Sequential)]保证内存对齐 字符串编码 阅读详情

相关推荐

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

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

sdl2005lyx的专栏 8180

C#调用C++ 平台调用P/Invoke 结构体--输入输出参数、返回值、返出值、结构体数组作为参数【五】

Git p-invoke 源码地址 【1】结构体作为输入输出参数 C++代码: typedef struct _testStru1 { int iVal; char cVal; __int64 llVal; }testStru1; EXPORTDLL_API void Struct_Change( testStru1 *pStru ) { if (NULL == pStru)...

郎涯技术 4550

自己实现的飞秋,详细源码

自己实现的飞秋,详细源码

【.Net】P/Invoke:托管与非托管代码的无缝桥梁

摘要:P/Invoke是.NET框架中托管代码调用非托管DLL函数的机制,通过DllImport声明外部函数并处理数据类型映射(如int、char*对应C#类型)。使用时需注意字符串编码、调用约定和结构体布局,避免常见错误。推荐使用.NET 7+的LibraryImport提升性能,或封装为安全API。调试时可捕获Win32错误码,确保与原生库的安全交互。P/Invoke是连接.NET与底层系统的关键手段,需掌握其规则以实现高效开发。

欢迎指错||纠正||建议||水评+点赞&&评论&&关注&&转发 1236

Pinocchio 结合 CasADi 进行 IK 逆运动学及 Mujoco 仿真

摘要:本视频演示了如何利用Pinocchio和CasADi实现机械臂逆运动学(IK)求解,并在Mujoco中进行仿真。通过构建优化问题(最小化位姿误差),结合IPOPT求解器对机械臂关节角度进行优化。代码支持导入URDF/MJCF模型,并以so100机械臂为例,实现了末端执行器位置跟踪的仿真测试。关键步骤包括:建立平移/旋转误差函数、设置约束条件、调用CasADi求解,最终在Mujoco中实时更新关节状态完成运动控制。

darcsdn 1004

PostgreSQL+Citus分布式集群实战:从零搭建到性能调优

本文详细介绍了如何从零开始搭建并优化PostgreSQL+Citus分布式数据库集群。内容涵盖集群规划、源码编译安装、Citus扩展部署、节点配置、分布式表创建以及核心性能调优策略,为应对海量数据增长提供了完整的部署指南和实战解决方案。

oo7890的博客 629

C语言之深入解析如何理解指针和结构体指针、指针函数、函数指针

一直觉得C语言较其它语言最伟大的地方就是C语言中的指针,有些人认为指针很简单,而有些人认为指针很难,当然这里的对简单和难并不是等价于对指针的理解程度。该文对C语言中的指针进行全面的总结,从底层的内存分析,彻底让读者明白指针的本质。

╰つ栺尖篴夢ゞ 1915

博图Advanced实战:VB脚本实现HMI画面智能弹出(附完整代码解析)

本文详细介绍了在西门子博图Advanced平台中利用VB脚本实现HMI画面智能弹出的实战方法。通过动态计算坐标、分辨率自适应方案和完整代码解析,帮助工程师解决不同显示环境下的定位难题,提升工业自动化界面的交互体验。

weixin_29041767的博客 344

C#调用C++ 平台调用P/Invoke 结构体--结构体嵌套【八】

Git p-invoke 源码地址 普通的结构体嵌套很简单,C#中直接定义成对应的结构体即可,这里介绍的是嵌套的结构体以指针的方式表达 【1】嵌套结构体指针 C++代码: typedef struct _testStru10Pre { int iVal; }testStru10Pre; typedef struct _testStru10 { testStru10Pre *pP...

郎涯技术 3360

利用XGBoost实现短期电力负荷预测

本数据集为某地区2009年-2015年的用电负荷,时间片为每分钟这里我们定义了XGB模型作为实例,来进行模型训练,之后我们会对其使用网格搜索找到最优的参数。

优快云 精品推荐 4497

手把手教你学Simulink——整流器场景:基于PWM控制的单相全桥整流器设计

摘要:本文详细介绍了使用Simulink搭建基于PWM控制的单相全桥整流器的完整设计流程。主要内容包括:系统结构设计(含交流电源、全桥整流器、滤波电路等模块)、分步骤建模过程(从创建项目到添加各功能模块)、双闭环控制系统设计(电压外环和电流内环PI控制)、参数设置建议及仿真分析。通过仿真验证,该整流器可实现单位功率因数运行和稳定输出电压,并提供了数字锁相环等优化方向。文章为电力电子系统设计提供了实用的Simulink建模指导。(149字)

MHD0815的博客 325

从工具收藏到工程实践:构建可复用的Web安全工具库方法论

在网络安全领域,工具的高效使用是工程师的核心能力之一。理解工具背后的原理,不仅涉及命令行参数和功能,更关乎其设计思想与适用场景。从工程化视角出发,工具的价值在于能否融入标准化工作流,实现可沉淀、可复用的自动化流程。通过建立工具档案、管理环境依赖、设计工具链串联,工程师能将零散操作转化为系统化方法。例如,在Web安全测试中,信息收集工具如subfinder与漏洞扫描工具如nuclei的协同,需要清晰的输入输出接口和流程编排。这种工程化实践不仅提升效率,更通过结果处理与知识沉淀,将经验固化为可迭代的规则库,最终

weixin_34326558的博客 301

平台调用P-INVOKE完全掌握, 结构体和结构体指针

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

lnc2003的专栏 721

【TCAD】Sentaurus 中的“陷阱trap”仿真设置

允许使用有限的数据集名称 SFactor 规范,例如 DeepLevels , xMoleFraction , 和 yMoleFraction (从兴奋剂档案中读取) eTrappedCharge 和 hTrappedCharge (从指定的文件中读取 DevFields 在 File 部分)或 PMI 用户字段 PMIUserField0...10 (从指定的文件中读取 PMIUserFields 在 File 部分)。给定的陷阱截面 eNeutral 陷阱是多种多样的 Xsec 项目参数。

荷塘阅色 1308

相位编码脉冲压缩信号的理论研究与 Matlab 实现

通过实际的代码示例,我们展示了如何生成信号、进行相位编码、传输信号、相位解码和脉冲压缩的步骤。相位编码脉冲压缩信号是通过在发射端对信号进行相位编码,并在接收端对信号进行相位解码来实现的。其主要原理是在发射端将信号的频率进行调制,使得信号具有宽带特性,然后在接收端通过匹配滤波器对信号进行解调和压缩,从而提高信号的分辨率和抗干扰能力。相位编码脉冲压缩信号是一种在信号处理中常用的技术,用于提高信号的分辨率和抗干扰能力。可以使用 Matlab 中的信号生成函数生成一个宽带脉冲信号,例如矩形脉冲信号或高斯脉冲信号。

CyberWizarDD的博客 906

C#调用带结构体指针的C Dll的方法

转载文章 如有侵权联系删除https://www.cnblogs.com/ye-ming/p/8004314.html C#调用带结构体指针的C Dll的方法 在C#中调用C(C++)类的DLL的时候,有时候C的接口函数包含很多参数,而且有的时候这些参数有可能是个结构体,而且有可能是结构体指针,那么在C#到底该如何安全的调用这样的DLL接口函数呢?本文将详细介绍如何调用各种参数的方法。 一、调用接口仅含普通变量 int fnAdd(int num1,int num2); 那么在C#调用这种函数.

芯创博客 1060

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

打印级输出不再是秘密:Goohaitools-ComfyUI指定300DPI自动保存图像指南

**Goohaitools-ComfyUI(孤海工具箱)** 是一个面向 ComfyUI 的实用插件合集,内含 70+ 个节点,其中"指定 DPI 自动保存"是打印级输出的核心能力:把 `dpi` 参数设为 **300**,图像保存时会自动写入 300DPI 元数据,让证件照、登记照、排版拼版图直接达到冲印交付标准,无需再手动进 Photoshop 改分辨率。本文带你从安装到实战,走通"指定 30

gitblog_01151的博客 258

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

平台调用P-INVOKE完全掌握, 指针大全

这篇是讲述P-INVOKE中,应对各种指针的方法。包括普通指针,字符串指针,二级指针,指针数组,函数指针,结构体指针。篇幅估计有点长,大家耐心点看。嘿嘿~~   第一:普通指针,包括char *,short *,int *,__int64 *,这些指针进行平台调用是都对应C#的IntPtr类型,然后使用Marshal.ReadXXX()系列函数读取就可,写内存时使用Marshal.Wr

lnc2003的专栏 858

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

c#编程指南(十三) 平台调用P-INVOKE完全掌握, 结构体和结构体指针

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

weixin_30251829的博客 228

C# P/Invoke中传递数组参数 Marshal

C# P/Invoke中传递数组参数 C#在调用动态库接口,有时需要在C#中分配非托管内存,以便动态库可以写入返回的数据;有时我们需要传递一个复杂的数组等等。在C++的方法原型中,是一个*即指针,在C#的方法原型,相对应的可以是IntPtr,有些也可以直接使用[Out,In]等属性。 对于基础数组的数组,可以使用 Byte[] photoData = new Byte[CompressPhotoSize]; GCHandle gh = GCHa...

努力学习中的博客 1248

C#调用C++ 平台调用P/Invoke 函数指针/回调函数【二】

Git p-invoke 源码地址 【1】C++代码 定义函数指针: typedef int (*PCALLBACKFUN)(int i); 定义导出函数,返回函数指针: /************************************************************************/ /* 2. 函...

郎涯技术 3910

关于P/Invoke和AccessViolationException

最近试验了一阵子.NET Framework中使用P/Invoke与Windows API进行互操作的方法,其间在封装标有CALLBACK标记的回调函数时遇到了随时间推移而出现的AccessViolationException异常。这个异常在.NET Framework 1.x中由更为一般的NullReferenceException描述,在2.0版中才分离出来,成为一类特殊的异常。由于它属于系统

1194

C# P/Invoke 基础

本文介绍了P/Invoke技术,它是C#调用非托管C/C++代码的核心方法。文章通过比喻将P/Invoke比作翻译官,帮助托管代码与非托管代码进行交互。主要内容包括:1) P/Invoke的基本概念和使用示例;2) 三个核心规则:数据类型对齐、调用约定和内存管理;3) 结构体、回调和字符串等复杂场景的处理方法;4) .NET 7+的新特性LibraryImport。文章强调了P/Invoke的强大能力与潜在风险,并提供了最佳实践建议,如集中管理声明、显式指定参数、优先使用Blittable类型等。

qq_60972641的博客 478
上一篇: 当然我这里讲的都是与数据库有关的字符串拼凑
下一篇: 【飞秋】关于结构体和结构体指针的P-INVOKE
普通网友
博客等级 码龄22年 1万+粉丝 3156原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值