Visual Studio 在 Windows 7 上将 WINVER/_WIN32_WINNT 设置为 Windows 8?

Visual Studio setting WINVER/_WIN32_WINNT to Windows 8 on Windows 7?

我正在使用 Visual Studio 2012 在 Windows 7 x64 上执行一些测试。微软的工具链似乎正在将 _WIN32_WINNT 设置为 0x602_WIN32_WINNT_WIN8 ). 运行 我们的测试程序结果为 The procedure entry point GetOverlappedResultEx could not be located in the dynamic link library KERNEL32.dll:

我有两个问题。首先,出于病态的好奇,为什么 Microsoft 将 _WIN32_WINNT 设置为对执行环境无效的值?我能理解用户是否想这样做,但微软不行,因为它破坏了东西 (q.v.).

其次,我们如何将WINVER_WIN32_WINNT设置为符号"This Platform"?在这种情况下,"this platform" 是 Windows 7. 当我在 Windows Vista 上测试时,它将是一个不同的平台。当我在 Windows 8 上测试时,它将是另一个平台。当我在 ARM 开发人员提示下测试 Windows Phone 和 Windows Store 时,它​​将是另一个平台。


这个问题很容易重现。这是步骤。我想任何拥有良好测试环境的人都已经完成了前八步。

  1. 架起一台Windows7、x64机器
  2. 对Windows7台机器
  3. 进行全面补丁
  4. 安装 Visual Studio 2008
  5. 完全补丁 Visual Studio 2008
  6. 安装 Visual Studio 2010
  7. 完全补丁 Visual Studio 2010
  8. 安装 Visual Studio 2012
  9. 完全补丁 Visual Studio 2012

然后:

  1. 在VS2008下创建一个空的"Hello World"项目
  2. 删除hello_world.cpp之外的所有内容(这应该留下 1 个解决方案文件、1 个项目文件、1 个源文件)
  3. 将其转换为 VS2010
  4. 用VS2012打开

我可以 post MCVE,它是一个空的源文件,以安抚一些人。这似乎是在浪费时间,因为问题出在工具链而不是源文件上。空 main 对这个问题真的很重要吗?无论文件中的内容如何,​​都会设置错误的 WINVER_WIN32_WINNT


我知道错误的来源。我们的代码最近更改为更好地支持 Windows 8, Phone 8, Store 8, Server 2012, Windows 10, Phone 10, Store 10 and Windows Universal Platform。更改如下所示:

#if defined(CRYPTOPP_WIN32_AVAILABLE)
# if ((WINVER >= 0x0602 /*_WIN32_WINNT_WIN8*/) || (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/))
#  include <synchapi.h>
#  include <ioapiset.h>
#  define USE_WINDOWS8_API
# endif
#endif
...

#if defined(USE_WINDOWS8_API)
    BOOL result = GetOverlappedResultEx(GetHandle(), &m_overlapped, &m_lastResult, INFINITE, FALSE);
#else
    BOOL result = GetOverlappedResult(GetHandle(), &m_overlapped, &m_lastResult, FALSE);
#endif

具有讽刺意味的是,我们首先添加了 USE_WINDOWS8_API、额外的包含和对 GetOverlappedResultEx 的调用以安抚这些工具。他们抱怨不推荐使用的函数并导致脏编译。脏编译给用户带来了治理、C&A 和 ST&E 问题。


我审核了代码以确保我们没有无意或错误地设置值。我确认我们在一个地方执行此操作,并且代码路径未激活,因为 Microsoft 的工具链将值设置为 0x602:

#ifdef CRYPTOPP_WIN32_AVAILABLE
# ifndef _WIN32_WINNT
#  define _WIN32_WINNT 0x0400
# endif
#endif

这里有一个相关的 Stack overflow 问题:What is WINVER?,但它没有讨论如何将其设置为 "this platform"。

这是微软关于该主题的文档:Using the Windows Headers and Modifying WINVER and _WIN32_WINNT。具有讽刺意味的是,他们并没有真正讨论问题或Windows 10,Windows Phone 10,Windows商店10或Windows通用平台。


顺便说一句,GCC 有一个准类似的 -march=native,基本上提供了 "this platform".

如果您没有明确提供目标平台版本,则 Windows SDK 将 select 默认版本(有关详细信息,请参阅 SDK 的 sdkddkver.h 文件)。

例如,Windows 8.0 SDK sdkddkver.h 文件具有以下片段:

#if !defined(_WIN32_WINNT) && !defined(_CHICAGO_)
#define  _WIN32_WINNT   0x0602
#endif

如果您不想要默认的 selection,则需要通过适当定义 _WIN32_WINNT and/or 相关宏来配置目标平台版本。您可以使用项目设置或 makefile 中的 /D 编译器选项来执行此操作,或者您可以在源文件或包含 SDK header 之前包含在所有内容中的公共 header 中定义它s.

