在项目开发时,要调用C++封装的DLL,普通的类型C#上一般都对应,只要用DllImport传入从DLL中引入函数就可以了。但是当传递的是结构体、结构体数组或者结构体指针的时候,就会发现C#上没有类型可以对应。这时怎么办,第一反应是C#也定义结构体,然后当成参数传弟。然而,当我们定义完一个结构体后想传递参数进去时,会抛异常,或者是传入了结构体,但是返回值却不是我们想要的,经过调试跟踪后发现,那些值压根没有改变过,代码如下。

 [DllImport("workStation.dll")]private static extern bool fetchInfos(Info[] infos);public struct Info{public int OrderNO;public byte[] UniqueCode;public float CpuPercent;                  };private void buttonTest_Click(object sender, EventArgs e){try{Info[] infos=new Info[128];if (fetchInfos(infos)){MessageBox.Show("Fail");}else{string message = "";foreach (Info info in infos){message += string.Format("OrderNO={0}\r\nUniqueCode={1}\r\nCpu={2}",info.OrderNO,Encoding.UTF8.GetString(info.UniqueCode),info.CpuPercent);}MessageBox.Show(message);}}catch (System.Exception ex){MessageBox.Show(ex.Message);}}

后来,经过查找资料,有文提到对于C#是属于托管内存,现在要传递结构体数组,是属性非托管内存,必须要用Marsh指定空间,然后再传递。于是将结构体变更如下。

   [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]public struct Info{public int OrderNO;[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]public byte[] UniqueCode;public float CpuPercent;                  };

但是经过这样的改进后,运行结果依然不理想,值要么出错,要么没有被改变。这究竟是什么原因?不断的搜资料,终于看到了一篇,里面提到结构体的传递,有的可以如上面所做,但有的却不行,特别是当参数在C++中是结构体指针或者结构体数组指针时,在C#调用的地方也要用指针来对应,后面改进出如下代码。

    [DllImport("workStation.dll")]private static extern bool fetchInfos(IntPtr infosIntPtr);[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]public struct Info{public int OrderNO;[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]public byte[] UniqueCode;public float CpuPercent;};private void buttonTest_Click(object sender, EventArgs e){try{int workStationCount = 128;int size = Marshal.SizeOf(typeof(Info));IntPtr infosIntptr = Marshal.AllocHGlobal(size * workStationCount);Info[] infos = new Info[workStationCount];if (fetchInfos(infosIntptr)){MessageBox.Show("Fail");return;}for (int inkIndex = 0; inkIndex < workStationCount; inkIndex++){IntPtr ptr = (IntPtr)((UInt32)infosIntptr + inkIndex * size);infos[inkIndex] = (Info)Marshal.PtrToStructure(ptr, typeof(Info));}Marshal.FreeHGlobal(infosIntptr);string message = "";foreach (Info info in infos){message += string.Format("OrderNO={0}\r\nUniqueCode={1}\r\nCpu={2}",info.OrderNO,Encoding.UTF8.GetString(info.UniqueCode),info.CpuPercent);}MessageBox.Show(message);}catch (System.Exception ex){MessageBox.Show(ex.Message);}}

要注意的是,这时接口已经改成IntPtr了。通过以上方式,终于把结构体数组给传进去了。不过,这里要注意一点,不同的编译器对结构体的大小会不一定,比如上面的结构体

在BCB中如果没有字节对齐的话,有时会比一般的结构体大小多出2两个字节。因为BCB默认的是2字节排序,而VC是默认1 个字节排序。要解决该问题,要么在BCB的结构体中增加字节对齐,要么在C#中多开两个字节(如果有多的话)。字节对齐代码如下。

#pragma pack(push,1)struct Info
{int OrderNO;char UniqueCode[32];float CpuPercent;
};
#pragma pack(pop)

用Marsh.AllocHGlobal为结构体指针开辟内存空间,目的就是转变化非托管内存,那么如果不用Marsh.AllocHGlobal,还有没有其他的方式呢?

