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

8/3/2015来源:C#应用人气:1321

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

因为公司一直都是做C++开发的,因客户需要要提供C#版本接口,研究了一下C#,发现其强大简洁, 在跨语言调用方面封装的很彻底,提供了强大的API与之交互.这点比JNA方便多了. java与C#都只能调用C格式导出动态库,因为C数据类型比较单一,容易映射. 两者都是在本地端提供一套与之映射的C#/java描述接口,通过底层处理这种映射关系达到调用的目的.

一.结构体的传递

Cpp代码收藏代码
  1. #defineJNAAPIextern"C"__declspec(dllexport)//C方式导出函数
  2. typedefstruct
  3. {
  4. intosVersion;
  5. intmajorVersion;
  6. intminorVersion;
  7. intbuildNum;
  8. intplatFormId;
  9. charszVersion[128];
  10. }OSINFO;
  11. //1.获取版本信息(传递结构体指针)
  12. JNAAPIboolGetVersionPtr(OSINFO*info);
  13. //2.获取版本信息(传递结构体引用)
  14. JNAAPIboolGetVersionRef(OSINFO&info);

可以通过二种方式来调用:

C#代码收藏代码
  1. //OSINFO定义
  2. [StructLayout(LayoutKind.Sequential)]
  3. publicstructOSINFO
  4. {
  5. publicintosVersion;
  6. publicintmajorVersion;
  7. publicintminorVersion;
  8. publicintbuildNum;
  9. publicintplatFormId;
  10. [MarshalAs(UnmanagedType.ByValTStr,SizeConst=128)]
  11. publicstringszVersion;
  12. }

1. 方式一(传入结构体引用),在C#中,结构体是以传值方式传递,类才是以传地址方式传递,加关键字ref即可. C端传递了两种不同类型的参数,都可以通过引用来解决.

C#代码收藏代码
  1. [DllImport("jnalib.dll",EntryPoint="GetVersionPtr")]
  2. publicstaticexternboolGetVersionPtr(refOSINFOinfo);
  3. publicstaticexternboolGetVersionRef(refOSINFOinfo);

2. 方式二(传入IntPtr(平台通用指针))

Cpp代码收藏代码
  1. IntPtrpv=Marshal.AllocHGlobal(148);//结构体在使用时一定要分配空间(4*sizeof(int)+128)
  2. Marshal.WriteInt32(pv,148);//向内存块里写入数值
  3. if(GetVersionPtr(pv))//直接以非托管内存块地址为参数
  4. {
  5. Console.WriteLine("--osVersion:{0}",Marshal.ReadInt32(pv,0));
  6. Console.WriteLine("--Major:{0}",Marshal.ReadInt32(pv,4));//移动4个字节
  7. Console.WriteLine("--BuildNum:"+Marshal.ReadInt32(pv,12));
  8. Console.WriteLine("--szVersion:"+Marshal.PtrToStringAnsi((IntPtr)(pv.ToInt32()+20)));
  9. }
  10. Marshal.FreeHGlobal(pv);//处理完记得释放内存

二.结构体数组的传递

Cpp代码收藏代码
  1. //传递结构体指针
  2. JNAAPIboolGetVersionArray(OSINFO*info,intnLen);

调用代码:

C#代码收藏代码
  1. /**
  2. *C#接口,对于包含数组类型,只能传递IntPtr
  3. */
  4. [DllImport("jnalib.dll",EntryPoint="GetVersionArray")]
  5. publicstaticexternboolGetVersionArray(IntPtrp,intnLen);
  6. //源目标参数
  7. OSINFO[]infos=newOSINFO[2];
  8. for(inti=0;i<infos.Length;i++)
  9. {
  10. infos[i]=newOSINFO();
  11. }
  12. IntPtr[]ptArr=newIntPtr[1];
  13. ptArr[0]=Marshal.AllocHGlobal(Marshal.SizeOf(typeof(OSINFO))*2);//分配包含两个元素的数组
  14. IntPtrpt=Marshal.AllocHGlobal(Marshal.SizeOf(typeof(OSINFO)));
  15. Marshal.Copy(ptArr,0,pt,1);//拷贝指针数组
  16. GetVersionArray(pt,2);//调用
  17. //还原成结构体数组
  18. for(inti=0;i<2;i++)
  19. {
  20. infos[i]=(OSINFO)Marshal.PtrToStructure((IntPtr)(pt.ToInt32()+i*Marshal.SizeOf(typeof(OSINFO))),typeof(OSINFO));
  21. Console.WriteLine("OsVersion:{0}szVersion:{1}",infos[i].osVersion,infos[i].szVersion);
  22. }

三.复杂结构体的传递

1. 输出参数,结构体作为指针传出

Cpp代码收藏代码
  1. typedefstruct
  2. {
  3. charname[20];
  4. intage;
  5. doublescores[30];
  6. }Student;
  7. //Class中包含结构体数组类型
  8. typedefstruct
  9. {
  10. intnumber;
  11. Studentstudents[50];
  12. }Class;
  13. //传入复杂结构体测试
  14. JNAAPIintGetClass(Class*pClass,intlen);

Cpp代码收藏代码
  1. //接口定义
  2. [DllImport("jnalib.dll",EntryPoint="GetClass")]
  3. publicstaticexternintGetClass(IntPtrpv,intlen);
  4. //结构体定义
  5. //Student
  6. [StructLayout(LayoutKind.Sequential)]
  7. publicstructStudent
  8. {
  9. [MarshalAs(UnmanagedType.ByValTStr,SizeConst=20)]
  10. publicstringname;
  11. publicintage;
  12. [MarshalAs(UnmanagedType.ByValArray,SizeConst=30)]
  13. publicdouble[]scores;
  14. }
  15. //Class
  16. [StructLayout(LayoutKind.Sequential)]
  17. publicstructClass
  18. {
  19. publicintnumber;
  20. [MarshalAs(UnmanagedType.ByValArray,SizeConst=50)]//指定数组尺寸
  21. publicStudent[]students;//结构体数组定义
  22. }
  23. //调用复杂结构体测试
  24. intsize=Marshal.SizeOf(typeof(Class))*50;
  25. IntPtrpBuff=Marshal.AllocHGlobal(size);//直接分配50个元素的空间,比Marshal.copy方便多了
  26. GetClass(pBuff,50);
  27. Class[]pClass=newClass[50];
  28. for(inti=0;i<50;i++)
  29. {
  30. IntPtrptr=newIntPtr(pBuff.ToInt64()+Marshal.SizeOf(typeof(Class))*i);
  31. pClass[i]=(Class)Marshal.PtrToStructure(ptr,typeof(Class));
  32. }
  33. Marshal.FreeHGlobal(pBuff);//释放内存

2. 输入参数, 给复杂结构体赋值后作为输入参数传入

对于比较大的结构体指针,无法直接应用结构体类型,转化成IntPtr类型, 此时需要将原生类型转化为指针,并给指针赋值

调用方法: Marshal.StructureToPtr(stu, ptr1, true)