在區域網路開發中,常需列舉網路上所有電腦,並根據 IP 位址查詢對應的 MAC 位址。本文提供基於 Windows 網路 API(WNetEnumResource、gethostbyname)的 C++ Builder 完整實作碼,可直接整合至專案。
<winnetwk.h>,連結 mpr.lib<winsock2.h>,連結 ws2_32.libWSAStartup,結束呼叫 WSACleanupvoid __fastcall TForm1::FormCreate(TObject *Sender) {
TStringList *netList = new TStringList();
TListItem *newItem;
ListView1->Items->Clear();
Screen->Cursor = crHourGlass;
// 遞迴列舉全域網路資源
EnumNetResource(netList, NULL, RESOURCE_GLOBALNET, RESOURCETYPE_ANY);
for (int i = 0; i < netList->Count; i++) {
newItem = ListView1->Items->Add();
newItem->Caption = netList->Strings[i]; // 電腦名稱
newItem->SubItems->Add(GetHostIP(netList->Strings[i])); // 對應 IP
}
Screen->Cursor = crDefault;
delete netList;
}
bool __fastcall TForm1::EnumNetResource(
TStrings *rcList, LPNETRESOURCE lpNR, DWORD dwScope, DWORD dwType) {
HANDLE hEnum = 0;
DWORD dwResult = WNetOpenEnum(dwScope, dwType, 0, lpNR, &hEnum);
if (dwResult != NO_ERROR) return false;
bool bRet = true;
DWORD dwEntries = 0xFFFFFFFF; // 列舉所有項目
NETRESOURCE NR[1024];
DWORD dwBuffer = 1024 * sizeof(NETRESOURCE);
while (true) {
dwResult = WNetEnumResource(hEnum, &dwEntries, (LPVOID)NR, &dwBuffer);
if (dwResult == ERROR_NO_MORE_ITEMS) break;
if (dwResult != NO_ERROR) { bRet = false; break; }
for (DWORD i = 0; i < dwEntries; i++) {
// 只處理伺服器類型(電腦)
if (NR[i].dwDisplayType == RESOURCEDISPLAYTYPE_SERVER) {
char *p = NR[i].lpRemoteName;
while (*p == '\\') p++; // 跳過前導雙反斜線
if (*p) rcList->Add(p); // 加入電腦名稱
}
// 容器類型(如工作群組、網域)遞迴進入
else if ((NR[i].dwUsage & RESOURCEUSAGE_CONTAINER) == RESOURCEUSAGE_CONTAINER) {
bRet = EnumNetResource(rcList, &NR[i], dwScope, dwType);
if (!bRet) break;
}
}
if (!bRet) break;
}
WNetCloseEnum(hEnum);
return bRet;
}
AnsiString __fastcall TForm1::GetHostIP(AnsiString &host) {
WSADATA wsaData;
AnsiString IP;
if (WSAStartup(MAKEWORD(2,0), &wsaData) != 0) return "";
try {
// 若 host 為空,取得本機名稱
if (host.IsEmpty()) {
char hostname[128];
if (gethostname(hostname, 128) != 0) return "";
host = hostname;
}
struct hostent *hp = gethostbyname(host.c_str());
if (WSAGetLastError() == WSAHOST_NOT_FOUND) {
IP = "無法獲取";
} else {
// 取得第一個 IPv4 位址
IP = inet_ntoa(*(struct in_addr*)hp->h_addr_list[0]);
}
} catch (...) {
IP = "";
}
WSACleanup();
return IP;
}
上述程式碼僅解析 IP。若需進一步根據 IP 取得 MAC 位址,可使用 SendARP(IP Helper API):
#include <iptypes.h>
#include <iphlpapi.h>
// 連結 iphlpapi.lib
ULONG GetMacFromIP(AnsiString ipStr, BYTE mac[6]) {
IPAddr destIp = inet_addr(ipStr.c_str());
IPAddr srcIp = 0; // 本機介面
ULONG macAddr[2];
ULONG physAddrLen = 6;
DWORD ret = SendARP(destIp, srcIp, macAddr, &physAddrLen);
if (ret == NO_ERROR && physAddrLen == 6) {
memcpy(mac, macAddr, 6);
return NO_ERROR;
}
return ret;
}
// 使用範例
BYTE mac[6];
if (GetMacFromIP("192.168.1.100", mac) == NO_ERROR) {
AnsiString macStr;
for (int i = 0; i < 6; i++) {
if (i) macStr += "-";
macStr += IntToHex(mac[i], 2);
}
ShowMessage("MAC: " + macStr); // 格式如 00-1A-2B-3C-4D-5E
}
gethostbyname 僅支援 IPv4,新專案建議改用 getaddrinfoSendARP 依賴本機 ARP 快取,目標必須近期有通訊紀錄