如何检测我的应用程序是否在Windows 10上运行

42

我正在寻找一种方法来检测我的C#应用程序是否在Windows 10上运行。

我原本希望使用Environment.OSVersion来完成,但似乎在Windows 8.1和Windows 10上返回版本6.3.9600.0

其他解决方案,如此处,也无法区分Windows 8和Windows 10。

有什么建议吗?


为什么需要这样做呢?

因为我正在使用WinForms WebBrowser控件来托管OAuth页面,在旧版IE中崩溃(我的应用程序连接到用户的Nest账户...)。

默认情况下,WebBrowser控件会模拟IE7。使用注册表键,可以让它模拟安装在主机PC上的最新版本的IE。然而,在Windows 8.1(和Windows 10的预发布版)中可用的值在Windows 10的最终版本中不起作用。


5
你是否为Windows 10制作了该应用程序? - GSerg
你是否使用兼容模式运行程序? - Graffito
有其他获取此信息的方法,请查看此帖子:https://dev59.com/MWw15IYBdhLWcg3w4_7k - Rahul
@Graffito - 我的应用程序无法在兼容模式下运行。 - Richard Ev
你尝试过将 FEATURE_BROWSER_EMULATION 设置为 1200012001 吗? - noseratio - open to work
7个回答

42

答案

使用 Environment.OSVersion 并添加一个应用程序清单文件,取消相关的 supportedOS 元素的注释。

例如,在 <asmv1:assembly> 下添加以下内容:

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> 
    <application> 
        <!-- Windows 10 --> 
        <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
        <!-- Windows 8.1 -->
        <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
        <!-- Windows Vista -->
        <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> 
        <!-- Windows 7 -->
        <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
        <!-- Windows 8 -->
        <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
    </application> 
</compatibility>

原因

我不喜欢@Mitat Koyuncu的答案,因为它无必要地使用了注册表,并且正如评论中提到的那样,使用了不可靠的字符串解析。

我也不喜欢@sstan的答案,因为它使用了第三方代码,并且无论如何都需要应用程序清单。

来自MSDN

Windows 10: VerifyVersionInfo 在被调用时返回 false,如果调用它的应用程序没有针对 Windows 8.1 或 Windows 10 的兼容性清单并且 lpVersionInfo 参数指定了 Windows 8.1 或 Windows 10,即使当前操作系统版本是 Windows 8.1 或 Windows 10。具体而言,VerifyVersionInfo 具有以下行为:

• 如果应用程序没有清单,则 VerifyVersionInfo 的行为就像操作系统版本为 Windows 8(6.2)。

• 如果应用程序具有包含与 Windows 8.1 相对应的 GUID 的清单,则 VerifyVersionInfo 的行为就像操作系统版本为 Windows 8.1(6.3)。

• 如果应用程序具有包含与 Windows 10 相对应的 GUID 的清单,则 VerifyVersionInfo 的行为就像操作系统版本为 Windows 10(10.0)。

原因是 VerifyVersionInfo 在 Windows 10 中已被弃用。

我已在 Windows 10 上测试过,确实当应用程序清单包含上述相关 GUID 时,Environment.OSVersion 正如预期的那样工作。这很可能也是为什么它没有从 .Net Framework 中更改或弃用的原因。


6
这是正确的、官方的方法。我想向其他人指出,当在你的 app.manifest 中进行测试时,请确保它实际上已经被复制到输出文件夹中。当处于调试配置时,我的文件没有被复制到 bin/Debug 中。直到我尝试了发布并深入挖掘后才发现 app.manifest 并没有起作用。 - DJH
对我有用。在将上述内容添加到app.manifest之前,它一直显示NT版本6.2。 - Mr Harno
这个问题唯一缺少的就是一个使用示例。 - Deantwo
这是正确的答案,而不是被接受的答案,Environment.OSVersion 使用 GetVersionGetVersionEx API。来自 MSDN:GetVersionEx API 的行为在返回操作系统版本的值方面发生了变化。GetVersionEx 函数返回的值现在取决于应用程序的清单方式。未针对 Windows 8.1 或 Windows 10 进行清单的应用程序将返回 Windows 8 操作系统版本值(6.2)。 - Ahmed Osama
顺便提一下,微软警告关于GetVersionEx API:在Windows 8.1之后的版本中,GetVersionEx可能会被更改或不可用。请改用Version Helper函数。 - Ahmed Osama

33

如果您查看注册表,您会发现环境名称:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProductName
例如,我的产品名称是Windows 10 Home

