应使用 WNetEnumResource 配合 WNetOpenEnum 枚举 RESOURCE_CONNECTED 资源,通过 NETRESOURCE 的 lpLocalName 和 lpRemoteName 获取有效网络驱动器,并用 GetDriveType 二次验证类型为 DRIVE_REMOTE 或 DRIVE_NETWORK,确保结果准确可靠。

Windows 下用 WNetEnumResource 枚举已连接的网络驱动器
Windows 系统中,网络共享映射为盘符(如 Z:)后,并不保存在注册表固定位置,也不能靠遍历 GetLogicalDrives() 直接区分本地/网络盘符。真正可靠的方式是调用 Windows 网络 API 的枚举接口:WNetEnumResource 配合 WNetOpenEnum,作用对象是当前会话中“已成功连接”的网络资源(即用户执行过 net use Z: \servershare 或资源管理器手动映射过的)。
关键点在于:必须指定枚举范围为 RESOURCE_CONNECTED,否则会拿到大量未连接或不可见的资源(比如域内所有可见共享),干扰判断。
-
WNetOpenEnum第二个参数必须传RESOURCE_CONNECTED,不能用RESOURCE_REMEMBERED(那是开机自动重连项,不一定当前有效) - 返回的每个
NETRESOURCE结构体中,lpLocalName字段就是盘符(如"Z:"),lpRemoteName是 UNC 路径(如"\\server\share") - 需循环调用
WNetEnumResource,因为一次最多只返回 1024 个条目;缓冲区要动态扩容,否则容易截断 - 记得最后调用
WNetCloseEnum,否则句柄泄漏(尤其在长期运行的服务中会出问题)
如何区分“真挂载”和“假盘符”(比如 ISO 挂载、OneDrive 同步盘)
仅靠 GetDriveType 返回 DRIVE_REMOTE 并不可靠——某些云同步客户端(如 OneDrive、Google Drive)会伪造该类型,而部分 SMB 共享在特定配置下反而返回 DRIVE_NETWORK 或误判为 DRIVE_FIXED。所以不能跳过 WNetEnumResource 直接查盘符类型。
更稳妥的做法是:先用 WNetEnumResource 拿到所有 RESOURCE_CONNECTED 条目,再对每个 lpLocalName 调用 GetDriveType 做二次确认。若返回非 DRIVE_REMOTE 且非 DRIVE_NETWORK,说明这个盘符虽然出现在网络资源列表里,但当前底层连接可能已失效或被其他机制接管,应过滤掉。
立即学习“C++免费学习笔记(深入)”;
- 常见误报:OneDrive 映射盘符常返回
DRIVE_FIXED,但它不是真实网络共享,lpRemoteName也为空或非法,直接丢弃 - 若
lpLocalName为空(NULL),说明这是无盘符的连接(如net use * \servershare),这类不计入“盘符列表”,按需跳过 - UNC 路径需检查是否以
"\\"开头,避免解析错误(有些旧程序会补全成"\\server\share",注意双反斜杠转义)
C++ 实操代码片段(带错误处理与内存管理)
#include <windows.h>
#include <winnetwk.h>
#include <vector>
#include <string>
<p>struct NetDriveInfo {
std::string localName; // e.g. "Z:"
std::string remoteName; // e.g. "\servershare"
};</p><p>std::vector<NetDriveInfo> GetConnectedNetworkDrives() {
std::vector<NetDriveInfo> result;
HANDLE hEnum = nullptr;
DWORD cbBuffer = 16384;
std::vector<BYTE> buffer(cbBuffer);
DWORD entries = 0;</p><pre class='brush:php;toolbar:false;'>if (WNetOpenEnum(RESOURCE_CONNECTED, RESOURCETYPE_DISK, 0, nullptr, &hEnum) != NO_ERROR) {
return result;
}
while (true) {
DWORD result = WNetEnumResource(hEnum, &entries, buffer.data(), &cbBuffer);
if (result == ERROR_MORE_DATA) {
buffer.resize(cbBuffer);
continue;
} else if (result != NO_ERROR) {
break;
}
NETRESOURCE* pRes = reinterpret_cast<NETRESOURCE*>(buffer.data());
for (DWORD i = 0; i < entries; ++i) {
if (pRes[i].dwDisplayType == RESOURCEDISPLAYTYPE_SHARE &&
pRes[i].lpLocalName && pRes[i].lpRemoteName) {
std::string local(pRes[i].lpLocalName);
std::string remote(pRes[i].lpRemoteName);
// 再次验证盘符有效性
UINT dt = GetDriveType((local + "\\").c_str());
if (dt == DRIVE_REMOTE || dt == DRIVE_NETWORK) {
result.push_back({local, remote});
}
}
}
break; // WNetEnumResource 通常一次返回全部(受限于 buffer)
}
if (hEnum) WNetCloseEnum(hEnum);
return result;}
注意:WNetOpenEnum 和 WNetEnumResource 在 winnetwk.h 中声明,链接时需加 -lNetapi32(MSVC 默认包含,MinGW 需显式链接)。另外,该 API 只返回当前用户会话的映射,不跨用户、不跨 session(如服务进程里调用将为空)。
为什么不用 PowerShell 或 net use 解析?
虽然 net use 命令输出看起来直观,但解析其文本输出极不稳定:不同系统语言(中文 Win10 输出含“状态”“本地”等字段)、不同版本格式微调(Win11 加了额外空格)、管道重定向时编码乱码(ANSI/UTF-8 混用),都会让正则匹配崩溃。PowerShell 的 Get-SmbMapping 更好些,但它依赖 SMB 协议栈,对 NFS 或 WebDAV 映射无效,且需要 .NET Framework / PowerShell 运行时,在嵌入式或精简系统中不可用。
原生 API 是唯一能覆盖所有 Windows 网络重定向器(包括第三方如 Samba Client、Dokan)的方案。只要系统显示“网络位置”里有该盘符,WNetEnumResource 就大概率能枚举出来——前提是调用者权限足够,且没被组策略禁用网络枚举(极少见,但生产环境要留意)。
真正的难点不在调用,而在容错:buffer 大小、多字节字符串截断、空指针访问、会话隔离。这些细节漏掉一个,程序在某台机器上就静默失败。



















