Unity加载C语言32dll,【Unity3D】C#调用C++的DLL

很多时候我们都会用到第三方C++的DLL,但是我们并不知道其中的原理和过程,考虑到以后需要用C++封装一些通用的接口,就花时间学习一下在unity中调用C++的接口。

在VS中就可以编译DLL,只是要注意unity系统版本。

编译安卓的.so,可以参考:使用eclipse编译.so动态链接库

三种参数类型:

1.值类型

2.引用类型

3.指针类型

4.函数类型

关于4函数类型:

C++:

//定义函数指针;

typedef void(__stdcall *CPPCallback)(int x, int y, int len);

// 定义接口

extern “C” void _DLLExport SetCallback(CPPCallback callback);

c#:

public delegate void CSCallback(int x, int y, int len);

[DllImport(“TestDLL”)]

private static extern void SetCallback(CSCallback callback);

tips:

unity运行提示找不到DLL,可能是放的目录不对,也可能是DLL编译的平台不对。

细节都在代码中:

C++部分:

TestDll.h

#ifdef TESTDLL_EXPORTS

// 将带有_DLLExport的函数声明为C语言方式的DLL导出函数,忽略重载机制

#define TESTDLL_API extern "C" __declspec(dllexport)

#else

#define TESTDLL_API extern "C"

#endif

TESTDLL_API int FunTest();

typedef struct

{

char name[128];

}TestInfoChild;

typedef struct

{

public:

int x;

int y;

char name[128];

TestInfoChild child;

}TestInfo;

// 通过传递【结构体引用】传递

TESTDLL_API bool GetInfoRef(TestInfo &info);

// 通过传递【结构体指针】传递(在C#端有两种写法)

TESTDLL_API bool GetInfoPtr(TestInfo *info);

// 传递【结构体数组】(在C#只能通过指针传递)

TESTDLL_API bool GetInfoArray(TestInfo *info, int iLen);

TestDll.cpp

#include "stdafx.h"

#include "TestDll.h"

#include

using namespace std;

int FunTest()

{

return 6666;

}

bool GetInfoPtr(TestInfo *info)

{

info->x = 6;

info->y = 8;

strcpy(info->name, "hello_ptr");

return true;

}

bool GetInfoRef(TestInfo &info)

{

info.x = 12;

info.y = 16;

strcpy(info.name, "hello_ref");

return true;

}

bool GetInfoArray(TestInfo *info, int iLen)

{

for (int i = 0; i < iLen; i ++)

{

info[i].x = 11 * (i + 1);

info[i].y = 11 * (i + 1);

// 索引转成字符

char strIndex[4];

itoa(i, strIndex, 16);

// 拼接name

sprintf(info[i].name, "%s%s%s","parent:", "name_", strIndex);

// 子结构体赋值

sprintf(info[i].child.name, "%s%s%s", "child:", "name_", strIndex);

}

return true;

}

c#部分:

using UnityEngine;

using System.Runtime.InteropServices;

using System;

public class TestDll : MonoBehaviour {

[DllImport("TestDll")]

static extern int FunTest();

// 通过【结构体引用】传递参数

[DllImport("TestDll")]

static extern bool GetInfoRef(ref TestInfo info);

// 通过【结构体指针】传递参数的两种写法

[DllImport("TestDll")]

//static extern bool GetInfoPtr(ref TestInfo info); // 写法1:结构体引用(和上面的一样)

static extern bool GetInfoPtr(IntPtr info);         // 写法2:通用指针

// 通过【结构体数组】传递参数

[DllImport("TestDll")]

static extern bool GetInfoArray(IntPtr p, int iLen);

[StructLayout(LayoutKind.Sequential)] // 在内存中按顺序排列,Explicit可通过FieldOffset指定位置

public struct TestInfoChild

{

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]

public string name;

}

[StructLayout(LayoutKind.Sequential)] // 在内存中按顺序排列,Explicit可通过FieldOffset指定位置

public struct TestInfo

{

public int x;

public int y;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]

public string name;

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]

public TestInfoChild child;

}

void Start () {

int data = FunTest();

Debug.Log("普通方法测试:" + data);

TestStruct();

TestArray();

}

private void TestStruct()

{

Debug.Log("引用的方式获取值=====================================");

TestInfo info = new TestInfo();

GetInfoRef(ref info);

Debug.Log("x:" + info.x);

Debug.Log("y:" + info.y);

Debug.Log("name:" + info.name);

Debug.Log("指针的方式获取值=====================================");

// 指针参数写法1,结构体引用

//TestInfo info = new TestInfo();  // 初始化结构体

//GetInfoPtr(ref info);

//Debug.Log("ptr:x:" + info.x);

//Debug.Log("ptr:y:" + info.y);

//Debug.Log("ptr:name:" + info.name);

// 指针参数写法2,通过平台通用指针(使用类似C的malloc, free,分配堆内存)

int totalSize = Marshal.SizeOf(typeof(TestInfo));

IntPtr pt = Marshal.AllocHGlobal(totalSize);

if (GetInfoPtr(pt))

{

// 其实这里这么写就可以直接转化为结构体数据,但是会产生gc

//TestInfo info1 = (TestInfo)Marshal.PtrToStructure((IntPtr)pt.ToInt32(), typeof(TestInfo));

// 下面这么写,就不会产生gc

TestInfo info1;

//ReadInt32 从非托管内存中读取一个 32 位有符号整数

info1.x = Marshal.ReadInt32(pt, 0);

info1.y = Marshal.ReadInt32(pt, 4);

// PtrToStringAnsi 为托管字符串的副本从非托管ANSI第一个空字符开始的所有字符串,可以带长度

// pv.ToInt32() 将此实例的值转换为32位有符号的整数

// 转成整数后,加上前面2个参数所在的字节数8,就可以拿到后面的字符串啦

info1.name = Marshal.PtrToStringAnsi((IntPtr)(8 + pt.ToInt32()));

Debug.Log("x:" + info1.x);

Debug.Log("y:" + info1.y);

Debug.Log("name:" + info1.name);

}

Marshal.FreeHGlobal(pt); // 释放内存

}

private void TestArray()

{

Debug.Log("结构体数组的方式获取值=====================================");

TestInfo[] infos = GetArray(2);

for (int i = 0; i < infos.Length; i++)

{

Debug.Log("x:" + infos[i].x);

Debug.Log("y:" + infos[i].y);

Debug.Log("name:" + infos[i].name);

Debug.Log("child name:" + infos[i].child.name);

}

}

///

/// 可根据实际需求封装

///

private T[] GetArray(int iLen)

{

int structLen = iLen;

T[] infos = new T[structLen]; // 定义结构体数组数量

int totalSize = Marshal.SizeOf(typeof(TestInfo)) * structLen;

IntPtr pt = Marshal.AllocHGlobal(totalSize);   // 分配totalSize大小的内存,返回指针

if (GetInfoArray(pt, structLen))

{

for(int i = 0; i < structLen; i ++)

{

infos[i] = (T)Marshal.PtrToStructure((IntPtr)(pt.ToInt32() + i * Marshal.SizeOf(typeof(T))), typeof(T));

}

}

Marshal.FreeHGlobal(pt); // 释放内存

return infos;

}

}

输出:

df57e7c1bd59bf68823e195a3ac70110.png

最后编辑:2019-07-23作者:木木猫

eebbd16dee0486790e109aa6cbd448c5?s=96&d=mm&r=g

364244206@qq.com

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值