其实,不论C++中的是指针还是数组,最终在内存中还是一个一个字节存储的,也就是说,最终是以一维的字节数组形式展现的,所以我们如果开一个等大小的一维数组,那是否就可以了呢?答案是可以的,下面给出了实现。

[DllImport("workStation.dll")]private static extern bool fetchInfos(IntPtr infosIntPtr);[DllImport("workStation.dll")]private static extern bool fetchInfos(byte[] infos);[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]public struct Info{public int OrderNO;[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]public byte[] UniqueCode;public float CpuPercent;};private void buttonTest_Click(object sender, EventArgs e){try{int count = 128;int size = Marshal.SizeOf(typeof(Info));byte[] inkInfosBytes = new byte[count * size];                if (fetchInfos(inkInfosBytes)){MessageBox.Show("Fail");return;}Info[] infos = new Info[count];for (int inkIndex = 0; inkIndex < count; inkIndex++){byte[] inkInfoBytes = new byte[size];Array.Copy(inkInfosBytes, inkIndex * size, inkInfoBytes, 0, size);infos[inkIndex] = (Info)bytesToStruct(inkInfoBytes, typeof(Info));}string message = "";foreach (Info info in infos){message += string.Format("OrderNO={0}\r\nUniqueCode={1}\r\nCpu={2}",info.OrderNO,Encoding.UTF8.GetString(info.UniqueCode),info.CpuPercent);}MessageBox.Show(message);}catch (System.Exception ex){MessageBox.Show(ex.Message);}}#region bytesToStruct/// <summary>/// Byte array to struct or classs./// </summary>/// <param name=”bytes”>Byte array</param>/// <param name=”type”>Struct type or class type./// Egg:class Human{...};/// Human human=new Human();/// Type type=human.GetType();</param>/// <returns>Destination struct or class.</returns>public static object bytesToStruct(byte[] bytes, Type type){int size = Marshal.SizeOf(type);//Get size of the struct or class.          if (bytes.Length < size){return null;}IntPtr structPtr = Marshal.AllocHGlobal(size);//Allocate memory space of the struct or class. Marshal.Copy(bytes, 0, structPtr, size);//Copy byte array to the memory space.object obj = Marshal.PtrToStructure(structPtr, type);//Convert memory space to destination struct or class.         Marshal.FreeHGlobal(structPtr);//Release memory space.    return obj;}#endregion

对于实在想不到要怎么传数据的时候,可以考虑byte数组来传(即便是整型也可以,只要是开辟4字节(在32位下)),只要开的长度对应的上,在拿到数据后,要按类型规则转换成所要的数据,一般都能达到目的。

转载请注明出处 http://blog.csdn.net/xxdddail/article/details/11781003

转载于:https://www.cnblogs.com/pangblog/p/3329198.html

