繁体   English   中英

如何从远程 Windows 计算机检索机器名称?

[英]How can I retrieve the machine name from a remote Windows computer?

我试图找到一种从 C# 中的 IP 地址检索计算机名称的方法,但是所有在线标记为检索机器名称或计算机名称的答案实际上是获取主机名,而不是计算机名称。 如果您转到“控制面板”>“系统”,则该菜单“计算机名称”中有一个属性...我正在远程计算机上查找此值。 AFAIK,如果没有 DNS 映射,HOSTNAME 将 =完整的计算机名称。 问题是我正在使用的这些服务器确实有 DNS 映射,因此主机名会返回它们的 DNS 地址。

如果我说错了什么,请随时纠正我的技术细节,但问题仍然存在。

我试过这个:

IPHostEntry hostEntry = Dns.GetHostEntry(_ip);

_hostname = hostEntry.HostName;

但显然返回主机名,而不是计算机名称。 我也可以接受返回的“完整计算机名称”属性,然后简单地去除字符串中不需要的部分以显示“计算机名称”。

另外,如果您知道如何使用 PowerShell 执行此操作,我也可以使用您的帮助。 无论如何,我在我的应用程序中托管 PowerShell 引擎......所以可以简单地将您的命令传递到PowerShellInstance.AddScript(_yourCommandHere); 并将其返回到我的应用程序中。

请告知是否可以这样做。

@DanielAWhite 编辑:这如何与列出的答案重复? 在该职位的回答说正是我张贴作为问题这个问题。 不,这不是重复的,因为我不是在寻找主机名。 我在我的 OP 中特别告诉过你,我不是在寻找那个,他们也没有问我在问什么。 如果无法从 .NET 中的 IP 获取计算机名称,那么只需回答这个问题。

从“重复”:

好吧,并非每个 IP 地址都有名称。 但是,给定 IPAddress,您可以使用 >Dns.GetHostEntry 来尝试解决它。 另请注意,如果它是 NAT > 路由器,您将获得路由器的 IP 地址,而不是它们的实际 > 机器。

看看我的 OP... .GetHostEntry 不起作用。 这就是我花时间打字的全部原因。

谢谢

双重编辑:培根有一个关于如何做到这一点的答案; 这篇文章被锁定是因为有人没有花时间真正阅读我写的内容。 由于它已锁定,您也无法给出更好的答案。 但我是这样做的,将其保存在此处以备将来参考:

        //declare a string to be our machinename
        string machineName;
        //declare a string which we will pass into powershell later as script
        //assigns the hostname or IP
        string getComputer = "$ip = " + "\"" + ip + "\"" + "\r\n";
        //add to the string this, which gets the Win32_ComputerSystem.. @BACON knew what I was after
        //we pipe that back using |select -expand Name
        getComputer += "get-wmiobject -class Win32_ComputerSystem -property Name -ComputerName " + "$ip " +
            "|select -expand Name";
        //create a powershell instance using
        using (PowerShell PowerShellInstance = PowerShell.Create())
        {
            //add the script into our instance of ps
            PowerShellInstance.AddScript(getComputer);
            //instantiate a collection to house our output from PS
            //you could also probably just instantiate a PSObject instead of a collection.. but this might be useful if modified to get an array of computer names... and this is how I did it so can't verify
            Collection<PSObject> psOutput;
            //assign psOutput from .Invoke() method
            psOutput = PowerShellInstance.Invoke();

            //you could trim this loop and get rid of it for only one IP
            foreach (var item in psOutput)
            {
               //machineName = MachineName||ComputerName string NOT hostname
                machineName = item.BaseObject.ToString();
            }
        }

哦,根据评论中的培根,您必须通过 Windows 防火墙允许 WMI 才能正常工作。 它非常适合我。

重新构建我的评论作为答案......

想象一下我们有一个这样的interface ......

namespace SO56585341
{
    public interface IComputerInfoSource
    {
        string GetComputerName();
    }
}

