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

3
我正在使用Visual Studio 2012在Windows 7 x64上进行一些测试。看起来微软的工具链将_WIN32_WINNT设置为0x602(_WIN32_WINNT_WIN8)。运行我们的测试程序会导致无法在动态链接库KERNEL32.dll中找到GetOverlappedResultEx过程入口点

enter image description here

我有两个问题。首先,出于好奇心,微软为什么要将_WIN32_WINNT设置为执行环境无效的值?我可以理解如果用户需要这样做,但是微软这样做会破坏一些东西(详见参考文献)。
其次,我们如何将WINVER_WIN32_WINNT设置为符号“此平台”?在这种情况下,“此平台”是Windows 7。当我在Windows Vista上测试时,它将是另一个平台。当我在Windows 8上测试时,它将是另一个平台。当我在Windows Phone和Windows Store的ARM开发人员提示下测试时,它将是另一个平台。
问题很容易复制。以下是步骤。我想任何具有良好测试环境的人都已经完成了前八个步骤。
  1. 搭建一个Windows 7 x64机器
  2. 完全打补丁Windows 7机器
  3. 安装Visual Studio 2008
  4. 完全打补丁Visual Studio 2008
  5. 安装Visual Studio 2010
  6. 完全打补丁Visual Studio 2010
  7. 安装Visual Studio 2012
  8. 完全打补丁Visual Studio 2012

然后:

  1. 在VS2008下创建一个空的“Hello World”项目
  2. 删除除了hello_world.cpp之外的所有内容(这应该留下1个解决方案文件,1个项目文件和1个源文件)
  3. 将其转换为VS2010
  4. 使用VS2012打开
我可以发布MCVE,它是一个空的源文件,以安抚一些人。但这似乎是浪费时间,因为问题出在工具链上而不是源文件上。一个空的“main”对于这个问题真的很关键吗?无论文件中写了什么,错误的“WINVER”和“_WIN32_WINNT”都会被设置。
我知道错误的起源。我们的代码最近进行了更改,以更好地支持Windows 8、Phone 8、Store 8、Server 2012、Windows 10、Phone 10、Store 10和Windows通用平台。更改如下所示:
#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问题:什么是WINVER?,但它并没有讨论如何将其设置为“此平台”。
这是Microsoft关于这个主题的文档:使用Windows头文件修改WINVER和_WIN32_WINNT。具有讽刺意味的是,它们并没有真正讨论问题或Windows 10、Windows Phone 10、Windows Store 10或Windows Universal Platform。
顺便说一下,GCC有一个准类似的-march=native选项,基本上提供了“此平台”的功能。

对于投票关闭者和投票反对者:也许您错过了这一点:“看起来微软的工具链将_WIN32_WINNT设置为0x602_WIN32_WINNT_WIN8)”。我们没有设置WINVER_WIN32_WINNT - 微软的工具链正在设置它,我不清楚如何向您提供除了我们不这样做的声明之外的任何其他内容。我只能提供证据表明我们对微软错误的值做出反应。 - jww
那是胡说八道。你正在运行命令和构建代码。展示它们/它。 - nobody
@Andrew - 你似乎没有你自己认为的那么精通。否则,我们正在经历的问题可能对你来说很老套,我猜你早就跳入回答了。 - jww
如果您的目标是支持UWP而不破坏桌面端,那么这篇文章是否有帮助? - Harry Johnston
另请参见http://stackoverflow.com/a/5440567/886887 - Harry Johnston
显示剩余5条评论
4个回答

7
"这个平台"是Platform Toolset中的隐式内容。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值不会在构建到构建时更新,因此您需要配置使用哪个并行Windows 10 SDK。有关详细信息,请参见Visual C++团队博客
对于Windows桌面应用程序(也称为经典Win32),您应该将支持的操作系统版本明确设置为构建配置的一部分。通常在pch.h或其他全局头文件中完成此操作,但也可以通过构建命令行/ 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或更高版本进行目标设置。

如果您需要支持Windows XP SP3或Windows Server 2003 SP2,则必须使用备用的Platform Toolset设置,选择Windows 7.1A SDK。有关差异的一些说明,请参见此文章

请参见使用Windows头文件此博客文章

请注意,VS 2015本身不支持针对Windows 7 RTM进行目标设置,只支持Windows 7 Service Pack 1。

对于GetOverlappedResultEx的特定情况,在我的代码中,我使用以下模式来支持构建下级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”构建中,您不能运行时选择并且必须仅引用受支持的API。


1
他不必为每个Windows版本重新配置项目,但是他必须为Win32经典桌面带有向下级别支持的版本、UWP版本、Windows 8商店版本、Windows 8.1商店版本、Windows phone 8版本和Windows phone 8.1版本创建不同的构建。一般来说,你不能构建一个单一的二进制文件,可以在所有这些情况下工作。顺便说一句,在这一点上,支持Windows 8.0商店或Windows phone 8.0或VS 2012通常没有太多理由。 - Chuck Walbourn

4
如果您没有明确提供目标平台版本,Windows SDK 将选择默认版本(有关详细信息,请参见 SDK 的 sdkddkver.h 文件)。
例如,Windows 8.0 SDK sdkddkver.h 文件包含以下片段:
#if !defined(_WIN32_WINNT) && !defined(_CHICAGO_)
#define  _WIN32_WINNT   0x0602
#endif

