From 8f3b65c6d4c1df50a4e40a1ce1683c9ce0462a87 Mon Sep 17 00:00:00 2001 From: CJKmkp <2564608840@qq.com> Date: Sun, 5 Apr 2026 14:58:32 +0800 Subject: [PATCH] =?UTF-8?q?improve:=E4=BB=85PPT=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ink Canvas/MainWindow.xaml.cs | 9 ++- Ink Canvas/MainWindow_cs/MW_PPT.cs | 122 +++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 2 deletions(-) diff --git a/Ink Canvas/MainWindow.xaml.cs b/Ink Canvas/MainWindow.xaml.cs index b0d971ff..1a3d2729 100644 --- a/Ink Canvas/MainWindow.xaml.cs +++ b/Ink Canvas/MainWindow.xaml.cs @@ -1407,6 +1407,7 @@ namespace Ink_Canvas // 检查模式设置并应用 CheckMainWindowVisibility(); + EnsurePptOnlyVisibilityProbeTimer(); // 检查是否通过--board参数启动,如果是则自动切换到白板模式 if (App.StartWithBoardMode) @@ -4377,9 +4378,11 @@ namespace Ink_Canvas { Hide(); LogHelper.WriteLogToFile("已切换到仅PPT模式,主窗口已隐藏", LogHelper.LogType.Event); + EnsurePptOnlyVisibilityProbeTimer(); } else { + StopPptOnlyVisibilityProbeTimer(); // 如果切换到正常模式,显示主窗口 Show(); LogHelper.WriteLogToFile("已切换到正常模式,主窗口已显示", LogHelper.LogType.Event); @@ -4401,8 +4404,10 @@ namespace Ink_Canvas { if (Settings.ModeSettings.IsPPTOnlyMode) { - // 仅PPT模式下,只有在PPT放映时才显示 - bool isInSlideShow = BtnPPTSlideShowEnd.Visibility == Visibility.Visible; + // 仅PPT模式:以 COM/UI 状态为主,Win32 检测全屏放映窗口(screenClass)作兜底,避免 COM 异常时无法唤出 + bool comUiSlideShow = BtnPPTSlideShowEnd.Visibility == Visibility.Visible; + bool win32SlideShow = IsPowerPointSlideshowSurfacePresentWin32(); + bool isInSlideShow = comUiSlideShow || win32SlideShow; if (isInSlideShow && !IsVisible) { Show(); diff --git a/Ink Canvas/MainWindow_cs/MW_PPT.cs b/Ink Canvas/MainWindow_cs/MW_PPT.cs index 00101bd5..60fb97ea 100644 --- a/Ink Canvas/MainWindow_cs/MW_PPT.cs +++ b/Ink Canvas/MainWindow_cs/MW_PPT.cs @@ -4,6 +4,7 @@ using Microsoft.Office.Core; using Microsoft.Office.Interop.PowerPoint; using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Text; @@ -167,6 +168,18 @@ namespace Ink_Canvas /// 断开连接后退出PPT模式的延迟时间(毫秒),即连接断开后多长时间才退出PPT模式。 /// private const int ExitPPTModeAfterDisconnectDelayMs = 1200; + + /// + /// 仅PPT模式下周期性探测放映界面(COM 失效时依赖 Win32),间隔不宜过小以免多余开销。 + /// + private DispatcherTimer _pptOnlyVisibilityProbeTimer; + + private const int PptOnlyVisibilityProbeIntervalMs = 800; + + /// + /// PowerPoint 全屏放映顶层窗口类名(与编辑态 PPTFrameClass 区分)。 + /// + private const string PowerPointSlideShowWindowClassName = "screenClass"; #endregion #region PPT Managers @@ -638,6 +651,8 @@ namespace Ink_Canvas ClosePowerPointApplication(); ClearStaticInteropState(); + StopPptOnlyVisibilityProbeTimer(); + LogHelper.WriteLogToFile("PPT管理器已释放", LogHelper.LogType.Event); } catch (Exception ex) @@ -700,6 +715,104 @@ namespace Ink_Canvas } #endregion + #region 仅PPT模式可见性(COM + Win32 兜底) + + /// + /// 在启用「仅PPT模式」时启动轻量探测,COM 事件延迟或失效时仍可根据全屏放映窗口显示主窗口。 + /// + internal void EnsurePptOnlyVisibilityProbeTimer() + { + try + { + if (!Settings.ModeSettings.IsPPTOnlyMode) + { + StopPptOnlyVisibilityProbeTimer(); + return; + } + + if (_pptOnlyVisibilityProbeTimer == null) + { + _pptOnlyVisibilityProbeTimer = new DispatcherTimer + { + Interval = TimeSpan.FromMilliseconds(PptOnlyVisibilityProbeIntervalMs) + }; + _pptOnlyVisibilityProbeTimer.Tick += (_, __) => CheckMainWindowVisibility(); + } + + if (!_pptOnlyVisibilityProbeTimer.IsEnabled) + _pptOnlyVisibilityProbeTimer.Start(); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"仅PPT可见性探测计时器启动失败: {ex.Message}", LogHelper.LogType.Warning); + } + } + + internal void StopPptOnlyVisibilityProbeTimer() + { + try + { + _pptOnlyVisibilityProbeTimer?.Stop(); + } + catch + { + } + } + + /// + /// 检测是否存在 PowerPoint 全屏放映顶层窗口(类名 screenClass,进程 powerpnt),用于 COM 不可用时的兜底。 + /// + internal bool IsPowerPointSlideshowSurfacePresentWin32() + { + if (!Settings.ModeSettings.IsPPTOnlyMode) + return false; + + try + { + bool found = false; + EnumWindows((hWnd, _) => + { + if (!IsWindow(hWnd) || !IsWindowVisible(hWnd)) + return true; + + var cls = new StringBuilder(256); + if (GetClassName(hWnd, cls, cls.Capacity) == 0) + return true; + + if (!string.Equals(cls.ToString(), PowerPointSlideShowWindowClassName, StringComparison.OrdinalIgnoreCase)) + return true; + + try + { + GetWindowThreadProcessId(hWnd, out uint pid); + using (var proc = Process.GetProcessById((int)pid)) + { + var name = proc.ProcessName; + if (string.Equals(name, "POWERPNT", StringComparison.OrdinalIgnoreCase)) + { + found = true; + return false; + } + } + } + catch + { + } + + return true; + }, IntPtr.Zero); + + return found; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"Win32 检测 PPT 放映窗口失败: {ex.Message}", LogHelper.LogType.Trace); + return false; + } + } + + #endregion + #region New PPT Event Handlers /// /// 处理 PowerPoint 连接状态的变更:更新界面连接/放映状态,并在断开时启动一个短延迟以安全退出 PPT 模式。 @@ -731,6 +844,8 @@ namespace Ink_Canvas _ = HandleManualSlideShowEnd(); if (Settings.PowerPointSettings.UseRotPptLink) _pptManager?.ReloadConnection(); + + CheckMainWindowVisibility(); } }); } @@ -1114,6 +1229,9 @@ namespace Ink_Canvas // 加载当前页墨迹 LoadCurrentSlideInk(currentSlide); + + // 仅PPT模式:放映开始立即同步主窗口可见性(勿仅依赖 SlideShowStateChanged 定时器) + CheckMainWindowVisibility(); }); if (!isFloatingBarFolded) @@ -1435,6 +1553,8 @@ namespace Ink_Canvas UpdateCurrentToolMode("cursor"); SetFloatingBarHighlightPosition("cursor"); + + CheckMainWindowVisibility(); } catch (Exception ex) { @@ -2291,6 +2411,7 @@ namespace Ink_Canvas _pptUIManager?.UpdateSlideShowStatus(false); _pptUIManager?.UpdateSidebarExitButtons(false); LogHelper.WriteLogToFile("手动更新放映结束UI状态", LogHelper.LogType.Trace); + CheckMainWindowVisibility(); }); // 手动处理自动收纳,因为OnPPTSlideShowEnd事件可能未触发 @@ -2323,6 +2444,7 @@ namespace Ink_Canvas { _pptUIManager?.UpdateSlideShowStatus(false); _pptUIManager?.UpdateSidebarExitButtons(false); + CheckMainWindowVisibility(); }); // 异常情况下也手动处理自动收纳