diff --git a/Ink Canvas/Helpers/PPTManager.cs b/Ink Canvas/Helpers/PPTManager.cs
index 7884baab..84446eea 100644
--- a/Ink Canvas/Helpers/PPTManager.cs
+++ b/Ink Canvas/Helpers/PPTManager.cs
@@ -29,7 +29,7 @@ namespace Ink_Canvas.Helpers
#endregion
#region Properties
- public object PPTApplication { get; private set; }
+ public dynamic PPTApplication { get; private set; }
public dynamic CurrentPresentation { get; private set; }
public dynamic CurrentSlides { get; private set; }
public dynamic CurrentSlide { get; private set; }
@@ -44,8 +44,7 @@ namespace Ink_Canvas.Helpers
if (!Marshal.IsComObject(PPTApplication)) return false;
// 尝试访问一个简单的属性来验证连接是否有效
- dynamic app = PPTApplication;
- var _ = app.Name;
+ var _ = PPTApplication.Name;
return true;
}
catch (COMException comEx)
@@ -75,8 +74,7 @@ namespace Ink_Canvas.Helpers
{
if (PPTApplication == null || !Marshal.IsComObject(PPTApplication)) return false;
- dynamic app = PPTApplication;
- slideShowWindows = app.SlideShowWindows;
+ slideShowWindows = PPTApplication.SlideShowWindows;
if (slideShowWindows == null) return false;
dynamic ssw = slideShowWindows;
@@ -130,9 +128,9 @@ namespace Ink_Canvas.Helpers
#endregion
#region Private Fields
- private Timer _connectionCheckTimer;
- private Timer _slideShowStateCheckTimer;
- private Timer _wpsProcessCheckTimer;
+ private Thread _monitoringThread;
+ private bool _shouldStop = false;
+ private readonly object _monitoringLock = new object();
private Process _wpsProcess;
private bool _isModuleUnloading = false;
private bool _hasWpsProcessId;
@@ -147,7 +145,7 @@ namespace Ink_Canvas.Helpers
private dynamic _pptActivePresentation;
private dynamic _pptSlideShowWindow;
private int _polling = 0;
- private bool _forcePolling = false;
+ private bool _forcePolling = true;
private bool _bindingEvents = false;
private DateTime _updateTime;
private int _lastPolledSlideNumber = -1;
@@ -156,36 +154,48 @@ namespace Ink_Canvas.Helpers
#region Constructor & Initialization
public PPTManager()
{
- InitializeConnectionTimer();
- }
-
- private void InitializeConnectionTimer()
- {
- _connectionCheckTimer = new Timer(500);
- _connectionCheckTimer.Elapsed += OnConnectionCheckTimerElapsed;
- _connectionCheckTimer.AutoReset = true;
-
- _slideShowStateCheckTimer = new Timer(1000);
- _slideShowStateCheckTimer.Elapsed += OnSlideShowStateCheckTimerElapsed;
- _slideShowStateCheckTimer.AutoReset = true;
}
public void StartMonitoring()
{
- if (!_disposed)
+ if (_disposed) return;
+
+ lock (_monitoringLock)
{
- _connectionCheckTimer?.Start();
- _slideShowStateCheckTimer?.Start();
+ if (_monitoringThread != null && _monitoringThread.IsAlive)
+ {
+ return; // 已经在运行
+ }
+
+ _shouldStop = false;
+ _monitoringThread = new Thread(PptComService)
+ {
+ IsBackground = true,
+ Name = "PPTMonitoringThread"
+ };
+ _monitoringThread.Start();
LogHelper.WriteLogToFile("PPT监控已启动", LogHelper.LogType.Trace);
}
}
public void StopMonitoring()
{
- _connectionCheckTimer?.Stop();
- _slideShowStateCheckTimer?.Stop();
- DisconnectFromPPT();
- LogHelper.WriteLogToFile("PPT监控已停止", LogHelper.LogType.Trace);
+ lock (_monitoringLock)
+ {
+ _shouldStop = true;
+
+ if (_monitoringThread != null && _monitoringThread.IsAlive)
+ {
+ // 等待线程退出,最多等待2秒
+ if (!_monitoringThread.Join(2000))
+ {
+ LogHelper.WriteLogToFile("等待监控线程退出超时", LogHelper.LogType.Warning);
+ }
+ }
+
+ DisconnectFromPPT();
+ LogHelper.WriteLogToFile("PPT监控已停止", LogHelper.LogType.Trace);
+ }
}
#endregion
@@ -287,8 +297,7 @@ namespace Ink_Canvas.Helpers
{
try
{
- dynamic app = PPTApplication;
- _pptActivePresentation = app.ActivePresentation;
+ _pptActivePresentation = PPTApplication.ActivePresentation;
if (_pptActivePresentation != null)
{
LogHelper.WriteLogToFile("轮询模式:初始化_pptActivePresentation", LogHelper.LogType.Trace);
@@ -326,8 +335,7 @@ namespace Ink_Canvas.Helpers
try
{
- dynamic app = PPTApplication;
- activePresentation = app.ActivePresentation;
+ activePresentation = PPTApplication.ActivePresentation;
if (activePresentation != null && _pptActivePresentation != null && !PPTROTConnectionHelper.AreComObjectsEqual(_pptActivePresentation, activePresentation))
{
@@ -357,12 +365,11 @@ namespace Ink_Canvas.Helpers
bool isSlideShowActive = false;
try
{
- dynamic app = PPTApplication;
if (activePresentation == null)
{
try
{
- activePresentation = app.ActivePresentation;
+ activePresentation = PPTApplication.ActivePresentation;
}
catch (Exception ex)
{
@@ -374,7 +381,7 @@ namespace Ink_Canvas.Helpers
{
try
{
- dynamic slideShowWindows = app.SlideShowWindows;
+ dynamic slideShowWindows = PPTApplication.SlideShowWindows;
if (slideShowWindows != null && slideShowWindows.Count > 0)
{
isSlideShowActive = true;
@@ -424,13 +431,9 @@ namespace Ink_Canvas.Helpers
if (isSlideShowActive)
{
- // 在轮询模式下,更频繁地检查(每500ms,与定时器同步)
- // 否则每3秒检查一次
- bool shouldPoll = _forcePolling || (DateTime.Now - _updateTime).TotalMilliseconds > 3000;
-
- if (shouldPoll)
+ if ((DateTime.Now - _updateTime).TotalMilliseconds > 3000 || _forcePolling)
{
- LogHelper.WriteLogToFile($"开始轮询检查: forcePolling={_forcePolling}, 距离上次更新={(DateTime.Now - _updateTime).TotalMilliseconds}ms", LogHelper.LogType.Trace);
+ LogHelper.WriteLogToFile($"轮询", LogHelper.LogType.Trace);
try
{
@@ -453,7 +456,6 @@ namespace Ink_Canvas.Helpers
{
SlidesCount = 0;
_polling = 0;
- LogHelper.WriteLogToFile("轮询: 无法获取总页数", LogHelper.LogType.Trace);
}
else
{
@@ -470,10 +472,7 @@ namespace Ink_Canvas.Helpers
if (currentPage >= tempTotalPage) _polling = 1;
else _polling = 0;
- LogHelper.WriteLogToFile($"轮询: 当前页={currentPage}, 总页={tempTotalPage}, 上次页={_lastPolledSlideNumber}, forcePolling={_forcePolling}", LogHelper.LogType.Trace);
-
- // 在轮询模式下,检测页码变化并触发事件
- if (_forcePolling && _lastPolledSlideNumber != -1 && currentPage != _lastPolledSlideNumber)
+ if (_lastPolledSlideNumber != -1 && currentPage != _lastPolledSlideNumber)
{
try
{
@@ -501,7 +500,7 @@ namespace Ink_Canvas.Helpers
catch (Exception ex)
{
SlidesCount = 0;
- LogHelper.WriteLogToFile($"轮询检查异常: {ex.Message}", LogHelper.LogType.Warning);
+ LogHelper.WriteLogToFile($"获取总页数失败: {ex.Message}", LogHelper.LogType.Warning);
}
finally
{
@@ -517,17 +516,16 @@ namespace Ink_Canvas.Helpers
{
int currentPage = GetCurrentSlideIndex(_pptSlideShowWindow);
- // 在轮询模式下,检测页码变化并触发事件
- if (_forcePolling && _lastPolledSlideNumber != -1 && currentPage != _lastPolledSlideNumber)
+ if (_lastPolledSlideNumber != -1 && currentPage != _lastPolledSlideNumber)
{
try
{
- LogHelper.WriteLogToFile($"轮询模式检测到页码变化: {_lastPolledSlideNumber} -> {currentPage}", LogHelper.LogType.Trace);
+ LogHelper.WriteLogToFile($"轮询模式检测到页码变化: {_lastPolledSlideNumber} -> {currentPage},触发事件", LogHelper.LogType.Trace);
SlideShowNextSlide?.Invoke(_pptSlideShowWindow);
}
catch (Exception ex)
{
- LogHelper.WriteLogToFile($"触发轮询模式幻灯片切换事件失败: {ex.Message}", LogHelper.LogType.Trace);
+ LogHelper.WriteLogToFile($"触发轮询模式幻灯片切换事件失败: {ex.Message}", LogHelper.LogType.Warning);
}
}
@@ -683,10 +681,11 @@ namespace Ink_Canvas.Helpers
{
try
{
- Type appType = typeof(Microsoft.Office.Interop.PowerPoint.Application);
- if (appType.IsInstanceOfType(PPTApplication))
+ // 使用 as 操作符进行类型转换,与 inkeys 保持一致
+ Microsoft.Office.Interop.PowerPoint.Application pptAppForEvents = PPTApplication as Microsoft.Office.Interop.PowerPoint.Application;
+
+ if (pptAppForEvents != null)
{
- Microsoft.Office.Interop.PowerPoint.Application pptAppForEvents = (Microsoft.Office.Interop.PowerPoint.Application)PPTApplication;
pptAppForEvents.SlideShowNextSlide += new EApplication_SlideShowNextSlideEventHandler(OnSlideShowNextSlide);
pptAppForEvents.SlideShowBegin += new EApplication_SlideShowBeginEventHandler(OnSlideShowBegin);
pptAppForEvents.SlideShowEnd += new EApplication_SlideShowEndEventHandler(OnSlideShowEnd);
@@ -701,7 +700,6 @@ namespace Ink_Canvas.Helpers
}
_bindingEvents = true;
- _forcePolling = false;
LogHelper.WriteLogToFile("PPT事件注册成功", LogHelper.LogType.Trace);
}
@@ -732,6 +730,9 @@ namespace Ink_Canvas.Helpers
LogHelper.WriteLogToFile($"无法注册事件: {ex.Message}", LogHelper.LogType.Warning);
}
+ _bindingEvents = false;
+ _forcePolling = true;
+
if (_pptActivePresentation != null)
{
UpdateCurrentPresentationInfo();
@@ -773,10 +774,11 @@ namespace Ink_Canvas.Helpers
{
try
{
- Type appType = typeof(Microsoft.Office.Interop.PowerPoint.Application);
- if (appType.IsInstanceOfType(PPTApplication))
+ // 使用 as 操作符进行类型转换,与 inkeys 保持一致
+ Microsoft.Office.Interop.PowerPoint.Application app = PPTApplication as Microsoft.Office.Interop.PowerPoint.Application;
+
+ if (app != null)
{
- Microsoft.Office.Interop.PowerPoint.Application app = (Microsoft.Office.Interop.PowerPoint.Application)PPTApplication;
app.SlideShowNextSlide -= new EApplication_SlideShowNextSlideEventHandler(OnSlideShowNextSlide);
app.SlideShowBegin -= new EApplication_SlideShowBeginEventHandler(OnSlideShowBegin);
app.SlideShowEnd -= new EApplication_SlideShowEndEventHandler(OnSlideShowEnd);
@@ -789,7 +791,6 @@ namespace Ink_Canvas.Helpers
}
_bindingEvents = false;
- _forcePolling = false;
}
}
catch { }
@@ -840,7 +841,7 @@ namespace Ink_Canvas.Helpers
CurrentSlide = null;
SlidesCount = 0;
_polling = 0;
- _forcePolling = false;
+ _forcePolling = true;
_bindingEvents = false;
GC.Collect();
@@ -993,8 +994,7 @@ namespace Ink_Canvas.Helpers
}
else
{
- dynamic app = PPTApplication;
- activeWindow = app.ActiveWindow;
+ activeWindow = PPTApplication.ActiveWindow;
if (activeWindow != null)
{
dynamic aw = activeWindow;
@@ -1095,8 +1095,7 @@ namespace Ink_Canvas.Helpers
{
try
{
- dynamic app = PPTApplication;
- _pptActivePresentation = app.ActivePresentation;
+ _pptActivePresentation = PPTApplication.ActivePresentation;
_updateTime = DateTime.Now;
}
catch { }
@@ -1122,10 +1121,11 @@ namespace Ink_Canvas.Helpers
{
try
{
- Type appType = typeof(Microsoft.Office.Interop.PowerPoint.Application);
- if (appType.IsInstanceOfType(PPTApplication))
+ // 使用 as 操作符进行类型转换,与 inkeys 保持一致
+ Microsoft.Office.Interop.PowerPoint.Application app = PPTApplication as Microsoft.Office.Interop.PowerPoint.Application;
+
+ if (app != null)
{
- Microsoft.Office.Interop.PowerPoint.Application app = (Microsoft.Office.Interop.PowerPoint.Application)PPTApplication;
app.SlideShowNextSlide -= new EApplication_SlideShowNextSlideEventHandler(OnSlideShowNextSlide);
app.SlideShowBegin -= new EApplication_SlideShowBeginEventHandler(OnSlideShowBegin);
app.SlideShowEnd -= new EApplication_SlideShowEndEventHandler(OnSlideShowEnd);
@@ -1138,7 +1138,6 @@ namespace Ink_Canvas.Helpers
}
_bindingEvents = false;
- _forcePolling = false;
}
DisconnectFromPPT();
@@ -1258,8 +1257,7 @@ namespace Ink_Canvas.Helpers
if (IsInSlideShow)
{
- dynamic app = PPTApplication;
- slideShowWindows = app.SlideShowWindows;
+ slideShowWindows = PPTApplication.SlideShowWindows;
if (slideShowWindows != null)
{
dynamic ssw = slideShowWindows;
@@ -1343,8 +1341,7 @@ namespace Ink_Canvas.Helpers
{
try
{
- dynamic app = PPTApplication;
- object slideShowWindows = app.SlideShowWindows;
+ object slideShowWindows = PPTApplication.SlideShowWindows;
if (slideShowWindows != null)
{
dynamic ssw = slideShowWindows;
@@ -1407,8 +1404,7 @@ namespace Ink_Canvas.Helpers
{
try
{
- dynamic app = PPTApplication;
- object slideShowWindows = app.SlideShowWindows;
+ object slideShowWindows = PPTApplication.SlideShowWindows;
if (slideShowWindows != null)
{
dynamic ssw = slideShowWindows;
@@ -1469,8 +1465,7 @@ namespace Ink_Canvas.Helpers
if (!IsConnected || !IsInSlideShow || PPTApplication == null) return false;
if (!Marshal.IsComObject(PPTApplication)) return false;
- dynamic app = PPTApplication;
- slideShowWindows = app.SlideShowWindows;
+ slideShowWindows = PPTApplication.SlideShowWindows;
if (slideShowWindows != null)
{
dynamic ssw = slideShowWindows;
@@ -1586,8 +1581,7 @@ namespace Ink_Canvas.Helpers
}
}
- dynamic app = PPTApplication;
- activeWindow = app.ActiveWindow;
+ activeWindow = PPTApplication.ActiveWindow;
if (activeWindow != null)
{
dynamic aw = activeWindow;
@@ -1701,8 +1695,7 @@ namespace Ink_Canvas.Helpers
}
}
- dynamic app = PPTApplication;
- activeWindow = app.ActiveWindow;
+ activeWindow = PPTApplication.ActiveWindow;
if (activeWindow != null)
{
dynamic aw = activeWindow;
@@ -1824,8 +1817,7 @@ namespace Ink_Canvas.Helpers
return false;
}
- dynamic app = PPTApplication;
- slideShowWindows = app.SlideShowWindows;
+ slideShowWindows = PPTApplication.SlideShowWindows;
if (slideShowWindows != null)
{
dynamic ssw = slideShowWindows;
@@ -1898,12 +1890,11 @@ namespace Ink_Canvas.Helpers
Process wpsProcess = null;
// 方法1:通过应用程序路径检测
- dynamic app = PPTApplication;
- if (app.Path.Contains("Kingsoft\\WPS Office\\") ||
- app.Path.Contains("WPS Office\\"))
+ if (PPTApplication.Path.Contains("Kingsoft\\WPS Office\\") ||
+ PPTApplication.Path.Contains("WPS Office\\"))
{
uint processId;
- GetWindowThreadProcessId((IntPtr)app.HWND, out processId);
+ GetWindowThreadProcessId((IntPtr)PPTApplication.HWND, out processId);
wpsProcess = Process.GetProcessById((int)processId);
}
@@ -2159,10 +2150,9 @@ namespace Ink_Canvas.Helpers
{
if (PPTApplication != null && Marshal.IsComObject(PPTApplication))
{
- dynamic app = PPTApplication;
- if (app.SlideShowWindows?.Count > 0)
+ if (PPTApplication.SlideShowWindows?.Count > 0)
{
- pptActWindow = app.SlideShowWindows[1];
+ pptActWindow = PPTApplication.SlideShowWindows[1];
}
}
}
@@ -2332,6 +2322,9 @@ namespace Ink_Canvas.Helpers
[DllImport("user32.dll")]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
+ [DllImport("user32.dll")]
+ private static extern int GetWindowTextLength(IntPtr hWnd);
+
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsWindowVisible(IntPtr hWnd);
@@ -2634,6 +2627,185 @@ namespace Ink_Canvas.Helpers
}
#endregion
+ #region Window Handle Methods
+ ///
+ /// 获取PPT窗口句柄
+ ///
+ /// 窗口句柄,如果获取失败返回 IntPtr.Zero
+ public IntPtr GetPptHwnd()
+ {
+ IntPtr ret = IntPtr.Zero;
+
+ // 方法1: 尝试从 SlideShowWindow 获取
+ ret = GetPptHwndFromSlideShowWindow(_pptSlideShowWindow);
+
+ if (ret == IntPtr.Zero)
+ {
+ // 方法2: 通过窗口标题匹配获取(备用方法)
+ try
+ {
+ if (_pptActivePresentation != null && PPTApplication != null)
+ {
+ dynamic pres = _pptActivePresentation;
+ string fullName = pres.FullName;
+ string appName = PPTApplication.Name;
+ ret = GetPptHwndWin32(fullName, appName);
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"获取PPT窗口句柄失败: {ex.Message}", LogHelper.LogType.Trace);
+ }
+ }
+
+ return ret;
+ }
+
+ ///
+ /// 从 SlideShowWindow 对象获取窗口句柄
+ ///
+ private IntPtr GetPptHwndFromSlideShowWindow(object pptSlideShowWindowObj)
+ {
+ IntPtr hwnd = IntPtr.Zero;
+ if (pptSlideShowWindowObj == null) return IntPtr.Zero;
+
+ try
+ {
+ // 尝试强类型转换
+ Microsoft.Office.Interop.PowerPoint.SlideShowWindow slideWindow =
+ pptSlideShowWindowObj as Microsoft.Office.Interop.PowerPoint.SlideShowWindow;
+
+ if (slideWindow != null)
+ {
+ int hwndVal = slideWindow.HWND;
+ hwnd = new IntPtr(hwndVal);
+ LogHelper.WriteLogToFile($"从SlideShowWindow获取窗口句柄成功: {hwnd}", LogHelper.LogType.Trace);
+ }
+ else
+ {
+ // 如果强类型转换失败,尝试使用dynamic
+ try
+ {
+ dynamic ssw = pptSlideShowWindowObj;
+ int hwndVal = ssw.HWND;
+ hwnd = new IntPtr(hwndVal);
+ LogHelper.WriteLogToFile($"从SlideShowWindow获取窗口句柄成功(dynamic): {hwnd}", LogHelper.LogType.Trace);
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"从SlideShowWindow获取窗口句柄失败: {ex.Message}", LogHelper.LogType.Trace);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LogHelper.WriteLogToFile($"从SlideShowWindow获取窗口句柄异常: {ex.Message}", LogHelper.LogType.Trace);
+ }
+
+ return hwnd;
+ }
+
+ ///
+ /// 通过窗口标题匹配获取PPT窗口句柄(备用方法)
+ ///
+ private IntPtr GetPptHwndWin32(string presFullName, string appName)
+ {
+ try
+ {
+ // 步骤 A: 基础参数校验
+ if (string.IsNullOrWhiteSpace(presFullName) || string.IsNullOrWhiteSpace(appName))
+ {
+ return IntPtr.Zero;
+ }
+
+ // 步骤 B: 提取关键信息 (应用类型 & 文件名)
+ string targetAppKeyword;
+ if (appName.IndexOf("WPS", StringComparison.OrdinalIgnoreCase) >= 0)
+ {
+ targetAppKeyword = "WPS";
+ }
+ else if (appName.IndexOf("PowerPoint", StringComparison.OrdinalIgnoreCase) >= 0)
+ {
+ targetAppKeyword = "PowerPoint";
+ }
+ else
+ {
+ // 既不是 WPS 也不是 PowerPoint,视为不支持
+ return IntPtr.Zero;
+ }
+
+ // 从路径中安全提取文件名(包含扩展名),如 "myppt.pptx"
+ string targetFileName = System.IO.Path.GetFileName(presFullName);
+ if (string.IsNullOrWhiteSpace(targetFileName))
+ {
+ return IntPtr.Zero;
+ }
+
+ // 步骤 C: 枚举窗口并查找匹配项
+ List candidates = new List();
+
+ // 调用 EnumWindows,使用 Lambda 表达式直接嵌入回调逻辑
+ EnumWindows((hWnd, lParam) =>
+ {
+ try
+ {
+ // [安全过滤] 1. 忽略不可见窗口
+ if (!IsWindowVisible(hWnd)) return true;
+
+ // [安全获取] 2. 获取窗口标题长度
+ int length = GetWindowTextLength(hWnd);
+ if (length == 0) return true;
+
+ // [安全获取] 3. 获取窗口标题文本
+ StringBuilder sb = new StringBuilder(length + 1);
+ GetWindowText(hWnd, sb, sb.Capacity);
+ string title = sb.ToString();
+
+ if (string.IsNullOrWhiteSpace(title)) return true;
+
+ // [核心匹配] 4. 判断标题是否同时包含 "文件名" 和 "应用关键字"
+ bool hasFileName = title.IndexOf(targetFileName, StringComparison.OrdinalIgnoreCase) >= 0;
+ bool hasAppKey = title.IndexOf(targetAppKeyword, StringComparison.OrdinalIgnoreCase) >= 0;
+
+ if (hasFileName && hasAppKey)
+ {
+ candidates.Add(hWnd);
+ }
+
+ // 继续枚举其他窗口
+ return true;
+ }
+ catch
+ {
+ // 回调内部容错,忽略单个窗口获取信息的错误,继续枚举
+ return true;
+ }
+ }, IntPtr.Zero);
+
+ // 步骤 D: 结果判定
+ // 只有当匹配到的窗口数量 唯一 (Count == 1) 时才返回句柄
+ // 0 个表示没找到,>1 个表示有歧义(无法确定是哪一个),均视为失败
+ if (candidates.Count == 1)
+ {
+ LogHelper.WriteLogToFile($"通过窗口标题匹配获取窗口句柄成功: {candidates[0]}", LogHelper.LogType.Trace);
+ return candidates[0];
+ }
+ else if (candidates.Count > 1)
+ {
+ LogHelper.WriteLogToFile($"通过窗口标题匹配找到多个候选窗口({candidates.Count}个),无法确定唯一窗口", LogHelper.LogType.Trace);
+ }
+
+ return IntPtr.Zero;
+ }
+ catch (Exception ex)
+ {
+ // 发生任何不可预知的异常(如Path解析错误等),返回安全值
+ LogHelper.WriteLogToFile($"GetPptHwndWin32异常: {ex.Message}", LogHelper.LogType.Trace);
+ return IntPtr.Zero;
+ }
+ }
+ #endregion
+
#region Dispose
public void Dispose()
{