Надійний спосіб отримати MAC-адресу машини в C #


131

Мені потрібен спосіб отримати MAC-адресу машини незалежно від ОС, на якій вона працює за допомогою C #. Програмі потрібно буде працювати на XP / Vista / Win7 32 та 64 біт, а також на цих ОС, але з іноземною мовою за замовчуванням. Багато команд C # та запити ОС не працюють в ОС. Будь-які ідеї? Я викреслював вихід "ipconfig / all", але це надзвичайно ненадійно, оскільки вихідний формат відрізняється на кожній машині.

Дякую


7
Коли ви говорите через ОС, ви маєте на увазі різні ОС Microsoft?
Джон Велдон

Відповіді:


136

Чистіший розчин

var macAddr = 
    (
        from nic in NetworkInterface.GetAllNetworkInterfaces()
        where nic.OperationalStatus == OperationalStatus.Up
        select nic.GetPhysicalAddress().ToString()
    ).FirstOrDefault();

Або:

String firstMacAddress = NetworkInterface
    .GetAllNetworkInterfaces()
    .Where( nic => nic.OperationalStatus == OperationalStatus.Up && nic.NetworkInterfaceType != NetworkInterfaceType.Loopback )
    .Select( nic => nic.GetPhysicalAddress().ToString() )
    .FirstOrDefault();

44
Або лямбда, якщо це твоя річ! return NetworkInterface.GetAllNetworkInterfaces().Where(nic => nic.OperationalStatus == OperationalStatus.Up).Select(nic => nic.GetPhysicalAddress().ToString()).FirstOrDefault();(Якщо це не твоя річ, це повинна бути твоя річ.)
GONeale

7
Короткий спосіб отримати найшвидший: var networks = NetworkInterface.GetAllNetworkInterfaces(); var activeNetworks = networks.Where(ni => ni.OperationalStatus == OperationalStatus.Up && ni.NetworkInterfaceType != NetworkInterfaceType.Loopback); var sortedNetworks = activeNetworks.OrderByDescending(ni => ni.Speed); return sortedNetworks.First().GetPhysicalAddress().ToString();
Грем Лайт

1
Вибір першого - це не завжди найкращий варіант. Вибір найбільш використовуваного з'єднання: stackoverflow.com/a/51821927/3667
Рамунас

Примітка щодо оптимізації: Ви можете зателефонувати FirstOrDefaultдо фіналу Select. Таким чином, він отримає лише фізичну адресу та серіалізує її для фактично NetworkInterfaceотриманого. Не забудьте додати нульовий чек (?) Після FirstOrDefault.
ГрегаМохорко

Більш швидкий обчислювальний спосіб отримати його, вам не потрібно оцінювати всі мережі, що відповідають заданій умові, вам потрібен лише перший з них: NetworkInterface .GetAllNetworkInterfaces() .FirstOrDefault(nic => nic.OperationalStatus == OperationalStatus.Up && nic.NetworkInterfaceType != NetworkInterfaceType.Loopback)? .GetPhysicalAddress().ToString();
Алессандро Муцці

80

Ось якийсь код C #, який повертає MAC-адресу першого операційного мережевого інтерфейсу. Якщо припустити, що NetworkInterfaceзбірка реалізована під час виконання (тобто Mono), що використовується в інших операційних системах, то це буде працювати в інших операційних системах.

Нова версія: повертає NIC з найшвидшою швидкістю, яка також має дійсну MAC-адресу.

/// <summary>
/// Finds the MAC address of the NIC with maximum speed.
/// </summary>
/// <returns>The MAC address.</returns>
private string GetMacAddress()
{
    const int MIN_MAC_ADDR_LENGTH = 12;
    string macAddress = string.Empty;
    long maxSpeed = -1;

    foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
    {
        log.Debug(
            "Found MAC Address: " + nic.GetPhysicalAddress() +
            " Type: " + nic.NetworkInterfaceType);

        string tempMac = nic.GetPhysicalAddress().ToString();
        if (nic.Speed > maxSpeed &&
            !string.IsNullOrEmpty(tempMac) &&
            tempMac.Length >= MIN_MAC_ADDR_LENGTH)
        {
            log.Debug("New Max Speed = " + nic.Speed + ", MAC: " + tempMac);
            maxSpeed = nic.Speed;
            macAddress = tempMac;
        }
    }

    return macAddress;
}

