출처 : 데브피아 김경진님의 글([VC++] 윈도우 버전 정확하게 얻어오는 새로운 방법)

 
기존 방식의 구현

  

지금까지 윈도우 버전을 얻어오기 위하여 주로 사용했던 방법은 바로 GetVersionEx 함수를 사용하는 것이었습니다. 하지만 Visual Studio 2013 버전에서 GetVersionEx 함수

(혹은 GetVersion 함수)를 사용하려고 하니 아래와 같은 오류를 내뱉으며 컴파일이 되질 않습니다.

컴파일 오류

  

물론 #pragma warning(disable:4996) 으로 에러가 발생하지 않게 막아놓고 사용할수야 있겠지만 이렇게 사용하기에는 찝찝한 기분을 떨쳐버릴 수가 없습니다. MS가 GetVersionEx 함수를 deprecated 시키고 컴파일 오류까지 발생시키며 사용하지 말 것을 권장한다면, 거기에는 분명히 그럴만한 이유가 있겠죠. 그렇다면 왜 지금까지 잘 사용해오던 GetVersionEx 함수를 이제와서 사용하지 말라고 하는걸까요?

 

 

호환 모드 문제

  

윈도우에서는 다음과 같이 어플리케이션을 호환 모드로 실행할 수 있게 되어있습니다.

 

 

 

 

어플리케이션을 호환 모드로 실행하였을 경우에 GetVersionEx 함수를 사용하면 현재 윈도우 버전을 얻어오는 것이 아니라 호환 모드에서 선택한 윈도우 버전을 얻어오게 됩니다. 실제로 윈도우 8에서 호환 모드를 Windows XP SP3로 설정하고 실행하니 다음과 같이 GetVersionEx 함수가 Windows XP SP3 버전(5.1.3)을 리턴하는 것을 볼 수 있습니다.

 

<그림: 호환 모드에서 GetVersionEx 함수의 오동작>

 

  

GetVersionEx 함수의 호환 모드 문제는 사실 어제 오늘의 문제가 아니라 지금까지 쭈욱 발생되어왔던 문제입니다. 일반적인 상황에서는 호환 모드를 사용할 일이 거의 없기 때문에 신경쓰지 않았을 뿐이죠. 하지만 만에 하나라도 사용자가 호환 모드를 사용한다면 GetVersionEx 함수의 결과값이 달라져서 예기치 못한 문제가 발생할 수 있습니다.

 

 

Windows 8.1 문제

  

GetVersionEx 함수는 앞서 살펴본 호환 모드 문제 뿐만 아니라 또 하나의 중대한 문제점을 가지고 있습니다. 바로 Windows 8.1 이상의 OS에서 제대로 동작하지 않는다는 것인데요, GetVersionEx 함수가 deprecated 된 이유도 이 때문이라 할 수 있습니다. 실제로 Windows 8.1 환경에서 테스트해보니 GetVersionEx 함수가 6.3.0을 리턴하는 대신 Windows 8의 버전인 6.2.0을 리턴하는 것을 볼 수 있습니다.

 

 

<그림: Windows 8.1에서 GetVersionEx 함수의 오동작>

 

 

이러한 결과가 발생하는 이유는 Windows 8.1 버전부터 GetVersionEx 함수가 매니페스트에 의존적으로 동작하도록 변경되었기 때문입니다. 이 문제를 해결하려면 어플리케이션이 Windows 8.1을 타겟팅하도록 매니페스트를 작성해주어야 합니다.

 

 

 

하지만 어플리케이션을 만들 때 마다 위와 같은 매니페스트를 작성해줘야 하고, 새 버전의 OS가 나오면 또 다시 매니페스트에 supportedOS 를 추가해줘야 하니 여간 번거로운게 아닙니다. MS가 GetVersionEx 를 deprecated 시킨 이유가 이제 이해가 되는군요.

 

 

VerifyVersionInfo / 

Version Helper API

  

MS에서 GetVersionEx 함수 대신 사용하도록 권장하는 방법은 VerifyVersionInfo 함수 또는 Version Helper API를 사용하는 것입니다. 두 가지 방식 모두 호환 모드 설정에 영향을 받지 않으며, Windows 8.1 이상에서도 문제없이 동작합니다. 

 

먼저 VerifyVersionInfo 함수는 현재 윈도우 버전이 지정한 버전과 같은지, 같거나 큰지, 작은지 등등을 판단하여 그에 대한 결과를 BOOL 타입으로 리턴하는 함수입니다. VerifyVersionInfo 함수를 이용하여 현재 윈도우 버전이 Windows 8 이상인지 검사하려면 다음과 같이 구현하면 됩니다. 

 

OSVERSIONINFOEX osvi;
DWORDLONG dwlConditionMask = 0;

ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
osvi.dwMajorVersion = 6;
osvi.dwMinorVersion = 2;

VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);

if (!VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, dwlConditionMask))
{
    ::MessageBox(NULL, _T("You need at least Windows 8"), _T("Version Not Supported"), MB_OK);
}

 

 

다음으로 Version Helper API는 Windows 8.1 SDK에 포함되어 있으며(Visual Studio 2012 이하 버전에서는 별도로 설치 필요), 현재까지 지원되는 Version Helper API의 목록은 다음과 같습니다.

 

  • IsWindowsXPOrGreater, IsWindowsXPSP1OrGreater, IsWindowsXPSP2OrGreater, IsWindowsXPSP3OrGreater
 
  • IsWindowsVistaOrGreater, IsWindowsVistaSP1OrGreater, IsWindowsVistaSP2OrGreater
 
  • IsWindows7OrGreater, IsWindows7SP1OrGreater
 
  • IsWindows8OrGreater, IsWindows8Point1OrGreater
 
  • IsWindowsServer
 
  • IsWindowsVersionOrGreater

 

 

