流程
通常情况下C++ 封装在so库,放在unity 工程里的Plugins文件夹里,需要设置对应的编译结构(32bit或者64bit), C# 通过DllImport调用C++接口
- unsafe 指针
- IntPtr 代替指针
通信文件结构
以sdk为例
//命名空间
namespace JOE
{
using System;
using System.Runtime.InteropServices;
public class JoeSdkWrapper : System.IDisposable
{
//so库的名称
private const string SDKLib = "joe";
//通用句柄
private IntPtr handle = IntPtr.Zero;
public JoeSdkWrapper() { }
//供外部C#调用的接口,封装了c++映射接口
public string GetVersion()
{
return sdk_api.GetVersion();
}
}
//用struct 封装 所有的c++ 映射接口
private struct sdk_api
{
/// <summary>
/// Get version number of SDK api
/// </summary>
/// <returns>version</returns>
[DllImport(STHandLib)]
public static extern string GetVersion();//名字必须和C++头文件的方法名一致
}
}
通用接口映射
-
必须映射重写部分
- struct
//C++
typedef struct JoeTime{
float resizeTime;
float joe_matrix[3][3];
}HandTime;
//C#
[StructLayout(LayoutKind.Sequential)]
public struct JoeTime
{
public float resizeTime;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]//特别注意这个一定要声明数组的size大小
public float[] joe_matrix; ///matrix [ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f ]
}
2. enum
//C++
typedef enum {
DOWN= 0,
UP = 1,
} JoeType;
//C#
public enum JoeType
{
DOWN= 0,
UP = 1,
}
3. function
//C++
const char* GetVersion();
//C#
[DllImport(SDKLib)]
public static extern string GetVersion();//名字必须和C++头文件的方法名一致
- 常见的类型映射总结 ,用到的时候可查询
//c++:HANDLE(void *) ---- c#:System.IntPtr
//c++:Byte(unsigned char) ---- c#:System.Byte
//c++:SHORT(short) ---- c#:System.Int16
//c++:WORD(unsigned short) ---- c#:System.UInt16
//c++:INT(int) ---- c#:System.Int16
//c++:INT(int) ---- c#:System.Int32
//c++:UINT(unsigned int) ---- c#:System.UInt16
//c++:UINT(unsigned int) ---- c#:System.UInt32
//c++:LONG(long) ---- c#:System.Int32
//c++:ULONG(unsigned long) ---- c#:System.UInt32
//c++:DWORD(unsigned long) ---- c#:System.UInt32
//c++:DECIMAL ---- c#:System.Decimal
//c++:BOOL(long) ---- c#:System.Boolean
//c++:CHAR(char) ---- c#:System.Char
//c++:LPSTR(char *) ---- c#:System.String
//c++:LPWSTR(wchar_t *) ---- c#:System.String
//c++:LPCSTR(const char *) ---- c#:System.String
//c++:LPCWSTR(const wchar_t *) ---- c#:System.String
//c++:PCAHR(char *) ---- c#:System.String
//c++:BSTR ---- c#:System.String
//c++:FLOAT(float) ---- c#:System.Single
//c++:DOUBLE(double) ---- c#:System.Double
//c++:VARIANT ---- c#:System.Object
//c++:PBYTE(byte *) ---- c#:System.Byte[]
//c++:BSTR ---- c#:StringBuilder
//c++:LPCTSTR ---- c#:StringBuilder
//c++:LPCTSTR ---- c#:string
//c++:LPTSTR ---- c#:[MarshalAs(UnmanagedType.LPTStr)] string
//c++:LPTSTR 输出变量名 ---- c#:StringBuilder 输出变量名
//c++:LPCWSTR ---- c#:IntPtr
//c++:BOOL ---- c#:bool
//c++:HMODULE ---- c#:IntPtr
//c++:HINSTANCE ---- c#:IntPtr
//c++:结构体 ---- c#:public struct 结构体{};
//c++:结构体 **变量名 ---- c#:out 变量名
//C#中提前申明一个结构体实例化后的变量名
//c++:结构体 &变量名 ---- c#:ref 结构体 变量名
//c++:WORD ---- c#:ushort
//c++:DWORD ---- c#:uint
//c++:DWORD ---- c#:int
//c++:UCHAR ---- c#:int
//c++:UCHAR ---- c#:byte
//c++:UCHAR* ---- c#:string
//c++:UCHAR* ---- c#:IntPtr
//c++:GUID ---- c#:Guid
//c++:Handle ---- c#:IntPtr
//c++:HWND ---- c#:IntPtr
//c++:DWORD ---- c#:int
//c++:COLORREF ---- c#:uint
//c++:unsigned char ---- c#:byte
//c++:unsigned char * ---- c#:ref byte
//c++:unsigned char * ---- c#:[MarshalAs(UnmanagedType.LPArray)] byte[]
//c++:unsigned char * ---- c#:[MarshalAs(UnmanagedType.LPArray)] IntPtr
//c++:unsigned char & ---- c#:ref byte
//c++:unsigned char 变量名 ---- c#:byte 变量名
//c++:unsigned short 变量名 ---- c#:ushort 变量名
//c++:unsigned int 变量名 ---- c#:uint 变量名
//c++:unsigned long 变量名 ---- c#:ulong 变量名
//c++:char 变量名 ---- c#:byte 变量名
//C++中一个字符用一个字节表示,C#中一个字符用两个字节表示
//c++:char 数组名[数组大小] ---- c#:MarshalAs(UnmanagedType.ByValTStr, SizeConst = 数组大小)]
public string 数组名; ushort
//c++:char * ---- c#:string
//传入参数
//c++:char * ---- c#:StringBuilder
//传出参数
//c++:char *变量名 ---- c#:ref string 变量名
//c++:char *输入变量名 ---- c#:string 输入变量名
//c++:char *输出变量名 ---- c#:[MarshalAs(UnmanagedType.LPStr)] StringBuilder 输出变量名
//c++:char ** ---- c#:string
//c++:char **变量名 ---- c#:ref string 变量名
//c++:const char * ---- c#:string
//c++:char[] ---- c#:string
//c++:char 变量名[数组大小] ---- c#:[MarshalAs(UnmanagedType.ByValTStr,SizeConst=数组大小)] public string 变量名;
//c++:struct 结构体名 *变量名 ---- c#:ref 结构体名 变量名
//c++:委托 变量名 ---- c#:委托 变量名
//c++:int ---- c#:int
//c++:int ---- c#:ref int
//c++:int & ---- c#:ref int
//c++:int * ---- c#:ref int
//C#中调用前需定义int 变量名 = 0;
//c++:*int ---- c#:IntPtr
//c++:int32 PIPTR * ---- c#:int32[]
//c++:float PIPTR * ---- c#:float[]
//c++:double** 数组名 ---- c#:ref double 数组名
//c++:double*[] 数组名 ---- c#:ref double 数组名
//c++:long ---- c#:int
//c++:ulong ---- c#:int
//c++:UINT8 * ---- c#:ref byte
//C#中调用前需定义byte 变量名 = new byte();
//c++:handle ---- c#:IntPtr
//c++:hwnd ---- c#:IntPtr
//c++:void * ---- c#:IntPtr
//c++:void * user_obj_param ---- c#:IntPtr user_obj_param
//c++:void * 对象名称 ---- c#:([MarshalAs(UnmanagedType.AsAny)]Object 对象名称
//c++:char, INT8, SBYTE, CHAR ---- c#:System.SByte
//c++:short, short int, INT16, SHORT ---- c#:System.Int16
//c++:int, long, long int, INT32, LONG32, BOOL , INT ---- c#:System.Int32
//c++:__int64, INT64, LONGLONG ---- c#:System.Int64
//c++:unsigned char, UINT8, UCHAR , BYTE ---- c#:System.Byte
//c++:unsigned short, UINT16, USHORT, WORD, ATOM, WCHAR , __wchar_t ---- c#:System.UInt16
//c++:unsigned, unsigned int, UINT32, ULONG32, DWORD32, ULONG, DWORD, UINT ---- c#:System.UInt32
//c++:unsigned __int64, UINT64, DWORDLONG, ULONGLONG ---- c#:System.UInt64
//c++:float, FLOAT ---- c#:System.Single
//c++:double, long double, DOUBLE ---- c#:System.Double
//双针指类型参数,可以用 ref IntPtr
调用接口
- 调用接口的时候需要将struct 数据结构转成 IntPtr,具体写法如下:
IntPtr ParamPtr = Marshal.AllocHGlobal(Marshal.SizeOf(IntrinsicParam));
Marshal.StructureToPtr(IntrinsicParam, ParamPtr, true);
//TODO :使用ParamPtr
Marshal.FreeHGlobal(ParamPtr);
- 将byte[] 转成IntPtr
//colorData 是byte[],rgb.data是IntPtr
rgb.data = Marshal.AllocHGlobal(colorData.Length);
Marshal.Copy(colorData, 0, rgb.data, colorData.Length);
- 将返回值是IntPtr 转回 struct
//hans 是IntPtr ,HAPPY 是struct类型
HAPPY handGesture = (HAPPY)Marshal.PtrToStructure(hands, typeof(HAPPY));
- 将返回值是IntPtr 转回 byte[]
/points_3d 是IntPtr ,KeyPointsPosArr,是byte[],KeyPointsCount * 3是byte[]的字节总数
Marshal.Copy(points_3d, KeyPointsPosArr, 0, KeyPointsCount * 3);
- 记得释放IntPtr
Marshal.FreeHGlobal(one);
特殊情况
- struct 数组里的string数组赋值
IntPtr[] model_files_array = new IntPtr[2];
// Each IntPtr array element will point to a copy of a
// string element in the openFileDialog.FileNames array.
for (int i = 0; i < 2; i++)
{
model_files_array[i] = Marshal.StringToHGlobalAnsi(Path[i]);
}
// In order to obtain the address of the IntPtr array,
// we must fix it in memory. We do this using GCHandle.
GCHandle gch = GCHandle.Alloc(model_files_array, GCHandleType.Pinned);
// pimage_files will point to the head of the IntPtr array.
IntPtr filesFirstPtr = gch.AddrOfPinnedObject();
- 将intprt转成结构体数组
public static void MarshalUnmananagedArray2Struct<T>(IntPtr unmanagedArray, int length, out T[] mangagedArray)
{
var size = Marshal.SizeOf(typeof(T));
mangagedArray = new T[length];
for (int i = 0; i < length; i++)
{
IntPtr ins = new IntPtr(unmanagedArray.ToInt64() + i * size);
mangagedArray[i] = Marshal.PtrToStructure<T>(ins);
}
}
常见问题
C++ long 是4字节,对应c#的int