1. 程式人生 > >關於在Unity3D中使用C++ DLL庫的記錄

關於在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();

主要的內容就是這樣啦~記錄完畢...