Windows8.1でもバージョン情報を正しく取得する C#編

Windows8.1でもバージョン情報を正しく取得するには?のC#編です。このコードが正しかったようなのでC#でも組んでみました。テンプレートとoffsetofを使っている部分の必殺技がC#だとどう化けるかが今回のおもしろいところです。

ちなみに先に書いておくと、前回のコードで取得した場合はWindows8.1でも正しくバージョンを取得できます。逆にGetVersionExを使うとマニフェストでWindows8.1対応をうたっていない場合Windows8のバージョン情報を返すようです。Windows7とかさらに過去に戻るわけではなかったようです。

かなり大がかりなサンプルコード

using System;
using System.Runtime.InteropServices;

namespace win81ver
{
    class Program
    {
        //メイン処理
        static void Main(string[] args)
        {
            OSVersionInfoEx verinfo;
            bool b = IsWindows8OrGreater();
            GetWindowsVersionDetails(out verinfo);
        }

        //OSVERSIONINFOEXの定義
        unsafe public struct OSVersionInfoEx
        {
            public uint dwOSVersionInfoSize;
            public uint dwMajorVersion;
            public uint dwMinorVersion;
            public uint dwBuildNumber;
            public uint dwPlatformId;
            public fixed char szCSDVersion[128];
            public ushort wServicePackMajor;
            public ushort wServicePackMinor;
            public ushort wSuiteMask;
            public byte wProductType;
            public byte wReserved;
        }

        //VerifyVersionInfoの取得
        [DllImport("kernel32.dll", EntryPoint = "VerifyVersionInfoW")]
        unsafe static extern int VerifyVersionInfo(OSVersionInfoEx info, uint typemask, ulong conditionmask);

        //VerSetConditionMaskの取得
        [DllImport("kernel32.dll", EntryPoint = "VerSetConditionMask")]
        static extern ulong VerSetConditionMask(ulong mask, uint type, ulong cond);

        //VerifyVersionInfoなどで使用する定数群
        class VVIConst{
            public static readonly uint VER_EQUAL = 1;
            public static readonly uint VER_GREATER = 2;
            public static readonly uint VER_GREATER_EQUAL = 3;
            public static readonly uint VER_LESS = 4;
            public static readonly uint VER_LESS_EQUAL = 5;
            public static readonly uint VER_AND = 6;
            public static readonly uint VER_OR = 7;
            public static readonly uint VER_CONDITION_MASK = 7;
            public static readonly uint VER_NUM_BITS_PER_CONDITION_MASK = 3;

            public static readonly uint VER_MINORVERSION = 0x00000001;
            public static readonly uint VER_MAJORVERSION = 0x00000002;
            public static readonly uint VER_BUILDNUMBER = 0x00000004;
            public static readonly uint VER_PLATFORMID = 0x00000008;
            public static readonly uint VER_SERVICEPACKMINOR = 0x00000010;
            public static readonly uint VER_SERVICEPACKMAJOR = 0x00000020;
            public static readonly uint VER_SUITENAME = 0x00000040;
            public static readonly uint VER_PRODUCT_TYPE = 0x00000080;

            public static readonly uint VER_NT_WORKSTATION = 0x00000001;
            public static readonly uint VER_NT_DOMAIN_CONTROLLER = 0x00000002;
            public static readonly uint VER_NT_SERVER = 0x00000003;
        }

        //IsWindows~の本体処理
        public static bool IsGreaterWindowsVersion(Int32 nMajor,Int32 nMinor,Int32 nServicePack,Int32 nProductType)
        {
            OSVersionInfoEx verinfo; uint typemask = 0x00; ulong conditionmask = 0x00;

            verinfo = new OSVersionInfoEx();
            verinfo.dwOSVersionInfoSize = (uint)Marshal.SizeOf(verinfo);
            if (nMajor >= 0) { verinfo.dwMajorVersion = (uint)nMajor; typemask |= VVIConst.VER_MAJORVERSION; conditionmask = VerSetConditionMask(conditionmask, VVIConst.VER_MAJORVERSION, VVIConst.VER_GREATER_EQUAL); }
            if (nMinor >= 0) { verinfo.dwMinorVersion = (uint)nMinor; typemask |= VVIConst.VER_MINORVERSION; conditionmask = VerSetConditionMask(conditionmask, VVIConst.VER_MINORVERSION, VVIConst.VER_GREATER_EQUAL); }
            if (nServicePack >= 0) { verinfo.wServicePackMajor = (ushort)nServicePack; typemask |= VVIConst.VER_SERVICEPACKMAJOR; conditionmask = VerSetConditionMask(conditionmask, VVIConst.VER_SERVICEPACKMAJOR, VVIConst.VER_GREATER_EQUAL); }
            if (nProductType >= 0) { verinfo.wProductType = (byte)nProductType; typemask |= VVIConst.VER_PRODUCT_TYPE; conditionmask = VerSetConditionMask(conditionmask, VVIConst.VER_PRODUCT_TYPE, VVIConst.VER_EQUAL); }
            return VerifyVersionInfo(verinfo, typemask, conditionmask) != 0;
        }

