因为公司一直都是做C++开发的,因客户需要要提供C#版本接口,研究了一下C#,发现其强大简洁, 在跨语言调用方面封装的很彻底,提供了强大的API与之交互.这点比JNA方便多了. 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; // 1. 获取版本信息(传递结构体指针) JNAAPI bool GetVersionPtr( OSINFO *info ); // 2.获取版本信息(传递结构体引用) JNAAPI bool GetVersionRef(OSINFO &info);
可以通过二种方式来调用:
// 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; }
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(平台通用指针))
IntPtr pv = 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); //处理完记得释放内存
二.结构体数组的传递
// 传递结构体指针 JNAAPI bool GetVersionArray(OSINFO *info,int nLen);
调用代码:
/** * C#接口,对于包含数组类型,只能传递IntPtr */ [DllImport("jnalib.dll", EntryPoint = "GetVersionArray")] public static extern bool GetVersionArray(IntPtr p, int nLen); // 源目标参数 OSINFO[] infos = new OSINFO[2]; for (int i = 0; i < infos.Length; i++) { infos[i] = new OSINFO(); } IntPtr[] ptArr = new IntPtr[1]; ptArr[0] = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(OSINFO)) * 2); //分配包含两个元素的数组 IntPtr pt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(OSINFO))); Marshal.Copy(ptArr, 0, pt, 1); //拷贝指针数组 GetVersionArray(pt, 2); //调用 //还原成结构体数组 for (int i = 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. 输出参数,结构体作为指针传出
typedef struct { char name[20]; int age; double scores[30]; }Student; // Class中包含结构体数组类型 typedef struct { int number; Student students[50]; }Class; // 传入复杂结构体测试 JNAAPI int GetClass(Class *pClass,int len);
// 接口定义 [DllImport("jnalib.dll", EntryPoint = "GetClass")] public static extern int GetClass(IntPtr pv,int len); // 结构体定义 // Student [StructLayout(LayoutKind.Sequential)] public struct Student { [MarshalAs(UnmanagedType.ByValTStr,SizeConst=20)] public string name; public int age; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)] public double[] scores; } // Class [StructLayout(LayoutKind.Sequential)] public struct Class { public int number; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)] // 指定数组尺寸 public Student[] students; // 结构体数组定义 } // 调用复杂结构体测试 int size = Marshal.SizeOf(typeof(Class)) * 50; IntPtr pBuff = Marshal.AllocHGlobal(size); // 直接分配50个元素的空间,比Marshal.copy方便多了 GetClass(pBuff, 50); Class[] pClass = new Class[50]; for (int i = 0; i < 50; i++) { IntPtr ptr = new IntPtr(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)
相关推荐
资源代码演示的是c#代码调用c++ DLL 的方式。该演示为原创,绝非搬砖。解决了c# 调用 C++ Dll获取相关信息之如何传递结构体数组引用以及如何处理获取到的结构体数组数据的问题。
c++调用C# COM 参数是结构体数组
c#调用C++动态库、执行回调函数,并回传结构体参数数据。vs2017环境编写C#和C++动态库,这个为完整工程例子,可供相关人员学习参考。
C# 再调用C或C++ 动态库的时候,总是被生成正确的C函数在C#中的正确声明而困扰,而生成C++中结构体在C#中的声明
下面小编就为大家分享一篇基于C#调用c++Dll结构体数组指针的问题详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
主要介绍了C#调用C++DLL传递结构体数组的终极解决方案的相关资料,需要的朋友可以参考下
在写C#TCP通信程序时,发送数据时,只能发送byte数组,处理起来比较麻烦不说,如果是和c++等写的程序通信的话,很多的都是传送结构体,在VC6.0中可以很方便的把...C#调用c++dll时也可以使用此函数来转换结构体或指针。
C#调用C++中动态链接库DLL中的结构体使用方法,范例。。。
1.C#调用C++封装的动态库dll 2.结构体含union共同体的C++转C#示例 3.C#结构体指针的调用演示 4.C#读取USB数据设备的示例 5.C#动态调用C++dll库示例 6.C#静态调用C++dll库示例 注意:UsbLibDotNet使用时候需要先安装...
C#调用C++ dll, 包括结构体, 数组, 函数指针, 字符串等等
C#调用C++封装成DLL的结构体及类库, 使用数据结构类型, 使用动态库调用方式,函数入参为结构体,返回参数为结构体,使用out,ref 传统参数
主要解决开发人员遇到的常规问题,针对 C# 结构体 和 Byte 数组之间互相转换遇到的问题,代码中进行了简单封装,方便客户调用,下载可以直接使用,方便快捷。
C#调用C++生成的DLL,并返回结构体引用或者结构体指针多个值,工程已编译好
C#调用C++封装成DLL的结构体及类
Java调用C语言动态库(JNA方式):回调函数、结构体数组传参、结构体数组返回-附件资源
C#调Native CPP 类 的DLL文件 首先C++/CLI过度对Navive CPP类的调用 其次C#调用C++/CLI转化后的类 经测试效率还是很高的
c# 调用c++对于字符串与复杂结构体的处理
C#实现对C++dll动态库的调用,主要是对自定义结构体的使用,包括结构体的转换、传值、指针等情况。
1、用C++编写lib类库生成DLL文件 2、C#调用C++生成的DLL文件