最可靠方式是通过INetworkListManager的GetConnectivity()检查NLM_CONNECTIVITY_IPV4_INTERNET或NLM_CONNECTIVITY_IPV6_INTERNET位掩码,而非依赖已过时且不探测真实连通性的InternetGetConnectedState。

用 NetworkListManager 判断是否有可用网络连接
Windows 不提供“联网”这种布尔值 API,而是暴露网络连接的拓扑状态。最可靠的方式是通过 COM 接口 INetworkListManager 查询当前是否至少有一个“已连接且有 Internet 访问能力”的网络。注意:它不等价于“有网卡”或“有本地连接”,而是基于 NLA(Network Location Awareness)服务的真实连通性感知。
实操要点:
- 必须初始化 COM(
CoInitializeEx(nullptr, COINIT_MULTITHREADED)),否则调用会失败 - 需要链接
ole32.lib和oleaut32.lib -
GetConnectivity()返回的是位掩码,要检查NLM_CONNECTIVITY_IPV4_INTERNET或NLM_CONNECTIVITY_IPV6_INTERNET,而非只看NLM_CONNECTIVITY_IPV4_LOCALNETWORK - 即使 WiFi 图标显示已连接,若 DHCP 失败或 DNS 不通,
GetConnectivity()也可能不返回 Internet 标志
// 示例片段(简化错误处理)
INetworkListManager* pNLM = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetworkListManager), nullptr,
CLSCTX_ALL, __uuidof(INetworkListManager),
(void**)&pNLM);
if (SUCCEEDED(hr) && pNLM) {
NLM_CONNECTIVITY conn;
hr = pNLM->GetConnectivity(&conn);
bool hasInternet = (conn & (NLM_CONNECTIVITY_IPV4_INTERNET | NLM_CONNECTIVITY_IPV6_INTERNET)) != 0;
pNLM->Release();
}
为什么 InternetGetConnectedState 不推荐用于实时判断
InternetGetConnectedState 是 WinINet 的遗留函数,它只读注册表键 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\Connections 并检查拨号/代理配置,**不触发真实网络探测**。在现代 Windows(Win10+)中,它几乎总是返回 TRUE,哪怕物理断网、WiFi 关闭、甚至禁用全部适配器。
常见误用场景:
立即学习“C++免费学习笔记(深入)”;
- 用它替代 ping 或 HTTP 请求做“在线检测” → 结果不可靠
- 在服务进程里调用 → 可能因权限或会话隔离返回过时结果
- 期望它反映 DNS 或路由可达性 → 它完全不检查这些
如果你只是想快速兜底(比如启动时粗略判断),它成本低;但要做 UI 状态同步或自动重连决策,必须换 INetworkListManager 或更底层方案。
监听网络变化:用 INetworkListManager::GetNetworks + 事件回调
单纯轮询 GetConnectivity() 效率低且易漏状态跳变。Windows 提供 INetworkConnectionEvents 回调机制,配合 Advise() 注册,可实时捕获连接/断开、网络类型变更(如从以太网切到手机热点)、IPv4/IPv6 可用性切换等事件。
关键注意事项:
- 回调运行在 STA 线程上,你的实现类必须支持 COM 套间模型(通常继承
IDispatch或用 ATL 实现) - 必须保存
cookie(Advise()返回值),并在释放前调用Unadvise(cookie),否则内存泄漏且后续通知可能 crash - 事件不保证顺序 —— 比如先收到“连接完成”,再收到“获得 IPv4 地址”,但中间可能跳过某些中间态
- 同一网络多次重连可能触发多个
ConnectivityChanged,需自己去重或节流
绕过 COM:用 GetIfTable2 查网卡状态(轻量但粗糙)
如果项目不能依赖 COM(如嵌入式子系统、沙箱环境),可退而求其次用 IP Helper API。调用 GetIfTable2 获取所有接口,遍历检查 InterfaceLuid 对应的 OperStatus == IfOperStatusUp 且 Ipv4Enabled || Ipv6Enabled。
但这只是“物理层/链路层就绪”,不是“能上网”。典型反例:
- 虚拟网卡(Hyper-V、Docker)常处于 Up 状态,但无路由
- 企业环境中,网卡 Up 但被策略限制访问外网
- PPPoE 连接成功但认证未完成,
OperStatus已为 Up
所以它只能作为辅助信号,必须搭配 DNS 查询(如 getaddrinfo 解析 www.microsoft.com)或 TCP 探测(如 connect 到 8.8.8.8:53)才能接近真实联网状态。
真正难的不是获取状态,而是定义“联网”——是能解析域名?能建 TCP 连接?能收 HTTP 响应?不同业务需求对应不同检测粒度。NLA 的 GetConnectivity 是 Windows 官方平衡点,但它的判定逻辑本身不公开,且受组策略(如 DisableLocationAwareness)影响。上线前务必在飞行模式、代理开启、防火墙拦截等边界条件下实测。


















