Windowsのバージョン情報

オペレーティングシステムのバージョン情報

Windows APIを使用していくにあたって、Windowsのバージョンが必要になることがありますよね。例えば、Windows NT系には提供されているが、Windows 9x系には提供されていないような機能を使いたい場合があるかもしれません。このような場合には、あらかじめNT系のOSでのみ起動できるようにしておき、もし9x系のOSで起動されたら、警告を表示して終了するようなプログラムにするのが望ましいと思います。

HSP標準関数sysinfo

Windowsのバージョン情報は、HSPの標準機能でも取得することができます。HSP標準関数のsysinfoにパラメータとして0を渡すとバージョン情報が返されます。例えば、次のようにすることで、Windowsのバージョンがメッセージボックスに表示されます。

dialog sysinfo(0)   ; OS名とバージョン番号を表示

sysinfoは、バージョン情報を文字列の形式で返します。例えば、筆者の環境では“WindowsNT ver5.1”という文字列が表示されます。

sysinfoが返すバージョン情報の形式は、“OS名 バージョン番号”のようになっています。OS名は、OSが9x系の場合には“Windows9X”となり、NT系の場合には“WindowsNT”となります。バージョン番号は、文字列“ver”の直後にメジャーバージョン番号とマイナーバージョン番号が小数点で区切られた形式で置かれます。OSの種類によってこれらの番号がどのような値になるのかは後で説明します。

はじめに提示した例のように、OSが9x系なのかNT系なのかを判断する場合には、sysinfoを使っても、あまり難しくないでしょう。sysinfoが返す文字列の先頭9文字分(または半角スペースまで)を取り出して、それが“WindowsNT”であればNT系、そうでなければ9x系と判断できます(文字列検索でもいいですが)。

バージョン番号まで必要な場合には、さらにメジャーバージョン番号とマイナーバージョン番号をそれぞれ数値の形式で取得する必要がありますね。OS名の取得よりもやや難しいですが、それでも取得することは可能でしょう。

しかし、sysinfoで取得できる情報は割と少ないのです。例えば、筆者の環境は、これを書いている時点では「Windows XP Home Edition Servise Pack 3」ですが、sysinfoを用いた場合には“WindowsNT ver5.1”とだけ表示されます。実は、この表示では「Windows XP」であることだけしか分からないのです。さらに詳しいバージョン情報を得るには、専用のWindows API関数を使用するしかありません。

Windows APIのGetVersionEx関数とOSVERSIONINFO構造体

Windows APIを使用してWindowsのバージョン情報を取得するには、kernel32.dllが提供するGetVersionEx関数を使います。

BOOL GetVersionExA(
    POSVERSIONINFO pVersionInfo
);

この関数のパラメータには、OSVERSIONINFO構造体のアドレスを指定しなければいけません。この構造体は次のように定義されています。

typedef struct _OSVERSIONINFO {
    DWORD dwOSVersionInfoSize;  // structure size
    DWORD dwMajorVersion;       // major version
    DWORD dwMinorVersion;       // minor version
    DWORD dwBuildNumber;        // build number
    DWORD dwPlatformId;         // platform ID
    TCHAR szCSDVersion[128];    // additional information
} OSVERSIONINFO, *POSVERSIONINFO, *LPOSVERSIONINFO;

この構造体には、WindowsのプラットフォームIDやバージョン番号を含むいくつかの情報が格納されます。

dwOSVersionInfoSizeメンバはこの構造体のサイズです。GetVersionEx関数を呼び出す前には、このメンバに構造体サイズを格納しておかなければいけません。OSVERSIONINFO構造体のサイズは、呼び出すAPI関数がANSI版かUnicode版かによって異なります。ここではANSI版の関数を呼び出すので、そのサイズは148になります。もちろん、構造体として割り当てる変数も148バイトぶん、すなわち数値型配列変数にして37要素ぶんをあらかじめ確保しておかなければいけません。

関数呼び出し前に値を格納しておくべきなのはdwOSVersionInfoSizeメンバだけです。この構造体のアドレスをパラメータとしてGetVersionEx関数を呼び出すと、残りのメンバに情報が格納されるようになっています。

dwMajorVersionメンバとdwMinorVersionメンバにはそれぞれOSのメジャーバージョン番号とマイナーバージョン番号が格納されるようになっています。

dwBuildNumberメンバにはビルド番号と呼ばれる番号が格納されます。Windows 9x系の場合には、このメンバの下位ワードが正確なビルド番号になります。