Оригінальна версія: просто повертає першу.

/// <summary>
/// Finds the MAC address of the first operation NIC found.
/// </summary>
/// <returns>The MAC address.</returns>
private string GetMacAddress()
{
    string macAddresses = string.Empty;

    foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
    {
        if (nic.OperationalStatus == OperationalStatus.Up)
        {
            macAddresses += nic.GetPhysicalAddress().ToString();
            break;
        }
    }

    return macAddresses;
}

Єдине, що мені не подобається в такому підході, це якщо вам подобається Nortel Packet Miniport або якийсь тип VPN-з'єднання, він може бути обраний. Наскільки я можу сказати, немає можливості відрізнити фактичний MAC фізичного пристрою від певного типу інтерфейсу віртуальної мережі.


6
Не вибирайте перший операційний інтерфейс. Це може повернути інтерфейси Loopback, підключені періодично 3G карти тощо, які, мабуть, не є тим, що потрібно. NetworkInterfaceType ( msdn.microsoft.com/en-us/library/… ) надасть вам більше інформації про підключення NetworkInterface, щоб ви могли зробити більш усвідомлений вибір. Також майте на увазі, що на машині може бути багато активних з'єднань, і їх порядок може бути не передбачуваним.
Дейв Р.

@DaveR. Я переглянув NetworkInterfaceType, він в основному майже завжди повертає Ethernet навіть тоді, коли він був віртуальним адаптером за моїм досвідом, тому я вважав його досить марним.
blak3r

1
Вам слід вибрати інтерфейс із найнижчою GatewayMetric. Це має бути з'єднання, яке має "найшвидший, найнадійніший або найменш ресурсомісткий маршрут". В основному це надасть вам інтерфейс, який Windows вважає за краще використовувати. Однак, я думаю, вам потрібна WMI, щоб насправді це отримати. Я побачу, чи зможу я це зробити на роботі ...
AVee

6
Для повноти доступ до класу NetworkInterface має доступ доusing System.Net.NetworkInformation;
iancoleman

1
FWIW, якщо у вас встановлені гігабітні NIC та Hyper-V, ви також матимете 10-гігабітний віртуальний NIC. :) Важко вирішити проблему ...
Крістофер Художник

10

MACADDRESS властивість класу Win32_NetworkAdapterConfiguration WMI може надати вам MAC - адреса закріплюючо в. (Простір імен System.Management Management)

MACAddress

    Data type: string
    Access type: Read-only

    Media Access Control (MAC) address of the network adapter. A MAC address is assigned by the manufacturer to uniquely identify the network adapter.

    Example: "00:80:C7:8F:6C:96"

Якщо ви не знайомі з API WMI (Windows Management Instrumentation), тут є хороший огляд програм .NET.

WMI доступний для всіх версій Windows із виконанням .Net.

Ось приклад коду:

System.Management.ManagementClass mc = default(System.Management.ManagementClass);
ManagementObject mo = default(ManagementObject);
mc = new ManagementClass("Win32_NetworkAdapterConfiguration");

ManagementObjectCollection moc = mc.GetInstances();
    foreach (var mo in moc) {
        if (mo.Item("IPEnabled") == true) {
              Adapter.Items.Add("MAC " + mo.Item("MacAddress").ToString());
         }
     }

9

WMI - найкраще рішення, якщо машина, до якої ви підключаєтесь, - це машина Windows, але якщо ви дивитесь на Linux, Mac чи інший тип мережевого адаптера, вам потрібно буде використовувати щось інше. Ось кілька варіантів:

  1. Використовуйте команду DOS nbtstat -a. Створіть процес, викликайте цю команду, проаналізуйте вихід.
  2. Спочатку введіть IP, щоб переконатися, що ваш NIC кешує команду в таблиці ARP, потім використовуйте команду DOS arp -a. Розбір результатів процесу, як у варіанті 1.
  3. Використовуйте жахливий некерований виклик, щоб відправити sendarp в iphlpapi.dll

