MAC 位址是網卡唯一硬體識別碼,常用於軟體授權綁定、資產管理、網路存取控制。Windows 提供多種獲取途徑,本文詳解三種主流方法:**NetBIOS API**(經典、需服務)、**WMI**(腳本友好、資訊豐富)、**IP Helper API**(原生、無相依、高效能)。附完整 C++ 代碼片段。
**優點**:速度快、資訊準確。
**缺點**:需啟用 NetBIOS over TCP/IP 服務(檔案共享通常已開啟)。
typedef struct _NCB {
UCHAR ncb_command; // 命令碼
UCHAR ncb_retcode; // 返回碼
UCHAR ncb_lsn; // 區段號
UCHAR ncb_num; // 編號
PUCHAR ncb_buffer; // 資料緩衝區指標
WORD ncb_length; // 緩衝區長度
UCHAR ncb_callname[NCBNAMSZ];
UCHAR ncb_name[NCBNAMSZ];
UCHAR ncb_rto; // 接收逾時
UCHAR ncb_sto; // 發送逾時
void (CALLBACK *ncb_post)(struct _NCB *); // 完成回調
UCHAR ncb_lana_num; // LANA 編號
UCHAR ncb_cmd_cplt; // 命令完成碼
UCHAR ncb_reserve[10]; // 保留
HANDLE ncb_event; // 事件控制代碼
} NCB, *PNCB;
LANA_ENUM 結構。ADAPTER_STATUS(含 MAC 位址)+ NAME_BUFFER 陣列。
// 1. 列舉 LANA
NCB ncb = {0}; ncb.ncb_command = NCBENUM;
UCHAR lanaEnum[256]; ncb.ncb_buffer = lanaEnum; ncb.ncb_length = 256;
Netbios(&ncb); // 成功後 lanaEnum[0] = 數量, [1..] = LANA 編號
// 2. 遍歷每個 LANA
for (int i=1; i<=lanaEnum[0]; i++) {
UCHAR lana = lanaEnum[i];
// 2.1 重置
NCB rst = {0}; rst.ncb_command = NCBRESET; rst.ncb_lana_num = lana;
Netbios(&rst);
// 2.2 查詢狀態
NCB astat = {0}; astat.ncb_command = NCBASTAT; astat.ncb_lana_num = lana;
astat.ncb_callname[0] = '*'; // 本機
ADAPTER_STATUS adapt; NAME_BUFFER names[30];
astat.ncb_buffer = (UCHAR*)&adapt; astat.ncb_length = sizeof(adapt) + sizeof(names);
Netbios(&astat);
// 2.3 取得 MAC (adapt.adapter_address, 6 bytes)
// 格式化為 "XX:XX:XX:XX:XX:XX"
}
**優點**:程式碼簡潔、支援遠端查詢、可同時獲取 IP、DHCP、DNS 等豐富屬性。
**缺點**:需 WMI 服務、COM 初始化開銷大、執行速度較慢。
CoInitializeEx(NULL, COINIT_MULTITHREADED);
IWbemLocator *pLoc; CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (void**)&pLoc);
IWbemServices *pSvc; pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &pSvc);
CoSetProxyBlanket(pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
IEnumWbemClassObject *pEnum;
pSvc->ExecQuery(bstr_t("WQL"), bstr_t("SELECT MACAddress FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled=TRUE"), WBEM_FLAG_FORWARD_ONLY, NULL, &pEnum);
IWbemClassObject *pObj; ULONG uRet;
while (pEnum->Next(WBEM_INFINITE, 1, &pObj, &uRet) == S_OK) {
VARIANT vt; pObj->Get(L"MACAddress", 0, &vt, 0, 0);
// vt.bstrVal 即為 "00:11:22:33:44:55" 格式
VariantClear(&vt); pObj->Release();
}
pEnum->Release(); pSvc->Release(); pLoc->Release(); CoUninitialize();
**優點**:原生 DLL (iphlpapi.dll)、無額外服務相依、效能最佳、支援 IPv6、可獲取完整介面卡資訊(單播/多播/任播位址、閘道、DNS 伺服器)。
**缺點**:結構體較複雜、需動態配置緩衝區。
#include
#pragma comment(lib, "iphlpapi.lib")
ULONG bufSize = 15000; // 初始緩衝區
PIP_ADAPTER_ADDRESSES pAddresses = (PIP_ADAPTER_ADDRESSES)malloc(bufSize);
DWORD ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &bufSize);
if (ret == ERROR_BUFFER_OVERFLOW) {
free(pAddresses);
pAddresses = (PIP_ADAPTER_ADDRESSES)malloc(bufSize);
ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &bufSize);
}
if (ret == NO_ERROR) {
for (PIP_ADAPTER_ADDRESSES p = pAddresses; p; p = p->Next) {
if (p->PhysicalAddressLength == 6) { // 乙太網
// p->PhysicalAddress 指向 6 bytes MAC
// p->FriendlyName, p->Description, p->OperStatus 等豐富資訊
}
}
}
free(pAddresses);
| 維度 | NetBIOS | WMI | IP Helper API |
|---|---|---|---|
| 相依服務 | NetBIOS over TCP/IP | WMI (Winmgmt) | 無 (核心 DLL) |
| 執行速度 | 快 | 慢 (COM 開銷) | 最快 |
| 程式碼複雜度 | 中 (NCB 結構) | 高 (COM 模板) | 中 (結構體巢狀) |
| 資訊豐富度 | 低 (僅 MAC/名稱) | 極高 (完整網路設定) | 高 (完整介面卡屬性) |
| 遠端查詢 | 不支援 | 原生支援 | 不支援 (本機) |
| 推薦場景 | 舊系統相容、簡單授權綁定 | 資產管理、庫存掃描、遠端審計 | 高效能本機偵測、安裝程式、授權驗證 |