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

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

本文关键字:设置 目标 版本 操作系统 构建 C++ Visual 何使用 编译器 Windows      更新时间:2023-10-16

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

我的库使用了一些仅在Windows 8/7中可用的函数/枚举值/结构成员。

我希望能够为Windows XP、Windows Vista、Windows 7和Windows 8/8.1操作系统版本以及x86、x64和arm架构构建库,即不是一个仅针对Windows XP并适用于所有地方的构建,而是针对特定操作系统的许多不同构建,因为较新的操作系统版本具有我的库可以提供的更多有用功能。

我的问题是:

  1. 如何告诉编译器针对特定的操作系统版本(即XP、Vista、7、8、8.1等)?

  2. 如何告诉编译器针对特定的体系结构(如x86、x64、arm等)?

  3. 如果我使用仅在Windows 8/7中可用的函数/枚举值/结构成员,但以Windows XP为目标构建库,会发生什么?编译器会警告我在WindowsXP上不存在这样的东西吗?或者它真的会编译但无法在Windows XP系统上运行?

  4. 当我为Windows XP编译时,我的代码如何跳过Windows XP中不存在的东西(Windows 7/8函数等)?

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

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

  1. 我只需要将_WIN32_WINNTWINVER定义设置为目标系统的适当值,仅此而已,除此之外,我不需要设置任何内容,我的应用程序将在指定的系统(即Windows XP)上运行。

    • 当使用"C:\Program Files(x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"设置编译器环境变量时,我需要使用适当的选项,即64位的"C:\Program File(x86)\Microsoft Visual Studio 12.0\WC\vcvarsarl.bat amd64">
    • 我还需要为链接器指定适当的/SUBSYSTEM值,即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的平台值相似。除了体系结构之外,他们是否还设置了目标操作系统?但(1)中的WINVER=502用于针对Windows Server 2003,根据维基百科,Windows Server 2003同时发布了64位和32位版本,但在这里5.02严格代表64位,这没有意义
  2. 编译器将无法编译,因为(1)中的WINVER定义将排除目标操作系统中不存在的函数和内容(Windows头文件使用定义为#ifdef的内容)。

  3. 我应该在自己的代码中使用基于WINVER#ifdef,就像Windows头文件一样,并在需要时为缺失的功能提供替代方案。

  4. 不知道。

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

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

您对自己问题的回答在很大程度上是正确的。一些澄清和更正:

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

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

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

Windows 8 SDK仅支持Windows Vista以下的软件开发。如果要将_WIN32_WINNTWINVER设置为针对Windows XP构建,则需要使用Windows 7 SDK(Visual Studio 2013将同时安装这两个SDK)。

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

对不起,这会很长时间:-(

1.如何告诉编译器针对特定的操作系统版本(即XP、Vista、7、8、8.1等)?

通常您不会告诉编译器针对特定的操作系统版本。相反,您可以使用WINVER和_WIN32_WINNT从SDK中定制头文件。头文件sdkddkver.h提供了有用的命名常量

如果将WINVER和_WIn32_WINNT设置得足够高,则头文件将声明仅在后续操作系统版本中可用的函数(假设您使用的SDK足够新,可以声明这些函数)。

但现在您有了一个程序,它可以调用旧操作系统版本上可能不存在的函数。如果你幸运的话,有某种兼容性垫片可以帮助你。如果运气不好,调用未知函数的尝试将失败。或者做一些比失败更糟糕的事情。但这是我的观点,我找不到具体的引用。

2.如何告诉编译器针对特定的体系结构(如x86、x64、arm等)?

你没有。每个目标平台使用不同的编译器。如果您查看C:Program Files (x86)Microsoft Visual Studio 12.0VC,您会发现bin目录和七个子目录:

amd64
amd64_arm

您可以使用相应目录中的编译器为该平台构建代码。在Win32版本的Windows上,从上面的路径中删除"(x86)"。

3.如果我使用仅在Windows 8/7中可用的函数/枚举值/结构成员,会发生什么…

我不确定是否定义了这种行为。如果您将_WIn32_WINNT设置为_WIn32_PINNT_WIN8,则相当于在说"我打算在Windows 8及更高版本上运行此程序"。但这是我的观点,不是确切的事实。可能是我没有足够仔细地阅读文件。

4.当我为Windows XP编译时,我的代码如何跳过Windows XP中没有的东西(Windows 7/8函数等)?

你必须明确地为它编码,我认为没有什么可以为你处理这个问题。您可以在编译时使用#ifdef和_WIN32_WINNT来执行此操作,也可以在运行时通过确定是否可以调用该函数,然后调用该函数或根据需要避免调用该函数来执行此任务。

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

5.在针对不同操作系统版本时,我使用哪个Windows SDK版本有关系吗?

可能。较旧版本的SDK可能不包含后续操作系统版本的函数定义。例如,当创建Vista版本的SDK时,Windows 8中引入的大多数功能可能都不存在。

如果我总是使用最新的SDK版本,即使是在针对Windows XP时,也可以吗?

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

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

1.我只需要设置_WIN32_WINNT和WINVER定义。。。

这将为您提供一个代码文件,该文件将在由_WIN32_WINNT值标识的操作系统版本上正确执行。它很可能也适用于更高版本的操作系统(微软在推出新的操作系统版本时,尽量不破坏现有程序)。它可能会也可能不会在比_Win32_WINNT识别的操作系统版本旧的操作系统上正确运行-我不会冒险。

•在设置编译器环境变量时,我需要使用适当的选项

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

•我还需要为链接器指定适当的/SUBSYSTEM值,即/SUBSYSTEM:WINDOWS,5.02或/SUBSYSTEM:WINDOWS,6.00 for x64

SUBSYSTEM主要/次要值与平台类型(ARM、x86、x64等)没有直接关系。他们指定了代码文件将运行的最低操作系统级别。因此,SUBSYSTEM 5.01表示"此代码文件将在XP上运行",而SUBSYSTEM 5.02表示"此程序文件将在Windows Server 2003上运行,但不会在XP上"。理想情况下,您应该将子系统主/辅与_WIN32_WINNT匹配,以避免文件在无法支持它的旧操作系统版本上运行的风险。

至少我记得这一点——我没有一个XP环境可以用来确认我是否正确地记住了这一点。