diff --git a/Ink Canvas/App.xaml.cs b/Ink Canvas/App.xaml.cs index fc01e00d..fe2eafba 100644 --- a/Ink Canvas/App.xaml.cs +++ b/Ink Canvas/App.xaml.cs @@ -929,6 +929,22 @@ namespace Ink_Canvas LogHelper.WriteLogToFile("通过IPC发送展开浮动栏命令失败", LogHelper.LogType.Warning); } } + // 检查是否有URI参数 + else if (e.Args.Any(a => a.StartsWith("icc:", StringComparison.OrdinalIgnoreCase))) + { + string uriArg = e.Args.FirstOrDefault(a => a.StartsWith("icc:", StringComparison.OrdinalIgnoreCase)); + LogHelper.WriteLogToFile($"检测到已运行实例且有URI参数: {uriArg}", LogHelper.LogType.Event); + + // 尝试通过IPC发送URI命令给已运行实例 + if (FileAssociationManager.TrySendUriCommandToExistingInstance(uriArg)) + { + LogHelper.WriteLogToFile("URI命令已通过IPC发送给已运行实例", LogHelper.LogType.Event); + } + else + { + LogHelper.WriteLogToFile("通过IPC发送URI命令失败", LogHelper.LogType.Warning); + } + } else { LogHelper.WriteLogToFile("检测到已运行实例,但无文件参数", LogHelper.LogType.Event); @@ -1021,6 +1037,21 @@ namespace Ink_Canvas mainWindow.Show(); + // 处理启动时的URI参数 + string startupUriArg = e.Args.FirstOrDefault(a => a.StartsWith("icc:", StringComparison.OrdinalIgnoreCase)); + if (!string.IsNullOrEmpty(startupUriArg)) + { + LogHelper.WriteLogToFile($"App | 处理启动URI参数: {startupUriArg}", LogHelper.LogType.Event); + // 延迟一点执行,确保窗口初始化完成 + Task.Delay(1000).ContinueWith(_ => + { + mainWindow.Dispatcher.Invoke(() => + { + mainWindow.HandleUriCommand(startupUriArg); + }); + }); + } + // 注册.icstk文件关联 try { diff --git a/Ink Canvas/Helpers/FileAssociationManager.cs b/Ink Canvas/Helpers/FileAssociationManager.cs index 4eb2ccaf..b237f51d 100644 --- a/Ink Canvas/Helpers/FileAssociationManager.cs +++ b/Ink Canvas/Helpers/FileAssociationManager.cs @@ -26,6 +26,7 @@ namespace Ink_Canvas.Helpers private const string IpcFilePrefix = "InkCanvasFileAssociation_"; private const string IpcBoardModePrefix = "InkCanvasBoardMode_"; private const string IpcShowModePrefix = "InkCanvasShowMode_"; + private const string IpcUriCommandPrefix = "InkCanvasUriCommand_"; private const int IpcTimeout = 5000; // 5秒超时 /// @@ -361,6 +362,57 @@ namespace Ink_Canvas.Helpers } } + /// + /// 尝试通过IPC将URI命令发送给已运行的实例 + /// + /// URI命令 + /// 是否成功发送 + public static bool TrySendUriCommandToExistingInstance(string uri) + { + try + { + LogHelper.WriteLogToFile($"尝试通过IPC发送URI命令给已运行实例: {uri}", LogHelper.LogType.Event); + + // 创建IPC文件 + string tempDir = Path.GetTempPath(); + string ipcFileName = IpcUriCommandPrefix + Guid.NewGuid().ToString("N") + ".tmp"; + string ipcFilePath = Path.Combine(tempDir, ipcFileName); + + // 写入URI命令到IPC文件 + File.WriteAllText(ipcFilePath, uri, Encoding.UTF8); + + // 创建事件通知已运行实例 + using (EventWaitHandle ipcEvent = new EventWaitHandle(false, EventResetMode.ManualReset, IpcEventName)) + { + ipcEvent.Set(); + } + + // 等待一段时间让已运行实例处理命令 + Thread.Sleep(1000); + + // 清理IPC文件 + try + { + if (File.Exists(ipcFilePath)) + { + File.Delete(ipcFilePath); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"清理IPC文件失败: {ex.Message}", LogHelper.LogType.Warning); + } + + LogHelper.WriteLogToFile("IPC URI命令发送完成", LogHelper.LogType.Event); + return true; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"通过IPC发送URI命令失败: {ex.Message}", LogHelper.LogType.Error); + return false; + } + } + /// /// 启动IPC监听器,等待其他实例发送文件路径 /// @@ -576,6 +628,56 @@ namespace Ink_Canvas.Helpers catch { } } } + + // 处理URI命令IPC文件 + string[] uriCommandFiles = Directory.GetFiles(tempDir, IpcUriCommandPrefix + "*.tmp"); + foreach (string ipcFile in uriCommandFiles) + { + try + { + // 读取命令内容 + string uri = File.ReadAllText(ipcFile, Encoding.UTF8); + + if (!string.IsNullOrEmpty(uri)) + { + LogHelper.WriteLogToFile($"IPC接收到URI命令: {uri}", LogHelper.LogType.Event); + + // 在UI线程中处理URI命令 + Application.Current.Dispatcher.BeginInvoke(new Action(() => + { + try + { + // 获取主窗口并处理URI命令 + if (Application.Current.MainWindow is MainWindow mainWindow) + { + mainWindow.HandleUriCommand(uri); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"IPC处理URI命令失败: {ex.Message}", LogHelper.LogType.Error); + } + })); + } + + // 删除IPC文件 + File.Delete(ipcFile); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"处理URI命令IPC文件失败: {ex.Message}", LogHelper.LogType.Warning); + + // 尝试删除损坏的IPC文件 + try + { + if (File.Exists(ipcFile)) + { + File.Delete(ipcFile); + } + } + catch { } + } + } } catch (Exception ex) { diff --git a/Ink Canvas/Helpers/UriSchemeHelper.cs b/Ink Canvas/Helpers/UriSchemeHelper.cs new file mode 100644 index 00000000..70a31b25 --- /dev/null +++ b/Ink Canvas/Helpers/UriSchemeHelper.cs @@ -0,0 +1,89 @@ +using Microsoft.Win32; +using System; +using System.Diagnostics; +using System.Security; + +namespace Ink_Canvas.Helpers +{ + public static class UriSchemeHelper + { + private const string SchemeName = "icc"; + private const string FriendlyName = "URL:Ink Canvas Protocol"; + + public static bool RegisterUriScheme() + { + try + { + string exePath = Process.GetCurrentProcess().MainModule.FileName; + + // 使用 CurrentUser\Software\Classes 代替 ClassesRoot,无需管理员权限 + using (RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\Classes\" + SchemeName)) + { + key.SetValue("", FriendlyName); + key.SetValue("URL Protocol", ""); + + using (RegistryKey defaultIconKey = key.CreateSubKey("DefaultIcon")) + { + // 修正引号转义 + defaultIconKey.SetValue("", "\"" + exePath + "\",1"); + } + + using (RegistryKey shellKey = key.CreateSubKey("shell")) + using (RegistryKey openKey = shellKey.CreateSubKey("open")) + using (RegistryKey commandKey = openKey.CreateSubKey("command")) + { + // 修正引号转义 + commandKey.SetValue("", "\"" + exePath + "\" \"%1\""); + } + } + LogHelper.WriteLogToFile($"成功注册URI Scheme: {SchemeName}://", LogHelper.LogType.Event); + return true; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"注册URI Scheme失败: {ex.Message}", LogHelper.LogType.Error); + return false; + } + } + + public static bool UnregisterUriScheme() + { + try + { + // 使用 CurrentUser\Software\Classes + Registry.CurrentUser.DeleteSubKeyTree(@"Software\Classes\" + SchemeName, false); + LogHelper.WriteLogToFile($"成功注销URI Scheme: {SchemeName}://", LogHelper.LogType.Event); + return true; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"注销URI Scheme失败: {ex.Message}", LogHelper.LogType.Error); + return false; + } + } + + public static bool IsUriSchemeRegistered() + { + try + { + // 使用 CurrentUser\Software\Classes + using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Classes\" + SchemeName)) + { + if (key == null) return false; + // 修正反斜杠路径 + using (RegistryKey shellKey = key.OpenSubKey(@"shell\open\command")) + { + if (shellKey == null) return false; + string command = shellKey.GetValue("") as string; + string exePath = Process.GetCurrentProcess().MainModule.FileName; + return !string.IsNullOrEmpty(command) && command.Contains(exePath); + } + } + } + catch + { + return false; + } + } + } +} \ No newline at end of file diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml index 13c617d0..2ad47907 100644 --- a/Ink Canvas/MainWindow.xaml +++ b/Ink Canvas/MainWindow.xaml @@ -2417,6 +2417,9 @@ + e) { if (!isLoaded) return; diff --git a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs index 32408a0f..4af1f250 100644 --- a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs +++ b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs @@ -857,6 +857,7 @@ namespace Ink_Canvas ToggleSwitchIsSecondConfimeWhenShutdownApp.IsOn = Settings.Advanced.IsSecondConfirmWhenShutdownApp; ToggleSwitchWindowMode.IsOn = Settings.Advanced.WindowMode; ToggleSwitchIsSpecialScreen.IsOn = Settings.Advanced.IsSpecialScreen; + ToggleSwitchIsEnableUriScheme.IsOn = Settings.Advanced.IsEnableUriScheme; ToggleSwitchIsQuadIR.IsOn = Settings.Advanced.IsQuadIR; ToggleSwitchEraserBindTouchMultiplier.IsOn = Settings.Advanced.EraserBindTouchMultiplier; ToggleSwitchIsEnableFullScreenHelper.IsOn = Settings.Advanced.IsEnableFullScreenHelper; diff --git a/Ink Canvas/MainWindow_cs/MW_UriHandler.cs b/Ink Canvas/MainWindow_cs/MW_UriHandler.cs new file mode 100644 index 00000000..0b124ed9 --- /dev/null +++ b/Ink Canvas/MainWindow_cs/MW_UriHandler.cs @@ -0,0 +1,77 @@ +using Ink_Canvas.Helpers; +using System; +using System.Windows; + +namespace Ink_Canvas +{ + public partial class MainWindow + { + public void HandleUriCommand(string uri) + { + try + { + if (string.IsNullOrEmpty(uri)) return; + + LogHelper.WriteLogToFile($"正在处理URI命令: {uri}", LogHelper.LogType.Event); + + // 解析URI + // 格式: icc://command?param=value + // 如果URI以icc:开头但不是标准URI格式,尝试手动解析 + string command = ""; + + if (Uri.TryCreate(uri, UriKind.Absolute, out Uri uriObj)) + { + command = uriObj.Host.ToLower(); + } + else if (uri.StartsWith("icc:", StringComparison.OrdinalIgnoreCase)) + { + // 简单的手动解析: icc:fold + string path = uri.Substring(4); + // 移除可能的斜杠 + command = path.Trim('/').ToLower(); + } + + switch (command) + { + case "fold": + if (!isFloatingBarFolded) + { + FoldFloatingBar_MouseUp(new object(), null); + ShowNotification("已进入收纳模式"); + } + break; + + case "unfold": + case "show": // 兼容旧习惯 + if (isFloatingBarFolded) + { + UnFoldFloatingBar_MouseUp(new object(), null); + ShowNotification("已退出收纳模式"); + } + break; + + case "toggle": + if (isFloatingBarFolded) + { + UnFoldFloatingBar_MouseUp(new object(), null); + ShowNotification("已退出收纳模式"); + } + else + { + FoldFloatingBar_MouseUp(new object(), null); + ShowNotification("已进入收纳模式"); + } + break; + + default: + LogHelper.WriteLogToFile($"未知的URI命令: {command}", LogHelper.LogType.Warning); + break; + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"处理URI命令时出错: {ex.Message}", LogHelper.LogType.Error); + } + } + } +} diff --git a/Ink Canvas/Resources/Settings.cs b/Ink Canvas/Resources/Settings.cs index c99d0d19..bb3dac6d 100644 --- a/Ink Canvas/Resources/Settings.cs +++ b/Ink Canvas/Resources/Settings.cs @@ -629,6 +629,9 @@ namespace Ink_Canvas [JsonProperty("enableUIAccessTopMost")] public bool EnableUIAccessTopMost { get; set; } = false; + [JsonProperty("isEnableUriScheme")] + public bool IsEnableUriScheme { get; set; } = false; + [JsonProperty("windowMode")] public bool WindowMode { get; set; } = true; } diff --git a/Ink Canvas/Windows/SettingsViews/SettingsViews/AdvancedPanel.xaml b/Ink Canvas/Windows/SettingsViews/SettingsViews/AdvancedPanel.xaml index f5a51a09..6b9576c3 100644 --- a/Ink Canvas/Windows/SettingsViews/SettingsViews/AdvancedPanel.xaml +++ b/Ink Canvas/Windows/SettingsViews/SettingsViews/AdvancedPanel.xaml @@ -430,12 +430,34 @@ -