如何获取“友好”的操作系统版本名称?

73

我正在寻找一种优雅的方法来获取操作系统版本,例如:"Windows XP Professional Service Pack 1"或"Windows Server 2008 Standard Edition"等。

是否有一种优雅的方法可以实现这一点?

我还对处理器架构(如x86或x64)感兴趣。


3
小心,我看过很多这方面的代码示例,在用户不是管理员时会出现错误...当然也有很多可以适用于非管理员用户的代码示例。只是要小心谨慎。^^ - Oskar Duveborn
12个回答

77

你可以使用 WMI 获取产品名称(“Microsoft® Windows Server® 2008 Enterprise”):

using System.Management;
var name = (from x in new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem").Get().Cast<ManagementObject>()
                      select x.GetPropertyValue("Caption")).FirstOrDefault();
return name != null ? name.ToString() : "Unknown";

5
如果集合为空,使用FirstOrDefault方法代替First方法,否则会抛出异常。原因是在空集合上调用First方法会导致异常。 - franza
3
使用Cast<T>而不是OfType<T>也可以略微提高性能。 - Derek W
3
我的一些用户在运行上述代码时遇到了“UnauthorizedAccessException”异常。有任何想法是什么原因造成的? - Walt D
@WaltD 我假设这些用户没有管理员权限。据我所知,使用 WMI 需要管理员权限。 - Andreas
Wmi 可以在安装完 Windows 更新后重启计算机后冻结进程。 - jjxtra

33

