C#调用C/C++动态库 封送结构体,结构体数组
因为实验室图像处理的算法都是在OpenCV下写的,还有就是导航的算法也是用C++写的,然后界面部分要求在C#下写,所以不管是Socket通信,还是调用OpenCV的DLL模块,都设计到了C#和C++数据类型的对应,还有结构体的封装使用。在夸语言调用方面,Java和C#都只能调用C格式导出的动态库,因为C数据类型比较单一,容易映射,两者都是在本地端提供一套与之映射的C#或者Java的描述接口,通过底层处理这种映射关系达到调用的目的。
一. 结构体的传递
#define JNAAPI extern "C" __declspec(dllexport) // C方式导出函数 typedef struct
{
int osVersion;
int majorVersion;
int minorVersion;
int buildNum;
int platFormId;
char szVersion[128];
}OSINFO; //获取版本信息(传递结构体指针)
JNAAPI bool SetVersionPtr(OSINFO *info)
{
char * str = "Hello DLL";
info->osVersion = 100;
strcpy(info->szVersion, str); return true;
}
//获取版本信息(传递结构体引用)
JNAAPI bool SetVersionRef(OSINFO &info)
{
char * str = "Hello DLL";
info.osVersion = 101;
strcpy(info.szVersion, str); return true;
}
在C#下对应的结构体定义:
// OSINFO定义
[StructLayout(LayoutKind.Sequential)]
public struct OSINFO
{
public int osVersion;
public int majorVersion;
public int minorVersion;
public int buildNum;
public int platFormId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string szVersion;
}
可以通过两种方式来调用非托管的函数SetVersionPtr:
1. 方式一(传入结构体引用),在C#中,结构体是以传值的方式传递的,类才是一传地址的方式传递的,加上关键字ref就可以了。C端传递了两种不同类型的参数,都可以通过引用来解决。
[DllImport("jnalib.dll", EntryPoint = "GetVersionPtr")]
public static extern bool GetVersionPtr(ref OSINFO info);
public static extern bool GetVersionRef(ref OSINFO info);
2. 方式二(传入IntPtr(平台通用指针))
对应的函数在C#中的声明:
[DllImport("CSharpInvokeCpp_CppDemo.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool SetVersionPtr(IntPtr pv);
测试代码:
IntPtr pv = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(OSINFO)));
//对申请的非托管的内存赋值
//但是没有对最后一个string赋值
for (int i = 0; i < 5; i++)
{
Marshal.WriteInt32(pv, i * Marshal.SizeOf(typeof(Int32)), ((Int32)(i + 1)));
} if (CppDLL.SetVersionPtr(pv))
{
Console.WriteLine("--osVersion:{0}", Marshal.ReadInt32(pv, 0));
Console.WriteLine("--Major:{0}", Marshal.ReadInt32(pv, 4));
Console.WriteLine("--Minor:{0}", Marshal.ReadInt32(pv, 8));
Console.WriteLine("--BuildNum:{0}", Marshal.ReadInt32(pv, 12));
Console.WriteLine("--szVersion:{0}", Marshal.PtrToStringAnsi((IntPtr)(pv.ToInt32()+20)));
}
Marshal.FreeHGlobal(pv);
上面的两种方法得区别是:
第一种方法是先创建一个等价的结构体,然后把这个结构体的引用传递给DLL中的接口函数。第二种方法是申请一块结构体大小的非托管内存,然后向内存中写入数据。调用的时候,只把这块非托管的内存的指针传递给DLL中的接口函数。其实第二种方法可以不用事先定义结构体,只需要知道结构体的大小,申请相应的非托管内存就可以了。
二.结构体数组的传递
DLL中非托管代码:
//传递结构体数组指针
JNAAPI bool SetVersionArray(OSINFO *info, int nLen)
{
char *str = "Hello DLL"; for(int i = 0; i < nLen; i++)
{
info[i].osVersion = 100;
strcpy(info[i].szVersion, str);
} return true;
}
调用代码接口:
//C#接口,对于包含数组类型,只能传递IntPtr
[DllImport("CSharpInvokeCpp_CppDemo.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool SetVersionArray(IntPtr pv, int nLen);
测试代码:
//结构体数组的传递测试代码
OSINFO[] infos = new OSINFO[2];
for (int i = 0; i < infos.Length; i++ )
{
infos[i] = new OSINFO();
}
IntPtr[] ptrArr = new IntPtr[1];
ptrArr[0] = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(OSINFO)) * 2);
IntPtr pt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(OSINFO))); Marshal.Copy(ptrArr, 0, pt, 1);
CppDLL.SetVersionArray(pt, 2); for (int i = 0; i < infos.Length; i++)
{
infos[i] = (OSINFO)Marshal.PtrToStructure((IntPtr)(pt.ToInt32() + i * Marshal.SizeOf(typeof(OSINFO))), typeof(OSINFO));
Console.WriteLine("OsVersion:{0} szVersion:{1}", infos[i].osVersion, infos[i].szVersion);
} Marshal.FreeHGlobal(ptrArr[0]);
Marshal.FreeHGlobal(pt);
上面的代码中有两处需要说明一下:
(1)C#中的IntPtr指针做相应的运算的时候,要首先把IntPtr做相应的转换,然后运算。运算结束之后还要再做相应的转换,还原成原来的IntPtr。
(2)Marshal.Copy这个函数的功能是:将数据从托管数组复制到非托管内存指针,或从非托管内存指针复制到托管数组。
三. 复杂结构体的传递
1. 输出参数,结构体作为指针传出
非托管部分代码:
typedef struct
{
char name[20];
int age;
double scores[32];
}Student; //Class中包含结构体数组类型
typedef struct
{
int number;
Student stedents[50];
}Class; JNAAPI int GetClass(Class *pClass,int len)
{
for(int i = 0; i < len; i++)
{
pClass[i].number = i;
for(int j = 0; j< 50; j++)
{
//把name中的前20个字节用0代替
memset(pClass[i].stedents[j].name, 0, 20);
//给每个同学命名
sprintf(pClass[i].stedents[j].name, "name_%d_%d", i, j);
pClass[i].stedents[j].age = j % 2 == 0 ? 15:20;
}//for
}//for return 0;
}
上面DLL 的导出函数要求传递的参数为它自定义的Class结构体数组, 那么我们在C#调用它时也要自定义对应的结构体了,
我们可以定义为如下:
[StructLayout(LayoutKind.Sequential)]
struct Student
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public string name;
public int age;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public double[] scores;
}
[StructLayout(LayoutKind.Sequential)]
struct Class
{
public int number;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)]
public Student[] students; }
需要注意的是,这2个结构体中的数组大小一定要跟C++中的限定一样大小哦,接下来如何使用这个API来正确的获取数据呢,大多数人可能想到像这样的处理方式:
Class myclass = new Class();
IntPtr ptr=Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Class)));
GetClass(ptr);
Marshal.FreeHGlobal(ptr);
没错,这样的处理是没问题的,但是我们的API的参数是Class数组,这种处理方式只是传递一个Class结构体参数,所以这种方式在这里就不太合适了,!
那大家就想到先Class[] myclass = new Class[MaxClass]; 然后在用Marshal.AllocHGlobal 来获取myclass 数据的指针,
其实这样也是错的, 因为 Class结构中包含了,不能直接封送的Student结构,所以无论如何上面的想法是错误的!
那要怎么办呢,其实很简单,就是先分配一段非托管内存,并调用API后,再将非托管内容数据读取到托管结构体数据中!
示例演示代码如下:
// 接口定义
[DllImport("CSharpInvokeCpp_CppDemo.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetClass(IntPtr pv, int len);
//复杂结构体传递测试代码
int size = Marshal.SizeOf(typeof(Class)) * 50;
IntPtr pBuff = Marshal.AllocHGlobal(size);
CppDLL.GetClass(pBuff, 50); Class[] pClass = new Class[50];
for (int i = 0; i < 50; i++)
{
IntPtr pr = new IntPtr(pBuff.ToInt64() + Marshal.SizeOf(typeof(Class)) * i);
pClass[i] = (Class)Marshal.PtrToStructure(pr, typeof(Class));
}
Marshal.FreeHGlobal(pBuff);
5月19日学习内容:
http://tcspecial.iteye.com/blog/1675621
http://www.cnblogs.com/naiking/archive/2013/01/17/2864132.html
http://blog.csdn.net/zhangj1012003_2007/article/details/6283032
http://blog.csdn.net/xiaowei_cqu/article/details/7693985
C#调用C/C++动态库 封送结构体,结构体数组的更多相关文章
- C#调用C/C++动态库 封送结构体,结构体数组
一. 结构体的传递 #define JNAAPI extern "C" __declspec(dllexport) // C方式导出函数 typedef struct { int ...
- C#调用C/C++动态库,封装各种复杂结构体
C#调用C/C++动态库,封装各种复杂结构体. 标签: c++结构内存typedefc# 2014-07-05 12:10 6571人阅读 评论(1) 收藏 举报 分类: C(8) C#(6) ...
- 关于C#调用非托管动态库方式的性能疑问
最近的项目中,因为一些原因,需要C#调用非托管(这里为C++)的动态库.网上喜闻乐见的方式是采用静态(DllImport)方式进行调用.偶然在园子里看到可以用动态(LoadLibrary,GetPro ...
- Android NDK开发及调用标准linux动态库.so文件
源:Android NDK开发及调用标准linux动态库.so文件 预备知识及环境搭建 1.NDK(native development Kit)原生开发工具包,用来快速开发C.C++动态库,并能自动 ...
- linux c编程调用系统的动态库时,要使用dlopen等函数吗?
同问 linux c编程调用系统的动态库时,要使用dlopen等函数吗? 2012-11-27 21:55 提问者: hnwlxyzhl 我来帮他解答 满意回答 2012-12-07 09:08 li ...
- cpp调用c的动态库
目录 cpp调用c的动态库 title: cpp调用c的动态库 date: 2019/11/22 20:34:29 toc: true --- cpp调用c的动态库 CPP文件里这么引用头文件即可 e ...
- C# 调用其他的动态库开发应注意的问题
1.背景 程序开发语言可以说是五花八门,这就引出了一个新问题 ,不同语言开发的系统进行对接时相关调用的问题. 下面我主要说一下我自己在做接口开发时遇到的问题及解决方法仅供参考,我使用的C#开发进行对接 ...
- 浙江省“一卡通”异地就医,C#调用省一卡通动态库
前言,最近学习调用 浙江省一卡通业务,主要就是调用一个DLL,动态库文件,这个动态库是浙大网新研发的. 借着自学的机会把心得体会都记录下来,方便感兴趣的小伙伴学习与讨论. 内容均系原创,欢迎大家转载分 ...
- 关于Android NDK中调用第三方的动态库
因为最近在整合Android 上RTSP播放器的网络库,因需要调用自己编译的网络库,调用一直出现问题,开始时是直接在Android.mk 中加入LOCAL_SHARED_LIBRARIES := li ...
随机推荐
- Java中的枚举类型详解
枚举类型介绍 枚举类型(Enumerated Type) 很早就出现在编程语言中,它被用来将一组类似的值包含到一种类型当中.而这种枚举类型的名称则会被定义成独一无二的类型描述符,在这一点上和常量的定义 ...
- PCB板常用知识简介——沉金板VS镀金板
一.PCB板表面处理: 抗氧化,喷锡,无铅喷锡,沉金,沉锡,沉银,镀硬金,全板镀金,金手指,镍钯金 OSP: 成本较底,可焊性好,存储条件苛刻,时间短,环保工艺.焊接好 .平整 . 喷锡:喷锡板一般 ...
- CCNA实验(9) -- Frame Relay
帧中继的一些特点:1.中小企业常用的广域网线路2.通信费用较低3.配置较为复杂 1.将Cisco路由器配置为帧中继交换机2.帧中继基本配置.帧中继映射3.在帧中继的链路上运行RIPv24.帧中继的多点 ...
- Java中static、this、super、final的用法
一. static 请先看下面这段程序: public class Hello{public static void main(String[] args){//(1)System. ...
- NGUI使用教程(1) 安装NGUI插件
前言 鉴于当前游戏开发的大势,Unity3d的发展势头超乎我的预期,作为一个Flash开发人员,也是为Flash在游戏开发尤其是手游开发中的地位感到担忧....所以 近期一段时间都在自己学习unity ...
- android 向serverGet和Post请求的两种方式,android向server发送文件,自己组装协议和借助第三方开源
一个适用于Android平台的第三方和apache的非常多东西类似,仅仅是用于Android上 我在项目里用的是这个 https://github.com/loopj/android-async-ht ...
- html文件引入其它html文件的几种方法:include方式
可以在一个html的文件当中读取另一个html文件的内容吗?答案是确定的,而且方法不只一种,在以前我只会使用iframe来引用,后来发现了另外的几种方法,那今天就总结这几种方法让大家参考一下. 1.I ...
- 面试题之java 编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串。 要求不能出现截半的情况
题目:10. 编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串. 但是要保证汉字不被截半个,如"我ABC"4,应该截为"我AB",输 ...
- 【Cocos2D-x 3.5实战】坦克大战(2)游戏开始界面
关于游戏的素材都是在网上到处搜集到的,然后自己再用二流的ps技术修修改改的,所以有可能混在一起有点不搭调(没有办法啊,没有美工Orz.. 项目已经建立好了,然后我们需要把我们下载的素材放到Resour ...
- java代理课程测试 spring AOP代理简单测试
jjava加强课程测试代码 反射. 代理 .泛型.beanUtils等 项目源码下载:http://download.csdn.net/detail/liangrui1988/6568169 热身运动 ...