确定程序是否在Windows Server上运行

18

我想确定我的程序是否在Windows Server版本上运行。显然,System.Environment不包含有关Windows是否为服务器版本的信息(在操作系统版本对象中没有此类信息)。

我知道可以使用SystemInformation.TerminalServerSession来检查程序是否在远程桌面上运行(参见这个问题),但如果用户仅远程访问普通客户端Windows机器,这也是正确的。

那么,有没有一种支持的方法来确定代码是在服务器上还是在客户机上运行?如有必要,我不介意使用P/Invoke。

注意:我不想在产品名称中搜索“Server”字符串,因为由于本地化,这可能在某些系统上无法正常工作。

6个回答

28

感谢Nick的回答提供的指针,我终于找到了我所需要的东西。函数IsOS(OS_ANYSERVER)正好符合我的要求。这是一个示例代码,应该适用于任何操作系统版本(包括Vista之前的版本,因为我们通过从shlwapi.dll中按序号导入IsOS函数):

class OS
{
    public static bool IsWindowsServer()
    {
        return OS.IsOS (OS.OS_ANYSERVER);
    }

    const int OS_ANYSERVER = 29;

    [DllImport("shlwapi.dll", SetLastError=true, EntryPoint="#437")]
    private static extern bool IsOS(int os);
}

1
如果Nick给了你答案,你用它来获取所需信息,那么你应该接受那个答案而不是自己的(这里的信息应该在你的问题中进行编辑)。 - Thorbjørn Ravn Andersen
7
好的,Nick没有提供IsOS方案。因此,将我的答案标记为被接受的答案对其他读者更有用。我已经点赞了Nick的回答。 - Pierre Arnaud
2
这是一个很好的答案,感谢帮助。我在调用外部方法时总是使用一个非常好的网站:http://www.pinvoke.net/。这里是IsOS方法:http://www.pinvoke.net/default.aspx/shlwapi/IsOS.html - Major
从 Windows 11 操作系统开始,即使是桌面操作系统,OS_ANYSERVER 标志也会返回 True。有什么解决方法吗? - ZappySys

6
你可以调用以下Win32函数:
- 对于Vista/Windows Server 2008+,使用GetProductInfo。 - 对于Windows 2000+,使用GetVersionEx
关于此内容,BJ Rollison在他的博客上有一篇好文章以及示例代码,可以进行参考。

谢谢提供的链接。我最终通过调用IsOS解决了这个问题。 - Pierre Arnaud

2

IsWindowsServer 是在 VersionHelpers.h 头文件中的一种内联函数。

您可以在计算机上找到并阅读该头文件。它使用了 API 函数 VerifyVersionInfoW

kernel32.dll 中没有 IswindowsServer 函数。


1
我曾经遇到过同样的问题,尽管是在脚本编写中。
我已经找到了这个值,并正在使用WMI进行查询:
https://msdn.microsoft.com/en-us/library/aa394239(v=vs.85).aspx
Win32_OperatingSystem
ProductType
    Data type: uint32
    Access type: Read-only
    Additional system information.
    Work Station (1)
    Domain Controller (2)
    Server (3)

我测试了以下操作系统版本:

  • Windows XP Professional SP3
  • Windows 7 Enterprise
  • Windows 8.1 Pro
  • Windows 8.1 Enterprise
  • Windows 10 Pro 10.0.16299
  • Windows Server 2003 R2 Standard Edition
  • Windows Server 2003 R2 Standard Edition x64
  • Windows Server 2008 R2 Standard
  • Windows Server 2012 Datacenter
  • Windows Server 2012 R2 Datacenter

以下是我的批处理文件示例。

Lucas。

for /f "tokens=2 delims==" %%a in ( 'wmic.exe os get producttype /value' ) do (
    set PRODUCT_TYPE=%%a
)
if %PRODUCT_TYPE%==1 set PRODUCT_TYPE=Workstation
if %PRODUCT_TYPE%==2 set PRODUCT_TYPE=DomainController
if %PRODUCT_TYPE%==3 set PRODUCT_TYPE=Server
echo %COMPUTERNAME%: %PRODUCT_TYPE%

您可以轻松地在C#中完成这个操作:

using Microsoft.Management.Infrastructure;
...
string Namespace = @"root\cimv2";
string className = "Win32_OperatingSystem";

CimInstance operatingSystem = new CimInstance(className, Namespace);