有几种方法可以实现这一点以获取本地计算机的机器名称。 最简单的是返回Environment.MachineName属性的值...

namespace SO56585341
{
    public class EnvironmentClassComputerInfoSource : IComputerInfoSource
    {
        public string GetComputerName()
        {
            return System.Environment.MachineName;
        }
    }
}

您还可以使用Environment.GetEnvironmentVariable()方法来检索%ComputerName%环境变量的值...

namespace SO56585341
{
    public class EnvironmentVariableComputerInfoSource : IComputerInfoSource
    {
        public string GetComputerName()
        {
            return System.Environment.GetEnvironmentVariable("ComputerName");
        }
    }
}

您可以调用GetComputerName() Windows API 函数,这就是Environment.MachineName 在幕后所做......

using System.Runtime.InteropServices;
using System.Text;

namespace SO56585341
{
    public class WinApiComputerInfoSource : IComputerInfoSource
    {
        private const int MAX_COMPUTERNAME_LENGTH = 15;

        [DllImport("Kernel32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool GetComputerName(
            StringBuilder lpBuffer,
            ref int nSize
        );

        public string GetComputerName()
        {
            int maxCapacity = MAX_COMPUTERNAME_LENGTH + 1;
            StringBuilder nameBuilder = new StringBuilder(maxCapacity, maxCapacity);

            if (!GetComputerName(nameBuilder, ref maxCapacity))
            {
                // TODO: Error handling...
                throw new System.ComponentModel.Win32Exception();
            }

            return nameBuilder.ToString();
        }
    }
}

您可以使用 WMI 来检索单例Win32_ComputerSystemName属性。 您可以通过实例化Win32_ComputerSystem类的ManagementClass实例并在其上调用GetInstances()以检索包含唯一实例的数组来实现此目的...

using System.Linq;
using System.Management;

namespace SO56585341
{
    public class WmiClassComputerInfoSource : IComputerInfoSource
    {
        public string GetComputerName()
        {
            using (ManagementClass computerSystemClass = new ManagementClass("Win32_ComputerSystem"))
            using (ManagementObjectCollection computerSystemCollection = computerSystemClass.GetInstances())
            using (ManagementObject computerSystem = computerSystemCollection.Cast<ManagementObject>().Single())
                return (string) computerSystem["Name"];
        }
    }
}

...或通过创建ManagementObjectSearcher并使用它来Get()唯一的Win32_ComputerSystem实例...

using System.Linq;
using System.Management;

namespace SO56585341
{
    public class WmiSearcherComputerInfoSource : IComputerInfoSource
    {
        public string GetComputerName()
        {
            ObjectQuery computerSystemQuery = new SelectQuery("Win32_ComputerSystem");

            using (ManagementObjectSearcher computerSystemSearcher = new ManagementObjectSearcher(computerSystemQuery))
            using (ManagementObjectCollection computerSystemCollection = computerSystemSearcher.Get())
            using (ManagementObject computerSystem = computerSystemCollection.Cast<ManagementObject>().Single())
                return (string) computerSystem["Name"];
        }
    }
}

最后,上述所有方法返回的值似乎最终都存储在注册表中,因此如果您不介意依赖该实现细节,您可以直接从那里检索它...

using Microsoft.Win32;

namespace SO56585341
{
    public class RegistryComputerInfoSource : IComputerInfoSource
    {
        public string GetComputerName()
        {
            // See also @"SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName\"
            // https://www.oreilly.com/library/view/windows-nt-workstation/9781565926134/10_chapter-07.html
            const string valueParentKeyPath = @"SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName\";

            using (RegistryKey parentKey = Registry.LocalMachine.OpenSubKey(valueParentKeyPath, false))
                return (string) parentKey.GetValue("ComputerName");
        }
    }
}

至于从远程计算机获取相同的值,只有上面的最后三个实现可以工作,尽管需要最少的调整。 首先,为了完成这个IComputerInfoSource示例,让我们创建一个abstract类来保存远程机器名称/地址“参数”...

namespace SO56585341
{
    public abstract class RemoteComputerInfoSource : IComputerInfoSource
    {
        public string RemoteNameOrIp
        {
            get;
        }

