C#:獲取本機的MAC地址與IP地址

網上能夠找到不少使用C#語言獲取本機MAC地址與IP地址的代碼,在這裏說一下個人使用心得。html

在windows控制檯中使用命令ipconfig /all能夠查到本機的網絡配置。但其中的網絡有不少是不用的,可能你的電腦鏈接外網只用到了其中的一個網絡。所以咱們要獲取的是實際鏈接外網的網絡鏈接。windows

第一種方式安全

使用WMI(Windows Management Instrumentation)從對象Win32_NetworkAdapterConfiguration中獲取MAC和IP。此對象在MSDN上的介紹頁面見:服務器

https://msdn.microsoft.com/en-us/library/aa394217(v=vs.85).aspx網絡

代碼以下:函數

/// <summary>
/// 獲取當前激活網絡的MAC地址、IPv4地址、IPv6地址 - 方法1
/// </summary>
/// <param name="mac">網卡物理地址</param>
/// <param name="ipv4">IPv4地址</param>
/// <param name="ipv6">IPv6地址</param>
public static void GetActiveIpAndMac1(out string mac, out string ipv4, out string ipv6)
{
    mac = "";
    ipv4 = "";
    ipv6 = "";

    //須要引用:System.Management;
    ManagementClass mc = new ManagementClass("Win32_NetworkAdapterConfiguration");
    ManagementObjectCollection moc = mc.GetInstances();
    foreach (ManagementObject mo in moc)
    {
        if (mo["IPEnabled"].ToString() == "True")
        {
            //獲取MAC地址,每兩位中間用橫線【-】隔開
            mac = mo["MacAddress"].ToString().Replace(":", "-");
            string[] ipAddrs = mo["IPAddress"] as string[];
            if (ipAddrs != null && ipAddrs.Length >= 1)
            {
                //獲取IPv4地址,4個十進制數字,中間用英文句號【.】隔開
                ipv4 = ipAddrs[0];
            }
            if (ipAddrs != null && ipAddrs.Length >= 2)
            {
                //獲取IPv6地址,5個十六進制數字,中間用冒號【:】隔開
                ipv6 = ipAddrs[1];
            }
            break;
        }
    }
}

注意事項:工具

一、使用此方法需添加引用:System.Management測試

二、在可用網絡列表中,上述代碼只獲取了第一個啓用的網絡配置的MAC地址與IP地址,如要獲取指定的MAC或IP,還需進一步添加限制條件spa

三、此方法獲取的MAC地址格式爲XX-XX-XX-XX-XX-XX,IP地址格式爲XXX.XXX.XXX.XXX日誌

四、此方法在大多數計算機上可正常使用,但昨天發現一臺機器調用此方法時報錯「拒絕訪問」,異常調用堆棧以下:

堆棧:   在 System.Management.ManagementException.ThrowWithExtendedInfo(ManagementStatus errorCode)
   在 System.Management.ManagementObjectCollection.ManagementObjectEnumerator.MoveNext()
   在 ... ...

從異常的描述看報此異常應是由於權限不夠,此問題的緣由詳見本篇博客附文

第二種方式

使用NetworkInterface獲取本地網絡接口。

代碼以下:

