两种加载模式回顾
-
隐式加载(h + import‑lib + dll,普通Qt共享库 Q_DECL_EXPORT):程序启动阶段操作系统自动加载DLL
-
显式加载(LoadLibrary / QPluginLoader,Qt插件模式:运行时手动加载DLL)
下面分开梳理工控项目最常踩的报错,报错表现、根因、排查方向,贴合 VS2019 + Qt + Windows。
一、隐式加载(启动自动加载dll)常见错误
特点:程序还没有进入main函数就报错。弹窗:无法启动此程序,因为计算机中丢失xxx.dll
1)DLL找不到(最常见)
现象:exe双击打不开,弹窗缺少xxx.dll,程序直接起不来。
原因:Windows DLL搜索路径找不到依赖dll。注意:不是exe同级目录就一定能找到,搜索路径有固定优先级;Qt程序经常漏带Qt5Core.dll、平台插件qwindows.dll。
⚠️关键点:隐式加载发生在main之前,你自己写的日志根本打印不出来,很难排查。
工控对策:发布时私有部署,所有依赖dll放在exe同级目录,使用windeployqt收集依赖。
2)DLL找到了,但是加载失败(依赖链里面的子DLL缺失)
这是新手最容易踩的坑:主DLL在,但是主DLL依赖的另外一个dll丢了。Windows只会提示"无法加载xxx.dll",不会告诉你缺了哪个下级依赖。
排查工具:Dependencies(依赖查看器),看哪一个依赖缺失。
举例:你的SDK.dll存在,SDK.dll依赖fast‑dds.dll,fast‑dds.dll缺失。Windows报错找不到SDK.dll,极具迷惑性。
3)ABI版本不匹配,DLL加载失败
现象:dll文件齐全,但是启动直接失败。
案例:exe是Release编译,dll是Debug编译;或者VS版本不一致,CRT版本不匹配。
Windows弹窗:应用程序无法正常启动(0xc000007b),这个码是工控最高频错误码。
0xc000007b:32位/64位混用,或者CRT版本冲突,ABI不匹配。比如exe是x64,dll是x86。
4)符号找不到(隐式链接期错误,编译阶段就报错,不是运行期)
LNK2019 unresolved external symbol。链接阶段找不到符号,程序根本生成不出来。
原因:头文件声明了函数,但是没有链接对应import‑lib;或者lib版本不对。
隐式加载最大痛点:全部依赖在程序启动一次性加载,一个DLL挂掉,整个程序无法启动。工控软件非常怕这个问题。
二、显式加载 QPluginLoader / LoadLibrary(插件模式)常见错误
特点:程序已经跑起来了,扫描插件目录,加载某个插件的时候失败,主程序可以继续运行,这个也是插件架构最大价值。
1、LoadLibrary返回NULL,dll加载失败
GetLastError()查看错误码,非常关键!
- Error 126:找不到指定模块
DLL文件明明在文件夹里面!依然报126。
90%不是插件dll本身丢了,是插件dll依赖的子dll缺失。
举例子:EntityPlugin.dll在目录,但是插件依赖fast‑dds.dll缺失 → LoadLibrary(EntityPlugin.dll)返回NULL,错误码126。这个坑在Qt插件项目里面出现频率最高,很多人误以为是插件文件不在。
- Error 193 %1不是有效的Win32应用程序
位数不对:宿主64位,插件编译成32位。
- Error 5:拒绝访问
dll被占用(已经加载,没有卸载),或者权限不足;杀毒软件隔离dll,工控机杀毒软件经常把插件dll隔离。
2、DLL加载成功,但是找不到工厂函数(Qt插件高频坑)
LoadLibrary成功,但是GetProcAddress("qt_plugin_instance")返回NULL
原因分三类:
-
忘记写Q_PLUGIN_METADATA宏!moc没有生成qt_plugin_instance这个extern‑C工厂函数。这是写Qt插件第一大坑;
-
插件项目没有开启MOC,CMakeLists里面没有开启AUTOMOC,元对象代码没生成;
-
极少数情况:宏写错IID字符串。
区分一下:dll加载失败 vs dll加载成功但是拿不到工厂函数,是两个完全不同的错误,很多人混在一起。
3、拿到接口指针,调用虚函数直接崩溃(Qt插件头号运行期bug,ABI问题)
✅LoadLibrary成功
✅拿到插件接口指针
❌调用第一个虚函数直接Access Violation崩溃。
根本原因:ABI不一致!
宿主:Qt5.15 VS2019 release;插件:Qt5.15 VS2022 release。头文件(API)完全一样。虚函数表布局不一致。宿主按照自己理解的偏移量调用虚函数,访问非法内存。
这就是Qt原生插件最大软肋。编译通过,dll加载成功,运行调用虚函数崩溃,最难排查。
4、插件卸载失败(QLibrary::unload返回false,内存泄漏)
Qt插件一旦创建出来QObject派生对象,Qt的对象树持有对象引用,引用没有释放,dll不能从内存卸载。工控做插件热插拔的时候经常踩坑。
很多人以为unload一定能卸载dll,实际上只要插件对象没有销毁,dll驻留在内存,dll文件被锁住,文件不能覆盖替换,升级插件失败。
5、插件内部new出来的对象,宿主delete,直接崩溃
不同CRT堆。跨dll边界不要传递拥有所有权的对象。这个也是ABI衍生的错误。
三、面试精简版(口述)
隐式加载主要错误集中在程序启动阶段:dll缺失,下级依赖dll缺失报0xc000007b,位数和CRT版本不匹配;缺点是一个依赖库异常整个程序启动失败。
显式插件加载:LoadLibrary报126错误大多是插件依赖的子库缺失;193是位数不匹配;加载成功拿不到工厂函数一般是漏写Q_PLUGIN_METADATA或者MOC没开;拿到接口调用虚函数崩溃是ABI不一致;插件无法卸载是插件对象引用未释放。