# 任务栏实时网速监测工具 **Repository Path**: user_gitee/NetSpeedMon ## Basic Information - **Project Name**: 任务栏实时网速监测工具 - **Description**: 在任务栏显示实时网速,界面简洁优美,多种主题可选,占用极低。 - **Primary Language**: C++ - **License**: AGPL-3.0 - **Default Branch**: master - **Homepage**: https://gitee.com/user_gitee/NetSpeedMon - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-10 - **Last Updated**: 2026-07-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: 实时网速, 网速监测, 任务栏工具 ## README # NetSpeedMon — Windows 任务栏实时网速悬浮窗 > Qt6 + C++17 + Win32 API | MSYS2 UCRT64 编译 | 稳定版 --- ## 目录 1. [项目概述](#1-项目概述) 2. [架构设计](#2-架构设计) 3. [悬浮窗稳定性机制](#3-悬浮窗稳定性机制) 4. [网络速度探测引擎](#4-网络速度探测引擎) 5. [配置系统](#5-配置系统) 6. [主题系统](#6-主题系统) 7. [系统托盘](#7-系统托盘) 8. [全屏检测](#8-全屏检测) 9. [单实例与启动参数](#9-单实例与启动参数) 10. [日志系统](#10-日志系统) 11. [构建指南](#11-构建指南) 12. [项目结构](#12-项目结构) 13. [发布包结构](#13-发布包结构) --- ## 1. 项目概述 NetSpeedMon 是一个 Windows 任务栏网速监测工具,在任务栏上方显示实时上传/下载速度。核心特性: - **透明悬浮窗**:无边框半透明窗口叠加在任务栏上方,只渲染文字无背景 - **实时网速**:每秒刷新,支持 auto/KB/MB/short 四种单位模式 - **双模探测**:PDH 性能计数器 + GetIfTable 备用引擎,自动降级 - **稳定常驻**:50ms 看门狗 + `WS_EX_TOPMOST` 三重防御,点击任务栏/桌面不消失 - **全屏自动隐藏**:检测前台全屏窗口覆盖任务栏时主动隐藏 - **系统托盘**:右键菜单(打开主界面/退出),悬停显示网速 - **主题系统**:霓虹 / 暗黑 / 明亮 / 赛博 / 简白 / 自定,6 套主题 - **字体自定义**:字体名称、大小、加粗、斜体、颜色完全可配 - **单实例**:重复打开激活已有窗口 - **配置持久化**:JSON 配置文件,窗口位置记忆 - **开机自启**:注册表 `HKCU\Run`,`--min` 静默启动 - **按天日志**:`logs/YYYY-MM-DD.log` --- ## 2. 架构设计 ### 2.1 类图 ``` main() ├─ CreateMutexW("NetSpeedMon_SingleInstance") ← 单实例互斥体 ├─ Config::load() ← JSON 配置读取 └─ QApplication └─ App (QObject) ← 应用主控 ├─ NetMon [QObject] ← 网络探测(PDH + GetIfTable) ├─ Win [QWidget] ← 设置主窗口 ├─ Ov [QWidget] ← 任务栏悬浮窗 └─ Tray [QObject] ← 系统托盘 ``` ### 2.2 各模块职责 | 模块 | 类型 | 职责 | |------|------|------| | `App` | QObject | 组件装配、信号连接、生命周期管理 | | `NetMon` | QObject | 网络速度探测,`updated(up, down)` 信号每秒发射 | | `Win` | QWidget | 设置界面:主题/字体/单位/位置/全屏/开机自启 | | `Ov` | QWidget | 任务栏悬浮窗:透明置顶、鼠标穿透、看门狗保活 | | `Tray` | QObject | 系统托盘:图标、悬停提示、右键菜单 | | `Config` | struct | JSON 配置读写,`load()` / `save()` | | `Lg` | static class | 按天分割的文件日志 | ### 2.3 信号流 ``` NetMon::onPoll() (每秒) │ ├─ PDH 引擎 │ ├─ PdhExpandWildCardPathW 展开通配符 │ ├─ PdhAddCounterW 逐实例添加计数器 │ ├─ PdhCollectQueryData 采样 │ └─ PdhGetFormattedCounterValue 求和 │ └─ GetIfTable 引擎(备用/降级) ├─ GetIfTable 获取所有接口统计 └─ dwInOctets/dwOutOctets 每秒差值 │ emit updated(up, down) │ ├─→ Win::setSpeedLabel() 设置窗口速度标签 ├─→ Ov::updateSpeed() 触发悬浮窗 update() → paintEvent() └─→ Tray::setToolTip() 托盘悬停提示 ``` --- ## 3. 悬浮窗稳定性机制 这是整个项目最核心、最复杂的部分。经过多次迭代,最终形成以下稳定方案。 ### 3.1 问题演进史 | 版本 | 方案 | 现象 | |------|------|------| | 原始 v1-v4 | `SetParent(hw, Shell_TrayWnd)` 设为任务栏子窗口 | 每隔 200ms `repairWindow()` 触发了 `SetParent` + `SetWindowLongPtrW` + `SetWindowPos(SWP_SHOWWINDOW)` 重绘链 → **每秒 5 次闪烁**;点击桌面/失焦 → 任务栏重排子窗口 → **消失** | | 尝试 1 | 移除 SetParent,`WS_EX_TOPMOST` 独立置顶 | 点击被悬浮窗拦截 → `mousePressEvent` → `activateWindow()` 切走焦点 → Windows 把 `WS_EX_TOOLWINDOW` 隐藏 → **消失** | | 尝试 2 | + `WS_EX_TRANSPARENT` 鼠标穿透 | `WS_EX_TRANSPARENT` 的 MSDN 描述表明它影响**绘制顺序**("should not be painted until siblings beneath..."),分层窗口下导致绘制冻结 | | 尝试 3 | 实色背景匹配任务栏色 | 视觉不完美(任务栏有模糊/渐变),用户不满意 | | **最终版** | **`WA_TranslucentBackground` + `WS_EX_TOPMOST` + `WM_NCHITTEST→HTTRANSPARENT` + 50ms 三重看门狗** | **稳定:透明 + 不闪 + 不消失** | ### 3.2 窗口属性完整矩阵 ``` ┌──────────────────────────────────────────────────────────────────┐ │ Qt 层 │ ├──────────────────────────────────────────────────────────────────┤ │ FramelessWindowHint 无边框 │ │ WA_TranslucentBackground 透明背景(Qt 内部使用分层窗口) │ │ WA_ShowWithoutActivating show() 时不激活窗口 │ │ WA_TransparentForMouseEvents Qt 事件系统不派发鼠标事件 │ ├──────────────────────────────────────────────────────────────────┤ │ Win32 层 │ ├──────────────────────────────────────────────────────────────────┤ │ WS_EX_NOACTIVATE 窗口不被激活,不受前台/后台切换影响 │ │ WS_EX_LAYERED 分层窗口(配合 WA_TranslucentBackground) │ │ WS_EX_TOOLWINDOW 不出现于 Alt+Tab 和任务栏按钮 │ │ WS_EX_TOPMOST 始终位于 z-order 最顶层(覆盖任务栏) │ ├──────────────────────────────────────────────────────────────────┤ │ nativeEvent 拦截 │ ├──────────────────────────────────────────────────────────────────┤ │ WM_NCHITTEST → 返回 HTTRANSPARENT(鼠标穿透到任务栏) │ │ WM_SHOWWINDOW(FALSE) → 非自主隐藏则拦截(*result=0; return true) │ │ WM_WINDOWPOSCHANGING → 过滤 SWP_HIDEWINDOW 标志 │ │ WM_NCCREATE → 重新应用全部 WS_EX_* 样式 │ └──────────────────────────────────────────────────────────────────┘ ``` ### 3.3 看门狗:50ms 三重防御 这是悬浮窗永不消失的最终保障。每 50ms 执行: ``` ┌─────────────────────────────────────────┐ │ onWatchdog() 每 50ms │ ├─────────────────────────────────────────┤ │ if (userHidden || wasFsHidden) return; │ │ │ │ ① 检查 WS_EX_TOPMOST 位 │ │ GetWindowLongPtrW(GWL_EXSTYLE) │ │ → 若被清除则 SetWindowLongPtrW 恢复 │ │ │ │ ② 检查可见性 │ │ IsWindowVisible(hw) │ │ → 若隐藏则 ShowWindow(SW_SHOWNOACTIVATE) │ │ │ │ ③ 无条件刷新 z-order │ │ SetWindowPos(HWND_TOPMOST, │ │ SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE) │ └─────────────────────────────────────────┘ ``` #### 为什么第③步是"无条件"的? Windows 在以下场景会重新计算顶层窗口的 z-order 带: - 点击任务栏按钮 - 打开/关闭开始菜单 - 通知弹出 - 桌面切换 - Alt+Tab 在这些操作中,即使 `WS_EX_TOPMOST` 位还在(第①步通过),窗口也可能已被挤到非 TOPMOST 的 z-order 带中,被其他 TOPMOST 窗口遮挡。**只有 `SetWindowPos(HWND_TOPMOST, ...)` 能将窗口强制拉回 TOPMOST 带的顶端。** ### 3.4 位置跟随:200ms 缓存对比 ```cpp void onRelocate() { // 计算 x, y 为屏幕坐标(悬浮窗应在任务栏上方位置) // 全屏检测 → 主动隐藏 if (fsc == "hide" && isFullscreenNow()) { SW_HIDE; return; } // 仅位置/大小实际变化时才调 SetWindowPos if (x != m_lastX || y != m_lastY || w != m_lastW || h != m_lastH) { m_lastX = x; m_lastY = y; m_lastW = w; m_lastH = h; SetWindowPos(hw, HWND_TOPMOST, x, y, w, h, SWP_NOACTIVATE); } } ``` **为什么用缓存对比?** 原始代码每 200ms 无条件 `SetWindowPos(SWP_SHOWWINDOW)`,触发 Windows 完整的重绘链。缓存对比确保位置不变时完全不调 SetWindowPos,消除周期性闪烁。 ### 3.5 鼠标穿透 两套机制叠加确保点击完全穿透到任务栏: | 层级 | 机制 | 效果 | |------|------|------| | Win32 | `WM_NCHITTEST` → 返回 `HTTRANSPARENT` | Windows 认为鼠标不在窗口上,点击路由到下方窗口 | | Qt | `WA_TransparentForMouseEvents` | Qt 事件系统不派发鼠标事件到 widget | > **为什么用 `WM_NCHITTEST` 而不是 `WS_EX_TRANSPARENT`?** > `WS_EX_TRANSPARENT` 的 MSDN 文档描述为影响绘制顺序("should not be painted until siblings beneath... have been painted"),在某些 Windows 版本会导致分层窗口绘制异常。`WM_NCHITTEST → HTTRANSPARENT` 是鼠标穿透的标准做法,不影响绘制。 ### 3.6 隐藏拦截 ```cpp // nativeEvent 中 if (m->message == WM_SHOWWINDOW && m->wParam == FALSE) { if (!m_allowHide) { *result = 0; return true; } // 拦截非自主隐藏 } if (m->message == WM_WINDOWPOSCHANGING) { auto* wp = (WINDOWPOS*)m->lParam; if ((wp->flags & SWP_HIDEWINDOW) && !m_allowHide) wp->flags &= ~SWP_HIDEWINDOW; // 过滤隐藏标志 } ``` `m_allowHide` 仅在 `setOvVisible()` 和 `prepareDestroy()` 内部设为 `true`,确保只有我们的代码能隐藏窗口。 --- ## 4. 网络速度探测引擎 ### 4.1 PDH 引擎(首选) ``` PdhExpandWildCardPathW("\\Network Interface(*)\\Bytes Sent/sec") │ ├─ 展开通配符,获取实际网卡实例名 │ 例如: "Realtek PCIe GbE Family Controller" │ ├─ 过滤虚拟接口: isatap, teredo, loopback │ ├─ PdhOpenQueryW → PdhAddCounterW (每个实例两条计数器) │ • \Network Interface(实例)\Bytes Sent/sec (上传) │ • \Network Interface(实例)\Bytes Received/sec (下载) │ └─ PdhCollectQueryData → PdhGetFormattedCounterValue → 求和 ``` #### 冻结检测 ```cpp // 值连续 3 次无变化 → 视为冻结 → 降级 GetIfTable if (changed) { m_pdhStaleCount = 0; // 使用 PDH 值 } else { m_pdhStaleCount++; if (m_pdhStaleCount >= 3) { // 降级到 GetIfTable } } ``` ### 4.2 GetIfTable 引擎(备用) ```cpp GetIfTable(&table, &size, FALSE); for (DWORD i = 0; i < table->dwNumEntries; i++) { MIB_IFROW& row = table->table[i]; if (row.dwType == IF_TYPE_SOFTWARE_LOOPBACK || row.dwType == IF_TYPE_TUNNEL) continue; // 跳过环回/隧道 totalIn += row.dwInOctets; totalOut += row.dwOutOctets; } // 每秒差值 = 当前累计 - 上次累计 // DWORD 溢出处理: 差值减法不受 4GB 回绕影响 ``` ### 4.3 优先级与降级策略 ``` 程序启动 │ ├─ PdhExpandWildCardPathW 成功 → 使用 PDH │ ├─ 有数据且变化 → 保持 PDH │ └─ 连续 3 秒无变化 → 降级 GetIfTable │ └─ PdhExpandWildCardPathW 失败 → 使用 GetIfTable ``` ### 4.4 单位格式化 | 模式 | < 1 KB | 1 KB ~ 1 MB | ≥ 1 MB | |------|--------|-------------|--------| | `auto` | `B/s` | `KB/s` | `MB/s` | | `KB` | `KB/s` | `KB/s` | `KB/s` | | `MB` | `MB/s` | `MB/s` | `MB/s` | | `short` | `B/s` | `K/s` | `M/s` | 数字精度: - 值 < 10 → 保留 2 位小数 - 值 < 100 → 保留 1 位小数 - 值 ≥ 100 → 整数 --- ## 5. 配置系统 ### 5.1 配置文件 位置:`/config/settings.json` ```json { "ff": "Microsoft YaHei UI", "fs": 12, "fb": false, "fi": false, "fc": "#00ff88", "theme": "霓虹", "sti": true, "sto": true, "tp": "right", "um": "auto", "fsc": "hide", "as": false, "wx": -1, "wy": -1, "ww": 640, "wh": 240 } ``` ### 5.2 配置项详解 | 键 | 类型 | 默认值 | 说明 | |----|------|--------|------| | `ff` | string | `"Microsoft YaHei UI"` | 字体名称,从系统字体列表选择 | | `fs` | int | `12` | 字号,范围 8-48 | | `fb` | bool | `false` | 加粗 | | `fi` | bool | `false` | 斜体 | | `fc` | string | `"#00ff88"` | 自定主题的文字颜色 | | `theme` | string | `"霓虹"` | 主题:霓虹/暗黑/明亮/赛博/简白/自定 | | `sti` | bool | `true` | 显示系统托盘图标 | | `sto` | bool | `true` | 显示任务栏悬浮窗 | | `tp` | string | `"right"` | 悬浮窗位置:left/center/right | | `um` | string | `"auto"` | 单位模式:auto/KB/MB/short | | `fsc` | string | `"hide"` | 全屏行为:show(显示)/hide(隐藏) | | `as` | bool | `false` | 开机自启(写入 `HKCU\Run`) | | `wx/wy` | int | `-1` | 设置窗口位置,`-1` 为自动 | | `ww/wh` | int | `640/240` | 设置窗口尺寸 | ### 5.3 读写机制 - 使用 `QJsonDocument` 序列化/反序列化 - 每次保存完整覆写文件 - 首次运行自动生成默认配置 - `Win::~Win()` 析构时自动保存窗口位置 ### 5.4 开机自启实现 ```cpp // 写入注册表 HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run "NetSpeedMon" = "" --min // --min 参数:启动时不显示设置窗口,仅托盘 + 悬浮窗 ``` --- ## 6. 主题系统 ### 6.1 内置主题 | 主题 | 背景色 | 文字色 | 强调色 | 悬浮窗箭头色 | |------|--------|--------|--------|-------------| | 霓虹 | `#1a1a2e` | `#00ff88` | `#00ff88` | ↑橙 `#FF7043` ↓蓝 `#4FC3F7` | | 暗黑 | `#ffffff` | `#000000` | `#000000` | 同文字色 | | 明亮 | `#f5f5f5` | `#333333` | `#2196F3` | 同文字色 | | 赛博 | `#0a0a1a` | `#00ffff` | `#ff00ff` | 同文字色 | | 简白 | `#000000` | `#ffffff` | `#ffffff` | 同文字色 | | 自定 | — | 用户选择 | — | 用户选择 | > **悬浮窗不受主题背景色影响**:悬浮窗使用 `WA_TranslucentBackground` 透明背景,只渲染文字。主题背景色仅影响设置主窗口。悬浮窗箭头色固定为橙(`#FF7043`)和蓝(`#4FC3F7`),自定主题时使用用户选择的颜色。 --- ## 7. 系统托盘 ### 7.1 图标 - 优先加载 `/app.ico` - 无此文件时内置生成蓝色圆角方块 "N" 图标 - 图标可见性由 `sti` 配置控制 ### 7.2 交互 | 操作 | 行为 | |------|------| | 左键单击 | 打开设置主窗口 | | 左键双击 | 打开设置主窗口 | | 悬停 | 显示实时网速 tooltip | | 右键 → 打开主界面 | 打开设置主窗口 | | 右键 → 退出 | 清理并退出 | ### 7.3 设置窗口关闭行为 点 ✕ 关闭设置窗口 → 隐藏到托盘,不退出程序。通过托盘右键菜单"退出"才能真正退出。 --- ## 8. 全屏检测 ### 8.1 检测逻辑 ```cpp bool isFullscreenNow() { HWND hFg = GetForegroundWindow(); HWND hTask = FindWindowW(L"Shell_TrayWnd", nullptr); if (!hFg || !hTask) return false; if (hFg == hTask || IsChild(hTask, hFg)) return false; // 检查前台窗口是否完全覆盖任务栏区域 return fgRect 完全覆盖 tbRect; } ``` ### 8.2 行为 | `fsc` 设置 | 检测到全屏时 | 全屏退出时 | |-----------|-------------|-----------| | `"hide"` | 主动 `ShowWindow(SW_HIDE)` | 主动 `ShowWindow(SW_SHOWNOACTIVATE)` | | `"show"` | 保持显示 | 保持显示 | 全屏隐藏期间看门狗跳过(`m_wasFsHidden = true`),避免与主动隐藏冲突。 --- ## 9. 单实例与启动参数 ### 9.1 单实例 ```cpp HANDLE hMutex = CreateMutexW(nullptr, FALSE, L"NetSpeedMon_SingleInstance"); if (GetLastError() == ERROR_ALREADY_EXISTS) { HWND hWnd = FindWindowW(nullptr, WIN_TITLE); if (hWnd) { ShowWindow(hWnd, SW_RESTORE); SetForegroundWindow(hWnd); } return 0; } ``` ### 9.2 启动参数 | 参数 | 行为 | |------|------| | (无) | 显示设置窗口 + 托盘 + 悬浮窗 | | `--min` | 仅托盘 + 悬浮窗,不显示设置窗口(开机自启用) | --- ## 10. 日志系统 ### 10.1 位置与格式 ``` /logs/YYYY-MM-DD.log ``` ``` [2026-05-27 10:30:00] NetSpeedMon v4 启动 [2026-05-27 10:30:00] PDH 展开 2 个网卡实例 [2026-05-27 10:30:05] 配置已保存并应用 (主题=霓虹, 字体=Microsoft YaHei UI, 大小=12) [2026-05-27 10:30:10] NetSpeedMon 退出 ``` ### 10.2 特性 - 按天自动分割,午夜首次写入时切换到新文件 - 线程安全(`QMutex` 保护) - 惰性创建:首次写入时才创建 `logs/` 目录和日志文件 --- ## 11. 构建指南 ### 11.1 环境要求 | 组件 | 来源 | 说明 | |------|------|------| | MSYS2 UCRT64 | [msys2.org](https://www.msys2.org/) | Unix 兼容环境 | | GCC ≥ 14.2 | `pacman -S mingw-w64-ucrt-x86_64-gcc` | C++17 编译器 | | CMake ≥ 3.20 | `pacman -S mingw-w64-ucrt-x86_64-cmake` | 构建系统 | | Qt6 ≥ 6.7 | `pacman -S mingw-w64-ucrt-x86_64-qt6-base` | GUI 框架 | | Ninja | `pacman -S mingw-w64-ucrt-x86_64-ninja` | 构建工具 | ### 11.2 安装依赖 ```bash pacman -S mingw-w64-ucrt-x86_64-qt6-base \ mingw-w64-ucrt-x86_64-cmake \ mingw-w64-ucrt-x86_64-ninja ``` ### 11.3 编译 ```bash # 配置 cmake -B build -G Ninja \ -DCMAKE_CXX_COMPILER=g++ \ -DCMAKE_BUILD_TYPE=Release # 编译 cmake --build build --config Release # 部署 Qt 运行库 windeployqt build/NetSpeedMon.exe --no-translations # 输出到 dist mkdir dist cp build/NetSpeedMon.exe dist/ cp build/*.dll dist/ cp -r build/platforms dist/ cp -r build/styles dist/ cp -r build/imageformats dist/ cp -r build/tls dist/ cp -r build/generic dist/ cp -r build/networkinformation dist/ cp app.ico dist/ ``` ### 11.4 CMakeLists.txt 关键配置 ```cmake cmake_minimum_required(VERSION 3.20) project(NetSpeedMon LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_AUTOMOC ON) # WIN32 = 无控制台窗口 add_executable(NetSpeedMon WIN32 main.cpp) find_package(Qt6 REQUIRED COMPONENTS Widgets) target_link_libraries(NetSpeedMon PRIVATE Qt6::Widgets iphlpapi # GetIfTable pdh # PDH 性能计数器 ) # 静态链接 GCC/MinGW 运行时,减少外部依赖 set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} \ -static-libgcc -static-libstdc++ -static") # 嵌入图标资源(Windows .rc 方式) configure_file("${CMAKE_SOURCE_DIR}/app.ico" "${CMAKE_BINARY_DIR}/app.ico" COPYONLY) file(WRITE "${CMAKE_BINARY_DIR}/app.rc" "IDI_ICON1 ICON \"${CMAKE_BINARY_DIR}/app.ico\"\n") target_sources(NetSpeedMon PRIVATE "${CMAKE_BINARY_DIR}/app.rc") ``` --- ## 12. 项目结构 ``` NetSpeedMon/ ├── main.cpp # 全部源码(单文件,~1400 行) ├── CMakeLists.txt # CMake 构建配置 ├── app.ico # 程序图标(多分辨率 ICO) ├── DEVELOPMENT.md # 本文档 └── bin/ # 编译产物(免安装运行) ├── NetSpeedMon.exe # 主程序 ├── app.ico # 图标副本 ├── Qt6Core.dll # Qt6 核心库 ├── Qt6Gui.dll # Qt6 图形库 ├── Qt6Widgets.dll # Qt6 控件库 ├── D3Dcompiler_47.dll # DirectX 编译器 ├── libgcc_s_seh-1.dll # GCC 运行时 ├── libstdc++-6.dll # C++ 标准库 ├── libwinpthread-1.dll # POSIX 线程 ├── libfreetype-6.dll # 字体渲染 ├── libharfbuzz-0.dll # 文字塑形 ├── libpng16-16.dll # PNG 图像 ├── libicudt78.dll # ICU Unicode 数据(最大依赖 ~33MB) ├── libicuin78.dll # ICU 国际化 ├── libicuuc78.dll # ICU Unicode 核心 ├── libzstd.dll # Zstandard 压缩 ├── zlib1.dll # zlib 压缩 ├── ...其他 DLL... ├── platforms/ # Qt 平台插件 │ └── qwindows.dll ├── styles/ # Qt 样式插件 │ └── qmodernwindowsstyle.dll ├── imageformats/ # Qt 图像格式插件 │ ├── qgif.dll │ ├── qico.dll │ └── qjpeg.dll ├── tls/ # Qt TLS 插件 │ ├── qcertonlybackend.dll │ └── qschannelbackend.dll ├── generic/ # Qt 通用插件 │ └── qtuiotouchplugin.dll └── networkinformation/ # Qt 网络信息插件 ├── qglib.dll └── qnetworklistmanager.dll ``` --- ## 13. 发布包结构 发布包(`release/NetSpeedMon/`)即开即用,无需安装: 1. 将整个 `release/NetSpeedMon/` 目录复制到任意位置 2. 双击 `bin/NetSpeedMon.exe` 启动 3. 首次运行自动在 exe 同级目录创建 `config/settings.json` **注意**:`config/` 和 `logs/` 目录在程序运行时自动创建于 `bin/`(exe 所在目录)中,与 `NetSpeedMon.exe` 同级。 --- ## 附录 A:已知限制与注意事项 1. **杀毒软件误报**:部分杀软可能对无数字签名的 EXE + DLL 组合报毒,属正常现象 2. **Windows 11 任务栏**:Win11 的任务栏布局与 Win10 不同,`TrayNotifyWnd` 查找可能失败,会自动 fallback 到右侧固定偏移 3. **高 DPI**:程序开启了 `PassThrough` 策略,在 125%/150% 缩放下应正常工作 4. **多网卡**:PDH 引擎自动求和所有物理网卡的速度 ## 附录 B:技术依赖 | 库 | 用途 | |----|------| | Qt6::Widgets | GUI 框架 | | pdh.lib | Windows 性能计数器 API | | iphlpapi.lib | GetIfTable 网络接口统计 | | kernel32.dll | CreateMutexW, SetWindowPos 等 Win32 API | | user32.dll | FindWindowW, ShowWindow 等窗口管理 | | gdi32.dll | 字体渲染(Qt 内部使用) | | dwmapi.dll | DWM 窗口合成(Qt 内部使用) | --- > 版本:v5 稳定版 | 更新日期:2026-05-27