本文主要演示一个简单范例及介绍相关知识。
先亮出演示代码,该程序演示的是在C#代码中传入结构体与委托,然后在C++代码中调用委托并将结构体传入以进行修改。
首先C++编写的“CppDll.dll”:
typedef struct _MyStruct
{
int value;
}MyStruct, *PMyStruct;
typedef void __stdcall ChangeValue(MyStruct *value);
void __stdcall UseCallBack(MyStruct * value, ChangeValue callback)
{
callback(value);
}
本人导出函数使用的是“模块定义文件(.def)”,建议使用这种方法,因为这样可以使在导出C++函数的时候函数名不会改变。如下:
LIBRARY CppDll
EXPORTS
UseCallBack @1
当然也可以使用导出符号,但是导出C++函数的时候,函数名会被修改,
所以为了方便起见,如果要使用这种方法最好导出C函数,以保证导出函数名不变,如下:
extern "C" __declspec(dllexport) ...
然后是Unity C#代码:
using UnityEngine;
using System.Runtime.InteropServices;
public class Test : MonoBehaviour {
[StructLayout(LayoutKind.Sequential)]
struct MyStruct
{
public int value;
}
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate void ChangeValue(ref MyStruct value);
[DllImport("CppDll", EntryPoint = "UseCallBack", CallingConvention = CallingConvention.StdCall)]
static extern void UseCallBack(ref MyStruct value, [MarshalAs(UnmanagedType.FunctionPtr)] ChangeValue callback);
[AOT.MonoPInvokeCallback(typeof(ChangeValue))]
static void Change(ref MyStruct my)
{
my.value += 10;
}
void Start () {
MyStruct my;
my.value = 0;
UseCallBack(ref my, Change);
print(my.value);
}
}
代码亮完了,接下来详细讲解,C++代码没什么好讲,直接讲C#部分。
先讲特性部分。
StructLayout特性:允许你控制内存中类或结构的数据字段的物理布局。
一般情况下,只需要是用Sequential方式就可以了。
UnmanagedFunctionPointer特性:控制作为非托管函数指针传入或传出非托管代码的委托的调用约定。
没有什么限制,只要与C++的回调函数调用约定一致即可。
DllImport特性:使用包含要导入的方法的 DLL。
用于指定导入的C++ DLL和导入的函数名称和其调用约定。
MarshalAs特性:指示如何在托管代码和非托管代码之间封送数据。
根据两边的参数类型来设置,如果是都有的类型就不用设置了。
MonoPInvokeCallback特性:用于将委托声明成回调函数。
如果是在Unity当中必须在C# 的委托上声明,如果是在C# Console工程中就不用声明。
如上就是特性部分,特性的参数本人只列出了一部分,剩下的读者可通过对应的链接查看。
接下来,梳理下要点。
C#中的委托类型对应C++中的函数指针类型,需要声明相同的参数列表。
如果C++参数类型C#这边没有的话,则需要使用MarshalAs特性指定类型。
通过UnmanagedFunctionPointer特性使委托类型的调用约定与其相同。
C++的指针与引用对应C#的“ref”关键字。
在Unity3D中委托函数必须是静态的且必须声明MonoPInvokeCallback特性,要不然会在运行是发生异常“ExecutionEngineException: Attempting to JIT compile method ‘…’ while running with –aot-only.”。
通过DllImport导入的函数在特性参数中如果写了EntryPoint参数则函数名可以不同,否则必须相同。
导入的函数的参数类型如果C#中没有的话,则需要使用MarshalAs特性指定类型。
如有疑问请留言,欢迎一起讨论。