Ось зразок предмета №3. Здається, це найкращий варіант, якщо WMI не є життєздатним рішенням:

using System.Runtime.InteropServices;
...
[DllImport("iphlpapi.dll", ExactSpelling = true)]
        public static extern int SendARP(int DestIP, int SrcIP, byte[] pMacAddr, ref uint PhyAddrLen);
...
private string GetMacUsingARP(string IPAddr)
{
    IPAddress IP = IPAddress.Parse(IPAddr);
    byte[] macAddr = new byte[6];
    uint macAddrLen = (uint)macAddr.Length;

    if (SendARP((int)IP.Address, 0, macAddr, ref macAddrLen) != 0)
        throw new Exception("ARP command failed");

    string[] str = new string[(int)macAddrLen];
    for (int i = 0; i < macAddrLen; i++)
        str[i] = macAddr[i].ToString("x2");

    return string.Join(":", str);
}

Щоб надавати кредит там, де належить, це основа для цього коду: http://www.pinvoke.net/default.aspx/iphlpapi.sendarp#


Я шукав те саме, що і ОП, і це саме те, що мені потрібно!
QueueHammer

У варіантах 1 і 2 ви маєте на увазі команди DOS, якщо ви працюєте на машині Windows та еквівалентній команді на Linux або Mac, правда?
Райкол Амаро

8

Ми використовуємо WMI для отримання mac-адреси інтерфейсу з найнижчою метрикою, наприклад, інтерфейс Windows вважатиме за краще використовувати, як це:

public static string GetMACAddress()
{
    ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_NetworkAdapterConfiguration where IPEnabled=true");
    IEnumerable<ManagementObject> objects = searcher.Get().Cast<ManagementObject>();
    string mac = (from o in objects orderby o["IPConnectionMetric"] select o["MACAddress"].ToString()).FirstOrDefault();
    return mac;
}

Або в Silverlight (потребує підвищеної довіри):

public static string GetMACAddress()
{
    string mac = null;
    if ((Application.Current.IsRunningOutOfBrowser) && (Application.Current.HasElevatedPermissions) && (AutomationFactory.IsAvailable))
    {
        dynamic sWbemLocator = AutomationFactory.CreateObject("WbemScripting.SWBemLocator");
        dynamic sWbemServices = sWbemLocator.ConnectServer(".");
        sWbemServices.Security_.ImpersonationLevel = 3; //impersonate

        string query = "SELECT * FROM Win32_NetworkAdapterConfiguration where IPEnabled=true";
        dynamic results = sWbemServices.ExecQuery(query);

        int mtu = int.MaxValue;
        foreach (dynamic result in results)
        {
            if (result.IPConnectionMetric < mtu)
            {
                mtu = result.IPConnectionMetric;
                mac = result.MACAddress;
            }
        }
    }
    return mac;
}

7
public static PhysicalAddress GetMacAddress()
{
    var myInterfaceAddress = NetworkInterface.GetAllNetworkInterfaces()
        .Where(n => n.OperationalStatus == OperationalStatus.Up && n.NetworkInterfaceType != NetworkInterfaceType.Loopback)
        .OrderByDescending(n => n.NetworkInterfaceType == NetworkInterfaceType.Ethernet)
        .Select(n => n.GetPhysicalAddress())
        .FirstOrDefault();

    return myInterfaceAddress;
}

Якщо я запускаю цей код, він отримає адресу особи, яка запускає додаток? Це означає, що він не отримає IP-адресу сервера, де це розміщено, правильно?
Нейт Пет

Він отримує MAC-адресу хост-машини, сервера.
Тоні

6