        protected RemoteComputerInfoSource(string nameOrIp)
        {
            RemoteNameOrIp = nameOrIp ?? throw new System.ArgumentNullException(nameof(nameOrIp));  
        }

        public abstract string GetComputerName();
    }
}

通过ManagementClass检索Win32_ComputerSystem实例只是显式传递给它一个ManagementPath的问题,该ManagementPath还指定了NamespacePathServer ...

using System.Linq;
using System.Management;

namespace SO56585341
{
    public class RemoteWmiClassComputerInfoSource : RemoteComputerInfoSource
    {
        public RemoteWmiClassComputerInfoSource(string nameOrIp)
            : base(nameOrIp)
        {
        }

        public override string GetComputerName()
        {
            ManagementPath computerSystemPath = new ManagementPath() {
                ClassName = "Win32_ComputerSystem",
                NamespacePath = @"root\cimv2",
                Server = RemoteNameOrIp
            };

            using (ManagementClass computerSystemClass = new ManagementClass(computerSystemPath))
            using (ManagementObjectCollection computerSystemCollection = computerSystemClass.GetInstances())
            using (ManagementObject computerSystem = computerSystemCollection.Cast<ManagementObject>().Single())
                return (string) computerSystem["Name"];
        }
    }
}

ManagementObjectSearcher可以通过传递一个类似被用于ManagementPath包裹在ManagementScope ...

using System.Linq;
using System.Management;

namespace SO56585341
{
    public class RemoteWmiSearcherComputerInfoSource : RemoteComputerInfoSource
    {
        public RemoteWmiSearcherComputerInfoSource(string nameOrIp)
            : base(nameOrIp)
        {
        }

        public override string GetComputerName()
        {
            ManagementScope computerSystemScope = new ManagementScope(
                new ManagementPath() {
                    NamespacePath = @"root\cimv2",
                    Server = RemoteNameOrIp
                }
            );
            ObjectQuery computerSystemQuery = new SelectQuery("Win32_ComputerSystem");

            using (ManagementObjectSearcher computerSystemSearcher = new ManagementObjectSearcher(computerSystemScope, computerSystemQuery))
            using (ManagementObjectCollection computerSystemCollection = computerSystemSearcher.Get())
            using (ManagementObject computerSystem = computerSystemCollection.Cast<ManagementObject>().Single())
                return (string) computerSystem["Name"];
        }
    }
}

查询远程注册表只需要额外调用OpenRemoteBaseKey()来获取远程配置单元根目录的句柄...

using Microsoft.Win32;

namespace SO56585341
{
    public class RemoteRegistryComputerInfoSource : RemoteComputerInfoSource
    {
        public RemoteRegistryComputerInfoSource(string nameOrIp)
        : base(nameOrIp)
        {
        }

        public override string GetComputerName()
        {
            // See also @"SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName\"
            // https://www.oreilly.com/library/view/windows-nt-workstation/9781565926134/10_chapter-07.html
            const string valueParentKeyPath = @"SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName\";

            using (RegistryKey baseKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, RemoteNameOrIp))
            using (RegistryKey parentKey = baseKey.OpenSubKey(valueParentKeyPath, false))
                return (string) parentKey.GetValue("ComputerName");
        }
    }
}

