在 Windows 平台取得網路介面卡 MAC 位址,常見三種 API 路徑:**NetBIOS (傳統)、WMI/COM GUID (簡易但不可靠)、SNMP MIB (標準管理)**。本文詳細比較三者原理、適用場景、程式碼實作與優缺點,助您選擇最適合專案的方案。
優點:速度快、資訊完整 (含多張網卡)、無需 WMI 服務。
缺點:需啟用 NetBIOS over TCP/IP (現代 Windows 預設開啟);需連結 Netapi32.lib。
NCBENUM 列舉 LANA 番号 (邏輯介面編號)。NCBRESET 重置。NCBASTAT 查詢介面卡狀態,從 ADAPTER_STATUS.adapter_address 取得 6 位元組 MAC。#include <windows.h>
#include <netbios.h>
#pragma comment(lib, "netapi32.lib")
bool GetMacByNetBIOS(std::vector<std::string>& macs) {
NCB ncb = {0};
LANA_ENUM le; ncb.ncb_command = NCBENUM; ncb.ncb_buffer = (UCHAR*)≤ ncb.ncb_length = sizeof(le);
if (Netbios(&ncb) != NRC_GOODRET) return false;
for (int i = 0; i < le.length; ++i) {
UCHAR lana = le.lana[i];
// Reset
ncb = {0}; ncb.ncb_command = NCBRESET; ncb.ncb_lana_num = lana;
if (Netbios(&ncb) != NRC_GOODRET) continue;
// ASTAT
struct { ADAPTER_STATUS ad; NAME_BUFFER nb[30]; } ast;
ncb = {0}; ncb.ncb_command = NCBASTAT; ncb.ncb_lana_num = lana;
ncb.ncb_callname[0] = '*'; ncb.ncb_buffer = (UCHAR*)* ncb.ncb_length = sizeof(ast);
if (Netbios(&ncb) == NRC_GOODRET) {
char mac[18];
sprintf_s(mac, "%02X:%02X:%02X:%02X:%02X:%02X",
ast.ad.adapter_address[0], ast.ad.adapter_address[1], ast.ad.adapter_address[2],
ast.ad.adapter_address[3], ast.ad.adapter_address[4], ast.ad.adapter_address[5]);
macs.push_back(mac);
}
}
return !macs.empty();
}
原理:GUID (UUID) 版本 1 由 時間戳 + MAC 位址 生成。CoCreateGuid 回傳的 GUID.Data4[2..7] 理應為 MAC。
致命缺陷:
Data4 為亂數,完全無關 MAC。GUID guid; CoCreateGuid(&guid);
char mac[18];
sprintf_s(mac, "%02X:%02X:%02X:%02X:%02X:%02X",
guid.Data4[2], guid.Data4[3], guid.Data4[4],
guid.Data4[5], guid.Data4[6], guid.Data4[7]);
優點:符合 RFC 1213 MIB-II 標準,可遠端查詢、支援 IPv6 (IF-MIB)。
缺點:需啟用 SNMP 服務 (現代 Windows 預設關閉);API 複雜、需動態載入 inetmib1.dll。
| OID | 名稱 | 說明 |
|---|---|---|
| 1.3.6.1.2.1.2.1 | ifNumber | 介面數量 |
| 1.3.6.1.2.1.2.2.1.3 | ifType | 介面類型 (6=ethernetCsmacd) |
| 1.3.6.1.2.1.2.2.1.6 | ifPhysAddress | MAC 位址 |
LoadLibrary("inetmib1.dll") 取得 SnmpExtensionInit/Query 函數指標。SnmpExtensionInit 初始化。SnmpExtensionQuery(GETNEXT) 遍歷 ifTable,篩選 ifType==6 且 ifPhysAddress 非全零/非撥號介面 (0x4445535400...)。| 維度 | NetBIOS | GUID | SNMP |
|---|---|---|---|
| 可靠度 | 高 | 極低 (隨機) | 高 (需服務) |
| 相依性 | NetBIOS 協定 | OLE32.dll | SNMP 服務 + inetmib1.dll |
| 權限需求 | 一般用戶 | 一般用戶 | 一般用戶 |
| 多網卡支援 | 完整 | 單一 (隨機) | 完整 |
| 現代適用性 | 良好 (預設開啟) | 不適用 | 差 (服務預設關閉) |
| 推薦等級 | ★★★★☆ | ☆☆☆☆☆ | ★★☆☆☆ |
Windows Vista+ 推薦使用 GetAdaptersAddresses (IPv4/IPv6 通用),無需額外服務,資訊最完整:
#include <iphlpapi.h>
#pragma comment(lib, "iphlpapi.lib")
ULONG bufLen = 0;
GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, nullptr, nullptr, &bufLen);
auto pBuf = std::make_unique<BYTE[]>(bufLen);
PIP_ADAPTER_ADDRESSES pAd = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(pBuf.get());
GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, nullptr, pAd, &bufLen);
for (; pAd; pAd = pAd->Next) {
if (pAd->PhysicalAddressLength == 6) {
// pAd->PhysicalAddress 即為 MAC
}
}
若專案需相容 Windows 2000/XP,**NetBIOS** 是最穩健選擇;Vista 以上**首選 GetAdaptersAddresses**;**嚴禁使用 GUID 取 MAC**。SNMP 僅適用於已部署 SNMP 基礎設施的企業管理軟體。