diff --git a/PersonalToolBox/App.xaml.cs b/PersonalToolBox/App.xaml.cs
index 8041e55..f540666 100644
--- a/PersonalToolBox/App.xaml.cs
+++ b/PersonalToolBox/App.xaml.cs
@@ -1,4 +1,6 @@
using System.IO;
+using System.Runtime.InteropServices;
+using System.Threading;
using System.Windows;
using Microsoft.Extensions.DependencyInjection;
using PersonalToolBox.Services;
@@ -17,6 +19,17 @@ public partial class App : System.Windows.Application
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"PersonalToolBox", "crash.log");
+ private const string AppMutexName = "PersonalToolBox_SingleInstance_8E2F4A1C";
+ private static Mutex? _mutex;
+
+ [DllImport("user32.dll")]
+ private static extern uint RegisterWindowMessage(string lpString);
+
+ [DllImport("user32.dll")]
+ private static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
+
+ private static readonly IntPtr HWND_BROADCAST = new IntPtr(0xFFFF);
+
public App()
{
// 监听未处理的异常,写入文件日志
@@ -36,6 +49,15 @@ public partial class App : System.Windows.Application
protected override async void OnStartup(StartupEventArgs e)
{
+ _mutex = new Mutex(true, AppMutexName, out bool createdNew);
+ if (!createdNew)
+ {
+ uint msg = RegisterWindowMessage("PersonalToolBox_ShowMain");
+ PostMessage(HWND_BROADCAST, msg, IntPtr.Zero, IntPtr.Zero);
+ Shutdown();
+ return;
+ }
+
try
{
base.OnStartup(e);
diff --git a/PersonalToolBox/Helpers/HotKeyManager.cs b/PersonalToolBox/Helpers/HotKeyManager.cs
index 02da5b2..61651b5 100644
--- a/PersonalToolBox/Helpers/HotKeyManager.cs
+++ b/PersonalToolBox/Helpers/HotKeyManager.cs
@@ -16,6 +16,8 @@ public class HotKeyManager
private readonly ILogService _logService;
private readonly IProcessExecutionService _processService;
+ public bool IsEnabled { get; set; } = true;
+
// ──────────────── Win32 API ────────────────
private const int WM_HOTKEY = 0x0312;
@@ -103,6 +105,7 @@ public class HotKeyManager
public bool TryHandleMessage(int msg, IntPtr wParam, IntPtr lParam)
{
if (msg != WM_HOTKEY) return false;
+ if (!IsEnabled) return true;
int id = wParam.ToInt32();
if (_hotkeyMap.TryGetValue(id, out var tool))
diff --git a/PersonalToolBox/PersonalToolBox.csproj b/PersonalToolBox/PersonalToolBox.csproj
index 5c2981e..f15fd21 100644
--- a/PersonalToolBox/PersonalToolBox.csproj
+++ b/PersonalToolBox/PersonalToolBox.csproj
@@ -7,6 +7,7 @@
disable
true
true
+ Resources\app.ico
@@ -16,4 +17,10 @@
+
+
+ PreserveNewest
+
+
+
diff --git a/PersonalToolBox/Resources/app.ico b/PersonalToolBox/Resources/app.ico
new file mode 100644
index 0000000..f3aeb5f
Binary files /dev/null and b/PersonalToolBox/Resources/app.ico differ
diff --git a/PersonalToolBox/ViewModels/MainViewModel.cs b/PersonalToolBox/ViewModels/MainViewModel.cs
index e669172..cffc297 100644
--- a/PersonalToolBox/ViewModels/MainViewModel.cs
+++ b/PersonalToolBox/ViewModels/MainViewModel.cs
@@ -66,6 +66,9 @@ public partial class MainViewModel : ObservableObject
[ObservableProperty]
private bool _isAutoStart;
+ [ObservableProperty]
+ private bool _isHotKeyEnabled = true;
+
// ───────────────────────────── 命令 ─────────────────────────────
[RelayCommand]
@@ -94,6 +97,15 @@ public partial class MainViewModel : ObservableObject
_logService.Info(IsAutoStart ? "已启用开机自启动" : "已关闭开机自启动");
}
+ [RelayCommand]
+ private void ToggleHotKey()
+ {
+ IsHotKeyEnabled = !IsHotKeyEnabled;
+ _hotKeyManager.IsEnabled = IsHotKeyEnabled;
+ var status = IsHotKeyEnabled ? "已恢复" : "已暂停";
+ _logService.Info($"{status}快捷键功能");
+ }
+
///
/// 供 MainWindow 外部调用,用于输出提示信息
///
diff --git a/PersonalToolBox/Views/MainWindow.xaml b/PersonalToolBox/Views/MainWindow.xaml
index 21c02e5..5499187 100644
--- a/PersonalToolBox/Views/MainWindow.xaml
+++ b/PersonalToolBox/Views/MainWindow.xaml
@@ -178,6 +178,7 @@
+
+
+
diff --git a/PersonalToolBox/Views/MainWindow.xaml.cs b/PersonalToolBox/Views/MainWindow.xaml.cs
index 951be3f..ac640d2 100644
--- a/PersonalToolBox/Views/MainWindow.xaml.cs
+++ b/PersonalToolBox/Views/MainWindow.xaml.cs
@@ -3,10 +3,13 @@ using System.Collections.Specialized;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
+using System.IO;
+using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Data;
using System.Windows.Forms;
using System.Windows.Interop;
+using System.Windows.Media.Imaging;
using FontAwesome.Sharp;
using PersonalToolBox.Helpers;
using PersonalToolBox.ViewModels;
@@ -18,8 +21,14 @@ public partial class MainWindow : Window
private readonly MainViewModel _viewModel;
private readonly HotKeyManager _hotKeyManager;
private NotifyIcon? _notifyIcon;
+ private ToolStripMenuItem? _hotKeyToggleMenuItem;
private bool _canActuallyClose;
+ [DllImport("user32.dll")]
+ private static extern uint RegisterWindowMessage(string lpString);
+
+ private readonly int _showMainMsg;
+
public MainWindow(MainViewModel viewModel, HotKeyManager hotKeyManager)
{
InitializeComponent();
@@ -28,7 +37,18 @@ public partial class MainWindow : Window
_hotKeyManager = hotKeyManager;
DataContext = viewModel;
+ _showMainMsg = (int)RegisterWindowMessage("PersonalToolBox_ShowMain");
+
+ var iconPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "app.ico");
+ if (File.Exists(iconPath))
+ {
+ using var stream = File.OpenRead(iconPath);
+ var decoder = BitmapDecoder.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
+ this.Icon = decoder.Frames[0];
+ }
+
viewModel.Logs.CollectionChanged += OnLogsCollectionChanged;
+ viewModel.PropertyChanged += OnViewModelPropertyChanged;
CreateTrayIcon();
}
@@ -53,9 +73,14 @@ public partial class MainWindow : Window
///
private void CreateTrayIcon()
{
+ var iconPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "app.ico");
+ var trayIcon = File.Exists(iconPath)
+ ? new System.Drawing.Icon(iconPath)
+ : System.Drawing.SystemIcons.Application;
+
_notifyIcon = new NotifyIcon
{
- Icon = CreateAppIcon(),
+ Icon = trayIcon,
Visible = true,
Text = "个人工具箱"
};
@@ -68,26 +93,17 @@ public partial class MainWindow : Window
var menu = new ContextMenuStrip();
menu.Items.Add("显示主界面", null, (_, _) => ShowMainWindow());
- menu.Items.Add("设置", null, (_, _) => OpenSettings());
+
+ _hotKeyToggleMenuItem = new ToolStripMenuItem(
+ _viewModel.IsHotKeyEnabled ? "暂停快捷键功能" : "恢复快捷键功能");
+ _hotKeyToggleMenuItem.Click += (_, _) => _viewModel.ToggleHotKeyCommand.Execute(null);
+ menu.Items.Add(_hotKeyToggleMenuItem);
+
menu.Items.Add(new ToolStripSeparator());
menu.Items.Add("彻底退出", null, (_, _) => ExitApplication());
_notifyIcon.ContextMenuStrip = menu;
}
- ///
- /// 程序化生成 32x32 托盘图标
- ///
- private static System.Drawing.Icon CreateAppIcon()
- {
- var bmp = new System.Drawing.Bitmap(32, 32);
- using var g = System.Drawing.Graphics.FromImage(bmp);
- g.Clear(System.Drawing.Color.FromArgb(30, 30, 46));
- using var font = new System.Drawing.Font(System.Drawing.FontFamily.GenericSansSerif, 16, System.Drawing.FontStyle.Bold);
- using var brush = new System.Drawing.SolidBrush(System.Drawing.Color.FromArgb(137, 180, 250));
- g.DrawString("T", font, brush, new System.Drawing.PointF(6, 3));
- return System.Drawing.Icon.FromHandle(bmp.GetHicon());
- }
-
///
/// 左键托盘图标:切换窗口显示/隐藏
///
@@ -109,15 +125,6 @@ public partial class MainWindow : Window
Activate();
}
- ///
- /// 打开设置(当前无额外设置界面,提示使用内置功能)
- ///
- private void OpenSettings()
- {
- ShowMainWindow();
- _viewModel.LogServiceMessage("设置功能可通过左侧栏管理分类和主题切换");
- }
-
///
/// 彻底退出程序
///
@@ -146,10 +153,26 @@ public partial class MainWindow : Window
private IntPtr WndProcHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
+ if (msg == _showMainMsg)
+ {
+ Dispatcher.Invoke(() => ShowMainWindow());
+ handled = true;
+ return IntPtr.Zero;
+ }
+
handled = _hotKeyManager.TryHandleMessage(msg, wParam, lParam);
return IntPtr.Zero;
}
+ private void OnViewModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(MainViewModel.IsHotKeyEnabled) && _hotKeyToggleMenuItem != null)
+ {
+ bool enabled = _viewModel.IsHotKeyEnabled;
+ _hotKeyToggleMenuItem.Text = enabled ? "暂停快捷键功能" : "恢复快捷键功能";
+ }
+ }
+
private void OnLogsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add && LogListBox.Items.Count > 0)
diff --git a/app.ico b/app.ico
new file mode 100644
index 0000000..0461a17
Binary files /dev/null and b/app.ico differ