/// <summary>
/// 獲取當前激活網絡的MAC地址、IPv4地址、IPv6地址 - 方法2
/// </summary>
/// <param name="mac">網卡物理地址</param>
/// <param name="ipv4">IPv4地址</param>
public static void GetActiveIpAndMac2(out string mac, out string ipv4, out string ipv6)
{
    mac = "";
    ipv4 = "";
    ipv6 = "";

    //須要引用:System.Net.NetworkInformation
    NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
    foreach (NetworkInterface adapter in nics)
    {
        IPInterfaceProperties adapterProperties = adapter.GetIPProperties();
        UnicastIPAddressInformationCollection allAddress = adapterProperties.UnicastAddresses;
        if (allAddress.Count > 0)
        {
            if (adapter.OperationalStatus == OperationalStatus.Up)
            {
                mac = adapter.GetPhysicalAddress().ToString();
                foreach (UnicastIPAddressInformation addr in allAddress)
                {
                    if (addr.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                    {
                        ipv4 = addr.Address.ToString();
                    }
                    if (addr.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
                    {
                        ipv6 = addr.Address.ToString();
                    }
                }

                if (string.IsNullOrWhiteSpace(mac) || 
                    (string.IsNullOrWhiteSpace(ipv4) && string.IsNullOrWhiteSpace(ipv6)))
                {
                    mac = "";
                    ipv4 = "";
                    ipv6 = "";
                    continue;
                }
                else
                {
                    if (mac.Length == 12)
                    {
                        mac = string.Format("{0}-{1}-{2}-{3}-{4}-{5}",
                            mac.Substring(0, 2), mac.Substring(2, 2), mac.Substring(4, 2),
                            mac.Substring(6, 2), mac.Substring(8, 2), mac.Substring(10, 2));
                    }
                    break;
                }
            }
        }
    }
}

注意事項:

一、使用此方法需添加引用:System.Net.NetworkInformation

二、同方法一同樣,在可用網絡列表中,上述代碼只獲取了第一個啓用的網絡配置的MAC地址與IP地址,如要獲取指定的MAC或IP,還需進一步添加限制條件

三、此方法獲取的MAC與IP可能與方法1不同,出現此問題的緣由是,一個電腦可能有多個網絡配置處於激活狀態,而方法1與方法2使用foreach遍歷的順序是不同的。獲取的第一個可用的MAC和IP並不必定是同一個。個人電腦上以前爲了在互聯網上和朋友玩局域網遊戲,安裝過nMatrix工具,以前在個人電腦上使用方法1獲取的第一個配置是無線網絡適配器的MAC和IP,使用方法2獲取的第一個配置是nMatrix工具的MAC和IP。

四、此方法獲取的MAC地址格式爲XX-XX-XX-XX-XX-XX,IP地址格式爲XXX.XXX.XXX.XXX

我爲了測試還寫過一個遍歷網絡的函數,也列在這裏:

/// <summary>
/// 打印網絡配置列表
/// </summary>
public static void PrintNetwork()
{
    NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
    foreach (NetworkInterface adapter in nics)
    {
        IPInterfaceProperties adapterProperties = adapter.GetIPProperties();
        UnicastIPAddressInformationCollection allAddress = adapterProperties.UnicastAddresses;
        if (allAddress.Count > 0)
        {
            Console.WriteLine("=================");
            Console.WriteLine("Name:" + adapter.Name);
            Console.WriteLine("Description:" + adapter.Description);
            Console.WriteLine("NetworkInterfaceType:" + adapter.NetworkInterfaceType.ToString());
            Console.WriteLine("OperationalStatus:" + adapter.OperationalStatus.ToString());
            Console.WriteLine("Mac:" + adapter.GetPhysicalAddress().ToString());
            foreach (UnicastIPAddressInformation addr in allAddress)
            {
                if (addr.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                {
                    Console.WriteLine("Address(V4):" + addr.Address);
                }
                if (addr.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
                {
                    Console.WriteLine("Address(V6):" + addr.Address);
                }
            }
        }  
    }
}

測試程序

創建一個Windows控制檯應用程序,將上面的函數放入到類NetworkHelper中,可測試運行效果,代碼以下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;

namespace GetNetworkInfoTest
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("方法1:WMI");
                string mac;
                string ipv4;
                string ipv6;
                NetworkHelper.GetActiveIpAndMac1(out mac, out ipv4, out ipv6);
                Console.WriteLine("mac:" + mac);
                Console.WriteLine("ipv4:" + ipv4);
                Console.WriteLine("ipv6:" + ipv6);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            try
            {
                Console.WriteLine("方法2:NetworkInterface");
                string mac;
                string ipv4;
                string ipv6;
                NetworkHelper.GetActiveIpAndMac2(out mac, out ipv4, out ipv6);
                Console.WriteLine("mac:" + mac);
                Console.WriteLine("ipv4:" + ipv4);
                Console.WriteLine("ipv6:" + ipv6);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            try
            {
                NetworkHelper.PrintNetwork();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            Console.Read();
        }
    }
}

在實際項目中的應用

一、由於方法1有可能由於用戶電腦的安全設置被禁用,所以我建議在獲取MAC地址時採用如下方式:

方式①:直接使用方法二獲取MAC與IP

方式②:因個人程序以前使用的是方法一,所以採用此法改進:先使用方法一(WMI)獲取MAC與IP,如使用方法一獲取不成功,捕獲異常後使用方法二(NetworkInterface)再次嘗試獲取,如方法二也獲取不成功,則認爲地址獲取失敗,記錄相關日誌,並進行相關處理。

二、這兩個方法都沒法直接斷定哪個網絡是真正鏈接外網的,所以如需肯定真正鏈接外部網絡的MAC和IP,須要具體問題具體分析。有一種實際場景,是要斷定只有指定MAC的電腦才能鏈接到服務,實現此場景須要客戶端程序採集當前計算機全部激活狀態的MAC,並與服務器端報錯的MAC進行比較,只要有一個MAC匹配,就認定爲此電腦是有權限的。

附文:方法1中使用WMI獲取MAC地址時出現「拒絕訪問」問題的排查(2016年12月27日11時補充)

後來我寫了一個VBS腳本測試這個問題:

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * from Win32_NetworkAdapterConfiguration",,48)
For Each objItem in colItems
    If objItem.MACAddress <> "" Then
	    Wscript.Echo "MACAddress: " & objItem.MACAddress
    End If
Next

在出現問題的計算機上執行此段腳本,發現錯誤碼爲:0x80041003,問題緣由爲「試圖鏈接命名空間的進程若是沒有必需的WMI權限」

有一篇文章對WMI問題講得比較詳細,可供參考:WMI問題全解(Windows管理規範)<轉>

http://www.cnblogs.com/haiq/archive/2011/01/14/1935377.html

個人電腦能夠正常執行上面的VBS腳本,我使用它復現出了此問題,方法以下:

一、使用管理員用戶新建用戶wmi_test,此時登陸用戶wmi_test,該用戶是能夠正常執行上面的VBS腳本的

二、進入WMI控件設置,方法爲:在桌面上用鼠標右鍵單擊「計算機」,在右鍵菜單中選擇「管理」,在左側樹形列表中,找到「計算機管理(本地)→服務和應用程序→WMI控件」,鼠標右鍵單擊此項,選擇「屬性」

三、找到Root/CIMV2,點擊「安全設置」,點擊「添加」按鈕,找到新建的用戶,將該用戶Root\CIMV2命名空間全部權限禁用

四、登陸用戶wmi_test,如當前已登陸此用戶則須要註銷後從新登陸之。執行上面的VBS腳本,就會報錯0x80041003了

END

相關文章
相關標籤/搜索