Registry

使用这个代码,你可以确定它是否为Windows 10:

 static bool IsWindows10()
 {
     var reg = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion");

     string productName = (string)reg.GetValue("ProductName");

     return productName.StartsWith("Windows 10");
 }
注:在你的using语句中添加using Microsoft.Win32;

注意:在你的using语句中添加using Microsoft.Win32;


4
@RichardEverett 好观点!这些年轻人和他们的“应用程序”,我不知道... :) - Rawling
19
在Windows 10上使用StartsWith是不正确的。 - Boann
5
@MitatKoyuncu 目前它并不存在。我只是在开玩笑(大多数情况下),说它不具备未来性。由于许多代码使用 StartsWith("Windows 9") 来检测 Windows 95 和 98,所以他们不得不跳过 Windows 9。 - Boann
3
不,不要这样做。 - damian
2
最好检查CurrentMajorVersionNumber中的实际数字版本值,就像Spiralis在他们的答案中建议的那样。如果该值不存在,那么您可以安全地假设它不是Win10 ;) - Nyerguds
显示剩余4条评论

16
在底层,Environment.OSVersion 使用了已经被弃用的 GetVersionEx 函数。文档警告了你所观察到的行为:

未经过 Windows 8.1 或 Windows 10 认证的应用将返回 Windows 8 操作系统版本号 (6.2)。

文档还建议:

识别当前操作系统通常不是确定特定操作系统功能是否存在的最佳方式。这是因为操作系统可能在可重新分配的 DLL 中添加了新功能。与其使用 GetVersionEx 确定操作系统平台或版本号,不如测试功能本身的存在性。

如果上述建议不适用于你的情况,并且你确实想要检查实际运行的操作系统版本,则文档也提供了有关此事的提示:

为了比较当前系统版本和所需版本,请使用 VerifyVersionInfo 函数而不是使用 GetVersionEx 自行执行比较。

以下文章发布了使用 VerifyVersionInfo 函数的有效解决方案:.NET 的版本助手 API
完全归功于该文章的作者,以下代码段应提供你所寻找的行为:
public class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(IsWindowsVersionOrGreater(6, 3, 0)); // Plug in appropriate values.
    }

    [StructLayout(LayoutKind.Sequential)]
    struct OsVersionInfoEx
    {
        public uint OSVersionInfoSize;
        public uint MajorVersion;
        public uint MinorVersion;
        public uint BuildNumber;
        public uint PlatformId;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string CSDVersion;
        public ushort ServicePackMajor;
        public ushort ServicePackMinor;
        public ushort SuiteMask;
        public byte ProductType;
        public byte Reserved;
    }

    [DllImport("kernel32.dll")]
    static extern ulong VerSetConditionMask(ulong dwlConditionMask,
       uint dwTypeBitMask, byte dwConditionMask);
    [DllImport("kernel32.dll")]
    static extern bool VerifyVersionInfo(
        [In] ref OsVersionInfoEx lpVersionInfo,
        uint dwTypeMask, ulong dwlConditionMask);

    static bool IsWindowsVersionOrGreater(
        uint majorVersion, uint minorVersion, ushort servicePackMajor)
    {
        OsVersionInfoEx osvi = new OsVersionInfoEx();
        osvi.OSVersionInfoSize = (uint)Marshal.SizeOf(osvi);
        osvi.MajorVersion = majorVersion;
        osvi.MinorVersion = minorVersion;
        osvi.ServicePackMajor = servicePackMajor;
        // These constants initialized with corresponding definitions in
        // winnt.h (part of Windows SDK)
        const uint VER_MINORVERSION = 0x0000001;
        const uint VER_MAJORVERSION = 0x0000002;
        const uint VER_SERVICEPACKMAJOR = 0x0000020;
        const byte VER_GREATER_EQUAL = 3;
        ulong versionOrGreaterMask = VerSetConditionMask(
            VerSetConditionMask(
                VerSetConditionMask(
                    0, VER_MAJORVERSION, VER_GREATER_EQUAL),
                VER_MINORVERSION, VER_GREATER_EQUAL),
            VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
        uint versionOrGreaterTypeMask = VER_MAJORVERSION |
            VER_MINORVERSION | VER_SERVICEPACKMAJOR;
        return VerifyVersionInfo(ref osvi, versionOrGreaterTypeMask,
            versionOrGreaterMask);
    }
}

