dllimport 最常见错误是调用约定不匹配:c# 默认 stdcall 而多数 c++ dll 用 cdecl,须显式指定 callingconvention.cdecl;导出名需匹配(避免名字修饰);字符串、结构体(需 sequential 布局)、回调(需静态委托+固定地址)等均须严格对齐类型与内存布局。

DllImport 基本写法错在哪?
最常见的错误是直接照抄 C++ 函数签名,不加修饰或忽略调用约定。C# 默认用 StdCall,而多数 C++ DLL(尤其 Visual Studio 默认生成的)用 Cdecl,结果一调就抛 System.EntryPointNotFoundException 或堆栈被破坏。
- 必须显式指定
CallingConvention = CallingConvention.Cdecl,除非你确认 C++ 侧用了__stdcall -
EntryPoint要么填导出函数的原始符号名(如"Add"),要么用extern "C" __declspec(dllexport)导出,否则 C++ 编译器会名字修饰(mangling),C# 找不到 - 字符串参数默认按
UnmanagedType.LPStr处理,若 C++ 传的是宽字符(wchar_t*),得加[MarshalAs(UnmanagedType.LPWStr)]
struct 传参崩溃?内存布局没对齐
C# 的 struct 默认是自动布局(LayoutKind.Auto),而 C++ 是顺序/显式布局。两者字段偏移不一致,传过去就是乱码或访问违规。
- 所有要跨语言传递的结构体,必须加
[StructLayout(LayoutKind.Sequential)] - 含指针或数组成员时,用
[MarshalAs]明确长度,比如[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] - 注意字节对齐:C++ 里若有
#pragma pack(1),C# 也得加Pack = 1
回调函数怎么传进 C++ DLL?
C++ DLL 若接受函数指针作为参数(比如注册日志回调),C# 不能直接传 lambda 或实例方法——它们不是固定地址,且可能被 GC 移动。
- 必须用静态方法 +
delegate类型,并用GCHandle.Alloc或Marshal.GetFunctionPointerForDelegate固定住 - 委托声明要和 C++ 函数指针签名严格一致,包括调用约定:
public delegate int LogCallback([MarshalAs(UnmanagedType.LPCStr)] string msg); - 别忘了在 DLL 卸载前释放委托引用,否则内存泄漏
为什么 Release 下能跑,Debug 就崩?
Debug 版常启用了运行时检查(如 /RTC),而 C++ DLL 可能返回了未初始化内存、或 C# 传了 null 指针但没做校验,Debug CRT 会直接触发断言。
立即学习“C++免费学习笔记(深入)”;
- 在 P/Invoke 方法上加
[DllImport(..., SetLastError = true)],出错后立刻查Marshal.GetLastWin32Error() - C++ 侧导出函数开头加空指针检查,别让 C# 传 null 进来就解引用
- Release 模式下优化可能掩盖越界读写,用 Application Verifier 或 AddressSanitizer 测 DLL 更可靠
最麻烦的从来不是怎么写第一行 [DllImport],而是两边对“同一块内存”的理解是否真的完全一致——类型、生命周期、所有权、线程安全,漏一个点,调试起来就全是玄学。










