首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

VC++ 獲取實體網卡 MAC 位址:GetAdaptersInfo 最佳實踐與虛擬/無線網卡過濾

     編輯:關於VC++

方案選擇對比

獲取 MAC 位址常見方法:NetBIOS、SNMP、GetAdaptersInfo。實測比較:

方法網線拔除時重複 MAC備註
NetBIOS❌ 失敗-需網路連線
SNMP✅ 成功⚠️ 易重複依賴 SNMP 服務
GetAdaptersInfo✅ 成功✅ 無重複推薦方案

GetAdaptersInfo 既有問題與解決方案

問題解決策略驗證環境
區分實體/虛擬網卡Description 含 "PCI" 字串 → 實體網卡3 台機器測試通過
區分無線/有線網卡Type == 71 (IF_TYPE_IEEE80211) → 無線網卡2 張無線網卡測試通過
「停用」的網卡無法獲取系統行為限制,無完美解法-

完整實作代碼

#include <atlbase.h>
#include <atlconv.h>
#include <iphlpapi.h>
#pragma comment(lib, "Iphlpapi.lib")

int main() {
    PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO*)malloc(sizeof(IP_ADAPTER_INFO));
    ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
    
    // 第一次呼叫取得所需緩衝區大小
    if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) != ERROR_SUCCESS) {
        free(pAdapterInfo);
        pAdapterInfo = (IP_ADAPTER_INFO*)malloc(ulOutBufLen);
    }
    
    DWORD dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
    if (dwRetVal == NO_ERROR) {
        PIP_ADAPTER_INFO pAdapter = pAdapterInfo;
        while (pAdapter) {
            // 過濾條件:實體網卡(含PCI) 或 無線網卡(Type=71)
            if (strstr(pAdapter->Description, "PCI") != NULL || pAdapter->Type == 71) {
                printf("------------------------------------------------------------\n");
                printf("Adapter Name: \t%s\n", pAdapter->AdapterName);
                printf("Adapter Desc: \t%s\n", pAdapter->Description);
                printf("Adapter Addr: \t");
                
                for (UINT i = 0; i < pAdapter->AddressLength; i++) {
                    printf("%02X%c", pAdapter->Address[i],
                           (i == pAdapter->AddressLength - 1) ? '\n' : '-');
                }
                
                printf("Adapter Type: \t%d\n", pAdapter->Type);
                printf("IP Address: \t%s\n", pAdapter->IpAddressList.IpAddress.String);
                printf("IP Mask: \t%s\n", pAdapter->IpAddressList.IpMask.String);
            }
            pAdapter = pAdapter->Next;
        }
    } else {
        printf("GetAdaptersInfo 失敗, 錯誤碼: %lu\n", dwRetVal);
    }
    
    free(pAdapterInfo);
    return 0;
}

關鍵結構體說明

typedef struct _IP_ADAPTER_INFO {
    struct _IP_ADAPTER_INFO* Next;           // 下一個介面卡
    DWORD ComboIndex;                        // 介面卡索引
    char AdapterName[MAX_ADAPTER_NAME_LENGTH + 4];  // 介面卡名稱
    char Description[MAX_ADAPTER_DESCRIPTION_LENGTH + 4]; // 描述字串
    UINT AddressLength;                      // MAC 位址長度 (通常為 6)
    BYTE Address[MAX_ADAPTER_ADDRESS_LENGTH]; // MAC 位址位元組
    DWORD Index;                             // 介面卡索引
    UINT Type;                               // 介面卡類型 (MIB_IF_TYPE_*)
    UINT DhcpEnabled;                        // 是否啟用 DHCP
    PIP_ADDR_STRING CurrentIpAddress;        // 目前 IP 位址
    IP_ADDR_STRING IpAddressList;            // IP 位址列表
    IP_ADDR_STRING GatewayList;              // 閘道列表
    IP_ADDR_STRING DhcpServer;               // DHCP 伺服器
    BOOL HaveWins;                           // 是否有 WINS
    IP_ADDR_STRING PrimaryWinsServer;        // 主要 WINS
    IP_ADDR_STRING SecondaryWinsServer;      // 次要 WINS
    time_t LeaseObtained;                    // 租約取得時間
    time_t LeaseExpires;                     // 租約到期時間
} IP_ADAPTER_INFO;

常見網卡類型常數 (MIB_IF_TYPE_*)

常數值名稱說明
6IF_TYPE_ETHERNET_CSMACD乙太網路
71IF_TYPE_IEEE80211無線網路 (WiFi)
24IF_TYPE_SOFTWARE_LOOPBACK軟體回環
131IF_TYPE_TUNNEL通道介面
144IF_TYPE_IEEE1394FireWire

進階應用建議

  • 授權綁定:取得第一張實體網卡 MAC 作為軟體授權指紋
  • 多網卡策略:優先選擇有效 IP、閘道非空、Type=6 的乙太網卡
  • 跨平台考量:Linux 下讀取 /sys/class/net/*/address 或 ioctl(SIOCGIFHWADDR)
  • 虛擬化環境:VMware/Hyper-V 虛擬網卡 Description 通常不含 "PCI",可有效過濾

此方案在實體機、虛擬機、筆電 (有線+無線) 等環境均表現穩定,是 Windows 平台獲取 MAC 位址的首選實作。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved