關於在Unity3D中使用C++ DLL庫的記錄
啊,long time no see...大概又是很久沒有更新部落格了。好訊息是我今天正式收到了轉正郵件了。
最近get到了一個新技能,就是在U3D中調我們自己寫的C++的庫。之前也有用到過,但都是導師來接好了,沒有自己研究過。現將內容記錄如下。
1. 關於Unity中匯入外部庫,在PC端和安卓端
pc端使用的是C++ test.dll, 安卓端使用的是 libtest.so. 其中安卓端的庫是以lib開頭,在Unity指令碼中不需要單獨處理。
檔案組織:
—Plugins
——Android
————libtest.so
——test.dll
2.接庫
- 需要在指令碼中包含 using System.Runtime.InteropServices
- 使用如下命令來指定下面的函式是來自外部庫的
[DllImport("test")]
static extern int Creat(IntPtr hMemMgr, ref IntPtr phEngine);
3.C++ 和 C#之間的資料型別對應以傳輸資料方式
C++和C#的型別對應,很多文章都有,這裡就不贅述整理,只將比較困擾我自己的部分記錄下來。
- C++ 中的void* 型別(一般咱們用來作handle,engine),在C#中是用IntPtr
如,C++端定義了一個空指標
void* handle = null;
在C# 中使用IntPtr.Zero對應
IntPtr handle = IntPtr.Zero;
- C++中的包含指標的結構體,在C#中的定義
如,C++端有如下結構體A
struct A
{
unsigned int num;
float* points;
int* states;
}
- 在C#端要如何定義並且傳輸到C++端呢?
這裡主要用到Marshal.AllocHGlobal為指標分配記憶體,和Marshal.FreeHGlobal()釋放
DANGDANGDANG...
struct A { public UInt32 num; public IntPtr points; public IntPtr states; //對IntPtr指標分配記憶體,即初始化A結構體 public Init(int num) { points = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(float)) * num); states = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(int)) * num); } //分配了記憶體,需要釋放 public void Release() { Marshal.FreeHGlobal(points); Marshal.FreeHGlobal(states); } }
定義好了C#端的結構體後,就可以為這個指標所指向的記憶體賦值,並將這個A結構體物件傳到C++的函式中。
主要是對給指標分配的這塊記憶體進行劃分,另起一個指標,不斷的對這塊記憶體賦值,並移動一定的位置,主要用到的是
Marshal.StructureToPtr()函式
A a;
a.Init();
long longptr = a.points.ToInt64();
long longptr1 = a.states.ToInt64();
for(int i = 0; i < num; ++i)
{
IntPtr temp1 = new IntPtr(longptr);
IntPtr temp2 = new IntPtr(longptr1);
Marshal.StructureToPtr(1.0f, temp1, false);
longptr += Marshal.SizeOf(typeof(float));
Marshal.StructureToPtr(1, temp2, false);
longptr1 += Marshal.SizeOf(typeof(int));
}
//傳入外部函式
SetPoints(ref a);
a.Release();
- 獲取從C++端的資料到C#的結構體中
其實整體和上面差不多,不過這裡分享一下對於比較複雜的結構體,比如一個結構體內還包含了另一個結構體
C++中有結構體A,A的定義如上
struct B
{
unsigned int num;
A* points;
}
C#中的定義:
public struct B
{
public UInt32 num;
public IntPtr pointslist;
public void Init(int num)
{
pointslist = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(A)) * num);
num = (UInt32)num;
}
public void Release()
{
Marshal.FreeHGlobal(pointslist);
}
}
在C#中獲取C++傳來的B, 主要用到的就是下面這個方法去將unmanaged資料,轉換成C#中的結構體,下面這個方法親測有效。
核心是Marshal.PtrToStructure()
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] = (T)Marshal.PtrToStructure(ins,typeof(T));
}
}
使用方法如下:
A[] alist;
B b;
b.Init(num);
//C++中的方法
GetB(ref b);
MarshalUnmananagedArray2Struct(b.pointslist, (int)b.num, out alist);
b.Release();
主要的內容就是這樣啦~記錄完畢...