Note 02 of 06: 笔记 02 / 06:Qt Windows Runtime DependenciesQt 程序的 Windows 运行依赖

Tracing a packaged Qt application through DLL loading, platform plugins and a clean-machine check.从缺少 DLL 的报错出发,研究 Qt 程序的动态依赖、平台插件与换机验证。

Working on Seamly2D at Nanjing Turing, I ran windeployqt to collect the dependencies, and the program still started up complaining about a missing xerces-c_3_2.dll. With a folder already carpeted in DLLs, one more missing file made me wonder whether all that packaging had been for nothing. Taking the program apart from startup to opening a pattern explains how both can be true at once.

在南京图灵做 Seamly2D 时,windeployqt 收集完依赖,程序一启动还是报缺 xerces-c_3_2.dll。目录里已经铺了一大片 DLL,又冒出来一个缺的,难免让人怀疑刚才那一通打包是不是白干了。把程序从启动到打开纸样的过程拆开看,就能明白这两件事为什么能同时发生。

Dynamic Linking and Runtime Dependencies动态链接与运行依赖

At compile time, headers tell the compiler which types and functions exist; at link time, import libraries help the linker resolve external symbols. With dynamic linking, the implementations still have to be loaded from DLLs at runtime. A development PC already has Qt, a compiler and all sorts of libraries installed, so the program may quietly find its dependencies in that environment. Only on another computer does the part the environment had been silently supplying come to light.

编译时,头文件告诉编译器有哪些类型和函数;链接时,导入库帮链接器处理外部符号。用了动态链接,这些实现到运行时还得从 DLL 里加载。开发电脑上 Qt、编译器、各种库都装好了,程序很可能顺手就从这些环境里找到了依赖。一换电脑,之前被环境悄悄补上的那部分才露出来。

Tracing down from the executable uncovers the runtime dependencies layer by layer. It depends directly on one library, which may in turn depend on others, and some components load only once a particular feature is actually called. A same-named DLL in the folder is therefore not enough; architecture, version and where it is really loaded from all need checking.

顺着可执行文件往下查,运行依赖能一层层找出来。它直接依赖一个库,那个库又可能依赖别的库;还有些组件要等某个功能真被调用了才加载。所以光看文件夹里有没有同名 DLL 不够,还得查架构、版本,以及它实际是从哪儿被加载的。

Take this MSVC x64 build: an x86 DLL simply cannot be loaded into an x64 process. MSVC versions are more involved than their folder names suggest. Microsoft's binary compatibility notes allow compatibility between certain versions, with constraints on the linker, runtime version and some compiler options. Two different toolset names call for checking the exact combination before writing it off.

拿这次的 MSVC x64 构建来说,x86 的 DLL 是装不进 x64 进程的。MSVC 的版本就比目录名复杂了。微软的二进制兼容说明允许特定版本之间兼容,同时对链接工具、运行库版本和部分编译选项有约束。所以看到两个工具集名字不一样,先核对具体组合,别急着判死刑。

Qt PluginsQt 插件

Qt talks to the window system through a platform plugin, and on Windows the deployment folder usually needs platforms/qwindows.dll. It is a DLL, yet it has its own discovery and loading process, and dropping it next to the executable may not put it in the right place. Qt's plugin deployment guide also lists the version and build-mode requirements between plugins and Qt libraries, and provides the QT_DEBUG_PLUGINS diagnostic switch.

Qt 靠平台插件和窗口系统打交道,在 Windows 上,部署目录里通常要有 platforms/qwindows.dll。它虽然也是 DLL,却有自己的一套发现和加载流程,随手扔到可执行文件旁边,未必放对了地方。Qt 插件部署说明还列了插件和 Qt 库之间的版本、构建模式要求,并给了 QT_DEBUG_PLUGINS 这个诊断开关。

So a platform-plugin error first needs sorting into one of two cases: the plugin was never found, or it was found and failed to load. The second may come from another library the plugin itself needs, or from a mismatched version or build mode. Both can produce errors naming the same plugin, and they are fixed in completely different ways. The search paths and failure reasons in the diagnostic output help far more than piling still more DLLs into the folder.

