Método confiável para obter o endereço MAC da máquina em C #

131

Preciso de uma maneira de obter o endereço MAC de uma máquina, independentemente do sistema operacional em execução usando C #. O aplicativo precisará funcionar no XP / Vista / Win7 de 32 e 64 bits, bem como nesses sistemas operacionais, mas com um padrão de idioma estrangeiro. Muitos dos comandos C # e consultas do SO não funcionam no SO. Alguma ideia? Eu raspei a saída de "ipconfig / all", mas isso é terrivelmente não confiável, pois o formato de saída difere em todas as máquinas.

obrigado


fonte
7
Quando você diz sobre o sistema operacional, quer dizer em diferentes sistemas operacionais da Microsoft?
John Weldon

Respostas:

136

Solução mais limpa

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

Ou:

String firstMacAddress = NetworkInterface
    .GetAllNetworkInterfaces()
    .Where( nic => nic.OperationalStatus == OperationalStatus.Up && nic.NetworkInterfaceType != NetworkInterfaceType.Loopback )
    .Select( nic => nic.GetPhysicalAddress().ToString() )
    .FirstOrDefault();
Mohammed A. Fadil
fonte
44
Ou lambda, se é isso que você gosta! return NetworkInterface.GetAllNetworkInterfaces().Where(nic => nic.OperationalStatus == OperationalStatus.Up).Select(nic => nic.GetPhysicalAddress().ToString()).FirstOrDefault();(Se não é o seu lugar, ele deve ser sua coisa.)
GONeale
7
Forma concisa para obter o mais rápido: 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
Selecionar primeiro nem sempre é a melhor opção. Selecionando a conexão mais usada: stackoverflow.com/a/51821927/3667
Ramunas
Nota de otimização: você pode ligar FirstOrDefaultantes da final Select. Dessa forma, ele obteria apenas o endereço físico e o serializaria para o real NetworkInterfaceque você obtém. Não se esqueça de adicionar a verificação nula (?) Após o FirstOrDefault.
GregaMohorko #
Uma maneira computacional mais rápida de obtê-lo, você não precisa avaliar todas as redes que atendem à condição especificada, precisa apenas do primeiro deles: NetworkInterface .GetAllNetworkInterfaces() .FirstOrDefault(nic => nic.OperationalStatus == OperationalStatus.Up && nic.NetworkInterfaceType != NetworkInterfaceType.Loopback)? .GetPhysicalAddress().ToString();
Alessandro Muzzi 03/04
80

Aqui está um código C # que retorna o endereço MAC da primeira interface de rede operacional. Supondo que a NetworkInterfacemontagem seja implementada no tempo de execução (ou seja, Mono) usado em outros sistemas operacionais, isso funcionaria em outros sistemas operacionais.

Nova versão: retorna a NIC com a velocidade mais rápida que também possui um endereço MAC válido.

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

Versão original: apenas retorna a primeira.

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

A única coisa que eu não gosto nessa abordagem é que, se você tem um Nortel Packet Miniport ou algum tipo de conexão VPN, ele pode ser escolhido. Pelo que sei, não há como distinguir o MAC de um dispositivo físico real de algum tipo de interface de rede virtual.

blak3r
fonte
6
Não basta escolher a primeira interface operacional. Isso pode retornar interfaces de loopback, placas 3G conectadas ocasionalmente e assim por diante, que provavelmente não são o que você deseja. O NetworkInterfaceType ( msdn.microsoft.com/en-us/library/… ) fornecerá mais informações sobre a conexão NetworkInterface para que você possa fazer uma escolha mais informada. Lembre-se também de que pode haver muitas conexões ativas em uma máquina e sua ordem pode não ser previsível.
Dave R.
@DaveR. Eu olhei para NetworkInterfaceType, basicamente, quase sempre retorna Ethernet, mesmo quando era um adaptador virtual na minha experiência, então eu achei bastante inútil.
blak3r
1
Você deve escolher a interface com o GatewayMetric mais baixo. Essa deve ser a conexão que possui a "rota mais rápida, mais confiável ou com menos recursos". Basicamente, ele fornecerá a interface que o Windows prefere usar. No entanto, acho que você precisa do WMI para obter isso. Vou ver se posso chegar a esse trabalho ...
AVEE
6
Para completar, a classe using System.Net.NetworkInformation;
NetworkInterface
1
FWIW, se você tiver uma NIC de gigabit e Hyper-V instalada, também terá uma NIC virtual de 10gigabit. :) Problema difícil de resolver ...
Christopher Painter
10

A propriedade MACAddress da classe WMI Win32_NetworkAdapterConfiguration pode fornecer o endereço MAC de um adaptador. (Espaço para nome 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 você não conhece a API do WMI (Windows Management Instrumentation), há uma boa visão geral aqui dos aplicativos .NET.

O WMI está disponível em todas as versões do Windows com o tempo de execução .Net.

Aqui está um exemplo de código:

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());
         }
     }
Bayard Randel
fonte
9

