簡體   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