所以碰到平台插件报错,先分清是插件压根没找到,还是找到了却加载失败。后一种可能是插件自己依赖的另一个库缺了,也可能是版本或构建模式对不上。两种情况的报错都可能提到同一个插件名,修法却完全不同。调试输出里的搜索路径和失败原因,比往目录里接着堆 DLL 有用得多。

It resembles checking an interface in embedded work. Finding the connector only tells you where the connector is; whether communication actually works means following the link further. Here, what has to be followed is which file loads which.

这跟嵌入式里查接口有点像。找到连接器,只知道连接器在哪儿;通信到底通没通,还得顺着链路往下查。只不过这里要追的,是文件之间谁加载谁。

Third-Party Libraries第三方依赖

xerces-c_3_2.dll is an XML parsing library. Qt's Windows deployment guide is explicit that windeployqt gathers Qt's own dependencies and leaves additional third-party libraries to the developer. A deployment folder with every Qt library present and Xerces missing is therefore entirely possible.

xerces-c_3_2.dll 是 XML 解析库。Qt 的 Windows 部署文档写得很明白,windeployqt 只负责收集 Qt 相关的依赖,额外的第三方库得自己处理。所以部署目录里 Qt 的库一个不少、Xerces 却缺着,这两件事完全可以同时成立。

Microsoft's DUMPBIN is a good first tool for inspecting the binaries: /DEPENDENTS lists imports and /HEADERS shows architecture. It suits following static import relationships downward; files brought in at runtime through plugins or explicit loading still have to be found by actually starting the program and using its features.

排查时可以先拿微软的 DUMPBIN 看二进制文件,/DEPENDENTS 查导入依赖,/HEADERS 查架构信息。它适合顺着静态导入关系往下找;运行中通过插件或显式加载才引入的文件,还得结合实际启动和功能操作来查。

A corrected package can be assembled in a brand-new folder, with dependencies filled in from the Qt installation matching this build and other known sources, then tested in an environment without Qt or Visual Studio so the development tools' search paths stop covering for it. The step-by-step build and installation live in the original packaging record; the concern here is which outside files the program really borrows.

重新整理部署包时,可以把修好的程序放进一个全新的目录,再从这次构建对应的 Qt 安装和已知来源把依赖补齐,然后拿到没有 Qt 和 Visual Studio 的环境里测,免得开发工具的搜索路径继续替它打掩护。具体的构建和安装步骤在原始打包记录里,这里更关心的是程序到底借用了哪些外部文件。

Deployment Testing on Another Computer换机部署测试

After the internship I ran the installer on a freshly installed Windows desktop at home, and registration, login and role operations all worked. Following the dependency thread, the check can go one step further: open an .sm2d pattern with its associated .smis measurements, save it and open it again. Only once the window is up do XML parsing and file access actually get exercised.

实习结束后,我在家里新装的 Windows 台式机上跑过安装包,注册、登录和角色操作都能用。顺着依赖这条线,检查还能再往前推一步:打开 .sm2d 纸样和它关联的 .smis 量体文件,保存,再重新打开。窗口出来以后,XML 解析和文件读写才真正派上用场。

ActionWhat to observe
Launch from the installation directoryLoading of Qt libraries, the platform plugin and runtime
Register, exit and log in againAccount-state recovery in a new process
Open a pattern and measurement fileXML parsing and associated file paths
Save and reopenThe connection between writing and reading data
Use the same installer on another machineDependence on the development environment
操作重点观察
从安装目录启动Qt 库、平台插件与运行库是否能被加载
注册后退出,再重新登录账户状态能否在新的进程中恢复
打开纸样和量体文件XML 解析、关联文件路径是否正常
保存并重新打开写入路径和数据读取能否衔接
用同一安装包换机结果是否依赖开发电脑上的环境

Checked this way, the finish line for packaging becomes concrete. What goes to someone else has to be a program that finds its own dependencies, opens real files and saves its results. Every real use case verified before handoff is one less hole left for a user to fall into.

这么一套查下来,打包的完成标准也具体了。交到别人手里的,得是一套自己能找到依赖、能打开真实文件、能把结果存下来的程序。交接前多验一个真实的使用动作,就少留一个要等用户撞上才发现的坑。

06Back to the project回到项目Seamly2D Qt Development Internship南京图灵 Seamly2D 开发实习