警告C4996:'GetVersionExW':声明已弃用

时间:2014-03-10 15:05:27

标签: visual-c++ visual-studio-2013 wdk

我正在Win 8.1中使用VS 2013。 如何解决这个警告?

4 个答案:

答案 0 :(得分:46)

基本问题是“你为什么首先打电话给GetVersionExW?”这个问题的答案决定了你应该做什么。

弃用警告可以让开发人员了解在Windows 8.1中启动的appcompat行为更改。见Windows and Windows Server compatibility cookbook: Windows 8, Windows 8.1, and Windows Server 2012简而言之,该函数不会返回您认为默认返回的内容。

从历史上看,写得不好的操作系统版本检查是Windows操作系统升级的appcompat错误的主要来源。尝试缓解此问题有许多不同的方法(AppVerifier版本谎言,VerifyVersionInfo API等),这是迄今为止最具侵略性的方法。

评论中提到的VersionHelpers.h位于Visual Studio 2013附带的Windows 8.1 SDK中。它们不是新的API;它们只是利用Windows 2000中引入的VerifyVersionInfo API的实用程序代码。这些函数用于执行“你必须这么高才能骑这种方式”样式检查,这是最常见的版本检查类经常写得很糟糕。代码非常简单。例如,IsWindowsVistaSP2OrGreater测试是:

VERSIONHELPERAPI
IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor)
{
    OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 };
    DWORDLONG        const dwlConditionMask = VerSetConditionMask(
        VerSetConditionMask(
        VerSetConditionMask(
            0, VER_MAJORVERSION, VER_GREATER_EQUAL),
               VER_MINORVERSION, VER_GREATER_EQUAL),
               VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);

    osvi.dwMajorVersion = wMajorVersion;
    osvi.dwMinorVersion = wMinorVersion;
    osvi.wServicePackMajor = wServicePackMajor;

    return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;
}

VERSIONHELPERAPI
IsWindowsVistaSP2OrGreater()
{
    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 2);
}

您不需要使用VersionHelpers.h,因为您可以自己执行此类代码,但如果您已经在使用VS 2013编译器,则它们很方便。对于游戏,我有一篇文章What's in a version number?,它使用VerifyVersionInfo来进行游戏部署所需的合理检查。

  

请注意,如果您使用带有v120_xp平台工具集的VS 2013来定位Windows XP,那么您实际上将使用Windows 7.1A SDK并且#include <VersionHelpers.h>将无效。您当然可以直接使用VerifyVersionInfo

GetVersionExW的另一个主要用途是诊断日志和遥测。在这种情况下,一种选择是继续使用该API,并确保您的应用程序中有正确的清单条目,以确保合理准确的结果。有关您在此处执行此操作的详细信息,请参阅Manifest Madness。要记住的主要事情是,除非您定期更新代码,否则最终将无法在未来版本的操作系统中获得完全准确的信息。

  

请注意,建议您将<compatibility>部分放在嵌入式清单中,无论您是否关心GetVersionEx作为一般最佳做法的结果。这允许操作系统根据最初测试应用程序的方式自动应用未来的appcompat修复程序。

对于诊断日志,另一种可能更健壮的方法是使用kernel32.dllGetFileVersionInfoW等系统DLL中获取版本号。这种方法有一个重要的问题:不要尝试解析,进行比较,或根据您通过这种方式获得的文件版本进行代码假设;只是把它写在某个地方。否则,您可能会重新创建使用VerifyVersionInfo更好地解决的错误操作系统版本检查问题。此选项不适用于Windows应用商店应用,Windows手机应用等,但应适用于Win32桌面应用。

#include <windows.h>
#include <stdint.h>
#include <memory>

#pragma comment(lib, "version.lib" )

bool GetOSVersionString( WCHAR* version, size_t maxlen )
{
    WCHAR path[ _MAX_PATH ];
    if ( !GetSystemDirectoryW( path, _MAX_PATH ) )
        return false;

    wcscat_s( path, L"\\kernel32.dll" );

    //
    // Based on example code from this article
    // http://support.microsoft.com/kb/167597
    //

    DWORD handle;
#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
    DWORD len = GetFileVersionInfoSizeExW( FILE_VER_GET_NEUTRAL, path, &handle );
#else
    DWORD len = GetFileVersionInfoSizeW( path, &handle );
#endif
    if ( !len )
        return false;

    std::unique_ptr<uint8_t> buff( new (std::nothrow) uint8_t[ len ] );
    if ( !buff )
        return false;

#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
    if ( !GetFileVersionInfoExW( FILE_VER_GET_NEUTRAL, path, 0, len, buff.get() ) )
#else
    if ( !GetFileVersionInfoW( path, 0, len, buff.get() ) )
#endif
        return false;

    VS_FIXEDFILEINFO *vInfo = nullptr;
    UINT infoSize;

    if ( !VerQueryValueW( buff.get(), L"\\", reinterpret_cast<LPVOID*>( &vInfo ), &infoSize ) )
        return false;

    if ( !infoSize )
        return false;

    swprintf_s( version, maxlen, L"%u.%u.%u.%u",
                HIWORD( vInfo->dwFileVersionMS ),
                LOWORD(vInfo->dwFileVersionMS),
                HIWORD(vInfo->dwFileVersionLS),
                LOWORD(vInfo->dwFileVersionLS) );

    return true;
}