O WMI é a melhor solução se a máquina à qual você está se conectando for uma máquina Windows, mas se você estiver procurando por um Linux, Mac ou outro tipo de adaptador de rede, precisará usar outra coisa. Aqui estão algumas opções:

  1. Use o comando DOS nbtstat -a. Crie um processo, chame este comando, analise a saída.
  2. Primeiro, faça ping no IP para garantir que sua NIC armazene em cache o comando em sua tabela ARP e, em seguida, use o comando DOS arp -a. Analise a saída do processo como na opção 1.
  3. Use uma chamada não gerenciada temida para sendarp no iphlpapi.dll

Heres uma amostra do item # 3. Essa parece ser a melhor opção se o WMI não for uma solução viável:

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

Para dar crédito onde é devido, esta é a base para esse código: http://www.pinvoke.net/default.aspx/iphlpapi.sendarp#

Brian Duncan
fonte
Eu estava procurando a mesma coisa que o OP e é exatamente isso que eu precisava!
QueueHammer
Nas opções 1 e 2, você quer dizer comandos do DOS se estiver em uma máquina Windows e o comando equivalente no Linux ou Mac, certo?
Raikol Amaro 24/10
8

Usamos o WMI para obter o endereço mac da interface com a métrica mais baixa, por exemplo, as janelas da interface preferem usar, assim:

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

Ou no Silverlight (precisa de confiança elevada):

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;
}
AVee
fonte
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;
}
Tony
fonte
Se eu executar esse código, ele obterá o endereço da pessoa que está executando o aplicativo? Significando que ele não receberá o endereço IP do servidor em que ele está hospedado, correto?
Nate Pet
Ele obtém o endereço MAC da máquina host, o servidor.
Tony
6

O IMHO retornando o primeiro endereço mac não é uma boa ideia, especialmente quando máquinas virtuais estão hospedadas. Portanto, verifico a soma de bytes enviados / recebidos e seleciono a conexão mais usada, que não é perfeita, mas deve estar correta 9/10 vezes.

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;
}
Ramunas
fonte
6

Este método determinará o endereço MAC da interface de rede usada para conectar-se à URL e porta especificadas.

Todas as respostas aqui não são capazes de atingir esse objetivo.

Eu escrevi essa resposta anos atrás (em 2014). Então eu decidi dar um pequeno "lifting facial". Por favor, olhe a seção de atualizações

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

Para chamá-lo, você precisa passar um URL para se conectar da seguinte maneira:

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

Você também pode especificar um número de porta. Se não especificado, o padrão é 80.

ATUALIZAÇÕES:

2020

  • Foram adicionados comentários para explicar o código.
  • Corrigido para ser usado com sistemas operacionais mais recentes que usam IPV4 mapeado para IPV6 (como o Windows 10).
  • Aninhamento reduzido.
  • Atualizado o código, use "var".
Jonathan Alfaro
fonte
1
Isso é muito interessante, vou tentar, pois, no meu caso, gostaria que o cliente descobrisse a) o endereço de origem usado para se comunicar com meu servidor (NÃO necessariamente será pela Internet) eb) qual o endereço MAC é de NIC que está fornecendo esse endereço IP ...
Brian B
5

Você pode ir para o NIC ID:

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

Não é o endereço MAC, mas é um identificador exclusivo, se é isso que você está procurando.

mmr
fonte
2

Eu realmente gosto da solução da AVee com a menor métrica de conexão IP! Mas se um segundo nic com a mesma métrica estiver instalado, a comparação MAC poderá falhar ...

Melhor você armazenar a descrição da interface com o MAC. Em comparações posteriores, você pode identificar o nic certo por essa sequência. Aqui está um código de exemplo:

   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;
    }
Detlef
fonte
2

digamos que eu tenho um TcpConnection usando meu ip local 192.168.0.182. Então, se eu gostaria de saber o endereço mac dessa NIC, chamarei o meothod como: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;
    }
Tono Nam
fonte
Isso não é eficiente ... Eu não recomendaria fazer isso.
Ivandro IG Jao
2

Realmente odeio desenterrar este post antigo, mas acho que a pergunta merece outra resposta específica para o Windows 8-10.

Usando NetworkInformation no namespace Windows.Networking.Connectivity , é possível obter o ID do adaptador de rede que o Windows está usando. Em seguida, você pode obter o endereço MAC da interface do GetAllNetworkInterfaces () mencionado anteriormente.

Isso não funcionará nos Aplicativos da Windows Store, pois o NetworkInterface em System.Net.NetworkInformation não expõe 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;
}
Greg Gorman
fonte
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);
hadi safari
fonte
2
Essa resposta pode ser melhorada com uma breve explicação sobre o que o código faz e como ele resolve o problema.
Greg the Incredulous
1

Mudou um pouco o código do blak3r. Caso você tenha dois adaptadores com a mesma velocidade. Classifique por MAC, para obter sempre o mesmo valor.

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;
}
user369122
fonte
1
foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
{
     if (nic.OperationalStatus == OperationalStatus.Up)
     {
            PhysicalAddress Mac = nic.GetPhysicalAddress();
     }
}
Mohamad Javadi
fonte
0

ipconfig.exeé implementado usando várias DLLs, incluindo iphlpapi.dll... A pesquisa no Google iphlpapirevela uma API Win32 correspondente documentada no MSDN.

ChrisW
fonte
0

Tente o seguinte:

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