VS2022 静态链接VC方式编译 Qt 5.7.1
2025-01-05 13:41:52

之前写过一篇 VS2017 编译 Qt 5.7.1 文章介绍了 Qt 的编译方式。为了支持 XP 系统,不得不使用 VS2017 来编译,这带来一个问题:无法享受 C++ 20 以后的现代式编程。

好在有 YY-Thunks 这个神器可以解决 XP 缺失一些 API 的问题。
总体的编译流程和之前差不多,只是一些配置细节不同。

编辑 msvc-desktop.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 控制 SDK 版本
DEFINES += UNICODE WIN32 _UNICODE _WIN32_WINNT=0x0501 _USING_V110_SDK71_

# 子系统版本,要运行在 XP 系统上必须是 5.01
QMAKE_SUBSYSTEM_SUFFIX = ,5.01

# -MD 改为 -MT,这样编译出的 Qt DLL 文件就不依赖VC运行库了
QMAKE_CFLAGS_RELEASE = -O2 -MT
QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO += -O2 -MT -Zi
QMAKE_CFLAGS_DEBUG = -Zi -MTd

# 链接 YY_Thunks_for_WinXP.obj 文件,让 YY-Thunks 实现 Vista+ 以后才有的 API
QMAKE_LFLAGS = /NOLOGO /DYNAMICBASE /NXCOMPAT C:\Qt\YY_Thunks_for_WinXP.obj

# 根据 YY-Thunks 的要求,对于 DLL 项目要修改入口点
QMAKE_LFLAGS_DLL = /DLL /ENTRY:"DllMainCRTStartupForYY_Thunks"

编译错误

在编译过程中会碰到一些错误而终止,要解决掉才能完成编译。

_FILE_ID_128 类型重定义

1
C:\Qt\qtbase-opensource-src-5.7.1\src\corelib\io\qfilesystemengine_win.cpp(629): error C2011: “_FILE_ID_128”:“struct”类型重定义
1
2
3
4
5
6
// MinGW-64 defines FILE_ID_128 as of gcc-4.8.1 along with FILE_SUPPORTS_INTEGRITY_STREAMS
#if !(defined(Q_CC_MINGW) && defined(FILE_SUPPORTS_INTEGRITY_STREAMS))
typedef struct _FILE_ID_128 {
BYTE Identifier[16];
} FILE_ID_128, *PFILE_ID_128;
#endif // !(Q_CC_MINGW && FILE_SUPPORTS_INTEGRITY_STREAMS)

因为新SDK中已经有改定义了,所以提示重定义错误,直接删除这一段即可。

缺少头文件

1
C:\Qt\qtbase-opensource-src-5.7.1\src\tools\qlalr\compress.cpp(40): error C2039: "binary_function": 不是 "std" 的成员

compress.cpp中添加头文件#include <functional>

缺少 tmschema.h

1
qwindowsxpstyle_p_p.h(64): fatal error C1083: 无法打开包括文件: “tmschema.h”: No such file or directory

这个头文件仅在 SDK v7.1A 中才有。可以从 v7.1A SDK 中提取出这个头文件,或从这里下载 https://github.com/tpn/winsdk-7/raw/refs/heads/master/v7.1A/Include/Tmschema.h 文件,将它放在一个单独的目录中,稍后编译前将这个目录加入头文件搜索路径即可。

未声明的标识符

1
2
3
4
C:\Qt\qtbase-opensource-src-5.7.1\src\widgets\util\qsystemtrayicon_win.cpp(262): error C2065: “NIIF_USER”: 未声明的标识符
C:\Qt\qtbase-opensource-src-5.7.1\src\widgets\util\qsystemtrayicon_win.cpp(270): error C2065: “NIF_SHOWTIP”: 未声明的标识符
C:\Qt\qtbase-opensource-src-5.7.1\src\widgets\util\qsystemtrayicon_win.cpp(283): error C2065: “NIF_SHOWTIP”: 未声明的标识符
C:\Qt\qtbase-opensource-src-5.7.1\src\widgets\util\qsystemtrayicon_win.cpp(327): error C2065: “NOTIFYICON_VERSION_4”: 未声明的标识符

这3个宏是 Vista 后才有的,直接在qsystemtrayicon_win.cpp文件中追加即可:

1
2
3
4
5
6
7
8
9
10
11
#ifndef NIIF_USER
#define NIIF_USER 0x00000004
#endif

#ifndef NIF_SHOWTIP
#define NIF_SHOWTIP 0x00000080
#endif

#ifndef NOTIFYICON_VERSION_4
#define NOTIFYICON_VERSION_4 4
#endif

缺少 RtlCaptureStackBackTrace

这个错误在以前的文章中提到过,在qtestcase.cpp文件中加入以下声明即可:

1
2
3
4
5
6
extern "C" __declspec(dllimport) USHORT WINAPI RtlCaptureStackBackTrace(
__in ULONG FramesToSkip,
__in ULONG FramesToCapture,
__out_ecount(FramesToCapture) PVOID *BackTrace,
__out_opt PULONG BackTraceHash
);

编译

我的编译参数

1
2
set INCLUDE=C:\Qt\winsdk-7.1A;%INCLUDE%
..\configure.bat -prefix "C:/Qt/Qt5.7.1-vc14.42-x86-static" -platform win32-msvc2015 -confirm-license -opensource -debug-and-release -shared -nomake examples -nomake tests -no-directwrite -no-angle -opengl desktop -mp

