使用 Visual C++ 编译器在 Windows 上构建库时如何正确设置目标 OS 版本

How to properly set target OS version when building a library on Windows using Visual C++ compiler

我在 Windows 平台上使用具有 C++11 功能的 Visual C++ 2013 编译器构建一个 cross-platform 库,特别是使用 CMake(NMake 生成器)作为构建系统。我正在使用 Windows 7.

我的图书馆使用一些 functions/enum values/structure 成员,仅在 Windows 8 月 7 日可用。

我希望能够为 Windows XP、Windows Vista、Windows 7 和 Windows 8/8.1 OS 版本构建库和 x86、x64 和 arm 架构,即不是一个只针对 Windows XP 并在任何地方工作的构建,而是许多针对特定 OS 的不同构建,因为较新的 OS 版本有更多我的图书馆可以提供的有用功能。

我的问题是:

  1. 如何告诉编译器以特定的 OS 版本为目标(即 XP、Vista、7、8、8.1 等)?

  2. 如何告诉编译器以特定架构为目标(即 x86、x64、arm 等)?

  3. 如果我使用仅在 Windows 8/7 中可用的 functions/enum values/structure 成员,但构建我的库针对 Windows XP,会发生什么情况?编译器会警告我这些东西在 Windows XP 上不存在吗?或者它会在 Windows XP 系统上实际编译但无法 运行 吗?

  4. 我该如何做到,当我为 Windows XP 编译时,我的代码会跳过 Windows XP 中不存在的内容(Windows 7/8 函数等)?

  5. 针对不同的 OS 版本时,我使用哪个 Windows SDK 版本有关系吗?我似乎已经安装了 8.1、8.0 和 7.1 Windows SDK。如果我始终使用最新的 SDK 版本,即使是针对 Windows XP,也可以吗?

以下是我找到的一些答案,但我不确定它们是否正确或完整:

  1. 我只需要设置_WIN32_WINNTWINVER定义为appropriate values for the target system就可以了,除此之外我不需要设置任何东西,我的应用程序将 运行 在指定的系统上(即 Windows XP),仅此而已。

    • 在使用 "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" 设置编译器环境变量时,我需要使用适当的选项,即 "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat amd64" 用于 64 位。
    • 我还需要为链接器指定 appropriate /SUBSYSTEM value,即 x64 的 /SUBSYSTEM:WINDOWS,5.02/SUBSYSTEM:WINDOWS,6.00。但是5.026.00有什么区别呢?为什么两个值指定相同的东西(64 位)? 5.016.00 也一样,为什么它们都指定 32 位?我想有一个 64/32 位的值就足够了。

      • 这些值(5.015.026.00)看起来与 (1) 中 WINVER 的平台值相似。除了架构之外,他们还设置了目标 OS 吗?但是 (1) 中的 WINVER=502 用于针对 Windows Server 2003,根据维基百科,它同时发布了 64 位和 32 位版本,但在这里 5.02 严格代表 64 位,这没有意义...
  2. 编译器将无法编译,因为 (1) 中的 WINVER 定义将排除目标 OS 中不存在的函数和内容(Windows header 文件使用定义 #ifdef 事物)。

  3. 我应该 #ifdef 在我自己的代码中基于 WINVER 的东西,就像 Windows header 做的那样,并为缺失的东西提供替代方案需要时的功能。

  4. 不知道。

请注意,我没有使用 Visual Studio IDE,所以告诉我在 IDE 中设置选项 X 有点毫无意义。

我来自 Linux 开发,所以 Windows 东西对我来说有点新鲜。

您对自己问题的回答大部分是正确的。一些澄清和更正:

子系统版本与目标体系结构正交。 /subsystem 文档所说的是 x86 的 minimum 子系统版本是 5.01,x64 的 minimum 子系统版本是 5.02。对于控制台和 Windows 应用程序,子系统版本与内部操作系统版本号相同。 5.01 是 x86 Windows XP; 5.02 是 x64 Windows XP。 Windows XP 有两个不同的版本号,因为 x64 Windows XP 的发布时间晚于 x86 Windows XP。较新的 OSes 对所有体系结构具有相同的版本号(例如,Windows Vista 是 x86 和 x64 的版本 6.0)。

请注意,通过设置子系统版本,您可以限制您的程序运行的操作系统集 运行。例如,如果您将子系统版本设置为 6.2,您的程序将仅在 Windows 8 及更高版本上 运行。如果您尝试 运行 该程序,例如Windows7、不会运行。 (对于 DLL 也是如此:如果您的 DLL 的目标 OS 比您正在 运行 更新的 OS 更新,加载程序将不会加载 DLL,至少不适用于代码执行。)

有关操作系统版本的列表,请参阅维基百科页面 "List of Microsoft Windows versions"。 Windows XP 是最旧的 Windows 版本,受 Visual Studio 2013 支持。

Windows 8 SDK 仅支持低至 Windows Vista 的软件开发。如果你想设置 _WIN32_WINNTWINVER 为 Windows XP 构建,你需要使用 Windows 7 SDK(Visual Studio 2013 将同时安装SDK)。

除非您的程序对于每个目标操作系统都大不相同,否则在您要支持的最旧操作系统(Windows XP)上构建一个 运行 的二进制文件可能会简单得多) 并延迟加载或动态加载(通过 LoadLibrary/GetProcAddress)您想要从更新的操作系统使用的任何功能,当该功能可用时。

