c# 如何调用c++的dll

来源:这里教程网 时间:2026-02-21 17:39:23 作者:

DllImport
声明函数,不是“引用”或“添加引用”

你不能像引用 .NET 程序集那样直接“添加引用”C++ DLL。C# 调用 C++ DLL 的核心机制是平台调用(P/Invoke),靠

DllImport
特性显式声明外部函数签名。如果直接把 C++ DLL 拖进项目或右键“添加引用”,会失败——它不是托管程序集。

关键前提:C++ DLL 必须导出 C 风格函数(即用

extern "C"
__declspec(dllexport)
),否则 C# 找不到符号(常见错误:
System.EntryPointNotFoundException
)。

确保 C++ 函数不带 C++ name mangling:在头文件中写
extern "C" __declspec(dllexport) int add(int a, int b);
编译为 x86 或 x64 时,C# 项目平台必须严格匹配(
AnyCPU
默认可能失败,建议显式设为
x64
x86
DLL 文件需放在运行时能被找到的位置:与 exe 同目录、
PATH
环境变量路径,或用
SetDllDirectory
指定

DllImport
的路径和调用约定不能写错

路径写相对路径(如

"MyNative.dll"
)比绝对路径更安全;调用约定(
CallingConvention
)必须和 C++ 导出函数一致,默认是
__cdecl
,但 Windows API 风格常用
__stdcall
。写错会导致栈失衡、崩溃或返回值异常(比如始终返回 0)。

常见组合:

立即学习“C++免费学习笔记(深入)”;

C++ 用
__declspec(dllexport) int func(...);
→ C# 用
CallingConvention = CallingConvention.Cdecl
(默认可省略)
C++ 用
__declspec(dllexport) int __stdcall func(...);
→ C# 必须显式写
CallingConvention = CallingConvention.StdCall
路径中不要写
.dll
后缀(
DllImport("MyNative")
即可,运行时自动补)

字符串、数组、结构体传参要特别小心内存布局

C# 和 C++ 对字符串、指针、结构体的默认处理完全不同。直接传

string
int[]
很容易引发访问冲突或乱码。

传字符串给 C++:C# 侧用
[MarshalAs(UnmanagedType.LPStr)]
[MarshalAs(UnmanagedType.LPWStr)]
明确编码,C++ 接收
const char*
const wchar_t*
传结构体:C# 结构体必须加
[StructLayout(LayoutKind.Sequential)]
,字段顺序、对齐(
Pack=1
)要和 C++ 一致;避免使用
string
字段,改用固定长度
char[256]
+
[MarshalAs]
输出缓冲区(如 C++ 写入 char*):C# 用
StringBuilder
并预先
.Capacity
,而非
string
[DllImport("MyNative.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetString([MarshalAs(UnmanagedType.LPStr)] StringBuilder buffer, int bufferSize);

调试时最常见的三个错误现象及定位方式

不是所有报错都提示“找不到 DLL”——很多崩溃发生在调用瞬间,没堆栈或只有

AccessViolationException

DllNotFoundException
:DLL 文件根本没找到。用
Process Monitor
过滤进程名,看它尝试加载了哪些路径
EntryPointNotFoundException
:函数名不匹配。用
dumpbin /exports MyNative.dll
查真实导出名(注意是否有前导下划线或 @ 后缀)
程序闪退 / 异常退出(无异常抛出):大概率是调用约定错、结构体布局错、或 C++ 释放了 C# 分配的内存(反之亦然)。启用非托管代码调试(项目属性 → 调试 → 勾选“启用本机代码调试”)

真正麻烦的从来不是“怎么写第一行

DllImport
”,而是让两边的数据解释方式严丝合缝。一个字节对齐差异、一个字符串编码误判,就足以让调用看起来“成功”却返回垃圾值。

相关推荐