P/Invode Unity调用C++库


Unity开发中,我们不免要调用C++库,P/InvodePlatform Invocation Services)则为我们提供这个桥梁。


🧠 什么是 P/Invoke?

P/Invoke 全称是:

Platform Invocation Services

它是 .NET/Mono 平台用于从托管代码(如 C#)中调用非托管代码(如 C/C++ DLL)的机制。


🔧 你可以用它做什么?

✅ 从 C# 调用 C/C++ 函数
✅ 调用 Windows API自己写的 .dll
✅ Unity 中调用 OpenCV、CUDA、点云算法等 C++ 模块


✅ 典型 P/Invoke 示例(Unity 或普通 C#)

🎯 假设你有一个 C++ 函数:

native.cpp

 
 
 
xxxxxxxxxx
 
 
 
 
extern "C" {
    __declspec(dllexport) int Add(int a, int b) {
        return a + b;
    }
}
 

🔁 C# 侧调用:

 
 
 
xxxxxxxxxx
 
 
 
 
using System.Runtime.InteropServices;

public class NativeLib
{
    [DllImport("YourLibraryName", CallingConvention = CallingConvention.Cdecl)]
    public static extern int Add(int a, int b);
}
 

注意:YourLibraryName 不要带 .dll 后缀,Unity 会自动找 YourLibraryName.dll


📦 常用属性解释

属性示例含义
DllImport("xxx") 指定调用的 DLL 名称  
CallingConvention Cdecl / StdCall / FastCall 必须匹配 C++ 函数
extern 表示函数定义来自外部  
__declspec(dllexport) 在 C++ 里导出函数  

🧠 重要规则

C++ 类型C# 类型对应
int int
float float
double double
char* stringIntPtr
void* IntPtr
struct 需要用 [StructLayout] 显式映射
C++ 类型C# 类型说明
const char* string 默认 ANSI 字符串
const wchar_t* string + CharSet.Unicode Unicode 字符串
char*(可写) StringBuilder 可写缓冲区,用于输出字符串
char[] byte[] 传入字节数组(手动处理)
C++ 类型C# 类型说明
int* IntPtr / ref int 原始指针
float* float[] / IntPtr 数组
void* IntPtr 通用指针
T*(结构体) ref struct / IntPtr 通常用 refout

⚛常用MarshalAs标记

用法说明
LPStr C 的 char*
LPWStr 宽字符 wchar_t*
I1 bool 映射为 1 字节
ByValArray 结构体中数组固定大小
⚠️std::string 没有对应的C#, 因此C++ 端需要将其先选换成如char* 类型  

✅ 总结推荐使用

场景推荐写法
数值、float、int 直接用 C# 对应类型
字符串传入 string + MarshalAs
可写字符串 StringBuilder
结构体传值 [StructLayout] + ref
指针 IntPtr / unsafe 指针
C 数组传入 T[] / IntPtr + Marshal.Copy

⚠️ 注意事项

问题原因解决方式
找不到 DLL 路径或平台不一致 放到 Assets/Plugins/x86_64/
类型不匹配 参数/返回值定义不一致 确保函数签名匹配
字符串乱码 编码不同 Encoding.UTF8 编解码或 IntPtr 传值

🧪 实战例子(带字符串)

C++ 侧:

 
 
 
xxxxxxxxxx
 
 
 
 
extern "C" {
    __declspec(dllexport) void Hello(const char* name)
    {
        printf("Hello %s\\n", name);
    }
}
 

C# 侧:

 
 
 
xxxxxxxxxx
 
 
 
 
[DllImport("NativeLib", CallingConvention = CallingConvention.Cdecl)]
public static extern void Hello([MarshalAs(UnmanagedType.LPStr)] string name);
 

🧪 实战例子(结构体)

C++侧:

 
 
 
xxxxxxxxxx
 
 
 
 
extern "C" MYDLL_API float pixelToLength(const myPointf topPoint, const myPointf lowerPoint);
 

C#侧

 
 
 
xxxxxxxxxx
 
 
 
[StructLayout(LayoutKind.Sequential)]
public struct myPointf
{
    public float x;
    public float y;
}

[DllImport("YourDllName", CallingConvention = CallingConvention.Cdecl)]
public static extern float pixelToLength(myPointf topPoint, myPointf lowerPoint);


myPointf top = new myPointf { x = 100.5f, y = 200.0f };
myPointf bottom = new myPointf { x = 100.5f, y = 500.0f };

float length = pixelToLength(top, bottom);
Debug.Log($"像素点距离 = {length}");

 

方便的工具

Dependencies GUI

确认依赖项是否缺失(依赖链未加载)

举例:你的 mydll.dll 引用了 opencv_world.dll,但你没把它一并放进去 → 也会报找不到 mydll

✅ 用工具查看依赖链:

  • 👉 推荐工具:Dependencies GUI(查看 DLL 依赖)
  • ✅ 确保所有依赖都在同目录或系统 PATH 可访问

ClangSharp P/Invoke Generator(微软官方)

支持从 .h 头文件自动生成 C# P/Invoke 绑定,精度高、现代、安全

  • ✅ 微软官方维护,基于 LLVM Clang
  • ✅ 支持复杂结构体、指针、函数导出
  • ✅ 可配置输出为 C# 代码、生成整套接口
  • 支持 Windows、Linux、macOS
  • 推荐用于企业级封装

SWIG(Simplified Wrapper and Interface Generator)**

支持将 C/C++ 接口导出为多种语言绑定,包括 C#

  • ✅ 支持类、指针、继承、模板
  • ✅ 自动生成 .cs.dll 封装
  • ❗生成代码有学习曲线,适合复杂接口工程

📌 总结一句话:

P/Invoke 是 C# / Unity 调用本地 C/C++ 函数的桥梁,掌握它可以让你把 C++ 算法库“无缝”接入到 C# 项目中。


 
posted @ 2025-04-20 22:16  世纪末の魔术师  阅读(246)  评论(0)    收藏  举报