抱歉,这会很长:-(

1.How do I tell the compiler to target a specific OS version (i.e. XP, Vista, 7, 8, 8.1, etc.)?

通常您不会告诉编译器以特定的 OS 版本为目标。相反,您使用 WINVER and _WIN32_WINNT 从 SDK 定制 header 文件。 header 文件 sdkddkver.h 提供有用的命名常量

如果您将 WINVER 和 _WIn32_WINNT 设置得足够高,header 文件将声明仅在以后的 OS 版本中可用的函数(假设您使用的是足够新的 SDK声明这些函数)。

但是现在您有一个程序可以调用旧 OS 版本中可能不存在的函数。如果幸运的话,可以使用某种兼容性垫片来帮助您。如果你不走运,调用未知函数的尝试就会失败。或者做比失败更糟糕的事情。但这是我的观点,我找不到具体的内容来引用。

2.How do I tell the compiler to target a specific architecture (i.e. x86, x64, arm, etc)?

你不知道。您为每个目标平台使用不同的编译器。如果您在 C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\ 下查看,您会找到 bin 目录和七个子目录:

amd64
amd64_arm
amd64_x86
手臂
x86_amd64
x86_arm

您使用适当目录中的编译器为该平台构建代码。在 Windows 的 Win32 版本上,从上面的路径中删除“(x86)”。

3.What happens if I use functions/enum values/structure members available only in Windows 8/7...

我不确定是否定义了这种行为。如果您将 _WIn32_WINNT 设置为 _WIN32_WINNT_WIN8,您几乎就是在说 "I intend to run this on Windows 8 and above"。但那是我的看法,不是确定的事实。可能是我看文档不够仔细

4.How do I make it so the when I compile for Windows XP, my code skips over things that are not present in Windows XP (Windows 7/8 functions and such)?

你必须明确地为它编写代码,我认为没有任何东西可以为你处理这个问题。您可以在编译时使用 #ifdef 和 _WIN32_WINNT 执行此操作,或者您可以在 运行 时通过确定是否可以调用该函数然后调用该函数或避免调用适当发挥作用。

在所有可能的世界中(不太可能)最坏的情况下,将会发生重大变化,例如(例如)结构参数的 Windows 8 格式与该函数的早期实现不兼容。在这种情况下,您必须弄清楚调用函数时要使用哪个版本的结构 - header 只会根据 _WIN32_WINNT 值为您提供一个结构定义。

5.Does it matter which Windows SDK version I use when targeting different OS versions?

可能吧。旧版本的 SDK 可能不包含更高 OS 版本的函数定义。例如,当创建 Vista 版本的 SDK 时,Windows 8 中引入的大多数功能可能并不存在。

Is it fine if I always use the latest SDK version even when targeting Windows XP?

这可能是最安全的做法。如果您使用最新的 SDK 并将 _WIN32_WINNT 设置为 _WIN32_WINNT_WINXP,那么 可能 会发生正确的事情 - 假设您想要在 XP 上 运行。

我说 "probably" 因为 XP 是一种特殊情况。它不受支持。最新的 SDK 可能已经过也可能没有经过 _WIN32_WINNT 设置为 XP 值的测试。

1.I just need to set _WIN32_WINNT and WINVER defines...

这应该会为您提供一个代码文件,该文件将在由 _WIN32_WINNT 值标识的 OS 版本上正确执行。它很可能也适用于以后的 OS 版本(Microsoft 在推出更新的 OS 版本时非常努力地不破坏现有程序)。在比 _Win32_WINNT 确定的版本早的 OS 版本上,它可能会或可能不会正确地 运行 - 我不会冒险。

•I need to use appropriate option when setting up compiler enviroment variables

由于您使用的是 NMAKE,我不确定这个问题的答案。 vcvarsall.bat 文件设置了各种环境变量,这些变量(除其他外)可以控制使用哪一组编译器(通过控制 MSBUILD 在何处查找二进制文件,如编译器)。但是我不知道 NMAKE 运行s MSBUILD 是否关注那些环境变量或者有自己不同的变量集还是什么。

•I also need to specify appropriate /SUBSYSTEM value to the linker, i.e. /SUBSYSTEM:WINDOWS,5.02 or /SUBSYSTEM:WINDOWS,6.00 for x64

SUBSYSTEM major/minor 值与平台类型(ARM、x86、x64 等)没有任何直接关系。它们指定代码文件 运行 的最低 OS 级别。所以 SUBSYSTEM 5.01 说 "this code file will run on XP" 而 SUBSYSTEM 5.02 说 "this code file will run on Windows Server 2003 but not on XP"。理想情况下,您应该将子系统 major/minor 与 _WIN32_WINNT 相匹配,以避免文件 运行ning 在不支持它的旧 OS 版本上的风险。

至少那是我记得的 - 我没有 XP 环境可以用来确认我是否记得正确。