IMHO, що повертає першу mac-адресу, не є хорошою ідеєю, особливо коли розміщуються віртуальні машини. Тому я перевіряю кількість відправлених / отриманих байтів і вибираю найбільш використовуване з'єднання, що не є ідеальним, але має бути правильним у 9/10 разів.

public string GetDefaultMacAddress()
{
    Dictionary<string, long> macAddresses = new Dictionary<string, long>();
    foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
    {
        if (nic.OperationalStatus == OperationalStatus.Up)
            macAddresses[nic.GetPhysicalAddress().ToString()] = nic.GetIPStatistics().BytesSent + nic.GetIPStatistics().BytesReceived;
    }
    long maxValue = 0;
    string mac = "";
    foreach(KeyValuePair<string, long> pair in macAddresses)
    {
        if (pair.Value > maxValue)
        {
            mac = pair.Key;
            maxValue = pair.Value;
        }
    }
    return mac;
}

6

Цей метод визначить MAC-адресу мережевого інтерфейсу, який використовується для підключення до вказаної URL-адреси та порту.

Усі відповіді тут не здатні досягти цієї мети.

Цю відповідь я написав років тому (у 2014 році). Тож я вирішив трохи підняти обличчя. Перегляньте розділ оновлень

    /// <summary>
    /// Get the MAC of the Netowrk Interface used to connect to the specified url.
    /// </summary>
    /// <param name="allowedURL">URL to connect to.</param>
    /// <param name="port">The port to use. Default is 80.</param>
    /// <returns></returns>
    private static PhysicalAddress GetCurrentMAC(string allowedURL, int port = 80)
    {
        //create tcp client
        var client = new TcpClient();

        //start connection
        client.Client.Connect(new IPEndPoint(Dns.GetHostAddresses(allowedURL)[0], port));

        //wai while connection is established
        while(!client.Connected)
        {
            Thread.Sleep(500);
        }

        //get the ip address from the connected endpoint
        var ipAddress = ((IPEndPoint)client.Client.LocalEndPoint).Address;

        //if the ip is ipv4 mapped to ipv6 then convert to ipv4
        if(ipAddress.IsIPv4MappedToIPv6)
            ipAddress = ipAddress.MapToIPv4();        

        Debug.WriteLine(ipAddress);

        //disconnect the client and free the socket
        client.Client.Disconnect(false);
        
        //this will dispose the client and close the connection if needed
        client.Close();

        var allNetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces();

        //return early if no network interfaces found
        if(!(allNetworkInterfaces?.Length > 0))
            return null;

        foreach(var networkInterface in allNetworkInterfaces)
        {
            //get the unicast address of the network interface
            var unicastAddresses = networkInterface.GetIPProperties().UnicastAddresses;
           
            //skip if no unicast address found
            if(!(unicastAddresses?.Count > 0))
                continue;

            //compare the unicast addresses to see 
            //if any match the ip address used to connect over the network
            for(var i = 0; i < unicastAddresses.Count; i++)
            {
                var unicastAddress = unicastAddresses[i];

                //this is unlikely but if it is null just skip
                if(unicastAddress.Address == null)
                    continue;
                
                var ipAddressToCompare = unicastAddress.Address;

                Debug.WriteLine(ipAddressToCompare);

                //if the ip is ipv4 mapped to ipv6 then convert to ipv4
                if(ipAddressToCompare.IsIPv4MappedToIPv6)
                    ipAddressToCompare = ipAddressToCompare.MapToIPv4();

                Debug.WriteLine(ipAddressToCompare);

                //skip if the ip does not match
                if(!ipAddressToCompare.Equals(ipAddress))
                    continue;

                //return the mac address if the ip matches
                return networkInterface.GetPhysicalAddress();
            }
              
        }

        //not found so return null
        return null;
    }

Щоб зателефонувати, потрібно ввести URL-адресу, щоб з’єднатися так:

var mac = GetCurrentMAC("www.google.com");

Ви також можете вказати номер порту. Якщо не вказано, за замовчуванням дорівнює 80.

ОНОВЛЕННЯ:

