Metodo affidabile per ottenere l'indirizzo MAC della macchina in C #


132

Ho bisogno di un modo per ottenere l'indirizzo MAC di una macchina indipendentemente dal sistema operativo in esecuzione con C #. L'applicazione dovrà funzionare su XP / Vista / Win7 a 32 e 64 bit, nonché su quei sistemi operativi ma con una lingua straniera predefinita. Molti dei comandi C # e le query del sistema operativo non funzionano su tutto il sistema operativo. Qualche idea? Ho raschiato l'output di "ipconfig / all" ma questo è terribilmente inaffidabile poiché il formato di output differisce su ogni macchina.

Grazie


7
Quando dici su tutto il sistema operativo, intendi su diversi sistemi operativi Microsoft?
John Weldon,

Risposte:


137

Soluzione più pulita

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

O:

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

44
O lambda, se questo è il tuo genere! return NetworkInterface.GetAllNetworkInterfaces().Where(nic => nic.OperationalStatus == OperationalStatus.Up).Select(nic => nic.GetPhysicalAddress().ToString()).FirstOrDefault();(Se non è la tua cosa, dovrebbe essere la tua cosa.)
GONeale

7
Modo conciso per ottenere il più veloce: 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();
Graham Laight,

1
Selezionare prima non è sempre l'opzione migliore. Selezione della connessione più utilizzata: stackoverflow.com/a/51821927/3667
Ramunas

Nota di ottimizzazione: è possibile chiamare FirstOrDefaultprima della finale Select. In questo modo, otterrebbe solo l'indirizzo fisico e lo serializzerebbe per l'effettivo NetworkInterfaceche si ottiene. Non dimenticare di aggiungere il segno di spunta null (?) Dopo il FirstOrDefault.
GregaMohorko,

Un modo computazionale più veloce per ottenerlo, non è necessario valutare tutte le reti che soddisfano la condizione data, è necessario solo il primo di essi: NetworkInterface .GetAllNetworkInterfaces() .FirstOrDefault(nic => nic.OperationalStatus == OperationalStatus.Up && nic.NetworkInterfaceType != NetworkInterfaceType.Loopback)? .GetPhysicalAddress().ToString();
Alessandro Muzzi

80

Ecco un codice C # che restituisce l'indirizzo MAC della prima interfaccia di rete operativa. Supponendo che l' NetworkInterfaceassembly sia implementato nel runtime (ovvero Mono) utilizzato su altri sistemi operativi, questo funzionerebbe su altri sistemi operativi.

Nuova versione: restituisce la scheda NIC alla massima velocità che ha anche un indirizzo MAC valido.

/// <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;
}

Versione originale: restituisce solo il primo.

/// <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;
}

L'unica cosa che non mi piace di questo approccio è che se hai un Nortel Packet Miniport o un qualche tipo di connessione VPN ha il potenziale per essere scelto. Per quanto ne so, non c'è modo di distinguere il MAC di un dispositivo fisico da un tipo di interfaccia di rete virtuale.


6
Non limitarti a scegliere la prima interfaccia operativa. Ciò potrebbe restituire interfacce Loopback, schede 3G collegate occasionalmente e così via, che probabilmente non sono quelle che desideri. Il NetworkInterfaceType ( msdn.microsoft.com/en-us/library/... ) vi darà ulteriori informazioni sulla connessione NetworkInterface modo da poter fare una scelta più consapevole. Ricordare inoltre che potrebbero esserci molte connessioni attive su una macchina e il loro ordine potrebbe non essere prevedibile.
Dave R.,

@DaveR. Ho guardato NetworkInterfaceType, praticamente restituisce quasi sempre Ethernet anche quando era un adattatore virtuale nella mia esperienza, quindi l'ho trovato piuttosto inutile.
Blak3r,

1
Dovresti scegliere l'interfaccia con GatewayMetric più basso. Questa dovrebbe essere la connessione che ha "la via più veloce, più affidabile o meno dispendiosa in termini di risorse". Fondamentalmente ti darà l'interfaccia che Windows preferisce usare. Tuttavia, penso che tu abbia bisogno di WMI per farlo davvero. Vedrò se riesco a farlo funzionare ...
Vedi

6
Per completezza, si accede alla classe NetworkInterface conusing System.Net.NetworkInformation;
iancoleman il

1
FWIW, se hai installato una scheda di rete Gigabit e Hyper-V avrai anche una scheda di rete virtuale da 10 gigabit. :) Difficile problema da risolvere ...
Christopher Painter,

10

La proprietà MACAddress della classe WMI Win32_NetworkAdapterConfiguration può fornire l'indirizzo MAC di un adattatore. (Spazio dei nomi System.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"

Se non hai familiarità con l'API WMI (Strumentazione gestione Windows), qui è disponibile una buona panoramica per le app .NET.

WMI è disponibile su tutte le versioni di Windows con il runtime .Net.