        //バージョンの比較処理
        public static bool IsWindowsXPOrGreater() { return IsGreaterWindowsVersion(5, 1, 0, (int)VVIConst.VER_NT_WORKSTATION); }
        public static bool IsWindowsXPSP1OrGreater() { return IsGreaterWindowsVersion(5, 1, 1, (int)VVIConst.VER_NT_WORKSTATION); }
        public static bool IsWindowsXPSP2OrGreater() { return IsGreaterWindowsVersion(5, 1, 2, (int)VVIConst.VER_NT_WORKSTATION); }
        public static bool IsWindowsXPSP3OrGreater() { return IsGreaterWindowsVersion(5, 1, 3, (int)VVIConst.VER_NT_WORKSTATION); }
        public static bool IsWindowsVistaOrGreater() { return IsGreaterWindowsVersion(6, 0, 0, (int)VVIConst.VER_NT_WORKSTATION); }
        public static bool IsWindowsVistaSP1OrGreater() { return IsGreaterWindowsVersion(6, 0, 1, (int)VVIConst.VER_NT_WORKSTATION); }
        public static bool IsWindowsVistaSP2OrGreater() { return IsGreaterWindowsVersion(6, 0, 2, (int)VVIConst.VER_NT_WORKSTATION); }
        public static bool IsWindows7OrGreater() { return IsGreaterWindowsVersion(6, 1, 0, (int)VVIConst.VER_NT_WORKSTATION); }
        public static bool IsWindows7SP1OrGreater() { return IsGreaterWindowsVersion(6, 1, 1, (int)VVIConst.VER_NT_WORKSTATION); }
        public static bool IsWindows8OrGreater() { return IsGreaterWindowsVersion(6, 2, 0, (int)VVIConst.VER_NT_WORKSTATION); }
        public static bool IsWindows8Point1OrGreater() { return IsGreaterWindowsVersion(6, 3, 0, (int)VVIConst.VER_NT_WORKSTATION); }
        public static bool IsWindowsServer() { return IsGreaterWindowsVersion(-1, -1, -1, (int)VVIConst.VER_NT_SERVER); }

        //GetVersionExを詳細にしたもの
        public static bool GetWindowsVersionDetails(out OSVersionInfoEx verinfo)
        {
            uint ui = uint.MaxValue; ushort us = ushort.MaxValue; byte uc = byte.MaxValue;

            verinfo = new OSVersionInfoEx();
            verinfo.dwOSVersionInfoSize = (uint)Marshal.SizeOf(verinfo);
            VerifyVersionInfoItem(ref verinfo, ref verinfo.dwMajorVersion, ref us, ref uc, VVIConst.VER_MAJORVERSION, 0, 16);
            VerifyVersionInfoItem(ref verinfo, ref verinfo.dwMinorVersion, ref us, ref uc, VVIConst.VER_MINORVERSION, 0, 16);
            VerifyVersionInfoItem(ref verinfo, ref verinfo.dwBuildNumber, ref us, ref uc, VVIConst.VER_BUILDNUMBER, 0, 100000);
            VerifyVersionInfoItem(ref verinfo, ref verinfo.dwPlatformId, ref us, ref uc, VVIConst.VER_PLATFORMID, 0, 8);
            VerifyVersionInfoItem(ref verinfo, ref ui, ref verinfo.wServicePackMajor, ref uc, VVIConst.VER_SERVICEPACKMAJOR, 0, 16);
            VerifyVersionInfoItem(ref verinfo, ref ui, ref verinfo.wServicePackMinor, ref uc, VVIConst.VER_SERVICEPACKMINOR, 0, 16);
            VerifyVersionInfoItem(ref verinfo, ref ui, ref verinfo.wSuiteMask, ref uc, VVIConst.VER_SUITENAME, 0, 0);
            VerifyVersionInfoItem(ref verinfo, ref ui, ref us, ref verinfo.wProductType, VVIConst.VER_PRODUCT_TYPE, 0, 16);

            return true;
        }

        //GetWindowsVersionDetailsのサポート処理
        private static void VerifyVersionInfoItem(ref OSVersionInfoEx verinfo,ref uint ui,ref ushort us,ref byte uc,uint type, uint min, uint max)
        {
            if (min != max)
            {
                ulong conditiongreater = VerSetConditionMask(0, type, VVIConst.VER_GREATER);
                ulong conditionequal = VerSetConditionMask(0, type, VVIConst.VER_EQUAL);

                while (min != max)
                {
                    uint mid = (max + min) / 2;
                    if (ui != uint.MaxValue) { ui = mid; } else if (us != ushort.MaxValue) { us = (ushort)mid; } else { uc = (byte)mid; }
                    if(VerifyVersionInfo(verinfo,type,conditionequal) != 0){ min = max = mid; }
                    else if(VerifyVersionInfo(verinfo,type,conditiongreater) != 0){ min = mid + 1; }
                    else{ max = mid; }
                }
            }
            else
            {
                ulong conditionand = VerSetConditionMask(0, type, VVIConst.VER_AND); uint mask = 0; uint bits;
                if(ui != uint.MaxValue){ bits = 32; } else if(us != ushort.MaxValue){ bits = 16; } else{ bits = 8; }

                for(uint i = 0;i < bits;i++){
                    uint value = (uint)(1 << (byte)i);
                    if(ui != uint.MaxValue){ ui = value; } else if(us != ushort.MaxValue){ us = (ushort)value; } else{ uc = (byte)value; }
                    if(VerifyVersionInfo(verinfo,type,conditionand) != 0){ mask |= value; }
                }
                if (ui != uint.MaxValue) { ui = mask; } else if (us != ushort.MaxValue) { us = (ushort)mask; } else { uc = (byte)mask; }
            }
        }
    }
}

呼び出し方はMainを参照のこと

結局offsetofによる間接的な構造体参照を参照渡しによる要素指定によって切り返した形になっています。よくもまあこんな実装を思いつくことやら。また相も変わらずunsafeが出てくるのは仕方がないですか

一応C言語版VersionHelpers.hの呼び出しをまねて実装しただけなので実はC#だと普通にバージョンが取ってこれる、とかだと非常に悲しいです。そうだとしてもどうやって移植するのか?の例にはなるでしょうが。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

この記事のトラックバック用URL