diff --git a/KeyGo/AppConfig.cs b/KeyGo/AppConfig.cs new file mode 100644 index 0000000..47b6273 --- /dev/null +++ b/KeyGo/AppConfig.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Serialization; + +namespace KeyGo +{ + /// + /// 应用配置帮助类 + /// + public class AppConfig + { + /// + /// Gets or sets a value indicating whether [power boot]. + /// + /// + /// true if [power boot]; otherwise, false. + /// + public bool PowerBoot { get; set; } = true; + + /// + /// Gets or sets a value indicating whether [close to hide]. + /// + /// + /// true if [close to hide]; otherwise, false. + /// + public bool CloseToHide { get; set; } = true; + + + /// + /// Loads the XML. + /// + /// The file path. + /// + public static AppConfig LoadXml(string filePath) + { + AppConfig data = null; + if (File.Exists(filePath)) + { + XmlSerializer formatter = new XmlSerializer(typeof(AppConfig)); + using (var stream = File.OpenRead(filePath)) + { + if (stream.Length > 0) + { + data = formatter.Deserialize(stream) as AppConfig; + } + } + } + return data; + } + + /// + /// Saves the XML. + /// + /// The file path. + public void SaveXml(string filePath) + { + if (!File.Exists(filePath)) + Directory.CreateDirectory(Path.GetDirectoryName(filePath)); + + XmlSerializer formatter = new XmlSerializer(typeof(AppConfig)); + using (var stream = File.Create(filePath)) + { + formatter.Serialize(stream, this); + } + } + } +} diff --git a/KeyGo/AppControl.cs b/KeyGo/AppControl.cs index 90fa6bd..808758f 100644 --- a/KeyGo/AppControl.cs +++ b/KeyGo/AppControl.cs @@ -41,7 +41,7 @@ namespace KeyGo if (instance != null && instance.MainWindowHandle != IntPtr.Zero) { if (IsIconic(instance.MainWindowHandle)) - ShowWindowAsync(instance.MainWindowHandle, (int)CmdShow.Restore); + ShowWindow(instance.MainWindowHandle, (int)CmdShow.Restore); SetForegroundWindow(instance.MainWindowHandle); } } @@ -53,9 +53,16 @@ namespace KeyGo public static void MinimizeWindow(Process instance) { if (instance != null && instance.MainWindowHandle != IntPtr.Zero) - ShowWindowAsync(instance.MainWindowHandle, (int)CmdShow.Minimize); + ShowWindow(instance.MainWindowHandle, (int)CmdShow.Minimize); } + /// + /// Determines whether [is foreground window] [the specified instance]. + /// + /// The instance. + /// + /// true if [is foreground window] [the specified instance]; otherwise, false. + /// public static bool IsForegroundWindow(Process instance) { return GetForegroundWindow() == instance.MainWindowHandle; @@ -143,10 +150,12 @@ namespace KeyGo } [DllImport("User32.dll")] - private static extern bool ShowWindowAsync(System.IntPtr hWnd, int cmdShow); + private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow); + [DllImport("User32.dll")] + private static extern bool ShowWindow(IntPtr hWnd, int cmdShow); [DllImport("User32.dll")] - private static extern bool SetForegroundWindow(System.IntPtr hWnd); + private static extern bool SetForegroundWindow(IntPtr hWnd); [DllImport("User32.dll")] private static extern IntPtr GetForegroundWindow(); diff --git a/KeyGo/FormMain.Designer.cs b/KeyGo/FormMain.Designer.cs index 5a7b62f..7bf56a0 100644 --- a/KeyGo/FormMain.Designer.cs +++ b/KeyGo/FormMain.Designer.cs @@ -35,9 +35,9 @@ namespace KeyGo this.BtnAdd = new System.Windows.Forms.Button(); this.NotifyIcon = new System.Windows.Forms.NotifyIcon(this.components); this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components); + this.TSMICloseToHide = new System.Windows.Forms.ToolStripMenuItem(); + this.TSMIPowerBoot = new System.Windows.Forms.ToolStripMenuItem(); this.TSMIExit = new System.Windows.Forms.ToolStripMenuItem(); - this.TSMIPowerOnStartup = new System.Windows.Forms.ToolStripMenuItem(); - this.TSMICloseToMin = new System.Windows.Forms.ToolStripMenuItem(); this.FLPHotKeys.SuspendLayout(); this.contextMenuStrip1.SuspendLayout(); this.SuspendLayout(); @@ -75,35 +75,35 @@ namespace KeyGo // contextMenuStrip1 // this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.TSMICloseToMin, - this.TSMIPowerOnStartup, + this.TSMICloseToHide, + this.TSMIPowerBoot, this.TSMIExit}); this.contextMenuStrip1.Name = "contextMenuStrip1"; this.contextMenuStrip1.Size = new System.Drawing.Size(149, 70); // + // TSMICloseToHide + // + this.TSMICloseToHide.CheckOnClick = true; + this.TSMICloseToHide.Name = "TSMICloseToHide"; + this.TSMICloseToHide.Size = new System.Drawing.Size(148, 22); + this.TSMICloseToHide.Text = "关闭为最小化"; + this.TSMICloseToHide.CheckedChanged += new System.EventHandler(this.TSMICloseToHide_CheckedChanged); + // + // TSMIPowerBoot + // + this.TSMIPowerBoot.CheckOnClick = true; + this.TSMIPowerBoot.Name = "TSMIPowerBoot"; + this.TSMIPowerBoot.Size = new System.Drawing.Size(148, 22); + this.TSMIPowerBoot.Text = "开机自启动"; + this.TSMIPowerBoot.CheckedChanged += new System.EventHandler(this.TSMIPowerBoot_CheckedChanged); + // // TSMIExit // this.TSMIExit.Name = "TSMIExit"; - this.TSMIExit.Size = new System.Drawing.Size(180, 22); + this.TSMIExit.Size = new System.Drawing.Size(148, 22); this.TSMIExit.Text = "退出"; this.TSMIExit.Click += new System.EventHandler(this.TSMIExit_Click); // - // TSMIPowerOnStartup - // - this.TSMIPowerOnStartup.CheckOnClick = true; - this.TSMIPowerOnStartup.Name = "TSMIPowerOnStartup"; - this.TSMIPowerOnStartup.Size = new System.Drawing.Size(180, 22); - this.TSMIPowerOnStartup.Text = "开机自启动"; - this.TSMIPowerOnStartup.CheckedChanged += new System.EventHandler(this.TSMIPowerOnStartup_CheckedChanged); - // - // TSMICloseToMin - // - this.TSMICloseToMin.CheckOnClick = true; - this.TSMICloseToMin.Name = "TSMICloseToMin"; - this.TSMICloseToMin.Size = new System.Drawing.Size(180, 22); - this.TSMICloseToMin.Text = "关闭为最小化"; - this.TSMICloseToMin.CheckedChanged += new System.EventHandler(this.TSMICloseToMin_CheckedChanged); - // // FormMain // this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F); @@ -135,8 +135,8 @@ namespace KeyGo private System.Windows.Forms.NotifyIcon NotifyIcon; private System.Windows.Forms.ContextMenuStrip contextMenuStrip1; private System.Windows.Forms.ToolStripMenuItem TSMIExit; - private System.Windows.Forms.ToolStripMenuItem TSMIPowerOnStartup; - private System.Windows.Forms.ToolStripMenuItem TSMICloseToMin; + private System.Windows.Forms.ToolStripMenuItem TSMIPowerBoot; + private System.Windows.Forms.ToolStripMenuItem TSMICloseToHide; } } diff --git a/KeyGo/FormMain.cs b/KeyGo/FormMain.cs index 9bdac8a..6bd7218 100644 --- a/KeyGo/FormMain.cs +++ b/KeyGo/FormMain.cs @@ -8,35 +8,73 @@ namespace KeyGo { public partial class FormMain : Form { - private static readonly string _DataFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "KeyGo", "HotKey.xml"); + #region 成员 + + private static readonly string _DataFolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "KeyGo"); + private static readonly string _DataFilePath = Path.Combine(_DataFolderPath, "HotKey.xml"); + private static readonly string _AppConfigFilePath = Path.Combine(_DataFolderPath, "AppConfig.xml"); private readonly KeyGo _KeyGo; + private readonly AppConfig _AppConfig; + private readonly string _CurrentProcessName; + private bool _Initializing; + + #endregion 成员 + + #region 构造 public FormMain() { InitializeComponent(); + _Initializing = true; + // 读取程序集版本,显示到标题栏 Assembly assembly = Assembly.GetExecutingAssembly(); AssemblyName thisAssemName = assembly.GetName(); - Text += $" - {thisAssemName.Version}"; + Text += $" - {thisAssemName.Version} - github.com/jie65535/KeyGo"; + // 载入并初始化配置 + _AppConfig = LoadAppConfig(_AppConfigFilePath); + TSMICloseToHide.Checked = _AppConfig.CloseToHide; + TSMIPowerBoot.Checked = _AppConfig.PowerBoot; + SetPowerBoot(_AppConfig.PowerBoot); + + // 载入热键数据 _KeyGo = LoadHotKeyItems(_DataFilePath); + _KeyGo.HotKeyTriggerEvent += KeyGo_HotKeyTriggerEvent; _KeyGo.FormHandle = Handle; var p = Process.GetCurrentProcess(); + _CurrentProcessName = p.ProcessName; if (_KeyGo.Items.Count == 0) { _KeyGo.Items.Add(new HotKeyItem { - ProcessName = p.ProcessName, + ProcessName = _CurrentProcessName, StartupPath = p.MainModule.FileName, HotKey = "Ctrl+G", }); } _KeyGo.RegAllKey(); + // 初始化UI FLPHotKeys.SuspendLayout(); foreach (var item in _KeyGo.Items) FLP_AddItem(item); FLPHotKeys.ResumeLayout(); + + _Initializing = false; + } + + #endregion 构造 + + #region 窗体事件 + + private void KeyGo_HotKeyTriggerEvent(object sender, HotKeyTriggerEventArgs e) + { + if (e.HotKeyItem.ProcessName == _CurrentProcessName) + { + ChangeVisible(); + e.Handle = true; + } } private void FormMain_Load(object sender, EventArgs e) @@ -44,22 +82,32 @@ namespace KeyGo Console.WriteLine(_DataFilePath); } - bool isExit; + private bool isExit; + private void FormMain_FormClosing(object sender, FormClosingEventArgs e) { - if (!isExit) + if (!isExit && _AppConfig.CloseToHide) { Hide(); e.Cancel = true; } } + private void FormMain_Deactivate(object sender, EventArgs e) + { + // 如果最小化,则隐藏窗体 + if (WindowState == FormWindowState.Minimized) + Hide(); + } + private void FormMain_FormClosed(object sender, FormClosedEventArgs e) { _KeyGo.UnRegAllKey(); SaveHotKeyItems(_KeyGo); } + #endregion 窗体事件 + #region 数据文件IO private KeyGo LoadHotKeyItems(string xmlFilePath) @@ -90,6 +138,36 @@ namespace KeyGo #endregion 数据文件IO + #region 配置文件IO + + private AppConfig LoadAppConfig(string xmlFilePath) + { + AppConfig instance = null; + try + { + instance = AppConfig.LoadXml(xmlFilePath); + } + catch (Exception ex) + { + MessageBox.Show("载入配置文件异常:" + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + return instance ?? new AppConfig(); + } + + private void SaveAppConfig(AppConfig config) + { + try + { + config.SaveXml(_AppConfigFilePath); + } + catch (Exception ex) + { + MessageBox.Show("保存配置文件异常:" + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + #endregion 配置文件IO + #region 窗体消息 热键回调 private const int WM_HOTKEY = 0x312; @@ -107,21 +185,7 @@ namespace KeyGo #endregion 窗体消息 热键回调 - private void BtnTest_Click(object sender, EventArgs e) - { - //_KeyGo.Items.Add(new HotKeyItem - //{ - // ProcessName = "QQ", - // StartupPath = "", - // HotKey = "Ctrl+Q", - // Enabled = true, - // TriggerCounter = 0, - // CreationTime = DateTime.Now, - // LastModifiedTime = DateTime.Now, - //}); - //SaveHotKeyItems(_KeyGo); - //new FormHotKey().ShowDialog(); - } + #region 热键管理 private void BtnAdd_Click(object sender, EventArgs e) { @@ -162,12 +226,21 @@ namespace KeyGo FLPHotKeys.Controls.SetChildIndex(BtnAdd, i1); } + #endregion 热键管理 + + #region 托盘图标管理 + private void NotifyIcon_MouseClick(object sender, MouseEventArgs e) { // 鼠标左键点击托盘图标才触发 if (e.Button != MouseButtons.Left) return; + ChangeVisible(); + } + + private void ChangeVisible() + { // 当前在前台则隐藏 if (Visible) { @@ -192,27 +265,38 @@ namespace KeyGo } } - private void FormMain_Deactivate(object sender, EventArgs e) - { - // 如果最小化,则隐藏窗体 - if (WindowState == FormWindowState.Minimized) - Hide(); - } - private void TSMIExit_Click(object sender, EventArgs e) { isExit = true; Close(); } - private void TSMIPowerOnStartup_CheckedChanged(object sender, EventArgs e) + private void TSMIPowerBoot_CheckedChanged(object sender, EventArgs e) { - MessageBox.Show("暂未完成", "TODO"); + if (_Initializing) + return; + _AppConfig.PowerBoot = TSMIPowerBoot.Checked; + SetPowerBoot(_AppConfig.PowerBoot); + SaveAppConfig(_AppConfig); } - private void TSMICloseToMin_CheckedChanged(object sender, EventArgs e) + private void TSMICloseToHide_CheckedChanged(object sender, EventArgs e) { - MessageBox.Show("暂未完成", "TODO"); + if (_Initializing) + return; + _AppConfig.CloseToHide = TSMICloseToHide.Checked; + SaveAppConfig(_AppConfig); + } + + #endregion 托盘图标管理 + + /// + /// 设置开机自启动 + /// + /// if set to true [enable]. + private void SetPowerBoot(bool enable) + { + // TODO } } } \ No newline at end of file diff --git a/KeyGo/KeyGo.cs b/KeyGo/KeyGo.cs index 33b842f..20ec5c7 100644 --- a/KeyGo/KeyGo.cs +++ b/KeyGo/KeyGo.cs @@ -9,15 +9,21 @@ using System.Xml.Serialization; namespace KeyGo { + /// + /// KeyGo 核心功能类 + /// public class KeyGo { - static int _RegMaxID; + #region Member + + private static int _RegMaxID; [XmlIgnore] public IntPtr FormHandle { get; set; } public List Items { get; set; } = new List(); + #endregion Member #region FILE IO @@ -59,7 +65,7 @@ namespace KeyGo } } - #endregion + #endregion FILE IO #region HotKey Register @@ -84,7 +90,6 @@ namespace KeyGo } } - /// /// Uns the reg all key. /// @@ -140,15 +145,19 @@ namespace KeyGo case "ctrl": keyModifiers |= AppHotKey.KeyModifiers.Ctrl; break; + case "shift": keyModifiers |= AppHotKey.KeyModifiers.Shift; break; + case "alt": keyModifiers |= AppHotKey.KeyModifiers.Alt; break; + case "win": keyModifiers |= AppHotKey.KeyModifiers.WindowsKey; break; + default: keyCode = (Keys)Enum.Parse(typeof(Keys), key); break; @@ -180,7 +189,21 @@ namespace KeyGo item.HotKeyID = 0; } - #endregion + #endregion HotKey Register + + #region HotKey Trigger + + /// + /// 热键触发时调用 + /// + public event EventHandler HotKeyTriggerEvent; + + private bool OnHotKeyTrigger(HotKeyItem item) + { + var args = new HotKeyTriggerEventArgs{ HotKeyItem = item }; + HotKeyTriggerEvent?.Invoke(this, args); + return args.Handle; + } /// /// Processes the hotkey. @@ -191,32 +214,41 @@ namespace KeyGo var hotkey = Items.Find(k => k.HotKeyID == hotKey_id); if (hotkey != null) { + //Console.WriteLine($"ID:{hotkey.HotKeyID} Keys:{hotkey.HotKey} ProcessName:{hotkey.ProcessName}\nStartupPath:{hotkey.StartupPath}"); ++hotkey.TriggerCounter; - + // 触发事件,若被外部处理,则内部不再执行 + if (OnHotKeyTrigger(hotkey)) + return; // 热键相应逻辑: // 若应用未启动:启动应用 // 若应用未在最前:激活窗体,推到最前 // 若应用已在最前:最小化窗体 - var process = Process.GetProcessesByName(hotkey.ProcessName).Where(p => p.MainWindowHandle != IntPtr.Zero).ToArray().FirstOrDefault(); - if (process != null ) - { - if (AppControl.IsForegroundWindow(process)) - AppControl.MinimizeWindow(process); - else - AppControl.ShowWindow(process); - } - else + var processes = Process.GetProcessesByName(hotkey.ProcessName); + if (processes == null || processes.Length < 1) { if (!string.IsNullOrWhiteSpace(hotkey.StartupPath) && File.Exists(hotkey.StartupPath)) Process.Start(hotkey.StartupPath); } - - Console.WriteLine($"ID:{hotkey.HotKeyID} Keys:{hotkey.HotKey} ProcessName:{hotkey.ProcessName}\nStartupPath:{hotkey.StartupPath}"); + else + { + var process = processes.Where(p => p.MainWindowHandle != IntPtr.Zero).ToArray().FirstOrDefault(); + if (process != null) + { + if (AppControl.IsForegroundWindow(process)) + AppControl.MinimizeWindow(process); + else + AppControl.ShowWindow(process); + } + } } } + #endregion HotKey Trigger + + #region HotKey Manager + /// /// 添加一个新热键 /// @@ -261,5 +293,23 @@ namespace KeyGo if (item.Enabled) RegKey(item); } + + #endregion HotKey Manager + } + + /// + /// 热键触发事件参数 + /// + public class HotKeyTriggerEventArgs + { + public HotKeyItem HotKeyItem { get; set; } + + /// + /// 获取或设置该事件是否已经被处理 + /// + /// + /// true if handle; otherwise, false. + /// + public bool Handle { get; set; } } } \ No newline at end of file diff --git a/KeyGo/KeyGo.csproj b/KeyGo/KeyGo.csproj index 8369c6a..23997af 100644 --- a/KeyGo/KeyGo.csproj +++ b/KeyGo/KeyGo.csproj @@ -69,6 +69,7 @@ + diff --git a/KeyGo/Program.cs b/KeyGo/Program.cs index b742d01..d9687fc 100644 --- a/KeyGo/Program.cs +++ b/KeyGo/Program.cs @@ -14,7 +14,10 @@ namespace KeyGo var p = AppControl.GetCurrentRunningInstance(); if (p != null) { - AppControl.ShowWindow(p); + if (p.MainWindowHandle == IntPtr.Zero) + MessageBox.Show("应用已启动,无需重复运行", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); + else + AppControl.ShowWindow(p); } else { diff --git a/KeyGo/Properties/AssemblyInfo.cs b/KeyGo/Properties/AssemblyInfo.cs index c9a1177..957e02b 100644 --- a/KeyGo/Properties/AssemblyInfo.cs +++ b/KeyGo/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ using System.Runtime.InteropServices; //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 //通过使用 "*",如下所示: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.1.0.3")] +[assembly: AssemblyVersion("0.1.1")] [assembly: AssemblyFileVersion("1.0.0.0")]