Método confiable para obtener la dirección MAC de la máquina en C #

131

Necesito una forma de obtener la dirección MAC de una máquina, independientemente del sistema operativo que se ejecuta con C #. La aplicación deberá funcionar en XP / Vista / Win7 de 32 y 64 bits, así como en esos sistemas operativos, pero con un idioma extranjero predeterminado. Muchos de los comandos de C # y las consultas del sistema operativo no funcionan en el sistema operativo. ¿Algunas ideas? He estado raspando la salida de "ipconfig / all" pero esto es terriblemente poco confiable ya que el formato de salida difiere en cada máquina.

Gracias


fuente
77
Cuando dices a través del sistema operativo, ¿te refieres a diferentes sistemas operativos de Microsoft?
John Weldon

Respuestas:

136

Solución más limpia

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();
Mohammed A. Fadil
fuente
44
O lambda, si eso es lo tuyo! return NetworkInterface.GetAllNetworkInterfaces().Where(nic => nic.OperationalStatus == OperationalStatus.Up).Select(nic => nic.GetPhysicalAddress().ToString()).FirstOrDefault();(Si no es lo tuyo, debería ser lo tuyo.)
GONeale
77
Forma concisa de obtener el más 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
Seleccionar primero no siempre es la mejor opción. Selección de la conexión más utilizada: stackoverflow.com/a/51821927/3667
Ramunas
Nota de optimización: puede llamar FirstOrDefaultantes de la final Select. De esta manera, solo obtendría una dirección física y la serializaría según el valor real NetworkInterfaceque obtenga. No olvide agregar el cheque nulo (?) Después de FirstOrDefault.
GregaMohorko
Una forma computacional más rápida de obtenerlo, no necesita evaluar todas las redes que coinciden con la condición dada, solo necesita la primera de ellas: NetworkInterface .GetAllNetworkInterfaces() .FirstOrDefault(nic => nic.OperationalStatus == OperationalStatus.Up && nic.NetworkInterfaceType != NetworkInterfaceType.Loopback)? .GetPhysicalAddress().ToString();
Alessandro Muzzi
80

Aquí hay un código C # que devuelve la dirección MAC de la primera interfaz de red operativa. Suponiendo que el NetworkInterfaceensamblaje se implemente en el tiempo de ejecución (es decir, Mono) utilizado en otros sistemas operativos, esto funcionaría en otros sistemas operativos.

Nueva versión: devuelve la NIC con la velocidad más rápida que también tiene una dirección MAC válida.

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

Versión original: solo devuelve la primera.

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

Lo único que no me gusta de este enfoque es que si tiene un Nortport Packet Miniport o algún tipo de conexión VPN, tiene el potencial de ser elegido. Por lo que puedo decir, no hay forma de distinguir el MAC de un dispositivo físico real de algún tipo de interfaz de red virtual.

blak3r
fuente
66
No solo elija la primera interfaz operativa. Esto podría devolver interfaces de bucle invertido, tarjetas 3G ocasionalmente conectadas, etc., que probablemente no sean lo que desea. NetworkInterfaceType ( msdn.microsoft.com/en-us/library/… ) le brindará más información sobre la conexión NetworkInterface para que pueda tomar una decisión más informada. También tenga en cuenta que podría haber muchas conexiones activas en una máquina y su orden puede no ser predecible.
Dave R.
@DaveR. Miré a NetworkInterfaceType, básicamente, en mi experiencia, casi siempre devuelve Ethernet, incluso cuando era un adaptador virtual, así que lo encontré bastante inútil.
blak3r
1
Debe elegir la interfaz con el GatewayMetric más bajo. Esta debería ser la conexión que tiene "la ruta más rápida, más confiable o menos intensiva en recursos". Básicamente le dará la interfaz que Windows prefiere usar. Sin embargo, creo que necesitas WMI para conseguirlo.
Veré
66
Para completar, la clase NetworkInterface se accede conusing System.Net.NetworkInformation;
iancoleman
1
FWIW, si tiene una NIC gigabit y Hyper-V instalado, también tendrá una NIC virtual de 10 gigabit. :) Problema difícil de resolver ...
Christopher Painter
10

El MACAddress propiedad de la clase WMI Win32_NetworkAdapterConfiguration puede proporcionarle la dirección MAC de un adaptador. (System.Management Namespace)

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"

Si no está familiarizado con la API WMI (Instrumental de administración de Windows), aquí encontrará una buena descripción general de las aplicaciones .NET.

WMI está disponible en todas las versiones de Windows con el tiempo de ejecución .Net.

Aquí hay un ejemplo 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
fuente
9

WMI es la mejor solución si la máquina a la que se está conectando es una máquina Windows, pero si está buscando un adaptador de red para Linux, Mac u otro tipo, entonces necesitará usar otra cosa. Aquí hay algunas opciones:

  1. Use el comando DOS nbtstat -a. Cree un proceso, llame a este comando, analice la salida.
  2. Primero haga ping a la IP para asegurarse de que su NIC almacena en caché el comando en su tabla ARP, luego use el comando DOS arp -a. Analice la salida del proceso como en la opción 1.
  3. Utilice una temida llamada no administrada a sendarp en iphlpapi.dll

