C#中获取计算机MAC地址的可靠方法

156

我需要一种使用C#获取机器的MAC地址的方法,不考虑它正在运行的操作系统。

该应用程序将需要在XP / Vista / Win7 32位和64位上运行,并且还需要在这些操作系统上但默认语言是外语的情况下工作。另外,许多C#命令和操作系统查询在所有操作系统上都无法正常工作。

您有任何想法吗?

我一直在爬取ipconfig /all的输出,但这非常不可靠,因为输出格式在每台计算机上都不同。


7
当您说"across OS"时,您指的是跨不同的微软操作系统吗? - John Weldon
17个回答

163

更简洁的解决方案

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

47
或者使用Lambda表达式,如果你喜欢的话!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(); - Graham Laight
1
选择第一个并不总是最好的选择。选择最常用的连接:https://dev59.com/x3RA5IYBdhLWcg3wuAYo#51821927 - Ramunas
1
优化提示:在最终的 Select 之前,您可以调用 FirstOrDefault。这样,它只会获取物理地址并将其序列化为实际获取的 NetworkInterface。不要忘记在 FirstOrDefault 后添加空值检查(?)。 - GregorMohorko
2
一种计算更快的方法是,您不需要评估所有与给定条件匹配的网络,只需要获取它们中的第一个:NetworkInterface .GetAllNetworkInterfaces() .FirstOrDefault(nic => nic.OperationalStatus == OperationalStatus.Up && nic.NetworkInterfaceType != NetworkInterfaceType.Loopback)? .GetPhysicalAddress().ToString(); - Alessandro Muzzi

84

这里有一些C#代码,可以返回第一个正在运行的网络接口的MAC地址。假设在其他操作系统上使用的运行时(例如Mono)中实现了NetworkInterface程序集,则此代码也可用于其他操作系统。

新版本:返回速度最快且具有有效MAC地址的NIC。

/// <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
不要只选择第一个操作接口。这可能会返回回环接口、偶尔连接的3G卡等,这可能不是你想要的。NetworkInterfaceType (http://msdn.microsoft.com/en-us/library/system.net.networkinformation.networkinterface.networkinterfacetype.aspx) 可以提供更多有关 NetworkInterface 连接的信息,以便您可以做出更明智的选择。还要记住,一台机器上可能有许多活动连接,并且它们的顺序可能是不可预测的。 - Dave R.
1
你应该选择具有最低的GatewayMetric值的接口。这应该是具有“最快、最可靠或资源消耗最少的路径”的连接。基本上,它会为你提供Windows首选使用的接口。然而,我认为你需要使用WMI才能真正获得这个结果。我会看看能否让它工作... - AVee
6
为了完整性,可以通过using System.Net.NetworkInformation;访问NetworkInterface类。 - iancoleman
1
顺便说一句,如果你有千兆网卡和安装了 Hyper-V,你也会有一个10千兆虚拟网卡。 :) 这是一个棘手的问题要解决... - Christopher Painter
最快的速度可能会成为一个问题,如果服务器有多个物理网卡,并且两者的流量几乎相等。返回的MAC地址可能会频繁交换。这取决于要求,但通常获取单个MAC地址与将许可证绑定到单个MAC地址相关。如果MAC地址更改,则许可证将失效。 - tjmoore
显示剩余4条评论

13

依我之见,返回第一个 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;
}

谢谢!顺便说一句,在一行代码中:public static NetworkInterface? GetDefaultMacAddress() => NetworkInterface.GetAllNetworkInterfaces().Where(i => i.OperationalStatus == OperationalStatus.Up).OrderByDescending(i => i.GetIPStatistics().BytesReceived + .GetIPStatistics().BytesSent).FirstOrDefault(); - Simon Mourier

11

MACAddress 属性是 Win32_NetworkAdapterConfiguration WMI 类 中的一个属性,可提供适配器的 MAC 地址。(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"

如果您不熟悉WMI API(Windows管理工具),这里有一个很好的.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());
         }
     }

10

如果您正在连接的机器是Windows机器,则WMI是最佳解决方案,但如果您要查看的是Linux、Mac或其他类型的网络适配器,则需要使用其他工具。以下是一些选项:

  1. 使用DOS命令nbtstat -a 。创建一个进程,调用该命令,解析输出。
  2. 首先ping IP以确保您的NIC将命令缓存到其ARP表中,然后使用DOS命令arp -a ,像选项1一样解析进程的输出。
  3. 使用iphlpapi.dll中的sendarp进行可怕的非托管调用。

这里是第三个选项的示例。如果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中,如果您使用的是Windows机器,则意味着DOS命令,并且在Linux或Mac上有相应的命令,对吗? - Raikol Amaro

10

这个方法将确定用于连接到指定URL和端口的网络接口的MAC地址。

所有这里的答案都不能实现这个目标。

我几年前写了这个答案(在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
这非常有趣,我会试一下,因为在我的情况下,我希望客户端能够发现:a) 用于与我的服务器通信的源地址(不一定是通过互联网)以及b) 提供此IP地址的NIC的MAC地址... - Brian B

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

8
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地址,对吗? - Nate Pet
它获取主机机器(服务器)的MAC地址。 - Tony

6
您可以选择使用NIC ID:
 foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) {
     if (nic.OperationalStatus == OperationalStatus.Up){
         if (nic.Id == "yay!")
     }
 }

它不是MAC地址,但它是一个唯一的标识符,如果这正是你所寻找的。


2
我非常喜欢AVee的解决方案,具有最低的IP连接度量!但是,如果安装了具有相同度量的第二个网卡,则MAC比较可能会失败...
最好将带有MAC地址的接口描述存储起来。在后续的比较中,您可以通过此字符串识别正确的网卡。下面是示例代码:
   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;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接