2014. 6. 3. 11:57

Windows 8.1 판단시 VersionHelpers.h의 IsWindows8Point1OrGreater를 Visual Studio 2008에서 사용. (GetVersionEx() 사용 금지)

GetVersionEx(...) MSDN[링크]에서,

GetVersionEx function

[GetVersionEx may be altered or unavailable for releases after Windows 8.1. Instead, use the Version Helper APIs]

...

와 같이 8.1 판단을 위해서는 Version Helper API를 사용해라고 표시됩니다. 즉, 되도록이면, Windows 8.1 이후부터는 GetVersionEx라는 Legacy API를 자제해서 사용하라는 뜻입니다. 물론, GetVersionEx를 통해 8.1을 판단할 수 있다고 하나, 사용하는 모듈에서 특정 manifest를 추가해야 하는등 제약 조건이 따릅니다. 예를 들어, Common Source File에서 GetVersionEx를 사용한다면, 사용하는 모듈에서는 manifest를 꼭 추가해야 한다는 뜻입니다. 이러한 제약 조건은 향후 제품 개발의 "폭탄"이 될 수 있음을 유의해야 합니다. "누구"는 해당 제약을 알고 있고, "누구"는 모르니깐요. 그리고, MSDN의 말 처럼, 향후 Windows 9, 10, 11, ... 때마다 신견을 써야 하는건 마찮가지 입니다. 왜냐하면, Windows 11 부터는 아예 GetVersionEx 사용을 금지시킬 수 있으니깐요. (물론, 몇몇 Article에 따르면, Manifest를 추가하면 GetVersionEx의 성능이 월등히 좋아진다고 하지만, 개발의 애매함을 제거하는것이 성능 향항보다 일반적으로 중요하다고 판단됩니다.)


문제는 Version Helper API인데,...

IsWindows8Point1OrGreater(...) MSDN[링크]에서,


IsWindows8Point1OrGreater function

Indicates if the current OS version matches, or is greater than, the Windows 8.1 version.

...

Requirements

Minimum supported client

Windows 2000 Professional [desktop apps only]

Minimum supported server

Windows 2000 Server [desktop apps only]

Header

VersionHelpers.h (include Windows.h)

Library

Kernel32.lib;
Ntdll.lib

와 같이 나옵니다.

즉, IsWindows8Point1OrGreater 함수는 Windows 2000에서도 사용할 수 있는데, kernel32.dll에 Export된 함수는 아닌듯 합니다.


그런데, Visual Studio 2008에서, IsWindows8Point1OrGreater를 사용하면, 컴파일 오류가 발생하며, VersionHelpers.h 또한 존재하지 않습니다. 해당 header 파일은 Windows 8.1 SDK에 포함되며, 다음과 같은 내용으로 되어 있습니다.

/******************************************************************
*                                                                 *
*  VersionHelpers.h -- This module defines helper functions to    *
*                      promote version check with proper          *
*                      comparisons.                               *
*                                                                 *
*  Copyright (c) Microsoft Corp.  All rights reserved.            *
*                                                                 *
******************************************************************/
#ifndef _versionhelpers_H_INCLUDED_
#define _versionhelpers_H_INCLUDED_

#include <winapifamily.h>

#ifdef _MSC_VER
#pragma once
#endif  // _MSC_VER

#pragma region Application Family
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)

#include <specstrings.h>    // for _In_, etc.

#if !defined(__midl) && !defined(SORTPP_PASS)

#if (NTDDI_VERSION >= NTDDI_WINXP)

#ifdef __cplusplus

#define VERSIONHELPERAPI inline bool

#else  // __cplusplus

#define VERSIONHELPERAPI FORCEINLINE BOOL

#endif // __cplusplus

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
IsWindowsXPOrGreater()
{
    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 0);
}

VERSIONHELPERAPI
IsWindowsXPSP1OrGreater()
{
    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 1);
}

VERSIONHELPERAPI
IsWindowsXPSP2OrGreater()
{
    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 2);
}

VERSIONHELPERAPI
IsWindowsXPSP3OrGreater()
{
    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINXP), LOBYTE(_WIN32_WINNT_WINXP), 3);
}

VERSIONHELPERAPI
IsWindowsVistaOrGreater()
{
    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 0);
}

VERSIONHELPERAPI
IsWindowsVistaSP1OrGreater()
{
    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_VISTA), LOBYTE(_WIN32_WINNT_VISTA), 1);
}

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

VERSIONHELPERAPI
IsWindows7OrGreater()
{
    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 0);
}

VERSIONHELPERAPI
IsWindows7SP1OrGreater()
{
    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN7), LOBYTE(_WIN32_WINNT_WIN7), 1);
}