Version Helper API를 이용하여 현재 윈도우 버전이 Windows 8 이상인지 검사하려면 다음과 같이 구현하면 됩니다.

 

#include <VersionHelpers.h>

if (!IsWindows8OrGreater())
{
    MessageBox(NULL, _T("You need at least Windows 8"), _T("Version Not Supported"), MB_OK);
}

 

 

위에서 설명한 두 가지 방식은 모두 현재 윈도우의 버전을 얻어오는 방식이 아니라, 현재 윈도우 버전이 해당 버전과 같은지(혹은 그 이상인지) 검증하는 방식입니다. 그러므로 GetVersionEx 함수처럼 현재 윈도우의 버전을 얻어오고 싶은 경우에는 또 다른 방법을 사용해야 합니다.

 

 

GetWindowsVersion 함수 구현

  

현재 윈도우의 정확한 버전을 얻기위해 사용할 수 있는 방법은 두 가지 방법이 있습니다. 첫 번째 방법은 VerifyVersionInfo 함수 또는 IsWindowsVersionOrGreater 함수를 반복적으로 호출하면서 현재 윈도우 버전을 구해나가는 것이고, 두 번째 방법은 Network Management Function 중에 하나인 NetWkstaGetInfo 함수를 이용하는 것입니다(

NetWkstaGetInfo 함수는 원래 네트워크상의 워크스테이션 정보를 얻어오는 함수지만 윈도우 버전 정보을 얻어오는 데에도 활용할 수 있습니다)개인적으로는 두 번째 방법이 더 깔끔한 것 같아서 두 번째 방법으로 GetWindowsVersion 함수를 구현해봤습니다.

 

#include <Lm.h>
#pragma comment(lib, "netapi32.lib")

BOOL GetWindowsVersion(DWORD& dwMajor, DWORD& dwMinor)
{
    static DWORD dwMajorCache = 0, dwMinorCache = 0;
    if (0 != dwMajorCache)
    {
        dwMajor = dwMajorCache;
        dwMinor = dwMinorCache;
        return TRUE;
    }

    LPWKSTA_INFO_100 pBuf = NULL;
    if (NERR_Success != NetWkstaGetInfo(NULL, 100, (LPBYTE*)&pBuf))
        return FALSE;

    dwMajor = dwMajorCache = pBuf->wki100_ver_major;
    dwMinor = dwMinorCache = pBuf->wki100_ver_minor;
    NetApiBufferFree(pBuf);

    return TRUE;
}

 

 

제가 참고한 자료에서는 NetWkstaGetInfo 함수가 다소 느리다는 얘기가 있어서 최초 한 번만 

NetWkstaGetInfo 함수를 호출하고, 그 다음부터는 캐시 값을 리턴하도록 구현했습니다. 그리고 NetWkstaGetInfo 함수로는 서비스팩 버전을 구할 수 없으므로 기존 GetWindowsVersion 함수와 

VerifyVersionInfo 함수를 조합하여 서비스팩 버전까지 얻어오는 오버로드 함수를 하나 더 만들어봤습니다.

 

 

BOOL GetWindowsVersion(DWORD& dwMajor, DWORD& dwMinor, DWORD& dwServicePack)
{
    if (!GetWindowsVersion(dwMajor, dwMinor))
        return FALSE;

    static DWORD dwServicePackCache = ULONG_MAX;
    if (ULONG_MAX != dwServicePackCache)
    {
        dwServicePack = dwServicePackCache;
        return TRUE;
    }

    const int nServicePackMax = 10;
    OSVERSIONINFOEX osvi;
    DWORDLONG dwlConditionMask = 0;

    ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMAJOR, VER_EQUAL);

    for (int i = 0; i < nServicePackMax; ++i)
    {
        osvi.wServicePackMajor = i;
        if (VerifyVersionInfo(&osvi, VER_SERVICEPACKMAJOR, dwlConditionMask))
        {
            dwServicePack = dwServicePackCache = i;
            return TRUE;
        }
    }
    return FALSE;
}

 

 

위에서 구현한 GetWindowsVersion 함수는 호환 모드에 영향을 받지않으며, Windows 8.1 이상에서도 정확한 윈도우 버전을 얻어올 수 있습니다.

 

<그림: 호환 모드에서  GetWindowsVersion  함수 호출>

 

<그림: Windows 8.1에서  GetWindowsVersion  함수 호출>

 

 

Reference

  

MSDN - GetVersionEx functionMSDN - Version Helper functions

Part1: Overcoming Windows 8.1's deprecation of GetVersionEx and GetVersion APIs

 

 

원문 링크

 



출처 : 데브피아 김경진님의 글([VC++] 윈도우 버전 정확하게 얻어오는 새로운 방법)

 

ATL 관련 헤더를 사용할 때 GUID_NULL 에러가 발생한다.


이때는 atl*.h 들 앞에 cguid.h 를 선언해 주면 된다.



#include <CGuid.h>
#include <atl____.h>






http://mycplusplusexperience.blogspot.com/2009/10/error-c2065-guidnull-undeclared.html





+ Recent posts