一, C# 和 C++ 的通信
前面我的文章写过 c#/c/lua 是如何交互的, 通过将 c# 的函数和属性, 注册到 lua 虚拟机中, 可以实现通过 c 来互相交互.
而 c# 和 c++ 的交互, 也是非常类似的, c# 可以直接的通过 P/Invoke 的方式来调用 c++ 的函数, 而 C++ 调用 C# 的函数, C++ 的函数是被封装成 DLL 来放在 Unity 的工程文件中的 Plugins 中, 则需要基于. NET 来操作, 利用 Marshal.GetFunctionPointerForDelegate 来获取函数的指针, 然后传递到 c++ 中进行操作.
二, 编辑器下实现实时的编译和脚本更新
在 Unity 中, 我们可以在打开的 Unity 中, 直接编译 c# 的文件, 这样不需要每次都关闭工程再打开来执行编译, 而 C++ 由于通过 DLL 来调用, 每次更新的 C++ 都需要关闭工程, 然后更新 DLL, 然后打开工程, 这样的操作, 对于编辑器下的开发是极其耗费的.
对于上面提到的反复开关工程执行 DLL 的更新, 可以利用 [DllImport] 的属性来实现在编辑器下的更新:
该属性是基于 OS 的, 所以不会存在跨平台的问题.
三, 示例代码展示
- show the code
- c# code part:
- using System;
- using System.IO;
- using System.Runtime.InteropServices;
- using UnityEngine;
- class TestScript:MonoBehaviour
- {
- #if UNITY_EDITOR
- // pointer handle to the C++ DLL
- public IntPtr libarayHandle;
- public delegate void InitDelegate(IntPtr gameObjectNew,
- IntPtr gameObjectGetTransform, IntPtr transformSetPosition);
- #endif
- }
- #if UNITY_EDITOR_OSX || UNITY_EDITOR_LINUX
- //OSX 和 Linux 下的导入
- [DLLImport("__Internal")]
- public static extern IntPtr dlopen(string path, int flag);
- [DllImport("__Internal")]
- public static extern IntPtr dlsym(IntPtr handle, string symbolName);
- [DllImport("__Internal")]
- public static extern int dlclose(IntPtr handle);
- public static IntPtr OpenLibrary(string path)
- {
- IntPtr handle = dlopen(path, 0);
- if(handle == IntPtr.Zero)
- {
- throw new Exception("Couldn't open native library: "+ path);
- }
- return handle;
- }
- public static void CloseLibrary(IntPtr libraryHandle)
- {
- dlclose(libraryHandle);
- }
- public static T GetDelegate<T>(IntPtr libraryHandle, string functionName)
- where T: class
- {
- IntPtr symbol = dlsym(libraryHandle, functionName);
- if(symbol == IntPtr.Zero)
- {
- throw new Exception("Couldn't get function:" + functionName);
- }
- return Marshal.GetDelegateForFunctionPointer(symbol, typeof(T)) as T;
- }
- #elif UNITY_EDITOR_WIN
- // win 编辑器下
- [DllImport("kernel32")]
- public static extern IntPtr LoadLibrary(string path);
- [DllImport("kernel32")]
- public static extern IntPtr GetProcAddress(IntPtr libraryHandle,
- string symbolName);
- [DllImport("kernel32)"]
- public static extern bool FreeLibrary(IntPtr libraryHandle);
- public static IntPtr OpenLibrary(string path)
- {
- IntPtr handle = LoadLibrary(path);
- if(handle == IntPtr.Zero)
- {
- throw new Exception("Couldn't open native library: "+ path);
- }
- return handle;
- }
- public static void CloseLibrary(IntPtr libraryHandle)
- {
- FreeLibrary(libraryHandle);
- }
- public static T GetDelegate<T>(IntPtr libraryHandle, string functionName)
- where T: class
- {
- IntPtr symbol = GetProcAddress(libraryHandle, functionName);
- if(symbol == IntPtr.Zero)
- {
- throw new Exception("Couldn't get function:" + functionName);
- }
- return Marshal.GetDelegateForFunctionPointer(symbol, typeof(T)) as T;
- }
- #else
- // 本地加载
- [DllImport("NativeScript")]
- static extern void Init(IntPtr gameObjectNew,
- IntPtr gameObjectGetTransform, IntPtr transformSetPosition);
- [DllImport("NativeScript")]
- static extern void MonoBehaviourUpdate();
- #endif
- delegate int GameObjectNewDelegate();
- delegate int GameObjectGetTransformDelegate(int thisHandle);
- delegate void TransformSetPositionDelegate(int thisHandle, Vector3 position);
- #if UNITY_EDITOR_OSX
- const string LIB_PATH = "/NativeScript.bundle/Contents/MacOS/NativeScript";
- #elif UNITY_EDITOR_LINUX
- const string LIB_PATH = "/NativeScript.so";
- #elif UNITY_EDITOR_WIN
- const string LIB_PATH = "/NativeScript.dll";
- #endif
- void Awake()
- {
- #if UNITY_EDITOR
- //open the native library
- libraryHandle = OpenLibrary(Application.dataPath + LIB_PATH);
- InitDelegate Init = GetDelegate<InitDelegate>(libraryHandle, "Init");
- MonoBehaviourUpdate = GetDelegate<MonoBehaviourUpdateDelegate>(
- libraryHandle,"MonoBehaviourUpdate");
- #endif
- //init the C++ Library
- ObjectStore.Init(1024);
- Init(
- Marshal.GetFunctionPointerForDelegate(new GameObjectNewDelegate(GameObjectNew)),
- Marshal.GetFunctionPointerForDelegate(new GameObjectGetTransformDelegate(GameObjectGetTransform)),
- Marshal.GetFunctionPointerForDelegate(new TransformSetPositionDelegate(TransformSetPosition))
- );
- }
- void Update()
- {
- MonoBehaviourUpdate();
- }
- void OnApplicationQuit()
- {
- #if UNITY_EDITOR
- CloseLibrary(libraryHandle);
- libraryHandle = IntPtr.Zero;
- #endif
- }
- //c# function for c++ call
- static int GameObjectNew()
- {
- GameObject go = new GameObject();
- return ObjectStore.Store(go);
- }
- static int GameObjectGetTransform(int thisHandle)
- {
- GameObject go = (GameObject)ObjectStore.Get(thisHandle);
- Transform transform = go.transform;
- return ObjectStore.Store(transform);
- }
- static void TransformSetPosition(int handle, Vector3 position)
- {
- Transform t =(Transform)ObjectStore.Get(handle);
- t.position = position;
- }
- }
- c++ code part:
- #ifdef _WIN32
- #define DLLEXPORT __declspec(dllexport)
- #else
- #define DLLEXPORT
- #endif
- extern "C"
- {
- //C# VECTOR STRUCT
- struct Vector3
- {
- float x;
- float y;
- float z;
- }
- //c# function for c++ to call
- int(*GameObjectNew)();
- int(*GameObjectGetTransform)(int thisHandle);
- void(*TransformSetPosition)(int thisHandle, Vector3 position);
- //c++ functions for c# to call
- int numCreated;
- DLLExport void Init(
- int(*gameObjectNew)(),
- int(*gameObjectGetTrasform)(int),
- void(*transformSetPosition)(int, Vector3)
- )
- {
- GameObjectNew = gameObjectNew;
- GameObjectGetTransform = gameObjectGetTransform;
- TransformSetPosition = trasformSetPosition;
- numCreated = 0;
- }
- //
- DLLEXPORT void MonoBehaviourUpdate(int thisHandle)
- {
- if( numCreated < 10)
- {
- // 获取函数 handle, 然后操作
- int goHandle = GameObjectNew();
- int transformHandle = GameObejctGetTransform(goHandle);
- float comp = 10.0f * (float)numCreated;
- Vector3 position = {comp, comp, comp};
- TransformSetPosition(transformHandle, position);
- numCreated++;
- }
- }
- }
四, 总结
C# 和 C++ 的相互交互, 是基于. NET 和 P/Invoke, 那么我们可以同理退出 c# 和 lua 的操作, 其实质就是对 handle 进行包装, 然后进行相关的操作, 这个在后续的文章中在研究, 先写到这儿, 祝大家五一快乐, 我也回家过节去了, 哈哈~
来源: https://www.cnblogs.com/zblade/p/8961446.html