VERSIONHELPERAPI
IsWindows8OrGreater()
{
    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0);
}

VERSIONHELPERAPI
IsWindows8Point1OrGreater()
{
    return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WINBLUE), LOBYTE(_WIN32_WINNT_WINBLUE), 0);
}

VERSIONHELPERAPI
IsWindowsServer()
{
    OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0, 0, VER_NT_WORKSTATION };
    DWORDLONG        const dwlConditionMask = VerSetConditionMask( 0, VER_PRODUCT_TYPE, VER_EQUAL );

    return !VerifyVersionInfoW(&osvi, VER_PRODUCT_TYPE, dwlConditionMask);
}

#endif // NTDDI_VERSION

#endif // defined(__midl)

#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) */
#pragma endregion

#endif // _VERSIONHELPERS_H_INCLUDED_

(위 파일은 C:\Program Files\Windows Kits\8.1의 하부에 포함됩니다.)

우선, <winapifamily.h>나 <specstrings.h> 등이 익숙하지 않습니다. 그리고, 주요한 Win32 API는 VerSetConditionMast와 VerifyVersionInfoW 입니다. 그러니, IsWindows8Point1OrGreater는 Windows 2000에서도 실행 가능한 함수가 됩니다. 이러한 이유로, 향후 Windows 9, 10, 11, ... 판단도 위와 같이 구성될것으로 예상됩니다.


Visual Studio 2008에서 사용은 위 함수를 그대로 쓰는 것으로 해결할 수 있습니다. 단, 향후 Windows 8.1 SDK가 누군가에 의해 사용된다면, 중복 선언으로 오류가 발생할 수 있으니, 위 함수와 정의들을 적절하게 이름을 변경하여 사용합니다. 대략 다음과 같이 사용 가능합니다.


// win8_1.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <conio.h>
#include <windows.h>

//////////////////////////////////////////////////////////////////////////
// http://msdn.microsoft.com/en-us/windows/desktop/bg162891.aspx
//
// 에서 win8.1 sdk를 설치한다.
//
// C:\Program Files\Windows Kits\8.1
//
// 의 Include에 각각의 include 파일이 들어간다.
//
// \Um\VersionHelpers.h
// 에 IsWindowsVersionOrGreater(...), IsWindowsXXXX(...)가 포함된다.
//
// \Shared\sdkddkver.h
// 에 _WIN32_WINNT_VISTA, ..., _WIN32_WINNT_WINBLUE가 포함된다.
//
// 향후, include나 define이 섞이면 빌드 오류가 발생할 수 있으므로,
// 이름을 약간 변경(DEF_*)하여, 오류를 회피하도록 한다.
//////////////////////////////////////////////////////////////////////////

#define _DEF_WIN32_WINNT_NT4                    0x0400
#define _DEF_WIN32_WINNT_WIN2K                  0x0500
#define _DEF_WIN32_WINNT_WINXP                  0x0501
#define _DEF_WIN32_WINNT_WS03                   0x0502
#define _DEF_WIN32_WINNT_WIN6                   0x0600
#define _DEF_WIN32_WINNT_VISTA                  0x0600
#define _DEF_WIN32_WINNT_WS08                   0x0600
#define _DEF_WIN32_WINNT_LONGHORN               0x0600
#define _DEF_WIN32_WINNT_WIN7                   0x0601
#define _DEF_WIN32_WINNT_WIN8                   0x0602
#define _DEF_WIN32_WINNT_WINBLUE                0x0603

BOOL DEF_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;
}

BOOL DEF_IsWindowsXPOrGreater()
{
	return DEF_IsWindowsVersionOrGreater(HIBYTE(_DEF_WIN32_WINNT_WINXP), LOBYTE(_DEF_WIN32_WINNT_WINXP), 0);
}

BOOL DEF_IsWindowsXPSP1OrGreater()
{
	return DEF_IsWindowsVersionOrGreater(HIBYTE(_DEF_WIN32_WINNT_WINXP), LOBYTE(_DEF_WIN32_WINNT_WINXP), 1);
}

BOOL DEF_IsWindowsXPSP2OrGreater()
{
	return DEF_IsWindowsVersionOrGreater(HIBYTE(_DEF_WIN32_WINNT_WINXP), LOBYTE(_DEF_WIN32_WINNT_WINXP), 2);
}

BOOL DEF_IsWindowsXPSP3OrGreater()
{
	return DEF_IsWindowsVersionOrGreater(HIBYTE(_DEF_WIN32_WINNT_WINXP), LOBYTE(_DEF_WIN32_WINNT_WINXP), 3);
}