如果将上述所有代码编译到一个项目中,则可以使用以下Program类对其进行测试...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace SO56585341
{
    public static class Program
    {
        private const string TestHost = "127.0.0.1";

        public static void Main()
        {
            // Get all non-abstract classes in the executing assembly that implement IComputerInfoSource
            IEnumerable<Type> computerInfoSourceTypes = Assembly.GetExecutingAssembly().GetTypes()
                .Where(type => type.IsClass && !type.IsAbstract && typeof(IComputerInfoSource).IsAssignableFrom(type));

            // For each constructor in each candidate class...
            foreach (Type computerInfoSourceType in computerInfoSourceTypes)
                foreach (ConstructorInfo constructor in computerInfoSourceType.GetConstructors())
                {
                    ParameterInfo[] constructorParameters = constructor.GetParameters();
                    object[] instanceParameters;

                    // If the constructor takes no parameters...
                    if (!constructorParameters.Any())
                        instanceParameters = Array.Empty<object>();
                    // ...or a single string parameter...
                    else if (constructorParameters.Length == 1 && constructorParameters[0].ParameterType == typeof(string))
                        instanceParameters = new object[1] { TestHost };
                    // ...otherwise skip this constructor
                    else
                        continue;

                    // Instantiate the class using the constructor parameters specified above
                    IComputerInfoSource computerInfoSource = (IComputerInfoSource) constructor.Invoke(instanceParameters);
                    string result;

                    try
                    {
                        result = computerInfoSource.GetComputerName();
                    }
                    catch (Exception ex)
                    {
                        result = ex.ToString();
                    }

                    Console.WriteLine(
                        "new {0}({1}).{2}(): \"{3}\"",
                        computerInfoSourceType.Name,
                        string.Join(
                            ", ",
                            instanceParameters.Select(value => $"\"{value}\"")
                        ),
                        nameof(IComputerInfoSource.GetComputerName),
                        result
                    );
                }
        }
    }
}

我发现无论TestHost设置为机器名称、CNAME 还是 IP 地址,此代码都可以工作。 请注意,如果出现以下情况, Remote*ComputerInfoSource类将失败...

  • 适当的服务( RemoteRegistryWinmgmt )没有在远程机器上运行,或者...
  • 远程计算机上未启用适当的防火墙规则(例如WMI-WINMGMT-In-TCP ),或者...
  • 代码不是以有权访问远程服务的用户身份运行的。

至于 PowerShell,应该能够从 C# 移植上述任何方法的代码(直接翻译或使用 PowerShell 的便利)并将它们包装在对Invoke-Command的调用中,因为该代码将在本地执行到远程机器。 例如...

Invoke-Command -ComputerName $nameOrIp -ScriptBlock { $Env:COMPUTERNAME }

...或者...

Invoke-Command -ComputerName $nameOrIp -ScriptBlock {
    # See also 'HKLM:\SYSTEM\CurrentControlSet\Control\ComputerName\ActiveComputerName\'
    # https://www.oreilly.com/library/view/windows-nt-workstation/9781565926134/10_chapter-07.html
    Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName\' -Name 'ComputerName'
}

PowerShell 还具有Get-WmiObject ...

Get-WmiObject   -Class 'Win32_ComputerSystem' -ComputerName $nameOrIp -Property 'Name'

...和Get-CimInstance cmdlet...

Get-CimInstance -Class 'Win32_ComputerSystem' -ComputerName $nameOrIp -Property 'Name'

...这使得使用 WMI 变得更加容易。 一般来说,我会推荐使用 WMI,因为它很容易在 C# 和 PowerShell 中用于本地和远程查询,而且它的存在正是为了检索系统详细信息而无需了解底层 API 调用或数据表示。

请注意,在使用Invoke-CommandGet-CimInstance cmdlet 时, WinRM服务必须在远程计算机上运行,​​并且必须启用适当的防火墙规则(例如WINRM-HTTP-In-TCP-NoScope )。 此外,当将 IP 地址传递给任一 cmdlet 的-ComputerName参数时,该地址必须与WSMan:\\localhost\\Client\\TrustedHosts的值匹配。 如果您需要通过 IP 地址扫描整个网络,我测试发现TrustedHosts接受*通配符但不接受子网掩码、CIDR 表示法或? 通配符。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM