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

我需要一种方法来获取一台机器的 MAC 地址,不管它运行的是什么操作系统,通过使用 C # 。

该应用程序将需要在 XP/Vista/Win732位和64位,以及这些操作系统,但外语默认。此外,许多 C # 命令和操作系统查询并不能在所有的操作系统中工作。

你有什么主意吗?

我一直在刮 ipconfig /all的输出,但这是非常不可靠的,因为每台机器的输出格式不同。

259787 次浏览

Win32 _ NetworkAdapterConfiguration WMI 类麦克风地址属性可以为您提供适配器的 MAC 地址(System.ManagementNamespace)

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 可以通过.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());
}
}

你可以使用 NIC ID:

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

这不是 MAC 地址但是这是个唯一标识符如果你要找的是这个的话。

ipconfig.exe是使用包括 iphlpapi.dll在内的各种 DLL 实现的... ... 为 iphlpapi进行的谷歌搜索揭示了 MSDN 中记录的相应 Win32 API。

如果您连接到的机器是一台 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#

下面是一些 C # 代码,它返回第一个操作网络接口的 MAC 地址。假设 NetworkInterface程序集是在其他操作系统上使用的运行时(即 Mono)中实现的,那么这将在其他操作系统上起作用。

新版本: 以最快的速度返回具有有效 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;
}

我唯一不喜欢这种方法的是,如果你有像北电数据包小端口或某种类型的 VPN 连接,它有被选择的潜力。据我所知,没有办法区分实际物理设备的 MAC 和某种类型的虚拟网络接口。

更干净的解决方案

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

我们使用 WMI 来获取具有最低指标的接口的 mac 地址,例如,接口窗口将更喜欢使用,如下所示:

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

我真的很喜欢 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;
}

假设我有一个 TcpConnection,使用的是192.168.0.182的本地 ip。然后,如果我想知道那个 NIC 的 mac 地址,我会调用这个方法: 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;
}
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;
}

试试这个:

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

此方法将确定用于连接到指定 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。

更新:

二零二零年

  • 添加了解释代码的注释。
  • 更正为与更新的一起使用 使用映射到 IPV6的 IPV4的操作系统(如 Windows10)。
  • 减少筑巢。
  • 升级代码使用“ var”。

真的很讨厌挖掘这个老职位,但我觉得这个问题值得另一个具体的答案,Windows 8-10。

使用 视窗,网络,连接命名空间中的 网络资讯,您可以获得正在使用的网络适配器窗口的 Id。然后您可以从前面提到的 GetAllNetworkInterfaces ()获得接口 MAC 地址。

这在 WindowsStore 应用程序中无效,因为 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;
}

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

恕我直言,返回第一个 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;
}
foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
{
if (nic.OperationalStatus == OperationalStatus.Up)
{
PhysicalAddress Mac = nic.GetPhysicalAddress();
}
}