[Unity3D]C# 调用C++ DLL

来源:互联网 发布:unity3d 寻路插件 编辑:程序博客网 时间:2024/06/07 06:26

本文主要演示一个简单范例及介绍相关知识。
先亮出演示代码,该程序演示的是在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 CppDllEXPORTS    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特性:允许你控制内存中类或结构的数据字段的物理布局。

  • LayoutKind:控制当导出到非托管代码时对象的布局
    • Sequential:对象的成员按照它们在被导出到非托管内存时出现的顺序依次布局。这些成员根据在 StructLayoutAttribute::Pack 中指定的封装进行布局,并且可以是不连续的。
    • Explicit:在未管理内存中的每一个对象成员的精确位置是被显式控制的,服从于 StructLayoutAttribute::Pack 字段的设置。每个成员必须使用 FieldOffsetAttribute 指示该字段在类型中的位置。
    • Auto:运行库自动为非托管内存中的对象的成员选择适当的布局。使用此枚举成员定义的对象不能在托管代码的外部公开。尝试这样做将引发异常。

一般情况下,只需要是用Sequential方式就可以了。

UnmanagedFunctionPointer特性:控制作为非托管函数指针传入或传出非托管代码的委托的调用约定。

  • CallingConvention:指定调用在非托管代码中实现的方法所需的调用约定。
    • Cdecl
    • FastCall
    • StdCall

没有什么限制,只要与C++的回调函数调用约定一致即可。

DllImport特性:使用包含要导入的方法的 DLL。

  • dllName:设置导入C++ Dll的路径或相对路径。
  • EntryPoint:指示要调用的 DLL 入口点的名称或序号。
  • CallingConvention:上面已讲。

用于指定导入的C++ DLL和导入的函数名称和其调用约定。

MarshalAs特性:指示如何在托管代码和非托管代码之间封送数据。

根据两边的参数类型来设置,如果是都有的类型就不用设置了。

  • UnmanagedType:指定如何将参数或字段封送到非托管代码。
    • FunctionPtr:一个可用作 C 样式函数指针的整数。可将此成员用于 Delegate 数据类型或从 Delegate 继承的类型。

MonoPInvokeCallback特性:用于将委托声明成回调函数。

  • Type:委托类型。

如果是在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特性指定类型。

如有疑问请留言,欢迎一起讨论。

这里写图片描述

0 0
原创粉丝点击