2020 рік

  • Додані коментарі для пояснення коду.
  • Виправлено для використання з новішими операційними системами, які використовують IPV4, відображений на IPV6 (як Windows 10).
  • Скорочення гніздування.
  • Оновлений код використовує "var".

1
Це дуже цікаво, я спробую це, оскільки в моєму випадку я хотів би, щоб клієнт виявив а) адресу джерела, яка використовується для спілкування з моїм сервером (він НЕ обов'язково буде через Інтернет) і б) яку MAC-адресу є NIC, який надає цю IP-адресу ...
Брайан Б

5

Ви можете скористатися ідентифікатором NIC:

 foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) {
     if (nic.OperationalStatus == OperationalStatus.Up){
         if (nic.Id == "yay!")
     }
 }

Це не MAC-адреса, але це унікальний ідентифікатор, якщо це те, що ви шукаєте.


2

Мені дуже подобається рішення AVee з найнижчою метрикою IP-з'єднання! Але якщо встановлена ​​друга ніка з такою ж метрикою, порівняння MAC може не вдатися ...

Краще ви зберігаєте опис інтерфейсу з MAC. У наступних порівняннях ви можете ідентифікувати правильний nic за цим рядком. Ось зразок коду:

   public static string GetMacAndDescription()
    {
        ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_NetworkAdapterConfiguration where IPEnabled=true");
        IEnumerable<ManagementObject> objects = searcher.Get().Cast<ManagementObject>();
        string mac = (from o in objects orderby o["IPConnectionMetric"] select o["MACAddress"].ToString()).FirstOrDefault();
        string description = (from o in objects orderby o["IPConnectionMetric"] select o["Description"].ToString()).FirstOrDefault();
        return mac + ";" + description;
    }

    public static string GetMacByDescription( string description)
    {
        ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_NetworkAdapterConfiguration where IPEnabled=true");
        IEnumerable<ManagementObject> objects = searcher.Get().Cast<ManagementObject>();
        string mac = (from o in objects where o["Description"].ToString() == description select o["MACAddress"].ToString()).FirstOrDefault();
        return mac;
    }

2

скажімо, у мене є TcpConnection, використовуючи мій локальний ip 192.168.0.182. Тоді, якщо мені захочеться дізнатися mac-адресу цього NIC, я буду називати меод як:GetMacAddressUsedByIp("192.168.0.182")

public static string GetMacAddressUsedByIp(string ipAddress)
    {
        var ips = new List<string>();
        string output;

        try
        {
            // Start the child process.
            Process p = new Process();
            // Redirect the output stream of the child process.
            p.StartInfo.UseShellExecute = false;

            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.CreateNoWindow = true;
            p.StartInfo.FileName = "ipconfig";
            p.StartInfo.Arguments = "/all";
            p.Start();
            // Do not wait for the child process to exit before
            // reading to the end of its redirected stream.
            // p.WaitForExit();
            // Read the output stream first and then wait.
            output = p.StandardOutput.ReadToEnd();
            p.WaitForExit();

        }
        catch
        {
            return null;
        }

        // pattern to get all connections
        var pattern = @"(?xis) 
(?<Header>
     (\r|\n) [^\r]+ :  \r\n\r\n
)
(?<content>
    .+? (?= ( (\r\n\r\n)|($)) )
)";

        List<Match> matches = new List<Match>();

        foreach (Match m in Regex.Matches(output, pattern))
            matches.Add(m);

        var connection = matches.Select(m => new
        {
            containsIp = m.Value.Contains(ipAddress),
            containsPhysicalAddress = Regex.Match(m.Value, @"(?ix)Physical \s Address").Success,
            content = m.Value
        }).Where(x => x.containsIp && x.containsPhysicalAddress)
        .Select(m => Regex.Match(m.content, @"(?ix)  Physical \s address [^:]+ : \s* (?<Mac>[^\s]+)").Groups["Mac"].Value).FirstOrDefault();

        return connection;
    }

Це не ефективно ... Я б не рекомендував це робити.
Ivandro IG Jao

2

