From d2aedfe45c02adc6a90dea9284bcaf9a17688efb Mon Sep 17 00:00:00 2001 From: Bebop Date: Sun, 7 Jun 2026 11:04:01 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=85=A8=E5=B1=80?= =?UTF-8?q?=E5=BF=AB=E6=8D=B7=E9=94=AE=E6=98=BE=E7=A4=BA/=E9=9A=90?= =?UTF-8?q?=E8=97=8F=E4=B8=BB=E7=AA=97=E5=8F=A3=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 默认热键 Ctrl+Shift+F12,可在常规设置中开关和自定义 - 使用 RegisterHotKey/WM_HOTKEY 实现系统级别全局热键 - 支持按键录制控件,可自定义修饰键组合 - 热键设置持久化保存在 config.ini - 支持中英文国际化 --- TrafficMonitor/CommonData.h | 7 +- TrafficMonitor/GeneralSettingsDlg.cpp | 119 +++++++++++++++++- TrafficMonitor/GeneralSettingsDlg.h | 10 ++ TrafficMonitor/TrafficMonitor.cpp | 10 ++ TrafficMonitor/TrafficMonitor.rc | Bin 107430 -> 107848 bytes TrafficMonitor/TrafficMonitorDlg.cpp | 56 +++++++++ TrafficMonitor/TrafficMonitorDlg.h | 4 + TrafficMonitor/language.h | 3 + TrafficMonitor/language/English.ini | 4 + .../language/Simplified_Chinese.ini | 4 + TrafficMonitor/resource.h | 7 +- 11 files changed, 220 insertions(+), 4 deletions(-) diff --git a/TrafficMonitor/CommonData.h b/TrafficMonitor/CommonData.h index 5d6f6b322..a2c3e4b90 100644 --- a/TrafficMonitor/CommonData.h +++ b/TrafficMonitor/CommonData.h @@ -397,7 +397,12 @@ struct GeneralSettingData hardware_monitor_item &= ~item_type; } - StringSet connections_hide; //用于保存哪些网络要从“选择网络连接”子菜单项中隐藏 + StringSet connections_hide; //用于保存哪些网络要从”选择网络连接”子菜单项中隐藏 + + // Global hotkey + bool hotkey_enabled{ true }; // 全局热键功能开关 + UINT hotkey_modifiers{ MOD_CONTROL | MOD_SHIFT }; // 热键修饰键(MOD_ALT | MOD_CONTROL | MOD_SHIFT | MOD_WIN) + UINT hotkey_vk{ VK_F12 }; // 热键虚拟键码 }; //定义监控时间间隔有效的最大值和最小值 diff --git a/TrafficMonitor/GeneralSettingsDlg.cpp b/TrafficMonitor/GeneralSettingsDlg.cpp index 00bc373e4..c9085a7a4 100644 --- a/TrafficMonitor/GeneralSettingsDlg.cpp +++ b/TrafficMonitor/GeneralSettingsDlg.cpp @@ -195,6 +195,7 @@ void CGeneralSettingsDlg::DoDataExchange(CDataExchange* pDX) DDX_Control(pDX, IDC_SELECT_CPU_COMBO, m_select_cpu_combo); DDX_Control(pDX, IDC_PLUGIN_MANAGE_BUTTON, m_plugin_manager_btn); DDX_Control(pDX, IDC_SELECT_CONNECTIONS_BUTTON, m_select_connection_btn); + DDX_Control(pDX, IDC_HOTKEY_BUTTON, m_hotkey_button); } void CGeneralSettingsDlg::SetControlEnable() @@ -215,6 +216,7 @@ void CGeneralSettingsDlg::SetControlEnable() EnableDlgCtrl(IDC_RESET_AUTO_RUN_BUTTON, m_data.auto_run); EnableDlgCtrl(IDC_AUTO_RUN_METHOD_REGESTRY_RADIO, m_data.auto_run); EnableDlgCtrl(IDC_AUTO_RUN_METHOD_TASK_SCHEDULE_RADIO, m_data.auto_run); + m_hotkey_button.EnableWindow(m_data.hotkey_enabled); } @@ -248,6 +250,8 @@ BEGIN_MESSAGE_MAP(CGeneralSettingsDlg, CTabDlg) ON_MESSAGE(WM_SPIN_EDIT_POS_CHANGED, &CGeneralSettingsDlg::OnSpinEditPosChanged) ON_BN_CLICKED(IDC_AUTO_RUN_METHOD_REGESTRY_RADIO, &CGeneralSettingsDlg::OnBnClickedAutoRunMethodRegestryRadio) ON_BN_CLICKED(IDC_AUTO_RUN_METHOD_TASK_SCHEDULE_RADIO, &CGeneralSettingsDlg::OnBnClickedAutoRunMethodTaskScheduleRadio) + ON_BN_CLICKED(IDC_HOTKEY_ENABLE_CHECK, &CGeneralSettingsDlg::OnBnClickedHotkeyEnableCheck) + ON_BN_CLICKED(IDC_HOTKEY_BUTTON, &CGeneralSettingsDlg::OnBnClickedHotkeyButton) END_MESSAGE_MAP() @@ -442,6 +446,11 @@ BOOL CGeneralSettingsDlg::OnInitDialog() m_plugin_manager_btn.SetIcon(theApp.GetMenuIcon(IDI_PLUGINS)); m_select_connection_btn.SetIcon(theApp.GetMenuIcon(IDI_CONNECTION)); + //初始化热键控件 + CheckDlgButton(IDC_HOTKEY_ENABLE_CHECK, m_data.hotkey_enabled); + SetHotKeyButtonText(); + m_hotkey_button.EnableWindow(m_data.hotkey_enabled); + return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } @@ -567,13 +576,121 @@ void CGeneralSettingsDlg::OnBnClickedShowAllConnectionCheck() BOOL CGeneralSettingsDlg::PreTranslateMessage(MSG* pMsg) { - // TODO: 在此添加专用代码和/或调用基类 + // 热键捕获模式 + if (m_hotkey_capturing) + { + if (pMsg->message == WM_KEYDOWN || pMsg->message == WM_SYSKEYDOWN) + { + UINT vk = static_cast(pMsg->wParam); + + // ESC 取消捕获 + if (vk == VK_ESCAPE) + { + EndHotKeyCapture(true); + return TRUE; + } + + // 过滤纯修饰键 + if (vk != VK_CONTROL && vk != VK_MENU && vk != VK_SHIFT && + vk != VK_LWIN && vk != VK_RWIN && + vk != VK_LCONTROL && vk != VK_RCONTROL && + vk != VK_LMENU && vk != VK_RMENU && + vk != VK_LSHIFT && vk != VK_RSHIFT) + { + // 获取当前修饰键状态 + UINT modifiers = 0; + if (GetKeyState(VK_CONTROL) & 0x8000) modifiers |= MOD_CONTROL; + if (GetKeyState(VK_MENU) & 0x8000) modifiers |= MOD_ALT; + if (GetKeyState(VK_SHIFT) & 0x8000) modifiers |= MOD_SHIFT; + if (GetKeyState(VK_LWIN) & 0x8000 || GetKeyState(VK_RWIN) & 0x8000) + modifiers |= MOD_WIN; + + if (modifiers != 0) + { + OnHotKeyCaptured(modifiers, vk); + return TRUE; + } + } + return TRUE; // 捕获期间拦截所有按键 + } + // 点击按钮外区域取消捕获 + if (pMsg->message == WM_LBUTTONDOWN || pMsg->message == WM_RBUTTONDOWN) + { + HWND hwndButton = m_hotkey_button.GetSafeHwnd(); + if (pMsg->hwnd != hwndButton) + { + EndHotKeyCapture(true); + } + } + } + if (pMsg->message == WM_MOUSEMOVE) m_toolTip.RelayEvent(pMsg); return CTabDlg::PreTranslateMessage(pMsg); } +void CGeneralSettingsDlg::SetHotKeyButtonText() +{ + CString text; + + if (m_data.hotkey_modifiers & MOD_CONTROL) text += _T("Ctrl+"); + if (m_data.hotkey_modifiers & MOD_ALT) text += _T("Alt+"); + if (m_data.hotkey_modifiers & MOD_SHIFT) text += _T("Shift+"); + if (m_data.hotkey_modifiers & MOD_WIN) text += _T("Win+"); + + // 使用 MapVirtualKey + GetKeyNameText 获取按键名称 + UINT scan_code = MapVirtualKeyW(m_data.hotkey_vk, MAPVK_VK_TO_VSC); + if (m_data.hotkey_vk == VK_INSERT || m_data.hotkey_vk == VK_DELETE || + m_data.hotkey_vk == VK_HOME || m_data.hotkey_vk == VK_END || + m_data.hotkey_vk == VK_PRIOR || m_data.hotkey_vk == VK_NEXT || + m_data.hotkey_vk == VK_LEFT || m_data.hotkey_vk == VK_RIGHT || + m_data.hotkey_vk == VK_UP || m_data.hotkey_vk == VK_DOWN) + { + scan_code |= 0x100; // 扩展键标志 + } + wchar_t key_name_buf[64]{}; + GetKeyNameTextW(scan_code << 16, key_name_buf, 64); + + text += key_name_buf; + m_hotkey_button.SetWindowText(text); +} + +void CGeneralSettingsDlg::StartHotKeyCapture() +{ + m_hotkey_capturing = true; + m_hotkey_button.GetWindowText(m_hotkey_original_text); + m_hotkey_button.SetWindowText(CCommon::LoadText(IDS_PRESS_HOTKEY, _T("Press a key combination..."))); + m_hotkey_button.SetFocus(); +} + +void CGeneralSettingsDlg::EndHotKeyCapture(bool cancelled) +{ + m_hotkey_capturing = false; + if (cancelled) + m_hotkey_button.SetWindowText(m_hotkey_original_text); + else + SetHotKeyButtonText(); +} + +void CGeneralSettingsDlg::OnHotKeyCaptured(UINT modifiers, UINT vk) +{ + m_data.hotkey_modifiers = modifiers; + m_data.hotkey_vk = vk; + EndHotKeyCapture(false); +} + +void CGeneralSettingsDlg::OnBnClickedHotkeyButton() +{ + StartHotKeyCapture(); +} + +void CGeneralSettingsDlg::OnBnClickedHotkeyEnableCheck() +{ + m_data.hotkey_enabled = (IsDlgButtonChecked(IDC_HOTKEY_ENABLE_CHECK) != 0); + SetControlEnable(); +} + afx_msg LRESULT CGeneralSettingsDlg::OnSpinEditPosChanged(WPARAM wParam, LPARAM lParam) { CSpinButtonCtrl* pSpin = (CSpinButtonCtrl*)wParam; diff --git a/TrafficMonitor/GeneralSettingsDlg.h b/TrafficMonitor/GeneralSettingsDlg.h index acec0447b..faaad4c4c 100644 --- a/TrafficMonitor/GeneralSettingsDlg.h +++ b/TrafficMonitor/GeneralSettingsDlg.h @@ -13,6 +13,11 @@ class CGeneralSettingsDlg : public CTabDlg CGeneralSettingsDlg(CWnd* pParent = NULL); // standard constructor virtual ~CGeneralSettingsDlg(); + void SetHotKeyButtonText(); + void StartHotKeyCapture(); + void EndHotKeyCapture(bool cancelled); + void OnHotKeyCaptured(UINT modifiers, UINT vk); + static void CheckTaskbarDisplayItem(); //选项设置数据 @@ -52,6 +57,9 @@ class CGeneralSettingsDlg : public CTabDlg CComboBox2 m_select_cpu_combo; CButton m_plugin_manager_btn; CButton m_select_connection_btn; + CButton m_hotkey_button; + bool m_hotkey_capturing{ false }; + CString m_hotkey_original_text; virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support @@ -102,6 +110,8 @@ class CGeneralSettingsDlg : public CTabDlg afx_msg void OnBnClickedSelectConnectionsButton(); afx_msg void OnBnClickedResetAutoRunButton(); afx_msg void OnEnChangeMonitorSpanEdit(); + afx_msg void OnBnClickedHotkeyEnableCheck(); + afx_msg void OnBnClickedHotkeyButton(); protected: public: afx_msg void OnBnClickedAutoRunMethodRegestryRadio(); diff --git a/TrafficMonitor/TrafficMonitor.cpp b/TrafficMonitor/TrafficMonitor.cpp index 17b32cb5e..f7cecfc88 100644 --- a/TrafficMonitor/TrafficMonitor.cpp +++ b/TrafficMonitor/TrafficMonitor.cpp @@ -83,6 +83,11 @@ void CTrafficMonitorApp::LoadConfig() ini.GetStringList(L"general", L"connections_hide", connections_hide, std::vector{}); m_general_data.connections_hide.FromVector(connections_hide); + //Global hotkey + m_general_data.hotkey_enabled = ini.GetBool(L"general", L"hotkey_enabled", true); + m_general_data.hotkey_modifiers = static_cast(ini.GetInt(L"general", L"hotkey_modifiers", MOD_CONTROL | MOD_SHIFT)); + m_general_data.hotkey_vk = static_cast(ini.GetInt(L"general", L"hotkey_vk", VK_F12)); + //Windows10颜色模式设置 bool is_windows10_light_theme = CWindowsSettingHelper::IsWindows10LightTheme(); if (is_windows10_light_theme) @@ -303,6 +308,11 @@ void CTrafficMonitorApp::SaveConfig() ini.WriteInt(L"general", L"hardware_monitor_item", m_general_data.hardware_monitor_item); ini.WriteStringList(L"general", L"connections_hide", m_general_data.connections_hide.ToVector()); + //Global hotkey + ini.WriteBool(L"general", L"hotkey_enabled", m_general_data.hotkey_enabled); + ini.WriteInt(L"general", L"hotkey_modifiers", static_cast(m_general_data.hotkey_modifiers)); + ini.WriteInt(L"general", L"hotkey_vk", static_cast(m_general_data.hotkey_vk)); + //主窗口设置 ini.WriteInt(L"config", L"transparency", m_cfg_data.m_transparency); ini.WriteBool(L"config", L"always_on_top", m_main_wnd_data.m_always_on_top); diff --git a/TrafficMonitor/TrafficMonitor.rc b/TrafficMonitor/TrafficMonitor.rc index d47601cd62b33695001fa19a682c7b5c5a46c1aa..456560456d9ab5a47c376145b4b635c553f5f266 100644 GIT binary patch delta 216 zcmZ2>obALZwhfcKm`xZ=CpUgrx!KP9kUX;`gV|)kTaKGuvcGXn_6z5j{NS*btp|fY zLkNR6gDXQMLp+f5V{l|}V(VK8AZ0Gg)* URB1AK;(X=ifU@lYWsG6E0131**#H0l delta 66 zcmV-I0KNan$_A##2C$M!12F(MlhGhClRmbhvuI109|1R$vD-tlL~Hg6m-tTs7ME;j Y0U(!1ECCFcAP@l{gFbk-K6n8NEr~7|SpWb4 diff --git a/TrafficMonitor/TrafficMonitorDlg.cpp b/TrafficMonitor/TrafficMonitorDlg.cpp index e7952e9df..c85f14ed2 100644 --- a/TrafficMonitor/TrafficMonitorDlg.cpp +++ b/TrafficMonitor/TrafficMonitorDlg.cpp @@ -127,6 +127,7 @@ BEGIN_MESSAGE_MAP(CTrafficMonitorDlg, CDialog) ON_COMMAND(ID_PLUGIN_DETAIL_TASKBAR, &CTrafficMonitorDlg::OnPluginDetailTaksbar) ON_WM_POWERBROADCAST() ON_WM_DWMCOLORIZATIONCOLORCHANGED() + ON_MESSAGE(WM_HOTKEY, &CTrafficMonitorDlg::OnHotKey) END_MESSAGE_MAP() @@ -742,6 +743,9 @@ void CTrafficMonitorDlg::ApplySettings(COptionsDlg& optionsDlg) bool is_show_notify_icon_changed = (optionsDlg.m_tab3_dlg.m_data.show_notify_icon != theApp.m_general_data.show_notify_icon); bool is_connections_hide_changed = (optionsDlg.m_tab3_dlg.m_data.connections_hide.data() != theApp.m_general_data.connections_hide.data()); bool d2d_turned_on = (theApp.m_taskbar_data.disable_d2d && !optionsDlg.m_tab2_dlg.m_data.disable_d2d); + bool is_hotkey_changed = (optionsDlg.m_tab3_dlg.m_data.hotkey_enabled != theApp.m_general_data.hotkey_enabled + || optionsDlg.m_tab3_dlg.m_data.hotkey_modifiers != theApp.m_general_data.hotkey_modifiers + || optionsDlg.m_tab3_dlg.m_data.hotkey_vk != theApp.m_general_data.hotkey_vk); //需要重新关闭再打开任务栏窗口的情况 bool taskbar_changed = (theApp.m_taskbar_data.show_taskbar_wnd_in_secondary_display != optionsDlg.m_tab2_dlg.m_data.show_taskbar_wnd_in_secondary_display || theApp.m_taskbar_data.secondary_display_index != optionsDlg.m_tab2_dlg.m_data.secondary_display_index @@ -860,6 +864,10 @@ void CTrafficMonitorDlg::ApplySettings(COptionsDlg& optionsDlg) CSkinManager::Instance().Save(); } + //如果热键设置变化了,重新注册全局热键 + if (is_hotkey_changed) + RegisterGlobalHotKey(); + theApp.SaveConfig(); theApp.SaveGlobalConfig(); } @@ -1141,6 +1149,9 @@ BOOL CTrafficMonitorDlg::OnInitDialog() SetTimer(TASKBAR_TIMER, 100, NULL); + //注册全局热键 + RegisterGlobalHotKey(); + return TRUE; // 除非将焦点设置到控件,否则返回 TRUE } @@ -2412,6 +2423,9 @@ void CTrafficMonitorDlg::OnDestroy() { CDialog::OnDestroy(); + //注销全局热键 + UnregisterGlobalHotKey(); + //程序退出时删除通知栏图标 ::Shell_NotifyIcon(NIM_DELETE, &m_ntIcon); @@ -2577,6 +2591,48 @@ void CTrafficMonitorDlg::OnShowMainWnd() theApp.SaveConfig(); } +void CTrafficMonitorDlg::RegisterGlobalHotKey() +{ + UnregisterGlobalHotKey(); + + if (!theApp.m_general_data.hotkey_enabled) + return; + + UINT modifiers = 0; + if (theApp.m_general_data.hotkey_modifiers & MOD_ALT) + modifiers |= MOD_ALT; + if (theApp.m_general_data.hotkey_modifiers & MOD_CONTROL) + modifiers |= MOD_CONTROL; + if (theApp.m_general_data.hotkey_modifiers & MOD_SHIFT) + modifiers |= MOD_SHIFT; + if (theApp.m_general_data.hotkey_modifiers & MOD_WIN) + modifiers |= MOD_WIN; + modifiers |= MOD_NOREPEAT; + + if (::RegisterHotKey(m_hWnd, 1, modifiers, theApp.m_general_data.hotkey_vk)) + { + m_hotkey_registered = true; + } +} + +void CTrafficMonitorDlg::UnregisterGlobalHotKey() +{ + if (m_hotkey_registered) + { + ::UnregisterHotKey(m_hWnd, 1); + m_hotkey_registered = false; + } +} + +LRESULT CTrafficMonitorDlg::OnHotKey(WPARAM wParam, LPARAM lParam) +{ + if (wParam == 1) + { + OnShowMainWnd(); + } + return 0; +} + void CTrafficMonitorDlg::OnChangeSkin() { diff --git a/TrafficMonitor/TrafficMonitorDlg.h b/TrafficMonitor/TrafficMonitorDlg.h index 7a5e5b905..65d2ce3ef 100644 --- a/TrafficMonitor/TrafficMonitorDlg.h +++ b/TrafficMonitor/TrafficMonitorDlg.h @@ -109,6 +109,7 @@ class CTrafficMonitorDlg : public CDialog unsigned int m_taskbar_timer_cnt{0}; //适用于TaskBarDlg的定时器触发次数(自程序启动以来的秒数) unsigned int m_monitor_time_cnt{}; int m_zero_speed_cnt{}; //如果检测不到网速,该变量就会自加 + bool m_hotkey_registered{ false }; int m_insert_to_taskbar_cnt{}; //用来统计尝试嵌入任务栏的次数 int m_cannot_insert_to_task_bar_warning{ true }; //指示是否会在无法嵌入任务栏时弹出提示框 @@ -237,6 +238,9 @@ class CTrafficMonitorDlg : public CDialog afx_msg void OnAppAbout(); afx_msg void OnShowCpuMemory2(); afx_msg void OnShowMainWnd(); + void RegisterGlobalHotKey(); + void UnregisterGlobalHotKey(); + afx_msg LRESULT OnHotKey(WPARAM wParam, LPARAM lParam); afx_msg void OnChangeSkin(); afx_msg LRESULT OnTaskBarCreated(WPARAM wParam, LPARAM lParam); //afx_msg void OnSetFont(); diff --git a/TrafficMonitor/language.h b/TrafficMonitor/language.h index 659fc2b0f..805f094ab 100644 --- a/TrafficMonitor/language.h +++ b/TrafficMonitor/language.h @@ -218,6 +218,9 @@ #define IDS_PLUGIN_NEW_VERSION_INFO L"IDS_PLUGIN_NEW_VERSION_INFO" #define IDS_TRAFFICMONITOR_PLUGIN_NITIFICATION L"IDS_TRAFFICMONITOR_PLUGIN_NITIFICATION" #define IDS_SKIN_FILE_ERROR_INFO L"IDS_SKIN_FILE_ERROR_INFO" +#define IDS_HOTKEY_ENABLE L"IDS_HOTKEY_ENABLE" +#define IDS_PRESS_HOTKEY L"IDS_PRESS_HOTKEY" +#define IDS_HOTKEY_REGISTER_FAILED L"IDS_HOTKEY_REGISTER_FAILED" #define TXT_OK L"TXT_OK" #define TXT_CANCEL L"TXT_CANCEL" diff --git a/TrafficMonitor/language/English.ini b/TrafficMonitor/language/English.ini index 2d62d80d5..39779ec91 100644 --- a/TrafficMonitor/language/English.ini +++ b/TrafficMonitor/language/English.ini @@ -222,6 +222,9 @@ IDS_RESTORE_FROM_SLEEP_LOG = "The system has been restored from hibernation, the IDS_PLUGIN_NEW_VERSION_INFO = "Update available, latest version: <%1%>" IDS_TRAFFICMONITOR_PLUGIN_NITIFICATION = "TrafficMonitor Plugin Notification" IDS_SKIN_FILE_ERROR_INFO = "Skin file parse failed!" +IDS_HOTKEY_ENABLE = "Enable global hotkey (show/hide main window)" +IDS_PRESS_HOTKEY = "Press a key combination..." +IDS_HOTKEY_REGISTER_FAILED = "Failed to register the global hotkey. The hotkey may already be in use by another application." TXT_OK = "OK" TXT_CANCEL = "Cancel" @@ -295,6 +298,7 @@ TXT_MAIN_BOARD = "Main board" TXT_SELECT_HDD_STATIC = "Select the hard disk to be monitored:" TXT_SELECT_CPU_STATIC = "Select the CPU temperature to be monitored:" TXT_ADVANCED = "Advanced" +TXT_HOTKEY_ENABLE = "Enable global hotkey (show/hide main window)" TXT_SHOW_ALL_CONNECTION_CHECK = "Show all network connections" TXT_SELECT_CONNECTIONS_BUTTON = "&Select the connection to monitor..." TXT_CPU_ACQUISITION_METHOD = "CPU usage acquisition method:" diff --git a/TrafficMonitor/language/Simplified_Chinese.ini b/TrafficMonitor/language/Simplified_Chinese.ini index 592e8d5cd..1585ed810 100644 --- a/TrafficMonitor/language/Simplified_Chinese.ini +++ b/TrafficMonitor/language/Simplified_Chinese.ini @@ -222,6 +222,9 @@ IDS_RESTORE_FROM_SLEEP_LOG = "系统已从休眠状态恢复,已重新初始 IDS_PLUGIN_NEW_VERSION_INFO = "有更新,最新版本:<%1%>" IDS_TRAFFICMONITOR_PLUGIN_NITIFICATION = "TrafficMonitor 插件通知" IDS_SKIN_FILE_ERROR_INFO = "皮肤文件解析失败!" +IDS_HOTKEY_ENABLE = "启用全局热键(显示/隐藏主窗口)" +IDS_PRESS_HOTKEY = "按下按键组合..." +IDS_HOTKEY_REGISTER_FAILED = "注册全局热键失败,该热键可能已被其他程序占用。" TXT_OK = "确定" TXT_CANCEL = "取消" @@ -295,6 +298,7 @@ TXT_MAIN_BOARD = "主板" TXT_SELECT_HDD_STATIC = "选择监控的硬盘:" TXT_SELECT_CPU_STATIC = "选择监控的CPU温度:" TXT_ADVANCED = "高级" +TXT_HOTKEY_ENABLE = "启用全局热键(显示/隐藏主窗口)" TXT_SHOW_ALL_CONNECTION_CHECK = "显示所有网络连接" TXT_SELECT_CONNECTIONS_BUTTON = "选择要监控的网络连接(&S)..." TXT_CPU_ACQUISITION_METHOD = "CPU使用率获取方式:" diff --git a/TrafficMonitor/resource.h b/TrafficMonitor/resource.h index f6a70b924..2dd64dd56 100644 --- a/TrafficMonitor/resource.h +++ b/TrafficMonitor/resource.h @@ -462,13 +462,16 @@ #define ID_PLUGIN_COMMAND_MAX 33664 #define ID_RESTORE_DEFAULT 33665 +#define IDC_HOTKEY_ENABLE_CHECK 1223 +#define IDC_HOTKEY_BUTTON 1224 + // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 349 #define _APS_NEXT_COMMAND_VALUE 33666 -#define _APS_NEXT_CONTROL_VALUE 1221 +#define _APS_NEXT_CONTROL_VALUE 1225 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif From c8920f727da1af401c958856299de709a54e2608 Mon Sep 17 00:00:00 2001 From: Bebop Date: Sun, 7 Jun 2026 18:56:49 +0800 Subject: [PATCH 2/5] =?UTF-8?q?@=20=E6=96=B0=E5=A2=9E=E8=83=8C=E6=99=AF?= =?UTF-8?q?=E5=A4=9A=E5=B8=A7=E5=8A=A8=E7=94=BB=E6=94=AF=E6=8C=81=EF=BC=8C?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E7=9A=AE=E8=82=A4=E5=8A=A8=E6=80=81=E6=95=88?= =?UTF-8?q?=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SkinFile: 背景存储改为 vector 支持多帧加载 - LoadMultiFramePng: 优先加载 background_N.png 序列,回退到单帧 background.png - GetCurrentBackgroundFrame: 每次 DrawInfo 循环切换帧,实现动画 - vcxproj: 修复 print_compile_time.bat 预构建事件路径问题 @ --- TrafficMonitor/SkinFile.cpp | 92 ++++++++++++++++++++++----- TrafficMonitor/SkinFile.h | 8 ++- TrafficMonitor/TrafficMonitor.vcxproj | 24 +++---- 3 files changed, 93 insertions(+), 31 deletions(-) diff --git a/TrafficMonitor/SkinFile.cpp b/TrafficMonitor/SkinFile.cpp index c888d54ae..c09b6726f 100644 --- a/TrafficMonitor/SkinFile.cpp +++ b/TrafficMonitor/SkinFile.cpp @@ -16,8 +16,12 @@ CSkinFile::CSkinFile() CSkinFile::~CSkinFile() { - SAFE_DELETE(m_background_png_s); - SAFE_DELETE(m_background_png_l); + for (auto* img : m_background_png_s) + SAFE_DELETE(img); + m_background_png_s.clear(); + for (auto* img : m_background_png_l) + SAFE_DELETE(img); + m_background_png_l.clear(); } static CSkinFile::LayoutItem LayoutItemFromXmlNode(tinyxml2::XMLElement* ele) @@ -127,21 +131,60 @@ bool CSkinFile::Load(const wstring& skin_name) wstring path_dir = file_path_helper.GetDir(); - //载入png背景图 + //载入png背景图(支持多帧动画) m_is_png = true; - //先尝试加载png格式,失败则加载bmp格式 - SAFE_DELETE(m_background_png_s); - m_background_png_s = new Gdiplus::Image((path_dir + BACKGROUND_IMAGE_S_PNG).c_str()); - if (m_background_png_s->GetLastStatus() != Gdiplus::Ok) + //先清理旧帧 + for (auto* img : m_background_png_s) + SAFE_DELETE(img); + m_background_png_s.clear(); + for (auto* img : m_background_png_l) + SAFE_DELETE(img); + m_background_png_l.clear(); + m_animation_frame = 0; + + // 辅助lambda:尝试加载多帧PNG(background_0.png, background_1.png, ...) + // 若background_0.png不存在则回退到background.png单帧 + auto LoadMultiFramePng = [&](const wstring& base_name, std::vector& out_frames) -> bool { + // 先尝试多帧格式 background_0.png + wstring frame0_path = path_dir + base_name + L"_0.png"; + if (CCommon::FileExist(frame0_path.c_str())) + { + for (int i = 0; ; i++) + { + wstring frame_path = path_dir + base_name + L"_" + std::to_wstring(i) + L".png"; + if (!CCommon::FileExist(frame_path.c_str())) + break; + auto* img = new Gdiplus::Image(frame_path.c_str()); + if (img->GetLastStatus() == Gdiplus::Ok) + out_frames.push_back(img); + else + { + delete img; + break; + } + } + return !out_frames.empty(); + } + // 回退到单帧 background.png + wstring single_path = path_dir + base_name + L".png"; + if (CCommon::FileExist(single_path.c_str())) + { + auto* img = new Gdiplus::Image(single_path.c_str()); + if (img->GetLastStatus() == Gdiplus::Ok) + { + out_frames.push_back(img); + return true; + } + delete img; + } + return false; + }; + + if (!LoadMultiFramePng(L"background", m_background_png_s)) m_is_png = false; - } - SAFE_DELETE(m_background_png_l); - m_background_png_l = new Gdiplus::Image((path_dir + BACKGROUND_IMAGE_L_PNG).c_str()); - if (m_background_png_l->GetLastStatus() != Gdiplus::Ok) - { + if (!LoadMultiFramePng(L"background_l", m_background_png_l)) m_is_png = false; - } //png背景图加载失败,使用bmp背景图 if (!m_is_png) @@ -398,6 +441,18 @@ void CSkinFile::SetAlpha(int alpha) m_alpha = alpha; } +Gdiplus::Image* CSkinFile::GetCurrentBackgroundFrame(bool show_more_info) +{ + auto& frames = show_more_info ? m_background_png_l : m_background_png_s; + if (frames.empty()) + return nullptr; + // 循环切换帧,实现动画效果 + size_t frame_idx = m_animation_frame % frames.size(); + Gdiplus::Image* current = frames[frame_idx]; + m_animation_frame++; + return current; +} + void CSkinFile::SetSettingData(const SkinSettingData& setting_data) { //如果字体有变化,则重新创建字体 @@ -429,8 +484,10 @@ void CSkinFile::DrawPreview(CDC* pDC, CRect rect) if (IsPNG()) //png背景使用GDI+绘制 { CDrawCommonEx gdiplus_drawer(pDC); - gdiplus_drawer.DrawImage(m_background_png_s, rect_s.TopLeft(), rect_s.Size(), IDrawCommon::StretchMode::STRETCH); - gdiplus_drawer.DrawImage(m_background_png_l, rect_l.TopLeft(), rect_l.Size(), IDrawCommon::StretchMode::STRETCH); + Gdiplus::Image* preview_s = m_background_png_s.empty() ? nullptr : m_background_png_s.front(); + Gdiplus::Image* preview_l = m_background_png_l.empty() ? nullptr : m_background_png_l.front(); + if (preview_s) gdiplus_drawer.DrawImage(preview_s, rect_s.TopLeft(), rect_s.Size(), IDrawCommon::StretchMode::STRETCH); + if (preview_l) gdiplus_drawer.DrawImage(preview_l, rect_l.TopLeft(), rect_l.Size(), IDrawCommon::StretchMode::STRETCH); } else { @@ -576,8 +633,9 @@ void CSkinFile::DrawInfo(CDC* pDC, bool show_more_info) //绘制背景 CDrawCommonEx gdiplus_drawer; gdiplus_drawer.Create(CDC::FromHandle(hdcMemory)); - Gdiplus::Image* background_image{ show_more_info ? m_background_png_l : m_background_png_s }; - gdiplus_drawer.DrawImage(background_image, CPoint(0, 0), rect.Size(), CDrawCommon::StretchMode::FILL); + Gdiplus::Image* background_image{ GetCurrentBackgroundFrame(show_more_info) }; + if (background_image) + gdiplus_drawer.DrawImage(background_image, CPoint(0, 0), rect.Size(), CDrawCommon::StretchMode::FILL); //保存完全透明的像素点 std::set alpha_points; diff --git a/TrafficMonitor/SkinFile.h b/TrafficMonitor/SkinFile.h index 903b29a6f..0d3da6419 100644 --- a/TrafficMonitor/SkinFile.h +++ b/TrafficMonitor/SkinFile.h @@ -138,6 +138,9 @@ class CSkinFile static void DrawSkinText(IDrawCommon& drawer, DrawStr draw_str, CRect rect, COLORREF color, Alignment align); + // 获取当前应显示的背景帧(按帧序列循环) + Gdiplus::Image* GetCurrentBackgroundFrame(bool show_more_info); + //绘制主界面中除背景图外所有显示项目 void DrawItemsInfo(IDrawCommon& drawer, Layout& layout, CFont& font) const; @@ -151,8 +154,9 @@ class CSkinFile CImage m_background_s; CImage m_background_l; bool m_is_png{}; - Gdiplus::Image* m_background_png_s{}; - Gdiplus::Image* m_background_png_l{}; + std::vector m_background_png_s; // 小布局背景帧(至少1帧) + std::vector m_background_png_l; // 大布局背景帧(至少1帧) + int m_animation_frame{}; // 当前动画帧索引,每次DrawInfo递增 int m_alpha{ 255 }; //不透明度,仅当背景为png时有效 SkinSettingData m_setting_data; bool m_is_error{ false }; diff --git a/TrafficMonitor/TrafficMonitor.vcxproj b/TrafficMonitor/TrafficMonitor.vcxproj index 8af662c88..9f4321451 100644 --- a/TrafficMonitor/TrafficMonitor.vcxproj +++ b/TrafficMonitor/TrafficMonitor.vcxproj @@ -301,7 +301,7 @@ $(IntDir);%(AdditionalIncludeDirectories) - print_compile_time.bat + call "$(ProjectDir)print_compile_time.bat" $(UM_IncludePath);%(AdditionalIncludeDirectories) @@ -340,7 +340,7 @@ $(IntDir);%(AdditionalIncludeDirectories) - print_compile_time.bat + call "$(ProjectDir)print_compile_time.bat" $(UM_IncludePath);%(AdditionalIncludeDirectories) @@ -379,7 +379,7 @@ $(IntDir);%(AdditionalIncludeDirectories) - print_compile_time.bat + call "$(ProjectDir)print_compile_time.bat" $(UM_IncludePath);%(AdditionalIncludeDirectories) @@ -418,7 +418,7 @@ $(IntDir);%(AdditionalIncludeDirectories) - print_compile_time.bat + call "$(ProjectDir)print_compile_time.bat" $(UM_IncludePath);%(AdditionalIncludeDirectories) @@ -456,7 +456,7 @@ $(IntDir);%(AdditionalIncludeDirectories) - print_compile_time.bat + call "$(ProjectDir)print_compile_time.bat" $(UM_IncludePath);%(AdditionalIncludeDirectories) @@ -494,7 +494,7 @@ $(IntDir);%(AdditionalIncludeDirectories) - print_compile_time.bat + call "$(ProjectDir)print_compile_time.bat" $(UM_IncludePath);%(AdditionalIncludeDirectories) @@ -538,7 +538,7 @@ $(IntDir);%(AdditionalIncludeDirectories) - print_compile_time.bat + call "$(ProjectDir)print_compile_time.bat" $(UM_IncludePath);%(AdditionalIncludeDirectories) @@ -581,7 +581,7 @@ $(IntDir);%(AdditionalIncludeDirectories) - print_compile_time.bat + call "$(ProjectDir)print_compile_time.bat" $(UM_IncludePath);%(AdditionalIncludeDirectories) @@ -624,7 +624,7 @@ $(IntDir);%(AdditionalIncludeDirectories) - print_compile_time.bat + call "$(ProjectDir)print_compile_time.bat" $(UM_IncludePath);%(AdditionalIncludeDirectories) @@ -667,7 +667,7 @@ $(IntDir);%(AdditionalIncludeDirectories) - print_compile_time.bat + call "$(ProjectDir)print_compile_time.bat" $(UM_IncludePath);%(AdditionalIncludeDirectories) @@ -709,7 +709,7 @@ $(IntDir);%(AdditionalIncludeDirectories) - print_compile_time.bat + call "$(ProjectDir)print_compile_time.bat" $(UM_IncludePath);%(AdditionalIncludeDirectories) @@ -751,7 +751,7 @@ $(IntDir);%(AdditionalIncludeDirectories) - print_compile_time.bat + call "$(ProjectDir)print_compile_time.bat" $(UM_IncludePath);%(AdditionalIncludeDirectories) From 32580f068f811c1e19ef718082211058aa19fc0f Mon Sep 17 00:00:00 2001 From: Bebop Date: Mon, 8 Jun 2026 10:40:14 +0800 Subject: [PATCH 3/5] =?UTF-8?q?@=20=E6=96=B0=E5=A2=9E=E5=8A=A8=E7=94=BB?= =?UTF-8?q?=E5=AE=9A=E6=97=B6=E5=99=A8=E9=A9=B1=E5=8A=A8=E7=9A=AE=E8=82=A4?= =?UTF-8?q?=E5=B8=A7=E5=88=B7=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - stdafx.h/TrafficMonitorDlg: 新增 ANIMATION_TIMER (66ms),独立于数据刷新率驱动背景重绘 - SkinFile: 帧切换改为基于 GetTickCount64 时间戳,动画帧率与监控间隔解耦 @ --- TrafficMonitor/SkinFile.cpp | 17 +++++++++++------ TrafficMonitor/SkinFile.h | 2 +- TrafficMonitor/TrafficMonitorDlg.cpp | 6 ++++++ TrafficMonitor/stdafx.h | 1 + 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/TrafficMonitor/SkinFile.cpp b/TrafficMonitor/SkinFile.cpp index c09b6726f..2d4df9721 100644 --- a/TrafficMonitor/SkinFile.cpp +++ b/TrafficMonitor/SkinFile.cpp @@ -140,7 +140,7 @@ bool CSkinFile::Load(const wstring& skin_name) for (auto* img : m_background_png_l) SAFE_DELETE(img); m_background_png_l.clear(); - m_animation_frame = 0; + m_animation_start_time = GetTickCount64(); // 辅助lambda:尝试加载多帧PNG(background_0.png, background_1.png, ...) // 若background_0.png不存在则回退到background.png单帧 @@ -446,11 +446,16 @@ Gdiplus::Image* CSkinFile::GetCurrentBackgroundFrame(bool show_more_info) auto& frames = show_more_info ? m_background_png_l : m_background_png_s; if (frames.empty()) return nullptr; - // 循环切换帧,实现动画效果 - size_t frame_idx = m_animation_frame % frames.size(); - Gdiplus::Image* current = frames[frame_idx]; - m_animation_frame++; - return current; + if (frames.size() == 1) + return frames.front(); + + // 基于时间戳选择帧,帧率 10fps,独立于数据刷新率 + ULONGLONG now = GetTickCount64(); + if (m_animation_start_time == 0) + m_animation_start_time = now; + ULONGLONG elapsed = now - m_animation_start_time; + size_t frame_idx = static_cast(elapsed / 66) % frames.size(); + return frames[frame_idx]; } void CSkinFile::SetSettingData(const SkinSettingData& setting_data) diff --git a/TrafficMonitor/SkinFile.h b/TrafficMonitor/SkinFile.h index 0d3da6419..da0feb399 100644 --- a/TrafficMonitor/SkinFile.h +++ b/TrafficMonitor/SkinFile.h @@ -156,7 +156,7 @@ class CSkinFile bool m_is_png{}; std::vector m_background_png_s; // 小布局背景帧(至少1帧) std::vector m_background_png_l; // 大布局背景帧(至少1帧) - int m_animation_frame{}; // 当前动画帧索引,每次DrawInfo递增 + ULONGLONG m_animation_start_time{}; // 动画起始时间戳 int m_alpha{ 255 }; //不透明度,仅当背景为png时有效 SkinSettingData m_setting_data; bool m_is_error{ false }; diff --git a/TrafficMonitor/TrafficMonitorDlg.cpp b/TrafficMonitor/TrafficMonitorDlg.cpp index c85f14ed2..4805cfbb3 100644 --- a/TrafficMonitor/TrafficMonitorDlg.cpp +++ b/TrafficMonitor/TrafficMonitorDlg.cpp @@ -1121,6 +1121,7 @@ BOOL CTrafficMonitorDlg::OnInitDialog() SetTimer(MAIN_TIMER, 1000, NULL); SetTimer(MONITOR_TIMER, theApp.m_general_data.monitor_time_span, NULL); + SetTimer(ANIMATION_TIMER, 66, NULL); // 动画刷新定时器 15fps AfxBeginThread(MonitorThreadCallback, (LPVOID)this); //初始化窗口位置 @@ -1985,6 +1986,11 @@ void CTrafficMonitorDlg::OnTimer(UINT_PTR nIDEvent) KillTimer(DELETE_NOTIFY_ICON_TIMER); } + if (nIDEvent == ANIMATION_TIMER) + { + Invalidate(FALSE); // 驱动动画帧刷新 + } + CDialog::OnTimer(nIDEvent); } diff --git a/TrafficMonitor/stdafx.h b/TrafficMonitor/stdafx.h index c67c801de..111f01050 100644 --- a/TrafficMonitor/stdafx.h +++ b/TrafficMonitor/stdafx.h @@ -89,6 +89,7 @@ using std::ofstream; #define RESTART_TASKBAR_TIMER 1240 #define INIT_CONNECT_TIMER 1241 #define DPI_CHANGE_TIMER 1242 +#define ANIMATION_TIMER 1243 //背景动画定时器(驱动多帧皮肤刷新) #define MAX_INSERT_TO_TASKBAR_CNT 200 //尝试嵌入任务栏的最大次数 #define WARN_INSERT_TO_TASKBAR_CNT 20 //尝试嵌入任务栏的警告次数 From 0df3c68977db21c3dbb4392d0df3a7ad9084d5bb Mon Sep 17 00:00:00 2001 From: Bebop Date: Mon, 8 Jun 2026 15:48:34 +0800 Subject: [PATCH 4/5] =?UTF-8?q?@=20=E8=B0=83=E6=95=B4=E5=8A=A8=E7=94=BB?= =?UTF-8?q?=E5=AE=9A=E6=97=B6=E5=99=A8=E9=97=B4=E9=9A=94=E5=88=B0=2033ms?= =?UTF-8?q?=20(30fps)=EF=BC=8C=E6=8F=90=E5=8D=87=E7=9A=AE=E8=82=A4?= =?UTF-8?q?=E5=8A=A8=E7=94=BB=E6=B5=81=E7=95=85=E5=BA=A6=20@?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TrafficMonitor/SkinFile.cpp | 2 +- TrafficMonitor/TrafficMonitorDlg.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TrafficMonitor/SkinFile.cpp b/TrafficMonitor/SkinFile.cpp index 2d4df9721..ae98d628b 100644 --- a/TrafficMonitor/SkinFile.cpp +++ b/TrafficMonitor/SkinFile.cpp @@ -454,7 +454,7 @@ Gdiplus::Image* CSkinFile::GetCurrentBackgroundFrame(bool show_more_info) if (m_animation_start_time == 0) m_animation_start_time = now; ULONGLONG elapsed = now - m_animation_start_time; - size_t frame_idx = static_cast(elapsed / 66) % frames.size(); + size_t frame_idx = static_cast(elapsed / 33) % frames.size(); return frames[frame_idx]; } diff --git a/TrafficMonitor/TrafficMonitorDlg.cpp b/TrafficMonitor/TrafficMonitorDlg.cpp index 4805cfbb3..67264af9b 100644 --- a/TrafficMonitor/TrafficMonitorDlg.cpp +++ b/TrafficMonitor/TrafficMonitorDlg.cpp @@ -1121,7 +1121,7 @@ BOOL CTrafficMonitorDlg::OnInitDialog() SetTimer(MAIN_TIMER, 1000, NULL); SetTimer(MONITOR_TIMER, theApp.m_general_data.monitor_time_span, NULL); - SetTimer(ANIMATION_TIMER, 66, NULL); // 动画刷新定时器 15fps + SetTimer(ANIMATION_TIMER, 33, NULL); // 动画刷新定时器 30fps AfxBeginThread(MonitorThreadCallback, (LPVOID)this); //初始化窗口位置 From 9c92b453c28527fe67b38225ca850c9bed3c0db4 Mon Sep 17 00:00:00 2001 From: Bebop Date: Mon, 8 Jun 2026 17:47:01 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E9=BB=98=E8=AE=A4=E7=83=AD=E9=94=AE?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=20Ctrl+Shift+F11?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TrafficMonitor/CommonData.h | 2 +- TrafficMonitor/TrafficMonitor.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TrafficMonitor/CommonData.h b/TrafficMonitor/CommonData.h index a2c3e4b90..704423d11 100644 --- a/TrafficMonitor/CommonData.h +++ b/TrafficMonitor/CommonData.h @@ -402,7 +402,7 @@ struct GeneralSettingData // Global hotkey bool hotkey_enabled{ true }; // 全局热键功能开关 UINT hotkey_modifiers{ MOD_CONTROL | MOD_SHIFT }; // 热键修饰键(MOD_ALT | MOD_CONTROL | MOD_SHIFT | MOD_WIN) - UINT hotkey_vk{ VK_F12 }; // 热键虚拟键码 + UINT hotkey_vk{ VK_F11 }; // 热键虚拟键码 }; //定义监控时间间隔有效的最大值和最小值 diff --git a/TrafficMonitor/TrafficMonitor.cpp b/TrafficMonitor/TrafficMonitor.cpp index f7cecfc88..d8a2f54c4 100644 --- a/TrafficMonitor/TrafficMonitor.cpp +++ b/TrafficMonitor/TrafficMonitor.cpp @@ -86,7 +86,7 @@ void CTrafficMonitorApp::LoadConfig() //Global hotkey m_general_data.hotkey_enabled = ini.GetBool(L"general", L"hotkey_enabled", true); m_general_data.hotkey_modifiers = static_cast(ini.GetInt(L"general", L"hotkey_modifiers", MOD_CONTROL | MOD_SHIFT)); - m_general_data.hotkey_vk = static_cast(ini.GetInt(L"general", L"hotkey_vk", VK_F12)); + m_general_data.hotkey_vk = static_cast(ini.GetInt(L"general", L"hotkey_vk", VK_F11)); //Windows10颜色模式设置 bool is_windows10_light_theme = CWindowsSettingHelper::IsWindows10LightTheme();