Вот уж не думал, что данная задача будет достойна заметки, но оказалось, что нормально определить версию (а главное, удобоваримое название и архитектуру) просто так не получится.
В .NET Framework в классе Environment
есть свойство OSVersion
, но анализировать информацию, полученную от него, довольно неудобно. Например:
— конструкция Console.WriteLine("Version: " + Environment.OSVersion.VersionString);
выведет следующую строку:
Microsoft Windows NT 6.1.7601 Service Pack 1
— можно получить нормально, пожалуй, только сервис-пак:
Console.WriteLine("ServicePack: " + Environment.OSVersion.ServicePack);
ServicePack: Service Pack 1
— и номер версии как в команде ver
, отдельно от остального, и то почти, остается паразитное 65536
из поля Reversion
:
Console.WriteLine("Version: " + Environment.OSVersion.Version.ToString());
Version: 6.1.7601.65536
В общем, без бубна и отдельной таблицы, сопоставляющей номера версий с конкретными названиями, нормально выводится только сервис-пак.
Интересующая информация хранится в классе Win32_OperatingSystem
(CIM_OperatingSystem
из пространства имен root\\CIMV2
), сначала сделаем общую функцию для обращения к полям класса:
private static string CallWMI(string WMIProperty)
{
string ret = null;
ManagementObjectSearcher mos = new
ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM CIM_OperatingSystem");
try
{
foreach (ManagementObject MObj in mos.Get())
{
ret = MObj.GetPropertyValue(WMIProperty).ToString();
}
}
catch (Exception ex)
{
ErrorMessage = ex.Message;
}
return ret;
}
Алгоритм довольно простой:
1. Создаем ManagementObjectSearcher
с запросом к нужному классу.
2. Выполняем запрос (mos.Get()
) и вытаскиваем ManagementObject
‘ы, на самом деле, в данном случае, объект будет один.
3. Получаем значение поля, название которого передается функции в переменной WMIProperty
(ret = MObj.GetPropertyValue(WMIProperty).ToString();
)
Внимание! Все операции по выполнению WMI-запроса и получению данных из полей, необходимо выполнять в конструкции try/catch
, потому что, если поля вдруг не будет, то произойдет exception, как например, произошло при обращении к полю, содержащему информацию о сервис-паке на системе Windows 8.1 без сервис-пака:

