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;