Qt for Windows - Deployment

本文档描述了 Windows 的部署过程. 我们在整个文档中引用了 plug & paint 示例应用程序来演示部署过程.

The Windows Deployment Tool

Windows 部署工具 (windeployqt) 旨在自动创建可部署文件夹的过程, 其中包含从该文件夹运行应用程序所需的 Qt 相关依赖项 (库, QML 导入, 差人, 翻译). 它为通用 Windows 平台 (UWP) 创建沙箱或 Windows 桌面应用程序的安装树, 可以轻松地将其捆绑到安装包中.

这个工具可以在 QTDIR/bin/windeployqt 中找到. 它需要在构建环境中运行才能正常运行. 使用 Qt Installer 时, 应使用脚本 QTDIR/bin/qtenv2.bat 设置.

windeployqt.exe 文件的目录作为参数, 并扫描可执行文件的依赖项. 如果使用 --qmldir 参数传递目录, windeployqt 将使用 qmlimportscanner 工具扫描目录内的 QML 文件以获取 QML 导入依赖项。 然后将识别出的依赖项复制到可执行文件的目录中.

如果 Qt 是在配置开关 -relocatable 关闭的情况下构建的, windeployqt 将用相对路径替换 Qt5Core.dll 中的硬编码本地路径.

对于 Windows 桌面应用程序, 默认情况下编译器所需的运行时文件也会复制到可部署文件夹中 (除非指定了 --no-compiler-runtime 选项). 对于使用 Microsoft Visual C++ 的发行版本, 这些版本由 Visual C++ 可再发行组件包组成, 旨在由应用程序安装程序在目标计算机上递归安装. 否则, 将使用编译器运行时的共享库.

应用程序可能需要额外的第 3 方库 (例如数据库库), windeployqt 未考虑这些库.

工具的帮助输出中描述了其他参数:


  Usage: windeployqt [options] [files]
  Qt Deploy Tool 5.9.0

  The simplest way to use windeployqt is to add the bin directory of your Qt
  installation (e.g. <QT_DIR\bin>) to the PATH variable and then run:
    windeployqt <path-to-app-binary>
  If ICU, ANGLE, etc. are not in the bin directory, they need to be in the PATH
  variable. If your application uses Qt Quick, run:
    windeployqt --qmldir <path-to-app-qml-files> <path-to-app-binary>

  Options:
    -?, -h, --help            Displays this help.
    -v, --version             Displays version information.
    --dir <directory>         Use directory instead of binary directory.
    --libdir <path>           Copy libraries to path.
    --plugindir <path>        Copy plugins to path.
    --debug                   Assume debug binaries.
    --release                 Assume release binaries.
    --pdb                     Deploy .pdb files (MSVC).
    --force                   Force updating files.
    --dry-run                 Simulation mode. Behave normally, but do not
                              copy/update any files.
    --no-patchqt              Do not patch the Qt5Core library.
    --no-plugins              Skip plugin deployment.
    --no-libraries            Skip library deployment.
    --qmldir <directory>      Scan for QML-imports starting from directory.
    --no-quick-import         Skip deployment of Qt Quick imports.
    --no-translations         Skip deployment of translations.
    --no-system-d3d-compiler  Skip deployment of the system D3D compiler.
    --compiler-runtime        Deploy compiler runtime (Desktop only).
    --no-compiler-runtime     Do not deploy compiler runtime (Desktop only).
    --webkit2                 Deployment of WebKit2 (web process).
    --no-webkit2              Skip deployment of WebKit2.
    --json                    Print to stdout in JSON format.
    --angle                   Force deployment of ANGLE.
    --no-angle                Disable deployment of ANGLE.
    --no-opengl-sw            Do not deploy the software rasterizer library.
    --list <option>           Print only the names of the files copied.
                              Available options:
                               source:   absolute path of the source files
                               target:   absolute path of the target files
                               relative: paths of the target files, relative
                                         to the target directory
                               mapping:  outputs the source and the relative
                                         target, suitable for use within an
                                         Appx mapping file
    --verbose <level>         Verbose level.

  Qt libraries can be added by passing their name (-xml) or removed by passing
  the name prepended by --no- (--no-xml). Available libraries:
  bluetooth concurrent core declarative designer designercomponents
  enginio gui qthelp multimedia multimediawidgets multimediaquick network nfc
  opengl positioning printsupport qml qmltooling quick quickparticles quickwidgets
  script scripttools sensors serialport sql svg test webkit webkitwidgets
  websockets widgets winextras xml xmlpatterns webenginecore webengine
  webenginewidgets 3dcore 3drenderer 3dquick 3dquickrenderer 3dinput geoservices
  webchannel texttospeech serialbus

  Arguments:
    [files]                   Binaries or directory containing the binary.

