【飞秋】关于结构体和结构体指针的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

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

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

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

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

╰つ栺尖篴夢ゞ 1915

DSP28335与Vofa+上位机通信实战:SCIb配置避坑指南(附完整代码)

本文提供DSP28335与Vofa+上位机通信的实战指南,重点解析SCIb模块配置的常见问题与解决方案。针对研旭开发板的硬件连接、波特率精确计算、FIFO高效使用以及FireWater协议封装进行了详细说明,并附有完整代码,帮助开发者快速实现实时数据可视化与波形分析,提升调试效率。

weixin_29172963的博客 152

从Gotham到Foundry:拆解Palantir三大产品线,看它如何用‘本体’统一企业数据与AI

本文深入解析Palantir的三大产品线Gotham、Foundry和Apollo,揭示其如何通过独特的'本体'架构实现企业数据与AI的无缝融合。从国防情报到商业决策,Palantir的产品架构展现了强大的数据整合与AI赋能能力,为企业构建数字孪生提供革命性解决方案。

weixin_30823001的博客 322

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

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

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

【100%通过率】华为OD机试真题 JavaScript 实现【对称美学】【2023 Q1 | 100分】

对称就是最大的美学,现有一道关于对称字符串的美学。 已知: 第1个字符串:R 第2个字符串:BR 第3个字符串:RBBR 第4个字符串:BRRBRBBR 第5个字符串:RBBRBRRBBRRBRBBR相信你已经发现规律了,没错! 就是第i个字符串=第i-1号字符串的取反+第i-1号字符串。取反即(R->B,B->R); 现在告诉你n和k,让你求得第n个字符串的第k个字符是多少。(k的编号从0开始)

MISAYAONE的博客 9850

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

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

lnc2003的专栏 721

CloudCompare 和 PCL:点云网格化分析与处理

点云网格化是将点云数据转化为规则网格的过程,其主要思想是将离散的点云投影到一个平面上,然后对该平面进行划分,形成一系列小区域。在点云网格化方面,PCL 提供了各种算法和工具,可以实现高效的点云网格化操作。以上示例代码中,首先加载了输入的点云数据,然后通过设置合适的网格化算法参数,如搜索半径和最大三角形边长等,执行了网格化操作,最后将网格化结果保存到输出文件中。以上示例代码中,首先加载了输入的点云数据,然后通过设置合适的网格化参数,如分辨率等,执行了网格化操作,最后将网格化结果保存到输出文件中。

TechPulseZ的博客 1293

基于 MFC 和 C++ 的语音识别客户端实现方案

语音 SDK:[Microsoft Speech SDK](https://learn.microsoft.com/zh-cn/azure/cognitive-services/speech-service/speech-sdk)(建议使用最新版)1. **Azure 语音服务密钥**:需要在 [Azure 门户](https://portal.azure.com/) 中创建语音资源,获取密钥和区域(如 `eastasia`),替换代码中的占位符。

寒寒豆豆软件公司的博客 156

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

SAP会计凭证导入ABAP开发模板+注意点—附带源码

开发并不仅仅只要考虑实现的功能,只实现功能的是程序,考虑到使用场景的是工程。使用bapi校验创建会计凭证的数据,报错和参数和正式创建一致。用途,防止产生错误的凭证,即使财务凭证可以冲销。正式创建会计凭证。隐藏的坑。

小九啥也不会 1233

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

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

gitblog_01151的博客 258

索尼IMX316 标定_ToF模块相机校准

很多东西一开始给出去的并不是最优的方案,而是在客户端支持中,以及在持续不断的芯片验证中,发现还可以这么做,还能够再提升Sensor的性能,所以说这是一个持续的过程。曾经听说过一个故事,在某厂里ST产品一直作为第一供应商多年,然后近段时间即将被新兴公司的ToF产品所打败,性能报告已经出来,但是ST并没有更新产品,而是在原有产品更新了固件以及标定流程,此时产品的性能又是超越其他供应商的产品。相机矩阵不考虑镜头失真,因为理想的针孔相机没有镜头。为了准确地代表一个真实的相机,相机模型包括径向和切向的镜头畸变。

dream it possible 1155

第五章 TTM 后备管理:5.11 迁移的执行

本文讨论 TTM 框架中缓冲区对象(BO)数据搬运的完整流程,涵盖搬运入口 ttm_bo_handle_move_mem() 的执行步骤、驱动 move 回调的约定、同步辅助函数的使用、多跳迁移机制及异步流水化搬运的处理。重点说明了中转位置的引入原因与执行逻辑,以及通过 ghost 对象和 fence 机制解决异步搬运中的资源释放与访问一致性问题,确保数据安全迁移的同时支持 GPU 异步操作。

图形/计算GPU的驱动与虚拟化技术分享园地 1716

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

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

lnc2003的专栏 858

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

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

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

努力学习中的博客 1248

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

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

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

weixin_30251829的博客 228

数学建模模型总结

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

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

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

郎涯技术 3910

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

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

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

郎涯技术 3360

关于P/Invoke和AccessViolationException

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

1194

C#调用C++ 平台调用P/Invoke 结构体--含有内置数据类型的一维、二维数组、字符串指针【六】

Git p-invoke 源码地址 【1】结构体中含有内置数据类型的一维数组 C++代码: typedef struct _testStru3 { int iValArrp[30]; WCHAR szChArr[30]; }testStru3; EXPORTDLL_API void Struct_ChangeArr( testStru3 *pStru ) { if (NULL ...

郎涯技术 3526
上一篇: 【飞秋】关于结构体和结构体指针的P-INVOKE
下一篇: 【飞秋】使用C# 4编写动态的代码
普通网友
博客等级 码龄22年 1万+粉丝 3156原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值