在 Windows 開發中,獲取網卡 MAC 位址是常見需求。常見方法包括 NetBIOS、SNMP、GetAdaptersInfo 等。經實測,NetBIOS 在網線拔除時無法獲取 MAC;SNMP 易產生重複資料;而 GetAdaptersInfo 表現最佳——即使網線斷開也能準確獲取,且不重複。本文分享基於此 API 的完整實作,並解決物理/虛擬、有線/無線網卡的區分問題。
觀察 pAdapter->Description 字串,若包含 "PCI" 關鍵字,判定為物理網卡(經 3 台機器驗證有效)。
依據 pAdapter->Type 欄位,值為 71 (IF_TYPE_IEEE80211) 者為無線網卡(經 2 張無線網卡驗證)。
HKLM\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318} 下的 PnPInstanceID 或 NetCfgInstanceId 做進一步驗證。#include <windows.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <stdlib.h>
#pragma comment(lib, "Iphlpapi.lib")
int main()
{
PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO*)malloc(sizeof(IP_ADAPTER_INFO));
if (!pAdapterInfo) return -1;
ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
DWORD dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
free(pAdapterInfo);
pAdapterInfo = (IP_ADAPTER_INFO*)malloc(ulOutBufLen);
if (!pAdapterInfo) return -1;
dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
}
if (dwRetVal == NO_ERROR) {
PIP_ADAPTER_INFO pAdapter = pAdapterInfo;
while (pAdapter) {
// 篩選:物理網卡(含 PCI) 或 無線網卡(Type==71)
bool isPhysical = (strstr(pAdapter->Description, "PCI") != NULL);
bool isWireless = (pAdapter->Type == 71); // IF_TYPE_IEEE80211
if (isPhysical || isWireless) {
printf("------------------------------------------------------------\n");
printf("Adapter Name : %s\n", pAdapter->AdapterName);
printf("Description : %s\n", pAdapter->Description);
printf("MAC Address : ");
for (UINT i = 0; i < pAdapter->AddressLength; i++) {
printf("%02X%c", pAdapter->Address[i],
(i == pAdapter->AddressLength - 1) ? '\n' : '-');
}
printf("Adapter Type : %u\n", pAdapter->Type);
printf("IP Address : %s\n", pAdapter->IpAddressList.IpAddress.String);
printf("IP Mask : %s\n", pAdapter->IpAddressList.IpMask.String);
}
pAdapter = pAdapter->Next;
}
} else {
printf("GetAdaptersInfo 失敗,錯誤碼: %lu\n", dwRetVal);
}
if (pAdapterInfo) free(pAdapterInfo);
return 0;
}
malloc/free,避免 MFC 專用 GlobalAlloc,提高可移植性。%02X 輸出,並以連字號分隔,符合慣例。ERROR_BUFFER_OVERFLOW 並給予明確錯誤訊息。若需更穩健的網卡分類,建議改用 GetAdaptersAddresses (Windows XP SP1+) 並搭配 IF_OPER_STATUS、PhysicalMediumType 等欄位,或直接查詢註冊表 NetCfgInstanceId 對應的 PnPInstanceID 判斷是否為 PCI\\ 開頭。對於現代專案,優先考慮 GetAdaptersAddresses 以支援 IPv6 並獲得更豐富的屬性。