dwPlatformIdメンバにはプラットフォームIDが格納されます。この値は、OSがWindows 9x系かWindows NT系かを判断する値となります。Windows 9x系では1 (VER_PLATFORM_WIN32_WINDOWS) が格納され、Windows NT系では2 (VER_PLATFORM_WIN32_NT) が格納されるようになっています。古い16ビットWindows 3.1用に0 (VER_PLATFORM_WIN32s) という値が定義されていますが、使われることはありません。

szCSDVersionメンバは128バイト分の文字列が格納できるようになっていますが、このメンバには、さまざまな拡張情報が格納されます。このメンバの文字列を取得するには、getstr命令を使用するのが便利です。

Windowsの種類はプラットフォームIDとメジャーバージョン番号・マイナーバージョン番号および拡張情報のテキストから取得できます。

Platform ID Major Minor Build バージョンテキスト OSの種類
1 4 0 950 - Windows 95
" A" ("a"?) Windows 95 OSR1 (or SP1)
1111 " B" Windows 95 OSR2
0 (03?) 12121214 Windows 95 OSR2.1
1214 " C" Windows 95 OSR2.5
10 1998 - Windows 98
2222 " A" Windows 98 SE
90 3000 - Windows Me
2 3 - - (Service Pack) Windows NT 3.x
4 0 - Windows NT 4.0
5 0 - Windows 2000
1 - Windows XP
2 - Windows Server 2003 / Windows Server 2003 R2
6 0 - Windows Vista / Windows Server 2008
1 - Windows 7 / Windows Server 2008 R2

ビルド番号は出荷時期にもよるので、場合によっては上記のビルド番号よりも少し大きくなることがあるかもしれません。また、上の表に書かれているバージョンテキスト(szCSDVersionメンバの文字列)の中に、“(Service Pack)”と表示されている部分がありますが、これは、マシンにサービスパックがインストールされている場合には、そのサービスパック名になります。例えば"Service Pack 2"などといった文字列になります。

ほとんどの情報はプラットフォームIDとメジャーバージョン番号・マイナーバージョン番号だけで事足りますが、例えばWindows 95 OSR2やWindows 98 SEなどの判定まで必要な場合は、拡張情報文字列が必要になります。また、Windows VistaとWindows Server 2008を区別したり、Windows 7とWindows Server 2008 R2を区別したりする場合には、この情報だけでは区別できません。

補足ですが、Windows 95 OSR 2 (OEM Service Release 2) 以降かどうかについては、ビルド番号(dwBuildNumberメンバの下位ワード)が1080よりも大きいかどうかで判断できるという記述もあります(上の表とやや違うのはデバッグバージョンも含むからかもしれません)。Windows 9x系では、Windows 95 OSR 2以降で使用可能な機能も多く、その場合にはこちらの方法で判定するのもいいでしょう。

ただし、NEC PC9801対応Windows 95に対してカーネルアップデートパッチ3(krnlupd3.exe)なるものを当てたときに、間違ったビルド番号を返すようになってしまうという情報があります(OSR 2ではないのにビルド番号1111を返す)。これは、もはやマシン側の問題なので、アプリケーション側が対応する必要はないと思うのですが、ユーザー側で問題が発生してユーザーから質問された場合に備えておきましょう。この問題とその対処法について、以下のマイクロソフトのサポートページに記載されています。このリンク先の「回避策」に書かれている手順を、問題が発生しているマシンを使用しているユーザーに行ってもらう必要があります。


実際にスクリプトを書いてみると、次のようになります。

#uselib "kernel32.dll"
#func GetVersionEx "GetVersionExA" int

; OSVERSIONINFO 構造体の準備
dim osver, 37
osver(0) = 148
; GetVersionEx 関数呼び出し
GetVersionEx varptr(osver)

; 各種情報の取得
platform = osver(4)
majorver = osver(1)
minorver = osver(2)
getstr vertext, osver, 20

mes "Platform ID   : " + platform
mes "Major Version : " + majorver
mes "Minor Version : " + minorver
mes "Version Text  : " + vertext

