CodeGuru 等網站雖有網絡鄰居枚舉類封裝,但對初學者理解底層原理不利。本文將核心邏輯直接實現於對話框類中,代碼直觀、無封裝干擾,適合學習 Windows 網絡 API 基礎。
WNetOpenEnum:開啟枚舉句柄WNetEnumResource:枚舉網絡資源WNetCloseEnum:關閉枚舉句柄NETRESOURCE:資源描述結構mpr.lib,包含 winnetwk.hBOOL CNetEnum2Dlg::Enumerate(LPNETRESOURCE lpNetRC_p) {
HANDLE hEnum = 0;
DWORD dwScope = RESOURCE_GLOBALNET;
DWORD dwType = RESOURCETYPE_ANY;
DWORD dwResult = WNetOpenEnum(dwScope, dwType, 0, lpNetRC_p, &hEnum);
if (dwResult != NO_ERROR) return FALSE;
DWORD dwBuffer = 16384; // 16KB 緩衝區
DWORD dwEntries = 0xFFFFFFFF; // 枚舉所有項目
LPNETRESOURCE lpnrLocal = 0;
BOOL bRet = TRUE;
do {
lpnrLocal = (LPNETRESOURCE)GlobalAlloc(GPTR, dwBuffer);
dwResult = WNetEnumResource(hEnum, &dwEntries, lpnrLocal, &dwBuffer);
if (dwResult == NO_ERROR) {
for (DWORD i = 0; i < dwEntries; i++) {
CString nsname;
nsname = lpnrLocal[i].lpRemoteName;
nsname += _T(" ");
nsname += lpnrLocal[i].lpLocalName;
nsname += _T(" ");
nsname += lpnrLocal[i].lpComment;
m_netList.AddString(nsname); // CListBox 成員變數
// 遞歸進入容器類資源(非伺服器)
if ((lpnrLocal[i].dwUsage & RESOURCEUSAGE_CONTAINER) &&
lpnrLocal[i].dwDisplayType != RESOURCEDISPLAYTYPE_SERVER) {
if (!Enumerate(&lpnrLocal[i])) return FALSE;
}
}
} else if (dwResult != ERROR_NO_MORE_ITEMS) {
return FALSE;
}
} while (dwResult != ERROR_NO_MORE_ITEMS);
if (lpnrLocal) GlobalFree((HGLOBAL)lpnrLocal);
WNetCloseEnum(hEnum);
return bRet;
}
void CNetEnum2Dlg::OnListWNet() {
Enumerate(0); // 從根開始枚舉
}
GlobalAlloc/GlobalFree 避免記憶體洩漏ERROR_NO_MORE_ITEMS 表示枚舉完成RESOURCEUSAGE_CONTAINER 標識可進入的資源(如共享資料夾),排除伺服器節點防止無限遞歸RESOURCETYPE_ANY 涵蓋磁碟、印表機等所有類型CListBox m_netList(關聯 IDC_LIST)OnListWNetmpr.lib| 現象 | 原因 | 對策 |
|---|---|---|
| 枚舉結果為空 | 網絡未啟用/無共享資源 | 檢查「網上鄰居」可見性、啟用檔案共享服務 |
| Access Denied | 權限不足 | 以管理員身份運行、檢查網絡存取權限 |
| 遞歸死循環 | 未正確過濾伺服器節點 | 嚴格判斷 dwDisplayType != RESOURCEDISPLAYTYPE_SERVER |
本實現直觀展示 Windows 網絡枚舉核心流程:開啟枚舉 → 分配緩衝 → 循環讀取 → 遞歸容器 → 釋放資源。代碼可直接移植至專案,或作為封裝類的基礎參考。