注意这里先将前面提到 tmschema.h 的路径加入到搜索路径中。
没意外的话编译就OK了。

使用

用 QtCreator 创建的项目默认是控制台程序,将其修改为WIN32

1
add_executable(untitled WIN32 ${PROJECT_SOURCES})

然后会继续报错:

1
qtmaind.lib(qtmain_win.obj):-1: error: LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MTd_StaticDebug”不匹配值“MDd_DynamicDebug”(mocs_compilation.cpp.obj 中)

很明显,项目也必须静态链接VC。
为了在 XP 下运行,还要作出其他配置,最终的设置如下:

1
2
3
4
5
6
7
8
9
10
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
target_compile_definitions(untitled PRIVATE UNICODE _UNICODE)
target_compile_options(untitled PRIVATE $<$<CONFIG:>:/MT> $<$<CONFIG:Debug>:/MTd> $<$<CONFIG:Release>:/MT> )

if (CMAKE_SIZEOF_VOID_P EQUAL 4)
target_compile_definitions(untitled PRIVATE _WIN32_WINNT=0x0501 _USING_V110_SDK71_)
set_target_properties(untitled PROPERTIES LINK_FLAGS "/SUBSYSTEM:WINDOWS,5.01")
target_link_options(untitled PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/YY_Thunks_for_WinXP.obj")
endif ()
endif ()

内存错误

在界面上随便放几个控件,然后用Debug方式运行,会弹出一个提示框:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
---------------------------
Microsoft Visual C++ Runtime Library
---------------------------
Debug Assertion Failed!

Program: C:\test\app.exe
File: minkernel\crts\ucrt\src\appcrt\heap\debug_heap.cpp
Line: 996

Expression: __acrt_first_block == header

For information on how your program can cause an assertion
failure, see the Visual C++ documentation on asserts.

(Press Retry to debug the application)

网上查了下,大概原因是因为在一个模块中申请的内存在另一个模块中释放导致的。因为使用/MT编译后不再依赖VC运行库,每个模块都有自己的堆管理器,导致内存的申请和释放都是在各个模块中进行的。
为了解决这个问题,必须遵循 谁申请谁释放 的原则,但我们肯定是无法魔改 Qt 的,于是我想到引入 VC-LTL5 是否能解决?实践发现确实可以!

引入 VC-LTL5

VC-LTL5 下载安装包,解压后运行Install.cmd安装即可。
接着在编译之前先运行:

1
2
set WindowsTargetPlatformMinVersion=5.1.2600.0
"VC-LTL helper for nmake.cmd"

然后再按照前面的方式正常编译即可,最终产生的二进制文件将依赖msvcrt.dll文件,这样内存的申请释放都在msvcrt.dll中,就不会有前面的问题了。


还要注意的是,在项目中也要使用 VC-LTL5,这样才能保持和 Qt DLL 一致的内存管理。
普通的做法是安装 VC-LTL5 后,将VC-LTL helper for cmake.cmake文件拷贝到项目目录下,在CMakeLists.txt文件中加入:

1
2
3
4
5
# 一定要在 add_executable 之前包含,否则无效,这点官方没有指出。而且 VC-LTL5 仅在 Release 时才工作,Debug 是没有的
set(WindowsTargetPlatformMinVersion "5.1.2600.0")
include("VC-LTL helper for cmake.cmake")

add_executable(untitled WIN32 ${PROJECT_SOURCES})


普通引用方式是要先安装 VC-LTL5,但是在工作中,项目是和同事们一起维护的,这要求每位开发者都要先安装,有些麻烦,我更希望将 VC-LTL5 集成到编译的 Qt 中,并精简掉无关的文件(移除除了 5.1.2600.0 之外的所有 TargetPlatform 文件),并指定VC_LTL_ROOT变量即可:

1
2
3
set(WindowsTargetPlatformMinVersion "5.1.2600.0")
set(VC_LTL_Root "${CMAKE_PREFIX_PATH}/vc-ltl5")
include("${VC_LTL_Root}/VC-LTL helper for cmake.cmake")


最后的最后,每个工程都要设置 XP 相关的设置很啰嗦,直接在入口CMakeLists.txt中全局设置一次更方便:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
add_compile_definitions(UNICODE _UNICODE)

link_libraries("${CMAKE_PREFIX_PATH}/obj/YY_Thunks_for_WinXP.obj")

set(WindowsTargetPlatformMinVersion "5.1.2600.0")
set(VC_LTL_Root "${CMAKE_PREFIX_PATH}/vc-ltl5")
include("${VC_LTL_Root}/VC-LTL helper for cmake.cmake")

add_compile_options(
$<$<CONFIG:>:/MT>
$<$<CONFIG:Debug>:/MTd>
$<$<CONFIG:Release>:/MT>
)

if (CMAKE_SIZEOF_VOID_P EQUAL 4)
add_compile_definitions(_WIN32_WINNT=0x0501 _USING_V110_SDK71_)
endif ()
endif()

参考

/MD、/MT、/LD(使用运行时库)
[C++][MSVC][Error] __acrt_first_block == header

上一页
2025-01-05 13:41:52