From 4913019c5c6d19477347747af8038f82babaefb9 Mon Sep 17 00:00:00 2001 From: CJKmkp <2564608840@qq.com> Date: Tue, 29 Jul 2025 01:15:32 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96PPT=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ink Canvas/Helpers/PPTInkManager.cs | 397 +++ Ink Canvas/Helpers/PPTManager.cs | 1334 ++++++++++ Ink Canvas/Helpers/PPTUIManager.cs | 435 ++++ Ink Canvas/MainWindow.xaml.cs | 16 +- .../MainWindow_cs/MW_FloatingBarIcons.cs | 12 +- Ink Canvas/MainWindow_cs/MW_PPT.cs | 2260 +++++------------ .../MainWindow_cs/MW_Save&OpenStrokes.cs | 39 +- Ink Canvas/MainWindow_cs/MW_Settings.cs | 101 +- Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs | 4 +- Ink Canvas/MainWindow_cs/MW_Timer.cs | 5 +- 10 files changed, 2901 insertions(+), 1702 deletions(-) create mode 100644 Ink Canvas/Helpers/PPTInkManager.cs create mode 100644 Ink Canvas/Helpers/PPTManager.cs create mode 100644 Ink Canvas/Helpers/PPTUIManager.cs diff --git a/Ink Canvas/Helpers/PPTInkManager.cs b/Ink Canvas/Helpers/PPTInkManager.cs new file mode 100644 index 00000000..549ab5ab --- /dev/null +++ b/Ink Canvas/Helpers/PPTInkManager.cs @@ -0,0 +1,397 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography; +using System.Text; +using System.Windows.Ink; +using System.Windows.Threading; +using Microsoft.Office.Interop.PowerPoint; + +namespace Ink_Canvas.Helpers +{ + /// + /// PPT墨迹管理器 - 负责PPT中墨迹的保存、加载和同步 + /// + public class PPTInkManager : IDisposable + { + #region Properties + public bool IsAutoSaveEnabled { get; set; } = true; + public string AutoSaveLocation { get; set; } = ""; + public StrokeCollection CurrentStrokes { get; private set; } = new StrokeCollection(); + #endregion + + #region Private Fields + private MemoryStream[] _memoryStreams; + private int _maxSlides = 100; + private string _currentPresentationId = ""; + private readonly object _lockObject = new object(); + private bool _disposed = false; + + // 墨迹锁定机制,防止翻页时的墨迹冲突 + private DateTime _inkLockUntil = DateTime.MinValue; + private int _lockedSlideIndex = -1; + private const int InkLockMilliseconds = 500; + #endregion + + #region Constructor + public PPTInkManager() + { + InitializeMemoryStreams(); + } + + private void InitializeMemoryStreams() + { + _memoryStreams = new MemoryStream[_maxSlides + 2]; + } + #endregion + + #region Public Methods + /// + /// 初始化新的演示文稿 + /// + public void InitializePresentation(Presentation presentation) + { + if (presentation == null) return; + + lock (_lockObject) + { + try + { + // 生成演示文稿唯一标识符 + _currentPresentationId = GeneratePresentationId(presentation); + + // 重新初始化内存流数组 + var slideCount = presentation.Slides.Count; + _memoryStreams = new MemoryStream[slideCount + 2]; + + // 如果启用自动保存,尝试加载已保存的墨迹 + if (IsAutoSaveEnabled && !string.IsNullOrEmpty(AutoSaveLocation)) + { + LoadSavedStrokes(); + } + + LogHelper.WriteLogToFile($"已初始化演示文稿墨迹管理: {presentation.Name}, 幻灯片数量: {slideCount}", LogHelper.LogType.Trace); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"初始化演示文稿墨迹管理失败: {ex}", LogHelper.LogType.Error); + } + } + } + + /// + /// 保存当前页面的墨迹 + /// + public void SaveCurrentSlideStrokes(int slideIndex, StrokeCollection strokes) + { + if (slideIndex <= 0 || strokes == null) return; + + lock (_lockObject) + { + try + { + // 检查墨迹锁定 + if (!CanWriteInk(slideIndex)) + { + LogHelper.WriteLogToFile($"墨迹写入被锁定,当前页:{slideIndex},锁定页:{_lockedSlideIndex}", LogHelper.LogType.Warning); + return; + } + + if (slideIndex < _memoryStreams.Length) + { + var ms = new MemoryStream(); + strokes.Save(ms); + ms.Position = 0; + + // 释放旧的内存流 + _memoryStreams[slideIndex]?.Dispose(); + _memoryStreams[slideIndex] = ms; + + LogHelper.WriteLogToFile($"已保存第{slideIndex}页墨迹,大小: {ms.Length} bytes", LogHelper.LogType.Trace); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"保存第{slideIndex}页墨迹失败: {ex}", LogHelper.LogType.Error); + } + } + } + + /// + /// 加载指定页面的墨迹 + /// + public StrokeCollection LoadSlideStrokes(int slideIndex) + { + if (slideIndex <= 0) return new StrokeCollection(); + + lock (_lockObject) + { + try + { + if (slideIndex < _memoryStreams.Length && _memoryStreams[slideIndex] != null && _memoryStreams[slideIndex].Length > 0) + { + _memoryStreams[slideIndex].Position = 0; + var strokes = new StrokeCollection(_memoryStreams[slideIndex]); + LogHelper.WriteLogToFile($"已加载第{slideIndex}页墨迹,笔画数量: {strokes.Count}", LogHelper.LogType.Trace); + return strokes; + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"加载第{slideIndex}页墨迹失败: {ex}", LogHelper.LogType.Error); + } + } + + return new StrokeCollection(); + } + + /// + /// 切换到指定页面并加载墨迹 + /// + public StrokeCollection SwitchToSlide(int slideIndex, StrokeCollection currentStrokes = null) + { + lock (_lockObject) + { + try + { + // 如果有当前墨迹,先保存 + if (currentStrokes != null && currentStrokes.Count > 0) + { + SaveCurrentSlideStrokes(_lockedSlideIndex > 0 ? _lockedSlideIndex : slideIndex, currentStrokes); + } + + // 设置墨迹锁定 + LockInkForSlide(slideIndex); + + // 加载新页面的墨迹 + return LoadSlideStrokes(slideIndex); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"切换到第{slideIndex}页失败: {ex}", LogHelper.LogType.Error); + return new StrokeCollection(); + } + } + } + + /// + /// 保存所有墨迹到文件 + /// + public void SaveAllStrokesToFile(Presentation presentation) + { + if (!IsAutoSaveEnabled || string.IsNullOrEmpty(AutoSaveLocation) || presentation == null) return; + + lock (_lockObject) + { + try + { + var folderPath = GetPresentationFolderPath(); + if (!Directory.Exists(folderPath)) + { + Directory.CreateDirectory(folderPath); + } + + // 保存位置信息 + try + { + File.WriteAllText(Path.Combine(folderPath, "Position"), _lockedSlideIndex.ToString()); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"保存位置信息失败: {ex}", LogHelper.LogType.Error); + } + + // 保存所有页面的墨迹 + int savedCount = 0; + for (int i = 1; i <= presentation.Slides.Count && i < _memoryStreams.Length; i++) + { + if (_memoryStreams[i] != null) + { + try + { + if (_memoryStreams[i].Length > 8) + { + var srcBuf = new byte[_memoryStreams[i].Length]; + _memoryStreams[i].Position = 0; + var byteLength = _memoryStreams[i].Read(srcBuf, 0, srcBuf.Length); + + var filePath = Path.Combine(folderPath, i.ToString("0000") + ".icstk"); + File.WriteAllBytes(filePath, srcBuf); + savedCount++; + + LogHelper.WriteLogToFile($"已保存第{i}页墨迹,大小: {byteLength} bytes", LogHelper.LogType.Trace); + } + else + { + // 删除空的墨迹文件 + var filePath = Path.Combine(folderPath, i.ToString("0000") + ".icstk"); + if (File.Exists(filePath)) + { + File.Delete(filePath); + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"保存第{i}页墨迹失败: {ex}", LogHelper.LogType.Error); + } + } + } + + LogHelper.WriteLogToFile($"已保存{savedCount}页墨迹到文件", LogHelper.LogType.Event); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"保存墨迹到文件失败: {ex}", LogHelper.LogType.Error); + } + } + } + + /// + /// 从文件加载已保存的墨迹 + /// + public void LoadSavedStrokes() + { + if (!IsAutoSaveEnabled || string.IsNullOrEmpty(AutoSaveLocation)) return; + + lock (_lockObject) + { + try + { + var folderPath = GetPresentationFolderPath(); + if (!Directory.Exists(folderPath)) return; + + var files = new DirectoryInfo(folderPath).GetFiles("*.icstk"); + int loadedCount = 0; + + foreach (var file in files) + { + try + { + if (int.TryParse(Path.GetFileNameWithoutExtension(file.Name), out int slideIndex)) + { + if (slideIndex > 0 && slideIndex < _memoryStreams.Length) + { + var fileBytes = File.ReadAllBytes(file.FullName); + _memoryStreams[slideIndex] = new MemoryStream(fileBytes); + _memoryStreams[slideIndex].Position = 0; + loadedCount++; + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"加载墨迹文件{file.Name}失败: {ex}", LogHelper.LogType.Error); + } + } + + LogHelper.WriteLogToFile($"已从文件加载{loadedCount}页墨迹", LogHelper.LogType.Event); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"从文件加载墨迹失败: {ex}", LogHelper.LogType.Error); + } + } + } + + /// + /// 清除所有墨迹 + /// + public void ClearAllStrokes() + { + lock (_lockObject) + { + try + { + for (int i = 0; i < _memoryStreams.Length; i++) + { + _memoryStreams[i]?.Dispose(); + _memoryStreams[i] = null; + } + + CurrentStrokes.Clear(); + LogHelper.WriteLogToFile("已清除所有墨迹", LogHelper.LogType.Trace); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"清除墨迹失败: {ex}", LogHelper.LogType.Error); + } + } + } + + /// + /// 翻页后锁定墨迹写入 + /// + public void LockInkForSlide(int slideIndex) + { + _inkLockUntil = DateTime.Now.AddMilliseconds(InkLockMilliseconds); + _lockedSlideIndex = slideIndex; + } + + /// + /// 检查是否可以写入墨迹 + /// + public bool CanWriteInk(int currentSlideIndex) + { + if (DateTime.Now < _inkLockUntil) return false; + if (currentSlideIndex != _lockedSlideIndex && _lockedSlideIndex > 0) return false; + return true; + } + #endregion + + #region Private Methods + private string GeneratePresentationId(Presentation presentation) + { + try + { + var presentationPath = presentation.FullName; + var fileHash = GetFileHash(presentationPath); + return $"{presentation.Name}_{presentation.Slides.Count}_{fileHash}"; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"生成演示文稿ID失败: {ex}", LogHelper.LogType.Error); + return $"unknown_{DateTime.Now.Ticks}"; + } + } + + private string GetFileHash(string filePath) + { + try + { + if (string.IsNullOrEmpty(filePath)) return "unknown"; + + using (var md5 = MD5.Create()) + { + byte[] hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(filePath)); + return BitConverter.ToString(hashBytes).Replace("-", "").Substring(0, 8); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"计算文件哈希值失败: {ex}", LogHelper.LogType.Error); + return "error"; + } + } + + private string GetPresentationFolderPath() + { + return Path.Combine(AutoSaveLocation, "Auto Saved - Presentations", _currentPresentationId); + } + #endregion + + #region Dispose + public void Dispose() + { + if (!_disposed) + { + lock (_lockObject) + { + ClearAllStrokes(); + } + _disposed = true; + } + } + #endregion + } +} diff --git a/Ink Canvas/Helpers/PPTManager.cs b/Ink Canvas/Helpers/PPTManager.cs new file mode 100644 index 00000000..0e511191 --- /dev/null +++ b/Ink Canvas/Helpers/PPTManager.cs @@ -0,0 +1,1334 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Timers; +using System.Windows; +using System.Windows.Threading; +using Microsoft.Office.Core; +using Microsoft.Office.Interop.PowerPoint; +using Application = System.Windows.Application; +using Timer = System.Timers.Timer; + +namespace Ink_Canvas.Helpers +{ + /// + /// PPT联动管理器 - 统一管理PPT和WPS的连接、事件处理和进程管理 + /// + public class PPTManager : 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; + #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 => PPTApplication != null; + public bool IsInSlideShow + { + get + { + try + { + if (PPTApplication == null || !Marshal.IsComObject(PPTApplication)) return false; + return PPTApplication.SlideShowWindows?.Count > 0; + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x8001010E || hr == 0x80004005) + { + // COM对象已失效,触发断开连接 + DisconnectFromPPT(); + } + return false; + } + catch + { + return false; + } + } + } + public bool IsSupportWPS { get; set; } = false; + #endregion + + #region Private Fields + private Timer _connectionCheckTimer; + private Timer _wpsProcessCheckTimer; + private Process _wpsProcess; + private bool _hasWpsProcessId; + private DateTime _wpsProcessRecordTime = DateTime.MinValue; + private int _wpsProcessCheckCount; + private WpsWindowInfo _lastForegroundWpsWindow; + private DateTime _lastWindowCheckTime = DateTime.MinValue; + private readonly object _lockObject = new object(); + private bool _disposed = false; + #endregion + + #region Constructor & Initialization + public PPTManager() + { + InitializeConnectionTimer(); + } + + private void InitializeConnectionTimer() + { + _connectionCheckTimer = new Timer(500); + _connectionCheckTimer.Elapsed += OnConnectionCheckTimerElapsed; + _connectionCheckTimer.AutoReset = true; + } + + public void StartMonitoring() + { + if (!_disposed) + { + _connectionCheckTimer?.Start(); + LogHelper.WriteLogToFile("PPT监控已启动", LogHelper.LogType.Trace); + } + } + + public void StopMonitoring() + { + _connectionCheckTimer?.Stop(); + DisconnectFromPPT(); + LogHelper.WriteLogToFile("PPT监控已停止", LogHelper.LogType.Trace); + } + #endregion + + #region Connection Management + private void OnConnectionCheckTimerElapsed(object sender, ElapsedEventArgs e) + { + try + { + CheckAndConnectToPPT(); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"PPT连接检查失败: {ex}", LogHelper.LogType.Error); + } + } + + private void CheckAndConnectToPPT() + { + lock (_lockObject) + { + try + { + // 尝试连接到PowerPoint + var pptApp = TryConnectToPowerPoint(); + if (pptApp == null && IsSupportWPS) + { + // 如果PowerPoint连接失败且支持WPS,尝试连接WPS + pptApp = TryConnectToWPS(); + } + + if (pptApp != null && PPTApplication == null) + { + ConnectToPPT(pptApp); + } + else if (pptApp == null && PPTApplication != null) + { + DisconnectFromPPT(); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"PPT连接检查异常: {ex}", LogHelper.LogType.Error); + if (PPTApplication != null) + { + DisconnectFromPPT(); + } + } + } + } + + private Microsoft.Office.Interop.PowerPoint.Application TryConnectToPowerPoint() + { + try + { + var pptApp = (Microsoft.Office.Interop.PowerPoint.Application)Marshal.GetActiveObject("PowerPoint.Application"); + + // 验证COM对象是否有效 + if (pptApp != null && Marshal.IsComObject(pptApp)) + { + // 尝试访问一个简单的属性来验证连接 + var _ = pptApp.Name; + return pptApp; + } + return null; + } + catch (COMException ex) + { + // 忽略常见的COM连接错误 + var hr = (uint)ex.HResult; + if (hr != 0x800401E3 && hr != 0x80004005 && hr != 0x800706B5 && hr != 0x8001010E) + { + LogHelper.WriteLogToFile($"连接PowerPoint失败: {ex}", LogHelper.LogType.Warning); + } + return null; + } + catch (InvalidCastException) + { + // COM对象类型转换失败 + return null; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"连接PowerPoint时发生意外错误: {ex}", LogHelper.LogType.Warning); + return null; + } + } + + private Microsoft.Office.Interop.PowerPoint.Application TryConnectToWPS() + { + try + { + var wpsApp = (Microsoft.Office.Interop.PowerPoint.Application)Marshal.GetActiveObject("kwpp.Application"); + + // 验证COM对象是否有效 + if (wpsApp != null && Marshal.IsComObject(wpsApp)) + { + // 尝试访问一个简单的属性来验证连接 + var _ = wpsApp.Name; + return wpsApp; + } + return null; + } + catch (COMException ex) + { + var hr = (uint)ex.HResult; + if (hr != 0x800401E3 && hr != 0x80004005 && hr != 0x800706B5 && hr != 0x8001010E) + { + LogHelper.WriteLogToFile($"连接WPS失败: {ex}", LogHelper.LogType.Warning); + } + return null; + } + catch (InvalidCastException) + { + // COM对象类型转换失败 + return null; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"连接WPS时发生意外错误: {ex}", LogHelper.LogType.Warning); + 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("PPT事件注册成功", LogHelper.LogType.Trace); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"PPT事件注册失败: {ex}", LogHelper.LogType.Error); + throw; // 重新抛出异常,让外层处理 + } + }, DispatcherPriority.Normal, System.Threading.CancellationToken.None, TimeSpan.FromSeconds(2)); + + // 获取当前演示文稿信息 + UpdateCurrentPresentationInfo(); + + // 停止连接检查定时器 + _connectionCheckTimer?.Stop(); + + // 触发连接成功事件 + PPTConnectionChanged?.Invoke(true); + + LogHelper.WriteLogToFile("成功连接到PPT应用程序", LogHelper.LogType.Event); + + // 如果已经在放映状态,立即触发放映开始事件 + if (IsInSlideShow) + { + OnSlideShowBegin(PPTApplication.SlideShowWindows[1]); + } + else if (CurrentPresentation != null) + { + OnPresentationOpen(CurrentPresentation); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"连接PPT应用程序失败: {ex}", LogHelper.LogType.Error); + PPTApplication = null; + } + } + + private void DisconnectFromPPT() + { + try + { + if (PPTApplication != null) + { + // 取消事件注册 - 使用更安全的方式 + try + { + // 检查COM对象是否仍然有效 + 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 (COMException comEx) + { + // COM对象已经被释放或在错误的线程中,忽略这些错误 + var hr = (uint)comEx.HResult; + if (hr != 0x8001010E && hr != 0x80004005 && hr != 0x800706B5) + { + LogHelper.WriteLogToFile($"取消PPT事件注册COM异常: {comEx}", LogHelper.LogType.Warning); + } + } + catch (InvalidCastException) + { + // COM对象类型转换失败,通常是因为对象已经被释放 + LogHelper.WriteLogToFile("PPT COM对象已被释放,跳过事件注册取消", LogHelper.LogType.Trace); + } + }, DispatcherPriority.Normal, System.Threading.CancellationToken.None, TimeSpan.FromSeconds(1)); + } + } + catch (Exception ex) + { + // 记录但不抛出异常,确保清理过程能够继续 + LogHelper.WriteLogToFile($"取消PPT事件注册失败: {ex.GetType().Name} - {ex.Message}", LogHelper.LogType.Warning); + } + + // 清理引用 + PPTApplication = null; + CurrentPresentation = null; + CurrentSlides = null; + CurrentSlide = null; + SlidesCount = 0; + + // 重新启动连接检查定时器 + _connectionCheckTimer?.Start(); + + // 触发连接断开事件 + PPTConnectionChanged?.Invoke(false); + + LogHelper.WriteLogToFile("已断开PPT连接", LogHelper.LogType.Event); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"断开PPT连接失败: {ex}", LogHelper.LogType.Error); + } + } + + private void UpdateCurrentPresentationInfo() + { + try + { + if (PPTApplication != null && Marshal.IsComObject(PPTApplication)) + { + // 检查是否有活动的演示文稿 + try + { + var activePresentation = PPTApplication.ActivePresentation; + if (activePresentation != null) + { + CurrentPresentation = activePresentation; + CurrentSlides = CurrentPresentation.Slides; + SlidesCount = CurrentSlides.Count; + + // 获取当前幻灯片 + try + { + if (IsInSlideShow && PPTApplication.SlideShowWindows.Count > 0) + { + CurrentSlide = PPTApplication.SlideShowWindows[1].View.Slide; + } + else if (PPTApplication.ActiveWindow?.Selection?.SlideRange?.SlideNumber > 0) + { + CurrentSlide = CurrentSlides[PPTApplication.ActiveWindow.Selection.SlideRange.SlideNumber]; + } + else if (SlidesCount > 0) + { + // 如果获取失败,使用第一张幻灯片 + CurrentSlide = CurrentSlides[1]; + } + } + catch (COMException comEx) + { + // COM异常,尝试使用第一张幻灯片 + var hr = (uint)comEx.HResult; + if (hr != 0x8001010E && hr != 0x80004005) + { + LogHelper.WriteLogToFile($"获取当前幻灯片失败: {comEx.Message}", LogHelper.LogType.Warning); + } + + if (SlidesCount > 0) + { + CurrentSlide = CurrentSlides[1]; + } + } + } + else + { + // 没有活动演示文稿,清理状态 + CurrentPresentation = null; + CurrentSlides = null; + CurrentSlide = null; + SlidesCount = 0; + } + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x8001010E || hr == 0x80004005) + { + // 常见的COM错误,可能是没有活动演示文稿 + CurrentPresentation = null; + CurrentSlides = null; + CurrentSlide = null; + SlidesCount = 0; + } + else + { + LogHelper.WriteLogToFile($"访问活动演示文稿失败: {comEx}", LogHelper.LogType.Warning); + } + } + } + else + { + // PPT应用程序无效,清理状态 + CurrentPresentation = null; + CurrentSlides = null; + CurrentSlide = null; + SlidesCount = 0; + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"更新演示文稿信息失败: {ex}", LogHelper.LogType.Error); + // 发生异常时清理状态 + CurrentPresentation = null; + CurrentSlides = null; + CurrentSlide = null; + SlidesCount = 0; + } + } + #endregion + + #region Event Handlers + private void OnPresentationOpen(Presentation pres) + { + try + { + UpdateCurrentPresentationInfo(); + PresentationOpen?.Invoke(pres); + LogHelper.WriteLogToFile($"演示文稿已打开: {pres?.Name}", LogHelper.LogType.Event); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"处理演示文稿打开事件失败: {ex}", LogHelper.LogType.Error); + } + } + + private void OnPresentationClose(Presentation pres) + { + try + { + PresentationClose?.Invoke(pres); + LogHelper.WriteLogToFile($"演示文稿已关闭: {pres?.Name}", LogHelper.LogType.Event); + + // 重新启动连接检查 + _connectionCheckTimer?.Start(); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"处理演示文稿关闭事件失败: {ex}", LogHelper.LogType.Error); + } + } + + private void OnSlideShowBegin(SlideShowWindow wn) + { + try + { + UpdateCurrentPresentationInfo(); + SlideShowBegin?.Invoke(wn); + LogHelper.WriteLogToFile("幻灯片放映开始", LogHelper.LogType.Event); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"处理幻灯片放映开始事件失败: {ex}", LogHelper.LogType.Error); + } + } + + private void OnSlideShowNextSlide(SlideShowWindow wn) + { + try + { + UpdateCurrentPresentationInfo(); + SlideShowNextSlide?.Invoke(wn); + LogHelper.WriteLogToFile($"幻灯片切换到第{wn?.View?.CurrentShowPosition}页", LogHelper.LogType.Trace); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"处理幻灯片切换事件失败: {ex}", LogHelper.LogType.Error); + } + } + + private void OnSlideShowEnd(Presentation pres) + { + try + { + // 记录WPS进程用于后续管理 + if (IsSupportWPS && PPTApplication != null) + { + RecordWpsProcessForManagement(); + } + + SlideShowEnd?.Invoke(pres); + LogHelper.WriteLogToFile("幻灯片放映结束", LogHelper.LogType.Event); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"处理幻灯片放映结束事件失败: {ex}", LogHelper.LogType.Error); + } + } + #endregion + + #region Public Methods + public bool TryNavigateToSlide(int slideNumber) + { + try + { + if (!IsConnected || !IsInSlideShow || PPTApplication == null) return false; + if (!Marshal.IsComObject(PPTApplication)) return false; + + var slideShowWindow = PPTApplication.SlideShowWindows[1]; + if (slideShowWindow?.View != null) + { + slideShowWindow.View.GotoSlide(slideNumber); + return true; + } + return false; + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x8001010E || hr == 0x80004005) + { + // COM对象已失效,触发断开连接 + DisconnectFromPPT(); + } + LogHelper.WriteLogToFile($"跳转到幻灯片{slideNumber}失败: {comEx.Message}", LogHelper.LogType.Error); + return false; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"跳转到幻灯片{slideNumber}失败: {ex}", LogHelper.LogType.Error); + return false; + } + } + + public bool TryNavigateNext() + { + try + { + if (!IsConnected || !IsInSlideShow || PPTApplication == null) return false; + if (!Marshal.IsComObject(PPTApplication)) return false; + + var slideShowWindow = PPTApplication.SlideShowWindows[1]; + if (slideShowWindow?.View != null) + { + slideShowWindow.Activate(); + slideShowWindow.View.Next(); + return true; + } + return false; + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x8001010E || hr == 0x80004005) + { + // COM对象已失效,触发断开连接 + DisconnectFromPPT(); + } + LogHelper.WriteLogToFile($"切换到下一页失败: {comEx.Message}", LogHelper.LogType.Error); + return false; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"切换到下一页失败: {ex}", LogHelper.LogType.Error); + return false; + } + } + + public bool TryNavigatePrevious() + { + try + { + if (!IsConnected || !IsInSlideShow || PPTApplication == null) return false; + if (!Marshal.IsComObject(PPTApplication)) return false; + + var slideShowWindow = PPTApplication.SlideShowWindows[1]; + if (slideShowWindow?.View != null) + { + slideShowWindow.Activate(); + slideShowWindow.View.Previous(); + return true; + } + return false; + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x8001010E || hr == 0x80004005) + { + // COM对象已失效,触发断开连接 + DisconnectFromPPT(); + } + LogHelper.WriteLogToFile($"切换到上一页失败: {comEx.Message}", LogHelper.LogType.Error); + return false; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"切换到上一页失败: {ex}", LogHelper.LogType.Error); + return false; + } + } + + public bool TryEndSlideShow() + { + try + { + if (!IsConnected || !IsInSlideShow || PPTApplication == null) return false; + if (!Marshal.IsComObject(PPTApplication)) return false; + + var slideShowWindow = PPTApplication.SlideShowWindows[1]; + if (slideShowWindow?.View != null) + { + slideShowWindow.View.Exit(); + return true; + } + return false; + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x8001010E || hr == 0x80004005) + { + // COM对象已失效,触发断开连接 + DisconnectFromPPT(); + } + LogHelper.WriteLogToFile($"结束幻灯片放映失败: {comEx.Message}", LogHelper.LogType.Error); + return false; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"结束幻灯片放映失败: {ex}", LogHelper.LogType.Error); + return false; + } + } + + 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) + { + // COM对象已失效,触发断开连接 + DisconnectFromPPT(); + } + LogHelper.WriteLogToFile($"开始幻灯片放映失败: {comEx.Message}", LogHelper.LogType.Error); + return false; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"开始幻灯片放映失败: {ex}", LogHelper.LogType.Error); + return false; + } + } + + public int GetCurrentSlideNumber() + { + try + { + if (!IsConnected || !IsInSlideShow || PPTApplication == null) return 0; + if (!Marshal.IsComObject(PPTApplication)) return 0; + + return PPTApplication.SlideShowWindows[1]?.View?.CurrentShowPosition ?? 0; + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x8001010E || hr == 0x80004005) + { + // COM对象已失效,触发断开连接 + DisconnectFromPPT(); + } + LogHelper.WriteLogToFile($"获取当前幻灯片编号失败: {comEx.Message}", LogHelper.LogType.Warning); + return 0; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"获取当前幻灯片编号失败: {ex}", LogHelper.LogType.Error); + return 0; + } + } + + 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) + { + // COM对象已失效,触发断开连接 + DisconnectFromPPT(); + } + LogHelper.WriteLogToFile($"获取演示文稿名称失败: {comEx.Message}", LogHelper.LogType.Warning); + return ""; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"获取演示文稿名称失败: {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) + { + // COM对象已失效,触发断开连接 + DisconnectFromPPT(); + } + LogHelper.WriteLogToFile($"获取演示文稿路径失败: {comEx.Message}", LogHelper.LogType.Warning); + return ""; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"获取演示文稿路径失败: {ex}", LogHelper.LogType.Error); + return ""; + } + } + + public bool TryShowSlideNavigation() + { + try + { + LogHelper.WriteLogToFile($"尝试显示幻灯片导航 - 连接状态: {IsConnected}, 放映状态: {IsInSlideShow}", LogHelper.LogType.Trace); + + if (!IsConnected || !IsInSlideShow || PPTApplication == null) + { + LogHelper.WriteLogToFile("PPT未连接或未在放映状态", LogHelper.LogType.Warning); + return false; + } + + if (!Marshal.IsComObject(PPTApplication)) + { + LogHelper.WriteLogToFile("PPT应用程序COM对象无效", LogHelper.LogType.Warning); + return false; + } + + var slideShowWindow = PPTApplication.SlideShowWindows[1]; + if (slideShowWindow == null) + { + LogHelper.WriteLogToFile("幻灯片放映窗口为空", LogHelper.LogType.Warning); + return false; + } + + // 检查是否为WPS,WPS可能不支持SlideNavigation + try + { + if (slideShowWindow.SlideNavigation != null) + { + slideShowWindow.SlideNavigation.Visible = true; + LogHelper.WriteLogToFile("成功显示幻灯片导航(PowerPoint模式)", LogHelper.LogType.Event); + return true; + } + else + { + LogHelper.WriteLogToFile("SlideNavigation对象为空,可能是WPS不支持此功能", LogHelper.LogType.Warning); + return false; + } + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + // 0x80020006: 未知名称 - WPS可能不支持SlideNavigation + if (hr == 0x80020006) + { + LogHelper.WriteLogToFile("WPS不支持SlideNavigation功能", LogHelper.LogType.Warning); + return false; + } + throw; // 重新抛出其他COM异常 + } + } + catch (COMException comEx) + { + var hr = (uint)comEx.HResult; + if (hr == 0x8001010E || hr == 0x80004005) + { + // COM对象已失效,触发断开连接 + DisconnectFromPPT(); + } + LogHelper.WriteLogToFile($"显示幻灯片导航COM异常: {comEx.Message} (HRESULT: 0x{hr:X8})", LogHelper.LogType.Error); + return false; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"显示幻灯片导航失败: {ex}", LogHelper.LogType.Error); + return false; + } + } + #endregion + + #region WPS Process Management + private void RecordWpsProcessForManagement() + { + if (!IsSupportWPS || PPTApplication == null) return; + + try + { + Process wpsProcess = null; + + // 方法1:通过应用程序路径检测 + if (PPTApplication.Path.Contains("Kingsoft\\WPS Office\\") || + PPTApplication.Path.Contains("WPS Office\\")) + { + uint processId; + GetWindowThreadProcessId((IntPtr)PPTApplication.HWND, out processId); + wpsProcess = Process.GetProcessById((int)processId); + } + + // 方法2:通过前台窗口检测 + if (wpsProcess == null) + { + var foregroundWpsWindow = GetForegroundWpsWindow(); + if (foregroundWpsWindow != null) + { + wpsProcess = Process.GetProcessById((int)foregroundWpsWindow.ProcessId); + } + } + + // 方法3:通过进程名检测 + if (wpsProcess == null) + { + var wpsProcesses = GetWpsProcesses(); + if (wpsProcesses.Count > 0) + { + wpsProcess = wpsProcesses.First(); + } + } + + if (wpsProcess != null) + { + _wpsProcess = wpsProcess; + _hasWpsProcessId = true; + _wpsProcessRecordTime = DateTime.Now; + _wpsProcessCheckCount = 0; + LogHelper.WriteLogToFile($"成功记录 WPS 进程 ID: {wpsProcess.Id}", LogHelper.LogType.Trace); + + StartWpsProcessCheckTimer(); + } + else + { + LogHelper.WriteLogToFile("未能检测到WPS进程", LogHelper.LogType.Warning); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"记录WPS进程失败: {ex}", LogHelper.LogType.Error); + } + } + + private void StartWpsProcessCheckTimer() + { + if (!IsSupportWPS) return; + + if (_wpsProcessCheckTimer != null) + { + _wpsProcessCheckTimer.Stop(); + _wpsProcessCheckTimer.Dispose(); + } + + _wpsProcessCheckTimer = new Timer(500); + _wpsProcessCheckTimer.Elapsed += OnWpsProcessCheckTimerElapsed; + _wpsProcessCheckTimer.Start(); + LogHelper.WriteLogToFile("启动 WPS 进程检测定时器", LogHelper.LogType.Trace); + } + + private void OnWpsProcessCheckTimerElapsed(object sender, ElapsedEventArgs e) + { + if (!IsSupportWPS) + { + StopWpsProcessCheckTimer(); + return; + } + + try + { + if (_wpsProcess == null || !_hasWpsProcessId) + { + StopWpsProcessCheckTimer(); + return; + } + + _wpsProcess.Refresh(); + _wpsProcessCheckCount++; + + if (_wpsProcess.HasExited) + { + LogHelper.WriteLogToFile("WPS 进程已正常关闭", LogHelper.LogType.Trace); + StopWpsProcessCheckTimer(); + return; + } + + // 检查前台WPS窗口是否存在 + bool isForegroundWpsWindowActive = IsForegroundWpsWindowStillActive(); + + if (isForegroundWpsWindowActive) + { + if (_wpsProcessCheckCount % 10 == 0) + { + LogHelper.WriteLogToFile($"前台WPS窗口仍然存在,继续监控(已检查{_wpsProcessCheckCount}次)", LogHelper.LogType.Trace); + } + return; + } + + // 前台窗口已消失,检查是否需要结束进程 + LogHelper.WriteLogToFile("检测到前台WPS窗口已消失", LogHelper.LogType.Event); + + // 检查所有WPS文档是否已保存 + bool allSaved = CheckAllWpsDocumentsSaved(); + + if (!allSaved) + { + LogHelper.WriteLogToFile("检测到有未保存的WPS文档,跳过进程结束", LogHelper.LogType.Trace); + } + + // 结束WPS进程 + try + { + if (!_wpsProcess.HasExited) + { + _wpsProcess.Kill(); + LogHelper.WriteLogToFile("成功结束WPS进程", LogHelper.LogType.Event); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"结束WPS进程失败: {ex}", LogHelper.LogType.Error); + } + finally + { + StopWpsProcessCheckTimer(); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"WPS 进程检测失败: {ex}", LogHelper.LogType.Error); + StopWpsProcessCheckTimer(); + } + } + + private bool CheckAllWpsDocumentsSaved() + { + try + { + if (PPTApplication?.Presentations != null) + { + foreach (Presentation pres in PPTApplication.Presentations) + { + if (pres.Saved == MsoTriState.msoFalse) + { + return false; + } + } + } + return true; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"检查WPS文档保存状态失败: {ex}", LogHelper.LogType.Error); + return false; + } + } + + private void StopWpsProcessCheckTimer() + { + if (_wpsProcessCheckTimer != null) + { + _wpsProcessCheckTimer.Stop(); + _wpsProcessCheckTimer.Dispose(); + _wpsProcessCheckTimer = null; + } + + _wpsProcess = null; + _hasWpsProcessId = false; + _wpsProcessRecordTime = DateTime.MinValue; + _wpsProcessCheckCount = 0; + _lastForegroundWpsWindow = null; + _lastWindowCheckTime = DateTime.MinValue; + LogHelper.WriteLogToFile("停止 WPS 进程检测定时器", LogHelper.LogType.Trace); + } + #endregion + + #region WPS Window Detection + [DllImport("user32.dll")] + private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); + + [DllImport("user32.dll")] + private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); + + [DllImport("user32.dll")] + private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool IsWindowVisible(IntPtr hWnd); + + [DllImport("user32.dll")] + private static extern bool IsIconic(IntPtr hWnd); + + [DllImport("user32.dll")] + private static extern bool IsZoomed(IntPtr hWnd); + + [DllImport("user32.dll")] + private static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll")] + private static extern bool IsWindow(IntPtr hWnd); + + [DllImport("user32.dll")] + private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); + + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + public int Left; + public int Top; + public int Right; + public int Bottom; + + public int Width => Right - Left; + public int Height => Bottom - Top; + } + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + + private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); + + private class WpsWindowInfo + { + public IntPtr Handle { get; set; } + public string Title { get; set; } + public string ClassName { get; set; } + public bool IsVisible { get; set; } + public bool IsMinimized { get; set; } + public bool IsMaximized { get; set; } + public RECT Rect { get; set; } + public uint ProcessId { get; set; } + public string ProcessName { get; set; } + } + + private WpsWindowInfo GetForegroundWpsWindow() + { + try + { + var foregroundHwnd = GetForegroundWindow(); + if (foregroundHwnd != IntPtr.Zero && IsWindow(foregroundHwnd)) + { + var windowInfo = GetWindowInfo(foregroundHwnd); + if (IsWpsWindow(windowInfo)) + { + return windowInfo; + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"获取前台WPS窗口失败: {ex}", LogHelper.LogType.Error); + } + return null; + } + + private WpsWindowInfo GetWindowInfo(IntPtr hWnd) + { + var windowInfo = new WpsWindowInfo + { + Handle = hWnd, + IsVisible = IsWindowVisible(hWnd), + IsMinimized = IsIconic(hWnd), + IsMaximized = IsZoomed(hWnd) + }; + + // 获取窗口标题 + var windowTitle = new StringBuilder(256); + GetWindowText(hWnd, windowTitle, 256); + windowInfo.Title = windowTitle.ToString().Trim(); + + // 获取窗口类名 + var className = new StringBuilder(256); + GetClassName(hWnd, className, 256); + windowInfo.ClassName = className.ToString().Trim(); + + // 获取窗口位置 + GetWindowRect(hWnd, out RECT rect); + windowInfo.Rect = rect; + + // 获取进程ID + uint processId; + GetWindowThreadProcessId(hWnd, out processId); + windowInfo.ProcessId = processId; + + // 获取进程名 + windowInfo.ProcessName = ""; + try + { + var proc = Process.GetProcessById((int)processId); + windowInfo.ProcessName = proc.ProcessName.ToLower(); + } + catch { } + + return windowInfo; + } + + private bool IsWpsWindow(WpsWindowInfo windowInfo) + { + if (string.IsNullOrEmpty(windowInfo.Title) && string.IsNullOrEmpty(windowInfo.ClassName)) + return false; + + var title = windowInfo.Title.ToLower(); + var className = windowInfo.ClassName.ToLower(); + var processName = windowInfo.ProcessName ?? ""; + + // WPS相关关键词 + var wpsKeywords = new[] { "wps", "wpp", "kingsoft", "金山", "wps演示", "wps presentation", "wps office", "kingsoft office" }; + // 微软Office相关进程名 + var msOfficeProcess = new[] { "powerpnt", "excel", "word", "onenote", "outlook", "microsoftoffice", "office" }; + + // 只要进程名是微软Office,直接排除 + if (msOfficeProcess.Any(keyword => processName.Contains(keyword))) + return false; + + // 只要进程名是WPS/WPP/Kingsoft,直接通过 + if (wpsKeywords.Any(keyword => processName.Contains(keyword))) + return true; + + // 标题或类名包含WPS相关关键词 + bool hasWpsTitle = wpsKeywords.Any(keyword => title.Contains(keyword)); + bool hasWpsClass = wpsKeywords.Any(keyword => className.Contains(keyword)); + bool isWpsClass = className.Contains("wps") || className.Contains("kingsoft") || className.Contains("wpp"); + bool hasValidSize = (windowInfo.Rect.Right - windowInfo.Rect.Left) > 0 && (windowInfo.Rect.Bottom - windowInfo.Rect.Top) > 0; + + return (hasWpsTitle || hasWpsClass || isWpsClass) && hasValidSize; + } + + private List GetWpsProcesses() + { + var wpsProcesses = new List(); + try + { + var allProcesses = Process.GetProcesses(); + foreach (var process in allProcesses) + { + try + { + var pname = process.ProcessName.ToLower(); + if ((pname.Contains("wps") || pname.Contains("wpp") || pname.Contains("presentation")) + && !pname.Contains("powerpnt") + && !pname.Contains("office") + && !pname.Contains("onenote") + && !pname.Contains("excel") + && !pname.Contains("word") + && !pname.Contains("outlook") + && !pname.Contains("microsoft")) + { + wpsProcesses.Add(process); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"检查进程{process.ProcessName}失败: {ex}", LogHelper.LogType.Error); + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"获取WPS进程失败: {ex}", LogHelper.LogType.Error); + } + return wpsProcesses; + } + + private bool IsForegroundWpsWindowStillActive() + { + try + { + var currentTime = DateTime.Now; + var currentForegroundWindow = GetForegroundWpsWindow(); + + // 检查窗口状态是否发生变化 + if (_lastForegroundWpsWindow != null && currentForegroundWindow != null) + { + if (_lastForegroundWpsWindow.Handle != currentForegroundWindow.Handle || + _lastForegroundWpsWindow.Title != currentForegroundWindow.Title) + { + LogHelper.WriteLogToFile($"前台WPS窗口发生变化: {_lastForegroundWpsWindow.Title} -> {currentForegroundWindow.Title}", LogHelper.LogType.Trace); + } + } + else if (_lastForegroundWpsWindow == null && currentForegroundWindow != null) + { + LogHelper.WriteLogToFile($"检测到新的前台WPS窗口: {currentForegroundWindow.Title}", LogHelper.LogType.Trace); + } + else if (_lastForegroundWpsWindow != null && currentForegroundWindow == null) + { + LogHelper.WriteLogToFile($"前台WPS窗口已消失: {_lastForegroundWpsWindow.Title}", LogHelper.LogType.Trace); + } + + // 更新记录 + _lastForegroundWpsWindow = currentForegroundWindow; + _lastWindowCheckTime = currentTime; + + if (currentForegroundWindow != null) + { + if (IsWindow(currentForegroundWindow.Handle) && IsWindowVisible(currentForegroundWindow.Handle)) + { + return true; + } + } + + // 检查所有WPS进程的活跃窗口 + var wpsProcesses = GetWpsProcesses(); + foreach (var process in wpsProcesses) + { + var windows = GetWpsWindowsByProcess(process.Id); + if (windows.Any(w => w.IsVisible && !w.IsMinimized && w.Handle == GetForegroundWindow())) + { + return true; + } + } + + return false; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"检查前台WPS窗口状态失败: {ex}", LogHelper.LogType.Error); + return false; + } + } + + private List GetWpsWindowsByProcess(int processId) + { + var wpsWindows = new List(); + + try + { + EnumWindows((hWnd, lParam) => + { + try + { + if (!IsWindow(hWnd)) return true; + + uint windowProcessId; + GetWindowThreadProcessId(hWnd, out windowProcessId); + + if ((int)windowProcessId == processId) + { + var windowInfo = GetWindowInfo(hWnd); + if (IsWpsWindow(windowInfo)) + { + wpsWindows.Add(windowInfo); + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"枚举窗口时出错: {ex}", LogHelper.LogType.Error); + } + return true; + }, IntPtr.Zero); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"获取WPS窗口失败: {ex}", LogHelper.LogType.Error); + } + + return wpsWindows; + } + #endregion + + #region Dispose + public void Dispose() + { + if (!_disposed) + { + StopMonitoring(); + StopWpsProcessCheckTimer(); + + _connectionCheckTimer?.Dispose(); + _wpsProcessCheckTimer?.Dispose(); + + _disposed = true; + } + } + #endregion + } +} diff --git a/Ink Canvas/Helpers/PPTUIManager.cs b/Ink Canvas/Helpers/PPTUIManager.cs new file mode 100644 index 00000000..3bd1713d --- /dev/null +++ b/Ink Canvas/Helpers/PPTUIManager.cs @@ -0,0 +1,435 @@ +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Threading; + +namespace Ink_Canvas.Helpers +{ + /// + /// PPT UI管理器 - 统一管理PPT相关的UI更新和样式设置 + /// + public class PPTUIManager + { + #region Properties + public bool ShowPPTButton { get; set; } = true; + public int PPTButtonsDisplayOption { get; set; } = 2222; + public int PPTSButtonsOption { get; set; } = 221; + public int PPTBButtonsOption { get; set; } = 121; + public int PPTLSButtonPosition { get; set; } = 0; + public int PPTRSButtonPosition { get; set; } = 0; + public bool EnablePPTButtonPageClickable { get; set; } = true; + #endregion + + #region Private Fields + private readonly MainWindow _mainWindow; + private readonly Dispatcher _dispatcher; + #endregion + + #region Constructor + public PPTUIManager(MainWindow mainWindow) + { + _mainWindow = mainWindow ?? throw new ArgumentNullException(nameof(mainWindow)); + _dispatcher = _mainWindow.Dispatcher; + } + #endregion + + #region Public Methods + /// + /// 更新PPT连接状态UI + /// + public void UpdateConnectionStatus(bool isConnected) + { + _dispatcher.InvokeAsync(() => + { + try + { + if (isConnected) + { + _mainWindow.StackPanelPPTControls.Visibility = Visibility.Visible; + _mainWindow.BtnPPTSlideShow.Visibility = Visibility.Visible; + } + else + { + _mainWindow.StackPanelPPTControls.Visibility = Visibility.Collapsed; + _mainWindow.BtnPPTSlideShow.Visibility = Visibility.Collapsed; + _mainWindow.BtnPPTSlideShowEnd.Visibility = Visibility.Collapsed; + HideAllNavigationPanels(); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"更新PPT连接状态UI失败: {ex}", LogHelper.LogType.Error); + } + }); + } + + /// + /// 更新幻灯片放映状态UI + /// + public void UpdateSlideShowStatus(bool isInSlideShow, int currentSlide = 0, int totalSlides = 0) + { + _dispatcher.InvokeAsync(() => + { + try + { + if (isInSlideShow) + { + _mainWindow.BtnPPTSlideShow.Visibility = Visibility.Collapsed; + _mainWindow.BtnPPTSlideShowEnd.Visibility = Visibility.Visible; + + if (currentSlide > 0 && totalSlides > 0) + { + _mainWindow.PPTBtnPageNow.Text = currentSlide.ToString(); + _mainWindow.PPTBtnPageTotal.Text = $"/ {totalSlides}"; + } + + UpdateNavigationPanelsVisibility(); + UpdateNavigationButtonStyles(); + } + else + { + _mainWindow.BtnPPTSlideShow.Visibility = Visibility.Visible; + _mainWindow.BtnPPTSlideShowEnd.Visibility = Visibility.Collapsed; + HideAllNavigationPanels(); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"更新幻灯片放映状态UI失败: {ex}", LogHelper.LogType.Error); + } + }); + } + + /// + /// 更新当前页码显示 + /// + public void UpdateCurrentSlideNumber(int currentSlide, int totalSlides) + { + _dispatcher.InvokeAsync(() => + { + try + { + _mainWindow.PPTBtnPageNow.Text = currentSlide.ToString(); + _mainWindow.PPTBtnPageTotal.Text = $"/ {totalSlides}"; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"更新页码显示失败: {ex}", LogHelper.LogType.Error); + } + }); + } + + /// + /// 更新导航面板显示状态 + /// + public void UpdateNavigationPanelsVisibility() + { + _dispatcher.InvokeAsync(() => + { + try + { + // 检查是否应该显示PPT按钮 + bool shouldShowButtons = ShowPPTButton && _mainWindow.BtnPPTSlideShowEnd.Visibility == Visibility.Visible; + + if (!shouldShowButtons) + { + HideAllNavigationPanels(); + return; + } + + // 设置侧边按钮位置 + _mainWindow.LeftSidePanelForPPTNavigation.Margin = new Thickness(0, 0, 0, PPTLSButtonPosition * 2); + _mainWindow.RightSidePanelForPPTNavigation.Margin = new Thickness(0, 0, 0, PPTRSButtonPosition * 2); + + // 根据显示选项设置面板可见性 + var displayOption = PPTButtonsDisplayOption.ToString(); + if (displayOption.Length >= 4) + { + var options = displayOption.ToCharArray(); + + // 左下角面板 + if (options[0] == '2') + AnimationsHelper.ShowWithFadeIn(_mainWindow.LeftBottomPanelForPPTNavigation); + else + _mainWindow.LeftBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed; + + // 右下角面板 + if (options[1] == '2') + AnimationsHelper.ShowWithFadeIn(_mainWindow.RightBottomPanelForPPTNavigation); + else + _mainWindow.RightBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed; + + // 左侧面板 + if (options[2] == '2') + AnimationsHelper.ShowWithFadeIn(_mainWindow.LeftSidePanelForPPTNavigation); + else + _mainWindow.LeftSidePanelForPPTNavigation.Visibility = Visibility.Collapsed; + + // 右侧面板 + if (options[3] == '2') + AnimationsHelper.ShowWithFadeIn(_mainWindow.RightSidePanelForPPTNavigation); + else + _mainWindow.RightSidePanelForPPTNavigation.Visibility = Visibility.Collapsed; + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"更新导航面板显示状态失败: {ex}", LogHelper.LogType.Error); + } + }); + } + + /// + /// 更新导航按钮样式 + /// + public void UpdateNavigationButtonStyles() + { + _dispatcher.InvokeAsync(() => + { + try + { + UpdateSideButtonStyles(); + UpdateBottomButtonStyles(); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"更新导航按钮样式失败: {ex}", LogHelper.LogType.Error); + } + }); + } + + /// + /// 隐藏所有导航面板 + /// + public void HideAllNavigationPanels() + { + _dispatcher.InvokeAsync(() => + { + try + { + _mainWindow.LeftBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed; + _mainWindow.RightBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed; + _mainWindow.LeftSidePanelForPPTNavigation.Visibility = Visibility.Collapsed; + _mainWindow.RightSidePanelForPPTNavigation.Visibility = Visibility.Collapsed; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"隐藏导航面板失败: {ex}", LogHelper.LogType.Error); + } + }); + } + + /// + /// 显示/隐藏侧边栏退出按钮 + /// + public void UpdateSidebarExitButtons(bool show) + { + _dispatcher.InvokeAsync(() => + { + try + { + var visibility = show ? Visibility.Visible : Visibility.Collapsed; + + if (_mainWindow.BtnExitPptFromSidebarLeft != null) + _mainWindow.BtnExitPptFromSidebarLeft.Visibility = visibility; + + if (_mainWindow.BtnExitPptFromSidebarRight != null) + _mainWindow.BtnExitPptFromSidebarRight.Visibility = visibility; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"更新侧边栏退出按钮失败: {ex}", LogHelper.LogType.Error); + } + }); + } + + /// + /// 设置浮动栏透明度 + /// + public void SetFloatingBarOpacity(double opacity) + { + _dispatcher.InvokeAsync(() => + { + try + { + _mainWindow.ViewboxFloatingBar.Opacity = opacity; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"设置浮动栏透明度失败: {ex}", LogHelper.LogType.Error); + } + }); + } + + /// + /// 设置主面板边距 + /// + public void SetMainPanelMargin(Thickness margin) + { + _dispatcher.InvokeAsync(() => + { + try + { + _mainWindow.ViewBoxStackPanelMain.Margin = margin; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"设置主面板边距失败: {ex}", LogHelper.LogType.Error); + } + }); + } + #endregion + + #region Private Methods + private void UpdateSideButtonStyles() + { + try + { + var sideOption = PPTSButtonsOption.ToString(); + if (sideOption.Length < 3) return; + + var options = sideOption.ToCharArray(); + + // 页码按钮显示 + var pageButtonVisibility = options[0] == '2' ? Visibility.Visible : Visibility.Collapsed; + _mainWindow.PPTLSPageButton.Visibility = pageButtonVisibility; + _mainWindow.PPTRSPageButton.Visibility = pageButtonVisibility; + + // 透明度设置 + var opacity = options[1] == '2' ? 0.5 : 1.0; + _mainWindow.PPTBtnLSBorder.Opacity = opacity; + _mainWindow.PPTBtnRSBorder.Opacity = opacity; + + // 颜色主题 + bool isDarkTheme = options[2] == '2'; + ApplyButtonTheme(_mainWindow.PPTBtnLSBorder, _mainWindow.PPTBtnRSBorder, isDarkTheme, true); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"更新侧边按钮样式失败: {ex}", LogHelper.LogType.Error); + } + } + + private void UpdateBottomButtonStyles() + { + try + { + var bottomOption = PPTBButtonsOption.ToString(); + if (bottomOption.Length < 3) return; + + var options = bottomOption.ToCharArray(); + + // 页码按钮显示 + var pageButtonVisibility = options[0] == '2' ? Visibility.Visible : Visibility.Collapsed; + _mainWindow.PPTLBPageButton.Visibility = pageButtonVisibility; + _mainWindow.PPTRBPageButton.Visibility = pageButtonVisibility; + + // 透明度设置 + var opacity = options[1] == '2' ? 0.5 : 1.0; + _mainWindow.PPTBtnLBBorder.Opacity = opacity; + _mainWindow.PPTBtnRBBorder.Opacity = opacity; + + // 颜色主题 + bool isDarkTheme = options[2] == '2'; + ApplyButtonTheme(_mainWindow.PPTBtnLBBorder, _mainWindow.PPTBtnRBBorder, isDarkTheme, false); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"更新底部按钮样式失败: {ex}", LogHelper.LogType.Error); + } + } + + private void ApplyButtonTheme(Border leftBorder, Border rightBorder, bool isDarkTheme, bool isSideButton) + { + try + { + Color backgroundColor, borderColor, foregroundColor, feedbackColor; + + if (isDarkTheme) + { + backgroundColor = Color.FromRgb(39, 39, 42); + borderColor = Color.FromRgb(82, 82, 91); + foregroundColor = Colors.White; + feedbackColor = Colors.White; + } + else + { + backgroundColor = Color.FromRgb(244, 244, 245); + borderColor = Color.FromRgb(161, 161, 170); + foregroundColor = Color.FromRgb(39, 39, 42); + feedbackColor = Color.FromRgb(24, 24, 27); + } + + // 应用背景和边框颜色 + var backgroundBrush = new SolidColorBrush(backgroundColor); + var borderBrush = new SolidColorBrush(borderColor); + + leftBorder.Background = backgroundBrush; + leftBorder.BorderBrush = borderBrush; + rightBorder.Background = backgroundBrush; + rightBorder.BorderBrush = borderBrush; + + // 应用图标和文字颜色 + var foregroundBrush = new SolidColorBrush(foregroundColor); + var feedbackBrush = new SolidColorBrush(feedbackColor); + + if (isSideButton) + { + ApplySideButtonColors(foregroundBrush, feedbackBrush); + } + else + { + ApplyBottomButtonColors(foregroundBrush, feedbackBrush); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"应用按钮主题失败: {ex}", LogHelper.LogType.Error); + } + } + + private void ApplySideButtonColors(SolidColorBrush foregroundBrush, SolidColorBrush feedbackBrush) + { + // 图标颜色 + _mainWindow.PPTLSPreviousButtonGeometry.Brush = foregroundBrush; + _mainWindow.PPTRSPreviousButtonGeometry.Brush = foregroundBrush; + _mainWindow.PPTLSNextButtonGeometry.Brush = foregroundBrush; + _mainWindow.PPTRSNextButtonGeometry.Brush = foregroundBrush; + + // 反馈背景颜色 + _mainWindow.PPTLSPreviousButtonFeedbackBorder.Background = feedbackBrush; + _mainWindow.PPTRSPreviousButtonFeedbackBorder.Background = feedbackBrush; + _mainWindow.PPTLSPageButtonFeedbackBorder.Background = feedbackBrush; + _mainWindow.PPTRSPageButtonFeedbackBorder.Background = feedbackBrush; + _mainWindow.PPTLSNextButtonFeedbackBorder.Background = feedbackBrush; + _mainWindow.PPTRSNextButtonFeedbackBorder.Background = feedbackBrush; + + // 文字颜色 + TextBlock.SetForeground(_mainWindow.PPTLSPageButton, foregroundBrush); + TextBlock.SetForeground(_mainWindow.PPTRSPageButton, foregroundBrush); + } + + private void ApplyBottomButtonColors(SolidColorBrush foregroundBrush, SolidColorBrush feedbackBrush) + { + // 图标颜色 + _mainWindow.PPTLBPreviousButtonGeometry.Brush = foregroundBrush; + _mainWindow.PPTRBPreviousButtonGeometry.Brush = foregroundBrush; + _mainWindow.PPTLBNextButtonGeometry.Brush = foregroundBrush; + _mainWindow.PPTRBNextButtonGeometry.Brush = foregroundBrush; + + // 反馈背景颜色 + _mainWindow.PPTLBPreviousButtonFeedbackBorder.Background = feedbackBrush; + _mainWindow.PPTRBPreviousButtonFeedbackBorder.Background = feedbackBrush; + _mainWindow.PPTLBPageButtonFeedbackBorder.Background = feedbackBrush; + _mainWindow.PPTRBPageButtonFeedbackBorder.Background = feedbackBrush; + _mainWindow.PPTLBNextButtonFeedbackBorder.Background = feedbackBrush; + _mainWindow.PPTRBNextButtonFeedbackBorder.Background = feedbackBrush; + + // 文字颜色 + TextBlock.SetForeground(_mainWindow.PPTLBPageButton, foregroundBrush); + TextBlock.SetForeground(_mainWindow.PPTRBPageButton, foregroundBrush); + } + #endregion + } +} diff --git a/Ink Canvas/MainWindow.xaml.cs b/Ink Canvas/MainWindow.xaml.cs index c96ff4b5..fcd72650 100644 --- a/Ink Canvas/MainWindow.xaml.cs +++ b/Ink Canvas/MainWindow.xaml.cs @@ -336,12 +336,21 @@ namespace Ink_Canvas { // 加载自定义背景颜色 LoadCustomBackgroundColor(); - + // 注册设置面板滚动事件 if (SettingsPanelScrollViewer != null) { SettingsPanelScrollViewer.ScrollChanged += SettingsPanelScrollViewer_ScrollChanged; } + + // 初始化PPT管理器 + InitializePPTManagers(); + + // 如果启用PPT支持,开始监控 + if (Settings.PowerPointSettings.PowerPointSupport) + { + StartPPTMonitoring(); + } // HasNewUpdateWindow hasNewUpdateWindow = new HasNewUpdateWindow(); if (Environment.Is64BitProcess) GroupBoxInkRecognition.Visibility = Visibility.Collapsed; @@ -500,8 +509,11 @@ namespace Ink_Canvas { private void Window_Closed(object sender, EventArgs e) { SystemEvents.DisplaySettingsChanged -= SystemEventsOnDisplaySettingsChanged; + // 释放PPT管理器资源 + DisposePPTManagers(); + LogHelper.WriteLogToFile("Ink Canvas closed", LogHelper.LogType.Event); - + // 检查是否有待安装的更新 CheckPendingUpdates(); } diff --git a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs index 32206b45..1ebf3391 100644 --- a/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs +++ b/Ink Canvas/MainWindow_cs/MW_FloatingBarIcons.cs @@ -672,7 +672,11 @@ namespace Ink_Canvas { if (Settings.Automation.IsAutoSaveStrokesAtClear && inkCanvas.Strokes.Count > Settings.Automation.MinimumAutomationStrokeNumber) { if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) - SaveScreenShot(true, $"{pptName}/{previousSlideID}_{DateTime.Now:HH-mm-ss}"); + { + var currentSlide = _pptManager?.GetCurrentSlideNumber() ?? 0; + var presentationName = _pptManager?.GetPresentationName() ?? ""; + SaveScreenShot(true, $"{presentationName}/{currentSlide}_{DateTime.Now:HH-mm-ss}"); + } else SaveScreenShot(true); } @@ -1287,7 +1291,11 @@ namespace Ink_Canvas { if (inkCanvas.Strokes.Count > 0 && inkCanvas.Strokes.Count > Settings.Automation.MinimumAutomationStrokeNumber) { if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) - SaveScreenShot(true, $"{pptName}/{previousSlideID}_{DateTime.Now:HH-mm-ss}"); + { + var currentSlide = _pptManager?.GetCurrentSlideNumber() ?? 0; + var presentationName = _pptManager?.GetPresentationName() ?? ""; + SaveScreenShot(true, $"{presentationName}/{currentSlide}_{DateTime.Now:HH-mm-ss}"); + } else SaveScreenShot(true); } diff --git a/Ink Canvas/MainWindow_cs/MW_PPT.cs b/Ink Canvas/MainWindow_cs/MW_PPT.cs index 834f04d8..695c5166 100644 --- a/Ink Canvas/MainWindow_cs/MW_PPT.cs +++ b/Ink Canvas/MainWindow_cs/MW_PPT.cs @@ -1,17 +1,11 @@ using System; -using System.Collections.Generic; -using System.Diagnostics; using System.IO; -using System.Linq; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; -using System.Timers; using System.Windows; -using System.Windows.Controls; -using System.Windows.Forms; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; @@ -23,12 +17,12 @@ using Microsoft.Office.Interop.PowerPoint; using Application = System.Windows.Application; using File = System.IO.File; using MessageBox = System.Windows.MessageBox; +using MouseButtonEventArgs = System.Windows.Input.MouseButtonEventArgs; using MouseEventArgs = System.Windows.Input.MouseEventArgs; -using Point = System.Drawing.Point; -using Timer = System.Timers.Timer; namespace Ink_Canvas { public partial class MainWindow : Window { + #region Win32 API Declarations [DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); @@ -38,9 +32,6 @@ namespace Ink_Canvas { [DllImport("user32.dll")] private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); - [DllImport("user32.dll")] - private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId, out uint lpdwThreadId); - [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool IsWindowVisible(IntPtr hWnd); @@ -51,8 +42,6 @@ namespace Ink_Canvas { [DllImport("user32.dll")] private static extern bool IsZoomed(IntPtr hWnd); - - [DllImport("user32.dll")] private static extern IntPtr GetForegroundWindow(); @@ -74,649 +63,231 @@ namespace Ink_Canvas { private const uint GW_HWNDNEXT = 2; private const uint GW_HWNDPREV = 3; - - private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); + #endregion + #region PPT Application Variables public static Microsoft.Office.Interop.PowerPoint.Application pptApplication; public static Presentation presentation; public static Slides slides; public static Slide slide; public static int slidescount; + #endregion - // 在类中添加字段 + #region PPT State Management private bool wasFloatingBarFoldedWhenEnterSlideShow; - - // 新增:用于控制WPS强制关闭提示只弹一次 private static bool hasShownWpsForceCloseWarning = false; + private bool isEnteredSlideShowEndEvent; //防止重复调用本函数导致墨迹保存失效 + private bool isPresentationHaveBlackSpace; + private string pptName; + private bool _isPptClickingBtnTurned; + #endregion - private void BtnCheckPPT_Click(object sender, RoutedEventArgs e) { - try { - pptApplication = - (Microsoft.Office.Interop.PowerPoint.Application)Marshal.GetActiveObject("kwpp.Application"); - //pptApplication.SlideShowWindows[1].View.Next(); - if (pptApplication != null) { - //获得演示文稿对象 - presentation = pptApplication.ActivePresentation; - pptApplication.SlideShowBegin += PptApplication_SlideShowBegin; - pptApplication.SlideShowNextSlide += PptApplication_SlideShowNextSlide; - pptApplication.SlideShowEnd += PptApplication_SlideShowEnd; - // 获得幻灯片对象集合 - slides = presentation.Slides; - // 获得幻灯片的数量 - slidescount = slides.Count; - memoryStreams = new MemoryStream[slidescount + 2]; - // 获得当前选中的幻灯片 - try { - // 在普通视图下这种方式可以获得当前选中的幻灯片对象 - // 然而在阅读模式下,这种方式会出现异常 - slide = slides[pptApplication.ActiveWindow.Selection.SlideRange.SlideNumber]; - } - catch { - // 在阅读模式下出现异常时,通过下面的方式来获得当前选中的幻灯片对象 - try { - if (pptApplication.SlideShowWindows != null && pptApplication.SlideShowWindows.Count >= 1) - { - slide = pptApplication.SlideShowWindows[1].View.Slide; - } - } - catch (Exception ex) { - LogHelper.WriteLogToFile($"获取当前幻灯片失败: {ex}", LogHelper.LogType.Error); - } - } - } + #region PPT Managers + private PPTManager _pptManager; + private PPTInkManager _pptInkManager; + private PPTUIManager _pptUIManager; + #endregion - if (pptApplication == null) throw new Exception(); - //BtnCheckPPT.Visibility = Visibility.Collapsed; - StackPanelPPTControls.Visibility = Visibility.Visible; - } - catch (Exception ex) { - LogHelper.WriteLogToFile($"检查PPT应用程序失败: {ex}", LogHelper.LogType.Error); - //BtnCheckPPT.Visibility = Visibility.Visible; - StackPanelPPTControls.Visibility = Visibility.Collapsed; - LeftBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed; - RightBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed; - LeftSidePanelForPPTNavigation.Visibility = Visibility.Collapsed; - RightSidePanelForPPTNavigation.Visibility = Visibility.Collapsed; - MessageBox.Show("未找到幻灯片"); - } - } + #region PPT Manager Initialization + private void InitializePPTManagers() + { + try + { + // 初始化PPT管理器 + _pptManager = new PPTManager(); + _pptManager.IsSupportWPS = Settings.PowerPointSettings.IsSupportWPS; - private void ToggleSwitchSupportWPS_Toggled(object sender, RoutedEventArgs e) { - if (!isLoaded) return; + // 注册事件 + _pptManager.PPTConnectionChanged += OnPPTConnectionChanged; + _pptManager.SlideShowBegin += OnPPTSlideShowBegin; + _pptManager.SlideShowNextSlide += OnPPTSlideShowNextSlide; + _pptManager.SlideShowEnd += OnPPTSlideShowEnd; + _pptManager.PresentationOpen += OnPPTPresentationOpen; + _pptManager.PresentationClose += OnPPTPresentationClose; - Settings.PowerPointSettings.IsSupportWPS = ToggleSwitchSupportWPS.IsOn; - SaveSettingsToFile(); - } + // 初始化墨迹管理器 + _pptInkManager = new PPTInkManager(); + _pptInkManager.IsAutoSaveEnabled = Settings.PowerPointSettings.IsAutoSaveStrokesInPowerPoint; + _pptInkManager.AutoSaveLocation = Settings.Automation.AutoSavedStrokesLocation; - private static bool isWPSSupportOn => Settings.PowerPointSettings.IsSupportWPS; + // 初始化UI管理器 + _pptUIManager = new PPTUIManager(this); + _pptUIManager.ShowPPTButton = Settings.PowerPointSettings.ShowPPTButton; + _pptUIManager.PPTButtonsDisplayOption = Settings.PowerPointSettings.PPTButtonsDisplayOption; + _pptUIManager.PPTSButtonsOption = Settings.PowerPointSettings.PPTSButtonsOption; + _pptUIManager.PPTBButtonsOption = Settings.PowerPointSettings.PPTBButtonsOption; + _pptUIManager.PPTLSButtonPosition = Settings.PowerPointSettings.PPTLSButtonPosition; + _pptUIManager.PPTRSButtonPosition = Settings.PowerPointSettings.PPTRSButtonPosition; + _pptUIManager.EnablePPTButtonPageClickable = Settings.PowerPointSettings.EnablePPTButtonPageClickable; - public static bool IsShowingRestoreHiddenSlidesWindow; - private static bool IsShowingAutoplaySlidesWindow; - - // WPP 相关变量 - private static Process wppProcess; - private static bool hasWppProcessID; - private static Timer wppProcessCheckTimer; - private static DateTime wppProcessRecordTime = DateTime.MinValue; // 记录进程时间 - private static int wppProcessCheckCount; // 检查次数计数器 - private static WpsWindowInfo lastForegroundWpsWindow; // 记录上次检测到的前台WPS窗口 - private static DateTime lastWindowCheckTime = DateTime.MinValue; // 记录上次窗口检查时间 - - - private void TimerCheckPPT_Elapsed(object sender, ElapsedEventArgs e) { - if (IsShowingRestoreHiddenSlidesWindow || IsShowingAutoplaySlidesWindow) return; - try { - - pptApplication = - (Microsoft.Office.Interop.PowerPoint.Application)Marshal.GetActiveObject("PowerPoint.Application"); - - if (pptApplication != null) { - timerCheckPPT.Stop(); - //获得演示文稿对象 - presentation = pptApplication.ActivePresentation; - - // 获得幻灯片对象集合 - slides = presentation.Slides; - - // 获得幻灯片的数量 - slidescount = slides.Count; - memoryStreams = new MemoryStream[slidescount + 2]; - // 获得当前选中的幻灯片 - try { - // 在普通视图下这种方式可以获得当前选中的幻灯片对象 - // 然而在阅读模式下,这种方式会出现异常 - slide = slides[pptApplication.ActiveWindow.Selection.SlideRange.SlideNumber]; - } - catch { - // 在阅读模式下出现异常时,通过下面的方式来获得当前选中的幻灯片对象 - try { - if (pptApplication.SlideShowWindows != null && pptApplication.SlideShowWindows.Count >= 1) - { - slide = pptApplication.SlideShowWindows[1].View.Slide; - } - } - catch (Exception ex) { - LogHelper.WriteLogToFile($"获取当前幻灯片失败: {ex}", LogHelper.LogType.Error); - } - } - - pptApplication.PresentationOpen += PptApplication_PresentationOpen; - pptApplication.PresentationClose += PptApplication_PresentationClose; - pptApplication.SlideShowBegin += PptApplication_SlideShowBegin; - pptApplication.SlideShowNextSlide += PptApplication_SlideShowNextSlide; - pptApplication.SlideShowEnd += PptApplication_SlideShowEnd; - } - - if (pptApplication == null) return; - //BtnCheckPPT.Visibility = Visibility.Collapsed; - - // 此处是已经开启了 - PptApplication_PresentationOpen(null); - - //如果检测到已经开始放映,则立即进入画板模式 - if (pptApplication.SlideShowWindows != null && pptApplication.SlideShowWindows.Count >= 1) - { - try { - PptApplication_SlideShowBegin(pptApplication.SlideShowWindows[1]); - } - catch (Exception ex) { - LogHelper.WriteLogToFile($"启动幻灯片放映失败: {ex}", LogHelper.LogType.Error); - } - } + LogHelper.WriteLogToFile("PPT管理器初始化完成", LogHelper.LogType.Event); } catch (Exception ex) { - // 忽略常见的COM对象失效错误 - if (ex is COMException comEx) - { - uint hr = (uint)comEx.HResult; - // 0x800401E3: 操作无法使用 - // 0x80004005: 未指定错误(常见于PPT已关闭) - // 0x800706B5: RPC服务器不可用 - // 0x80048240: 没有活动的演示文稿 - // 0x800706BE: 远程过程调用失败 - if (hr == 0x800401E3 || hr == 0x80004005 || hr == 0x800706B5 || hr == 0x80048240 || hr == 0x800706BE) - { - Application.Current.Dispatcher.Invoke(() => { BtnPPTSlideShow.Visibility = Visibility.Collapsed; }); - timerCheckPPT.Start(); - return; - } - } - LogHelper.WriteLogToFile($"检查PPT状态失败: {ex}", LogHelper.LogType.Error); - Application.Current.Dispatcher.Invoke(() => { BtnPPTSlideShow.Visibility = Visibility.Collapsed; }); - timerCheckPPT.Start(); + LogHelper.WriteLogToFile($"PPT管理器初始化失败: {ex}", LogHelper.LogType.Error); } } - private void PptApplication_PresentationOpen(Presentation Pres) { - // 新增逻辑:如果开启"重新进入放映时回到首页",则直接跳转第一页 - if (Settings.PowerPointSettings.IsAlwaysGoToFirstPageOnReenter) + private void StartPPTMonitoring() + { + if (Settings.PowerPointSettings.PowerPointSupport) { - Application.Current.Dispatcher.BeginInvoke(new Action(() => { - try - { - if (presentation == null) - { - LogHelper.WriteLogToFile("演示文稿为空,无法跳转到首页", LogHelper.LogType.Warning); - return; - } - if (pptApplication != null && pptApplication.SlideShowWindows != null && pptApplication.SlideShowWindows.Count >= 1) - presentation.SlideShowWindow.View.GotoSlide(1); - else if (presentation.Windows != null && presentation.Windows.Count >= 1) - presentation.Windows[1].View.GotoSlide(1); - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"跳转到首页失败: {ex}", LogHelper.LogType.Error); - } - }), DispatcherPriority.Normal); - } - else if (Settings.PowerPointSettings.IsNotifyPreviousPage) - Application.Current.Dispatcher.BeginInvoke(new Action(() => { - try { - // 添加安全检查 - if (presentation == null) - { - LogHelper.WriteLogToFile("演示文稿为空,无法跳转到上次播放页", LogHelper.LogType.Warning); - return; - } - - // 使用更精确的文件标识符:文件名_页数_文件路径哈希值 - string presentationPath = presentation.FullName; - string fileHash = GetFileHash(presentationPath); - string folderName = presentation.Name + "_" + presentation.Slides.Count + "_" + fileHash; - var folderPath = Settings.Automation.AutoSavedStrokesLocation + - @"\Auto Saved - Presentations\" + folderName; - try { - if (!File.Exists(folderPath + "/Position")) return; - if (!int.TryParse(File.ReadAllText(folderPath + "/Position"), out var page)) return; - if (page <= 0) return; - new YesOrNoNotificationWindow($"上次播放到了第 {page} 页, 是否立即跳转", () => { - try { - if (pptApplication != null && pptApplication.SlideShowWindows != null && pptApplication.SlideShowWindows.Count >= 1) - // 如果已经播放了的话, 跳转 - presentation.SlideShowWindow.View.GotoSlide(page); - else if (presentation.Windows != null && presentation.Windows.Count >= 1) - presentation.Windows[1].View.GotoSlide(page); - } - catch (Exception ex) { - LogHelper.WriteLogToFile($"跳转到指定页面失败: {ex}", LogHelper.LogType.Error); - } - }).ShowDialog(); - } - catch (Exception ex) { - LogHelper.WriteLogToFile($"读取上次播放位置失败: {ex}", LogHelper.LogType.Error); - } - } - catch (Exception ex) { - LogHelper.WriteLogToFile($"处理上次播放页跳转失败: {ex}", LogHelper.LogType.Error); - } - }), DispatcherPriority.Normal); - - - //检查是否有隐藏幻灯片 - if (Settings.PowerPointSettings.IsNotifyHiddenPage) { - try { - var isHaveHiddenSlide = false; - if (slides != null) - { - foreach (Slide slide in slides) - if (slide.SlideShowTransition.Hidden == MsoTriState.msoTrue) { - isHaveHiddenSlide = true; - break; - } - } - - Application.Current.Dispatcher.BeginInvoke(new Action(() => { - if (isHaveHiddenSlide && !IsShowingRestoreHiddenSlidesWindow) { - IsShowingRestoreHiddenSlidesWindow = true; - new YesOrNoNotificationWindow("检测到此演示文档中包含隐藏的幻灯片,是否取消隐藏?", - () => { - try { - if (slides != null) - { - foreach (Slide slide in slides) - if (slide.SlideShowTransition.Hidden == - MsoTriState.msoTrue) - slide.SlideShowTransition.Hidden = - MsoTriState.msoFalse; - } - } - catch (Exception ex) { - LogHelper.WriteLogToFile($"取消隐藏幻灯片失败: {ex}", LogHelper.LogType.Error); - } - finally { - IsShowingRestoreHiddenSlidesWindow = false; - } - }, () => { IsShowingRestoreHiddenSlidesWindow = false; }, - () => { IsShowingRestoreHiddenSlidesWindow = false; }).ShowDialog(); - } - - BtnPPTSlideShow.Visibility = Visibility.Visible; - }), DispatcherPriority.Normal); - } - catch (Exception ex) { - LogHelper.WriteLogToFile($"检查隐藏幻灯片失败: {ex}", LogHelper.LogType.Error); - } - } - - //检测是否有自动播放 - if (Settings.PowerPointSettings.IsNotifyAutoPlayPresentation - // && presentation.SlideShowSettings.AdvanceMode == PpSlideShowAdvanceMode.ppSlideShowUseSlideTimings - && BtnPPTSlideShowEnd.Visibility != Visibility.Visible) { - try { - bool hasSlideTimings = false; - if (presentation != null && presentation.Slides != null) - { - foreach (Slide slide in presentation.Slides) { - if (slide.SlideShowTransition.AdvanceOnTime == MsoTriState.msoTrue && - slide.SlideShowTransition.AdvanceTime > 0) { - hasSlideTimings = true; - break; - } - } - } - - if (hasSlideTimings) { - Application.Current.Dispatcher.BeginInvoke((Action)(() => { - if (hasSlideTimings && !IsShowingAutoplaySlidesWindow) { - IsShowingAutoplaySlidesWindow = true; - new YesOrNoNotificationWindow("检测到此演示文档中自动播放或排练计时已经启用,可能导致幻灯片自动翻页,是否取消?", - () => { - try { - if (presentation != null) - { - presentation.SlideShowSettings.AdvanceMode = - PpSlideShowAdvanceMode.ppSlideShowManualAdvance; - } - } - catch (Exception ex) { - LogHelper.WriteLogToFile($"设置手动播放模式失败: {ex}", LogHelper.LogType.Error); - } - finally { - IsShowingAutoplaySlidesWindow = false; - } - }, () => { IsShowingAutoplaySlidesWindow = false; }, - () => { IsShowingAutoplaySlidesWindow = false; }).ShowDialog(); - } - })); - try { - if (presentation != null) - { - presentation.SlideShowSettings.AdvanceMode = PpSlideShowAdvanceMode.ppSlideShowManualAdvance; - } - } - catch (Exception ex) { - LogHelper.WriteLogToFile($"设置演示文稿播放模式失败: {ex}", LogHelper.LogType.Error); - } - } - } - catch (Exception ex) { - LogHelper.WriteLogToFile($"检查自动播放设置失败: {ex}", LogHelper.LogType.Error); - } + _pptManager?.StartMonitoring(); + LogHelper.WriteLogToFile("PPT监控已启动", LogHelper.LogType.Event); } } - private void PptApplication_PresentationClose(Presentation Pres) { - try { - pptApplication.PresentationOpen -= PptApplication_PresentationOpen; - pptApplication.PresentationClose -= PptApplication_PresentationClose; - pptApplication.SlideShowBegin -= PptApplication_SlideShowBegin; - pptApplication.SlideShowNextSlide -= PptApplication_SlideShowNextSlide; - pptApplication.SlideShowEnd -= PptApplication_SlideShowEnd; - - - timerCheckPPT.Start(); - - Application.Current.Dispatcher.Invoke(() => { - BtnPPTSlideShow.Visibility = Visibility.Collapsed; - BtnPPTSlideShowEnd.Visibility = Visibility.Collapsed; + private void StopPPTMonitoring() + { + _pptManager?.StopMonitoring(); + LogHelper.WriteLogToFile("PPT监控已停止", LogHelper.LogType.Event); + } + + private void DisposePPTManagers() + { + try + { + _pptManager?.Dispose(); + _pptInkManager?.Dispose(); + _pptManager = null; + _pptInkManager = null; + _pptUIManager = null; + LogHelper.WriteLogToFile("PPT管理器已释放", LogHelper.LogType.Event); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"释放PPT管理器失败: {ex}", LogHelper.LogType.Error); + } + } + #endregion + + #region New PPT Event Handlers + private void OnPPTConnectionChanged(bool isConnected) + { + try + { + Application.Current.Dispatcher.InvokeAsync(() => + { + _pptUIManager?.UpdateConnectionStatus(isConnected); + + if (isConnected) + { + LogHelper.WriteLogToFile("PPT连接已建立", LogHelper.LogType.Event); + } + else + { + LogHelper.WriteLogToFile("PPT连接已断开", LogHelper.LogType.Event); + // 清理墨迹管理器 + _pptInkManager?.ClearAllStrokes(); + } }); } - catch (Exception ex) { - LogHelper.WriteLogToFile(ex.ToString(), LogHelper.LogType.Error); + catch (Exception ex) + { + LogHelper.WriteLogToFile($"处理PPT连接状态变化失败: {ex}", LogHelper.LogType.Error); } } - private bool isPresentationHaveBlackSpace; - private string pptName; + private void OnPPTPresentationOpen(Presentation pres) + { + try + { + Application.Current.Dispatcher.InvokeAsync(() => + { + // 初始化墨迹管理器 + _pptInkManager?.InitializePresentation(pres); - private void UpdatePPTBtnStyleSettingsStatus() { - try { - var sopt = Settings.PowerPointSettings.PPTSButtonsOption.ToString(); - char[] soptc = sopt.ToCharArray(); - if (soptc[0] == '2') - { - PPTLSPageButton.Visibility = Visibility.Visible; - PPTRSPageButton.Visibility = Visibility.Visible; - } - else - { - PPTLSPageButton.Visibility = Visibility.Collapsed; - PPTRSPageButton.Visibility = Visibility.Collapsed; - } - if (soptc[2] == '2') - { - // 这里先堆一点屎山,没空用Resources了 - PPTBtnLSBorder.Background = new SolidColorBrush(Color.FromRgb(39, 39, 42)); - PPTBtnRSBorder.Background = new SolidColorBrush(Color.FromRgb(39, 39, 42)); - PPTBtnLSBorder.BorderBrush = new SolidColorBrush(Color.FromRgb(82, 82, 91)); - PPTBtnRSBorder.BorderBrush = new SolidColorBrush(Color.FromRgb(82, 82, 91)); - PPTLSPreviousButtonGeometry.Brush = new SolidColorBrush(Colors.White); - PPTRSPreviousButtonGeometry.Brush = new SolidColorBrush(Colors.White); - PPTLSNextButtonGeometry.Brush = new SolidColorBrush(Colors.White); - PPTRSNextButtonGeometry.Brush = new SolidColorBrush(Colors.White); - PPTLSPreviousButtonFeedbackBorder.Background = new SolidColorBrush(Colors.White); - PPTRSPreviousButtonFeedbackBorder.Background = new SolidColorBrush(Colors.White); - PPTLSPageButtonFeedbackBorder.Background = new SolidColorBrush(Colors.White); - PPTRSPageButtonFeedbackBorder.Background = new SolidColorBrush(Colors.White); - PPTLSNextButtonFeedbackBorder.Background = new SolidColorBrush(Colors.White); - PPTRSNextButtonFeedbackBorder.Background = new SolidColorBrush(Colors.White); - TextBlock.SetForeground(PPTLSPageButton, new SolidColorBrush(Colors.White)); - TextBlock.SetForeground(PPTRSPageButton, new SolidColorBrush(Colors.White)); - } - else - { - PPTBtnLSBorder.Background = new SolidColorBrush(Color.FromRgb(244, 244, 245)); - PPTBtnRSBorder.Background = new SolidColorBrush(Color.FromRgb(244, 244, 245)); - PPTBtnLSBorder.BorderBrush = new SolidColorBrush(Color.FromRgb(161, 161, 170)); - PPTBtnRSBorder.BorderBrush = new SolidColorBrush(Color.FromRgb(161, 161, 170)); - PPTLSPreviousButtonGeometry.Brush = new SolidColorBrush(Color.FromRgb(39, 39, 42)); - PPTRSPreviousButtonGeometry.Brush = new SolidColorBrush(Color.FromRgb(39, 39, 42)); - PPTLSNextButtonGeometry.Brush = new SolidColorBrush(Color.FromRgb(39, 39, 42)); - PPTRSNextButtonGeometry.Brush = new SolidColorBrush(Color.FromRgb(39, 39, 42)); - PPTLSPreviousButtonFeedbackBorder.Background = new SolidColorBrush(Color.FromRgb(24, 24, 27)); - PPTRSPreviousButtonFeedbackBorder.Background = new SolidColorBrush(Color.FromRgb(24, 24, 27)); - PPTLSPageButtonFeedbackBorder.Background = new SolidColorBrush(Color.FromRgb(24, 24, 27)); - PPTRSPageButtonFeedbackBorder.Background = new SolidColorBrush(Color.FromRgb(24, 24, 27)); - PPTLSNextButtonFeedbackBorder.Background = new SolidColorBrush(Color.FromRgb(24, 24, 27)); - PPTRSNextButtonFeedbackBorder.Background = new SolidColorBrush(Color.FromRgb(24, 24, 27)); - TextBlock.SetForeground(PPTLSPageButton, new SolidColorBrush(Color.FromRgb(24, 24, 27))); - TextBlock.SetForeground(PPTRSPageButton, new SolidColorBrush(Color.FromRgb(24, 24, 27))); - } - if (soptc[1] == '2') - { - PPTBtnLSBorder.Opacity = 0.5; - PPTBtnRSBorder.Opacity = 0.5; - } - else - { - PPTBtnLSBorder.Opacity = 1; - PPTBtnRSBorder.Opacity = 1; - } + // 处理跳转到首页或上次播放页的逻辑 + HandlePresentationOpenNavigation(pres); - var bopt = Settings.PowerPointSettings.PPTBButtonsOption.ToString(); - char[] boptc = bopt.ToCharArray(); - if (boptc[0] == '2') - { - PPTLBPageButton.Visibility = Visibility.Visible; - PPTRBPageButton.Visibility = Visibility.Visible; - } - else - { - PPTLBPageButton.Visibility = Visibility.Collapsed; - PPTRBPageButton.Visibility = Visibility.Collapsed; - } - if (boptc[2] == '2') - { - // 这里先堆一点屎山,没空用Resources了 - PPTBtnLBBorder.Background = new SolidColorBrush(Color.FromRgb(39, 39, 42)); - PPTBtnRBBorder.Background = new SolidColorBrush(Color.FromRgb(39, 39, 42)); - PPTBtnLBBorder.BorderBrush = new SolidColorBrush(Color.FromRgb(82, 82, 91)); - PPTBtnRBBorder.BorderBrush = new SolidColorBrush(Color.FromRgb(82, 82, 91)); - PPTLBPreviousButtonGeometry.Brush = new SolidColorBrush(Colors.White); - PPTRBPreviousButtonGeometry.Brush = new SolidColorBrush(Colors.White); - PPTLBNextButtonGeometry.Brush = new SolidColorBrush(Colors.White); - PPTRBNextButtonGeometry.Brush = new SolidColorBrush(Colors.White); - PPTLBPreviousButtonFeedbackBorder.Background = new SolidColorBrush(Colors.White); - PPTRBPreviousButtonFeedbackBorder.Background = new SolidColorBrush(Colors.White); - PPTLBPageButtonFeedbackBorder.Background = new SolidColorBrush(Colors.White); - PPTRBPageButtonFeedbackBorder.Background = new SolidColorBrush(Colors.White); - PPTLBNextButtonFeedbackBorder.Background = new SolidColorBrush(Colors.White); - PPTRBNextButtonFeedbackBorder.Background = new SolidColorBrush(Colors.White); - TextBlock.SetForeground(PPTLBPageButton, new SolidColorBrush(Colors.White)); - TextBlock.SetForeground(PPTRBPageButton, new SolidColorBrush(Colors.White)); - } - else - { - PPTBtnLBBorder.Background = new SolidColorBrush(Color.FromRgb(244, 244, 245)); - PPTBtnRBBorder.Background = new SolidColorBrush(Color.FromRgb(244, 244, 245)); - PPTBtnLBBorder.BorderBrush = new SolidColorBrush(Color.FromRgb(161, 161, 170)); - PPTBtnRBBorder.BorderBrush = new SolidColorBrush(Color.FromRgb(161, 161, 170)); - PPTLBPreviousButtonGeometry.Brush = new SolidColorBrush(Color.FromRgb(39, 39, 42)); - PPTRBPreviousButtonGeometry.Brush = new SolidColorBrush(Color.FromRgb(39, 39, 42)); - PPTLBNextButtonGeometry.Brush = new SolidColorBrush(Color.FromRgb(39, 39, 42)); - PPTRBNextButtonGeometry.Brush = new SolidColorBrush(Color.FromRgb(39, 39, 42)); - PPTLBPreviousButtonFeedbackBorder.Background = new SolidColorBrush(Color.FromRgb(24, 24, 27)); - PPTRBPreviousButtonFeedbackBorder.Background = new SolidColorBrush(Color.FromRgb(24, 24, 27)); - PPTLBPageButtonFeedbackBorder.Background = new SolidColorBrush(Color.FromRgb(24, 24, 27)); - PPTRBPageButtonFeedbackBorder.Background = new SolidColorBrush(Color.FromRgb(24, 24, 27)); - PPTLBNextButtonFeedbackBorder.Background = new SolidColorBrush(Color.FromRgb(24, 24, 27)); - PPTRBNextButtonFeedbackBorder.Background = new SolidColorBrush(Color.FromRgb(24, 24, 27)); - TextBlock.SetForeground(PPTLBPageButton, new SolidColorBrush(Color.FromRgb(24, 24, 27))); - TextBlock.SetForeground(PPTRBPageButton, new SolidColorBrush(Color.FromRgb(24, 24, 27))); - } - if (boptc[1] == '2') - { - PPTBtnLBBorder.Opacity = 0.5; - PPTBtnRBBorder.Opacity = 0.5; - } - else - { - PPTBtnLBBorder.Opacity = 1; - PPTBtnRBBorder.Opacity = 1; - } + // 检查隐藏幻灯片 + if (Settings.PowerPointSettings.IsNotifyHiddenPage) + { + CheckAndNotifyHiddenSlides(pres); + } + + // 检查自动播放设置 + if (Settings.PowerPointSettings.IsNotifyAutoPlayPresentation) + { + CheckAndNotifyAutoPlaySettings(pres); + } + + _pptUIManager?.UpdateConnectionStatus(true); + }); } - catch (Exception ex) { - LogHelper.WriteLogToFile(ex.ToString(), LogHelper.LogType.Error); + catch (Exception ex) + { + LogHelper.WriteLogToFile($"处理演示文稿打开事件失败: {ex}", LogHelper.LogType.Error); } } - private void UpdatePPTBtnDisplaySettingsStatus() { - try { - // 检查是否应该显示PPT按钮 - bool shouldShowButtons = Settings.PowerPointSettings.ShowPPTButton && - (BtnPPTSlideShowEnd.Visibility == Visibility.Visible || - (pptApplication != null && pptApplication.SlideShowWindows != null && pptApplication.SlideShowWindows.Count > 0)); - - if (!shouldShowButtons) + private void OnPPTPresentationClose(Presentation pres) + { + try + { + Application.Current.Dispatcher.InvokeAsync(() => { - LeftBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed; - RightBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed; - LeftSidePanelForPPTNavigation.Visibility = Visibility.Collapsed; - RightSidePanelForPPTNavigation.Visibility = Visibility.Collapsed; - return; - } + // 保存所有墨迹 + _pptInkManager?.SaveAllStrokesToFile(pres); - var lsp = Settings.PowerPointSettings.PPTLSButtonPosition; - LeftSidePanelForPPTNavigation.Margin = new Thickness(0, 0, 0, lsp*2); - var rsp = Settings.PowerPointSettings.PPTRSButtonPosition; - RightSidePanelForPPTNavigation.Margin = new Thickness(0, 0, 0, rsp*2); + // 清理墨迹管理器 + _pptInkManager?.ClearAllStrokes(); - var dopt = Settings.PowerPointSettings.PPTButtonsDisplayOption.ToString(); - char[] doptc = dopt.ToCharArray(); - if (doptc[0] == '2') AnimationsHelper.ShowWithFadeIn(LeftBottomPanelForPPTNavigation); - else LeftBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed; - if (doptc[1] == '2') AnimationsHelper.ShowWithFadeIn(RightBottomPanelForPPTNavigation); - else RightBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed; - if (doptc[2] == '2') AnimationsHelper.ShowWithFadeIn(LeftSidePanelForPPTNavigation); - else LeftSidePanelForPPTNavigation.Visibility = Visibility.Collapsed; - if (doptc[3] == '2') AnimationsHelper.ShowWithFadeIn(RightSidePanelForPPTNavigation); - else RightSidePanelForPPTNavigation.Visibility = Visibility.Collapsed; + _pptUIManager?.UpdateConnectionStatus(false); + }); } - catch (Exception ex) { - LogHelper.WriteLogToFile($"更新PPT按钮显示状态失败: {ex}", LogHelper.LogType.Error); + catch (Exception ex) + { + LogHelper.WriteLogToFile($"处理演示文稿关闭事件失败: {ex}", LogHelper.LogType.Error); } } - private async void PptApplication_SlideShowBegin(SlideShowWindow Wn) { - try { + private async void OnPPTSlideShowBegin(SlideShowWindow wn) + { + try + { // 记录进入放映时浮动栏收纳状态 wasFloatingBarFoldedWhenEnterSlideShow = isFloatingBarFolded; - + if (Settings.Automation.IsAutoFoldInPPTSlideShow && !isFloatingBarFolded) FoldFloatingBar_MouseUp(new object(), null); - else if (isFloatingBarFolded) await UnFoldFloatingBar(new object()); + else if (isFloatingBarFolded) + await UnFoldFloatingBar(new object()); isStopInkReplay = true; - LogHelper.WriteLogToFile("PowerPoint Application Slide Show Begin", LogHelper.LogType.Event); - - await Application.Current.Dispatcher.InvokeAsync(() => { - // 新增:如果设置开启,进入放映时强制跳转到第一页 + await Application.Current.Dispatcher.InvokeAsync(() => + { + // 处理跳转到首页 if (Settings.PowerPointSettings.IsAlwaysGoToFirstPageOnReenter) { - try - { - if (Wn != null && Wn.Presentation != null && Wn.View != null) - { - Wn.View.GotoSlide(1); - } - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"放映开始时跳转首页失败: {ex}", LogHelper.LogType.Error); - } + _pptManager?.TryNavigateToSlide(1); } - //调整颜色 - var screenRatio = SystemParameters.PrimaryScreenWidth / SystemParameters.PrimaryScreenHeight; - if (Math.Abs(screenRatio - 16.0 / 9) <= -0.01) { - if (Wn.Presentation.PageSetup.SlideWidth / Wn.Presentation.PageSetup.SlideHeight < 1.65) { - isPresentationHaveBlackSpace = true; + // 更新UI状态 + var currentSlide = _pptManager?.GetCurrentSlideNumber() ?? 0; + var totalSlides = _pptManager?.SlidesCount ?? 0; + _pptUIManager?.UpdateSlideShowStatus(true, currentSlide, totalSlides); - if (BtnSwitchTheme.Content.ToString() == "深色") { - //Light - BtnExit.Foreground = Brushes.White; - ThemeManager.Current.ApplicationTheme = ApplicationTheme.Dark; - } - //Dark - } - } else if (screenRatio == -256 / 135) { } + // 设置浮动栏透明度和边距 + _pptUIManager?.SetFloatingBarOpacity(Settings.Appearance.ViewboxFloatingBarOpacityInPPTValue); + _pptUIManager?.SetMainPanelMargin(new Thickness(10, 10, 10, 10)); - lastDesktopInkColor = 1; - - slidescount = Wn.Presentation.Slides.Count; - previousSlideID = 0; - memoryStreams = new MemoryStream[slidescount + 2]; - - pptName = Wn.Presentation.Name; - LogHelper.NewLog("Name: " + Wn.Presentation.Name); - LogHelper.NewLog("Slides Count: " + slidescount); - - //检查是否有已有墨迹,并加载 - if (Settings.PowerPointSettings.IsAutoSaveStrokesInPowerPoint) - { - // 使用更精确的文件标识符:文件名_页数_文件路径哈希值 - string presentationPath = Wn.Presentation.FullName; - string fileHash = GetFileHash(presentationPath); - string folderName = Wn.Presentation.Name + "_" + Wn.Presentation.Slides.Count + "_" + fileHash; - - if (Directory.Exists(Settings.Automation.AutoSavedStrokesLocation + - @"\Auto Saved - Presentations\" + folderName)) { - LogHelper.WriteLogToFile("Found saved strokes", LogHelper.LogType.Trace); - var files = new DirectoryInfo(Settings.Automation.AutoSavedStrokesLocation + - @"\Auto Saved - Presentations\" + folderName).GetFiles(); - var count = 0; - foreach (var file in files) - if (file.Name != "Position") { - var i = -1; - try { - i = int.Parse(Path.GetFileNameWithoutExtension(file.Name)); - memoryStreams[i] = new MemoryStream(File.ReadAllBytes(file.FullName)); - memoryStreams[i].Position = 0; - count++; - } - catch (Exception ex) { - LogHelper.WriteLogToFile( - $"Failed to load strokes on Slide {i}\n{ex}", - LogHelper.LogType.Error); - } - } - - LogHelper.WriteLogToFile($"Loaded {count.ToString()} saved strokes"); - } - } - - StackPanelPPTControls.Visibility = Visibility.Visible; - UpdatePPTBtnDisplaySettingsStatus(); - UpdatePPTBtnStyleSettingsStatus(); - - BtnPPTSlideShow.Visibility = Visibility.Collapsed; - BtnPPTSlideShowEnd.Visibility = Visibility.Visible; - ViewBoxStackPanelMain.Margin = new Thickness(10, 10, 10, 10); - ViewboxFloatingBar.Opacity = Settings.Appearance.ViewboxFloatingBarOpacityInPPTValue; + // 显示侧边栏退出按钮 + _pptUIManager?.UpdateSidebarExitButtons(true); + // 处理画板显示 if (Settings.PowerPointSettings.IsShowCanvasAtNewSlideShow && !Settings.Automation.IsAutoFoldInPPTSlideShow && - GridTransparencyFakeBackground.Background == Brushes.Transparent && !isFloatingBarFolded) { + GridTransparencyFakeBackground.Background == Brushes.Transparent && !isFloatingBarFolded) + { BtnHideInkCanvas_Click(BtnHideInkCanvas, null); } if (currentMode != 0) { - ImageBlackboard_MouseUp(null,null); + ImageBlackboard_MouseUp(null, null); BtnHideInkCanvas_Click(BtnHideInkCanvas, null); } @@ -727,362 +298,481 @@ namespace Ink_Canvas { BtnColorRed_Click(null, null); isEnteredSlideShowEndEvent = false; - PPTBtnPageNow.Text = $"{Wn.View.CurrentShowPosition}"; - PPTBtnPageTotal.Text = $"/ {Wn.Presentation.Slides.Count}"; - LogHelper.NewLog("PowerPoint Slide Show Loading process complete"); - // 新增:主动加载当前页墨迹,解决首次放映时当前页墨迹不显示的问题 - try - { - var currentPage = Wn.View.CurrentShowPosition; - if (memoryStreams != null && currentPage < memoryStreams.Length && memoryStreams[currentPage] != null && memoryStreams[currentPage].Length > 0) - { - memoryStreams[currentPage].Position = 0; - inkCanvas.Strokes.Clear(); - inkCanvas.Strokes.Add(new StrokeCollection(memoryStreams[currentPage])); - } - else - { - inkCanvas.Strokes.Clear(); - } - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"加载当前页墨迹失败: {ex}", LogHelper.LogType.Error); - } + // 加载当前页墨迹 + LoadCurrentSlideInk(currentSlide); + }); - if (!isFloatingBarFolded) { - new Thread(() => { - Thread.Sleep(100); - Application.Current.Dispatcher.Invoke(() => { - ViewboxFloatingBarMarginAnimation(60); - }); - }).Start(); - } - }); - await Application.Current.Dispatcher.InvokeAsync(() => { - if (BtnExitPptFromSidebarLeft != null) - BtnExitPptFromSidebarLeft.Visibility = Visibility.Visible; - if (BtnExitPptFromSidebarRight != null) - BtnExitPptFromSidebarRight.Visibility = Visibility.Visible; - }); + if (!isFloatingBarFolded) + { + new Thread(() => + { + Thread.Sleep(100); + Application.Current.Dispatcher.Invoke(() => + { + ViewboxFloatingBarMarginAnimation(60); + }); + }).Start(); + } } - catch (Exception ex) { - LogHelper.WriteLogToFile("PowerPoint Application Slide Show Begin Error: " + ex, LogHelper.LogType.Error); - LogHelper.WriteLogToFile(ex.ToString(), LogHelper.LogType.Error); + catch (Exception ex) + { + LogHelper.WriteLogToFile($"处理幻灯片放映开始事件失败: {ex}", LogHelper.LogType.Error); } } - private bool isEnteredSlideShowEndEvent; //防止重复调用本函数导致墨迹保存失效 + private void OnPPTSlideShowNextSlide(SlideShowWindow wn) + { + try + { + Application.Current.Dispatcher.InvokeAsync(() => + { + var currentSlide = _pptManager?.GetCurrentSlideNumber() ?? 0; + var totalSlides = _pptManager?.SlidesCount ?? 0; - private async void PptApplication_SlideShowEnd(Presentation Pres) { - try { - // 新增逻辑:如果设置开启且进入PPT放映时浮动栏是收纳的,退出时也自动收纳;否则自动展开 - if (Settings.Automation.IsAutoFoldAfterPPTSlideShow && wasFloatingBarFoldedWhenEnterSlideShow) { + // 保存上一页墨迹并加载当前页墨迹 + SwitchSlideInk(currentSlide); + + // 更新UI + _pptUIManager?.UpdateCurrentSlideNumber(currentSlide, totalSlides); + + LogHelper.WriteLogToFile($"幻灯片切换到第{currentSlide}页", LogHelper.LogType.Trace); + }); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"处理幻灯片切换事件失败: {ex}", LogHelper.LogType.Error); + } + } + + private async void OnPPTSlideShowEnd(Presentation pres) + { + try + { + // 处理浮动栏状态 + if (Settings.Automation.IsAutoFoldAfterPPTSlideShow && wasFloatingBarFoldedWhenEnterSlideShow) + { if (!isFloatingBarFolded) FoldFloatingBar_MouseUp(new object(), null); - } else { + } + else + { if (isFloatingBarFolded) await UnFoldFloatingBar(new object()); } - // 记录 WPP 进程 ID,用于后续检测未关闭的进程 - if (pptApplication != null) + if (isEnteredSlideShowEndEvent) return; + isEnteredSlideShowEndEvent = true; + + // 保存所有墨迹 + _pptInkManager?.SaveAllStrokesToFile(pres); + + await Application.Current.Dispatcher.InvokeAsync(() => { try { - // 尝试多种方式获取WPS进程 - Process wpsProcess = null; - - // 方法1:通过应用程序路径检测 - if (pptApplication.Path.Contains("Kingsoft\\WPS Office\\") || - pptApplication.Path.Contains("WPS Office\\")) - { - uint processId; - GetWindowThreadProcessId((IntPtr)pptApplication.HWND, out processId); - wpsProcess = Process.GetProcessById((int)processId); - //LogHelper.WriteLogToFile($"通过路径检测到WPS进程: {processId}", LogHelper.LogType.Trace); - } - - // 方法2:通过前台窗口检测 - if (wpsProcess == null) - { - var foregroundWpsWindow = GetForegroundWpsWindow(); - if (foregroundWpsWindow != null) - { - wpsProcess = Process.GetProcessById((int)foregroundWpsWindow.ProcessId); - //LogHelper.WriteLogToFile($"通过前台窗口检测到WPS进程: {foregroundWpsWindow.ProcessId}", LogHelper.LogType.Trace); - } - } - - // 方法3:通过进程名检测 - if (wpsProcess == null) - { - var wpsProcesses = GetWpsProcesses(); - if (wpsProcesses.Count > 0) - { - wpsProcess = wpsProcesses.First(); - //LogHelper.WriteLogToFile($"通过进程名检测到WPS进程: {wpsProcess.Id}", LogHelper.LogType.Trace); - } - } - - if (wpsProcess != null) - { - wppProcess = wpsProcess; - hasWppProcessID = true; - wppProcessRecordTime = DateTime.Now; - wppProcessCheckCount = 0; - LogHelper.WriteLogToFile($"成功记录 WPP 进程 ID: {wpsProcess.Id}", LogHelper.LogType.Trace); - } - else - { - LogHelper.WriteLogToFile("未能检测到WPS进程", LogHelper.LogType.Warning); - } - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"记录WPS进程失败: {ex}", LogHelper.LogType.Error); - } - } - - LogHelper.WriteLogToFile("PowerPoint Slide Show End", LogHelper.LogType.Event); - if (isEnteredSlideShowEndEvent) { - LogHelper.WriteLogToFile("Detected previous entrance, returning"); - return; - } - - isEnteredSlideShowEndEvent = true; - if (Settings.PowerPointSettings.IsAutoSaveStrokesInPowerPoint) { - // 使用更精确的文件标识符:文件名_页数_文件路径哈希值 - string presentationPath = Pres.FullName; - string fileHash = GetFileHash(presentationPath); - string folderName = Pres.Name + "_" + Pres.Slides.Count + "_" + fileHash; - var folderPath = Settings.Automation.AutoSavedStrokesLocation + @"\Auto Saved - Presentations\" + folderName; - if (!Directory.Exists(folderPath)) Directory.CreateDirectory(folderPath); - try { - File.WriteAllText(folderPath + "/Position", previousSlideID.ToString()); - } - catch (Exception ex) { - LogHelper.WriteLogToFile(ex.ToString(), LogHelper.LogType.Error); - } - - for (var i = 1; i <= Pres.Slides.Count; i++) - if (memoryStreams[i] != null) - try { - if (memoryStreams[i].Length > 8) { - var srcBuf = new byte[memoryStreams[i].Length]; - memoryStreams[i].Position = 0; - var byteLength = memoryStreams[i].Read(srcBuf, 0, srcBuf.Length); - // 使用Path.Combine构建文件路径 - File.WriteAllBytes(folderPath + @"\" + i.ToString("0000") + ".icstk", srcBuf); - LogHelper.WriteLogToFile(string.Format( - "Saved strokes for Slide {0}, size={1}, byteLength={2}", i.ToString(), - memoryStreams[i].Length, byteLength)); - } else { - if (File.Exists(folderPath + @"\" + i.ToString("0000") + ".icstk")) - File.Delete(folderPath + @"\" + i.ToString("0000") + ".icstk"); - } - } - catch (Exception ex) { - LogHelper.WriteLogToFile( - $"Failed to save strokes for Slide {i}\n{ex}", - LogHelper.LogType.Error); - if (File.Exists(folderPath + @"\" + i.ToString("0000") + ".icstk")) - File.Delete(folderPath + @"\" + i.ToString("0000") + ".icstk"); - } - } - - await Application.Current.Dispatcher.InvokeAsync(() => { - try { isPresentationHaveBlackSpace = false; - if (BtnSwitchTheme.Content.ToString() == "深色") { - //Light + // 恢复主题 + if (BtnSwitchTheme.Content.ToString() == "深色") + { BtnExit.Foreground = Brushes.Black; ThemeManager.Current.ApplicationTheme = ApplicationTheme.Light; } - //Dark - BtnPPTSlideShow.Visibility = Visibility.Visible; - BtnPPTSlideShowEnd.Visibility = Visibility.Collapsed; - StackPanelPPTControls.Visibility = Visibility.Collapsed; - LeftBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed; - RightBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed; - LeftSidePanelForPPTNavigation.Visibility = Visibility.Collapsed; - RightSidePanelForPPTNavigation.Visibility = Visibility.Collapsed; + // 更新UI状态 + _pptUIManager?.UpdateSlideShowStatus(false); + _pptUIManager?.UpdateSidebarExitButtons(false); + _pptUIManager?.SetMainPanelMargin(new Thickness(10, 10, 10, 55)); + _pptUIManager?.SetFloatingBarOpacity(Settings.Appearance.ViewboxFloatingBarOpacityValue); - ViewBoxStackPanelMain.Margin = new Thickness(10, 10, 10, 55); - - if (currentMode != 0) { + if (currentMode != 0) + { CloseWhiteboardImmediately(); currentMode = 0; } ClearStrokes(true); - - // 清空备份历史记录,防止退出白板时恢复已结束PPT的墨迹 TimeMachineHistories[0] = null; if (GridTransparencyFakeBackground.Background != Brushes.Transparent) BtnHideInkCanvas_Click(BtnHideInkCanvas, null); - - ViewboxFloatingBar.Opacity = Settings.Appearance.ViewboxFloatingBarOpacityValue; } - catch (Exception ex) { - LogHelper.WriteLogToFile(ex.ToString(), LogHelper.LogType.Error); + catch (Exception ex) + { + LogHelper.WriteLogToFile($"处理幻灯片放映结束UI更新失败: {ex}", LogHelper.LogType.Error); } }); await Task.Delay(150); - - await Application.Current.Dispatcher.InvokeAsync(() => { + await Application.Current.Dispatcher.InvokeAsync(() => + { ViewboxFloatingBarMarginAnimation(100, true); }); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"处理幻灯片放映结束事件失败: {ex}", LogHelper.LogType.Error); + } + } + #endregion - // 启动 WPP 进程检测定时器 - if (hasWppProcessID && wppProcess != null) + #region Helper Methods + private void HandlePresentationOpenNavigation(Presentation pres) + { + try + { + if (Settings.PowerPointSettings.IsAlwaysGoToFirstPageOnReenter) { - StartWppProcessCheckTimer(); + _pptManager?.TryNavigateToSlide(1); + } + else if (Settings.PowerPointSettings.IsNotifyPreviousPage) + { + ShowPreviousPageNotification(pres); } } - catch (Exception ex) { - LogHelper.WriteLogToFile(ex.ToString(), LogHelper.LogType.Error); + catch (Exception ex) + { + LogHelper.WriteLogToFile($"处理演示文稿导航失败: {ex}", LogHelper.LogType.Error); } } - private int previousSlideID; - private MemoryStream[] memoryStreams = new MemoryStream[50]; + private void ShowPreviousPageNotification(Presentation pres) + { + try + { + if (pres == null) return; - private DateTime inkLockUntil = DateTime.MinValue; - private int lockedSlideIndex; - private const int InkLockMilliseconds = 500; + var presentationPath = pres.FullName; + var fileHash = GetFileHash(presentationPath); + var folderName = pres.Name + "_" + pres.Slides.Count + "_" + fileHash; + var folderPath = Path.Combine(Settings.Automation.AutoSavedStrokesLocation, "Auto Saved - Presentations", folderName); + var positionFile = Path.Combine(folderPath, "Position"); - private void PptApplication_SlideShowNextSlide(SlideShowWindow Wn) { - try { - // 添加安全检查 - if (Wn == null || Wn.View == null) + if (!File.Exists(positionFile)) return; + + if (int.TryParse(File.ReadAllText(positionFile), out var page) && page > 0) { - LogHelper.WriteLogToFile("幻灯片放映窗口或视图为空", LogHelper.LogType.Warning); - return; + new YesOrNoNotificationWindow($"上次播放到了第 {page} 页, 是否立即跳转", () => + { + _pptManager?.TryNavigateToSlide(page); + }).ShowDialog(); } - LogHelper.WriteLogToFile($"PowerPoint Next Slide (Slide {Wn.View.CurrentShowPosition})", - LogHelper.LogType.Event); - // 先保存旧页墨迹 - Application.Current.Dispatcher.Invoke(() => { - var ms = new MemoryStream(); - inkCanvas.Strokes.Save(ms); - ms.Position = 0; - if (previousSlideID > 0 && previousSlideID < memoryStreams.Length) - memoryStreams[previousSlideID] = ms; - ClearStrokes(true); - timeMachine.ClearStrokeHistory(); - int newPage = Wn.View.CurrentShowPosition; - if (newPage > 0 && newPage < memoryStreams.Length && - memoryStreams[newPage] != null && memoryStreams[newPage].Length > 0) { - memoryStreams[newPage].Position = 0; - inkCanvas.Strokes.Add(new StrokeCollection(memoryStreams[newPage])); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"显示上次播放页通知失败: {ex}", LogHelper.LogType.Error); + } + } + + private void CheckAndNotifyHiddenSlides(Presentation pres) + { + try + { + bool hasHiddenSlides = false; + if (pres?.Slides != null) + { + foreach (Slide slide in pres.Slides) + { + if (slide.SlideShowTransition.Hidden == MsoTriState.msoTrue) + { + hasHiddenSlides = true; + break; + } } - PPTBtnPageNow.Text = $"{Wn.View.CurrentShowPosition}"; - PPTBtnPageTotal.Text = $"/ {Wn.Presentation.Slides.Count}"; - previousSlideID = newPage; // 最后赋值,确保索引同步 - LockInkForCurrentSlide(newPage); // 翻页后加锁 + } + + if (hasHiddenSlides && !IsShowingRestoreHiddenSlidesWindow) + { + IsShowingRestoreHiddenSlidesWindow = true; + new YesOrNoNotificationWindow("检测到此演示文档中包含隐藏的幻灯片,是否取消隐藏?", + () => + { + try + { + if (pres?.Slides != null) + { + foreach (Slide slide in pres.Slides) + { + if (slide.SlideShowTransition.Hidden == MsoTriState.msoTrue) + slide.SlideShowTransition.Hidden = MsoTriState.msoFalse; + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"取消隐藏幻灯片失败: {ex}", LogHelper.LogType.Error); + } + finally + { + IsShowingRestoreHiddenSlidesWindow = false; + } + }, + () => { IsShowingRestoreHiddenSlidesWindow = false; }, + () => { IsShowingRestoreHiddenSlidesWindow = false; }).ShowDialog(); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"检查隐藏幻灯片失败: {ex}", LogHelper.LogType.Error); + } + } + + private void CheckAndNotifyAutoPlaySettings(Presentation pres) + { + try + { + if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) return; + + bool hasSlideTimings = false; + if (pres?.Slides != null) + { + foreach (Slide slide in pres.Slides) + { + if (slide.SlideShowTransition.AdvanceOnTime == MsoTriState.msoTrue && + slide.SlideShowTransition.AdvanceTime > 0) + { + hasSlideTimings = true; + break; + } + } + } + + if (hasSlideTimings && !IsShowingAutoplaySlidesWindow) + { + IsShowingAutoplaySlidesWindow = true; + new YesOrNoNotificationWindow("检测到此演示文档中自动播放或排练计时已经启用,可能导致幻灯片自动翻页,是否取消?", + () => + { + try + { + if (pres != null) + { + pres.SlideShowSettings.AdvanceMode = PpSlideShowAdvanceMode.ppSlideShowManualAdvance; + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"设置手动播放模式失败: {ex}", LogHelper.LogType.Error); + } + finally + { + IsShowingAutoplaySlidesWindow = false; + } + }, + () => { IsShowingAutoplaySlidesWindow = false; }, + () => { IsShowingAutoplaySlidesWindow = false; }).ShowDialog(); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"检查自动播放设置失败: {ex}", LogHelper.LogType.Error); + } + } + + private void LoadCurrentSlideInk(int slideIndex) + { + try + { + var strokes = _pptInkManager?.LoadSlideStrokes(slideIndex); + if (strokes != null) + { + inkCanvas.Strokes.Clear(); + inkCanvas.Strokes.Add(strokes); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"加载当前页墨迹失败: {ex}", LogHelper.LogType.Error); + } + } + + private void SwitchSlideInk(int newSlideIndex) + { + try + { + var newStrokes = _pptInkManager?.SwitchToSlide(newSlideIndex, inkCanvas.Strokes); + if (newStrokes != null) + { + inkCanvas.Strokes.Clear(); + inkCanvas.Strokes.Add(newStrokes); + } + + // 设置墨迹锁定 + _pptInkManager?.LockInkForSlide(newSlideIndex); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"切换页面墨迹失败: {ex}", LogHelper.LogType.Error); + } + } + + private string GetFileHash(string filePath) + { + try + { + if (string.IsNullOrEmpty(filePath)) return "unknown"; + + using (var md5 = MD5.Create()) + { + byte[] hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(filePath)); + return BitConverter.ToString(hashBytes).Replace("-", "").Substring(0, 8); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"计算文件哈希值失败: {ex}", LogHelper.LogType.Error); + return "error"; + } + } + #endregion + + private void BtnCheckPPT_Click(object sender, RoutedEventArgs e) { + try { + // 使用新的PPT管理器进行连接检查 + if (_pptManager == null) + { + InitializePPTManagers(); + } + + // 手动触发一次连接检查 + _pptManager?.StartMonitoring(); + + // 等待一小段时间让连接建立 + Task.Delay(500).ContinueWith(_ => + { + Application.Current.Dispatcher.Invoke(() => + { + if (_pptManager?.IsConnected == true) + { + LogHelper.WriteLogToFile("手动PPT连接检查成功", LogHelper.LogType.Event); + } + else + { + MessageBox.Show("未找到幻灯片"); + LogHelper.WriteLogToFile("手动PPT连接检查失败", LogHelper.LogType.Warning); + } + }); }); } catch (Exception ex) { - LogHelper.WriteLogToFile($"幻灯片切换事件处理失败: {ex}", LogHelper.LogType.Error); + LogHelper.WriteLogToFile($"手动检查PPT应用程序失败: {ex}", LogHelper.LogType.Error); + _pptUIManager?.UpdateConnectionStatus(false); + MessageBox.Show("未找到幻灯片"); } } - private bool _isPptClickingBtnTurned; + private void ToggleSwitchSupportWPS_Toggled(object sender, RoutedEventArgs e) { + if (!isLoaded) return; + + Settings.PowerPointSettings.IsSupportWPS = ToggleSwitchSupportWPS.IsOn; + + // 更新PPT管理器的WPS支持设置 + if (_pptManager != null) + { + _pptManager.IsSupportWPS = Settings.PowerPointSettings.IsSupportWPS; + } + + SaveSettingsToFile(); + } + + private static bool isWPSSupportOn => Settings.PowerPointSettings.IsSupportWPS; + + public static bool IsShowingRestoreHiddenSlidesWindow; + private static bool IsShowingAutoplaySlidesWindow; + + + + + + + + + + + + + + + + + + private void BtnPPTSlidesUp_Click(object sender, RoutedEventArgs e) { Application.Current.Dispatcher.Invoke(() => { - // 切换前同步保存墨迹 - SaveCurrentSlideInkStrokes(); - _isPptClickingBtnTurned = true; - // 添加安全检查 - if (pptApplication == null) - { - LogHelper.WriteLogToFile("PPT应用程序为空,无法执行上一页操作", LogHelper.LogType.Warning); - return; - } try { - // 检查SlideShowWindows是否存在且有效 - if (pptApplication.SlideShowWindows == null || pptApplication.SlideShowWindows.Count == 0) + _isPptClickingBtnTurned = true; + + // 保存当前页墨迹 + var currentSlide = _pptManager?.GetCurrentSlideNumber() ?? 0; + if (currentSlide > 0) { - LogHelper.WriteLogToFile("PPT放映窗口不存在,无法执行上一页操作", LogHelper.LogType.Warning); - return; + _pptInkManager?.SaveCurrentSlideStrokes(currentSlide, inkCanvas.Strokes); } - // 安全访问当前幻灯片信息 - if (pptApplication.SlideShowWindows.Count >= 1) + + // 保存截图(如果启用) + if (inkCanvas.Strokes.Count > Settings.Automation.MinimumAutomationStrokeNumber && + Settings.PowerPointSettings.IsAutoSaveScreenShotInPowerPoint) { - var slideShowWindow = pptApplication.SlideShowWindows[1]; - if (slideShowWindow != null && slideShowWindow.View != null) - { - if (inkCanvas.Strokes.Count > Settings.Automation.MinimumAutomationStrokeNumber && - Settings.PowerPointSettings.IsAutoSaveScreenShotInPowerPoint) - SaveScreenShot(true, - slideShowWindow.Presentation.Name + "/" + - slideShowWindow.View.CurrentShowPosition); - slideShowWindow.Activate(); - slideShowWindow.View.Previous(); - LockInkForCurrentSlide(slideShowWindow.View.CurrentShowPosition); // 翻页后加锁 - } + var presentationName = _pptManager?.GetPresentationName() ?? ""; + SaveScreenShot(true, $"{presentationName}/{currentSlide}"); + } + + // 执行翻页 + if (_pptManager?.TryNavigatePrevious() == true) + { + // 翻页成功,等待事件处理墨迹切换 + LogHelper.WriteLogToFile("成功切换到上一页", LogHelper.LogType.Trace); + } + else + { + LogHelper.WriteLogToFile("切换到上一页失败", LogHelper.LogType.Warning); + _pptUIManager?.UpdateConnectionStatus(false); } } catch (Exception ex) { LogHelper.WriteLogToFile($"PPT上一页操作异常: {ex}", LogHelper.LogType.Error); - StackPanelPPTControls.Visibility = Visibility.Collapsed; - LeftBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed; - RightBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed; - LeftSidePanelForPPTNavigation.Visibility = Visibility.Collapsed; - RightSidePanelForPPTNavigation.Visibility = Visibility.Collapsed; + _pptUIManager?.UpdateConnectionStatus(false); } }); } private void BtnPPTSlidesDown_Click(object sender, RoutedEventArgs e) { Application.Current.Dispatcher.Invoke(() => { - // 切换前同步保存墨迹 - SaveCurrentSlideInkStrokes(); - _isPptClickingBtnTurned = true; - // 添加安全检查 - if (pptApplication == null) - { - LogHelper.WriteLogToFile("PPT应用程序为空,无法执行下一页操作", LogHelper.LogType.Warning); - return; - } try { - // 检查SlideShowWindows是否存在且有效 - if (pptApplication.SlideShowWindows == null || pptApplication.SlideShowWindows.Count == 0) + _isPptClickingBtnTurned = true; + + // 保存当前页墨迹 + var currentSlide = _pptManager?.GetCurrentSlideNumber() ?? 0; + if (currentSlide > 0) { - LogHelper.WriteLogToFile("PPT放映窗口不存在,无法执行下一页操作", LogHelper.LogType.Warning); - return; + _pptInkManager?.SaveCurrentSlideStrokes(currentSlide, inkCanvas.Strokes); } - // 安全访问当前幻灯片信息 - if (pptApplication.SlideShowWindows.Count >= 1) + + // 保存截图(如果启用) + if (inkCanvas.Strokes.Count > Settings.Automation.MinimumAutomationStrokeNumber && + Settings.PowerPointSettings.IsAutoSaveScreenShotInPowerPoint) { - var slideShowWindow = pptApplication.SlideShowWindows[1]; - if (slideShowWindow != null && slideShowWindow.View != null) - { - if (inkCanvas.Strokes.Count > Settings.Automation.MinimumAutomationStrokeNumber && - Settings.PowerPointSettings.IsAutoSaveScreenShotInPowerPoint) - SaveScreenShot(true, - slideShowWindow.Presentation.Name + "/" + - slideShowWindow.View.CurrentShowPosition); - slideShowWindow.Activate(); - slideShowWindow.View.Next(); - LockInkForCurrentSlide(slideShowWindow.View.CurrentShowPosition); // 翻页后加锁 - } + var presentationName = _pptManager?.GetPresentationName() ?? ""; + SaveScreenShot(true, $"{presentationName}/{currentSlide}"); + } + + // 执行翻页 + if (_pptManager?.TryNavigateNext() == true) + { + // 翻页成功,等待事件处理墨迹切换 + LogHelper.WriteLogToFile("成功切换到下一页", LogHelper.LogType.Trace); + } + else + { + LogHelper.WriteLogToFile("切换到下一页失败", LogHelper.LogType.Warning); + _pptUIManager?.UpdateConnectionStatus(false); } } catch (Exception ex) { LogHelper.WriteLogToFile($"PPT下一页操作异常: {ex}", LogHelper.LogType.Error); - StackPanelPPTControls.Visibility = Visibility.Collapsed; - LeftBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed; - RightBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed; - LeftSidePanelForPPTNavigation.Visibility = Visibility.Collapsed; - RightSidePanelForPPTNavigation.Visibility = Visibility.Collapsed; + _pptUIManager?.UpdateConnectionStatus(false); } }); } @@ -1152,39 +842,27 @@ namespace Ink_Canvas { if (!Settings.PowerPointSettings.EnablePPTButtonPageClickable) return; - // 添加安全检查 - if (pptApplication == null) + // 使用新的PPT管理器检查连接状态 + if (_pptManager?.IsConnected != true || _pptManager?.IsInSlideShow != true) { - LogHelper.WriteLogToFile("PPT应用程序为空,无法执行翻页操作", LogHelper.LogType.Warning); + LogHelper.WriteLogToFile("PPT未连接或未在放映状态,无法执行页码点击操作", LogHelper.LogType.Warning); return; } try { - // 检查SlideShowWindows是否存在且有效 - if (pptApplication.SlideShowWindows == null || pptApplication.SlideShowWindows.Count == 0) - { - LogHelper.WriteLogToFile("PPT放映窗口不存在,无法执行翻页操作", LogHelper.LogType.Warning); - return; - } - GridTransparencyFakeBackground.Opacity = 1; GridTransparencyFakeBackground.Background = new SolidColorBrush(StringToColor("#01FFFFFF")); CursorIcon_Click(null, null); - - try { - // 安全访问SlideShowWindows[1] - if (pptApplication.SlideShowWindows.Count >= 1) - { - var slideShowWindow = pptApplication.SlideShowWindows[1]; - if (slideShowWindow != null) - { - slideShowWindow.SlideNavigation.Visible = true; - } - } + + // 使用新的PPT管理器显示导航 + if (_pptManager.TryShowSlideNavigation()) + { + LogHelper.WriteLogToFile("成功显示PPT幻灯片导航", LogHelper.LogType.Trace); } - catch (Exception ex) { - LogHelper.WriteLogToFile($"设置PPT导航可见性失败: {ex}", LogHelper.LogType.Error); + else + { + LogHelper.WriteLogToFile("显示PPT幻灯片导航失败", LogHelper.LogType.Warning); } // 控制居中 @@ -1202,71 +880,46 @@ namespace Ink_Canvas { private void BtnPPTSlideShow_Click(object sender, RoutedEventArgs e) { new Thread(() => { try { - presentation.SlideShowSettings.Run(); + if (_pptManager?.TryStartSlideShow() != true) + { + LogHelper.WriteLogToFile("启动幻灯片放映失败", LogHelper.LogType.Warning); + } + } + catch (Exception ex) { + LogHelper.WriteLogToFile($"启动幻灯片放映异常: {ex}", LogHelper.LogType.Error); } - catch { } }).Start(); } private async void BtnPPTSlideShowEnd_Click(object sender, RoutedEventArgs e) { - // 添加安全检查 - if (pptApplication == null) - { - LogHelper.WriteLogToFile("PPT应用程序为空,无法结束放映", LogHelper.LogType.Warning); - return; - } - - // 切换前同步保存墨迹 - Application.Current.Dispatcher.Invoke(() => { - SaveCurrentSlideInkStrokes(); - }); - try { - // 检查SlideShowWindows是否存在且有效 - if (pptApplication.SlideShowWindows == null || pptApplication.SlideShowWindows.Count == 0) + // 保存当前页墨迹 + var currentSlide = _pptManager?.GetCurrentSlideNumber() ?? 0; + if (currentSlide > 0) { - LogHelper.WriteLogToFile("PPT放映窗口不存在,无法结束放映", LogHelper.LogType.Warning); - return; + Application.Current.Dispatcher.Invoke(() => { + _pptInkManager?.SaveCurrentSlideStrokes(currentSlide, inkCanvas.Strokes); + timeMachine.ClearStrokeHistory(); + }); } - Application.Current.Dispatcher.Invoke(() => { - try { - // 安全访问SlideShowWindows[1] - if (pptApplication.SlideShowWindows.Count >= 1) - { - var slideShowWindow = pptApplication.SlideShowWindows[1]; - if (slideShowWindow != null && slideShowWindow.View != null) - { - var ms = new MemoryStream(); - inkCanvas.Strokes.Save(ms); - ms.Position = 0; - memoryStreams[slideShowWindow.View.CurrentShowPosition] = ms; - timeMachine.ClearStrokeHistory(); - } - } - } - catch (Exception ex) { - LogHelper.WriteLogToFile($"保存当前页面墨迹失败: {ex}", LogHelper.LogType.Error); - } - }); - - new Thread(() => { - try { - // 安全访问SlideShowWindows[1] - if (pptApplication.SlideShowWindows.Count >= 1) - { - var slideShowWindow = pptApplication.SlideShowWindows[1]; - if (slideShowWindow != null && slideShowWindow.View != null) - { - slideShowWindow.View.Exit(); - } - } - } - catch (Exception ex) { - LogHelper.WriteLogToFile($"退出PPT放映失败: {ex}", LogHelper.LogType.Error); - } - }).Start(); + // 结束放映 + if (_pptManager?.TryEndSlideShow() == true) + { + LogHelper.WriteLogToFile("成功结束幻灯片放映", LogHelper.LogType.Event); + } + else + { + LogHelper.WriteLogToFile("结束幻灯片放映失败", LogHelper.LogType.Warning); + + // 手动更新UI状态,防止事件未触发 + await Application.Current.Dispatcher.InvokeAsync(() => { + _pptUIManager?.UpdateSlideShowStatus(false); + _pptUIManager?.UpdateSidebarExitButtons(false); + LogHelper.WriteLogToFile("手动更新放映结束UI状态", LogHelper.LogType.Trace); + }); + } HideSubPanels("cursor"); await Task.Delay(150); @@ -1275,30 +928,13 @@ namespace Ink_Canvas { catch (Exception ex) { LogHelper.WriteLogToFile($"结束PPT放映操作异常: {ex}", LogHelper.LogType.Error); + + // 确保UI状态正确 + await Application.Current.Dispatcher.InvokeAsync(() => { + _pptUIManager?.UpdateSlideShowStatus(false); + _pptUIManager?.UpdateSidebarExitButtons(false); + }); } - await Application.Current.Dispatcher.InvokeAsync(() => { - // 隐藏侧边栏退出按钮 - if (BtnExitPptFromSidebarLeft != null) - BtnExitPptFromSidebarLeft.Visibility = Visibility.Collapsed; - if (BtnExitPptFromSidebarRight != null) - BtnExitPptFromSidebarRight.Visibility = Visibility.Collapsed; - - // 确保所有放映模式按钮都被隐藏,防止PptApplication_SlideShowEnd事件未触发的情况 - try { - BtnPPTSlideShow.Visibility = Visibility.Visible; - BtnPPTSlideShowEnd.Visibility = Visibility.Collapsed; - StackPanelPPTControls.Visibility = Visibility.Collapsed; - LeftBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed; - RightBottomPanelForPPTNavigation.Visibility = Visibility.Collapsed; - LeftSidePanelForPPTNavigation.Visibility = Visibility.Collapsed; - RightSidePanelForPPTNavigation.Visibility = Visibility.Collapsed; - - LogHelper.WriteLogToFile("手动隐藏所有放映模式按钮", LogHelper.LogType.Trace); - } - catch (Exception ex) { - LogHelper.WriteLogToFile($"手动隐藏放映模式按钮失败: {ex}", LogHelper.LogType.Error); - } - }); } private void GridPPTControlPrevious_MouseDown(object sender, MouseButtonEventArgs e) @@ -1403,719 +1039,27 @@ namespace Ink_Canvas { BtnPPTSlideShowEnd_Click(BtnPPTSlideShowEnd, null); } - private void StartWppProcessCheckTimer() - { - // 新增:WPS联动未启用时不查杀wpp进程 - if (!Settings.PowerPointSettings.IsSupportWPS) - { - LogHelper.WriteLogToFile("WPS联动未启用,跳过WPP进程查杀", LogHelper.LogType.Trace); - return; - } - if (wppProcessCheckTimer != null) - { - wppProcessCheckTimer.Stop(); - wppProcessCheckTimer.Dispose(); - } - wppProcessCheckTimer = new Timer(500); // 改为500ms检查一次,提高响应速度 - wppProcessCheckTimer.Elapsed += WppProcessCheckTimer_Elapsed; - wppProcessCheckTimer.Start(); - LogHelper.WriteLogToFile("启动 WPP 进程检测定时器(前台窗口监控模式)", LogHelper.LogType.Trace); - } - private void WppProcessCheckTimer_Elapsed(object sender, ElapsedEventArgs e) - { - // 新增:WPS联动未启用时不查杀wpp进程 - if (!Settings.PowerPointSettings.IsSupportWPS) - { - LogHelper.WriteLogToFile("WPS联动未启用,跳过WPP进程查杀", LogHelper.LogType.Trace); - StopWppProcessCheckTimer(); - return; - } - try - { - if (wppProcess == null || hasWppProcessID == false) - { - StopWppProcessCheckTimer(); - return; - } - if (!Settings.PowerPointSettings.EnableWppProcessKill) - { - LogHelper.WriteLogToFile("WPP进程查杀功能已被关闭,跳过查杀", LogHelper.LogType.Trace); - StopWppProcessCheckTimer(); - return; - } - // 刷新进程状态 - wppProcess.Refresh(); - wppProcessCheckCount++; - if (wppProcess.HasExited) - { - LogHelper.WriteLogToFile("WPP 进程已正常关闭", LogHelper.LogType.Trace); - StopWppProcessCheckTimer(); - return; - } - // 检查前台WPS窗口是否存在 - bool isForegroundWpsWindowActive = IsForegroundWpsWindowStillActive(); - - if (isForegroundWpsWindowActive) - { - // 前台窗口仍然存在,继续监控 - if (wppProcessCheckCount % 10 == 0) // 每5秒记录一次日志,避免日志过多 - { - LogHelper.WriteLogToFile($"前台WPS窗口仍然存在,继续监控(已检查{wppProcessCheckCount}次)", LogHelper.LogType.Trace); - } - return; - } - - // 前台窗口已消失,立即结束WPP进程 - LogHelper.WriteLogToFile("检测到前台WPS窗口已消失,立即结束WPP进程", LogHelper.LogType.Event); - - // 检查所有WPS文档是否已保存 - bool allSaved = true; - try - { - if (pptApplication != null) - { - foreach (Presentation pres in pptApplication.Presentations) - { - if (pres.Saved == MsoTriState.msoFalse) - { - allSaved = false; - break; - } - } - } - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"检查WPS文档保存状态失败: {ex}", LogHelper.LogType.Error); - allSaved = false; // 出错时默认不安全 - } - if (!allSaved) - { - // 直接跳过弹窗,自动继续 - LogHelper.WriteLogToFile("检测到有未保存的WPS文档,但已取消弹窗提示,自动继续。", LogHelper.LogType.Trace); - } - // 立即结束WPP进程 - try - { - LogHelper.WriteLogToFile("前台窗口消失,开始结束WPP进程", LogHelper.LogType.Event); - - // 尝试优雅地结束进程 - if (!wppProcess.HasExited) - { - wppProcess.Kill(); - LogHelper.WriteLogToFile("前台窗口消失,成功结束WPP进程", LogHelper.LogType.Event); - } - else - { - LogHelper.WriteLogToFile("WPP进程已经自然结束", LogHelper.LogType.Trace); - } - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"结束WPP进程失败: {ex}", LogHelper.LogType.Error); - - // 如果常规方法失败,尝试强制结束 - try - { - var processes = Process.GetProcessesByName(wppProcess.ProcessName); - foreach (var process in processes) - { - if (process.Id == wppProcess.Id) - { - process.Kill(); - LogHelper.WriteLogToFile("强制结束WPP进程成功", LogHelper.LogType.Event); - break; - } - } - } - catch (Exception forceKillEx) - { - LogHelper.WriteLogToFile($"强制结束WPP进程也失败: {forceKillEx}", LogHelper.LogType.Error); - } - } - finally - { - StopWppProcessCheckTimer(); - } - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"WPP 进程检测失败: {ex}", LogHelper.LogType.Error); - StopWppProcessCheckTimer(); - } - } - private bool CheckForOtherWpsWindows() - { - try - { - if (wppProcess != null) - { - var wpsWindows = GetWpsWindowsByProcess(wppProcess.Id); - LogHelper.WriteLogToFile($"检测到{wpsWindows.Count}个WPS窗口", LogHelper.LogType.Trace); - - foreach (var window in wpsWindows) - { - LogHelper.WriteLogToFile($"WPS窗口: 标题='{window.Title}', 类名='{window.ClassName}', 可见={window.IsVisible}, 最小化={window.IsMinimized}", LogHelper.LogType.Trace); - } - - // 只要当前wpp进程没有可见窗口,就允许Kill - return wpsWindows.Any(w => w.IsVisible && !w.IsMinimized); - } - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"检查WPP窗口失败: {ex}", LogHelper.LogType.Error); - } - return false; // 出错时,默认允许Kill - } - /// - /// WPS窗口信息结构 - /// - private class WpsWindowInfo - { - public IntPtr Handle { get; set; } - public string Title { get; set; } - public string ClassName { get; set; } - public bool IsVisible { get; set; } - public bool IsMinimized { get; set; } - public bool IsMaximized { get; set; } - public ForegroundWindowInfo.RECT Rect { get; set; } - public uint ProcessId { get; set; } - public string ProcessName { get; set; } // 新增 - } - /// - /// 获取指定进程的所有WPS窗口 - /// - private List GetWpsWindowsByProcess(int processId) - { - var wpsWindows = new List(); - - try - { - EnumWindows((hWnd, lParam) => - { - try - { - if (!IsWindow(hWnd)) return true; - - uint windowProcessId; - GetWindowThreadProcessId(hWnd, out windowProcessId); - - if ((int)windowProcessId == processId) - { - var windowInfo = GetWindowInfo(hWnd); - if (IsWpsWindow(windowInfo)) - { - wpsWindows.Add(windowInfo); - LogHelper.WriteLogToFile($"发现WPS窗口: {windowInfo.Title} ({windowInfo.ClassName})", LogHelper.LogType.Trace); - } - } - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"枚举窗口时出错: {ex}", LogHelper.LogType.Error); - } - return true; - }, IntPtr.Zero); - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"获取WPS窗口失败: {ex}", LogHelper.LogType.Error); - } - - return wpsWindows; - } - /// - /// 获取窗口详细信息 - /// - private WpsWindowInfo GetWindowInfo(IntPtr hWnd) - { - var windowInfo = new WpsWindowInfo - { - Handle = hWnd, - IsVisible = IsWindowVisible(hWnd), - IsMinimized = IsIconic(hWnd), - IsMaximized = IsZoomed(hWnd) - }; - // 获取窗口标题 - var windowTitle = new StringBuilder(256); - GetWindowText(hWnd, windowTitle, 256); - windowInfo.Title = windowTitle.ToString().Trim(); - // 获取窗口类名 - var className = new StringBuilder(256); - GetClassName(hWnd, className, 256); - windowInfo.ClassName = className.ToString().Trim(); - // 获取窗口位置 - GetWindowRect(hWnd, out ForegroundWindowInfo.RECT rect); - windowInfo.Rect = rect; - // 获取进程ID - uint processId; - GetWindowThreadProcessId(hWnd, out processId); - windowInfo.ProcessId = processId; - // 新增:获取进程名 - windowInfo.ProcessName = ""; - try - { - var proc = Process.GetProcessById((int)processId); - windowInfo.ProcessName = proc.ProcessName.ToLower(); - } - catch { } - return windowInfo; - } - /// - /// 判断是否为WPS窗口 - /// - private bool IsWpsWindow(WpsWindowInfo windowInfo) - { - if (string.IsNullOrEmpty(windowInfo.Title) && string.IsNullOrEmpty(windowInfo.ClassName)) - return false; - var title = windowInfo.Title.ToLower(); - var className = windowInfo.ClassName.ToLower(); - var processName = windowInfo.ProcessName ?? ""; - - // WPS相关关键词 - var wpsKeywords = new[] { "wps", "wpp", "kingsoft", "金山", "wps演示", "wps presentation", "wps office", "kingsoft office" }; - // 微软Office相关进程名 - var msOfficeProcess = new[] { "powerpnt", "excel", "word", "onenote", "outlook", "microsoftoffice", "office" }; - - // 只要进程名是微软Office,直接排除 - if (msOfficeProcess.Any(keyword => processName.Contains(keyword))) - return false; - - // 只要进程名是WPS/WPP/Kingsoft,直接通过 - if (wpsKeywords.Any(keyword => processName.Contains(keyword))) - return true; - - // 标题或类名包含WPS相关关键词 - bool hasWpsTitle = wpsKeywords.Any(keyword => title.Contains(keyword)); - bool hasWpsClass = wpsKeywords.Any(keyword => className.Contains(keyword)); - bool isWpsClass = className.Contains("wps") || className.Contains("kingsoft") || className.Contains("wpp"); - bool hasValidSize = (windowInfo.Rect.Right - windowInfo.Rect.Left) > 0 && (windowInfo.Rect.Bottom - windowInfo.Rect.Top) > 0; - - return (hasWpsTitle || hasWpsClass || isWpsClass) && hasValidSize; - } - - /// - /// 获取前台WPS窗口 - /// - private WpsWindowInfo GetForegroundWpsWindow() - { - try - { - var foregroundHwnd = GetForegroundWindow(); - if (foregroundHwnd != IntPtr.Zero && IsWindow(foregroundHwnd)) - { - var windowInfo = GetWindowInfo(foregroundHwnd); - if (IsWpsWindow(windowInfo)) - { - LogHelper.WriteLogToFile($"前台WPS窗口: {windowInfo.Title}", LogHelper.LogType.Trace); - return windowInfo; - } - } - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"获取前台WPS窗口失败: {ex}", LogHelper.LogType.Error); - } - return null; - } - - /// - /// 检查前台WPS窗口是否仍然存在(更精确的检测) - /// - private bool IsForegroundWpsWindowStillActive() - { - try - { - var currentTime = DateTime.Now; - var currentForegroundWindow = GetForegroundWpsWindow(); - - // 检查窗口状态是否发生变化 - bool windowStateChanged = false; - if (lastForegroundWpsWindow != null && currentForegroundWindow != null) - { - // 检查窗口是否发生了变化 - if (lastForegroundWpsWindow.Handle != currentForegroundWindow.Handle || - lastForegroundWpsWindow.Title != currentForegroundWindow.Title) - { - windowStateChanged = true; - LogHelper.WriteLogToFile($"前台WPS窗口发生变化: {lastForegroundWpsWindow.Title} -> {currentForegroundWindow.Title}", LogHelper.LogType.Trace); - } - } - else if (lastForegroundWpsWindow == null && currentForegroundWindow != null) - { - // 从无窗口变为有窗口 - windowStateChanged = true; - LogHelper.WriteLogToFile($"检测到新的前台WPS窗口: {currentForegroundWindow.Title}", LogHelper.LogType.Trace); - } - else if (lastForegroundWpsWindow != null && currentForegroundWindow == null) - { - // 从有窗口变为无窗口 - windowStateChanged = true; - LogHelper.WriteLogToFile($"前台WPS窗口已消失: {lastForegroundWpsWindow.Title}", LogHelper.LogType.Trace); - } - - // 更新记录 - lastForegroundWpsWindow = currentForegroundWindow; - lastWindowCheckTime = currentTime; - - if (currentForegroundWindow != null) - { - // 验证窗口仍然有效 - if (IsWindow(currentForegroundWindow.Handle) && IsWindowVisible(currentForegroundWindow.Handle)) - { - return true; - } - } - - // 方法2:检查所有WPS进程的活跃窗口 - var wpsProcesses = GetWpsProcesses(); - foreach (var process in wpsProcesses) - { - var windows = GetWpsWindowsByProcess(process.Id); - if (windows.Any(w => w.IsVisible && !w.IsMinimized && w.Handle == GetForegroundWindow())) - { - return true; - } - } - - // 方法3:检查顶级WPS窗口 - var topLevelWindows = GetTopLevelWpsWindows(); - if (topLevelWindows.Any(w => w.IsVisible && !w.IsMinimized)) - { - return true; - } - - return false; - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"检查前台WPS窗口状态失败: {ex}", LogHelper.LogType.Error); - return false; - } - } - - /// - /// 获取顶级WPS窗口(包括前台窗口和最近的活跃窗口) - /// - private List GetTopLevelWpsWindows() - { - var topLevelWindows = new List(); - - try - { - // 获取前台窗口 - var foregroundWindow = GetForegroundWpsWindow(); - if (foregroundWindow != null) - { - topLevelWindows.Add(foregroundWindow); - } - - // 获取所有可见的WPS窗口,按层级排序 - var allWpsWindows = new List(); - var wpsProcesses = GetWpsProcesses(); - - foreach (var process in wpsProcesses) - { - var windows = GetWpsWindowsByProcess(process.Id); - allWpsWindows.AddRange(windows.Where(w => w.IsVisible && !w.IsMinimized)); - } - - // 按窗口位置排序,优先选择屏幕中央的窗口 - var screenCenter = new Point( - Screen.PrimaryScreen.Bounds.Width / 2, - Screen.PrimaryScreen.Bounds.Height / 2 - ); - - var sortedWindows = allWpsWindows - .Where(w => !topLevelWindows.Any(t => t.Handle == w.Handle)) // 排除已添加的前台窗口 - .OrderBy(w => Math.Abs((w.Rect.Left + w.Rect.Right) / 2 - screenCenter.X) + - Math.Abs((w.Rect.Top + w.Rect.Bottom) / 2 - screenCenter.Y)) - .Take(3); // 取最近的3个窗口 - - topLevelWindows.AddRange(sortedWindows); - - LogHelper.WriteLogToFile($"找到{topLevelWindows.Count}个顶级WPS窗口", LogHelper.LogType.Trace); - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"获取顶级WPS窗口失败: {ex}", LogHelper.LogType.Error); - } - - return topLevelWindows; - } - - /// - /// 检查是否有活跃的WPS窗口(包括前台窗口) - /// - private bool HasActiveWpsWindows() - { - return HasActiveWpsWindowsWithRetry(3); // 重试3次 - } - - /// - /// 带重试机制的WPS窗口检测 - /// - private bool HasActiveWpsWindowsWithRetry(int maxRetries) - { - for (int attempt = 1; attempt <= maxRetries; attempt++) - { - try - { - LogHelper.WriteLogToFile($"第{attempt}次尝试检测WPS窗口", LogHelper.LogType.Trace); - - // 首先检查前台窗口 - var foregroundWpsWindow = GetForegroundWpsWindow(); - if (foregroundWpsWindow != null) - { - LogHelper.WriteLogToFile($"第{attempt}次尝试检测到前台WPS窗口", LogHelper.LogType.Trace); - return true; - } - - // 检查顶级WPS窗口 - var topLevelWindows = GetTopLevelWpsWindows(); - if (topLevelWindows.Any()) - { - LogHelper.WriteLogToFile($"第{attempt}次尝试检测到{topLevelWindows.Count}个顶级WPS窗口", LogHelper.LogType.Trace); - return true; - } - - // 然后检查所有WPS进程的窗口 - var wpsProcesses = GetWpsProcesses(); - foreach (var process in wpsProcesses) - { - var windows = GetWpsWindowsByProcess(process.Id); - if (windows.Any(w => w.IsVisible && !w.IsMinimized)) - { - LogHelper.WriteLogToFile($"第{attempt}次尝试检测到进程{process.Id}的活跃WPS窗口", LogHelper.LogType.Trace); - return true; - } - } - - // 如果还有重试机会,等待一小段时间再重试 - if (attempt < maxRetries) - { - Thread.Sleep(100); // 等待100ms - } - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"第{attempt}次尝试检查活跃WPS窗口失败: {ex}", LogHelper.LogType.Error); - - // 如果还有重试机会,等待一小段时间再重试 - if (attempt < maxRetries) - { - Thread.Sleep(100); // 等待100ms - } - } - } - - LogHelper.WriteLogToFile($"经过{maxRetries}次尝试,未检测到活跃的WPS窗口", LogHelper.LogType.Trace); - return false; - } - - /// - /// 获取所有WPS相关进程 - /// - private List GetWpsProcesses() - { - var wpsProcesses = new List(); - try - { - var allProcesses = Process.GetProcesses(); - foreach (var process in allProcesses) - { - try - { - var pname = process.ProcessName.ToLower(); - // 只允许WPS/WPP相关进程,排除PowerPoint及微软Office - if ((pname.Contains("wps") || pname.Contains("wpp") || pname.Contains("presentation")) - && !pname.Contains("powerpnt") - && !pname.Contains("office") - && !pname.Contains("onenote") - && !pname.Contains("excel") - && !pname.Contains("word") - && !pname.Contains("outlook") - && !pname.Contains("microsoft")) - { - wpsProcesses.Add(process); - LogHelper.WriteLogToFile($"发现WPS进程: {process.ProcessName} (PID: {process.Id})", LogHelper.LogType.Trace); - } - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"检查进程{process.ProcessName}失败: {ex}", LogHelper.LogType.Error); - } - } - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"获取WPS进程失败: {ex}", LogHelper.LogType.Error); - } - return wpsProcesses; - } - - private bool CheckForWpsWindowsByEnumeration() - { - try - { - var wpsWindowCount = 0; - var currentProcessId = wppProcess?.Id ?? 0; - - EnumWindows((hWnd, lParam) => - { - try - { - uint windowProcessId; - GetWindowThreadProcessId(hWnd, out windowProcessId); - - // 检查是否是WPP进程的窗口 - if (windowProcessId == currentProcessId) - { - var windowTitle = new StringBuilder(256); - GetWindowText(hWnd, windowTitle, 256); - var title = windowTitle.ToString().Trim(); - - // 检查窗口标题是否包含WPS相关标识 - if (!string.IsNullOrEmpty(title) && - (title.Contains("WPS") || title.Contains("演示文稿") || title.Contains(".ppt") || title.Contains(".pptx"))) - { - wpsWindowCount++; - LogHelper.WriteLogToFile($"发现WPS窗口: {title}", LogHelper.LogType.Trace); - } - } - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"枚举窗口时出错: {ex}", LogHelper.LogType.Error); - } - - return true; // 继续枚举 - }, IntPtr.Zero); - - if (wpsWindowCount > 1) - { - LogHelper.WriteLogToFile($"检测到{wpsWindowCount}个WPS窗口,可能存在多个文档", LogHelper.LogType.Trace); - return true; - } - - return false; - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"通过枚举检查WPS窗口失败: {ex}", LogHelper.LogType.Error); - return false; - } - } - - private void StopWppProcessCheckTimer() - { - if (wppProcessCheckTimer != null) - { - wppProcessCheckTimer.Stop(); - wppProcessCheckTimer.Dispose(); - wppProcessCheckTimer = null; - } - - wppProcess = null; - hasWppProcessID = false; - wppProcessRecordTime = DateTime.MinValue; - wppProcessCheckCount = 0; - lastForegroundWpsWindow = null; - lastWindowCheckTime = DateTime.MinValue; - LogHelper.WriteLogToFile("停止 WPP 进程检测定时器", LogHelper.LogType.Trace); - } - - /// - /// 计算文件路径的哈希值,用于生成唯一的文件夹标识符 - /// - /// 文件路径 - /// 文件路径的哈希值字符串 - private string GetFileHash(string filePath) - { - try - { - if (string.IsNullOrEmpty(filePath)) - return "unknown"; - - // 使用文件路径的哈希值作为唯一标识符 - using (var md5 = MD5.Create()) - { - byte[] hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(filePath)); - return BitConverter.ToString(hashBytes).Replace("-", "").Substring(0, 8); - } - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"计算文件哈希值失败: {ex}", LogHelper.LogType.Error); - return "error"; - } - } - - // 新增:同步保存当前页面墨迹的方法 - private void SaveCurrentSlideInkStrokes() - { - try - { - if (pptApplication != null && pptApplication.SlideShowWindows != null && pptApplication.SlideShowWindows.Count >= 1) - { - var slideShowWindow = pptApplication.SlideShowWindows[1]; - if (slideShowWindow != null && slideShowWindow.View != null) - { - int currentSlide = slideShowWindow.View.CurrentShowPosition; - if (!CanWriteInk(currentSlide)) - { - LogHelper.WriteLogToFile($"墨迹写入被锁定或页码不符,当前页:{currentSlide},锁定页:{lockedSlideIndex}", LogHelper.LogType.Warning); - return; - } - var ms = new MemoryStream(); - inkCanvas.Strokes.Save(ms); - ms.Position = 0; - memoryStreams[currentSlide] = ms; - } - } - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"保存当前页面墨迹失败: {ex}", LogHelper.LogType.Error); - } - } - - // 翻页后调用 - private void LockInkForCurrentSlide(int slideIndex) - { - inkLockUntil = DateTime.Now.AddMilliseconds(InkLockMilliseconds); - lockedSlideIndex = slideIndex; - } - - // 写入墨迹前调用 - private bool CanWriteInk(int currentSlideIndex) - { - if (DateTime.Now < inkLockUntil) return false; - if (currentSlideIndex != lockedSlideIndex) return false; - return true; - } } } diff --git a/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs b/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs index 8fc996b1..16809605 100644 --- a/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs +++ b/Ink Canvas/MainWindow_cs/MW_Save&OpenStrokes.cs @@ -57,18 +57,21 @@ namespace Ink_Canvas { List allPageStrokes = new List(); // 检查PPT放映模式下的多页面墨迹 - if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible && pptApplication != null) + if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible && _pptManager?.IsConnected == true) { hasMultiplePages = true; // 收集PPT放映模式下的所有页面墨迹 - for (int i = 1; i <= pptApplication.SlideShowWindows[1].Presentation.Slides.Count; i++) + var totalSlides = _pptManager.SlidesCount; + var currentSlide = _pptManager.GetCurrentSlideNumber(); + + for (int i = 1; i <= totalSlides; i++) { - if (memoryStreams[i] != null && memoryStreams[i].Length > 8) + var slideStrokes = _pptInkManager?.LoadSlideStrokes(i); + if (slideStrokes != null && slideStrokes.Count > 0) { - memoryStreams[i].Position = 0; - allPageStrokes.Add(new StrokeCollection(memoryStreams[i])); + allPageStrokes.Add(slideStrokes); } - else if (i == previousSlideID && inkCanvas.Strokes.Count > 0) + else if (i == currentSlide && inkCanvas.Strokes.Count > 0) { // 当前页面的墨迹 allPageStrokes.Add(inkCanvas.Strokes.Clone()); @@ -485,10 +488,8 @@ namespace Ink_Canvas { timeMachine.ClearStrokeHistory(); // 重置PPT墨迹存储 - if (memoryStreams == null) { - memoryStreams = new MemoryStream[50]; - } - + _pptInkManager?.ClearAllStrokes(); + // 读取所有页面的墨迹文件 var files = Directory.GetFiles(tempDir, "page_*.icstk"); foreach (var file in files) { @@ -497,23 +498,19 @@ namespace Ink_Canvas { using (var fs = new FileStream(file, FileMode.Open, FileAccess.Read)) { var strokes = new StrokeCollection(fs); if (strokes.Count > 0) { - var ms = new MemoryStream(); - strokes.Save(ms); - ms.Position = 0; - memoryStreams[pageNumber] = ms; + _pptInkManager?.SaveCurrentSlideStrokes(pageNumber, strokes); } } } } - + // 恢复当前页面的墨迹 - if (pptApplication.SlideShowWindows.Count > 0) { - int currentSlide = pptApplication.SlideShowWindows[1].View.CurrentShowPosition; - if (memoryStreams[currentSlide] != null && memoryStreams[currentSlide].Length > 0) { - memoryStreams[currentSlide].Position = 0; - inkCanvas.Strokes.Add(new StrokeCollection(memoryStreams[currentSlide])); + if (_pptManager?.IsInSlideShow == true) { + int currentSlide = _pptManager.GetCurrentSlideNumber(); + var currentStrokes = _pptInkManager?.LoadSlideStrokes(currentSlide); + if (currentStrokes != null && currentStrokes.Count > 0) { + inkCanvas.Strokes.Add(currentStrokes); } - previousSlideID = currentSlide; } LogHelper.WriteLogToFile($"成功恢复PPT墨迹,共{files.Length}页"); diff --git a/Ink Canvas/MainWindow_cs/MW_Settings.cs b/Ink Canvas/MainWindow_cs/MW_Settings.cs index b0fd4ffb..527cc08b 100644 --- a/Ink Canvas/MainWindow_cs/MW_Settings.cs +++ b/Ink Canvas/MainWindow_cs/MW_Settings.cs @@ -95,10 +95,19 @@ namespace Ink_Canvas { Settings.PowerPointSettings.PowerPointSupport = ToggleSwitchSupportPowerPoint.IsOn; SaveSettingsToFile(); + // 使用新的PPT管理器 if (Settings.PowerPointSettings.PowerPointSupport) - timerCheckPPT.Start(); + { + if (_pptManager == null) + { + InitializePPTManagers(); + } + StartPPTMonitoring(); + } else - timerCheckPPT.Stop(); + { + StopPPTMonitoring(); + } } private void ToggleSwitchShowCanvasAtNewSlideShow_Toggled(object sender, RoutedEventArgs e) { @@ -419,7 +428,12 @@ namespace Ink_Canvas { if (!isLoaded) return; Settings.PowerPointSettings.ShowPPTButton = ToggleSwitchShowPPTButton.IsOn; SaveSettingsToFile(); - UpdatePPTBtnDisplaySettingsStatus(); + // 更新PPT UI管理器设置 + if (_pptUIManager != null) + { + _pptUIManager.ShowPPTButton = Settings.PowerPointSettings.ShowPPTButton; + _pptUIManager.UpdateNavigationPanelsVisibility(); + } UpdatePPTBtnPreview(); } @@ -436,7 +450,12 @@ namespace Ink_Canvas { c[0] = (bool)((CheckBox)sender).IsChecked ? '2' : '1'; Settings.PowerPointSettings.PPTButtonsDisplayOption = int.Parse(new string(c)); SaveSettingsToFile(); - if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) UpdatePPTBtnDisplaySettingsStatus(); + // 更新PPT UI管理器设置 + if (_pptUIManager != null && BtnPPTSlideShowEnd.Visibility == Visibility.Visible) + { + _pptUIManager.PPTButtonsDisplayOption = Settings.PowerPointSettings.PPTButtonsDisplayOption; + _pptUIManager.UpdateNavigationPanelsVisibility(); + } UpdatePPTBtnPreview(); } @@ -448,7 +467,12 @@ namespace Ink_Canvas { c[1] = (bool)((CheckBox)sender).IsChecked ? '2' : '1'; Settings.PowerPointSettings.PPTButtonsDisplayOption = int.Parse(new string(c)); SaveSettingsToFile(); - if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) UpdatePPTBtnDisplaySettingsStatus(); + // 更新PPT UI管理器设置 + if (_pptUIManager != null && BtnPPTSlideShowEnd.Visibility == Visibility.Visible) + { + _pptUIManager.PPTButtonsDisplayOption = Settings.PowerPointSettings.PPTButtonsDisplayOption; + _pptUIManager.UpdateNavigationPanelsVisibility(); + } UpdatePPTBtnPreview(); } @@ -460,7 +484,12 @@ namespace Ink_Canvas { c[2] = (bool)((CheckBox)sender).IsChecked ? '2' : '1'; Settings.PowerPointSettings.PPTButtonsDisplayOption = int.Parse(new string(c)); SaveSettingsToFile(); - if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) UpdatePPTBtnDisplaySettingsStatus(); + // 更新PPT UI管理器设置 + if (_pptUIManager != null && BtnPPTSlideShowEnd.Visibility == Visibility.Visible) + { + _pptUIManager.PPTButtonsDisplayOption = Settings.PowerPointSettings.PPTButtonsDisplayOption; + _pptUIManager.UpdateNavigationPanelsVisibility(); + } UpdatePPTBtnPreview(); } @@ -472,7 +501,12 @@ namespace Ink_Canvas { c[3] = (bool)((CheckBox)sender).IsChecked ? '2' : '1'; Settings.PowerPointSettings.PPTButtonsDisplayOption = int.Parse(new string(c)); SaveSettingsToFile(); - if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) UpdatePPTBtnDisplaySettingsStatus(); + // 更新PPT UI管理器设置 + if (_pptUIManager != null && BtnPPTSlideShowEnd.Visibility == Visibility.Visible) + { + _pptUIManager.PPTButtonsDisplayOption = Settings.PowerPointSettings.PPTButtonsDisplayOption; + _pptUIManager.UpdateNavigationPanelsVisibility(); + } UpdatePPTBtnPreview(); } @@ -484,7 +518,12 @@ namespace Ink_Canvas { c[0] = (bool)((CheckBox)sender).IsChecked ? '2' : '1'; Settings.PowerPointSettings.PPTSButtonsOption = int.Parse(new string(c)); SaveSettingsToFile(); - if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) UpdatePPTBtnStyleSettingsStatus(); + // 更新PPT UI管理器设置 + if (_pptUIManager != null && BtnPPTSlideShowEnd.Visibility == Visibility.Visible) + { + _pptUIManager.PPTSButtonsOption = Settings.PowerPointSettings.PPTSButtonsOption; + _pptUIManager.UpdateNavigationButtonStyles(); + } UpdatePPTBtnPreview(); } @@ -496,7 +535,12 @@ namespace Ink_Canvas { c[1] = (bool)((CheckBox)sender).IsChecked ? '2' : '1'; Settings.PowerPointSettings.PPTSButtonsOption = int.Parse(new string(c)); SaveSettingsToFile(); - if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) UpdatePPTBtnStyleSettingsStatus(); + // 更新PPT UI管理器设置 + if (_pptUIManager != null && BtnPPTSlideShowEnd.Visibility == Visibility.Visible) + { + _pptUIManager.PPTSButtonsOption = Settings.PowerPointSettings.PPTSButtonsOption; + _pptUIManager.UpdateNavigationButtonStyles(); + } UpdatePPTBtnPreview(); } @@ -508,7 +552,12 @@ namespace Ink_Canvas { c[2] = (bool)((CheckBox)sender).IsChecked ? '2' : '1'; Settings.PowerPointSettings.PPTSButtonsOption = int.Parse(new string(c)); SaveSettingsToFile(); - if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) UpdatePPTBtnStyleSettingsStatus(); + // 更新PPT UI管理器设置 + if (_pptUIManager != null && BtnPPTSlideShowEnd.Visibility == Visibility.Visible) + { + _pptUIManager.PPTSButtonsOption = Settings.PowerPointSettings.PPTSButtonsOption; + _pptUIManager.UpdateNavigationButtonStyles(); + } UpdatePPTBtnPreview(); } @@ -520,7 +569,12 @@ namespace Ink_Canvas { c[0] = (bool)((CheckBox)sender).IsChecked ? '2' : '1'; Settings.PowerPointSettings.PPTBButtonsOption = int.Parse(new string(c)); SaveSettingsToFile(); - if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) UpdatePPTBtnStyleSettingsStatus(); + // 更新PPT UI管理器设置 + if (_pptUIManager != null && BtnPPTSlideShowEnd.Visibility == Visibility.Visible) + { + _pptUIManager.PPTBButtonsOption = Settings.PowerPointSettings.PPTBButtonsOption; + _pptUIManager.UpdateNavigationButtonStyles(); + } UpdatePPTBtnPreview(); } @@ -532,7 +586,7 @@ namespace Ink_Canvas { c[1] = (bool)((CheckBox)sender).IsChecked ? '2' : '1'; Settings.PowerPointSettings.PPTBButtonsOption = int.Parse(new string(c)); SaveSettingsToFile(); - if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) UpdatePPTBtnStyleSettingsStatus(); + UpdatePPTUIManagerSettings(); UpdatePPTBtnPreview(); } @@ -544,7 +598,7 @@ namespace Ink_Canvas { c[2] = (bool)((CheckBox)sender).IsChecked ? '2' : '1'; Settings.PowerPointSettings.PPTBButtonsOption = int.Parse(new string(c)); SaveSettingsToFile(); - if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) UpdatePPTBtnStyleSettingsStatus(); + UpdatePPTUIManagerSettings(); UpdatePPTBtnPreview(); } @@ -552,7 +606,7 @@ namespace Ink_Canvas { if (!isLoaded) return; Settings.PowerPointSettings.PPTLSButtonPosition = (int)PPTButtonLeftPositionValueSlider.Value; UpdatePPTBtnSlidersStatus(); - if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) UpdatePPTBtnDisplaySettingsStatus(); + UpdatePPTUIManagerSettings(); SliderDelayAction.DebounceAction(2000, null, SaveSettingsToFile); UpdatePPTBtnPreview(); } @@ -686,11 +740,28 @@ namespace Ink_Canvas { if (!isLoaded) return; Settings.PowerPointSettings.PPTRSButtonPosition = (int)PPTButtonRightPositionValueSlider.Value; UpdatePPTBtnSlidersStatus(); - if (BtnPPTSlideShowEnd.Visibility == Visibility.Visible) UpdatePPTBtnDisplaySettingsStatus(); + UpdatePPTUIManagerSettings(); SliderDelayAction.DebounceAction(2000,null, SaveSettingsToFile); UpdatePPTBtnPreview(); } + /// + /// 更新PPT UI管理器设置的通用方法 + /// + private void UpdatePPTUIManagerSettings() + { + if (_pptUIManager != null && BtnPPTSlideShowEnd.Visibility == Visibility.Visible) + { + _pptUIManager.PPTButtonsDisplayOption = Settings.PowerPointSettings.PPTButtonsDisplayOption; + _pptUIManager.PPTSButtonsOption = Settings.PowerPointSettings.PPTSButtonsOption; + _pptUIManager.PPTBButtonsOption = Settings.PowerPointSettings.PPTBButtonsOption; + _pptUIManager.PPTLSButtonPosition = Settings.PowerPointSettings.PPTLSButtonPosition; + _pptUIManager.PPTRSButtonPosition = Settings.PowerPointSettings.PPTRSButtonPosition; + _pptUIManager.UpdateNavigationPanelsVisibility(); + _pptUIManager.UpdateNavigationButtonStyles(); + } + } + private void UpdatePPTBtnPreview() { //new BitmapImage(new Uri("pack://application:,,,/Resources/new-icons/unfold-chevron.png")); var bopt = Settings.PowerPointSettings.PPTBButtonsOption.ToString(); diff --git a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs index 8f761e53..03e8dcd7 100644 --- a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs +++ b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs @@ -278,10 +278,10 @@ namespace Ink_Canvas { if (Settings.PowerPointSettings.PowerPointSupport) { ToggleSwitchSupportPowerPoint.IsOn = true; - timerCheckPPT.Start(); + // PPT监控将在Window_Loaded中启动 } else { ToggleSwitchSupportPowerPoint.IsOn = false; - timerCheckPPT.Stop(); + // PPT监控将保持停止状态 } ToggleSwitchShowCanvasAtNewSlideShow.IsOn = Settings.PowerPointSettings.IsShowCanvasAtNewSlideShow; diff --git a/Ink Canvas/MainWindow_cs/MW_Timer.cs b/Ink Canvas/MainWindow_cs/MW_Timer.cs index 26d3dd55..f1948f2d 100644 --- a/Ink Canvas/MainWindow_cs/MW_Timer.cs +++ b/Ink Canvas/MainWindow_cs/MW_Timer.cs @@ -88,8 +88,9 @@ namespace Ink_Canvas { } private void InitTimers() { - timerCheckPPT.Elapsed += TimerCheckPPT_Elapsed; - timerCheckPPT.Interval = 500; + // PPT检查现在由PPTManager处理,不再需要定时器 + // timerCheckPPT.Elapsed += TimerCheckPPT_Elapsed; + // timerCheckPPT.Interval = 500; timerKillProcess.Elapsed += TimerKillProcess_Elapsed; timerKillProcess.Interval = 2000; timerCheckAutoFold.Elapsed += timerCheckAutoFold_Elapsed;