Heres una muestra del artículo # 3. Esta parece ser la mejor opción si WMI no es una solución viable:

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 donde es debido, esta es la base de ese código: http://www.pinvoke.net/default.aspx/iphlpapi.sendarp#

Brian Duncan
fuente
¡Estaba buscando lo mismo que el OP y esto es exactamente lo que necesitaba!
QueueHammer
En las opciones 1 y 2, te refieres a los comandos de DOS si estás en una máquina con Windows y el comando equivalente en Linux o Mac, ¿verdad?
Raikol Amaro
8

Usamos WMI para obtener la dirección mac de la interfaz con la métrica más baja, por ejemplo, las ventanas de la interfaz preferirán usar, así:

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 en Silverlight (necesita una confianza 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
fuente
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
fuente
Si ejecuto este código, ¿obtendrá la dirección de la persona que ejecuta la aplicación? Lo que significa que no obtendrá la dirección IP del servidor donde está alojado, ¿correcto?
Nate Pet
Obtiene la dirección MAC de la máquina host, el servidor.
Tony
6

En mi humilde opinión, devolver la primera dirección mac no es una buena idea, especialmente cuando las máquinas virtuales están alojadas. Por lo tanto, verifico la suma de bytes enviados / recibidos y selecciono la conexión más utilizada, eso no es perfecto, pero debe ser correcto 9/10 veces.

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
fuente
6

Este método determinará la dirección MAC de la interfaz de red utilizada para conectarse a la url y el puerto especificados.

Todas las respuestas aquí no son capaces de lograr este objetivo.

Escribí esta respuesta hace años (en 2014). Así que decidí darle un poco de "estiramiento facial". Por favor mira la sección de actualizaciones

    /// <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 llamarlo, debe pasar una URL para conectarse de esta manera:

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

También puede especificar un número de puerto. Si no se especifica, el valor predeterminado es 80.

ACTUALIZACIONES:

2020

  • Comentarios agregados para explicar el código.
  • Se corrigió para usarse con sistemas operativos más nuevos que usan IPV4 mapeado a IPV6 (como Windows 10).
  • Reducción de la anidación.
  • El código actualizado utiliza "var".
Jonathan Alfaro
fuente
1
Esto es muy interesante, lo intentaré, ya que en mi caso me gustaría que el cliente descubriera a) la dirección de origen utilizada para comunicarse con mi servidor (NO necesariamente será a través de Internet) yb) cuál es la dirección MAC es de NIC que proporciona esta dirección IP ...
Brian B
5

Podrías buscar la ID de NIC:

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

No es la dirección MAC, pero es un identificador único, si eso es lo que estás buscando.

mmr
fuente
2

¡Realmente me gusta la solución de AVee con la métrica de conexión IP más baja! Pero si se instala un segundo nic con la misma métrica, la comparación MAC podría fallar ...

Mejor guarde la descripción de la interfaz con el MAC. En comparaciones posteriores, puede identificar el nic correcto mediante esta cadena. Aquí hay un código de muestra:

   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
fuente
2

Digamos que tengo una TcpConnection usando mi ip local de 192.168.0.182. Entonces, si me gustaría saber la dirección MAC de esa NIC, llamaré al método 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
fuente
Esto no es eficiente ... No recomendaría hacer esto.
Ivandro IG Jao
2

Realmente odio desenterrar esta publicación anterior, pero creo que la pregunta merece otra respuesta específica para Windows 8-10.

Usando NetworkInformation del espacio de nombres Windows.Networking.Connectivity , puede obtener el Id. Del adaptador de red que Windows está usando. Luego puede obtener la dirección MAC de la interfaz de GetAllNetworkInterfaces () mencionado anteriormente.

Esto no funcionará en las aplicaciones de la Tienda Windows, ya que NetworkInterface en System.Net.NetworkInformation no expone 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
fuente
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);
safari hadi
fuente
2
Esta respuesta podría mejorarse con una breve explicación de lo que hace el código y cómo resuelve el problema.
Greg the Incredulous
1

Cambió un poco su código blak3r. En caso de que tenga dos adaptadores con la misma velocidad. Ordenar por MAC, para que siempre obtenga el mismo 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;
}
usuario369122
fuente
1
foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
{
     if (nic.OperationalStatus == OperationalStatus.Up)
     {
            PhysicalAddress Mac = nic.GetPhysicalAddress();
     }
}
Mohamad Javadi
fuente
0

ipconfig.exese implementa usando varias DLL, incluyendo iphlpapi.dll... Google para iphlpapirevela una API Win32 correspondiente documentada en MSDN.

ChrisW
fuente
0

Prueba esto:

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