Ecco un esempio di codice:

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 è la soluzione migliore se la macchina a cui ti stai connettendo è una macchina Windows, ma se stai guardando un linux, un mac o un altro tipo di scheda di rete, dovrai usare qualcos'altro. Ecco alcune opzioni:

  1. Utilizzare il comando DOS nbtstat -a. Crea un processo, chiama questo comando, analizza l'output.
  2. Prima esegui il ping dell'IP per assicurarti che la tua NIC memorizzi nella cache il comando nella sua tabella ARP, quindi usa il comando DOS arp -a. Analizzare l'output del processo come nell'opzione 1.
  3. Utilizzare una chiamata non gestita temuta per sendarp in iphlpapi.dll

Ecco un esempio dell'articolo n. 3. Questa sembra essere l'opzione migliore se WMI non è una soluzione praticabile:

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);
}

Per dare credito dove è dovuto, questa è la base per quel codice: http://www.pinvoke.net/default.aspx/iphlpapi.sendarp#


Stavo cercando la stessa cosa dell'OP e questa è esattamente la cosa di cui avevo bisogno!
QueueHammer,

Nelle opzioni 1 e 2, intendi i comandi DOS se sei su una macchina Windows e il comando equivalente su Linux o Mac, giusto?
Raikol Amaro,

8

Usiamo WMI per ottenere l'indirizzo mac dell'interfaccia con la metrica più bassa, ad esempio le finestre dell'interfaccia preferiranno usare, in questo modo:

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;
}

O in Silverlight (ha bisogno di una fiducia elevata):

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;
}

Se eseguo questo codice otterrà l'indirizzo della persona che esegue l'applicazione? Significa che non otterrà l'indirizzo IP del server in cui è ospitato, giusto?
Nate Pet,

Ottiene l'indirizzo MAC della macchina host, il server.
Tony,

6

IMHO che restituisce il primo indirizzo mac non è una buona idea, soprattutto quando sono ospitate macchine virtuali. Pertanto controllo la somma dei byte di invio / ricezione e seleziono la connessione più utilizzata, che non è perfetta, ma dovrebbe essere corretta 9/10 volte.

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

Questo metodo determinerà l'indirizzo MAC dell'interfaccia di rete utilizzata per connettersi all'URL e alla porta specificati.

Tutte le risposte qui non sono in grado di raggiungere questo obiettivo.

Ho scritto questa risposta anni fa (nel 2014). Così ho deciso di dargli un piccolo "lifting". Consulta la sezione degli aggiornamenti

    /// <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;
    }

Per chiamarlo devi passare un URL per connetterti in questo modo:

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

È inoltre possibile specificare un numero di porta. Se non specificato, il valore predefinito è 80.

AGGIORNAMENTI:

2020

  • Aggiunti commenti per spiegare il codice.
  • Corretto per essere utilizzato con i sistemi operativi più recenti che utilizzano IPV4 mappato su IPV6 (come Windows 10).
  • Nesting ridotto.
  • Aggiornato il codice usa "var".

1
Questo è molto interessante, lo proverò, poiché nel mio caso vorrei che il client scoprisse a) l'indirizzo di origine utilizzato per comunicare con il mio server (NON sarà necessariamente su Internet) eb) quale indirizzo MAC è di NIC che fornisce questo indirizzo IP ...
Brian B,

5

Potresti scegliere l'ID NIC:

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

Non è l'indirizzo MAC, ma è un identificatore univoco, se è quello che stai cercando.


2

Mi piace molto la soluzione di AVee con la metrica di connessione IP più bassa! Ma se è installato un secondo nic con la stessa metrica, il confronto MAC potrebbe non riuscire ...

Meglio memorizzare la descrizione dell'interfaccia con il MAC. Nei confronti successivi puoi identificare la nic giusta con questa stringa. Ecco un codice di esempio:

   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

diciamo che ho un TcpConnection usando il mio ip locale di 192.168.0.182. Quindi, se mi piacerebbe conoscere l'indirizzo mac di quella scheda NIC chiamerò il meothod come: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;
    }

Questo non è efficiente ... Non consiglierei di farlo.
Ivandro IG Jao,

2

Odio davvero scavare questo vecchio post, ma penso che la domanda meriti un'altra risposta specifica per Windows 8-10.

Utilizzando NetworkInformation dallo spazio dei nomi Windows.Networking.Connectivity , è possibile ottenere l'ID della scheda di rete utilizzata da Windows. Quindi è possibile ottenere l'interfaccia MAC Address dall'interfaccia GetAllNetworkInterfaces () precedentemente menzionata.

Ciò non funzionerà nelle app di Windows Store come NetworkInterface in System.Net.NetworkInformation non espone 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
Questa risposta potrebbe essere migliorata con una breve spiegazione di cosa fa il codice e di come risolve il problema.
Greg the Incredulous,

1

Modificato un po 'il suo codice blak3r. Nel caso abbiate due adattatori con la stessa velocità. Ordina per MAC, in modo da ottenere sempre lo stesso valore.

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è implementato usando varie DLL tra cui iphlpapi.dll... Googling per iphlpapirivela una corrispondente API Win32 documentata in MSDN.


0

Prova questo:

    /// <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;
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.