如果你不想使用默认选择,你需要通过定义_WIN32_WINNT和/或相关宏来配置目标平台版本。你可以在项目设置或makefile中使用编译器选项/D进行配置 ,或者在源文件或包含SDK头文件之前的公共头文件中定义它。例如,/D _WIN32_WINNT=0x0601 可能适合你(0x0601 对应Win7)。

谢谢Michael。我们如何将其设置为符号化的“此平台”。将其设置为Windows 8对于Windows 7构建和执行环境是无用和愚蠢的。这会导致问题。 - jww
Windows SDK/Microsoft 工具链没有“此平台”的概念。每个工具链都有一个默认目标(对于“Windows SDK”来说是命名平台,对于 VS 工具链通常是当前的平台)。如果您想要针对特定操作系统进行目标设置,则必须使用默认针对该操作系统的 SDK 或手动指定。-march 完全不相关 - 它与 CPU 功能有关,而不是操作系统版本。 - nobody
иҝҮеҺ»пјҢжҲ‘дёҚеҫ—дёҚзј–иҫ‘WindowsеӨҙж–Ү件д»Ҙж¶ҲйҷӨе®ғ们еңЁ/W4е’Ң/Wallеј•е…Ҙзҡ„иӯҰе‘ҠпјҲиҝҳиҜ·еҸӮйҳ…й»ҳи®Өжғ…еҶөдёӢе…ій—ӯзҡ„зј–иҜ‘еҷЁиӯҰе‘ҠпјүгҖӮжҲ‘жғіжҲ‘еҸҜиғҪдјҡд»ҘеҗҢж ·зҡ„ж–№ејҸдҝ®еӨҚиҝҷдәӣеӨҙж–Ү件гҖӮ - jww
2
也许我对您想要做什么有所误解,但这似乎是一种极端的方式来修复可以通过项目/Makefile设置修复的问题。 - Michael Burr
@MichaelBurr - "那似乎是一种极端的修复方式,可以通过项目/Makefile设置来修复" - 请原谅我的无知...我们该如何解决呢?如果我们为Windows 7设置***/D _WIN32_WINNT=0x0601***,那么Down-level和up-level客户机都会出现问题。像XP这样的Down-level客户端将没有某些API。up-level客户端将错过一些功能。尤其重要的是让up-level客户满意,因为Microsoft在Windows电话8和Windows Store 8中缺少关键的API。 - jww
显示剩余5条评论

2
这里的根本问题是您正在使用_WIN32_WINNT来确定要采取哪个代码路径,但实际上并没有设置它。这适用于Windows Store/Phone/UWP版本(如Chuck的答案所述),但不适用于桌面版本。
对于桌面版本,正确的解决方案是要么使用最低公共分母,要么在运行时选择代码路径。否则,您需要为不同版本的Windows构建不同的桌面可执行文件。
在这种特定情况下,由于您没有使用GetOverlappedResultEx提供的额外功能,因此在桌面版本中坚持使用GetOverlappedResult可能更明智。您可以使用如何:在通用 Windows 平台应用中使用现有的 C++ 代码中列出的宏定义来确定是否正在为桌面版本构建。 GetOverlappedResult API似乎并未被桌面应用程序弃用,因此只要您在UWP/Store/Phone版本中使用GetOverlappedResultEx,就不应该得到错误编译。 (?)
据我所知,没有一种真正优雅的方法使构建输出依赖于构建工具运行的Windows版本 - 请注意,无论是编译器还是构建环境的预定义宏都不允许您确定操作系统版本。
然而,如果这确实是那些适当情况之一,您可以通过使用预构建事件或自定义构建步骤来运行检查操作系统版本并构建一个头文件的程序来实现。您的代码可以包含此文件。如果其他Windows开发人员将构建您的库,则不建议使用此方法,因为他们将不会预期此行为;但如果需要,它是可用的。

实际上,这是完全合法使用_WIN32_WINNT预处理器定义的方式。显式链接具有运行时开销,并且您无论如何都不能在UWP中使用显式链接。请参见此博客文章 - Chuck Walbourn
@ChuckWalbourn: 共享库不是一种特殊情况吗? - Harry Johnston
有一些值得检查并设置软故障回退的功能和API,但它们具有运行时成本。这种低级Win32 API可能不是它。 - Chuck Walbourn
@ChuckWalbourn:这是一个很好的观点;在这种特定情况下,他甚至没有使用新API提供的额外功能。我已经相应地进行了重写。 - Harry Johnston

-1

问题在于,(1) 微软的营销文献中说明 VS2012 在 Windows 7 上是支持的配置;但是 (2) 微软的工程师表示它不是一个受支持的配置,可以在 MSDN 社区支持 中查看。所需的最低平台是 Windows 8。


1
“社区支持者”并非工程师,他们并不总是知道自己在谈论什么。如果你按照那个回答中的链接,它明确表示 Windows 7 被支持的。(当然,“被支持”只意味着它按设计工作,而不是按你想要的方式工作。) - Harry Johnston

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接