跳转到内容
新建笔记

Qt Windows 安装、Kit 匹配与 DLL 发布诊断

从同一个 Kit 贯通安装、构建和运行

跳转到“从同一个 Kit 贯通安装、构建和运行”

Qt Creator 的 Kit 组合了 Qt 版本、编译器、目标架构等设置。安装了 Qt 不等于任意 Qt DLL 都能给任意程序使用;MinGW 与 MSVC、32/64 位、Qt 版本、Debug/Release 及编译器运行库都要按目标匹配。Qt Creator Kit

原记录中的 qt_assert 入口点错误出现在一个 Windows 程序中。截图同时列出 Qt 6.8.0 的 MSVC 和 MinGW 路径,作者记录把指定 MinGW bin 加入 Makefile 的 PATH 后能运行。这个现场值得保留,但仅凭截图无法知道进程最后加载了哪份 DLL,也不能断言“入口点缺失”一定等于“完全没有找到 DLL”。

原 PATH 截图:Qt 6.8.0 的 MSVC 与 MinGW bin、include、lib 目录同时存在

安装包先选操作系统,再选组件

跳转到“安装包先选操作系统,再选组件”

旧安装笔记把 Windows PowerShell 与 qt-unified-linux-x64-4.4.0-online.run 写在一起,这是操作系统混用。Windows 应下载当前适用的 .exe,Linux 使用适用架构的 .run;4.4.0 是旧安装器版本,不是当前 Qt 库版本。

从 Qt 官方安装器目录 获取适用文件后,按实际文件名运行。Qt 官方支持 --mirror,USTC 的说明也给出了它的镜像入口;镜像根路径不要误写成某个深层组件目录。Qt 安装器参数、USTC 使用说明

下面是 PowerShell 写法示意,先把变量改成实际下载的 Windows 安装器路径:

终端窗口
$qtInstaller = '.\actual-windows-installer.exe'
& $qtInstaller --mirror 'https://mirrors.ustc.edu.cn/qtproject'

镜像参数只改变下载来源,不会把 Linux 包转换成 Windows 程序。安装时选择对应编译器的 Qt 库及需要的模块,之后在 Kit 中确认路径;切换编译器或架构时使用独立构建目录,避免旧 CMake 缓存继续指向另一套工具链。

现象优先核对
编译时找不到头文件对应模块是否安装,目标是否链接正确 Qt CMake target,Kit 是否正确
启动时找不到某个 DLL部署目录、依赖库和搜索路径中是否存在需要的文件
找到库但缺少某个入口点实际库的版本/ABI/构建配置是否与程序及其依赖匹配,是否加载了同名旧库

普通未打包程序的 DLL 查找还涉及程序目录、系统目录、已加载模块及加载方式等因素;应用目录中的旧 DLL 可能比 PATH 中的新 DLL 更早被使用。只移动 PATH 条目的先后次序不一定能修复问题。Windows DLL 搜索顺序

include 是编译头文件目录,lib 是链接输入目录;把它们加入 PATH 通常不能解决运行依赖。原 Makefile 中 QT_DLIBS=... 与 PATH := $(QT_DLIBS);$(PATH) 的办法本质上是调整构建/启动相关进程的环境,是否传给子进程还取决于 make 的导出设置。它不会自动更新系统环境,也不是完整发布包。生成的 Makefile 还可能被重新生成覆盖,开发时应优先使用 Kit 的运行环境设置。

一个报告当前程序 Qt 环境的探针

跳转到“一个报告当前程序 Qt 环境的探针”

下面程序使用标准命名的 Windows Qt 6 动态库。它报告自己的进程,不能替代对另一个启动失败程序的加载记录调查。编译期版本来自头文件,qVersion() 来自实际 Qt Core;安装前缀也不应误当成 DLL 的真实加载位置。

main.cpp:

#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#include <QCoreApplication>
#include <QLibraryInfo>
#include <QSysInfo>
#include <QString>
#include <QtGlobal>
#include <climits>
#include <iostream>
#include <vector>
int main(int argc, char **argv) {
QCoreApplication app(argc, argv);
std::cout << "Qt headers: " << QT_VERSION_STR << '\n'
<< "Qt runtime: " << qVersion() << '\n'
<< "Pointer bits: " << sizeof(void *) * CHAR_BIT << '\n'
<< "Build ABI: " << QSysInfo::buildAbi().toStdString() << '\n'
<< "Qt prefix: " << QLibraryInfo::path(QLibraryInfo::PrefixPath)
.toStdString() << '\n';
HMODULE module = GetModuleHandleW(L"Qt6Core.dll");
if (module == nullptr) module = GetModuleHandleW(L"Qt6Cored.dll");
if (module == nullptr) {
std::cerr << "No standard-named shared Qt6Core module found\n";
return 1;
}
std::vector<wchar_t> filename(32768);
const DWORD length = GetModuleFileNameW(module, filename.data(),
static_cast<DWORD>(filename.size()));
if (length == 0 || length >= filename.size()) {
std::cerr << "Cannot obtain an untruncated module path\n";
return 1;
}
std::cout << "Loaded Core: "
<< QString::fromWCharArray(filename.data(), static_cast<int>(length))
.toUtf8().constData() << '\n';
}

CMakeLists.txt:

cmake_minimum_required(VERSION 3.20)
project(QtWindowsProbe LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt6 REQUIRED COMPONENTS Core)
add_executable(qt_windows_probe main.cpp)
target_link_libraries(qt_windows_probe PRIVATE Qt6::Core)

自定义 DLL 名称、静态 Qt 或进程尚未开始执行时不适用这段探针;应改用系统加载诊断、调试器模块列表与链接依赖检查。版本字符串相同也不单独证明 ABI 完全兼容。

发布时收集依赖,再离开开发环境验证

跳转到“发布时收集依赖,再离开开发环境验证”

使用构建该程序的同一套 Qt 中的 windeployqt,先对明确的可执行文件做 --dry-run,检查将收集的库、插件及配置,再生成独立分发目录。工具支持 --debug/--release,要与实际产物一致。Widgets 程序还需要正确位置的 Windows 平台插件;QML 项目另按源码目录提供 --qmldir。第三方 DLL 不一定由这个工具自动收齐。Qt Windows 发布

发布检查应在没有开发机 Qt PATH 的环境或干净测试机上进行,并覆盖图片插件、数据库驱动等实际功能。仅在 Qt Creator 中启动成功,或 dry-run 返回成功,均不等于分发包已经完整。本页的验证范围是本机探针的编译运行与发布工具的只读分析,不声称替原历史程序完成了故障复现或安装。

来源:入口点故障原记录、安装镜像原记录、GetModuleFileNameW。