BOOL DEF_IsWindowsVistaOrGreater()
{
	return DEF_IsWindowsVersionOrGreater(HIBYTE(_DEF_WIN32_WINNT_VISTA), LOBYTE(_DEF_WIN32_WINNT_VISTA), 0);
}

BOOL DEF_IsWindowsVistaSP1OrGreater()
{
	return DEF_IsWindowsVersionOrGreater(HIBYTE(_DEF_WIN32_WINNT_VISTA), LOBYTE(_DEF_WIN32_WINNT_VISTA), 1);
}

BOOL DEF_IsWindowsVistaSP2OrGreater()
{
	return DEF_IsWindowsVersionOrGreater(HIBYTE(_DEF_WIN32_WINNT_VISTA), LOBYTE(_DEF_WIN32_WINNT_VISTA), 2);
}

BOOL DEF_IsWindows7OrGreater()
{
	return DEF_IsWindowsVersionOrGreater(HIBYTE(_DEF_WIN32_WINNT_WIN7), LOBYTE(_DEF_WIN32_WINNT_WIN7), 0);
}

BOOL DEF_IsWindows7SP1OrGreater()
{
	return DEF_IsWindowsVersionOrGreater(HIBYTE(_DEF_WIN32_WINNT_WIN7), LOBYTE(_DEF_WIN32_WINNT_WIN7), 1);
}

BOOL DEF_IsWindows8OrGreater()
{
	return DEF_IsWindowsVersionOrGreater(HIBYTE(_DEF_WIN32_WINNT_WIN8), LOBYTE(_DEF_WIN32_WINNT_WIN8), 0);
}

BOOL DEF_IsWindows8Point1OrGreater()
{
	return DEF_IsWindowsVersionOrGreater(HIBYTE(_DEF_WIN32_WINNT_WINBLUE), LOBYTE(_DEF_WIN32_WINNT_WINBLUE), 0);
}

int _tmain(int argc, _TCHAR* argv[])
{
	printf("IsWindowsXPOrGreater : %d\r\n", DEF_IsWindowsXPOrGreater());
	printf("IsWindowsXPSP1OrGreater : %d\r\n", DEF_IsWindowsXPSP1OrGreater());
	printf("IsWindowsXPSP2OrGreater : %d\r\n", DEF_IsWindowsXPSP2OrGreater());
	printf("IsWindowsXPSP3OrGreater : %d\r\n", DEF_IsWindowsXPSP3OrGreater());
	printf("IsWindowsVistaOrGreater : %d\r\n", DEF_IsWindowsVistaOrGreater());
	printf("IsWindowsVistaSP1OrGreater : %d\r\n", DEF_IsWindowsVistaSP1OrGreater());
	printf("IsWindowsVistaSP2OrGreater : %d\r\n", DEF_IsWindowsVistaSP2OrGreater());
	printf("IsWindows7OrGreater : %d\r\n", DEF_IsWindows7OrGreater());
	printf("IsWindows7SP1OrGreater : %d\r\n", DEF_IsWindows7SP1OrGreater());
	printf("IsWindows8OrGreater : %d\r\n", DEF_IsWindows8OrGreater());
	printf("IsWindows8Point1OrGreater : %d\r\n", DEF_IsWindows8Point1OrGreater());
	_getch();
	return 0;
}

이러면, 문제없이 Visual Studio 2008에서 사용 가능해 집니다. 그리고, 향후 Windows 8.1 SDK 내부 header가 사용되더라고 오류가 발생하지 않습니다.

또한, Windows 9, 10, 11, ... 이 등장하더라도, 그때의 Windows x SDK를 설치하여, VersionHelpers.h의 내용을 참고하여 약간 수정하면 해결되리라 예상됩니다.

위 함수를 테스트해보면,

IsWindowsXPOrGreater : 1
IsWindowsXPSP1OrGreater : 1
IsWindowsXPSP2OrGreater : 1
IsWindowsXPSP3OrGreater : 1
IsWindowsVistaOrGreater : 1
IsWindowsVistaSP1OrGreater : 1
IsWindowsVistaSP2OrGreater : 1
IsWindows7OrGreater : 1
IsWindows7SP1OrGreater : 1
IsWindows8OrGreater : 0
IsWindows8Point1OrGreater : 0

와 같습니다. (Windows 7 SP1)

물론, Windows 8.1에서는 모든 값이 1로 표시되는 것을 확인하였습니다.


이와 같이, manifest 추가 없이 Visual Studio 2008에서 사용 가능한 Windows 8.1 판단 방법을 알아 봤습니다.

관련 소스와 실행파일은 다음과 같습니다.


win8_1.zip