Static Linking

要构建静态应用程序, 使用 -static 配置静态构建 Qt:


  cd C:\path\to\Qt
  configure -static <any other options you need>

如果稍后需要从同一位置重新配置和重建 Qt, 确保在再次运行 configure 之前进入构建目录并运行 nmake distcleanmingw32-make distclean 删除先前配置的所有痕迹.

Linking the Application to the Static Version of Qt

作为示例, 本节将静态构建 Plug & Paint 示例.

Qt 完成构建后, 构建 Plug & Paint 应用程序. 首先我们必须进入包含应用程序的目录:


  cd examples\tools\plugandpaint

运行 qmake 为应用程序创建一个新的 makefile, 并执行干净构建以创建静态链接的可执行文件:


  nmake clean
  qmake -config release
  nmake

你可能想要链接到发布库, 并且可以在调用 qmake 时指定这一点. 现在, 只要编译和链接的所有内容都没有任何错误, 我们就应该有一个可供部署的 plugandpaint.exe 文件. 要检查应用程序是否具有所需的库, 请将可执行文件复制到未安装 Qt 或任何 Qt 应用程序的计算机, 然后在该计算机上运行它.

记住, 如果你的应用程序依赖于编译器特定的库, 则这些库仍必须与你的应用程序一起重新分发. 你可以使用 depends 工具检查你的应用程序正在链接哪些库. 详见 Application Dependencies.

由于我们无法使用静态链接方法部署插件, 因此我们准备的应用程序是不完整的. 它将运行, 但由于缺少插件, 该功能将被禁用. 要部署基于插件的应用程序, 我们应该使用共享库方法.

Shared Libraries

使用共享库方法部署 Plug & Paint 应用程序时, 我们面临两个挑战: Qt 运行时必须与应用程序可执行文件一起正确重新分发, 并且插件必须安装在目标系统上的正确位置, 以便应用程序能够正常运行, 可以找到他们.

Building Qt as a Shared Library

对于此示例, 我们假设 Qt 作为共享库安装, 这是安装 Qt 时的默认设置, 位于 C:\path\to\Qt 目录.

Linking the Application to Qt as a Shared Library

确保 Qt 构建为共享库后, 我们可以构建 Plug & Paint 应用程序. 首先, 我们必须进入包含应用程序的目录:


  cd examples\tools\plugandpaint

现在, 运行 qmake 为应用程序创建一个新的 makefile, 并执行干净构建以创建动态链接的可执行文件:


  nmake clean
  qmake -config release
  nmake

以下将构建插件:


  cd ..\plugandpaint/plugins
  nmake clean
  qmake -config release
  nmake

如果所有内容都编译和链接没有任何错误, 我们将获得 plugandpaint.exe 可执行文件, pnp_basictools.dllpnp_extrafilters.dll 插件文件.

Creating the Application Package

要部署应用程序, 我们必须确保将相关的 Qt DLL (对应于应用程序中使用的 Qt 模块) 和 Windows 平台插件 qwindows.dll, 及可执行文件复制到同一目录中.

与用户插件相反, Qt 插件必须放入与插件类型匹配的子目录中. 平台插件的正确位置是名为 platforms 的子目录. Qt Plugins 提供了有关插件以及 Qt 如何搜索它们的附加信息.

如果使用动态 OpenGL, 你还需要包含 ANGLE 和软件渲染所需的库. 对于 ANGLE, 需要 Qt 的 lib 目录中的 libEGL.dll 和 libGLESv2.dll 以及 DirectX 中的 HLSL 编译器. HLSL 编译器库 d3dcompiler_XX.dll, 其中 XX 是 ANGLE (libGLESv2) 链接的版本号.

如果 Qt 配置为链接到 ICU 或 OpenSSL, 则还需要将相应的 DLL 添加到发布文件夹中.

注意: Qt WebEngine 应用程序有 部署 Qt WebEngine 应用程序 中列出的要求.

记住, 如果你的应用程序依赖于编译器特定的库, 则这些库必须与你的应用程序一起重新分发. 你可以使用 depends 工具检查你的应用程序正在链接哪些库. 详见 Application Dependencies.