C#调用C++DLL传递结构体数组的终极解决方案相关推荐

  1. 托管代码C#调用非托管C++ API, 封送嵌套结构体数组

    一.前言: 最近这两天由于项目需要,提供给客户的C++ 动态库需要返回自定义结构体数组,网上也查了很多资料, 推荐一本书, <精通.NET互操作:P/Invoke.C++ Interop和COM ...

  2. C语言数组、结构体、结构体数组作为函数参数

    数组作为函数参数 如果想把一个数组名参数传递给函数,正确的函数形参应该是怎样的?它是应该声明为一个指针还是一个数组? 调用函数时实际传递的是一个指针,所以函数的形参实际上是一个指针,但为了使程序员新手 ...

  3. c语言调用dll实例 结构体传参,Matlab调用Dll,对于自定义结构体数组如何传参? - 程序语言 - 小木虫 - 学术 科研 互动社区...

    %[例子1] % Matlab调用DLL,整形数组传参 v = [1,22,33;44,-56,67]; pv = libpointer('int16Ptr',v); m = get(pv,'Valu ...

  4. python结构体数组传出接收c动态库_使用Python向C语言的链接库传递数组、结构体、指针类型的数据...

    使用python向C语言的链接库传递数组.结构体.指针类型的数据 由于最近的项目频繁使用python调用同事的C语言代码,在调用过程中踩了很多坑,一点一点写出来供大家参考,我们仍然是使用ctypes来 ...

  5. c# 与c++接口间调用时传递结构体

    复杂结构体的传递 1. 输出参数,结构体作为指针传出 非托管部分代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 2 ...

  6. C#:向C++封送结构体数组

    在使用第三方的非托管API时,我们经常会遇到参数为指针或指针的指针这种情况, 一般我们会用IntPtr指向我们需要传递的参数地址: 但是当遇到这种一个导出函数时,我们如何正确的使用IntPtr呢, e ...

  7. 用python实现结构体数组

    结构体数组 在C语言中我们可以通过struct关键字定义结构类型,结构中的字段占据连续的内存空间,每个结构体占用的内存大小都相同,因此可以很容易地定义结构数组.和C语言一样,在NumPy中也很容易对这 ...

  8. NumPy-快速处理数据--ndarray对象--多维数组的存取、结构体数组存取、内存对齐、Numpy内存结构...

    本文摘自<用Python做科学计算>,版权归原作者所有. 上一篇讲到:NumPy-快速处理数据--ndarray对象--数组的创建和存取 接下来接着介绍多维数组的存取.结构体数组存取.内存 ...

  9. 【C++】【一】结构体数组

    demo7:函数份文件编写 swap.h #include <iostream> using namespace std;//函数的声明 void swap(int a, int b); ...

最新文章

  1. 【C语言】学习笔记3——字符串
  2. linux驱动内核,Linux内核设备驱动之Linux内核基础笔记整理
  3. 怎么全量备份oracle数据库,Oracle 数据库全量备份恢复和部分备份恢复 | 学步园...
  4. 二维码提升对比度文献调研(3)--A Low-Complexity Algorithm for Contrast Enhancement of Digital Images
  5. 如何用c语言随机输出除法题,随机产生30道四则运算(C语言)
  6. linux c++应用程序内存高或者占用CPU高的解决方案_20161213
  7. 东南大学成贤c语言试卷,成贤高数(下)期中试卷参考答案
  8. 【干货】400+页的《面向机器学习的数学》pdf
  9. 机器学习入门——K近邻算法
  10. 拓端tecdat|R语言在RCT中调整基线时对错误指定的稳健性
  11. 使用一键重装工具制作U盘启动盘失败的解决方法
  12. GD32F103ZKT6替换STM32F103调试手记
  13. VMware Esxi 下载地址
  14. angular的生命周期
  15. 网上订餐python_网上订餐管理系统摘要.doc
  16. 手机怎样转换php格式,手机视频格式转换方法适合所有手机视频格式转换|如何转换手机视频格式...
  17. Python + Django 入门级开发手顺
  18. 用卷积神经网络实现对小狗品种的识别
  19. nyoj 82-迷宫寻宝(一) (多重BFS)
  20. 气象学需要计算机知识吗,没毕业就被签走的铁饭碗专业 气象学

热门文章

  1. Access把每一天的数据累加_7天学会Power BI分析淘宝数据
  2. 无插件HTML,HTML5+CSS3实现无插件拖拽上传图片(支持预览与批量)分享!
  3. python遍历二维数组需要注意的是_for循环获取二维数组的元素时的bug
  4. 提高c 语言的方法,提高C程序效率的方法
  5. 网站不大但加载很慢怎么优化_博客网站首页加载优化
  6. python中json模块_Python的标准模块包json的实例用法
  7. C语言编程模拟超市抹零结账,STL实践项目之用queue模拟超市结账环节
  8. java 缓存一致性_Java多线程——CPU缓存原理和缓存一致性问题
  9. php正则匹配sg-nc-wap_php正则匹配
  10. 来了,单片机最强科普总结!