0
我也尝试了@ylax的答案,但它指向了一个C++宏,而不是你可以从C#中调用的内容。
我已经生成了这个辅助方法,希望能对某人有所帮助:
static class WindowsOperatingSystemHelper
{
    [DllImport("kernel32.dll", SetLastError = true)]
    [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)]
    public static extern bool GetProductInfo(
       int dwOSMajorVersion,
       int dwOSMinorVersion,
       int dwSpMajorVersion,
       int dwSpMinorVersion,
       out int pdwReturnedProductType);

    public enum OperatingSystemSKU
    {
        UNDEFINED = 0,
        ULTIMATE = 1,
        HOME_BASIC = 2,
        HOME_PREMIUM = 3,
        ENTERPRISE = 4,
        HOME_BASIC_N = 5,
        BUSINESS = 6,
        STANDARD_SERVER = 7,
        DATACENTER_SERVER = 8,
        SMALLBUSINESS_SERVER = 9,
        ENTERPRISE_SERVER = 10,
        STARTER = 11,
        DATACENTER_SERVER_CORE = 12,
        STANDARD_SERVER_CORE = 13,
        ENTERPRISE_SERVER_CORE = 14,
        ENTERPRISE_SERVER_IA64 = 15,
        BUSINESS_N = 16,
        WEB_SERVER = 17,
        CLUSTER_SERVER = 18,
        HOME_SERVER = 19,
        STORAGE_EXPRESS_SERVER = 20,
        STORAGE_STANDARD_SERVER = 21,
        STORAGE_WORKGROUP_SERVER = 22,
        STORAGE_ENTERPRISE_SERVER = 23,
        SERVER_FOR_SMALLBUSINESS = 24,
        SMALLBUSINESS_SERVER_PREMIUM = 25,
        HOME_PREMIUM_N = 26,
        ENTERPRISE_N = 27,
        ULTIMATE_N = 28,
        WEB_SERVER_CORE = 29,
        MEDIUMBUSINESS_SERVER_MANAGEMENT = 30,
        MEDIUMBUSINESS_SERVER_SECURITY = 31,
        MEDIUMBUSINESS_SERVER_MESSAGING = 32,
        SERVER_FOUNDATION = 33,
        HOME_PREMIUM_SERVER = 34,
        SERVER_FOR_SMALLBUSINESS_V = 35,
        STANDARD_SERVER_V = 36,
        DATACENTER_SERVER_V = 37,
        ENTERPRISE_SERVER_V = 38,
        DATACENTER_SERVER_CORE_V = 39,
        STANDARD_SERVER_CORE_V = 40,
        ENTERPRISE_SERVER_CORE_V = 41,
        HYPERV = 42,
        STORAGE_EXPRESS_SERVER_CORE = 43,
        STORAGE_STANDARD_SERVER_CORE = 44,
        STORAGE_WORKGROUP_SERVER_CORE = 45,
        STORAGE_ENTERPRISE_SERVER_CORE = 46,
        STARTER_N = 47,
        PROFESSIONAL = 48,
        PROFESSIONAL_N = 49,
        SB_SOLUTION_SERVER = 50,
        SERVER_FOR_SB_SOLUTIONS = 51,
        STANDARD_SERVER_SOLUTIONS = 52,
        STANDARD_SERVER_SOLUTIONS_CORE = 53,
        SB_SOLUTION_SERVER_EM = 54,
        SERVER_FOR_SB_SOLUTIONS_EM = 55,
        SOLUTION_EMBEDDEDSERVER = 56,
        SOLUTION_EMBEDDEDSERVER_CORE = 57,
        PROFESSIONAL_EMBEDDED = 58,
        ESSENTIALBUSINESS_SERVER_MGMT = 59,
        ESSENTIALBUSINESS_SERVER_ADDL = 60,
        ESSENTIALBUSINESS_SERVER_MGMTSVC = 61,
        ESSENTIALBUSINESS_SERVER_ADDLSVC = 62,
        SMALLBUSINESS_SERVER_PREMIUM_CORE = 63,
        CLUSTER_SERVER_V = 64,
        EMBEDDED = 65,
        STARTER_E = 66,
        HOME_BASIC_E = 67,
        HOME_PREMIUM_E = 68,
        PROFESSIONAL_E = 69,
        ENTERPRISE_E = 70,
        ULTIMATE_E = 71,
        ENTERPRISE_EVALUATION = 72,
        MULTIPOINT_STANDARD_SERVER = 76,
        MULTIPOINT_PREMIUM_SERVER = 77,
        STANDARD_EVALUATION_SERVER = 79,
        DATACENTER_EVALUATION_SERVER = 80,
        ENTERPRISE_N_EVALUATION = 84,
        EMBEDDED_AUTOMOTIVE = 85,
        EMBEDDED_INDUSTRY_A = 86,
        THINPC = 87,
        EMBEDDED_A = 88,
        EMBEDDED_INDUSTRY = 89,
        EMBEDDED_E = 90,
        EMBEDDED_INDUSTRY_E = 91,
        EMBEDDED_INDUSTRY_A_E = 92,
        STORAGE_WORKGROUP_EVALUATION_SERVE = 95,
        STORAGE_STANDARD_EVALUATION_SERVER = 96,
        CORE_ARM = 97,
        CORE_N = 98,
        CORE_COUNTRYSPECIFIC = 99,
        CORE_SINGLELANGUAGE = 100,
        CORE = 101,
        PROFESSIONAL_WMC = 103,
        EMBEDDED_INDUSTRY_EVAL = 105,
        EMBEDDED_INDUSTRY_E_EVAL = 106,
        EMBEDDED_EVAL = 107,
        EMBEDDED_E_EVAL = 108,
        NANO_SERVER = 109,
        CLOUD_STORAGE_SERVER = 110,
        CORE_CONNECTED = 111,
        PROFESSIONAL_STUDENT = 112,
        CORE_CONNECTED_N = 113,
        PROFESSIONAL_STUDENT_N = 114,
        CORE_CONNECTED_SINGLELANGUAGE = 115,
        CORE_CONNECTED_COUNTRYSPECIFIC = 116,
        CONNECTED_CAR = 117,
        INDUSTRY_HANDHELD = 118,
        PPI_PRO = 119,
        ARM64_SERVER = 120,
        EDUCATION = 121,
        EDUCATION_N = 122,
        IOTUAP = 123,
        CLOUD_HOST_INFRASTRUCTURE_SERVER = 124,
        ENTERPRISE_S = 125,
        ENTERPRISE_S_N = 126,
        PROFESSIONAL_S = 127,
        PROFESSIONAL_S_N = 128,
        ENTERPRISE_S_EVALUATION = 129,
        ENTERPRISE_S_N_EVALUATION = 130,
        HOLOGRAPHIC = 135,
        PRO_SINGLE_LANGUAGE = 138,
        PRO_CHINA = 139,
        ENTERPRISE_SUBSCRIPTION = 140,
        ENTERPRISE_SUBSCRIPTION_N = 141,
        DATACENTER_NANO_SERVER = 143,
        STANDARD_NANO_SERVER = 144,
        DATACENTER_A_SERVER_CORE = 145,
        STANDARD_A_SERVER_CORE = 146,
        DATACENTER_WS_SERVER_CORE = 147,
        STANDARD_WS_SERVER_CORE = 148,
        UTILITY_VM = 149,
        DATACENTER_EVALUATION_SERVER_CORE = 159,
        STANDARD_EVALUATION_SERVER_CORE = 160,
        PRO_WORKSTATION = 161,
        PRO_WORKSTATION_N = 162,
        PRO_FOR_EDUCATION = 164,
        PRO_FOR_EDUCATION_N = 165,
        AZURE_SERVER_CORE = 168,
        AZURE_NANO_SERVER = 169,
        ENTERPRISEG = 171,
        ENTERPRISEGN = 172,
        SERVERRDSH = 175,
        CLOUD = 178,
        CLOUDN = 179,
        HUBOS = 180,
        ONECOREUPDATEOS = 182,
        CLOUDE = 183,
        ANDROMEDA = 184,
        IOTOS = 185,
        CLOUDEN = 186,
    }


    public static bool TryGetOperatingSystem(this Version version, out OperatingSystemSKU sku)
    {
        sku = OperatingSystemSKU.UNDEFINED;
        if (GetProductInfo(version.Major, version.Minor, 0, 0, out int productType))
        {
            sku = (OperatingSystemSKU)productType;
        }
        return sku != OperatingSystemSKU.UNDEFINED;
    }
    public static bool IsWindowsServer()
    {
        if (TryGetOperatingSystem(Environment.OSVersion.Version, out var osSKU))
        {
            return osSKU.ToString().Contains("SERVER") || osSKU.ToString().Contains("DATA_CENTER") || osSKU == OperatingSystemSKU.CLUSTER_SERVER;
        }
        else
        {
            return false;
        }
    }
}

