C#调用C/C++动态库 封送结构体,结构体数组 因为公司一直都是做C++开发的,因客户需要要提供C#版本接口,研究了一下C#,发现其强大简洁, 在跨语言调用方面封装的很彻底,提供了强大的API与之交互.这点比JNA方便多了. java与C#都只能调用C格式导出动态库,因为C数据类型比较单一,容易映射. 两者都是在本地端提供一套与之映射的C#/java描述接口,通过底层处理这种映射关系达到调用的目的.
一.结构体的传递
Cpp代码- #defineJNAAPIextern"C"__declspec(dllexport)//C方式导出函数
- typedefstruct
- {
- intosVersion;
- intmajorVersion;
- intminorVersion;
- intbuildNum;
- intplatFormId;
- charszVersion[128];
- }OSINFO;
- //1.获取版本信息(传递结构体指针)
- JNAAPIboolGetVersionPtr(OSINFO*info);
- //2.获取版本信息(传递结构体引用)
- JNAAPIboolGetVersionRef(OSINFO&info);
可以通过二种方式来调用:
C#代码- //OSINFO定义
- [StructLayout(LayoutKind.Sequential)]
- publicstructOSINFO
- {
- publicintosVersion;
- publicintmajorVersion;
- publicintminorVersion;
- publicintbuildNum;
- publicintplatFormId;
- [MarshalAs(UnmanagedType.ByValTStr,SizeConst=128)]
- publicstringszVersion;
- }
1. 方式一(传入结构体引用),在C#中,结构体是以传值方式传递,类才是以传地址方式传递,加关键字ref即可. C端传递了两种不同类型的参数,都可以通过引用来解决.
C#代码- [DllImport("jnalib.dll",EntryPoint="GetVersionPtr")]
- publicstaticexternboolGetVersionPtr(refOSINFOinfo);
- publicstaticexternboolGetVersionRef(refOSINFOinfo);
2. 方式二(传入IntPtr(平台通用指针))
Cpp代码- IntPtrpv=Marshal.AllocHGlobal(148);//结构体在使用时一定要分配空间(4*sizeof(int)+128)
- Marshal.WriteInt32(pv,148);//向内存块里写入数值
- if(GetVersionPtr(pv))//直接以非托管内存块地址为参数
- {
- Console.WriteLine("--osVersion:{0}",Marshal.ReadInt32(pv,0));
- Console.WriteLine("--Major:{0}",Marshal.ReadInt32(pv,4));//移动4个字节
- Console.WriteLine("--BuildNum:"+Marshal.ReadInt32(pv,12));
- Console.WriteLine("--szVersion:"+Marshal.PtrToStringAnsi((IntPtr)(pv.ToInt32()+20)));
- }
- Marshal.FreeHGlobal(pv);//处理完记得释放内存
二.结构体数组的传递
Cpp代码- //传递结构体指针
- JNAAPIboolGetVersionArray(OSINFO*info,intnLen);
调用代码:
C#代码- /**
- *C#接口,对于包含数组类型,只能传递IntPtr
- */
- [DllImport("jnalib.dll",EntryPoint="GetVersionArray")]
- publicstaticexternboolGetVersionArray(IntPtrp,intnLen);
- //源目标参数
- OSINFO[]infos=newOSINFO[2];
- for(inti=0;i<infos.Length;i++)
- {
- infos[i]=newOSINFO();
- }
- IntPtr[]ptArr=newIntPtr[1];
- ptArr[0]=Marshal.AllocHGlobal(Marshal.SizeOf(typeof(OSINFO))*2);//分配包含两个元素的数组
- IntPtrpt=Marshal.AllocHGlobal(Marshal.SizeOf(typeof(OSINFO)));
- Marshal.Copy(ptArr,0,pt,1);//拷贝指针数组
- GetVersionArray(pt,2);//调用
- //还原成结构体数组
- for(inti=0;i<2;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);
- }
三.复杂结构体的传递
1. 输出参数,结构体作为指针传出
Cpp代码- typedefstruct
- {
- charname[20];
- intage;
- doublescores[30];
- }Student;
- //Class中包含结构体数组类型
- typedefstruct
- {
- intnumber;
- Studentstudents[50];
- }Class;
- //传入复杂结构体测试
- JNAAPIintGetClass(Class*pClass,intlen);
Cpp代码- //接口定义
- [DllImport("jnalib.dll",EntryPoint="GetClass")]
- publicstaticexternintGetClass(IntPtrpv,intlen);
- //结构体定义
- //Student
- [StructLayout(LayoutKind.Sequential)]
- publicstructStudent
- {
- [MarshalAs(UnmanagedType.ByValTStr,SizeConst=20)]
- publicstringname;
- publicintage;
- [MarshalAs(UnmanagedType.ByValArray,SizeConst=30)]
- publicdouble[]scores;
- }
- //Class
- [StructLayout(LayoutKind.Sequential)]
- publicstructClass
- {
- publicintnumber;
- [MarshalAs(UnmanagedType.ByValArray,SizeConst=50)]//指定数组尺寸
- publicStudent[]students;//结构体数组定义
- }
- //调用复杂结构体测试
- intsize=Marshal.SizeOf(typeof(Class))*50;
- IntPtrpBuff=Marshal.AllocHGlobal(size);//直接分配50个元素的空间,比Marshal.copy方便多了
- GetClass(pBuff,50);
- Class[]pClass=newClass[50];
- for(inti=0;i<50;i++)
- {
- IntPtrptr=newIntPtr(pBuff.ToInt64()+Marshal.SizeOf(typeof(Class))*i);
- pClass[i]=(Class)Marshal.PtrToStructure(ptr,typeof(Class));
- }
- Marshal.FreeHGlobal(pBuff);//释放内存
2. 输入参数, 给复杂结构体赋值后作为输入参数传入
对于比较大的结构体指针,无法直接应用结构体类型,转化成IntPtr类型, 此时需要将原生类型转化为指针,并给指针赋值
调用方法: Marshal.StructureToPtr(stu, ptr1, true)