2009-05-12 22 views
93

Ho bisogno di un modo per ottenere l'indirizzo MAC di una macchina indipendentemente dal sistema operativo in esecuzione utilizzando C#. L'applicazione dovrà funzionare su XP/Vista/Win7 a 32 e 64 bit e 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 in quanto il formato di output è diverso su ogni macchina.Metodo affidabile per ottenere l'indirizzo MAC della macchina in C#

Grazie

+6

Quando si dice attraverso SO, si intende tra diversi sistemi operativi Microsoft? –

risposta

9

Il MACAddress proprietà della Win32_NetworkAdapterConfiguration WMI class grado di fornire con l'indirizzo MAC di un adattatore. (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" 

Se non hai familiarità con l'API WMI (Windows Management Instrumentation), c'è un good overview here per le applicazioni .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()); 
     } 
    } 
4

si potrebbe andare per il NIC ID:

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

Non è l'indirizzo MAC, ma è un identificatore univoco, se è questo che sei cercando.

0

ipconfig.exe viene implementato utilizzando varie DLL tra cui iphlpapi.dll ... Googling per iphlpapi rivela una API Win32 corrispondente documentata in MSDN.

9

WMI è la soluzione migliore se la macchina a cui ci si connette è una macchina Windows, ma se si sta guardando un linux, mac o un altro tipo di scheda di rete, sarà necessario utilizzare qualcos'altro. Ecco alcune opzioni:

  1. Utilizzare il comando DOS nbtstat -a. Crea un processo, chiama questo comando, analizza l'output.
  2. Prima eseguire il ping dell'IP per assicurarsi che la propria NIC memorizzi il comando nella sua tabella ARP, quindi utilizzare il comando DOS arp -a. Analizzare l'output del processo, come nell'opzione 1.
  3. utilizzare una chiamata non gestito temuta al sendarp nel iphlpapi.dll

Heres un campione di item # 3. Questa sembra essere la migliore opzione 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); 
} 

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

+0

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

+1

Fantastico, grazie! – detay

68

ecco qualche codice C# che restituisce l'indirizzo MAC della prima interfaccia di rete operativa. Supponendo che l'assembly NetworkInterface sia implementato nel runtime (cioè Mono) utilizzato su altri sistemi operativi, ciò funzionerebbe su altri sistemi operativi.

Nuova versione: restituisce la scheda NIC con la velocità più veloce 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 è se si dispone come un Nortel Packet Miniport o qualche tipo di connessione VPN ha il potenziale di essere scelti. Per quanto posso dire, non c'è modo di distinguere un MAC di un dispositivo fisico reale da un qualche tipo di interfaccia di rete virtuale.

+6

Non basta scegliere la prima interfaccia operativa. Ciò potrebbe restituire interfacce Loopback, schede 3G connesse occasionalmente e così via, che probabilmente non sono ciò che si desidera. Il NetworkInterfaceType (http://msdn.microsoft.com/en-us/library/system.net.networkinformation.networkinterface.networkinterfacetype.aspx) fornirà ulteriori informazioni sulla connessione NetworkInterface in modo da poter effettuare una scelta più informata. Inoltre, tieni presente che potrebbero esserci molte connessioni attive su una macchina e il loro ordine potrebbe non essere prevedibile. –

+0

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

+1

È necessario selezionare l'interfaccia con il parametro GatewayMetric più basso. Questa dovrebbe essere la connessione che ha "il percorso più veloce, più affidabile o meno dispendioso in termini di risorse". Fondamentalmente ti darà l'interfaccia che Windows preferisce usare. Tuttavia, penso che tu abbia bisogno di WMI per ottenerlo. Vedrò se riesco a farlo funzionare ... – AVee

92

Cleaner soluzione

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

Oppure:

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

O lambda, se è la tua cosa! 'return NetworkInterface.GetAllNetworkInterfaces(). Dove (nic => nic.OperationalStatus == OperationalStatus.Up) .Select (nic => nic.GetPhysicalAddress(). ToString()). FirstOrDefault();' (Se non lo è 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 ordinatoNetworks.First(). GetPhysicalAddress(). ToString(); ' –

7

Usiamo WMI per ottenere l'indirizzo MAC dell'interfaccia con il più basso metrica, ad esempio, le finestre di interfaccia preferiranno utilizzare, 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 (necessita di elevata fiducia):

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

Mi piace molto la soluzione di AVEE con la connessione IP più basso metrica! Ma se viene installato un secondo server con la stessa metrica, il confronto con MAC potrebbe fallire ...

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

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. Poi se mi piacerebbe sapere l'indirizzo MAC di quel 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; 
    } 
+0

Questo non è efficiente ... Non consiglierei di farlo. –

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

avevo bisogno per ottenere il MAC per l'attuale NIC utilizzato per connettersi a Internet; per determinare quale interfaccia è stata utilizzata da WCF nella mia app client.

Vedo molte risposte qui ma nessuna che mi abbia aiutato. Spero che questa risposta aiuti qualcuno.

Questa soluzione restituisce il MAC della NIC utilizzata per connettere a Internet.

private static PhysicalAddress GetCurrentMAC(string allowedURL) 
{ 
    TcpClient client = new TcpClient(); 
    client.Client.Connect(new IPEndPoint(Dns.GetHostAddresses(allowedURL)[0], 80)); 
    while(!client.Connected) 
    { 
    Thread.Sleep(500); 
    } 
    IPAddress address2 = ((IPEndPoint)client.Client.LocalEndPoint).Address; 
    client.Client.Disconnect(false); 
    NetworkInterface[] allNetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces(); 
    client.Close(); 
    if(allNetworkInterfaces.Length > 0) 
    { 
    foreach(NetworkInterface interface2 in allNetworkInterfaces) 
    { 
     UnicastIPAddressInformationCollection unicastAddresses = interface2.GetIPProperties().UnicastAddresses; 
     if((unicastAddresses != null) && (unicastAddresses.Count > 0)) 
     { 
     for(int i = 0; i < unicastAddresses.Count; i++) 
      if(unicastAddresses[i].Address.Equals(address2)) 
      return interface2.GetPhysicalAddress(); 
     } 
    } 
    } 
    return null; 
} 

chiamarlo è necessario passare un URL per connettersi a come questo:

PhysicalAddress mac = GetCurrentMAC("www.google.com"); 
2

odio davvero a scavare questo vecchio post, ma mi sento la questione merita un altro risposta specifica alle finestre 8- 10.

Utilizzando NetworkInformation dallo spazio dei nomi Windows.Networking.Connectivity, è possibile ottenere l'ID delle finestre della scheda di rete in uso. Quindi è possibile ottenere l'indirizzo MAC dell'interfaccia dal precedente GetAllNetworkInterfaces().

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

Modificato blak3r il suo codice un po '. Nel caso in cui hai due adattatori con la stessa velocità. Ordina per MAC, quindi ottieni 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; 
} 
Problemi correlati