你应该尽量避免在本地使用WMI。它非常方便,但在性能方面会付出代价。这是快速简单的方法:

    public string HKLM_GetString(string path, string key)
    {
        try
        {
            RegistryKey rk = Registry.LocalMachine.OpenSubKey(path);
            if (rk == null) return "";
            return (string)rk.GetValue(key);
        }
        catch { return ""; }
    }

    public string FriendlyName()
    {
        string ProductName = HKLM_GetString(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "ProductName");
        string CSDVersion = HKLM_GetString(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CSDVersion");
        if (ProductName != "")
        {
            return (ProductName.StartsWith("Microsoft") ? "" : "Microsoft ") + ProductName +
                        (CSDVersion != "" ? " " + CSDVersion : "");
        }
        return "";
    }

1
如果问题如此简单,为什么人们要这么吃苦? - NateS
1
获取本地计算机操作系统的“友好”名称非常简单。当涉及到操作系统版本号时,情况可能会变得更加复杂,但这不是所要求的内容。 - domskey
5
@NateS,这不是痛苦。这个 才是痛苦! - Sophit
2
请注意:此代码仅适用于本地计算机。要将其更改为真正的 WMI 替代品(通常用于连接远程计算机),您需要将 Registry.LocalMachine.OpenSubKey(path) 更改为 Registry.OpenRemoteBaseKey(RegistryHive.LocalMachine, computer).OpenSubKey(path);,并传入计算机名称。您还需要使用 ServiceController sc = new ServiceController("RemoteRegistry", computer); if (sc.Status.Equals(ServiceControllerStatus.Running)) { ... // do your stuff } 来检查远程注册表服务是否正在运行,并且如果未运行,则可以启动它:sc.Start(); - vapcguy

28
为什么不使用 Environment.OSVersion?它还会告诉你操作系统是什么 - Windows,Mac OS X,Unix等等。要查找当前运行的是64位还是32位,请使用 IntPtr.Size - 这将返回32位的4个字节和64位的8个字节。

4
Environment.OSVersion 可以获得操作系统的版本信息,但返回的是计算机可读的版本号码,和人们常用的操作系统名称不同。例如,WMI 可以返回 Microsoft Windows 8.1 Pro 这样的具体名称,而 Environment.OSVersion 返回的则是 Microsoft Windows NT 6.2.9200.0 这样的编码格式。 - Sean Kearon
16
我发现 Environment.OSVersion 不太合适,除非你有一个声明支持的操作系统的 app.manifest 文件。否则,如果你的应用程序在 Windows Vista 上运行而不是 Windows 10,你可能会得到完全错误的操作系统版本。 - Adam Venezia
2
如果您正在为x86平台编译.NET应用程序,IntPtr.Size将返回4,即使在64位操作系统上运行。更好的解决方案在这里:https://dev59.com/z3RC5IYBdhLWcg3wUvQS - Marc Clifton
6
从配置器的链接中可以看到:“从Windows 8开始,OSVersion属性返回所有Windows平台相同的主要和次要版本号。因此,我们不建议您检索此属性的值以确定操作系统版本。” - Steve Smith
除了@AdamVenezia所述的内容外,自定义app.manifest不能应用于类库。而且,app.manifest需要支持的操作系统GUID进行注释。我需要在遥测客户端中使用OS友好名称,即使该应用程序没有(官方)支持特定的Windows版本,因此在app.manifest中将其标记为这样是误导性的。这两个限制使得我的情况下无法使用Environmen.OSVersion - NexX

17

尝试:

new ComputerInfo().OSVersion;

输出:

Microsoft Windows 10 企业版

注意: 添加对 Microsoft.VisualBasic.Devices; 的引用。


8
new ComputerInfo().OSFullName 的输出是操作系统的全名。 - andySF
4
OSVersion 方法返回操作系统版本号,而非名称。如果需要获取名称,请使用 OSFullName 方法。ComputerInfo Class - akinuri

10
以下代码对我有效,可以输出以下结果:Microsoft Windows 10.0.18362
System.Runtime.InteropServices.RuntimeInformation.OSDescription

它可以用于获取架构等信息,例如:
属性
FrameworkDescription:返回一个字符串,指示应用程序正在运行的.NET安装的名称。
OSArchitecture:获取当前应用程序正在运行的平台架构。
OSDescription:获取描述应用程序运行的操作系统的字符串。
ProcessArchitecture:获取当前运行的应用程序的进程架构。 https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.runtimeinformation?view=netframework-4.8

这似乎是来自Microsoft文档的Environment.OSVersion的官方替代方法,请参阅Environment.OSVersion Property - Kiroul
2
这是一个不错的选择,但至少需要 .NET Framework 4.7.1。 - Kadeer Mughal

9

有点晚了,但这是我做的方法。可能会帮助未来的某个人。

using Microsoft.Win32;

RegistryKey registryKey = Registry.LocalMachine.OpenSubKey("Software\\Microsoft\\Windows NT\\CurrentVersion");
        string pathName = (string)registryKey.GetValue("productName");

1
这太棒了。或者用这个来远程控制计算机:registryKey = Registry.OpenRemoteBaseKey(RegistryHive.LocalMachine, computer).OpenSubKey("Software\\Microsoft\\Windows NT\\CurrentVersion"); string version = registryKey.GetValue("productName").ToString(); - vapcguy
1
此外,为了满足 OP 的要求,服务包位于相同的位置,不同的字符串:string servicePack = rk.GetValue("CSDVersion").ToString(); - vapcguy

7

示例输出:

Name = Windows Vista
Edition = Home Premium
Service Pack = Service Pack 1
Version = 6.0.6001.65536
Bits = 64

示例类:

class Program
{
    static void Main( string[] args )
    {
        Console.WriteLine( "Operation System Information" );
        Console.WriteLine( "----------------------------" );
        Console.WriteLine( "Name = {0}", OSInfo.Name );
        Console.WriteLine( "Edition = {0}", OSInfo.Edition );
        Console.WriteLine( "Service Pack = {0}", OSInfo.ServicePack );
        Console.WriteLine( "Version = {0}", OSInfo.VersionString );
        Console.WriteLine( "Bits = {0}", OSInfo.Bits );
        Console.ReadLine();
    }
}

OSInfo类的源代码: http://www.csharp411.com/determine-windows-version-and-edition-with-c/ 但是,这段代码中存在错误,您需要将“case 6”语句(即在#endregion NAME之前)替换为以下内容:

case 6:
    switch (minorVersion)
    {
        case 0:

            switch (productType)
            {
                case 1:
                    name = "Windows Vista";
                    break;
                case 3:
                    name = "Windows Server 2008";
                    break;
            }
            break;
        case 1:
            switch (productType)
            {
                case 1:
                    name = "Windows 7";
                    break;
                case 3:
                    name = "Windows Server 2008 R2";
                    break;
            }
            break;
    }
    break;

如果您想更进一步,查看您的程序是否在64位或32位运行:

public static class Wow
{
    public static bool Is64BitProcess
    {
        get { return IntPtr.Size == 8; }
    }

    public static bool Is64BitOperatingSystem
    {
        get
        {
            // Clearly if this is a 64-bit process we must be on a 64-bit OS.
            if (Is64BitProcess)
                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;
        }
    }

    static bool ModuleContainsFunction(string moduleName, string methodName)
    {
        IntPtr hModule = GetModuleHandle(moduleName);
        if (hModule != IntPtr.Zero)
            return GetProcAddress(hModule, methodName) != IntPtr.Zero;
        return false;
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    extern static bool IsWow64Process(IntPtr hProcess, [MarshalAs(UnmanagedType.Bool)] out bool isWow64);
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    extern static IntPtr GetCurrentProcess();
    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    extern static IntPtr GetModuleHandle(string moduleName);
    [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
    extern static IntPtr GetProcAddress(IntPtr hModule, string methodName);
}

1
我有Windows 8.1,但它显示为Windows Vista...查看源代码,甚至没有Windows 7或8的case语句... - The Muffin Man
2
@TheMuffinMan - 可能是因为那是2.5年前编写的吧?随着Windows新版本的发布,你需要更新它。 - Orwellophile

3
需要翻译的内容:

需要注意的是,这些信息通常是本地化的,并且会根据操作系统语言而有所不同。

您可以从WMI中获取大量信息,查找Win32_OperatingSystem类。


WMI似乎是未来的趋势,因为它可以直接返回友好名称而无需任何转换...我会仔细研究的。谢谢... - Stefan Koell

2
请注意,处理器架构问题比较复杂:
你的意思是(更高的数字要求低数字为真):
1. CPU能够处理64位(即支持AMD / Intel x64或Itanium) 2. 操作系统是64位
- GPR和指针为64位,即XP 64、Vista 64、64位服务器版本或用于mono的64位操作系统
3. 当前正在执行的进程是64位进程(例如,不在Wow64下执行)
如果您确认这三个条件都必须为真,则:
IntPtr.Size == 8

表示三个条件都为真


这不就相当于 IntPtr.Size == 8 吗? - Hosam Aly
我理解这个问题很复杂 - 就像你所说的一样。但是我只是想知道哪个框架版本正在处理我的可执行文件。因此,我认为IntPtr方法就足够了。 - Stefan Koell
@Hosam:不完全是。IntPtr.Size 是正确的做法。 - configurator
@configurator:您能否详细解释一下它们的区别? - Hosam Aly
2
为什么不使用 Environment.Is64BitOperatingSystem 或 Environment.Is64BitProcess? - Dave

2
你可以使用Visual Basic设备获取版本信息。 代码:
using Microsoft.VisualBasic.Devices;

var versionID = new ComputerInfo().OSVersion;//6.1.7601.65536
var versionName = new ComputerInfo().OSFullName;//Microsoft Windows 7 Ultimate
var verionPlatform = new ComputerInfo().OSPlatform;//WinNT

Console.WriteLine(versionID);
Console.WriteLine(versionName);
Console.WriteLine(verionPlatform);

输出:

6.1.7601.65536

Microsoft Windows 10 Enterprise

WinNT

注意: 您需要添加对 Microsoft.VisualBasic; 的引用。


1
虽然我可以理解在某些项目中添加VisualBasic引用的犹豫,但我的项目的范围和上下文并不会让我感到困扰。这对我来说是一个很好的解决方案。 - omJohn8372
Windows 10具有版本10.0.x.x。而6.1.7601.x对应的是Windows 7。从这里能够得到的最大值是6.2.9200.0,它是Windows 8(.1)。 - Vertigo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接