网站建设教学后记,天元建设集团有限公司枣庄,网站设计所需软件,优化网站seo什么是平台调用 (P/Invoke)
P/Invoke 是可用于从托管代码访问非托管库中的结构、回调和函数的一种技术。
托管代码与非托管的区别 托管代码和非托管代码的主要区别是内存管理方式和对计算机资源的访问方式。托管代码通常运行在托管环境中#xff0c;如 mono 或 java 虚拟机等…什么是平台调用 (P/Invoke)
P/Invoke 是可用于从托管代码访问非托管库中的结构、回调和函数的一种技术。
托管代码与非托管的区别 托管代码和非托管代码的主要区别是内存管理方式和对计算机资源的访问方式。托管代码通常运行在托管环境中如 mono 或 java 虚拟机等这些环境提供了垃圾回收器(GC)等工具来管理内存。在托管环境中程序员通常不需要手动分配和释放内存因为这些任务由运行时系统自动完成。托管代码通常具有更高的安全性和可移植性因为它们运行在虚拟机中而不是直接在操作系统上运行。相比之下非托管代码通常是直接在操作系统上运行的这意味着程序员需要手动分配和释放内存来避免内存泄漏和其他内存相关的问题。非托管代码可以更直接地访问计算机硬件和操作系统资源因此它们通常具有更高的性能和更好的控制性。P/Invoke API 包含在以下两个命名空间中System 和 System.Runtime.InteropServices。 使用这两个命名空间可提供用于如何与 native 通信的工具。 P/Invoke 从功能上来说只支持函数调用在被导出的函数前面一定要添加extern C来指明导出函数的时候使用C语言方式编译和连接这样保证函数定义的名字和导出的名字相同否则如果默认按C方式导出那个函数的名字就会变得乱七八糟我们的程序就无法找到入口点了。
互调的基本原理
首先我们需要了解 数据类型 的概念 在大多数编译型语言中定义变量的时候都需要先指定其数据类型所谓数据类型是语言约定的一个便于记忆的名称(int,long,float)究其本质就是在指明了这个数据在内存中到底是占用了几个字节程序在运行的时候首先找到这个数据的地址然后再按照该类型的长度读取响应的内存然后再处理。 基于这个原理编程语言直接就可以进行互调了对于不同语言之间的互调只要将改数据的指针(内存地址)传递给另一个语言在另一个语言中根据通信协议将指针所指向的数据存储入长度对应的数据类型即可当然要满足以下条件
对于像 java,C# 这样有运行时虚拟机变成语言来讲由于虚拟内存会让堆内存来回转移因此在进行互调的时候需要保证被调用的数据所在内存一定要固定不能被转移。有一些编程语言支持指针有一些语言不支持指针如Java这个问题并不重要所谓指针其实就是一个内存地址对于32位OS的指针是一个32位整数而对于64位机OS的指针是一个64位整数。因为大多数语言中都有整型数所以可以利用整型来接收指针。
Native 库的加载
举例被加载库的名称为 nativelib.so/nativelib.dll
在 windows 平台运行时将按以下顺序搜寻 dll: nativelibnativelib.dll
[DllImport(nativelib)]
[DllImport(nativelib.dll)]
static extern int ExportedFunction();在 linux 或 macOS 上运行时运行时会尝试在 lib 前添加并追加规范共享库扩展。在这些 OS 上按以下顺序尝试名称变体 nativelib.so/nativelib.dyliblibnativelib.so/libnativelib.dylibnativeliblibnativelib
基本数据类型传递
C# 关键字.Net类型C/CbyteSystem.Byteuint8_tsbyteSystem.SByteint8_tshortSystem.Int16int16_tushortSystem.UInt16uint16_tintSystem.Int32int32_tuintSystem.UInt32uint32_tlongSystem.Int64int64_tulongSystem.UInt64uint64_tcharSystem.CharcharnintSystem.IntPtrintptr_tboolSystem.BooleanWin32 BOOL 类型decimalSystem.DecimalCOM DECIMAL 结构
调用流程
基础数据类型调用
#define EXPORTAPI __declspec(dllexport)extern C EXPORTAPI int add(int a, int b)
{return a b;
}
extern C EXPORTAPI void writeString(char* content)
{cout content endl;
}
第一行代码中定义了一个 EXPORTAPI 的宏,对应的内容是 __declspec(dllexport) 意思是将后面修饰的内容定义为 Dll 中要导出的内容 当然也剋以不使用这个宏直接将 __declspec(dllexport) 写在要导出的函数前。第二行中的 extern C 表示该函数编译和连接时使用 C 语言的方式以保证函数名称不变由于 C 支持函数重载因此编译器会将函数的参数类型也加到编译后的代码中。例如函数 void fun(int,int),编译后的可能是 _fun_int_int(不同的编译器可能不同但都采取了类似的机制)extern 是 C/C 语言中表明函数和全局变量的作用范围的关键字这个关键字告诉编译器其声明的函数和变量可以在本模块或其他模块中使用。 [DllImport(TestCPPDll.dll,EntryPoint add)]extern static int add(int a, int b);[DllImport(TestCPPDll.dll)]extern static void writeString(string content);public static void Add(){int result add(1, 2);Console.WriteLine($CallLibraryAdd result :{result});}public static void WriteString(string str){writeString(str);}DllImport 中第一个参数表示 Dll 文件的位置第二个参数 EntryPoint 用来指明对应的 C/C 中的函数名称是什么 extern 关键字表明该方法是一个外部调用这个方法声明后就可以像调用一个普通的静态方法一样去使用了。
指针的传递
extern C EXPORTAPI void addInt(int* i)
{*i 1;cout *i endl;
}//传入一个整型数组的指针以及数组长度遍历每一个元素并且输出
extern C EXPORTAPI void sendIntArray(int* firstElement, int arraylength)
{cout C# send int array to CPP endl;int* currentPointer firstElement;for (int i 0; i arraylength; i){cout currentPointer[i];}cout endl;
}在第一个方法中参数为一个 int 类型的指针并将其所指向的内容 1
第二个方法传入一个整型数组的指针以及数组长度遍历数组的每一个元素并且输出 #region 指针的传递[DllImport(TestCPPDll.dll)]extern unsafe static void addInt(int* i);[DllImport(TestCPPDll.dll)]extern unsafe static void sendIntArray(int* firstElement, int arraylength);[DllImport(TestCPPDll.dll, EntryPoint getArrayFromCPP)]extern unsafe static IntPtr getArrayFromCPPUseInpPtr(IntPtr count);[DllImport(TestCPPDll.dll, EntryPoint getArrayFromCPP)]extern unsafe static IntPtr getArrayFromCPPUseRef(ref int count);// 调用 C 中的 AddInt 方法public static void AddInt(){int i 10;unsafe{addInt(i);}}//调用 C 中的 sendIntArray 方法将 C# 中的数组数据传递到 C 中并在 C 中输出public static void AddIntArry(){int[] csArry new int[10];for (int iArr 0; iArr 10; iArr){csArry[iArr] iArr;}unsafe{fixed (int* pCSArray csArry[0]){sendIntArray(pCSArray, 10);}}}//调用C中的GetArrayFromCPP方法获取一个C中建立的数组使用 InpPrt 类型传参IntPtr 与指针可以互相强转public static void GetArrayFromCPPUseInpPtr(){Console.WriteLine(CPP send int array to C# user IntPtr);int count 0;GCHandle gch GCHandle.Alloc(count, GCHandleType.Pinned);IntPtr countInptr gch.AddrOfPinnedObject();IntPtr pArrayPointer getArrayFromCPPUseInpPtr(countInptr);count Marshal.PtrToStructureint(countInptr);int[] array new int[count];for (int i 0; i count; i){IntPtr intPtr IntPtr.Add(pArrayPointer, i * Marshal.SizeOfint());array[i] Marshal.PtrToStructureint(intPtr);Console.Write(array[i]);}Console.WriteLine();}//调用C中的GetArrayFromCPP方法获取一个C中建立的数组, 使用 ref 会出现防御性拷贝造成 GCpublic static void GetArrayFromCPPUseRef(){Console.WriteLine(CPP send int array to C# user ref);int count 0;IntPtr pArrayPointer getArrayFromCPPUseRef(ref count);int[] array new int[count];for (int i 0; i count; i){IntPtr intPtr IntPtr.Add(pArrayPointer, i * Marshal.SizeOfint());array[i] Marshal.PtrToStructureint(intPtr);Console.Write(array[i]);}Console.WriteLine();}#endregion函数指针的传递
C# 中并没有函数指针的概念但是可以使用 委托 来代替函数指针
在 C/C 运行的某一时刻调用 C# 的对应函数 这个时候就需要将一个 C# 中已经指向某一个函数的函数指针(委托)传递给 C
//定义一个函数指针
typedef void (*OnCSCallBack)(int value);OnCSCallBack onCSCallBack;//用于设置函数指针的方法
extern C EXPORTAPI void setCallback(OnCSCallBack callback)
{onCSCallBack callback;cout set CPP callback success endl;cout endl;
}//对 C# 中传递过来的委托进行调用
extern C EXPORTAPI void testCPPInvoke() {int value 999;onCSCallBack(value);
}public delegate void OnCSDelegate(int value);[DllImport(TestCPPDll.dll, CharSet CharSet.Auto, CallingConvention CallingConvention.Cdecl)]extern static void setCallback(OnCSDelegate action);[DllImport(TestCPPDll.dll)]extern static void testCPPInvoke();//测试过程中发现 .Net 中 deletegate 可以传递。actionint 不行。//而在 Unity 中 将 Action 方法添加[MonoPInvokeCallback(typeof(Actionint))] 标签后可以. MonoPInvokeCallback 标签在 AOT 命名空间下public static void SetCPPCallBack(){OnCSDelegate onCSCallBack OnCSCallback;setCallback(onCSCallBack);}public static void OnCSCallback(int value){Console.WriteLine($CPP call CSharp founction value:{value});}
结构体的传递
传递结构体的想法和传递一个int类型数据类似struct中的数据是在内存中顺序排列的
C#中结构体字段类型与C结构体中的字段类型相兼容C#结构中的字段顺序与C结构体中的字段顺序相同要保证该功能需要将C#结构体标记为[StructLayout( LayoutKind.Sequential)]
typedef struct Vector3_
{float x;float y;float z;
} Vector3;// 输出从 C# 侧传入的结构体内容
extern C EXPORTAPI void sendStructFormCSToCPP(Vector3 vector3)
{cout get vector3 int cpp,x:;cout vector3.x;cout ,y:;cout vector3.y;cout ,z:;cout vector3.z endl;cout endl;
}//包含固定长度数组的结构体
typedef struct ContainArray_
{Vector3 vectorArray[5];float valueArray[2];
}ContainArray;//不固定数组长度的结构体
typedef struct ContainArrayHasCount_
{int arrayCount;float* floatArray;int vectorArrayCount;Vector3* vectorArray;
}ContainArrayHasCount;//输出从 C# 侧传入数组固定长度的结构体信息
extern C EXPORTAPI void sendContainArrayStructFormCSToCPP(ContainArray containArray)
{int vectorArrayCount sizeof(containArray.vectorArray) / sizeof(*containArray.vectorArray);for (size_t i 0; i vectorArrayCount; i){cout vectorArray i: i ,x: containArray.vectorArray[i].x ,y: containArray.vectorArray[i].y ,z: containArray.vectorArray[i].z endl;}int valueArrayCount sizeof(containArray.valueArray) / sizeof(*containArray.valueArray);for (size_t i 0; i valueArrayCount; i){cout valueArray i: i , value: containArray.valueArray[i] endl;}cout endl;
}//输出运行时决定数组长度的结构体信息
extern C EXPORTAPI void sendContainArrayHasCountStructFormCSToCPP(ContainArrayHasCount containArrayHasCount)
{for (size_t i 0; i containArrayHasCount.arrayCount; i){cout containArrayHasCount i: i ,ArrayValue: containArrayHasCount.floatArray[i] endl;}for (size_t i 0; i containArrayHasCount.vectorArrayCount; i){cout vectorArray i: i ,x: containArrayHasCount.vectorArray[i].x ,y: containArrayHasCount.vectorArray[i].y ,z: containArrayHasCount.vectorArray[i].z endl;}
}//返回 C 侧创建的结构体指针
extern C EXPORTAPI ContainArrayHasCount * getArrayStructFormCPP()
{ContainArrayHasCount* containArrayHasCount new ContainArrayHasCount();containArrayHasCount-arrayCount 3;containArrayHasCount-floatArray new float[3];for (size_t i 0; i containArrayHasCount-arrayCount; i){containArrayHasCount-floatArray[i] i;}containArrayHasCount-vectorArrayCount 4;containArrayHasCount-vectorArray new Vector3[4];for (size_t i 0; i containArrayHasCount-vectorArrayCount; i){containArrayHasCount-vectorArray[i].x i;containArrayHasCount-vectorArray[i].y i;containArrayHasCount-vectorArray[i].z i;}return containArrayHasCount;
}[StructLayout(LayoutKind.Sequential)]public struct Vector3{public Vector3(float x, float y, float z){X x;Y y;Z z;}public float X, Y, Z;}[StructLayout(LayoutKind.Sequential)]public struct ContainArray{[MarshalAs(UnmanagedType.ByValArray, SizeConst 5)]public Vector3[] vectorArray;[MarshalAs(UnmanagedType.ByValArray, SizeConst 2)]public float[] valueArray;}[StructLayout(LayoutKind.Sequential)]public struct ContainArrayHasCount{public int floatArrayCount;public IntPtr floatArray;public int vectorArrayCount;public IntPtr vectorArray;}[DllImport(TestCPPDll)]extern static void sendStructFormCSToCPP(Vector3 vector3);[DllImport(TestCPPDll)]extern static void sendContainArrayStructFormCSToCPP(ContainArray containArrayStruct);[DllImport(TestCPPDll)]extern static void sendContainArrayHasCountStructFormCSToCPP(ContainArrayHasCount containArrayHasCount);[DllImport(TestCPPDll)]extern static IntPtr getArrayStructFormCPP();// 结构体作为参数传递给 C 方法public static void SendStructFormCSToCPP(){Vector3 vector3 new Vector3();vector3.X 1;vector3.Y 2;vector3.Z 3;sendStructFormCSToCPP(vector3);}// 传入包含指定长度数组的结构体public static void SendContainArrayStructFormCSToCPP(){ContainArray containArrayStruct new ContainArray();containArrayStruct.vectorArray new Vector3[5];for (int i 0; i 5; i){containArrayStruct.vectorArray[i].X i;containArrayStruct.vectorArray[i].Y i;containArrayStruct.vectorArray[i].Z i;}containArrayStruct.valueArray new float[2];containArrayStruct.valueArray[0] 1.234f;containArrayStruct.valueArray[1] 5.678f;sendContainArrayStructFormCSToCPP(containArrayStruct);}// 传入运行时决定数组大小的结构体public static void SendContainArrayHasCountStructFormCSToCPP(){ContainArrayHasCount containArrayHasCount new ContainArrayHasCount();float[] floatArray new float[] { 1, 22, 333, 4444, 55555 };Vector3[] vectorArray new Vector3[] { new Vector3(123, 123, 123), new Vector3(456, 456, 456), new Vector3(789, 789, 789) };containArrayHasCount.floatArrayCount floatArray.Length;containArrayHasCount.vectorArrayCount vectorArray.Length;GCHandle floatArrayHandle GCHandle.Alloc(floatArray,GCHandleType.Pinned);containArrayHasCount.floatArray floatArrayHandle.AddrOfPinnedObject();GCHandle vectorArrayHandle GCHandle.Alloc(vectorArray,GCHandleType.Pinned);containArrayHasCount.vectorArray vectorArrayHandle.AddrOfPinnedObject();sendContainArrayHasCountStructFormCSToCPP(containArrayHasCount);floatArrayHandle.Free();vectorArrayHandle.Free();}// 接收从 C 返回的结构体public static void GetArrayStructFormCPP(){unsafe{IntPtr structIntPtr getArrayStructFormCPP();ContainArrayHasCount containArrayHasCount Marshal.PtrToStructureContainArrayHasCount(structIntPtr);for (int i 0; i containArrayHasCount.floatArrayCount; i){IntPtr floatIntPtr IntPtr.Add(containArrayHasCount.floatArray,i*Marshal.SizeOffloat());float value Marshal.PtrToStructurefloat(floatIntPtr);Console.WriteLine($floatArray {i}:{value});}for (int i 0; i containArrayHasCount.vectorArrayCount; i){IntPtr vectorIntPtr IntPtr.Add(containArrayHasCount.vectorArray,i*Marshal.SizeOfVector3());Vector3 vector Marshal.PtrToStructureVector3(vectorIntPtr);Console.WriteLine($vectorArray {i}:{vector.X},{vector.Y},{vector.Z});}Console.WriteLine($floatArrayCount: {containArrayHasCount.floatArrayCount});Console.WriteLine($vectorArrayCount: {containArrayHasCount.vectorArrayCount});}}