我从一个Powershell网站上得到了这个枚举,我猜当微软生成一个新的操作系统时,每隔一段时间就需要更新它。
像这样调用它:
if (WindowsOperatingSystemHelper.IsWindowsServer())
{
... your magic
}

或者如果你想知道操作系统:
 if (WindowsOperatingSystemHelper.TryGetOperatingSystem(Environment.OSVersion.Version, out var operatingSystem))
 {
    ... your magic
 }

-1

WinAPI 中的 VersionHelpers.h 头文件中应该有一组“版本助手函数”的定义,这些函数在内核32.DLL 的汇编中实现。根据文档,适用于您情况的函数是 IsWindowsServer(void)。描述如下:

http://msdn.microsoft.com/en-us/library/windows/desktop/dn424963%28v=vs.85%29.aspx

在C#中,代码应该是这样的(未经测试):
using System.Runtime.InteropServices;

public static class MyClass
{
    [DllImport("Kernel32.dll")]
    public static extern Boolean IsWindowsServer();
}

然后消费代码将会是这样:

bool is_it_a_server = MyClass.IsWindowsServer();

我从未使用过这些函数,所以请告诉我它们是如何工作的...


当我使用Kernel32.dll或Ntdll.dll进行DllImport时,出现了一个错误,无法在这些程序集中找到IsWindowsServer()函数。 - Brundle
请查看Paul Hoepping的回答,他解释了为什么在kernel32.dll中找不到IsWindowsServer - Pierre Arnaud
这个函数不是可互操作的,总是要检查一下这个网站http://www.pinvoke.net/,每次都对我很有帮助。 - Major

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