diff --git a/Ink Canvas/Helpers/COMPPTManager.cs b/Ink Canvas/Helpers/COMPPTManager.cs new file mode 100644 index 00000000..d5eaf574 --- /dev/null +++ b/Ink Canvas/Helpers/COMPPTManager.cs @@ -0,0 +1,1255 @@ +using Microsoft.Office.Interop.PowerPoint; +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using System.Timers; +using System.Windows.Threading; +using Application = System.Windows.Application; +using Timer = System.Timers.Timer; + +namespace Ink_Canvas.Helpers +{ + /// + /// 基于 COM 的 PPT 联动管理器 + /// + public class ComPPTManager : IDisposable + { + #region Events + public event Action SlideShowBegin; + public event Action SlideShowNextSlide; + public event Action SlideShowEnd; + public event Action PresentationOpen; + public event Action PresentationClose; + public event Action PPTConnectionChanged; + public event Action SlideShowStateChanged; + #endregion + + #region Properties + public Microsoft.Office.Interop.PowerPoint.Application PPTApplication { get; private set; } + public Presentation CurrentPresentation { get; private set; } + public Slides CurrentSlides { get; private set; } + public Slide CurrentSlide { get; private set; } + public int SlidesCount { get; private set; } + + public bool IsConnected + { + get + { + try + { + if (PPTApplication == null) return false; + if (!Marshal.IsComObject(PPTApplication)) return false; + + // 尝试访问一个简单的属性来验证连接是否有效 + var _ = PPTApplication.Name; + return true; + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + // 如果COM对象已失效,返回false + if (hr == 0x8001010E || hr == 0x80004005 || hr == 0x800706B5) + { + return false; + } + return false; + } + catch + { + return false; + } + } + } + + public bool IsInSlideShow + { + get + { + object slideShowWindows = null; + object slideShowWindow = null; + object view = null; + try + { + if (PPTApplication == null || !Marshal.IsComObject(PPTApplication)) return false; + + slideShowWindows = PPTApplication.SlideShowWindows; + if (slideShowWindows == null) return false; + + dynamic ssw = slideShowWindows; + if (ssw.Count == 0) return false; + + try + { + slideShowWindow = ssw[1]; + if (slideShowWindow == null) return false; + + dynamic sswObj = slideShowWindow; + view = sswObj.View; + if (view == null) return false; + + return true; + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x8001010E || hr == 0x80004005) + { + DisconnectFromPPT(); + } + return false; + } + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x8001010E || hr == 0x80004005) + { + DisconnectFromPPT(); + } + LogHelper.WriteLogToFile($"检查PPT放映状态失败: {comEx.Message} (HR: 0x{hr:X8})", LogHelper.LogType.Warning); + return false; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"检查PPT放映状态时发生意外错误: {ex}", LogHelper.LogType.Warning); + return false; + } + finally + { + SafeReleaseComObject(view); + SafeReleaseComObject(slideShowWindow); + SafeReleaseComObject(slideShowWindows); + } + } + } + + /// + /// 是否尝试支持 WPS(通过 COM 接口 kwpp.Application) + /// + public bool IsSupportWPS { get; set; } = false; + #endregion + + #region Private Fields + private Timer _connectionCheckTimer; + private Timer _slideShowStateCheckTimer; + private bool _isModuleUnloading = false; + private bool _lastSlideShowState; + private readonly object _lockObject = new object(); + private bool _disposed; + #endregion + + #region Constructor & Initialization + public ComPPTManager() + { + InitializeConnectionTimer(); + } + + private void InitializeConnectionTimer() + { + _connectionCheckTimer = new Timer(500); + _connectionCheckTimer.Elapsed += OnConnectionCheckTimerElapsed; + _connectionCheckTimer.AutoReset = true; + + _slideShowStateCheckTimer = new Timer(1000); + _slideShowStateCheckTimer.Elapsed += OnSlideShowStateCheckTimerElapsed; + _slideShowStateCheckTimer.AutoReset = true; + } + + public void StartMonitoring() + { + if (!_disposed) + { + _connectionCheckTimer?.Start(); + _slideShowStateCheckTimer?.Start(); + LogHelper.WriteLogToFile("ComPPTManager: PPT监控已启动", LogHelper.LogType.Trace); + } + } + + public void StopMonitoring() + { + _connectionCheckTimer?.Stop(); + _slideShowStateCheckTimer?.Stop(); + DisconnectFromPPT(); + LogHelper.WriteLogToFile("ComPPTManager: PPT监控已停止", LogHelper.LogType.Trace); + } + #endregion + + #region Connection Management + private void OnConnectionCheckTimerElapsed(object sender, ElapsedEventArgs e) + { + try + { + if (!_isModuleUnloading) + { + CheckAndConnectToPPT(); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: PPT连接检查失败: {ex}", LogHelper.LogType.Error); + } + } + + private void OnSlideShowStateCheckTimerElapsed(object sender, ElapsedEventArgs e) + { + try + { + if (!_isModuleUnloading && IsConnected) + { + CheckSlideShowState(); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: PPT放映状态检查失败: {ex}", LogHelper.LogType.Error); + } + } + + private void CheckAndConnectToPPT() + { + if (_isModuleUnloading) return; + + lock (_lockObject) + { + try + { + if (_isModuleUnloading) return; + + // 尝试连接到PowerPoint + var pptApp = TryConnectToPowerPoint(); + if (pptApp == null && IsSupportWPS) + { + // 如果PowerPoint连接失败且支持WPS,尝试连接WPS + pptApp = TryConnectToWPS(); + } + + if (pptApp != null && !IsConnected) + { + // 有可用的PPT/WPS应用程序且当前未连接,建立连接 + ConnectToPPT(pptApp); + } + else if (pptApp == null && IsConnected) + { + // 没有可用的PPT/WPS应用程序但当前显示已连接,断开连接 + DisconnectFromPPT(); + } + else if (pptApp == null && PPTApplication != null) + { + // PPT/WPS应用程序不可用但PPTApplication对象仍存在,清理无效连接 + DisconnectFromPPT(); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: PPT连接检查异常: {ex}", LogHelper.LogType.Error); + if (PPTApplication != null) + { + DisconnectFromPPT(); + } + } + } + } + + private void CheckSlideShowState() + { + try + { + if (!IsConnected) return; + + var currentSlideShowState = IsInSlideShow; + if (currentSlideShowState != _lastSlideShowState) + { + _lastSlideShowState = currentSlideShowState; + SlideShowStateChanged?.Invoke(currentSlideShowState); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: 检查PPT放映状态异常: {ex}", LogHelper.LogType.Error); + } + } + + private Microsoft.Office.Interop.PowerPoint.Application TryConnectToPowerPoint() + { + try + { + var pptApp = (Microsoft.Office.Interop.PowerPoint.Application)Marshal.GetActiveObject("PowerPoint.Application"); + + if (pptApp != null && Marshal.IsComObject(pptApp)) + { + var _ = pptApp.Name; + return pptApp; + } + return null; + } + catch + { + return null; + } + } + + private Microsoft.Office.Interop.PowerPoint.Application TryConnectToWPS() + { + try + { + var wpsApp = (Microsoft.Office.Interop.PowerPoint.Application)Marshal.GetActiveObject("kwpp.Application"); + + if (wpsApp != null && Marshal.IsComObject(wpsApp)) + { + var _ = wpsApp.Name; + return wpsApp; + } + return null; + } + catch + { + return null; + } + } + + private void ConnectToPPT(Microsoft.Office.Interop.PowerPoint.Application pptApp) + { + try + { + PPTApplication = pptApp; + + // 在主线程中注册事件,确保COM对象在正确的线程中 + Application.Current?.Dispatcher?.Invoke(() => + { + try + { + PPTApplication.PresentationOpen += OnPresentationOpen; + PPTApplication.PresentationClose += OnPresentationClose; + PPTApplication.SlideShowBegin += OnSlideShowBegin; + PPTApplication.SlideShowNextSlide += OnSlideShowNextSlide; + PPTApplication.SlideShowEnd += OnSlideShowEnd; + + LogHelper.WriteLogToFile("ComPPTManager: PPT事件注册成功", LogHelper.LogType.Trace); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: PPT事件注册失败: {ex}", LogHelper.LogType.Error); + throw; // 重新抛出异常,让外层处理 + } + }, DispatcherPriority.Normal, default, TimeSpan.FromSeconds(2)); + + // 获取当前演示文稿信息 + UpdateCurrentPresentationInfo(); + + // 停止连接检查定时器,避免重复连接 + _connectionCheckTimer?.Stop(); + + // 触发连接成功事件 + PPTConnectionChanged?.Invoke(true); + + LogHelper.WriteLogToFile("ComPPTManager: 成功连接到PPT应用程序", LogHelper.LogType.Event); + + if (IsInSlideShow && PPTApplication.SlideShowWindows.Count > 0) + { + OnSlideShowBegin(PPTApplication.SlideShowWindows[1]); + } + else if (CurrentPresentation != null) + { + OnPresentationOpen(CurrentPresentation); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: 连接PPT应用程序失败: {ex}", LogHelper.LogType.Error); + PPTApplication = null; + } + } + + private void DisconnectFromPPT() + { + try + { + if (PPTApplication != null) + { + // 取消事件注册 + try + { + if (Marshal.IsComObject(PPTApplication)) + { + Application.Current?.Dispatcher?.Invoke(() => + { + try + { + PPTApplication.PresentationOpen -= OnPresentationOpen; + PPTApplication.PresentationClose -= OnPresentationClose; + PPTApplication.SlideShowBegin -= OnSlideShowBegin; + PPTApplication.SlideShowNextSlide -= OnSlideShowNextSlide; + PPTApplication.SlideShowEnd -= OnSlideShowEnd; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: 取消PPT事件注册时发生异常: {ex}", LogHelper.LogType.Warning); + } + }, DispatcherPriority.Normal, default, TimeSpan.FromSeconds(1)); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: 取消PPT事件注册失败: {ex}", LogHelper.LogType.Warning); + } + + SafeReleaseComObject(CurrentSlide); + SafeReleaseComObject(CurrentSlides); + SafeReleaseComObject(CurrentPresentation); + + if (PPTApplication != null && Marshal.IsComObject(PPTApplication)) + { + try + { + Marshal.FinalReleaseComObject(PPTApplication); + } + catch + { + try + { + int refCount = Marshal.ReleaseComObject(PPTApplication); + while (refCount > 0) + { + refCount = Marshal.ReleaseComObject(PPTApplication); + } + } + catch { } + } + } + } + + PPTApplication = null; + CurrentPresentation = null; + CurrentSlides = null; + CurrentSlide = null; + SlidesCount = 0; + + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + + _isModuleUnloading = true; + _connectionCheckTimer?.Stop(); + _slideShowStateCheckTimer?.Stop(); + + // 触发连接断开事件 + PPTConnectionChanged?.Invoke(false); + + LogHelper.WriteLogToFile("ComPPTManager: 已断开PPT连接", LogHelper.LogType.Event); + + ThreadPool.QueueUserWorkItem(_ => + { + try + { + Thread.Sleep(2000); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + + Thread.Sleep(1000); + + _isModuleUnloading = false; + _connectionCheckTimer?.Start(); + _slideShowStateCheckTimer?.Start(); + + LogHelper.WriteLogToFile("ComPPTManager: PPT联动模块已重新加载", LogHelper.LogType.Trace); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: 重新加载PPT联动模块失败: {ex}", LogHelper.LogType.Error); + _isModuleUnloading = false; + } + }); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: 断开PPT连接失败: {ex}", LogHelper.LogType.Error); + _isModuleUnloading = false; + } + } + + private void SafeReleaseComObject(object comObject) + { + try + { + if (comObject != null && Marshal.IsComObject(comObject)) + { + Marshal.ReleaseComObject(comObject); + } + } + catch { } + } + + private void UpdateCurrentPresentationInfo() + { + object activePresentation = null; + object slideShowWindows = null; + object slideShowWindow = null; + object activeWindow = null; + object view = null; + object selection = null; + object slideRange = null; + + try + { + if (PPTApplication != null && Marshal.IsComObject(PPTApplication)) + { + try + { + activePresentation = PPTApplication.ActivePresentation; + if (activePresentation != null) + { + SafeReleaseComObject(CurrentPresentation); + CurrentPresentation = activePresentation as Presentation; + CurrentSlides = CurrentPresentation.Slides; + + try + { + var slideCount = CurrentSlides.Count; + SlidesCount = slideCount > 0 ? slideCount : 0; + } + catch + { + SlidesCount = 0; + } + + try + { + slideShowWindows = PPTApplication.SlideShowWindows; + if (IsInSlideShow && slideShowWindows != null) + { + dynamic ssw = slideShowWindows; + if (ssw.Count > 0) + { + slideShowWindow = ssw[1]; + if (slideShowWindow != null) + { + dynamic sswObj = slideShowWindow; + view = sswObj.View; + if (view != null) + { + dynamic viewObj = view; + CurrentSlide = viewObj.Slide as Slide; + } + } + } + } + else + { + activeWindow = PPTApplication.ActiveWindow; + if (activeWindow != null) + { + dynamic aw = activeWindow; + selection = aw.Selection; + if (selection != null) + { + dynamic sel = selection; + slideRange = sel.SlideRange; + if (slideRange != null) + { + dynamic sr = slideRange; + int slideNumber = sr.SlideNumber; + if (slideNumber > 0 && slideNumber <= SlidesCount) + { + CurrentSlide = CurrentSlides[slideNumber]; + } + } + } + } + + if (CurrentSlide == null && SlidesCount > 0) + { + CurrentSlide = CurrentSlides[1]; + } + } + } + catch + { + if (SlidesCount > 0) + { + CurrentSlide = CurrentSlides[1]; + } + } + } + else + { + CurrentPresentation = null; + CurrentSlides = null; + CurrentSlide = null; + SlidesCount = 0; + } + } + catch + { + CurrentPresentation = null; + CurrentSlides = null; + CurrentSlide = null; + SlidesCount = 0; + } + } + else + { + CurrentPresentation = null; + CurrentSlides = null; + CurrentSlide = null; + SlidesCount = 0; + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: 更新演示文稿信息失败: {ex}", LogHelper.LogType.Error); + CurrentPresentation = null; + CurrentSlides = null; + CurrentSlide = null; + SlidesCount = 0; + } + finally + { + SafeReleaseComObject(slideRange); + SafeReleaseComObject(selection); + SafeReleaseComObject(view); + SafeReleaseComObject(slideShowWindow); + SafeReleaseComObject(activeWindow); + SafeReleaseComObject(slideShowWindows); + if (activePresentation != null && !ReferenceEquals(activePresentation, CurrentPresentation)) + { + SafeReleaseComObject(activePresentation); + } + } + } + #endregion + + #region Event Handlers + private void OnPresentationOpen(Presentation pres) + { + try + { + UpdateCurrentPresentationInfo(); + PresentationOpen?.Invoke(pres); + LogHelper.WriteLogToFile($"ComPPTManager: 演示文稿已打开: {pres?.Name}", LogHelper.LogType.Event); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: 处理演示文稿打开事件失败: {ex}", LogHelper.LogType.Error); + } + } + + private void OnPresentationClose(Presentation pres) + { + try + { + PresentationClose?.Invoke(pres); + DisconnectFromPPT(); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: 处理演示文稿关闭事件失败: {ex}", LogHelper.LogType.Error); + } + } + + private void OnSlideShowBegin(SlideShowWindow wn) + { + try + { + UpdateCurrentPresentationInfo(); + SlideShowBegin?.Invoke(wn); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: 处理幻灯片放映开始事件失败: {ex}", LogHelper.LogType.Error); + } + } + + private void OnSlideShowNextSlide(SlideShowWindow wn) + { + try + { + UpdateCurrentPresentationInfo(); + SlideShowNextSlide?.Invoke(wn); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: 处理幻灯片切换事件失败: {ex}", LogHelper.LogType.Error); + } + } + + private void OnSlideShowEnd(Presentation pres) + { + try + { + SlideShowEnd?.Invoke(pres); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"处理幻灯片放映结束事件失败: {ex}", LogHelper.LogType.Error); + } + } + #endregion + + #region Public Navigation APIs + public bool TryNavigateToSlide(int slideNumber) + { + object slideShowWindows = null; + object slideShowWindow = null; + object view = null; + object windows = null; + object window = null; + object windowView = null; + try + { + if (!IsConnected || PPTApplication == null) return false; + if (!Marshal.IsComObject(PPTApplication)) return false; + + if (IsInSlideShow) + { + slideShowWindows = PPTApplication.SlideShowWindows; + if (slideShowWindows != null) + { + dynamic ssw = slideShowWindows; + if (ssw.Count >= 1) + { + slideShowWindow = ssw[1]; + if (slideShowWindow != null) + { + dynamic sswObj = slideShowWindow; + view = sswObj.View; + if (view != null) + { + dynamic viewObj = view; + viewObj.GotoSlide(slideNumber); + return true; + } + } + } + } + } + else if (CurrentPresentation != null) + { + windows = CurrentPresentation.Windows; + if (windows != null) + { + dynamic win = windows; + if (win.Count >= 1) + { + window = win[1]; + if (window != null) + { + dynamic winObj = window; + windowView = winObj.View; + if (windowView != null) + { + dynamic viewObj = windowView; + viewObj.GotoSlide(slideNumber); + return true; + } + } + } + } + } + return false; + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x8001010E || hr == 0x80004005) + { + DisconnectFromPPT(); + } + LogHelper.WriteLogToFile($"ComPPTManager: 跳转到幻灯片{slideNumber}失败: {comEx.Message}", LogHelper.LogType.Error); + return false; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: 跳转到幻灯片{slideNumber}失败: {ex}", LogHelper.LogType.Error); + return false; + } + finally + { + SafeReleaseComObject(windowView); + SafeReleaseComObject(window); + SafeReleaseComObject(windows); + SafeReleaseComObject(view); + SafeReleaseComObject(slideShowWindow); + SafeReleaseComObject(slideShowWindows); + } + } + + public bool TryNavigateNext() + { + object slideShowWindows = null; + object slideShowWindow = null; + object view = null; + try + { + if (!IsConnected || !IsInSlideShow || PPTApplication == null) return false; + if (!Marshal.IsComObject(PPTApplication)) return false; + + slideShowWindows = PPTApplication.SlideShowWindows; + if (slideShowWindows != null) + { + dynamic ssw = slideShowWindows; + slideShowWindow = ssw[1]; + if (slideShowWindow != null) + { + dynamic sswObj = slideShowWindow; + sswObj.Activate(); + view = sswObj.View; + if (view != null) + { + dynamic viewObj = view; + viewObj.Next(); + return true; + } + } + } + return false; + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x8001010E || hr == 0x80004005) + { + DisconnectFromPPT(); + } + LogHelper.WriteLogToFile($"ComPPTManager: 切换到下一页失败: {comEx.Message}", LogHelper.LogType.Error); + return false; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: 切换到下一页失败: {ex}", LogHelper.LogType.Error); + return false; + } + finally + { + SafeReleaseComObject(view); + SafeReleaseComObject(slideShowWindow); + SafeReleaseComObject(slideShowWindows); + } + } + + public bool TryNavigatePrevious() + { + object slideShowWindows = null; + object slideShowWindow = null; + object view = null; + try + { + if (!IsConnected || !IsInSlideShow || PPTApplication == null) return false; + if (!Marshal.IsComObject(PPTApplication)) return false; + + slideShowWindows = PPTApplication.SlideShowWindows; + if (slideShowWindows != null) + { + dynamic ssw = slideShowWindows; + slideShowWindow = ssw[1]; + if (slideShowWindow != null) + { + dynamic sswObj = slideShowWindow; + sswObj.Activate(); + view = sswObj.View; + if (view != null) + { + dynamic viewObj = view; + viewObj.Previous(); + return true; + } + } + } + return false; + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x8001010E || hr == 0x80004005) + { + DisconnectFromPPT(); + } + LogHelper.WriteLogToFile($"ComPPTManager: 切换到上一页失败: {comEx.Message}", LogHelper.LogType.Error); + return false; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: 切换到上一页失败: {ex}", LogHelper.LogType.Error); + return false; + } + finally + { + SafeReleaseComObject(view); + SafeReleaseComObject(slideShowWindow); + SafeReleaseComObject(slideShowWindows); + } + } + + public bool TryEndSlideShow() + { + object slideShowWindows = null; + object slideShowWindow = null; + object view = null; + try + { + if (!IsConnected || !IsInSlideShow || PPTApplication == null) return false; + if (!Marshal.IsComObject(PPTApplication)) return false; + + slideShowWindows = PPTApplication.SlideShowWindows; + if (slideShowWindows != null) + { + dynamic ssw = slideShowWindows; + slideShowWindow = ssw[1]; + if (slideShowWindow != null) + { + dynamic sswObj = slideShowWindow; + view = sswObj.View; + if (view != null) + { + dynamic viewObj = view; + viewObj.Exit(); + return true; + } + } + } + return false; + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x8001010E || hr == 0x80004005) + { + DisconnectFromPPT(); + } + LogHelper.WriteLogToFile($"ComPPTManager: 结束幻灯片放映失败: {comEx.Message}", LogHelper.LogType.Error); + return false; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: 结束幻灯片放映失败: {ex}", LogHelper.LogType.Error); + return false; + } + finally + { + SafeReleaseComObject(view); + SafeReleaseComObject(slideShowWindow); + SafeReleaseComObject(slideShowWindows); + } + } + + public bool TryStartSlideShow() + { + try + { + if (!IsConnected || CurrentPresentation == null || PPTApplication == null) return false; + if (!Marshal.IsComObject(PPTApplication) || !Marshal.IsComObject(CurrentPresentation)) return false; + + CurrentPresentation.SlideShowSettings.Run(); + return true; + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x8001010E || hr == 0x80004005) + { + DisconnectFromPPT(); + } + LogHelper.WriteLogToFile($"ComPPTManager: 开始幻灯片放映失败: {comEx.Message}", LogHelper.LogType.Error); + return false; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: 开始幻灯片放映失败: {ex}", LogHelper.LogType.Error); + return false; + } + } + + public Presentation GetCurrentActivePresentation() + { + try + { + if (!IsConnected || PPTApplication == null) return null; + if (!Marshal.IsComObject(PPTApplication)) return null; + + if (IsInSlideShow && PPTApplication.SlideShowWindows.Count > 0) + { + try + { + var slideShowWindow = PPTApplication.SlideShowWindows[1]; + if (slideShowWindow?.View != null) + { + return (Presentation)slideShowWindow.View.Slide.Parent; + } + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x80048240) + { + return null; + } + throw; + } + } + + if (PPTApplication.ActiveWindow?.Presentation != null) + { + return PPTApplication.ActiveWindow.Presentation; + } + + return CurrentPresentation; + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x8001010E || hr == 0x80004005) + { + DisconnectFromPPT(); + } + LogHelper.WriteLogToFile($"ComPPTManager: 获取当前活跃演示文稿失败: {comEx.Message}", LogHelper.LogType.Warning); + return CurrentPresentation; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: 获取当前活跃演示文稿失败: {ex}", LogHelper.LogType.Error); + return CurrentPresentation; + } + } + + public int GetCurrentSlideNumber() + { + object slideShowWindows = null; + object slideShowWindow = null; + object view = null; + object activeWindow = null; + object selection = null; + object slideRange = null; + try + { + if (!IsConnected || PPTApplication == null) return 0; + if (!Marshal.IsComObject(PPTApplication)) return 0; + + if (IsInSlideShow) + { + slideShowWindows = PPTApplication.SlideShowWindows; + if (slideShowWindows != null) + { + dynamic ssw = slideShowWindows; + if (ssw.Count > 0) + { + slideShowWindow = ssw[1]; + if (slideShowWindow != null) + { + dynamic sswObj = slideShowWindow; + view = sswObj.View; + if (view != null) + { + dynamic viewObj = view; + return viewObj.CurrentShowPosition; + } + } + } + } + } + + activeWindow = PPTApplication.ActiveWindow; + if (activeWindow != null) + { + dynamic aw = activeWindow; + selection = aw.Selection; + if (selection != null) + { + dynamic sel = selection; + slideRange = sel.SlideRange; + if (slideRange != null) + { + dynamic sr = slideRange; + int slideNumber = sr.SlideNumber; + if (slideNumber > 0) + { + return slideNumber; + } + } + } + } + + if (CurrentSlide != null && Marshal.IsComObject(CurrentSlide)) + { + return CurrentSlide.SlideNumber; + } + + return 0; + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x8001010E || hr == 0x80004005) + { + DisconnectFromPPT(); + } + return 0; + } + catch + { + return 0; + } + finally + { + SafeReleaseComObject(slideRange); + SafeReleaseComObject(selection); + SafeReleaseComObject(view); + SafeReleaseComObject(slideShowWindow); + SafeReleaseComObject(slideShowWindows); + SafeReleaseComObject(activeWindow); + } + } + + public string GetPresentationName() + { + try + { + if (CurrentPresentation == null || !Marshal.IsComObject(CurrentPresentation)) return ""; + + return CurrentPresentation.Name ?? ""; + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x8001010E || hr == 0x80004005) + { + DisconnectFromPPT(); + } + LogHelper.WriteLogToFile($"ComPPTManager: 获取演示文稿名称失败: {comEx.Message}", LogHelper.LogType.Warning); + return ""; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: 获取演示文稿名称失败: {ex}", LogHelper.LogType.Error); + return ""; + } + } + + public string GetPresentationPath() + { + try + { + if (CurrentPresentation == null || !Marshal.IsComObject(CurrentPresentation)) return ""; + + return CurrentPresentation.FullName ?? ""; + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x8001010E || hr == 0x80004005) + { + DisconnectFromPPT(); + } + LogHelper.WriteLogToFile($"ComPPTManager: 获取演示文稿路径失败: {comEx.Message}", LogHelper.LogType.Warning); + return ""; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: 获取演示文稿路径失败: {ex}", LogHelper.LogType.Error); + return ""; + } + } + + public bool TryShowSlideNavigation() + { + object slideShowWindows = null; + object slideShowWindow = null; + object slideNavigation = null; + try + { + LogHelper.WriteLogToFile($"ComPPTManager: 尝试显示幻灯片导航 - 连接状态: {IsConnected}, 放映状态: {IsInSlideShow}", LogHelper.LogType.Trace); + + if (!IsConnected || !IsInSlideShow || PPTApplication == null) + { + LogHelper.WriteLogToFile("ComPPTManager: PPT未连接或未在放映状态", LogHelper.LogType.Warning); + return false; + } + + if (!Marshal.IsComObject(PPTApplication)) + { + LogHelper.WriteLogToFile("ComPPTManager: PPT应用程序COM对象无效", LogHelper.LogType.Warning); + return false; + } + + slideShowWindows = PPTApplication.SlideShowWindows; + if (slideShowWindows != null) + { + dynamic ssw = slideShowWindows; + slideShowWindow = ssw[1]; + if (slideShowWindow == null) + { + LogHelper.WriteLogToFile("ComPPTManager: 幻灯片放映窗口为空", LogHelper.LogType.Warning); + return false; + } + + try + { + dynamic sswObj = slideShowWindow; + slideNavigation = sswObj.SlideNavigation; + if (slideNavigation != null) + { + dynamic sn = slideNavigation; + sn.Visible = true; + LogHelper.WriteLogToFile("ComPPTManager: 成功显示幻灯片导航(PowerPoint模式)", LogHelper.LogType.Event); + return true; + } + + LogHelper.WriteLogToFile("ComPPTManager: SlideNavigation对象为空,可能是WPS不支持此功能", LogHelper.LogType.Warning); + return false; + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x80020006) + { + LogHelper.WriteLogToFile("ComPPTManager: WPS不支持SlideNavigation功能", LogHelper.LogType.Warning); + return false; + } + throw; + } + } + return false; + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x8001010E || hr == 0x80004005) + { + DisconnectFromPPT(); + } + LogHelper.WriteLogToFile($"ComPPTManager: 显示幻灯片导航COM异常: {comEx.Message} (HRESULT: 0x{hr:X8})", LogHelper.LogType.Error); + return false; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"ComPPTManager: 显示幻灯片导航失败: {ex}", LogHelper.LogType.Error); + return false; + } + finally + { + SafeReleaseComObject(slideNavigation); + SafeReleaseComObject(slideShowWindow); + SafeReleaseComObject(slideShowWindows); + } + } + #endregion + + #region Dispose + public void Dispose() + { + if (!_disposed) + { + StopMonitoring(); + + _connectionCheckTimer?.Dispose(); + _slideShowStateCheckTimer?.Dispose(); + + _disposed = true; + } + } + #endregion + } +} + + diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml index 95032d06..1e58b0dc 100644 --- a/Ink Canvas/MainWindow.xaml +++ b/Ink Canvas/MainWindow.xaml @@ -1456,12 +1456,21 @@ - + + + + + diff --git a/Ink Canvas/MainWindow_cs/MW_Settings.cs b/Ink Canvas/MainWindow_cs/MW_Settings.cs index c696af39..72d81493 100644 --- a/Ink Canvas/MainWindow_cs/MW_Settings.cs +++ b/Ink Canvas/MainWindow_cs/MW_Settings.cs @@ -140,6 +140,23 @@ namespace Ink_Canvas } } + private void ToggleSwitchUseRotPptLink_Toggled(object sender, RoutedEventArgs e) + { + if (!isLoaded) return; + + Settings.PowerPointSettings.UseRotPptLink = ToggleSwitchUseRotPptLink.IsOn; + SaveSettingsToFile(); + + try + { + InitializePPTManagers(); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"切换 PPT 联动架构失败: {ex}", LogHelper.LogType.Error); + } + } + private void ToggleSwitchShowCanvasAtNewSlideShow_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 4af1f250..0f3de7e1 100644 --- a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs +++ b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs @@ -469,6 +469,11 @@ namespace Ink_Canvas ToggleSwitchShowCanvasAtNewSlideShow.IsOn = Settings.PowerPointSettings.IsShowCanvasAtNewSlideShow; + if (ToggleSwitchUseRotPptLink != null) + { + ToggleSwitchUseRotPptLink.IsOn = Settings.PowerPointSettings.UseRotPptLink; + } + ToggleSwitchEnableTwoFingerGestureInPresentationMode.IsOn = Settings.PowerPointSettings.IsEnableTwoFingerGestureInPresentationMode; diff --git a/Ink Canvas/Resources/Settings.cs b/Ink Canvas/Resources/Settings.cs index 2fbedc70..cc34cf62 100644 --- a/Ink Canvas/Resources/Settings.cs +++ b/Ink Canvas/Resources/Settings.cs @@ -340,6 +340,8 @@ namespace Ink_Canvas public bool EnablePPTTimeCapsule { get; set; } = true; [JsonProperty("pptTimeCapsulePosition")] public int PPTTimeCapsulePosition { get; set; } = 1; + [JsonProperty("useRotPptLink")] + public bool UseRotPptLink { get; set; } = false; } public class Automation