Да, WMI штука местами непредсказуемая и падучая в самых неожиданных местах, так что не забываем отлавливать эксепшны.
Наименования полей с нужной информацией:
— Version
: содержит номер версии (как в консольной команде ver
), например 6.1.7601
— Caption
: человеко-читаемое название операционной системы (Microsoft Windows 7 Максимальная
)
— CSDVersion
: версия сервис-пака (Service Pack 1
)
— OSArchitecture
: архитектура операционной системы (32-bit
или 64-bit
). К этому полю вернемся позже.
Пример получения наименования ОС:
public static string GetOSVersionWMI()
{
ErrorMessage = string.Empty;
return CallWMI("Version");
}
Интересующие данные хранятся в HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\
Нужные значения ключа:
— CurrentVersion
: первая часть номера версии ОС (6.1
)
— CurrentBuildNumber
: билд, вторая часть номера версии (7601
)
— ProductName
: наименование операционной системы (Windows 7 Ultimate
)
— CSDVersion
: сервис-пак (Service Pack 1
)
Минус способа очевиден. Фактически, все эти поля в Реестре нужны исключительно для информации, и какая-нибудь «колхозная» сборка, шаловливый пользователь или дурная программа вполне могут эти значения переписать.
Можно также создать общую функцию для доступа к данному ключу Реестра:
private static string CallRegistry(string RegValueName)
{
string ret = null;
try
{
RegistryKey key = Registry.LocalMachine.
OpenSubKey(@"Software\Microsoft\Windows NT\CurrentVersion\");
ret = key.GetValue(RegValueName).ToString();
key.Close();
}
catch (Exception ex)
{
ErrorMessage = ex.Message;
ret = null;
}
return ret;
}
Алгоритм стандартный:
1. Открываем ключ Реестра
2. Получаем нужное значение по его имени
3. Закрываем ключ
И опять же, не забывайте про отлов exceptions с помощью try/catch
! Ровно по той же причине, что и при использовании WMI. И, в данном случае, даже в том же самом месте. Убедиться можно на скриншотах в конце заметки.
Примеры использования.
Получение названия ОС:
public static string GetOSNameReg()
{
ErrorMessage = string.Empty;
return CallRegistry("ProductName");
}
Получение номера версии:
public static string GetOSVersionReg()
{
ErrorMessage = string.Empty;
string Version = CallRegistry("CurrentVersion");
string Build = CallRegistry("CurrentBuildNumber");
if ((Version == null) || (Build == null))
{
return null;
}
return Version + "." + Build;
}
Следующая задача — определить архитектуру системы (32- или 64-битная).
Как уже упоминалось выше, данные об архитектуре ОС можно получить через WMI. Эти сведения хранятся в поле OSArchitecture
класса Win32_OperatingSystem
, однако, этот способ не сработает для Windows XP, а возможно и Vista. В XP поля OSArchitecture
нет, и обращение к нему вызовет exception:

Честно подсмотрен на stackoverflow.
Алгоритм таков:
1. Проверяем размер IntPtr
. IntPtr
— это специальный платформо-зависимый тип, который используется для сохранения указателя, если нет желания использовать «небезопасный» код.
По замыслу автора оригинального алгоритма, если IntPtr.Size == 8
, значит, сам выполняемый процесс 64-разрядный, а значит, и система, в которой он выполняется — 64-разрядная. В оригинальном модуле Wow.cs даже было заведено отдельное свойство Is64BitProcess
, которое устанавливалось по результату этой проверки.
Но, насколько я понимаю, в том и фишка среды выполнения .NET Framework, что она сама подстраивается под ОС, и на 32-разрядной ОС .NET’овские процессы 32-разрядные, а на 64-разрядных, соответственно, 64-разрядные. Выполняется же в итоге код MSIL, и как его выполнять, решает именно что Framework. Поправьте меня, если я ошибаюсь.
Во всяком случае, при использовании оригинального класса, у меня и свойство Is64BitProcess
(для процесса), и свойство Is64BitOperatingSystem
(для ОС) принимали одно и то же значение в зависимости от разрядности ОС, но вне зависимости от версии (XP, 7, 8.1, 10).
2. Если IntPtr.Size
не равен 8
, значит, с помощью WinAPI функций проверяется:
2.1 Наличие в kernel32.dll
функции IsWow64Process
2.2 Проверяется, успешно ли завершилась функция IsWow64Process
, примененная к текущему процессу.
3.3. Проверяется ее результат.
3.4. Если все условия — истина, значит это 32-разрядный процесс, выполняющийся на 64-разрядной системе.
Функция, проверяющая, присутствует ли в DLL указанная функция (естественно, в секции export):
static bool ModuleContainsFunction(string moduleName, string methodName)
{
IntPtr hModule = GetModuleHandle(moduleName);
if (hModule != IntPtr.Zero)
return GetProcAddress(hModule, methodName) != IntPtr.Zero;
return false;
}
Свойство класса, ответственное за определение архитектуры:
public static bool Is64BitOperatingSystem
{
get
{
// Clearly if this is a 64-bit process we must be on a 64-bit OS.
if (IntPtr.Size == 8)
return true;
// Ok, so we are a 32-bit process, but is the OS 64-bit?
// If we are running under Wow64 than the OS is 64-bit.
bool isWow64;
return ModuleContainsFunction("kernel32.dll", "IsWow64Process")
&& IsWow64Process(GetCurrentProcess(), out isWow64) && isWow64;
}
}
Класс VersionDetect.cs
Весь пример целиком
Win32_OperatingSystem class
Тема на Киберфоруме
Вопрос на stackowerflow