我们将很快介绍这些插件, 但首先我们将检查应用程序是否可以在已部署的环境中运行: 将可执行文件和 Qt DLL 复制到未安装 Qt 或任何 Qt 应用程序的计算机, 或者如果你想要在构建机器上测试, 确保该机器的环境中没有 Qt.

如果应用程序启动没有任何问题, 那么我们就成功制作了 Plug & Paint 应用程序的动态链接版本. 但由于我们尚未部署关联的插件, 因此应用程序的功能仍然会丢失.

插件的工作方式与普通 DLL 不同, 因此我们不能像使用 Qt DLL 那样将它们复制到与应用程序可执行文件相同的目录中. 查找插件时, 应用程序会在应用程序可执行文件目录内的 plugins 子目录中搜索.

因此, 为了使插件可供我们的应用程序使用, 我们必须创建 plugins 子目录并复制相关的 DLL:


  plugins\pnp_basictools.dll
  plugins\pnp_extrafilters.dll

发布运行 Plug & Paint 应用程序所需的所有 Qt DLL 和应用程序特定插件的存档必须包含以下文件:

ComponentFile Name
The executableplugandpaint.exe
The Basic Tools pluginplugins\pnp_basictools.dll
The ExtraFilters pluginplugins\pnp_extrafilters.dll
The Qt Windows platform pluginplatforms\qwindows.dll
The Qt Core moduleQt5Core.dll
The Qt GUI moduleQt5Gui.dll
The Qt Widgets moduleQt5Widgets.dll

根据应用程序使用的功能 (图标引擎, 图像格式), 可能需要其他插件.

此外, 存档必须包含以下编译器特定库 (假设 Visual Studio 14.0 (2015) 或 15.0 (2017) 或 16.0 (2019)):

VC++ 12.0 (2013)VC++ 14.0 (2015)
The C run-timemsvcr120.dllvccorlib140.dll, vcruntime140.dll
The C++ run-timemsvcp120.dllmsvcp140.dll

如果 Qt 使用 ICU, 发布包必须包含:

File Name
icudtXX.dllicuinXX.dllicuucXX.dll

最后, 如果使用 ANGLE, 发布包必须包含:

File Name
QtANGLE.dlld3dcompiler_XX.dll

要验证应用程序现在是否可以成功部署, 你可以在没有 Qt 且没有安装任何编译器的计算机上提取此存档, 并尝试运行它.

将插件放在插件子目录中的另一种方法是在调用 QCoreApplication::addLibraryPath() 或 QCoreApplication::setLibraryPaths() 启动应用程序时添加自定义搜索路径.


  QCoreApplication::addLibraryPath("C:/some/other/path");

使用插件的一个好处是可以轻松地将它们提供给整个应用程序系列.

在创建 QApplication 对象之后, 在应用程序的 main() 函数中添加路径通常是最方便的. 添加路径后, 应用程序除了在所在目录的 plugins 子目录中查找之外, 还将在其中搜索插件. 可以添加任意数量的附加路径.

Manifest files

部署使用 Visual Studio 编译的应用程序时, 需要执行一些额外的步骤.

首先, 我们需要复制链接应用程序时创建的清单文件. 此清单文件包含有关应用程序对并行程序集 (例如运行时库) 的依赖关系的信息.

清单文件需要复制到与应用程序可执行文件相同的文件夹中. 你不需要复制共享库 (DLL) 的清单文件, 因为它们不被使用.

如果共享库具有与使用它的应用程序不同的依赖项, 则需要将清单文件嵌入到 DLL 二进制文件中. 从 Qt 4.1.3 开始, 下列 CONFIG 选项可用于嵌入清单:


  embed_manifest_dll
  embed_manifest_exe

默认情况下, 两个选项是启用的. 若想移除 embed_manifest_exe, 添加


  CONFIG -= embed_manifest_exe

到你的 .pro 文件.

您可以在 MSDN 网站找到有关清单文件和并行程序集的更多信息.

将运行时库包含在应用程序中的正确方法是确保它们安装在最终用户的系统上.

要在最终用户的系统上安装运行时库, 你需要在应用程序中包含适当的 Visual C++ Redistributable Package (VCRedist) 可执行文件, 并确保在用户安装应用程序时执行它.

它们被称为 vcredist_x64.exe (IA64 and 64-bit) 或 vcredist_x86.exe (32-bit), 可以在目录 c{Visual Studio install path>/VC/redist/<language-code>} 找到.

或者, 你可以从官网下载, 例如 vcredist_x64.exe for Visual Studio 2015.

