web-dev-qa-db-ja.com

警告C4996: 'GetVersionExW':非推奨と宣言されました

私はWin 8.1でVS 2013に取り組んでいます。この警告を解決する方法は?

31
yemans

基本的な質問は、「そもそもなぜGetVersionExWを呼び出すのですか?」です。その質問に対する答えによって、代わりに何をすべきかが決まります。

非推奨の警告は、Windows 8.1で開始されたappcompatの動作の変更について開発者に注意を促すためのものです。 WindowsおよびWindows Server互換性クックブック:Windows 8、Windows 8.1、およびWindows Server 2012 を参照してください。 要するに、その関数はデフォルトで返すと思われるものを返しません。

歴史的に、不適切に作成されたOSバージョンチェックは、Windows OSアップグレードの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コンパイラを既に使用している場合は便利です。ゲームについては、記事があります バージョン番号には何が記載されていますか? これはVerifyVersionInfoを使用して、ゲームの展開に必要な妥当なチェックを行います。

_v120_xp_プラットフォームツールセットでVS 2013を使用してWindows XPをターゲットにしている場合、実際にはWindows 7.1A SDKを使用しているため、_#include <VersionHelpers.h>_は機能しません。もちろん、VerifyVersionInfoを直接使用できます。

GetVersionExWのその他の主な用途は、診断ログとテレメトリです。この場合、1つのオプションは、そのAPIを引き続き使用し、アプリケーションに適切なマニフェストエントリがあることを確認して、合理的に正確な結果を確保することです。これを達成するためにここで何をするかの詳細については、 マニフェストマッドネス を参照してください。留意すべき主なことは、コードを定期的に更新しない限り、将来のバージョンのOSで完全に正確な情報の取得をやめることです。

一般的なベストプラクティスとしてGetVersionExの結果を気にするかどうかに関係なく、埋め込みマニフェストに_<compatibility>_セクションを配置することをお勧めします。これにより、アプリが最初にテストされた方法を知ることに基づいて、OSが将来のappcompat修正を自動的に適用できます。

診断ログの場合、もう少し堅牢なもう1つの方法は、GetFileVersionInfoWを使用して_kernel32.dll_のようなシステムDLLからバージョン番号を取得することです。このアプローチには大きな重要性があります:この方法で取得したファイルバージョンに基づいて、解析、比較、コードの仮定を試みないでください。どこかに書いてください。それ以外の場合は、VerifyVersionInfoでより適切に解決される同じ不良OSバージョンチェック問題を再作成するリスクがあります。このオプションは、Windowsストアアプリ、Windows Phoneアプリなどでは使用できませんが、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のような「このライドチェックに乗るにはこれだけ高い必要があります」を設定する必要がありますが、実行時にはLoadLibraryまたはLoadLibaryExによる明示的なリンクを使用してエラーまたは_MFPLAT.DLL_が見つからない場合はフォールバックを使用します。

明示的なリンクは、Windowsストアアプリのオプションではありません。 Windows 8.xは、スタブ_MFPLAT.DLL_とMFStartUpがE_NOTIMPLを返すことにより、この特定の問題を解決します。 「[Windows Media] Cheeseを移動した人」を参照してください。

別の例:アプリケーションでDirect3D 11.2が使用可能であれば使用し、DirectX 11.0を使用する場合は、おそらく D3D11InstallHelper を使用して、展開に_IsWindowsVistaSP2OrGreater_最小バーを設定します。その後、実行時にDirectX 11.0デバイスを作成し、失敗した場合はエラーを報告します。 _ID3D11Device_を取得した場合、_ID3D11Device2_のQueryInterfaceになります。成功した場合は、DirectX 11.2をサポートするOSを使用していることを意味します。 Direct3D 11 Create Deviceの構造 を参照してください。

この仮想のDirect3DアプリケーションがWindows XPをサポートしている場合、_IsWindowsXPSP2OrGreater_または_IsWindowsXPSP3OrGreater_の展開バーを使用し、実行時に明示的なリンクを使用して_D3D11.DLL_を見つけようとします。存在しない場合は、Direct3D 9の使用に戻ります。最小バーを設定しているため、DirectX 9.0c以降が常に存在することがわかります。

ここで重要な点は、ほとんどの場合、GetVersionExを使用しないことです。

Windows 10では、VerifyVersionInfoおよびkernel32.libのGetFileVersionInfoを介したファイルバージョンスタンプの取得は、GetVersionExと同じマニフェストベースの動作に従うことに注意してください(つまり、 Windows 10のマニフェストGUIDがなければ、OSバージョンが10.0ではなく6.2であるかのように結果を返します。

Windows 10のユニバーサルWindowsアプリの場合、新しいWinRT API AnalyticsInfo を使用して、診断ログとテレメトリのバージョンスタンプ文字列を取得できます。

51
Chuck Walbourn

GetVersionExは非推奨と宣言されましたが、Windows 8.1およびWindows 10との互換性を宣言する適切な互換性マニフェストを投げると、GetVersionExは正しいバージョン番号を返します。 GetVersionExを使用してWindows 8以降を検出します。Windows8はWindowsの最後のバージョンであり、適切なWindowsバージョンを返すためにマニフェストを必要としないため、APIがWindows 6.2、6.3、6.4を返すかどうかに関係なくコードは正常に動作します初期のWindows 10プレビュー)、または10.0。

とはいえ、MicrosoftはこのAPIの不適切な使用のために、この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を返します。1行追加するとこれが修正されます。

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以上であればXPよりも大きいことを知っているため、正しく機能します。

3
William

とにかくこの警告を無効にしてGetVersionExを使用できます:

#pragma warning(disable : 4996)
3
user1438233