Files
community/Ink Canvas/Helpers/ROTPPTManager.cs
T
2026-05-01 10:39:04 +08:00

2996 lines
113 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Microsoft.Office.Interop.PowerPoint;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Timers;
using Timer = System.Timers.Timer;
namespace Ink_Canvas.Helpers
{
public class ROTPPTManager : IPPTLinkManager
{
#region Events
public event Action<object> SlideShowBegin;
public event Action<object> SlideShowNextSlide;
public event Action<object> SlideShowEnd;
public event Action<object> PresentationOpen;
public event Action<object> PresentationClose;
public event Action<bool> PPTConnectionChanged;
public event Action<bool> SlideShowStateChanged;
#endregion
#region Properties
public dynamic PPTApplication { get; private set; }
public dynamic CurrentPresentation { get; private set; }
public dynamic CurrentSlides { get; private set; }
public dynamic CurrentSlide { get; private set; }
public int SlidesCount { get; private set; }
private volatile bool _cachedIsConnected;
private volatile bool _cachedIsInSlideShow;
public bool IsConnected => _cachedIsConnected && PPTApplication != null;
public bool IsInSlideShow => _cachedIsInSlideShow && _pptSlideShowWindow != null;
public bool IsSupportWPS { get; set; } = false;
public bool SkipAnimationsWhenNavigating { get; set; } = false;
#endregion
#region Private Fields
private Thread _monitoringThread;
private bool _shouldStop = false;
private readonly object _monitoringLock = new object();
private Timer _wpsProcessCheckTimer;
private Process _wpsProcess;
private bool _isModuleUnloading = false;
private bool _hasWpsProcessId;
private DateTime _wpsProcessRecordTime = DateTime.MinValue;
private int _wpsProcessCheckCount;
private WpsWindowInfo _lastForegroundWpsWindow;
private DateTime _lastWindowCheckTime = DateTime.MinValue;
private bool _lastSlideShowState;
private readonly object _lockObject = new object();
private bool _disposed;
private dynamic _pptActivePresentation;
private dynamic _pptSlideShowWindow;
private int _polling = 0;
private bool _forcePolling = true;
private bool _bindingEvents = false;
private DateTime _updateTime;
private int _lastPolledSlideNumber = -1;
private int _reconnectFailureCount = 0;
private DateTime _nextReconnectAttemptUtc = DateTime.MinValue;
#endregion
#region Constructor & Initialization
public ROTPPTManager()
{
}
public void StartMonitoring()
{
if (_disposed) return;
lock (_monitoringLock)
{
if (_monitoringThread != null && _monitoringThread.IsAlive)
{
return;
}
_shouldStop = false;
_monitoringThread = new Thread(PptComService)
{
IsBackground = true,
Name = "PPTMonitoringThread"
};
_monitoringThread.Start();
}
}
public void StopMonitoring()
{
lock (_monitoringLock)
{
_shouldStop = true;
if (_monitoringThread != null && _monitoringThread.IsAlive)
{
// 等待线程退出,最多等待2秒
if (!_monitoringThread.Join(2000))
{
LogHelper.WriteLogToFile("等待监控线程退出超时", LogHelper.LogType.Warning);
}
}
DisconnectFromPPT();
}
}
private int _isReloading; // 0 = idle, 1 = in reload
private volatile bool _suppressDisconnectEvent;
public void ReloadConnection()
{
if (_disposed) return;
if (Interlocked.CompareExchange(ref _isReloading, 1, 0) != 0)
{
return;
}
try
{
LogHelper.WriteLogToFile("[ROT] 执行热重载:强制断开并重新连接", LogHelper.LogType.Event);
lock (_monitoringLock)
{
_shouldStop = true;
if (_monitoringThread != null && _monitoringThread.IsAlive)
{
if (!_monitoringThread.Join(2000))
{
LogHelper.WriteLogToFile("等待监控线程退出超时(热重载)", LogHelper.LogType.Warning);
}
}
_suppressDisconnectEvent = true;
try
{
DisconnectFromPPT();
}
finally
{
_suppressDisconnectEvent = false;
}
_monitoringThread = null;
_shouldStop = false;
_isModuleUnloading = false;
}
StartMonitoring();
}
finally
{
Interlocked.Exchange(ref _isReloading, 0);
}
}
#endregion
#region Connection Management
private void PptComService()
{
_bindingEvents = false;
_lastPolledSlideNumber = -1;
_polling = 0;
int tempTotalPage = -1;
try
{
while (!_shouldStop && !_isModuleUnloading)
{
object bestApp = PPTROTConnectionHelper.GetAnyActivePowerPoint(PPTApplication, out int bestPriority, out int targetPriority);
bool needRebind = false;
if (bestApp == null)
{
var appFallback = TryConnectToPowerPoint();
if (appFallback != null)
{
bestApp = appFallback;
bestPriority = 1;
}
}
if (PPTApplication == null && bestApp != null)
{
needRebind = true;
}
else if (PPTApplication != null && bestApp != null && bestPriority > targetPriority)
{
if (!PPTROTConnectionHelper.AreComObjectsEqual(PPTApplication, bestApp))
{
needRebind = true;
}
}
if (needRebind)
{
bool wait = (PPTApplication != null);
DisconnectFromPPT();
if (bestApp != null)
{
if (wait) Thread.Sleep(1000);
try
{
ConnectToPPT(bestApp);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"绑定失败: {ex.Message}", LogHelper.LogType.Warning);
DisconnectFromPPT();
}
}
}
else
{
if (bestApp != null && (PPTApplication == null || !PPTROTConnectionHelper.AreComObjectsEqual(PPTApplication, bestApp)))
{
PPTROTConnectionHelper.SafeReleaseComObject(bestApp);
bestApp = null;
}
else if (bestApp == null && PPTApplication != null)
{
DisconnectFromPPT();
}
}
if (PPTApplication != null && _pptActivePresentation != null)
{
dynamic activePresentation = null;
dynamic slideShowWindow = null;
try
{
// 检查COM对象是否仍然有效
if (!System.Runtime.InteropServices.Marshal.IsComObject(PPTApplication))
{
DisconnectFromPPT();
continue;
}
try
{
var _ = System.Runtime.InteropServices.Marshal.GetIUnknownForObject(PPTApplication);
System.Runtime.InteropServices.Marshal.Release(_);
}
catch (System.Runtime.InteropServices.InvalidComObjectException)
{
// COM对象已失效
DisconnectFromPPT();
continue;
}
activePresentation = PPTApplication.ActivePresentation;
if (!PPTROTConnectionHelper.AreComObjectsEqual(_pptActivePresentation, activePresentation))
{
DisconnectFromPPT();
continue;
}
}
catch (System.Runtime.InteropServices.InvalidComObjectException)
{
// COM对象已失效
DisconnectFromPPT();
continue;
}
catch (COMException ex) when ((uint)ex.ErrorCode == 0x8001010A)
{
}
catch (COMException comEx)
{
// COM异常,对象可能已失效
var hr = (uint)comEx.HResult;
LogHelper.WriteLogToFile($"检查演示文稿状态COM异常: {comEx.Message} (HR: 0x{hr:X8})", LogHelper.LogType.Warning);
DisconnectFromPPT();
continue;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"检查演示文稿状态失败: {ex.Message}", LogHelper.LogType.Warning);
DisconnectFromPPT();
continue;
}
finally
{
PPTROTConnectionHelper.SafeReleaseComObject(activePresentation);
activePresentation = null;
}
bool isSlideShowActive = false;
try
{
activePresentation = PPTApplication.ActivePresentation;
dynamic slideShowWindows = PPTApplication.SlideShowWindows;
int count = 0;
if (slideShowWindows != null)
{
count = slideShowWindows.Count;
}
if (activePresentation != null && count > 0)
{
isSlideShowActive = true;
dynamic activeSlideShowWindow = null;
try
{
activeSlideShowWindow = activePresentation.SlideShowWindow;
}
catch { }
if (activeSlideShowWindow != null)
{
slideShowWindow = activeSlideShowWindow;
if (_pptSlideShowWindow == null || !PPTROTConnectionHelper.IsValidSlideShowWindow(_pptSlideShowWindow))
{
if (!PPTROTConnectionHelper.AreComObjectsEqual(_pptSlideShowWindow, slideShowWindow))
{
bool isNewWindow = _pptSlideShowWindow == null;
PPTROTConnectionHelper.SafeReleaseComObject(_pptSlideShowWindow);
_pptSlideShowWindow = slideShowWindow;
if (isNewWindow)
{
try
{
OnSlideShowBegin(_pptSlideShowWindow);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"手动触发SlideShowBegin失败: {ex.Message}", LogHelper.LogType.Warning);
}
}
}
}
}
}
PPTROTConnectionHelper.SafeReleaseComObject(slideShowWindows);
}
catch (COMException ex) when ((uint)ex.ErrorCode == 0x8001010A)
{
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"发现窗口失败: {ex.Message}", LogHelper.LogType.Warning);
}
finally
{
PPTROTConnectionHelper.SafeReleaseComObject(activePresentation);
activePresentation = null;
if (!PPTROTConnectionHelper.AreComObjectsEqual(_pptSlideShowWindow, slideShowWindow))
{
PPTROTConnectionHelper.SafeReleaseComObject(slideShowWindow);
slideShowWindow = null;
}
}
if (isSlideShowActive)
{
if ((DateTime.Now - _updateTime).TotalMilliseconds > 3000 || _forcePolling)
{
try
{
slideShowWindow = _pptActivePresentation.SlideShowWindow;
if (slideShowWindow != null)
{
tempTotalPage = GetTotalSlideIndex(_pptActivePresentation);
}
else
{
tempTotalPage = -1;
}
}
catch (Exception ex)
{
tempTotalPage = -1;
LogHelper.WriteLogToFile($"获取总页数失败: {ex.Message}", LogHelper.LogType.Warning);
}
finally
{
PPTROTConnectionHelper.SafeReleaseComObject(slideShowWindow);
slideShowWindow = null;
}
if (tempTotalPage == -1)
{
_lastPolledSlideNumber = -1;
_polling = 0;
}
else
{
try
{
int currentPage = GetCurrentSlideIndex(_pptSlideShowWindow);
int lastPage = _lastPolledSlideNumber;
if (currentPage >= GetTotalSlideIndex(_pptActivePresentation)) _polling = 1;
else _polling = 0;
if (lastPage != -1 && currentPage != lastPage)
{
try
{
SlideShowNextSlide?.Invoke(_pptSlideShowWindow);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"触发轮询模式幻灯片切换事件失败: {ex.Message}", LogHelper.LogType.Warning);
}
}
_lastPolledSlideNumber = currentPage;
}
catch (Exception ex)
{
_lastPolledSlideNumber = -1;
_polling = 1;
LogHelper.WriteLogToFile($"获取当前页数失败: {ex}", LogHelper.LogType.Warning);
}
}
_updateTime = DateTime.Now;
}
if (_polling != 0)
{
try
{
int currentPage = GetCurrentSlideIndex(_pptSlideShowWindow);
if (_lastPolledSlideNumber != -1 && currentPage != _lastPolledSlideNumber)
{
try
{
SlideShowNextSlide?.Invoke(_pptSlideShowWindow);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"触发轮询模式幻灯片切换事件失败: {ex.Message}", LogHelper.LogType.Warning);
}
}
_lastPolledSlideNumber = currentPage;
UpdateCurrentPresentationInfo();
_polling = 2;
}
catch
{
_lastPolledSlideNumber = -1;
}
}
}
else
{
if (_lastSlideShowState)
{
PPTROTConnectionHelper.SafeReleaseComObject(_pptSlideShowWindow);
_pptSlideShowWindow = null;
_lastPolledSlideNumber = -1;
_polling = 1;
SlidesCount = 0;
_lastSlideShowState = false;
_cachedIsInSlideShow = false;
SlideShowStateChanged?.Invoke(false);
if (_pptActivePresentation != null)
{
try
{
OnSlideShowEnd(_pptActivePresentation);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"触发SlideShowEnd事件失败: {ex.Message}", LogHelper.LogType.Warning);
}
}
}
else
{
_lastPolledSlideNumber = -1;
SlidesCount = 0;
}
}
}
else
{
_lastPolledSlideNumber = -1;
SlidesCount = 0;
}
if (_shouldStop || _isModuleUnloading)
{
break;
}
Thread.Sleep(500);
}
}
catch (InvalidComObjectException ex)
{
LogHelper.WriteLogToFile($"PptComService异常: COM对象已失效 - {ex.Message}", LogHelper.LogType.Error);
DisconnectFromPPT();
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
LogHelper.WriteLogToFile($"PptComService异常: COM异常 (HR: 0x{hr:X8}) - {comEx.Message}", LogHelper.LogType.Error);
DisconnectFromPPT();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"PptComService异常: {ex.Message}", LogHelper.LogType.Error);
DisconnectFromPPT();
}
}
private Microsoft.Office.Interop.PowerPoint.Application TryConnectToPowerPoint()
{
try
{
var pptApp = PPTROTConnectionHelper.TryConnectViaROT(IsSupportWPS);
return pptApp;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"ROT连接PowerPoint异常: {ex}", LogHelper.LogType.Error);
return null;
}
}
private void ConnectToPPT(object pptApp)
{
try
{
if (pptApp != null)
{
PPTApplication = pptApp;
}
try
{
dynamic pptAppDynamic = PPTApplication;
try
{
_pptActivePresentation = pptAppDynamic.ActivePresentation;
_updateTime = DateTime.Now;
_lastPolledSlideNumber = -1;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"访问ActivePresentation失败: {ex.Message},继续使用轮询模式", LogHelper.LogType.Warning);
_pptActivePresentation = null;
_updateTime = DateTime.Now;
_lastPolledSlideNumber = -1;
}
int tempTotalPage = -1;
if (_pptActivePresentation != null)
{
try
{
_pptSlideShowWindow = _pptActivePresentation.SlideShowWindow;
tempTotalPage = GetTotalSlideIndex(_pptActivePresentation);
}
catch
{
tempTotalPage = -1;
}
}
else
{
tempTotalPage = -1;
}
if (tempTotalPage == -1)
{
SlidesCount = 0;
_polling = 0;
}
else
{
try
{
if (_pptSlideShowWindow != null)
{
int currentPage = GetCurrentSlideIndex(_pptSlideShowWindow);
SlidesCount = tempTotalPage;
if (currentPage >= tempTotalPage) _polling = 1;
else _polling = 0;
_lastPolledSlideNumber = currentPage;
}
else
{
SlidesCount = tempTotalPage;
_polling = 0;
_lastPolledSlideNumber = -1;
}
}
catch
{
SlidesCount = 0;
_polling = 1;
}
}
try
{
if (PPTApplication != null)
{
try
{
Microsoft.Office.Interop.PowerPoint.Application pptAppForEvents = PPTApplication as Microsoft.Office.Interop.PowerPoint.Application;
if (pptAppForEvents != null)
{
pptAppForEvents.SlideShowNextSlide += new EApplication_SlideShowNextSlideEventHandler(OnSlideShowNextSlide);
pptAppForEvents.SlideShowBegin += new EApplication_SlideShowBeginEventHandler(OnSlideShowBegin);
pptAppForEvents.SlideShowEnd += new EApplication_SlideShowEndEventHandler(OnSlideShowEndForComEvent);
try
{
pptAppForEvents.PresentationBeforeClose += new EApplication_PresentationBeforeCloseEventHandler(OnPresentationBeforeClose);
}
catch
{
LogHelper.WriteLogToFile("无法注册PresentationBeforeClose事件", LogHelper.LogType.Warning);
}
_bindingEvents = true;
_forcePolling = false;
}
else
{
_bindingEvents = false;
_forcePolling = true;
}
}
catch (Exception)
{
_bindingEvents = false;
_forcePolling = true;
}
}
else
{
_bindingEvents = false;
_forcePolling = true;
}
}
catch (Exception ex)
{
_bindingEvents = false;
_forcePolling = true;
LogHelper.WriteLogToFile($"无法注册事件: {ex.Message}", LogHelper.LogType.Warning);
}
if (_pptActivePresentation != null)
{
UpdateCurrentPresentationInfo();
}
bool wasConnected = _cachedIsConnected;
_cachedIsConnected = true;
if (!wasConnected)
{
PPTConnectionChanged?.Invoke(true);
}
try
{
dynamic pptAppDynamic2 = PPTApplication;
LogHelper.WriteLogToFile($"成功绑定! {pptAppDynamic2.Name}", LogHelper.LogType.Event);
}
catch
{
LogHelper.WriteLogToFile("成功绑定!", LogHelper.LogType.Event);
}
TryRaiseSlideShowBeginOnConnect();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"ConnectToPPT内部异常: {ex.Message}", LogHelper.LogType.Warning);
if (_pptActivePresentation == null && PPTApplication == null)
{
DisconnectFromPPT();
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"连接PPT应用程序失败: {ex}", LogHelper.LogType.Error);
PPTApplication = null;
}
}
private void TryRaiseSlideShowBeginOnConnect()
{
dynamic slideShowWindows = null;
dynamic slideShowWindow = null;
try
{
if (!IsConnected || !IsInSlideShow || PPTApplication == null)
return;
if (!Marshal.IsComObject(PPTApplication))
return;
dynamic app = PPTApplication;
slideShowWindows = app.SlideShowWindows;
if (slideShowWindows == null || slideShowWindows.Count == 0)
return;
slideShowWindow = slideShowWindows[1];
if (slideShowWindow == null)
return;
SlideShowBegin?.Invoke(slideShowWindow);
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
}
catch (Exception)
{
}
finally
{
if (slideShowWindow != null && !PPTROTConnectionHelper.AreComObjectsEqual(_pptSlideShowWindow, slideShowWindow))
{
SafeReleaseComObject(slideShowWindow);
}
if (slideShowWindows != null)
{
SafeReleaseComObject(slideShowWindows);
}
}
}
private void ApplyReconnectBackoff()
{
_reconnectFailureCount = Math.Min(_reconnectFailureCount + 1, 4);
int delayMs = 250 * (1 << (_reconnectFailureCount - 1));
_nextReconnectAttemptUtc = DateTime.UtcNow.AddMilliseconds(delayMs);
}
private void UnbindEvents()
{
try
{
if (_bindingEvents && PPTApplication != null)
{
try
{
Microsoft.Office.Interop.PowerPoint.Application app = PPTApplication as Microsoft.Office.Interop.PowerPoint.Application;
if (app != null)
{
app.SlideShowNextSlide -= new EApplication_SlideShowNextSlideEventHandler(OnSlideShowNextSlide);
app.SlideShowBegin -= new EApplication_SlideShowBeginEventHandler(OnSlideShowBegin);
app.SlideShowEnd -= new EApplication_SlideShowEndEventHandler(OnSlideShowEndForComEvent);
app.PresentationBeforeClose -= new EApplication_PresentationBeforeCloseEventHandler(OnPresentationBeforeClose);
}
}
catch (Exception)
{
}
_bindingEvents = false;
}
}
catch { }
}
private static bool IsObjectNull(object comObject)
{
return ReferenceEquals(comObject, null);
}
private void DisconnectFromPPT()
{
object pptApplication = PPTApplication;
object activePresentation = _pptActivePresentation;
object slideShowWindow = _pptSlideShowWindow;
object currentPresentation = CurrentPresentation;
object currentSlides = CurrentSlides;
object currentSlide = CurrentSlide;
if (IsObjectNull(pptApplication) && IsObjectNull(activePresentation) && IsObjectNull(slideShowWindow) &&
IsObjectNull(currentPresentation) && IsObjectNull(currentSlides) && IsObjectNull(currentSlide))
{
return;
}
_isModuleUnloading = true;
try
{
UnbindEvents();
if (activePresentation != null)
{
try
{
PresentationClose?.Invoke(activePresentation);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"触发PresentationClose事件失败: {ex.Message}", LogHelper.LogType.Warning);
}
}
SafeReleaseComObject(slideShowWindow, "_pptSlideShowWindow");
SafeReleaseComObject(activePresentation, "_pptActivePresentation");
SafeReleaseComObject(currentSlide, "CurrentSlide");
SafeReleaseComObject(currentSlides, "CurrentSlides");
SafeReleaseComObject(currentPresentation, "CurrentPresentation");
SafeFinalReleaseComObject(pptApplication, "PPTApplication");
PPTApplication = null;
_pptActivePresentation = null;
_pptSlideShowWindow = null;
CurrentPresentation = null;
CurrentSlides = null;
CurrentSlide = null;
SlidesCount = 0;
_polling = 0;
_forcePolling = true;
_bindingEvents = false;
bool wasConnected = _cachedIsConnected;
_cachedIsConnected = false;
_cachedIsInSlideShow = false;
if (wasConnected && !_suppressDisconnectEvent)
{
PPTConnectionChanged?.Invoke(false);
}
LogHelper.WriteLogToFile("已断开PPT连接,并显式释放所有COM对象", LogHelper.LogType.Event);
ThreadPool.QueueUserWorkItem(_ =>
{
try
{
Thread.Sleep(2000);
_isModuleUnloading = false;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"重新启用PPT联动模块失败: {ex}", LogHelper.LogType.Error);
_isModuleUnloading = false;
}
});
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"断开PPT连接失败: {ex}", LogHelper.LogType.Error);
_isModuleUnloading = false;
}
}
/// <summary>
/// 安全释放COM对象
/// </summary>
private void SafeReleaseComObject(object comObject)
{
try
{
if (comObject != null && Marshal.IsComObject(comObject))
{
Marshal.ReleaseComObject(comObject);
}
}
catch { }
}
private void SafeReleaseComObject(object comObject, string objectName)
{
try
{
if (comObject != null)
{
// 检查是否为有效的COM对象
if (!Marshal.IsComObject(comObject))
{
return; // 不是COM对象,无需释放
}
// 检查COM对象是否仍然有效
try
{
// 尝试访问对象以验证其有效性
var _ = Marshal.GetIUnknownForObject(comObject);
Marshal.Release(_);
}
catch (InvalidComObjectException)
{
// COM对象已失效,直接返回
return;
}
catch (COMException comEx) when (IsIgnorableDisconnectComException(comEx))
{
return;
}
int refCount = Marshal.ReleaseComObject(comObject);
}
}
catch (InvalidComObjectException)
{
// COM对象已失效,这是正常的,无需记录错误
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
var logType = IsIgnorableDisconnectComException(comEx) ? LogHelper.LogType.Trace : LogHelper.LogType.Warning;
LogHelper.WriteLogToFile($"释放COM对象 {objectName} 时COM异常: {comEx.Message} (HR: 0x{hr:X8})", logType);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"释放COM对象 {objectName} 时发生异常: {ex}", LogHelper.LogType.Warning);
}
}
/// <summary>
/// 终态释放 COM 对象:先尝试 FinalReleaseComObject 一次性归零引用计数,失败时回退为循环 ReleaseComObject。
/// </summary>
private void SafeFinalReleaseComObject(object comObject, string objectName)
{
if (comObject == null) return;
try
{
if (!Marshal.IsComObject(comObject)) return;
try
{
Marshal.FinalReleaseComObject(comObject);
}
catch
{
try
{
int refCount = Marshal.ReleaseComObject(comObject);
while (refCount > 0)
{
refCount = Marshal.ReleaseComObject(comObject);
}
}
catch { }
}
}
catch (InvalidComObjectException)
{
}
catch (COMException comEx) when (IsIgnorableDisconnectComException(comEx))
{
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"最终释放COM对象 {objectName} 时发生异常: {ex.Message}", LogHelper.LogType.Warning);
}
}
private static bool IsIgnorableDisconnectComException(COMException comEx)
{
var hr = (uint)comEx.HResult;
return hr == 0x800706BA || // RPC server unavailable
hr == 0x80010108 || // object disconnected from clients
hr == 0x8001010D || // server died
hr == 0x800706BE; // remote procedure call failed
}
private void UpdateCurrentPresentationInfo()
{
object activeWindow = null;
object view = null;
object selection = null;
object slideRange = null;
try
{
if (PPTApplication != null && Marshal.IsComObject(PPTApplication))
{
try
{
if (_pptActivePresentation != null)
{
try
{
// 检查COM对象是否仍然有效
if (!Marshal.IsComObject(_pptActivePresentation))
{
CurrentPresentation = null;
CurrentSlides = null;
}
else
{
try
{
var _ = Marshal.GetIUnknownForObject(_pptActivePresentation);
Marshal.Release(_);
}
catch (InvalidComObjectException)
{
CurrentPresentation = null;
CurrentSlides = null;
return;
}
dynamic pres = _pptActivePresentation;
CurrentPresentation = pres;
CurrentSlides = pres.Slides;
}
}
catch (InvalidComObjectException)
{
LogHelper.WriteLogToFile($"访问演示文稿属性失败: COM对象已失效", LogHelper.LogType.Warning);
CurrentPresentation = null;
CurrentSlides = null;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"访问演示文稿属性失败: {ex.Message}", LogHelper.LogType.Warning);
CurrentPresentation = null;
CurrentSlides = null;
}
if (CurrentSlides != null)
{
try
{
var slideCount = CurrentSlides.Count;
if (slideCount > 0)
{
SlidesCount = slideCount;
}
else
{
SlidesCount = 0;
LogHelper.WriteLogToFile("PPT演示文稿页数为0,可能为空演示文稿", LogHelper.LogType.Warning);
}
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
SlidesCount = 0;
LogHelper.WriteLogToFile($"读取PPT页数失败: {comEx.Message} (HR: 0x{hr:X8})", LogHelper.LogType.Warning);
}
try
{
if (IsInSlideShow && _pptSlideShowWindow != null)
{
try
{
dynamic ssw = _pptSlideShowWindow;
view = ssw.View;
if (view != null)
{
dynamic viewObj = view;
CurrentSlide = viewObj.Slide;
}
}
catch (Exception)
{
}
}
else
{
activeWindow = PPTApplication.ActiveWindow;
if (activeWindow != null)
{
dynamic aw = activeWindow;
selection = aw.Selection;
if (selection != null)
{
dynamic sel = selection;
slideRange = sel.SlideRange;
if (slideRange != null)
{
dynamic sr = slideRange;
int slideNumber = sr.SlideNumber;
if (slideNumber > 0 && slideNumber <= SlidesCount)
{
CurrentSlide = CurrentSlides[slideNumber];
}
}
}
}
if (CurrentSlide == null && SlidesCount > 0)
{
CurrentSlide = CurrentSlides[1];
}
}
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
if (hr != 0x8001010E && hr != 0x80004005 && hr != 0x80048240)
{
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)
{
CurrentPresentation = null;
CurrentSlides = null;
CurrentSlide = null;
SlidesCount = 0;
}
else
{
LogHelper.WriteLogToFile($"访问活动演示文稿失败: {comEx}", LogHelper.LogType.Warning);
}
}
}
else
{
CurrentPresentation = null;
CurrentSlides = null;
CurrentSlide = null;
SlidesCount = 0;
}
}
catch (InvalidComObjectException)
{
LogHelper.WriteLogToFile($"更新演示文稿信息失败: COM对象已失效", LogHelper.LogType.Warning);
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;
}
finally
{
SafeReleaseComObject(slideRange);
SafeReleaseComObject(selection);
SafeReleaseComObject(view);
SafeReleaseComObject(activeWindow);
}
}
#endregion
#region Event Handlers
private void OnPresentationOpen(Presentation pres)
{
try
{
if (PPTApplication != null && pres != null)
{
try
{
_pptActivePresentation = PPTApplication.ActivePresentation;
_updateTime = DateTime.Now;
}
catch { }
}
UpdateCurrentPresentationInfo();
PresentationOpen?.Invoke(pres);
LogHelper.WriteLogToFile($"演示文稿已打开: {pres?.Name}", LogHelper.LogType.Event);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理演示文稿打开事件失败: {ex}", LogHelper.LogType.Error);
}
}
private void OnPresentationBeforeClose(Presentation pres, ref bool cancel)
{
try
{
if (_bindingEvents && PPTApplication != null)
{
try
{
Microsoft.Office.Interop.PowerPoint.Application app = PPTApplication as Microsoft.Office.Interop.PowerPoint.Application;
if (app != null)
{
app.SlideShowNextSlide -= new EApplication_SlideShowNextSlideEventHandler(OnSlideShowNextSlide);
app.SlideShowBegin -= new EApplication_SlideShowBeginEventHandler(OnSlideShowBegin);
app.SlideShowEnd -= new EApplication_SlideShowEndEventHandler(OnSlideShowEndForComEvent);
app.PresentationBeforeClose -= new EApplication_PresentationBeforeCloseEventHandler(OnPresentationBeforeClose);
}
}
catch (Exception)
{
}
_bindingEvents = false;
}
DisconnectFromPPT();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理演示文稿关闭前事件失败: {ex}", LogHelper.LogType.Error);
}
}
private void OnSlideShowBegin(object wn)
{
try
{
_updateTime = DateTime.Now;
_pptSlideShowWindow = wn;
_lastPolledSlideNumber = -1; // 重置页码跟踪
// 兜底:ROT 路径下事件触发时 _pptActivePresentation 可能尚未绑定,
// 先从 SlideShowWindow.Presentation 取,再退回 PPTApplication.ActivePresentation
// 保证后续 SlidesCount 在 UI 读取前一定有效。
if (_pptActivePresentation == null)
{
try
{
if (wn != null)
{
dynamic ssw = wn;
dynamic presFromWindow = ssw.Presentation;
if (presFromWindow != null)
{
_pptActivePresentation = presFromWindow;
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"OnSlideShowBegin 从 SlideShowWindow 兜底获取演示文稿失败: {ex.Message}", LogHelper.LogType.Warning);
}
if (_pptActivePresentation == null && PPTApplication != null)
{
try
{
dynamic app = PPTApplication;
_pptActivePresentation = app.ActivePresentation;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"OnSlideShowBegin 从 PPTApplication 兜底获取演示文稿失败: {ex.Message}", LogHelper.LogType.Warning);
}
}
}
try
{
if (_pptActivePresentation != null)
{
int currentPage = GetCurrentSlideIndex(_pptSlideShowWindow);
int totalPage = GetTotalSlideIndex(_pptActivePresentation);
if (currentPage >= totalPage) _polling = 1;
else _polling = 0;
SlidesCount = totalPage;
_lastPolledSlideNumber = currentPage; // 初始化页码跟踪
}
}
catch
{
_polling = 1;
_lastPolledSlideNumber = -1;
}
UpdateCurrentPresentationInfo();
if (!_lastSlideShowState)
{
_lastSlideShowState = true;
_cachedIsInSlideShow = true;
SlideShowStateChanged?.Invoke(true);
}
SlideShowBegin?.Invoke(wn);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理幻灯片放映开始事件失败: {ex}", LogHelper.LogType.Error);
}
}
private void OnSlideShowNextSlide(SlideShowWindow wn)
{
try
{
_updateTime = DateTime.Now;
try
{
if (_pptActivePresentation != null && _pptSlideShowWindow != null)
{
int currentPage = GetCurrentSlideIndex(_pptSlideShowWindow);
int totalPage = GetTotalSlideIndex(_pptActivePresentation);
if (currentPage >= totalPage) _polling = 1;
else _polling = 0;
_lastPolledSlideNumber = currentPage; // 更新页码跟踪
}
}
catch
{
_polling = 1;
}
UpdateCurrentPresentationInfo();
SlideShowNextSlide?.Invoke(wn);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理幻灯片切换事件失败: {ex}", LogHelper.LogType.Error);
}
}
private void OnSlideShowEnd(object pres)
{
try
{
_updateTime = DateTime.Now;
SlidesCount = 0;
_lastPolledSlideNumber = -1;
_polling = 1;
PPTROTConnectionHelper.SafeReleaseComObject(_pptSlideShowWindow);
_pptSlideShowWindow = null;
if (IsSupportWPS && PPTApplication != null)
{
RecordWpsProcessForManagement();
}
UpdateCurrentPresentationInfo();
if (_lastSlideShowState)
{
_lastSlideShowState = false;
_cachedIsInSlideShow = false;
SlideShowStateChanged?.Invoke(false);
}
SlideShowEnd?.Invoke(pres);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"处理幻灯片放映结束事件失败: {ex}", LogHelper.LogType.Error);
}
}
private void OnSlideShowEndForComEvent(Presentation pres)
{
OnSlideShowEnd(pres);
}
#endregion
#region Public Methods
public bool TryNavigateToSlide(int slideNumber)
{
object slideShowWindows = null;
object slideShowWindow = null;
object view = null;
object windows = null;
object window = null;
object windowView = null;
try
{
if (!IsConnected || PPTApplication == null) return false;
if (!Marshal.IsComObject(PPTApplication)) return false;
if (IsInSlideShow)
{
slideShowWindows = PPTApplication.SlideShowWindows;
if (slideShowWindows != null)
{
dynamic ssw = slideShowWindows;
if (ssw.Count >= 1)
{
slideShowWindow = ssw[1];
if (slideShowWindow != null)
{
dynamic sswObj = slideShowWindow;
view = sswObj.View;
if (view != null)
{
dynamic viewObj = view;
viewObj.GotoSlide(slideNumber);
return true;
}
}
}
}
}
else if (CurrentPresentation != null)
{
windows = CurrentPresentation.Windows;
if (windows != null)
{
dynamic win = windows;
if (win.Count >= 1)
{
window = win[1];
if (window != null)
{
dynamic winObj = window;
windowView = winObj.View;
if (windowView != null)
{
dynamic viewObj = windowView;
viewObj.GotoSlide(slideNumber);
return true;
}
}
}
}
}
return false;
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
if (hr == 0x8001010E || hr == 0x80004005)
{
DisconnectFromPPT();
}
LogHelper.WriteLogToFile($"跳转到幻灯片{slideNumber}失败: {comEx.Message}", LogHelper.LogType.Error);
return false;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"跳转到幻灯片{slideNumber}失败: {ex}", LogHelper.LogType.Error);
return false;
}
finally
{
SafeReleaseComObject(windowView);
SafeReleaseComObject(window);
SafeReleaseComObject(windows);
SafeReleaseComObject(view);
SafeReleaseComObject(slideShowWindow);
SafeReleaseComObject(slideShowWindows);
}
}
public bool TryNavigateNext()
{
try
{
if (!IsConnected || !IsInSlideShow || PPTApplication == null) return false;
if (!Marshal.IsComObject(PPTApplication)) return false;
// 在新线程中执行翻页操作,避免等待动画完成
new Thread(() =>
{
try
{
object slideShowWindows = PPTApplication.SlideShowWindows;
if (slideShowWindows != null)
{
dynamic ssw = slideShowWindows;
object slideShowWindow = ssw[1];
if (slideShowWindow != null)
{
dynamic sswObj = slideShowWindow;
try
{
sswObj.Activate();
}
catch { }
try
{
object view = sswObj.View;
if (view != null)
{
dynamic viewObj = view;
viewObj.Next();
}
}
catch { }
SafeReleaseComObject(slideShowWindow);
}
SafeReleaseComObject(slideShowWindows);
}
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
if (hr == 0x8001010E || hr == 0x80004005)
{
DisconnectFromPPT();
}
LogHelper.WriteLogToFile($"切换到下一页失败: {comEx.Message}", LogHelper.LogType.Error);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"切换到下一页失败: {ex}", LogHelper.LogType.Error);
}
}).Start();
return true;
}
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;
// 在新线程中执行翻页操作,避免等待动画完成
new Thread(() =>
{
try
{
object slideShowWindows = PPTApplication.SlideShowWindows;
if (slideShowWindows != null)
{
dynamic ssw = slideShowWindows;
object slideShowWindow = ssw[1];
if (slideShowWindow != null)
{
dynamic sswObj = slideShowWindow;
try
{
sswObj.Activate();
}
catch { }
try
{
object view = sswObj.View;
if (view != null)
{
dynamic viewObj = view;
viewObj.Previous();
}
}
catch { }
SafeReleaseComObject(slideShowWindow);
}
SafeReleaseComObject(slideShowWindows);
}
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
if (hr == 0x8001010E || hr == 0x80004005)
{
DisconnectFromPPT();
}
LogHelper.WriteLogToFile($"切换到上一页失败: {comEx.Message}", LogHelper.LogType.Error);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"切换到上一页失败: {ex}", LogHelper.LogType.Error);
}
}).Start();
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"启动上一页线程失败: {ex}", LogHelper.LogType.Error);
return false;
}
}
public bool TryEndSlideShow()
{
object slideShowWindows = null;
object slideShowWindow = null;
object view = null;
try
{
if (!IsConnected || !IsInSlideShow || PPTApplication == null) return false;
if (!Marshal.IsComObject(PPTApplication)) return false;
slideShowWindows = PPTApplication.SlideShowWindows;
if (slideShowWindows != null)
{
dynamic ssw = slideShowWindows;
slideShowWindow = ssw[1];
if (slideShowWindow != null)
{
dynamic sswObj = slideShowWindow;
view = sswObj.View;
if (view != null)
{
dynamic viewObj = view;
viewObj.Exit();
return true;
}
}
}
return false;
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
if (hr == 0x8001010E || hr == 0x80004005)
{
DisconnectFromPPT();
}
LogHelper.WriteLogToFile($"结束幻灯片放映失败: {comEx.Message}", LogHelper.LogType.Error);
return false;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"结束幻灯片放映失败: {ex}", LogHelper.LogType.Error);
return false;
}
finally
{
SafeReleaseComObject(view);
SafeReleaseComObject(slideShowWindow);
SafeReleaseComObject(slideShowWindows);
}
}
public bool TryStartSlideShow()
{
try
{
if (!IsConnected || CurrentPresentation == null || PPTApplication == null) return false;
if (!Marshal.IsComObject(PPTApplication) || !Marshal.IsComObject(CurrentPresentation)) return false;
CurrentPresentation.SlideShowSettings.Run();
return true;
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
if (hr == 0x8001010E || hr == 0x80004005)
{
// COM对象已失效,触发断开连接
DisconnectFromPPT();
}
LogHelper.WriteLogToFile($"开始幻灯片放映失败: {comEx.Message}", LogHelper.LogType.Error);
return false;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"开始幻灯片放映失败: {ex}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 获取当前活跃的演示文稿
/// </summary>
public object GetCurrentActivePresentation()
{
object slideShowWindows = null;
object slideShowWindow = null;
object view = null;
object slide = null;
object activeWindow = null;
object presentation = null;
try
{
if (!IsConnected || PPTApplication == null) return null;
if (!Marshal.IsComObject(PPTApplication)) return null;
// 检查COM对象是否仍然有效
try
{
var _ = Marshal.GetIUnknownForObject(PPTApplication);
Marshal.Release(_);
}
catch (InvalidComObjectException)
{
// COM对象已失效
return null;
}
if (IsInSlideShow)
{
dynamic pptAppForSSW = PPTApplication;
slideShowWindows = pptAppForSSW.SlideShowWindows;
if (slideShowWindows != null)
{
dynamic ssw = slideShowWindows;
if (ssw.Count > 0)
{
slideShowWindow = ssw[1];
if (slideShowWindow != null)
{
dynamic sswObj = slideShowWindow;
view = sswObj.View;
if (view != null)
{
dynamic viewObj = view;
slide = viewObj.Slide;
if (slide != null)
{
dynamic slideObj = slide;
presentation = slideObj.Parent;
return presentation;
}
}
}
}
}
}
activeWindow = PPTApplication.ActiveWindow;
if (activeWindow != null)
{
dynamic aw = activeWindow;
presentation = aw.Presentation;
if (presentation != null)
{
return presentation;
}
}
return CurrentPresentation;
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
if (hr == 0x8001010E || hr == 0x80004005)
{
DisconnectFromPPT();
}
if (hr == 0x80048240)
{
return null;
}
LogHelper.WriteLogToFile($"获取当前活跃演示文稿失败: {comEx.Message}", LogHelper.LogType.Warning);
return CurrentPresentation;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"获取当前活跃演示文稿失败: {ex}", LogHelper.LogType.Error);
return CurrentPresentation;
}
finally
{
if (presentation != null && !ReferenceEquals(presentation, CurrentPresentation))
{
SafeReleaseComObject(presentation);
}
SafeReleaseComObject(slide);
SafeReleaseComObject(view);
SafeReleaseComObject(slideShowWindow);
SafeReleaseComObject(slideShowWindows);
SafeReleaseComObject(activeWindow);
}
}
/// <summary>
/// 获取当前幻灯片编号
/// </summary>
private int GetCurrentSlideIndex(dynamic slideShowWindow)
{
object view = null;
object slide = null;
try
{
if (slideShowWindow == null) return 0;
// 检查COM对象是否有效
if (!Marshal.IsComObject(slideShowWindow))
{
return 0;
}
try
{
var _ = Marshal.GetIUnknownForObject(slideShowWindow);
Marshal.Release(_);
}
catch (InvalidComObjectException)
{
return 0;
}
dynamic ssw = slideShowWindow;
view = ssw.View;
if (view != null)
{
dynamic viewObj = view;
slide = viewObj.Slide;
if (slide != null)
{
dynamic slideObj = slide;
return slideObj.SlideIndex;
}
}
return 0;
}
catch (COMException comEx)
{
// 处理 0x80048240: SlideShowView.Slide : Invalid request. No slide is currently in view.
var hr = (uint)comEx.HResult;
if (hr == 0x80048240)
{
return 0;
}
throw;
}
catch (InvalidComObjectException)
{
return 0;
}
finally
{
SafeReleaseComObject(slide);
SafeReleaseComObject(view);
}
}
private int GetTotalSlideIndex(dynamic presentation)
{
try
{
if (presentation == null) return 0;
dynamic pres = presentation;
return pres.Slides.Count;
}
catch
{
return 0;
}
}
public int GetCurrentSlideNumber()
{
object activeWindow = null;
object selection = null;
object slideRange = null;
try
{
if (!IsConnected || PPTApplication == null) return 0;
if (!Marshal.IsComObject(PPTApplication)) return 0;
if (IsInSlideShow && _pptSlideShowWindow != null)
{
try
{
return GetCurrentSlideIndex(_pptSlideShowWindow);
}
catch
{
return 0;
}
}
activeWindow = PPTApplication.ActiveWindow;
if (activeWindow != null)
{
dynamic aw = activeWindow;
selection = aw.Selection;
if (selection != null)
{
dynamic sel = selection;
slideRange = sel.SlideRange;
if (slideRange != null)
{
dynamic sr = slideRange;
int slideNumber = sr.SlideNumber;
if (slideNumber > 0)
{
return slideNumber;
}
}
}
}
if (CurrentSlide != null && Marshal.IsComObject(CurrentSlide))
{
return CurrentSlide.SlideNumber;
}
return 0;
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
if (hr == 0x8001010E || hr == 0x80004005)
{
DisconnectFromPPT();
}
return 0;
}
catch (Exception)
{
return 0;
}
finally
{
SafeReleaseComObject(slideRange);
SafeReleaseComObject(selection);
SafeReleaseComObject(activeWindow);
}
}
public string GetPresentationName()
{
try
{
if (CurrentPresentation == null || !Marshal.IsComObject(CurrentPresentation)) return "";
return CurrentPresentation.Name ?? "";
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
if (hr == 0x8001010E || hr == 0x80004005)
{
// 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()
{
object slideShowWindows = null;
object slideShowWindow = null;
object slideNavigation = null;
try
{
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;
}
slideShowWindows = PPTApplication.SlideShowWindows;
if (slideShowWindows != null)
{
dynamic ssw = slideShowWindows;
slideShowWindow = ssw[1];
if (slideShowWindow == null)
{
LogHelper.WriteLogToFile("幻灯片放映窗口为空", LogHelper.LogType.Warning);
return false;
}
try
{
dynamic sswObj = slideShowWindow;
slideNavigation = sswObj.SlideNavigation;
if (slideNavigation != null)
{
dynamic sn = slideNavigation;
sn.Visible = true;
return true;
}
LogHelper.WriteLogToFile("SlideNavigation对象为空,可能是WPS不支持此功能", LogHelper.LogType.Warning);
return false;
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
if (hr == 0x80020006)
{
LogHelper.WriteLogToFile("WPS不支持SlideNavigation功能", LogHelper.LogType.Warning);
return false;
}
throw;
}
}
return false;
}
catch (COMException comEx)
{
var hr = (uint)comEx.HResult;
if (hr == 0x8001010E || hr == 0x80004005)
{
DisconnectFromPPT();
}
LogHelper.WriteLogToFile($"显示幻灯片导航COM异常: {comEx.Message} (HRESULT: 0x{hr:X8})", LogHelper.LogType.Error);
return false;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"显示幻灯片导航失败: {ex}", LogHelper.LogType.Error);
return false;
}
finally
{
SafeReleaseComObject(slideNavigation);
SafeReleaseComObject(slideShowWindow);
SafeReleaseComObject(slideShowWindows);
}
}
#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;
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();
}
// 增加检查间隔到2秒,减少性能开销
_wpsProcessCheckTimer = new Timer(2000);
_wpsProcessCheckTimer.Elapsed += OnWpsProcessCheckTimerElapsed;
_wpsProcessCheckTimer.Start();
}
private void OnWpsProcessCheckTimerElapsed(object sender, ElapsedEventArgs e)
{
if (!IsSupportWPS)
{
StopWpsProcessCheckTimer();
return;
}
try
{
if (_wpsProcess == null || !_hasWpsProcessId)
{
StopWpsProcessCheckTimer();
return;
}
_wpsProcess.Refresh();
_wpsProcessCheckCount++;
if (_wpsProcess.HasExited)
{
StopWpsProcessCheckTimer();
return;
}
// 检查前台WPS窗口是否存在
bool isForegroundWpsWindowActive = IsForegroundWpsWindowStillActiveOptimized();
if (isForegroundWpsWindowActive)
{
if (_wpsProcessCheckCount % 5 == 0) // 每10秒记录一次日志
{
}
return;
}
// 多重验证确保准确性
if (!PerformMultipleWpsWindowChecks())
{
return;
}
// 前台窗口已消失,准备结束WPS进程
LogHelper.WriteLogToFile("多重验证确认WPS窗口已消失,准备结束WPS进程", LogHelper.LogType.Event);
// 安全结束WPS进程
SafeTerminateWpsProcess();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"WPS 进程检测失败: {ex}", LogHelper.LogType.Error);
StopWpsProcessCheckTimer();
}
}
/// <summary>
/// 前台WPS窗口检测
/// </summary>
private bool IsForegroundWpsWindowStillActiveOptimized()
{
try
{
// 快速检查:直接检查前台窗口
var foregroundWindow = GetForegroundWindow();
if (foregroundWindow == IntPtr.Zero) return false;
// 获取前台窗口的进程ID
uint processId;
GetWindowThreadProcessId(foregroundWindow, out processId);
// 如果前台窗口就是我们监控的WPS进程,则认为仍然活跃
if (processId == _wpsProcess?.Id)
{
return true;
}
// 检查是否为WPS相关窗口
var windowInfo = GetWindowInfo(foregroundWindow);
return IsWpsWindow(windowInfo);
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"WPS窗口检测失败: {ex}", LogHelper.LogType.Error);
return false;
}
}
/// <summary>
/// 多重验证WPS窗口状态,确保查杀准确性
/// </summary>
private bool PerformMultipleWpsWindowChecks()
{
try
{
// 第一重验证:等待1秒后再次检查
Thread.Sleep(1000);
if (IsForegroundWpsWindowStillActiveOptimized())
{
return false;
}
// 第二重验证:检查所有WPS进程的窗口
var wpsProcesses = GetWpsProcesses();
foreach (var process in wpsProcesses)
{
if (process.Id == _wpsProcess?.Id) continue; // 跳过当前监控的进程
var windows = GetWpsWindowsByProcess(process.Id);
if (windows.Any(w => w.IsVisible && !w.IsMinimized))
{
return false;
}
}
// 第三重验证:检查任务栏中的WPS窗口
if (HasWpsWindowInTaskbar())
{
return false;
}
LogHelper.WriteLogToFile("多重验证完成:确认WPS窗口已全部消失", LogHelper.LogType.Event);
return true;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"多重验证失败: {ex}", LogHelper.LogType.Error);
return false; // 出错时保守处理,不进行查杀
}
}
/// <summary>
/// 检查任务栏中是否有WPS窗口
/// </summary>
private bool HasWpsWindowInTaskbar()
{
try
{
var allWindows = new List<WpsWindowInfo>();
EnumWindows((hWnd, lParam) =>
{
try
{
if (IsWindow(hWnd) && IsWindowVisible(hWnd))
{
var windowInfo = GetWindowInfo(hWnd);
if (IsWpsWindow(windowInfo) && !string.IsNullOrEmpty(windowInfo.Title))
{
allWindows.Add(windowInfo);
}
}
}
catch { }
return true;
}, IntPtr.Zero);
return allWindows.Count > 0;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"检查任务栏WPS窗口失败: {ex}", LogHelper.LogType.Error);
return true; // 出错时保守处理,认为仍有窗口
}
}
/// <summary>
/// 安全地结束WPS进程 - 通过释放PPTCOM对象
/// </summary>
private void SafeTerminateWpsProcess()
{
try
{
if (_wpsProcess == null || _wpsProcess.HasExited)
{
StopWpsProcessCheckTimer();
return;
}
LogHelper.WriteLogToFile($"开始通过释放PPTCOM对象安全结束WPS进程 (PID: {_wpsProcess.Id})", LogHelper.LogType.Event);
// 第一步:释放 pptActWindow 对象(SlideShowWindow
SlideShowWindow pptActWindow = null;
try
{
if (PPTApplication != null && Marshal.IsComObject(PPTApplication))
{
if (PPTApplication.SlideShowWindows?.Count > 0)
{
pptActWindow = PPTApplication.SlideShowWindows[1];
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"获取SlideShowWindow对象时发生异常: {ex}", LogHelper.LogType.Warning);
}
if (pptActWindow != null)
{
Marshal.ReleaseComObject(pptActWindow);
pptActWindow = null;
}
// 第二步:释放 pptActDoc 对象(CurrentPresentation
Presentation pptActDoc = CurrentPresentation;
if (pptActDoc != null)
{
Marshal.ReleaseComObject(pptActDoc);
pptActDoc = null;
CurrentPresentation = null;
}
// 第三步:释放 pptApp 对象(PPTApplication
if (PPTApplication != null)
{
try
{
Marshal.ReleaseComObject(PPTApplication);
PPTApplication = null;
}
catch (Exception)
{
PPTApplication = null;
}
}
// 第四步:强制垃圾回收及等待终结器执行
GC.Collect();
GC.WaitForPendingFinalizers();
// 等待一段时间让COM对象完全释放
Thread.Sleep(1000);
// 检查进程是否已经结束
try
{
_wpsProcess.Refresh();
if (_wpsProcess.HasExited)
{
LogHelper.WriteLogToFile("WPS进程已通过COM对象释放成功结束", LogHelper.LogType.Event);
StopWpsProcessCheckTimer();
return;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"检查WPS进程状态失败: {ex}", LogHelper.LogType.Warning);
}
// 备用方案:如果COM对象释放后进程仍未结束,尝试关闭
try
{
LogHelper.WriteLogToFile("COM对象释放后进程仍在运行,尝试关闭", LogHelper.LogType.Warning);
_wpsProcess.CloseMainWindow();
if (_wpsProcess.WaitForExit(3000)) // 等待3秒
{
LogHelper.WriteLogToFile("WPS进程已关闭", LogHelper.LogType.Event);
StopWpsProcessCheckTimer();
return;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"关闭WPS进程失败: {ex}", LogHelper.LogType.Warning);
}
// 最后备用方案:强制结束进程
try
{
if (!_wpsProcess.HasExited)
{
LogHelper.WriteLogToFile("所有方法都失败,强制结束WPS进程", LogHelper.LogType.Warning);
_wpsProcess.Kill();
LogHelper.WriteLogToFile("WPS进程已强制结束", LogHelper.LogType.Event);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"强制结束WPS进程失败: {ex}", LogHelper.LogType.Error);
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"安全结束WPS进程时发生异常: {ex}", LogHelper.LogType.Error);
}
finally
{
// 确保清理状态
if (CurrentSlide != null && Marshal.IsComObject(CurrentSlide))
{
try { Marshal.ReleaseComObject(CurrentSlide); } catch { }
}
if (CurrentSlides != null && Marshal.IsComObject(CurrentSlides))
{
try { Marshal.ReleaseComObject(CurrentSlides); } catch { }
}
if (CurrentPresentation != null && Marshal.IsComObject(CurrentPresentation))
{
try { Marshal.ReleaseComObject(CurrentPresentation); } catch { }
}
if (PPTApplication != null && Marshal.IsComObject(PPTApplication))
{
try { Marshal.ReleaseComObject(PPTApplication); } catch { }
}
CurrentSlide = null;
CurrentSlides = null;
CurrentPresentation = null;
PPTApplication = null;
SlidesCount = 0;
StopWpsProcessCheckTimer();
bool wasConnected = _cachedIsConnected;
_cachedIsConnected = false;
_cachedIsInSlideShow = false;
if (wasConnected)
{
PPTConnectionChanged?.Invoke(false);
}
LogHelper.WriteLogToFile("WPS进程结束后已清理所有COM对象并重启连接检查", LogHelper.LogType.Event);
}
}
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;
}
#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")]
private static extern int GetWindowTextLength(IntPtr hWnd);
[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<Process> GetWpsProcesses()
{
var wpsProcesses = new List<Process>();
try
{
var allProcesses = Process.GetProcesses();
foreach (var process in allProcesses)
{
try
{
var pname = process.ProcessName.ToLower();
// 精确的WPS进程名匹配,避免误杀
var exactWpsNames = new[] { "wps", "wpp", "et", "wpspdf", "wpsoffice" };
var microsoftOfficeNames = new[] { "powerpnt", "excel", "word", "onenote", "outlook", "winword", "msaccess" };
// 排除微软Office进程
if (microsoftOfficeNames.Any(name => pname.Contains(name)))
{
continue;
}
// 精确匹配WPS进程名
bool isWpsProcess = exactWpsNames.Any(name => pname.Equals(name) || pname.StartsWith(name + "."));
// 额外验证:检查进程路径
if (isWpsProcess)
{
try
{
var processPath = process.MainModule?.FileName?.ToLower() ?? "";
if (processPath.Contains("kingsoft") || processPath.Contains("wps office"))
{
wpsProcesses.Add(process);
}
}
catch
{
// 无法访问进程路径时,基于进程名判断
if (exactWpsNames.Contains(pname))
{
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)
{
}
}
else if (_lastForegroundWpsWindow == null && currentForegroundWindow != null)
{
}
else if (_lastForegroundWpsWindow != null && currentForegroundWindow == null)
{
}
// 更新记录
_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<WpsWindowInfo> GetWpsWindowsByProcess(int processId)
{
var wpsWindows = new List<WpsWindowInfo>();
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 Window Handle Methods
/// <summary>
/// 获取PPT窗口句柄
/// </summary>
/// <returns>窗口句柄,如果获取失败返回 IntPtr.Zero</returns>
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)
{
}
}
return ret;
}
/// <summary>
/// 从 SlideShowWindow 对象获取窗口句柄
/// </summary>
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);
}
else
{
// 如果强类型转换失败,尝试使用dynamic
try
{
dynamic ssw = pptSlideShowWindowObj;
int hwndVal = ssw.HWND;
hwnd = new IntPtr(hwndVal);
}
catch (Exception)
{
}
}
}
catch (Exception)
{
}
return hwnd;
}
/// <summary>
/// 通过窗口标题匹配获取PPT窗口句柄(备用方法)
/// </summary>
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<IntPtr> candidates = new List<IntPtr>();
// 调用 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)
{
return candidates[0];
}
else if (candidates.Count > 1)
{
}
return IntPtr.Zero;
}
catch (Exception)
{
// 发生任何不可预知的异常(如Path解析错误等),返回安全值
return IntPtr.Zero;
}
}
#endregion
#region Dispose
public void Dispose()
{
if (!_disposed)
{
StopMonitoring();
StopWpsProcessCheckTimer();
_disposed = true;
}
}
#endregion
}
}