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

在项目开发时,要调用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; public 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
错误

绝对好文C#调用C++DLL传递结构体数组的终极解决方案的更多相关文章

  1. C#调用C++DLL传递结构体数组的终极解决方案

    在项目开发时,要调用C++封装的DLL,普通的类型C#上一般都对应,只要用DllImport传入从DLL中引入函数就可以了.但是当传递的是结构体.结构体数组或者结构体指针的时候,就会发现C#上没有类型 ...

  2. C#调用C dll,结构体传参

    去年用wpf弄了个航线规划软件,用于生成无人机喷洒农药的作业航线,里面包含了不少算法.年后这几天将其中的算法移植到C,以便其他同事调用.昨天在用C#调用生成的dll时,遇到一些问题,折腾了好久才解决. ...

  3. C#调用C++ dll时,结构体引用传参的方法

    写了一个C++的LogLog Logit 四参数等算法的接口dll,给C#调用,但是发现传参有问题 如 extern "C" _declspec(dllexport)  bool ...

  4. C#调用C、C++结构体数组的方法总结

    一个客户要使用C#调用我们用C++开发的一个动态链接库,本来我没有C#的开发经验,就随便写了一个例程.以为很简单就可以搞定,没想到客户开发的过程中遇到了不少问题,最困难的就是用C#调用C++接口中的自 ...

  5. C#调用C/C++动态库 封送结构体,结构体数组

    因为实验室图像处理的算法都是在OpenCV下写的,还有就是导航的算法也是用C++写的,然后界面部分要求在C#下写,所以不管是Socket通信,还是调用OpenCV的DLL模块,都设计到了C#和C++数 ...

  6. python调用c/c++时传递结构体参数

    背景:使用python调用linux的动态库SO文件,并调用里边的c函数,向里边传递结构体参数.直接上代码 //test1.c # include <stdio.h> # include ...

  7. C#调用C/C++动态库 封送结构体,结构体数组

    一. 结构体的传递 #define JNAAPI extern "C" __declspec(dllexport) // C方式导出函数 typedef struct { int ...

  8. WPF向系统发送消息 并传递结构体

    场景 :需要开发一个通讯组件 流程为:界面-开启接收服务-通过发送组件发送信息到 其他客户端和服务端 接受服务接收其他客户端发送的消息 需要传递给对应组件或者界面 因此会出现类库重复引用问题.因为采用 ...

  9. C#引用c++DLL结构体数组注意事项(数据发送与接收时)

    本文转载自:http://blog.csdn.net/lhs198541/article/details/7593045 最近做的项目,需要在C# 中调用C++ 写的DLL,因为C# 默认的编码方式是 ...

随机推荐

  1. 【水题递归】【HDU2044】我大沙茶了

    有一只经过训练的蜜蜂只能爬向右侧相邻的蜂房,不能反向爬行.请编程计算蜜蜂从蜂房a爬到蜂房b的可能路线数. 其中,蜂房的结构如下所示.   Input 输入数据的第一行是一个整数N,表示测试实例的个数, ...

  2. linq读书笔记2-查询内存中的对象

    上次我们说到了linq对数组内容的检索,自.net2.0以后,泛型成了很常见的一种应用技术,linq对泛型的检索也提供了完善的支持 如对list类型的支持,范例如下: class Program    ...

  3. 重写TextView,实现圆形背景,文本居中显示

    最近,在做考试试题排版,产品提出题号希望显示成圆形背景,序号文本居中显示. (有点问题:文本没有绝对居中,暂时没做处理.) 为此,我采取的方式是重写TextView的onDraw方法,绘制一个圆形背景 ...

  4. jQuery图片懒加载lazyload插件

    http://www.neoease.com/lazy-load-jquery-plugin-delay-load-image/ js 模板引擎

  5. 寻找最大数--nyoj题目448

    寻找最大数 时间限制:1000 ms  |  内存限制:65535 KB 难度:2   描述 请在整数 n 中删除m个数字, 使得余下的数字按原次序组成的新数最大, 比如当n=920813467185 ...

  6. Oracle EBS-SQL (INV-11):检查子库存会计信息.sql

    select          OOD.ORGANIZATION_CODE                                               库存组织代码,          ...

  7. QT Creator 2.7.2 代码自动补全快捷键设置

    在QT Creater界面点[工具]再进[选项]找到[环境]下的[键盘]选项,搜索[CompleteThis]发现默认快捷键就是CTRL+SPACE,把它删除,然后添加自己想设置的快捷键(因为之前用e ...

  8. apache httpd, nginx, tomcat, jboss

    web上的server都叫web server,但是大家分工也有不同的. nginx常用做静态内容服务和代理服务器(不是你FQ那个代理),直面外来请求转发给后面的应用服务(tomcat,django什 ...

  9. VS2010启动奔溃

    最近由于项目需要,装了git,需要用bitbucket管理源代码.没想到装了git得插件后 vs2010不能启动,卸载重新安装也行,修复也不行. 解决方案: 删除 目录C:\Users\用户名\App ...

  10. yield语句

        自C#的第一个版本以来,使用foreach语句可以轻松地迭代集合.在C#1.0中,创建枚举器仍需要做大量的工作.C#2.0添加了yield语句,以便于创建枚举器.yield return语句返 ...