; オペレーティングシステム名の判定
if platform == 2 {
    ; Windows NT system
    if majorver == 5 {
        if minorver == 0 {
            s = "Windows 2000"
        } else:if minorver == 1 {
            s = "Windows XP"
        } else:if minorver == 2 {
            s = "Windows Server 2003 or Windows Server 2003 R2"
        } else {
            s = "Windows NT 5." + minorver
        }
    } else:if majorver == 6 {
        if minorver == 0 {
            s = "Windows Vista or Windows Server 2008"
        } else:if minorver == 1 {
            s = "Windows 7 or Windows Server 2008 R2"
        } else {
            s = "Windows NT 6." + minorver
        }
    } else {
        s = "Windows NT " + majorver + "." + minorver
    }
    s += " " + vertext

} else:if platform == 1 {
    ; Windows 9x system
    if majorver == 4 {
        if minorver >= 90 {
            s = "Windows Millennium Edition"
        } else:if minorver >= 10 {
            s = "Windows 98"
            if vertext == " A" {
                s += " Second Edition"
            }
        } else {
            s = "Windows 95"
            if (vertext == " B")|(vertext == " C") {
                s += " OEM Service Release 2"
            }
        }
    } else {
        s = "Unknown OS"
    }
} else {
    s = "Unknown OS"
}
mes s
stop

より詳細な情報を得るOSVERSIONINFOEX構造体

さて、ほとんどの場合は、上の方法で取得できる情報だけで十分でしょう。しかし、OSのバージョン情報としてはまだ不完全ですね。例えば、Windows XPの場合には、“Home Edition”と“Professional”がありますし、他のWindows NT系でもいくつかの種類に分かれているものがあります。

Windows NT SP6以降では、GetVersionEx関数に、より詳細な情報を格納できるOSVERSIONINFOEX構造体を渡すことができるようになっています。使用可能なWindowsのバージョンが限られており、Windows 9x系や、Windows NT SP5以前では、OSVERSIONINFOEX構造体を渡すとエラーが返ってしまいます。

OSVERSIONINFOEX構造体は以下のように定義されています。

typedef struct _OSVERSIONINFO {
    DWORD dwOSVersionInfoSize; // structure size
    DWORD dwMajorVersion;      // major version
    DWORD dwMinorVersion;      // minor version
    DWORD dwBuildNumber;       // build number
    DWORD dwPlatformId;        // platform ID
    TCHAR szCSDVersion[128];   // additional string
    WORD  wServicePackMajor;   // SP major version
    WORD  wServicePackMinor;   // SP minor version
    WORD  wSuiteMask;          // product suites
    BYTE  wProductType;        // additional information
    BYTE  wReserved;           // reserved
} OSVERSIONINFOEX, *POSVERSIONINFOEX, *LPOSVERSIONINFOEX;

この構造体はOSVERSIONINFO構造体を拡張したものという位置づけなので、前半のメンバはOSVERSIONINFO構造体と同じです。ただし、構造体サイズを表すdwOSVersionInfoSizeメンバには、新しいOSVERSIONINFOEX構造体のサイズである156(ANSI版の場合)を格納しておかなくてはいけません。もちろん、構造体として割り当てる変数も156バイト、数値型配列変数にして39要素ぶんをあらかじめ確保しておく必要があります。

そのほかのメンバは、この構造体で拡張されたメンバです。それぞれのメンバは2バイト整数(WORD型)や1バイト整数(BYTE型)であるので、それぞれの値を取得するにはwpeekpeekを使用するのが便利です。

ここではそれぞれのメンバで使用することのできる値について、個別に詳しく説明していくことはできません。代表的な定数名とその値について記しておきますので、MSDN等のAPIドキュメントを参照してみてください。

wServicePackMajorメンバとdwMinorVersionメンバには、それぞれサービスパックのメジャーバージョン番号とマイナーバージョン番号が格納されます。例えば、“Service Pack 2”がインストールされている場合は、メジャーバージョン番号が2に、マイナーバージョン番号が0になります。

wSuiteMaskメンバは利用可能な製品スイートを表すビットマスクで、以下の値の組み合わせです。

定数名
VER_SUITE_SMALLBUSINESS 0x00000001
VER_SUITE_ENTERPRISE 0x00000002
VER_SUITE_BACKOFFICE 0x00000004
VER_SUITE_TERMINAL 0x00000010
VER_SUITE_SMALLBUSINESS_RESTRICTED 0x00000020
VER_SUITE_EMBEDDEDNT 0x00000040
VER_SUITE_DATACENTER 0x00000080
VER_SUITE_SINGLEUSERTS 0x00000100
VER_SUITE_PERSONAL 0x00000200
VER_SUITE_BLADE 0x00000400
VER_SUITE_STORAGE_SERVER 0x00002000
VER_SUITE_COMPUTE_SERVER 0x00004000
VER_SUITE_WH_SERVER 0x00008000

wProductTypeメンバはシステムに関する追加情報で、以下の値のいずれか1つになります。

定数名
VER_NT_WORKSTATION 0x0000001
VER_NT_DOMAIN_CONTROLLER 0x0000002
VER_NT_SERVER 0x0000003

