一. 结构体的传递
#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 pt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(OSINFO)) * 2); GetVersionArray(pt, 2); //调用 //还原成结构体数组 for (int i = 0; i < 2; i++) {
IntPtr ptr = (IntPtr)((UInt32)pt + i * Marshal.SizeOf(typeof(OSINFO))); infos[i]=(OSINFO)Marshal.PtrToStructure(ptr,typeof(OSINFO)); Console.WriteLine("OsVersion:{0} szVersion:{1}", infos[i].osVersion, infos[i].szVersion); }
Marshal.FreeHGlobal (pt);
三. 复杂结构体的传递
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)
转摘自