From f5a657d5c3125e881d218e25260dc01a617aea8c Mon Sep 17 00:00:00 2001 From: CJKmkp <2564608840@qq.com> Date: Sun, 5 Apr 2026 09:38:16 +0800 Subject: [PATCH] =?UTF-8?q?improve:=E5=B1=95=E5=8F=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增将展台替换为希沃展台快捷启动功能 --- Ink Canvas/Helpers/SoftwareLauncher.cs | 171 +++++++++++++----- Ink Canvas/MainWindow.xaml | 9 + Ink Canvas/MainWindow_cs/MW_Settings.cs | 9 + Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs | 5 +- Ink Canvas/MainWindow_cs/MW_VideoPresenter.cs | 8 + Ink Canvas/Properties/Strings.enUS.xml | 2 + Ink Canvas/Properties/Strings.resx | 2 + Ink Canvas/Resources/Settings.cs | 4 + .../SettingsViews/CanvasAndInkPanel.xaml | 16 +- .../SettingsViews/CanvasAndInkPanel.xaml.cs | 10 + .../SettingsViews/MainWindowSettingsHelper.cs | 1 + 11 files changed, 188 insertions(+), 49 deletions(-) diff --git a/Ink Canvas/Helpers/SoftwareLauncher.cs b/Ink Canvas/Helpers/SoftwareLauncher.cs index e3dc7188..395eadf3 100644 --- a/Ink Canvas/Helpers/SoftwareLauncher.cs +++ b/Ink Canvas/Helpers/SoftwareLauncher.cs @@ -1,72 +1,151 @@ -using Microsoft.Win32; +using Microsoft.Win32; using System; using System.Diagnostics; using System.IO; -using System.Runtime.InteropServices; +using System.Windows; namespace Ink_Canvas.Helpers { - internal class SoftwareLauncher + internal static class SoftwareLauncher { - [DllImport("user32.dll")] - private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); - + /// 与 ICA 一致:在「程序和功能」卸载列表中按 DisplayName 匹配后启动 sweclauncher.exe。 public static void LaunchEasiCamera(string softwareName) { string executablePath = FindEasiCameraExecutablePath(softwareName); - if (!string.IsNullOrEmpty(executablePath)) + if (string.IsNullOrEmpty(executablePath)) { - try - { - Process.Start(executablePath); - //Console.WriteLine(softwareName + " 启动成功!"); - } - catch (Exception ex) - { - Console.WriteLine("启动失败: " + ex.Message); - //MessageBox.Show("启动失败: " + ex.Message); - } + MessageBox.Show( + "未找到希沃视频展台安装信息(已扫描 64 位与 32 位卸载注册表)。请确认已通过官方安装包安装「希沃视频展台」。", + "Ink Canvas", + MessageBoxButton.OK, + MessageBoxImage.Information); + return; + } + + try + { + var directory = Path.GetDirectoryName(executablePath); + var psi = new ProcessStartInfo + { + FileName = executablePath, + UseShellExecute = true, + WorkingDirectory = string.IsNullOrEmpty(directory) ? Environment.SystemDirectory : directory + }; + Process.Start(psi); + } + catch (Exception ex) + { + MessageBox.Show( + "无法启动希沃视频展台:" + ex.Message, + "Ink Canvas", + MessageBoxButton.OK, + MessageBoxImage.Warning); } - //Console.WriteLine(softwareName + " 未找到可执行文件路径。"); } private static string FindEasiCameraExecutablePath(string softwareName) { - string executablePath = null; + if (string.IsNullOrWhiteSpace(softwareName)) + return null; - using (RegistryKey key = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall")) + // 64 位进程默认只枚举 64 位注册表视图;32 位希沃常写在 WOW6432Node 下,需一并扫描。 + string[] uninstallRoots = { - foreach (string subkeyName in key.GetSubKeyNames()) - { - using (RegistryKey subkey = key.OpenSubKey(subkeyName)) - { - string displayName = subkey.GetValue("DisplayName") as string; - string installLocation = subkey.GetValue("InstallLocation") as string; - string uninstallString = subkey.GetValue("UninstallString") as string; + @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", + @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall", + }; - if (!string.IsNullOrEmpty(displayName) && displayName.Contains(softwareName)) - { - if (!string.IsNullOrEmpty(installLocation)) - { - executablePath = Path.Combine(installLocation, "sweclauncher.exe"); - } - else if (!string.IsNullOrEmpty(uninstallString)) - { - int lastSlashIndex = uninstallString.LastIndexOf("\\"); - if (lastSlashIndex >= 0) - { - string folderPath = uninstallString.Substring(0, lastSlashIndex); - executablePath = Path.Combine(folderPath, "sweclauncher", "sweclauncher.exe"); - } - } - break; - } - } + foreach (string root in uninstallRoots) + { + using (RegistryKey key = Registry.LocalMachine.OpenSubKey(root)) + { + if (key == null) continue; + string found = FindInUninstallKey(key, softwareName); + if (!string.IsNullOrEmpty(found)) + return found; } } - return executablePath; + return null; + } + + private static string FindInUninstallKey(RegistryKey uninstallKey, string softwareName) + { + foreach (string subkeyName in uninstallKey.GetSubKeyNames()) + { + using (RegistryKey subkey = uninstallKey.OpenSubKey(subkeyName)) + { + if (subkey == null) continue; + + string displayName = subkey.GetValue("DisplayName") as string; + if (string.IsNullOrEmpty(displayName) || !displayName.Contains(softwareName)) + continue; + + string installLocation = subkey.GetValue("InstallLocation") as string; + string uninstallString = subkey.GetValue("UninstallString") as string; + + string resolved = TryResolveSweclauncher(installLocation, uninstallString); + if (!string.IsNullOrEmpty(resolved) && File.Exists(resolved)) + return resolved; + } + } + + return null; + } + + private static string TryResolveSweclauncher(string installLocation, string uninstallString) + { + if (!string.IsNullOrWhiteSpace(installLocation)) + { + string fromLoc = ResolveSweclauncherUnderInstallRoot(installLocation.Trim().TrimEnd('\\')); + if (!string.IsNullOrEmpty(fromLoc)) + return fromLoc; + } + + if (!string.IsNullOrWhiteSpace(uninstallString)) + { + // 常见:"...\uninstall.exe" 或带引号路径 + string trimmed = uninstallString.Trim(); + if (trimmed.Length >= 2 && trimmed[0] == '"') + { + int end = trimmed.IndexOf('"', 1); + if (end > 1) + trimmed = trimmed.Substring(1, end - 1); + } + + int lastSlash = trimmed.LastIndexOf('\\'); + if (lastSlash < 0) + return null; + + string folderPath = trimmed.Substring(0, lastSlash); + string candidate = Path.Combine(folderPath, "sweclauncher", "sweclauncher.exe"); + if (File.Exists(candidate)) + return candidate; + + candidate = Path.Combine(folderPath, "sweclauncher.exe"); + if (File.Exists(candidate)) + return candidate; + } + + return null; + } + + private static string ResolveSweclauncherUnderInstallRoot(string installRoot) + { + string[] candidates = + { + Path.Combine(installRoot, "sweclauncher.exe"), + Path.Combine(installRoot, "sweclauncher", "sweclauncher.exe"), + }; + + foreach (string p in candidates) + { + if (File.Exists(p)) + return p; + } + + return null; } } } diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml index 6a5ebc67..698fd6da 100644 --- a/Ink Canvas/MainWindow.xaml +++ b/Ink Canvas/MainWindow.xaml @@ -903,6 +903,15 @@ IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold" Toggled="ToggleSwitchCompressPicturesUploaded_Toggled" /> + + + + + diff --git a/Ink Canvas/MainWindow_cs/MW_Settings.cs b/Ink Canvas/MainWindow_cs/MW_Settings.cs index a3e2eead..cedea1b4 100644 --- a/Ink Canvas/MainWindow_cs/MW_Settings.cs +++ b/Ink Canvas/MainWindow_cs/MW_Settings.cs @@ -2705,6 +2705,15 @@ namespace Ink_Canvas SaveSettingsToFile(); } + private void ToggleSwitchLaunchSeewoVideoShowcaseForWhiteboardBooth_Toggled(object sender, RoutedEventArgs e) + { + if (!isLoaded) return; + + Settings.Canvas.LaunchSeewoVideoShowcaseForWhiteboardBooth = + ToggleSwitchLaunchSeewoVideoShowcaseForWhiteboardBooth.IsOn; + SaveSettingsToFile(); + } + private void ToggleSwitchAutoStraightenLine_Toggled(object sender, RoutedEventArgs e) { if (!isLoaded) return; diff --git a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs index dafc9971..13277e7a 100644 --- a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs +++ b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs @@ -852,6 +852,9 @@ namespace Ink_Canvas ToggleSwitchDisablePressure.IsOn = Settings.Canvas.DisablePressure; inkCanvas.DefaultDrawingAttributes.IgnorePressure = Settings.Canvas.DisablePressure; + ToggleSwitchLaunchSeewoVideoShowcaseForWhiteboardBooth.IsOn = + Settings.Canvas.LaunchSeewoVideoShowcaseForWhiteboardBooth; + if (Settings.Canvas.EnableVelocityBrushTip) { Settings.Canvas.InkStyle = 3; @@ -874,8 +877,6 @@ namespace Ink_Canvas ToggleSwitchClearCanvasAlsoClearImages.IsOn = Settings.Canvas.ClearCanvasAlsoClearImages; ToggleSwitchShowCircleCenter.IsOn = Settings.Canvas.ShowCircleCenter; - ApplyWhiteboardBoothToolbarFromSettings(); - switch (Settings.Canvas.EraserShapeType) { case 0: diff --git a/Ink Canvas/MainWindow_cs/MW_VideoPresenter.cs b/Ink Canvas/MainWindow_cs/MW_VideoPresenter.cs index e55d24c8..e276e9b5 100644 --- a/Ink Canvas/MainWindow_cs/MW_VideoPresenter.cs +++ b/Ink Canvas/MainWindow_cs/MW_VideoPresenter.cs @@ -51,6 +51,14 @@ namespace Ink_Canvas /// 鼠标按钮事件的参数。 private void BtnToggleVideoPresenter_Click(object sender, System.Windows.Input.MouseButtonEventArgs e) { + if (Settings?.Canvas?.LaunchSeewoVideoShowcaseForWhiteboardBooth == true) + { + // 与主窗口「希沃视频展台」入口(BoardLaunchEasiCamera_MouseUp)一致:先走黑板/白板入口逻辑再启动 + ImageBlackboard_MouseUp(null, null); + SoftwareLauncher.LaunchEasiCamera("希沃视频展台"); + return; + } + ToggleVideoPresenterSidebar(); } diff --git a/Ink Canvas/Properties/Strings.enUS.xml b/Ink Canvas/Properties/Strings.enUS.xml index 8b85772e..84f509cb 100644 --- a/Ink Canvas/Properties/Strings.enUS.xml +++ b/Ink Canvas/Properties/Strings.enUS.xml @@ -179,6 +179,8 @@ Ask each time # Disabling may cause undo bugs. Show circle center + Whiteboard booth opens Seewo Video Showcase + When enabled, the whiteboard toolbar Booth button launches Seewo Video Showcase (must be installed). When disabled, the built-in booth is used. WPF default Bezier smoothing Advanced curve smoothing (recommended) Ink fade diff --git a/Ink Canvas/Properties/Strings.resx b/Ink Canvas/Properties/Strings.resx index 3f00ba51..df7fb722 100644 --- a/Ink Canvas/Properties/Strings.resx +++ b/Ink Canvas/Properties/Strings.resx @@ -194,6 +194,8 @@ 每次询问 # 请注意,若不保留双曲线渐近线可能会有遇到撤回相关的 BUG… 绘制圆时显示圆心位置 + 白板展台按钮启动希沃视频展台 + 开启后,点击白板工具栏「展台」将打开希沃视频展台(需已安装);关闭则使用内置展台。 使用WPF默认贝塞尔曲线平滑 使用高级曲线平滑(推荐) 启用墨迹渐隐功能 diff --git a/Ink Canvas/Resources/Settings.cs b/Ink Canvas/Resources/Settings.cs index e0b93049..4f3baec5 100644 --- a/Ink Canvas/Resources/Settings.cs +++ b/Ink Canvas/Resources/Settings.cs @@ -151,6 +151,10 @@ namespace Ink_Canvas [JsonProperty("enableVelocityBrushTip")] public bool EnableVelocityBrushTip { get; set; } + /// 为 true 时,白板工具栏「展台」按钮启动希沃视频展台(sweclauncher),否则使用内置展台。 + [JsonProperty("launchSeewoVideoShowcaseForWhiteboardBooth")] + public bool LaunchSeewoVideoShowcaseForWhiteboardBooth { get; set; } = false; + } public enum OptionalOperation diff --git a/Ink Canvas/Windows/SettingsViews/SettingsViews/CanvasAndInkPanel.xaml b/Ink Canvas/Windows/SettingsViews/SettingsViews/CanvasAndInkPanel.xaml index b0353e3e..c6c9891b 100644 --- a/Ink Canvas/Windows/SettingsViews/SettingsViews/CanvasAndInkPanel.xaml +++ b/Ink Canvas/Windows/SettingsViews/SettingsViews/CanvasAndInkPanel.xaml @@ -63,7 +63,7 @@ - + @@ -155,6 +155,20 @@ + + + + + + + + + + + + + + diff --git a/Ink Canvas/Windows/SettingsViews/SettingsViews/CanvasAndInkPanel.xaml.cs b/Ink Canvas/Windows/SettingsViews/SettingsViews/CanvasAndInkPanel.xaml.cs index d7812d80..e3b5bfaf 100644 --- a/Ink Canvas/Windows/SettingsViews/SettingsViews/CanvasAndInkPanel.xaml.cs +++ b/Ink Canvas/Windows/SettingsViews/SettingsViews/CanvasAndInkPanel.xaml.cs @@ -101,6 +101,9 @@ namespace Ink_Canvas.Windows.SettingsViews // 插入图片时自动压缩 SetToggleSwitchState(FindToggleSwitch("ToggleSwitchCompressPicturesUploaded"), canvas.IsCompressPicturesUploaded); + // 白板展台按钮启动希沃视频展台 + SetToggleSwitchState(FindToggleSwitch("ToggleSwitchLaunchSeewoVideoShowcaseForWhiteboardBooth"), canvas.LaunchSeewoVideoShowcaseForWhiteboardBooth); + // 保留双曲线渐近线 SetOptionButtonState("HyperbolaAsymptote", (int)canvas.HyperbolaAsymptoteOption); @@ -247,6 +250,8 @@ namespace Ink_Canvas.Windows.SettingsViews return canvas.EnablePressureTouchMode; case "DisablePressure": return canvas.DisablePressure; + case "LaunchSeewoVideoShowcaseForWhiteboardBooth": + return canvas.LaunchSeewoVideoShowcaseForWhiteboardBooth; case "HideStrokeWhenSelecting": return canvas.HideStrokeWhenSelecting; case "ClearCanvasAndClearTimeMachine": @@ -342,6 +347,11 @@ namespace Ink_Canvas.Windows.SettingsViews } break; + case "LaunchSeewoVideoShowcaseForWhiteboardBooth": + MainWindowSettingsHelper.InvokeToggleSwitchToggled( + "ToggleSwitchLaunchSeewoVideoShowcaseForWhiteboardBooth", newState); + break; + case "HideStrokeWhenSelecting": // 调用 MainWindow 中的方法 MainWindowSettingsHelper.InvokeToggleSwitchToggled("ToggleSwitchHideStrokeWhenSelecting", newState); diff --git a/Ink Canvas/Windows/SettingsViews/SettingsViews/MainWindowSettingsHelper.cs b/Ink Canvas/Windows/SettingsViews/SettingsViews/MainWindowSettingsHelper.cs index 34eddb0c..8f78b5a1 100644 --- a/Ink Canvas/Windows/SettingsViews/SettingsViews/MainWindowSettingsHelper.cs +++ b/Ink Canvas/Windows/SettingsViews/SettingsViews/MainWindowSettingsHelper.cs @@ -419,6 +419,7 @@ namespace Ink_Canvas.Windows.SettingsViews { "ToggleSwitchShowCursor", "CanvasAndInkPanel" }, { "ToggleSwitchDisablePressure", "CanvasAndInkPanel" }, { "ToggleSwitchEnablePressureTouchMode", "CanvasAndInkPanel" }, + { "ToggleSwitchLaunchSeewoVideoShowcaseForWhiteboardBooth", "CanvasAndInkPanel" }, { "ComboBoxEraserSize", "CanvasAndInkPanel" }, { "ComboBoxHyperbolaAsymptoteOption", "CanvasAndInkPanel" }, { "ComboBoxAutoSaveStrokesInterval", "CanvasAndInkPanel" },