免责声明:我尚未拥有 Windows 10,因此尚未在 Windows 10 上测试过此代码。


3
在Windows 10中,VerifyVersionInfo函数也已被弃用。尽管您仍然可以调用不推荐使用的函数,但如果您的应用程序没有专门针对Windows 8.1或Windows 10进行目标设置,则会获得Windows 8版本(6.2.0.0)。 - Jf Beaulac
Windows 10:调用VerifyVersionInfo时返回false,仍然返回6.2! - Martin.Martinsson

7

我建议使用注册表来查找您想要的值。由于微软已更改了Windows 10在注册表中的列出方式,因此代码需要进行适应。

这是我使用的代码,可以正确地识别Windows 10:

namespace Inspection
{
    /// <summary>
    /// Static class that adds convenient methods for getting information on the running computers basic hardware and os setup.
    /// </summary>
    public static class ComputerInfo
    {
        /// <summary>
        ///     Returns the Windows major version number for this computer.
        /// </summary>
        public static uint WinMajorVersion
        {
            get
            {
                dynamic major;
                // The 'CurrentMajorVersionNumber' string value in the CurrentVersion key is new for Windows 10, 
                // and will most likely (hopefully) be there for some time before MS decides to change this - again...
                if (TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMajorVersionNumber", out major))
                {
                    return (uint) major;
                }

                // When the 'CurrentMajorVersionNumber' value is not present we fallback to reading the previous key used for this: 'CurrentVersion'
                dynamic version;
                if (!TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentVersion", out version))
                    return 0;

                var versionParts = ((string) version).Split('.');
                if (versionParts.Length != 2) return 0;
                uint majorAsUInt;
                return uint.TryParse(versionParts[0], out majorAsUInt) ? majorAsUInt : 0;
            }
        }

        /// <summary>
        ///     Returns the Windows minor version number for this computer.
        /// </summary>
        public static uint WinMinorVersion
        {
            get
            {
                dynamic minor;
                // The 'CurrentMinorVersionNumber' string value in the CurrentVersion key is new for Windows 10, 
                // and will most likely (hopefully) be there for some time before MS decides to change this - again...
                if (TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentMinorVersionNumber",
                    out minor))
                {
                    return (uint) minor;
                }

                // When the 'CurrentMinorVersionNumber' value is not present we fallback to reading the previous key used for this: 'CurrentVersion'
                dynamic version;
                if (!TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentVersion", out version))
                    return 0;

                var versionParts = ((string) version).Split('.');
                if (versionParts.Length != 2) return 0;
                uint minorAsUInt;
                return uint.TryParse(versionParts[1], out minorAsUInt) ? minorAsUInt : 0;
            }
        }

        /// <summary>
        ///     Returns whether or not the current computer is a server or not.
        /// </summary>
        public static uint IsServer
        {
            get
            {
                dynamic installationType;
                if (TryGetRegistryKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "InstallationType",
                    out installationType))
                {
                    return (uint) (installationType.Equals("Client") ? 0 : 1);
                }

                return 0;
            }
        }

        private static bool TryGetRegistryKey(string path, string key, out dynamic value)
        {
            value = null;
            try
            {
                var rk = Registry.LocalMachine.OpenSubKey(path);
                if (rk == null) return false;
                value = rk.GetValue(key);
                return value != null;
            }
            catch
            {
                return false;
            }
        }
    }
}

2
不错!这对我来说已经足够完成任务了。虽然我没有检查除“CurrentMajorVersionNumber”之外的任何内容,但应用程序只需要知道它是否在Win10上运行。如果注册表键不存在,因为它是旧版本,那么我想任务已经完成了。 - Nyerguds

2
试试这个:
string subKey = @"SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion";
Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.LocalMachine;
Microsoft.Win32.RegistryKey skey = key.OpenSubKey(subKey);

string name = skey.GetValue("ProductName").ToString();

然后你只需使用一个 if 语句:

if(name.Contains("Windows 10"))
{
    //... procedures
}
else
{
   //... procedures
}

2

1

你尝试过以下方法吗?[你需要添加对Microsoft.VisualBasic dll的引用]

new Microsoft.VisualBasic.Devices.ComputerInfo().OSFullName

在我的电脑上,它显示为Microsoft Windows 7 Ultimate

2
在Windows 10中,此功能已被弃用。 - Claies
@Claies,我不知道那是什么,但那很糟糕 :) - Rahul
5
另外...我宁愿避免在我的C#项目中添加VisualBasic引用。 - Richard Ev

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