wReservedメンバは予約されているメンバで、現在は使用されていません。


実際にスクリプトを書いてみると、次のようになります。まずはじめにOSVERSIONINFOEX構造体(構造体サイズを156に設定したもの)を渡してGetVersionEx関数を呼び出します。このOSVERSIONINFOEX構造体をサポートしていないバージョンのWindowsの場合はエラーとなって戻り値が0になるので、その場合にはOSVERSIONINFO構造体(構造体サイズを148に設定したもの)を渡すようにします。

スクリプトを載せておきますが、おまけ的なものですので、細かい説明はしません。また、詳細な情報を記述するのはWindows XPまたはWindows Server 2003までとなっています。それよりも新しいWindows VistaやWindows Server 2008を含めてのバージョン情報表示となると、さらに他のAPI関数を使用しなければ実現できませんので、ここでは省略することにします。

#uselib "kernel32.dll"
#func GetVersionEx "GetVersionExA" int

; 定数名の定義
#const VER_PLATFORM_WIN32_WINDOWS 1
#const VER_PLATFORM_WIN32_NT      2
#const VER_NT_WORKSTATION         0x00000001
#const VER_NT_SERVER              0x00000003
#const VER_SUITE_ENTERPRISE       0x00000002
#const VER_SUITE_DATACENTER       0x00000080
#const VER_SUITE_PERSONAL         0x00000200
#const VER_SUITE_BLADE            0x00000400

; OSVERSIONINFOEX 構造体の準備
dim osver, 39
osver(0) = 156
exflag = 1
; GetVersionEx 関数呼び出し
GetVersionEx varptr(osver)
if stat == 0 {
    ; エラー時は OSVERSIONINFO として渡す (サイズ 148)
    osver(0) = 148
    GetVersionEx varptr(osver)
    exflag = 0
}

; 各種情報の取得
platform = osver(4)
majorver = osver(1)
minorver = osver(2)
getstr vertext, osver, 20

; オペレーティングシステム名の判定
if platform == VER_PLATFORM_WIN32_NT {
    ; Windows NT system
    if majorver == 5 {
        if minorver == 0 {
            s = "Windows 2000 "
        } else:if minorver == 1 {
            s = "Windows XP "
        } else:if minorver == 2 {
            s = "Windows Server 2003 family "
        } else {
            s = "Windows NT "
        }
    } else {
        s = "Windows NT "
    }
    if exflag {
        suite = wpeek(osver,152)
        product = peek(osver,154)
        if ( product == VER_NT_WORKSTATION ) {
            if majorver == 4 {
                s += "Workstation 4.0 "
            } else:if ( suite & VER_SUITE_PERSONAL ) {
                s += "Home Edition "
            } else {
                s += "Professional "
            }
        } else ( product == VER_NT_SERVER ) {
            if ( majorver == 5 )&( minorver == 2 ) {
                if ( suite & VER_SUITE_DATACENTER ) {
                    s += "Datacenter Edition "
                } else:if ( suite & VER_SUITE_ENTERPRISE ) {
                    s += "Enterprise Edition "
                } else:if ( suite & VER_SUITE_BLADE ) {
                    s += "Web Edition "
                } else {
                    s += "Standard Edition "
                }
            } else:if ( majorver == 5 )&( minorver == 0 ) {
                if ( suite & VER_SUITE_DATACENTER ) {
                    s += "Datacenter Server "
                } else:if ( suite & VER_SUITE_ENTERPRISE ) {
                    s += "Advanced Server "
                } else {
                    s += "Server "
                }
            } else {
                s += "Server " + majorver + "." + minorver
                if ( suite & VER_SUITE_ENTERPRISE ) {
                    s += " Enterprise Edition "
                }
            }
        }
    } else {
        ; -- Windows NT SP5 以前 --
        ; (レジストリからの情報取得ができるがここでは省略)
        s += "" + majorver + "." + minorver
    }
    s += vertext

} else:if platform == VER_PLATFORM_WIN32_WINDOWS {
    ; Windows 9x system
    if majorver == 4 {
        if minorver >= 90 {
            s = "Windows Millennium Edition"
        } else:if minorver >= 10 {
            s = "Windows 98"
            if vertext == " A" {
                s += " Second Edition"
            }
        } else {
            s = "Windows 95"
            if (vertext == " B")|(vertext == " C") {
                s += " OEM Service Release 2"
            }
        }
    } else {
        s = "Unknown OS"
    }
} else {
    s = "Unknown OS"
}
mes s
stop