C#怎么调用C++写的DLL_C#如何使用DllImport导入外部函数【教程】

2次阅读

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

C#怎么调用C++写的DLL_C#如何使用DllImport导入外部函数【教程】

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.AllocMarshal.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],而是两边对“同一块内存”的理解是否真的完全一致——类型、生命周期、所有权、线程安全,漏一个点,调试起来就全是玄学。

text=ZqhQzanResources