注意: 你发布的应用程序必须使用完全相同的编译器版本针对相同的 C 运行时版本编译. 这可以防止由不同版本的 C 运行时库引起的部署错误.

Application Dependencies

Additional Libraries

根据配置, 编译器特定的库必须与你的应用程序一起分发.

例如, 如果 Qt 是使用 ANGLE 构建的, 则其共享库和来自 DirectX 的 HLSL 编译器也将附带.

你可以使用 Dependency Walker 工具检查你的应用程序正在链接哪些库. 你需要做的就是像这样运行它:


  depends <application executable>

这将提供你的应用程序所依赖的库的列表以及其他信息.

当使用 depends 工具查看 Plug & Paint 可执行文件 (plugandpaint.exe) 时, 该工具列出了以下对非系统库的直接依赖项:

QtVC++ 12.0 (2013)VC++ 14.0 (2015)MinGW
  • QT5CORE.DLL - The QtCore runtime
  • QT5GUI.DLL - The QtGui runtime
  • QT5WIDGETS.DLL - The QtWidgets runtime
  • MSVCR120.DLL - The C runtime
  • MSVCP120.DLL - The C++ runtime
  • VCCORLIB140.DLL, VCRUNTIME140D.DLL - The C runtime
  • MSVCP140.DLL - The C++ runtime
  • LIBWINPTHREAD-1.DLL
  • LIBGCC_S_DW2-1.DLL
  • LIBSTDC++-6.DLL

当查看插件 DLL 时, 会列出完全相同的依赖项.

从 Qt 5.2 版本开始, OpenSSL 官方支持的版本为 1.0.0 或更高版本. >= 0.9.7 和 < 1.0.0 的版本可能有效, 但不保证有效.

Qt Plugins

所有 Qt GUI 应用程序都需要一个在 Qt 5 中实现 Qt Platform Abstraction (QPA) 层的插件. 对于 Windows, 平台插件的名称是 qwindows.dll. 该文件必须位于分发目录下的特定子目录 (默认情况下为 platforms) 中. 或者, 可以调整 Qt 用于查找其插件的搜索路径, 如下所述.

你的应用程序还可能依赖于一个或多个 Qt 插件, 例如打印支持插件, JPEG 图像格式插件或 SQL 驱动程序插件. 请务必随应用程序分发你需要的所有 Qt 插件. 与平台插件类似, 每种类型的插件必须位于分发目录中的特定子目录 (如 printsupport, imageformatssqldrivers) 中.

从 Qt 5.14 开始, 库是可重定位的, 除非 Qt 是在关闭配置开关 -relocatable 的情况下构建的. Qt 插件的搜索路径与 QtCore 库的位置相关, 无需进一步的步骤即可确保在目标计算机上安装应用程序后找到插件.

Ensuring Plugins Are Found when Using Non-Relocatable Builds

对于不可重定位的构建, 必须采取额外的步骤来确保在目标计算机上安装应用程序后找到插件.

在这种情况下, Qt 插件的搜索路径被硬编码到 QtCore 库中. 默认情况下, Qt安装的 plugins 子目录是第一个插件搜索路径. 然而, 像默认路径这样的预先确定的路径有一定的缺点. 例如, 它们可能不存在于目标计算机上. 因此, 你需要检查各种替代方案以确保找到 Qt 插件:

  • 使用 qt.conf. 如果你在不同位置有共享相同插件的可执行文件, 则建议使用此方法.
  • 使用 QApplication::addLibraryPath() 或 QApplication::setLibraryPaths(). 如果你只有一个将使用该插件的可执行文件, 则建议使用此方法.
  • 使用第三方安装实用程序更改 QtCore 库中的硬编码路径.

如果你使用 QApplication::addLibraryPath 添加自定义路径, 它可能如下所示:


  QCoreApplication::addLibraryPath("C:/customPath/plugins");

QCoreApplication::libraryPaths() 返回值可能如下:

  • C:/customPath/plugins
  • C:/Qt/%VERSION%/plugins
  • E:/myApplication/directory

可执行文件将在这些目录中查找插件, 顺序与 QCoreApplication::libraryPaths() 返回的 QStringList 相同. 新添加的路径被添加到 QCoreApplication::libraryPaths() 之前, 这意味着它将首先被搜索. 但是, 如果你使用 QCoreApplication::setLibraryPaths(), 你能自定义搜索哪些路径以及搜索顺序.

How to Create Qt Plugins 概述了为 Qt 应用程序构建和部署插件时需要注意的问题.