Дуже ненавиджу копати цю стару посаду, але я вважаю, що це питання заслуговує на ще одну відповідь, характерну для Windows 8-10.

Використовуючи NetworkInformation з простору імен Windows.Networking.Connectivity , ви можете отримати ідентифікатор використовуваних вікон мережевого адаптера. Тоді ви можете отримати MAC-адресу інтерфейсу із згаданого раніше GetAllNetworkInterfaces ().

Це не буде працювати в додатках Windows Store, оскільки NetworkInterface в System.Net.NetworkInformation не відкриває GetAllNetworkInterfaces.

string GetMacAddress()
{
    var connectionProfile = NetworkInformation.GetInternetConnectionProfile();
    if (connectionProfile == null) return "";

    var inUseId = connectionProfile.NetworkAdapter.NetworkAdapterId.ToString("B").ToUpperInvariant();
    if(string.IsNullOrWhiteSpace(inUseId)) return "";

    var mac = NetworkInterface.GetAllNetworkInterfaces()
        .Where(n => inUseId == n.Id)
        .Select(n => n.GetPhysicalAddress().GetAddressBytes().Select(b=>b.ToString("X2")))
        .Select(macBytes => string.Join(" ", macBytes))
        .FirstOrDefault();

    return mac;
}

2
string mac = "";
foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
            {

                if (nic.OperationalStatus == OperationalStatus.Up && (!nic.Description.Contains("Virtual") && !nic.Description.Contains("Pseudo")))
                {
                    if (nic.GetPhysicalAddress().ToString() != "")
                    {
                        mac = nic.GetPhysicalAddress().ToString();
                    }
                }
            }
MessageBox.Show(mac);

2
Цю відповідь можна було б покращити за допомогою короткого пояснення того, що робить код і як він вирішує проблему.
Грег Невірний

1

Трохи змінив blak3r його код. Якщо у вас є два адаптери з однаковою швидкістю. Сортувати за MAC, щоб ви завжди отримували однакове значення.

public string GetMacAddress()
{
    const int MIN_MAC_ADDR_LENGTH = 12;
    string macAddress = string.Empty;
    Dictionary<string, long> macPlusSpeed = new Dictionary<string, long>();
    try
    {
        foreach(NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
        {
            System.Diagnostics.Debug.WriteLine("Found MAC Address: " + nic.GetPhysicalAddress() + " Type: " + nic.NetworkInterfaceType);

            string tempMac = nic.GetPhysicalAddress().ToString();

            if(!string.IsNullOrEmpty(tempMac) && tempMac.Length >= MIN_MAC_ADDR_LENGTH)
                macPlusSpeed.Add(tempMac, nic.Speed);
        }

        macAddress = macPlusSpeed.OrderByDescending(row => row.Value).ThenBy(row => row.Key).FirstOrDefault().Key;
    }
    catch{}

    System.Diagnostics.Debug.WriteLine("Fastest MAC address: " + macAddress);

    return macAddress;
}

1
foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
{
     if (nic.OperationalStatus == OperationalStatus.Up)
     {
            PhysicalAddress Mac = nic.GetPhysicalAddress();
     }
}

0

ipconfig.exeреалізовано за допомогою різних DLL-файлів, включаючи iphlpapi.dll... Googling для iphlpapiвиявлення відповідного API Win32, задокументованого в MSDN.


0

Спробуйте це:

    /// <summary>
    /// returns the first MAC address from where is executed 
    /// </summary>
    /// <param name="flagUpOnly">if sets returns only the nic on Up status</param>
    /// <returns></returns>
    public static string[] getOperationalMacAddresses(Boolean flagUpOnly)
    {
        string[] macAddresses = new string[NetworkInterface.GetAllNetworkInterfaces().Count()];

        int i = 0;
        foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
        {
            if (nic.OperationalStatus == OperationalStatus.Up || !flagUpOnly)
            {
                macAddresses[i] += ByteToHex(nic.GetPhysicalAddress().GetAddressBytes());
                //break;
                i++;
            }
        }
        return macAddresses;
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.