通过p/invoke调用c++ dll需严格匹配函数签名、callingconvention(多为cdecl)、charset(推荐显式指定unicode或lpstr)、平台位数(x64/x86一致),结构体须用[structlayout(sequential, pack=1)],字符串避免隐式封送,dll路径须正确且异常无法透传。

直接调用 C++ DLL 是可行的,但绝大多数失败不是因为语法写错,而是卡在函数签名不匹配、调用约定不对、字符串封送失效或位数/平台不一致上。
DllImport 声明必须显式指定 CallingConvention
Windows 上多数 C++ DLL 默认用 __cdecl,而 [DllImport] 默认是 StdCall。不匹配会导致栈失衡、程序崩溃或返回垃圾值。
- 用
extern "C" __declspec(dllexport)导出的 C 风格函数,几乎都该配CallingConvention = CallingConvention.Cdecl - Win32 API 函数(如
MessageBoxA)才用StdCall,别凭感觉猜 - 如果不确定,用
dumpbin /exports NativeLib.dll查看导出符号名:带@n后缀的是StdCall,无后缀的是Cdecl
字符串参数默认封送为 ANSI,64 位下容易崩
string 传给 const char* 时,.NET 默认用 CharSet.Ansi 封送——这在中文 Windows 上会把 UTF-16 字符串截断成乱码,且 .NET 5+ 在 64 位进程里对 ANSI 封送更敏感,常触发访问冲突。
- 强制指定
CharSet = CharSet.Unicode,并在 C++ 侧改用const wchar_t* - 或保持 ANSI,但 C# 端加
[MarshalAs(UnmanagedType.LPStr)]显式控制,避免隐式行为 - 绝不要在未声明
CharSet的情况下传中文字符串
DLL 路径和位数必须严格匹配
运行时报 System.DllNotFoundException 或 BadImageFormatException,90% 是路径或平台问题。
-
[DllImport("NativeLib.dll")]只查当前目录、PATH环境变量路径、以及 .NET 的NativeLibrary搜索路径(.NET 5+),不查项目根目录或子文件夹 - 调试时把 DLL 放进
bin\Debug或bin\Release,不是obj目录 - C++ 工程必须设为 x64 或 x86(不能 AnyCPU),C# 项目
Platform Target必须完全一致;AnyCPU + Prefer32 仍可能在 64 位系统加载失败
结构体传参要手动控制封送和内存布局
结构体跨边界时,C# 默认按 .NET 规则排布字段,而 C++ 按编译器规则(可能有填充、对齐差异),直接传会读错字段或越界。
- 所有参与 P/Invoke 的结构体必须加
[StructLayout(LayoutKind.Sequential, Pack = 1)],Pack = 1消除填充干扰(除非你明确知道 C++ 端的#pragma pack值) - 字符串成员不能直接用
string,得用[MarshalAs(UnmanagedType.LPStr)] public IntPtr name;,再手动Marshal.PtrToStringAnsi() - 数组要用
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] public int[] data;,不能用int[]自动推导
最常被忽略的一点:P/Invoke 不做异常透传。C++ 抛的 std::exception 会直接终止进程,不会变成 SEHException 或其他托管异常——所有错误必须靠返回码、输出参数或 C++ 层主动调用回调来暴露。










