错误。go调用windows dll既可用syscall动态加载,也可用cgo链接导入库(.lib)实现静态绑定;cgo支持dll调用,前提是提供对应导入库并正确配置#cg ldflags,非仅限于静态库。

Go 调用 Windows DLL 必须用 syscall,cgo 不行
Go 原生不支持直接加载和调用 DLL 中的导出函数,cgo 也不能用于封装 DLL 调用——它只对接 C 静态/动态库(且需编译时链接),而 DLL 是运行时按需加载的。真正可行路径只有 syscall 包提供的 LoadDLL、FindProc 和 Call 三件套。
常见错误是试图写 #include "mylib.dll" 或用 //export 暴露 Go 函数给 DLL 调用,这完全走反了方向;也有人误以为 plugin 包能加载 DLL,但它只支持 Linux 的 .so 和 macOS 的 .dylib,Windows 下无效。
- 必须用
syscall.LoadDLL("xxx.dll")加载,路径需为绝对路径或在PATH中 - 函数名必须与 DLL 导出表中一致(注意是否带下划线前缀或 @ 后缀,取决于调用约定)
- 所有参数和返回值类型要严格匹配 C ABI,Go 的
int在 64 位 Windows 下是 64 位,但多数 Win32 API 期望 32 位int,得用int32或uintptr
正确处理调用约定和函数签名:__stdcall 是默认,别硬套 cdecl
Windows 系统 DLL(尤其是 Win32 API)绝大多数使用 __stdcall,其特征是函数名在导出表中常带 @N 后缀(如 MessageBoxA@16),且由被调用方清理栈。而 __cdecl(C 默认)由调用方清栈,名字无修饰。如果用 FindProc 找不到函数,大概率是名字没对上,或调用约定不匹配导致栈失衡、程序崩溃。
例如调用 user32.dll 的 MessageBoxA:
dll := syscall.MustLoadDLL("user32.dll")
proc := dll.MustFindProc("MessageBoxA") // 注意不是 MessageBoxA@16,syscall 会自动处理
ret, _, _ := proc.Call(0, uintptr(unsafe.Pointer(&title[0])), uintptr(unsafe.Pointer(&text[0])), 0)
- 不要手动拼
@16,MustFindProc内部已按__stdcall规则查找 - 字符串必须转为
UTF16PtrFromString,不能直接传 Go 字符串底层数组 - 指针参数一律用
uintptr(unsafe.Pointer(...)),不能漏uintptr转换 - 若 DLL 是自研 C/C++ 编译的,导出函数需显式加
__declspec(dllexport)和WINAPI(即__stdcall)
资源泄漏和并发调用风险:DLL 和 Proc 必须显式释放
syscall.LoadDLL 返回的 *syscall.DLL 是引用计数对象,不调用 dll.Release() 会导致句柄泄漏,多次重复 LoadDLL 同一 DLL 也不会复用已加载实例——每次都是新句柄。更危险的是,*syscall.Proc 不是线程安全的,多个 goroutine 并发调用同一 proc.Call() 可能引发栈混乱或 panic。
- 每个 DLL 加载后,应在合适时机(如
defer dll.Release())释放 - 避免全局复用
*syscall.Proc,高并发场景建议每次调用前FindProc(开销极小),或用sync.Pool缓存Proc实例 - 若 DLL 本身非线程安全(比如内部用静态缓冲区),需自行加互斥锁,syscall 层不提供保护
跨平台兼容性差,别把它当通用插件方案
这套机制仅限 Windows,代码里出现 syscall.LoadDLL 就彻底失去跨平台能力。有人想“先判断 OS 再分支调用”,但 DLL 文件格式、符号命名、调用约定、字符编码(ANSI vs UTF-16)全都不兼容其他系统,无法抽象出统一接口。
如果你的真实需求是“让 Go 程序支持用户扩展模块”,优先考虑:
- 用
plugin+.so(Linux/macOS)+ 运行时进程通信(Windows 上起子进程跑 Go 插件) - 定义 JSON/Protocol Buffers 接口,通过 stdin/stdout 或本地 socket 通信
- 用 WebAssembly(WASI)加载沙箱化逻辑,Windows/Linux/macOS 全支持
硬啃 DLL 调用只适用于必须对接遗留 Windows 组件(如硬件 SDK、银行控件、OA 插件)的场景,且要接受调试困难、错误信息模糊、栈跟踪失效等代价。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