/D _WIN32_WINNT=0x0601 之类的内容可能适合您(0x0601 对应 Win7)。

这里的根本问题是您使用 _WIN32_WINNT 来确定要采用的代码路径,而不是实际设置它。这适用于 Windows Store/Phone/UWP 构建(如 Chuck 的回答中所述)但不适用于桌面构建。

对于桌面构建,正确的解决方案是使用最小公分母或在 运行 时选择代码路径。否则,您需要为不同版本的 Windows.

构建不同的桌面可执行文件

在这种特殊情况下,由于您没有使用 GetOverlappedResultEx 提供的附加功能,因此坚持使用 GetOverlappedResult 进行桌面构建可能更明智。您可以使用 How to: Use Existing C++ Code in a Universal Windows Platform App 中列出的宏定义来确定您是否正在为桌面构建。

GetOverlappedResult API 似乎并未被桌面应用程序弃用,因此如果您将 GetOverlappedResultEx 用于 UWP/Store/Phone 构建,则不应进行脏编译. (?)


据我所知,没有真正优雅的方法可以使构建输出取决于构建工具 运行 正在使用的 Windows 版本 - 你会注意到 none 的任一预定义宏 the compiler or the build environment 允许您确定操作系统版本。

但是,如果这确实是适合的罕见情况之一,您可以通过使用预构建事件或自定义构建步骤来做到这一点 运行 一个检查操作系统版本并构建您的代码可以包含的头文件的程序。如果其他 Windows 开发人员将构建您的库,我不会推荐它,因为他们不会期望这种行为;但如果你需要它是可用的。

“此平台”隐含在平台工具集中。 VS 2012 使用默认为 _WIN32_WINNT=0x0602 (Windows 8) 的 Windows 8.0 SDK。 VS 2013 / 2015 使用默认为 _WIN32_WINNT=0x0603 (Windows 8.1) 的 Windows 8.1 SDK。如果你使用 VS 2015 和 Windows 10 SDK,它默认为 _WIN32_WINNT=0x0A00 (Windows 10).

这主要是为了 Windows Store / UWP 应用程序的利益,这些应用程序需要 _WIN32_WINNT 的最新值才能正确构建。对于 Windows 10,_WIN32_WINNT 值不会更新 build-to-build,因此您配置要使用的 side-by-side Windows 10 SDK。有关详细信息,请参阅 Visual C++ Team blog

对于 Windows 桌面应用程序(又名经典 Win32),您应该在构建配置中明确设置您支持的 OS 版本。通常这是在 pch.h 或其他全局 header 中完成的,但也可以通过构建 command-line/makefile/vcxproj:

完成
#include <WinSDKVer.h>
#define _WIN32_WINNT 0x0600 // Windows Vista SP2 or later
#include <SDKDDKVer.h>

#include <WinSDKVer.h>
#define _WIN32_WINNT 0x0601 // Windows 7 or later
#include <SDKDDKVer.h>

Windows 8.x SDK 可以针对 Windows Vista SP2、Windows 7、Windows 8.0、Windows 8.1 或更高版本。

If you need support for Windows XP SP3 or Windows Server 2003 SP2, then you have to use an alternative Platform Toolset setting which selects the Windows 7.1A SDK. See this post for some notes about the differences.

Using the Windows Headers and this blog article

Keep in mind that VS 2015 itself does not support targeting Windows 7 RTM, only Windows 7 Service Pack 1.

对于 GetOverlappedResultEx 的特定情况,我在我的代码中使用了以下模式,支持构建 down-level Windows 7 以及 UWP/Windows Store。

    HANDLE hEvent = CreateEventEx( nullptr, nullptr,
        CREATE_EVENT_MANUAL_RESET, EVENT_MODIFY_STATE | SYNCHRONIZE );

...

    // Read and verify header
    OVERLAPPED request = {};
    request.hEvent = hEvent;

    bool wait = false;
    if( !ReadFile( hFile, ..., &request ) )
    {
        DWORD error = GetLastError();
        if ( error != ERROR_IO_PENDING )
            return HRESULT_FROM_WIN32( error );
        wait = true;
    }

    DWORD bytes;
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
    BOOL result = GetOverlappedResultEx( hFile, &request, &bytes, INFINITE, FALSE );
#else
    if ( wait  )
        (void)WaitForSingleObject( hEvent, INFINITE );

    BOOL result = GetOverlappedResult( hFile, &request, &bytes, FALSE );
#endif

然后我为每个支持的平台提供多个构建的静态库。请记住,WACK 工具会检查您的 EXE/DLL 导入和导出表,并将标记任何不受支持的 API。因此,您无法选择运行时,并且必须仅引用“Windows 8”构建中受支持的 APIs。

问题是 (1) Microsoft 营销文献指出 Windows 7 上的 VS2012 是受支持的配置;但是 (2) Microsoft 工程师在 MSDN Community Support 上声明其不受支持的配置。所需的最低平台是 Windows 8.