概要

Windowsのバージョンを表示します。GetVersionEX APIはマニフェストを設定しないとWindows 8.1などに対応していないのは既知となっていますが、VerfyVersionInfo APIでもWindows 10ではWindows 7相当の結果が取得されました。
ここでは、ドライバの開発などに用いるRtlGetVersion APIを使用してWindows 10でも正常に動作するプログラムを作成しました。
本来RtlGetVersion APIをVsiaul C++で使用するにはntddk.hとNtoskml.libが必要であり、このファイルはおそらくWindows DDKに含まれていると思われます。
API自身はntddl.ddlというありふれたDLLの中にあります。
本プログラムはDDKをインストールしていない環境でもコンパイルできるように、APIを動的に呼び出すようにソースコードの中で記述しています。
マクロソフトのホームページによるとAPIの引数は構造体RTL_OSVERSIONINFOWおよびRTL_OSVERSIONINFOEXW となっていますが、 実態は、windows.hからインクルードされるwinnt.hのOSVERSIONINFOWおよびOSVERSIONINFOEXWと同じです。
サービスパックの情報は文字列で返されますが、RtlGetVersion APIはユニコード版しか存在しないので、マルチバイトでも使用しやすいようにヘルパー関数GetVersion2を経由して呼び出します。
マルチバイトでコンパイルした場合は、ユニコードをANSIに変換するソースが有効になります。
Windows 2000未満ではRtlGetVersionAPIをサポートしておりません。


テスト環境

コンパイラ

Visual C++ 2005/2008/2010/2013 Express 32/64bit マルチバイト/UNICODE

実行環境

Visual C++ 2005のマルチバイトでコンパイルした場合、Windows98SEで実行できますが、RtlGetVersion APIをサポートしておりませんのでそのようなメッセージをプログラムで出るように記述しています。
Visual C++ 2008でコンパイルした場合、Windows 2000以上で実行できます。
マニフェストは指定していません。

プログラムソースの概要

rtlgetver.cpp

_tWinMain

GetVersion2関数を呼び出しOSVERINFOEX型構造体にバージョン情報を取得します。
GetVersion2関数の戻り値がFALSEの場合、APIをサポートしてない旨のメッセージを表示します。
GetVersion2関数の戻り値がTRUEの場合、OSVERSIONINFOEX型構造体の中身を表示します。

GetVersion2

ntdll.dllをロードし、RtlGetVersion APIのエントリーポイントを取得しAPIを呼び出します。
このAPIはUNICODEのみサポートしているので、マルチバイトでコンパイルする場合は、UNICODEのOSVERINFOWEX型の変数を定義しAPIを呼び出し、取得結果をOSVERINFOEXに変換します。
UNICODEからマルチバイトへの変換は、サービスパック名のみですので、半角英数及び半角数字のみと仮定できるので、APIを使用せずに単純に上位バイトを切り捨てる処理で対応しています。

ソースコード

RtlGetVer.cpp

//	RtlGetVersion APIを使用してWindowsのバージョン番号を取得する
//	RtlGetVersion APIをサポートしていない場合は、GetVersionEx APIを使用する
//	Visual C++ 2005/2008/2013 Unicode/マルチバイト

#include <windows.h>
#include <stdio.h>
#include <tchar.h>

//	DLL内の関数へのポインタ型を定義
typedef void (WINAPI *RtlGetVersion_FUNC)(OSVERSIONINFOEXW* );

BOOL GetVersion2(OSVERSIONINFOEX* os);

int WINAPI _tWinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,LPTSTR lpsCmdLine, int nCmdShow){
	TCHAR buf[256];

	OSVERSIONINFOEX os;
	if(GetVersion2(&os)==TRUE){

		_stprintf_s(buf, sizeof(buf) / sizeof(TCHAR), _TEXT("MajorVersion %d\nMinorVersion %d\nBuild %d\n"),
			os.dwMajorVersion, os.dwMinorVersion, os.dwBuildNumber );

		MessageBox(0,buf,_TEXT("Windows Version"),MB_OK);
	}else{
		MessageBox(0,_TEXT("RtlGetVersion APIがサポートされていません"),_TEXT("Error"),MB_OK);
	}
	return (int)0;
}

BOOL GetVersion2(OSVERSIONINFOEX* os){
	HMODULE hMod;
	RtlGetVersion_FUNC func;
#ifdef UNICODE
	OSVERSIONINFOEXW* osw=os;
#else
	OSVERSIONINFOEXW o;
	OSVERSIONINFOEXW* osw=&o;
#endif

	hMod= LoadLibrary(TEXT("ntdll.dll"));
	if(hMod){
		func=(RtlGetVersion_FUNC)GetProcAddress(hMod,"RtlGetVersion");
		if(func==0){
			FreeLibrary(hMod);
			return FALSE;
		}
		ZeroMemory(osw,sizeof(*osw));
		osw->dwOSVersionInfoSize=sizeof(*osw);
		func(osw);
#ifndef	UNICODE
		os->dwBuildNumber=osw->dwBuildNumber;
		os->dwMajorVersion=osw->dwMajorVersion;
		os->dwMinorVersion=osw->dwMinorVersion;
		os->dwPlatformId=osw->dwPlatformId;
		os->dwOSVersionInfoSize=sizeof(*os);
		DWORD sz=sizeof(os->szCSDVersion);
		WCHAR* src=osw->szCSDVersion;
		unsigned char* dtc=(unsigned char*)os->szCSDVersion;
		while(*src)
			*dtc++ =(unsigned char) *src++;
		*dtc='\0';
#endif

	}else
		return FALSE;
	FreeLibrary(hMod);
	return TRUE;
}

実行ファイル