如果还有其他原因你打电话给GetVersionExW,你可能不应该打电话给它。检查可能缺少的组件不应与版本检查绑定。例如,如果您的应用程序需要Media Foundation,您应该像VersionHelpers.h IsWindowsVistaOrGreater那样设置一个“你必须这么高才能进行这次乘坐检查”,但是在运行时你应该通过{{{ 1}}或LoadLibrary报告错误或使用后备如果找不到LoadLibaryEx

  

显式链接不是Windows应用商店应用的选项。 Windows 8.x解决了这个问题   使用存根MFPLAT.DLLMFPLAT.DLL将返回E_NOTIMPL的特殊问题。   见"Who moved my [Windows Media] Cheese"?

另一个例子:如果您的应用程序想要使用Direct3D 11.2(如果可用)并且使用DirectX 11.0,您可以使用MFStartUp最小条设置IsWindowsVistaSP2OrGreater。然后在运行时,您将创建DirectX 11.0设备,如果它失败,您将报告错误。如果您获得ID3D11Device,则QueryInterface获得ID3D11Device2,如果成功则意味着您使用的是支持DirectX 11.2的操作系统。请参阅D3D11InstallHelper

如果这个假设的Direct3D应用程序支持Windows XP,您可以使用IsWindowsXPSP2OrGreaterIsWindowsXPSP3OrGreater的部署栏,然后在运行时使用显式链接尝试查找D3D11.DLL 。如果它不存在,你将回归使用Direct3D 9 - 因为我们设置了最小条,我们知道DirectX 9.0c或更高版本始终存在。

这里的关键点是,在大多数情况下,您不应该使用GetVersionEx

  

请注意,对于 Windows 10 VerifyVersionInfo并通过GetFileVersionInfo获取kernel32.lib的文件版本标记现在受到与{{1}相同的基于清单的行为的约束(即,如果没有Windows 10的清单GUID,它会返回结果,就好像操作系统版本是6.2而不是10.0)。

     

对于Windows 10上的通用Windows应用程序,您可以使用新的WinRT API Anatomy of Direct3D 11 Create Device来获取诊断日志和遥测的版本标记字符串。

答案 1 :(得分:3)

虽然声明了GetVersionEx已过时,但如果您抛出一个声明与Windows 8.1和Windows 10兼容的适当兼容性清单,则GetVersionEx将返回正确的版本号。我使用GetVersionEx来检测Windows 8或更高版本,并且由于Windows 8是Windows的最新版本,不需要清单即可返回正确的Windows版本,因此无论API是否返回Windows 6.2、6.3、6.4(对于Windows 10早期预览)或10.0。

话虽如此,Microsoft弃用了该API的部分原因是使用不当。以这种尝试检测Windows XP或更高版本的尝试为例:

BOOL IsXPOrGreater;
OSVERSIONINFO osver;
osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osver);
if((osver.dwMajorVersion >= 5) && (osver.dwMinorVersion >=1) IsXPOrGreater = TRUE;
else IsXPOrGreater = FALSE;

此示例在Windows XP,Server 2003、7、8和8.1上将返回TRUE,但在Windows Vista或10上将返回FALSE。添加一行将解决此问题:

BOOL IsXPOrGreater;
OSVERSIONINFO osver;
osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osver);
if((osver.dwMajorVersion >= 5) && (osver.dwMinorVersion >=1) IsXPOrGreater = TRUE;
else if(osver.dwMajorVersion >= 6) IsXPOrGreater = TRUE;
else IsXPOrGreater = FALSE;

此示例将正常运行,因为它知道主版本为6或大于6或大于XP。

答案 2 :(得分:2)

你可以通过添加:

来禁用此警告并使用GetVersionEx
#pragma warning(disable : 4996)

答案 3 :(得分:0)

如果您查看sysinfoapi.h,您会发现以下内容

NOT_BUILD_WINDOWS_DEPRECATE
WINBASEAPI
__drv_preferredFunction("IsWindows*", "Deprecated. Use VerifyVersionInfo* or IsWindows* macros from VersionHelpers.")
BOOL
WINAPI
GetVersionExW(
    _Inout_ LPOSVERSIONINFOW lpVersionInformation
    );

#ifdef UNICODE
#define GetVersionEx  GetVersionExW
#else
#define GetVersionEx  GetVersionExA
#endif // !UNICODE

如果您在同一个头文件中跟踪 NOT_BUILD_WINDOWS_DEPRECATE,您会发现

#if defined(FKG_FORCED_USAGE) || defined(WINPHONE) || defined(BUILD_WINDOWS)
# define NOT_BUILD_WINDOWS_DEPRECATE
#else
# define NOT_BUILD_WINDOWS_DEPRECATE __declspec(deprecated)
#endif

如果您定义了以下其中一项,以上代码的意思很简单 BUILD_WINDOWSWINPHONEFKG_FORCED_USAGE 它不会显示已弃用的错误 所以在你的代码中你可以做这样的事情:

#define BUILD_WINDOWS#include <windows.h> 之前 这应该会为您解决。