using Microsoft.Office.Interop.PowerPoint; using System; using System.Runtime.InteropServices; using System.Timers; using System.Windows.Threading; using Application = System.Windows.Application; using Timer = System.Timers.Timer; namespace Ink_Canvas.Helpers { public class ROTPPTManager : IPPTLinkManager { #region Events public event Action SlideShowBegin; public event Action SlideShowNextSlide; public event Action SlideShowEnd; public event Action PresentationOpen; public event Action PresentationClose; public event Action PPTConnectionChanged; public event Action SlideShowStateChanged; #endregion #region Properties /// /// 当前 PowerPoint 应用程序实例(通过 ROT 获取)。 /// public object PPTApplication => _pptApplication; public bool IsConnected { get { try { if (_pptApplication == null) return false; if (!Marshal.IsComObject(_pptApplication)) return false; // 访问简单属性验证 COM 是否仍然有效 var _ = _pptApplication.Name; return true; } catch (COMException comEx) { var hr = (uint)comEx.HResult; if (hr == 0x8001010E || hr == 0x80004005 || hr == 0x800706B5) { return false; } return false; } catch { return false; } } } public bool IsInSlideShow { get { object slideShowWindows = null; object slideShowWindow = null; object view = null; try { if (_pptApplication == null || !Marshal.IsComObject(_pptApplication)) return false; slideShowWindows = _pptApplication.SlideShowWindows; if (slideShowWindows == null) return false; dynamic ssw = slideShowWindows; if (ssw.Count == 0) return false; try { slideShowWindow = ssw[1]; if (slideShowWindow == null) return false; dynamic sswObj = slideShowWindow; view = sswObj.View; if (view == null) return false; return true; } catch (COMException comEx) { var hr = (uint)comEx.HResult; if (hr == 0x8001010E || hr == 0x80004005) { DisconnectFromPPT(); } return false; } } catch (COMException comEx) { var hr = (uint)comEx.HResult; if (hr == 0x8001010E || hr == 0x80004005) { DisconnectFromPPT(); } LogHelper.WriteLogToFile($"检查 ROT PPT 放映状态失败: {comEx.Message} (HR: 0x{hr:X8})", LogHelper.LogType.Warning); return false; } catch (Exception ex) { LogHelper.WriteLogToFile($"检查 ROT PPT 放映状态时发生意外错误: {ex}", LogHelper.LogType.Warning); return false; } finally { SafeReleaseComObject(view); SafeReleaseComObject(slideShowWindow); SafeReleaseComObject(slideShowWindows); } } } public bool IsSupportWPS { get; set; } = false; public int SlidesCount { get; private set; } #endregion #region Private Fields private Microsoft.Office.Interop.PowerPoint.Application _pptApplication; private Presentation _currentPresentation; private Slides _currentSlides; private Slide _currentSlide; private Timer _connectionCheckTimer; private Timer _slideShowStateCheckTimer; private bool _isModuleUnloading; private bool _lastSlideShowState; private readonly object _lockObject = new object(); private bool _disposed; #endregion #region Lifecycle public ROTPPTManager() { InitializeTimers(); } private void InitializeTimers() { _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) return; _connectionCheckTimer?.Start(); _slideShowStateCheckTimer?.Start(); LogHelper.WriteLogToFile("ROTPPTManager 监控已启动", LogHelper.LogType.Trace); } public void StopMonitoring() { _connectionCheckTimer?.Stop(); _slideShowStateCheckTimer?.Stop(); DisconnectFromPPT(); LogHelper.WriteLogToFile("ROTPPTManager 监控已停止", LogHelper.LogType.Trace); } #endregion #region Connection Management private void OnConnectionCheckTimerElapsed(object sender, ElapsedEventArgs e) { try { if (!_isModuleUnloading) { CheckAndConnectToPPTViaRot(); } } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 连接检查失败: {ex}", LogHelper.LogType.Error); } } private void OnSlideShowStateCheckTimerElapsed(object sender, ElapsedEventArgs e) { try { if (!_isModuleUnloading && IsConnected) { CheckSlideShowState(); } } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 放映状态检查失败: {ex}", LogHelper.LogType.Error); } } /// /// 使用 ROT 尝试连接到 PowerPoint。 /// private void CheckAndConnectToPPTViaRot() { if (_isModuleUnloading) return; lock (_lockObject) { try { if (_isModuleUnloading) return; var pptApp = PPTROTConnectionHelper.TryConnectViaROT(IsSupportWPS); if (pptApp != null && _pptApplication == null) { // 从未连接 -> 连接 ConnectToPPT(pptApp); } else if (pptApp == null && _pptApplication != null) { // 原来有,现在没有 -> 断开 DisconnectFromPPT(); } else if (pptApp != null && _pptApplication != null) { // 已连接,检查是否切换到了另一份 PPT if (!PPTROTConnectionHelper.AreComObjectsEqual(_pptApplication, pptApp)) { DisconnectFromPPT(); ConnectToPPT(pptApp); } else { // 相同实例,释放多余引用 PPTROTConnectionHelper.SafeReleaseComObject(pptApp); } } } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 连接检查异常: {ex}", LogHelper.LogType.Error); if (_pptApplication != null) { DisconnectFromPPT(); } } } } private void CheckSlideShowState() { try { if (!IsConnected) return; var currentSlideShowState = IsInSlideShow; if (currentSlideShowState != _lastSlideShowState) { _lastSlideShowState = currentSlideShowState; SlideShowStateChanged?.Invoke(currentSlideShowState); } } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 检查 PPT 放映状态异常: {ex}", LogHelper.LogType.Error); } } private void ConnectToPPT(Microsoft.Office.Interop.PowerPoint.Application pptApp) { try { _pptApplication = pptApp; // 在 UI 线程上注册事件 Application.Current?.Dispatcher?.Invoke(() => { try { _pptApplication.PresentationOpen += OnPresentationOpenInternal; _pptApplication.PresentationClose += OnPresentationCloseInternal; _pptApplication.SlideShowBegin += OnSlideShowBeginInternal; _pptApplication.SlideShowNextSlide += OnSlideShowNextSlideInternal; _pptApplication.SlideShowEnd += OnSlideShowEndInternal; LogHelper.WriteLogToFile("ROTPPTManager 已注册 PPT 事件", LogHelper.LogType.Trace); } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 注册 PPT 事件失败: {ex}", LogHelper.LogType.Error); throw; } }, DispatcherPriority.Normal); UpdateCurrentPresentationInfo(); // 连接成功后暂停频繁的连接检查,由状态定时器维持 _connectionCheckTimer?.Stop(); PPTConnectionChanged?.Invoke(true); LogHelper.WriteLogToFile("ROTPPTManager 成功连接到 PPT 应用程序", LogHelper.LogType.Event); if (IsInSlideShow && _pptApplication.SlideShowWindows.Count > 0) { OnSlideShowBeginInternal(_pptApplication.SlideShowWindows[1]); } else if (_currentPresentation != null) { OnPresentationOpenInternal(_currentPresentation); } } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 连接 PPT 应用程序失败: {ex}", LogHelper.LogType.Error); _pptApplication = null; } } private void DisconnectFromPPT() { try { if (_pptApplication != null) { try { if (Marshal.IsComObject(_pptApplication)) { Application.Current?.Dispatcher?.Invoke(() => { try { _pptApplication.PresentationOpen -= OnPresentationOpenInternal; _pptApplication.PresentationClose -= OnPresentationCloseInternal; _pptApplication.SlideShowBegin -= OnSlideShowBeginInternal; _pptApplication.SlideShowNextSlide -= OnSlideShowNextSlideInternal; _pptApplication.SlideShowEnd -= OnSlideShowEndInternal; } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 取消 PPT 事件注册异常: {ex}", LogHelper.LogType.Warning); LogHelper.WriteLogToFile(ex.ToString(), LogHelper.LogType.Trace); } }, DispatcherPriority.Normal); } } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 取消 PPT 事件注册失败: {ex}", LogHelper.LogType.Warning); } SafeReleaseComObject(_currentSlide); SafeReleaseComObject(_currentSlides); SafeReleaseComObject(_currentPresentation); if (Marshal.IsComObject(_pptApplication)) { try { Marshal.FinalReleaseComObject(_pptApplication); } catch { try { int refCount = Marshal.ReleaseComObject(_pptApplication); while (refCount > 0) { refCount = Marshal.ReleaseComObject(_pptApplication); } } catch { } } } } _pptApplication = null; _currentPresentation = null; _currentSlides = null; _currentSlide = null; SlidesCount = 0; GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); _isModuleUnloading = true; _connectionCheckTimer?.Stop(); _slideShowStateCheckTimer?.Stop(); PPTConnectionChanged?.Invoke(false); LogHelper.WriteLogToFile("ROTPPTManager 已断开 PPT 连接", LogHelper.LogType.Event); // 一段时间后重新尝试连接 System.Threading.ThreadPool.QueueUserWorkItem(_ => { try { System.Threading.Thread.Sleep(2000); GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); System.Threading.Thread.Sleep(1000); _isModuleUnloading = false; _connectionCheckTimer?.Start(); _slideShowStateCheckTimer?.Start(); LogHelper.WriteLogToFile("ROTPPTManager 联动模块已重新进入监控状态", LogHelper.LogType.Trace); } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 重新进入监控状态失败: {ex}", LogHelper.LogType.Error); _isModuleUnloading = false; } }); } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 断开 PPT 连接失败: {ex}", LogHelper.LogType.Error); _isModuleUnloading = false; } } #endregion #region PPT Event Handlers private void OnPresentationOpenInternal(Presentation pres) { try { _currentPresentation = pres; _currentSlides = pres.Slides; _currentSlide = null; SlidesCount = _currentSlides?.Count ?? 0; PresentationOpen?.Invoke(pres); } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 处理演示文稿打开事件失败: {ex}", LogHelper.LogType.Error); } } private void OnPresentationCloseInternal(Presentation pres) { try { PresentationClose?.Invoke(pres); if (_currentPresentation != null && pres != null && ReferenceEquals(_currentPresentation, pres)) { SafeReleaseComObject(_currentSlide); SafeReleaseComObject(_currentSlides); SafeReleaseComObject(_currentPresentation); _currentPresentation = null; _currentSlides = null; _currentSlide = null; SlidesCount = 0; } } catch (COMException comEx) { var hr = (uint)comEx.HResult; if (hr == 0x8001010E || hr == 0x80004005 || hr == 0x800706BA || hr == 0x800706BE || hr == 0x80048010) { // COM 对象已失效,静默忽略 } } catch (Exception) { } } private void OnSlideShowBeginInternal(SlideShowWindow wn) { try { if (wn?.Presentation != null) { _currentPresentation = wn.Presentation; _currentSlides = _currentPresentation.Slides; SlidesCount = _currentSlides?.Count ?? 0; } SlideShowBegin?.Invoke(wn); } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 处理放映开始事件失败: {ex}", LogHelper.LogType.Error); } } private void OnSlideShowNextSlideInternal(SlideShowWindow wn) { try { if (wn?.View != null) { try { _currentSlide = wn.View.Slide; } catch { _currentSlide = null; } } SlideShowNextSlide?.Invoke(wn); } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 处理放映翻页事件失败: {ex}", LogHelper.LogType.Error); } } private void OnSlideShowEndInternal(Presentation pres) { try { SlideShowEnd?.Invoke(pres); } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 处理放映结束事件失败: {ex}", LogHelper.LogType.Error); } } #endregion #region IPPTLinkManager Methods public bool TryStartSlideShow() { try { if (!IsConnected || _currentPresentation == null || _pptApplication == null) return false; if (!Marshal.IsComObject(_pptApplication) || !Marshal.IsComObject(_currentPresentation)) return false; _currentPresentation.SlideShowSettings.Run(); return true; } catch (COMException comEx) { var hr = (uint)comEx.HResult; if (hr == 0x8001010E || hr == 0x80004005) { DisconnectFromPPT(); } LogHelper.WriteLogToFile($"ROTPPTManager 开始幻灯片放映失败: {comEx.Message}", LogHelper.LogType.Error); return false; } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 开始幻灯片放映失败: {ex}", LogHelper.LogType.Error); return false; } } public bool TryEndSlideShow() { object slideShowWindows = null; object slideShowWindow = null; object view = null; try { if (!IsConnected || _pptApplication == null) return false; if (!Marshal.IsComObject(_pptApplication)) return false; slideShowWindows = _pptApplication.SlideShowWindows; if (slideShowWindows != null) { dynamic ssw = slideShowWindows; int count = 0; try { count = ssw.Count; } catch { count = 0; } for (int i = 1; i <= count; i++) { try { slideShowWindow = ssw[i]; if (slideShowWindow != null) { dynamic sswObj = slideShowWindow; view = sswObj.View; if (view != null) { dynamic viewObj = view; viewObj.Exit(); } } } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 结束第 {i} 个放映窗口失败: {ex}", LogHelper.LogType.Warning); } finally { SafeReleaseComObject(view); SafeReleaseComObject(slideShowWindow); view = null; slideShowWindow = null; } } } return true; } catch (COMException comEx) { var hr = (uint)comEx.HResult; if (hr == 0x8001010E || hr == 0x80004005) { DisconnectFromPPT(); } LogHelper.WriteLogToFile($"ROTPPTManager 结束幻灯片放映失败: {comEx.Message}", LogHelper.LogType.Error); return false; } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 结束幻灯片放映失败: {ex}", LogHelper.LogType.Error); return false; } finally { SafeReleaseComObject(view); SafeReleaseComObject(slideShowWindow); SafeReleaseComObject(slideShowWindows); } } public bool TryNavigateToSlide(int slideNumber) { object slideShowWindows = null; object slideShowWindow = null; object view = null; try { if (!IsConnected || !IsInSlideShow || _pptApplication == null) return false; if (!Marshal.IsComObject(_pptApplication)) return false; slideShowWindows = _pptApplication.SlideShowWindows; if (slideShowWindows != null) { dynamic ssw = slideShowWindows; slideShowWindow = ssw[1]; if (slideShowWindow != null) { dynamic sswObj = slideShowWindow; sswObj.Activate(); view = sswObj.View; if (view != null) { dynamic viewObj = view; viewObj.GotoSlide(slideNumber); return true; } } } return false; } catch (COMException comEx) { var hr = (uint)comEx.HResult; if (hr == 0x8001010E || hr == 0x80004005) { DisconnectFromPPT(); } LogHelper.WriteLogToFile($"ROTPPTManager 跳转到第 {slideNumber} 页失败: {comEx.Message}", LogHelper.LogType.Error); return false; } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 跳转到第 {slideNumber} 页失败: {ex}", LogHelper.LogType.Error); return false; } finally { SafeReleaseComObject(view); SafeReleaseComObject(slideShowWindow); SafeReleaseComObject(slideShowWindows); } } public bool TryNavigateNext() { object slideShowWindows = null; object slideShowWindow = null; object view = null; try { if (!IsConnected || !IsInSlideShow || _pptApplication == null) return false; if (!Marshal.IsComObject(_pptApplication)) return false; slideShowWindows = _pptApplication.SlideShowWindows; if (slideShowWindows != null) { dynamic ssw = slideShowWindows; slideShowWindow = ssw[1]; if (slideShowWindow != null) { dynamic sswObj = slideShowWindow; sswObj.Activate(); view = sswObj.View; if (view != null) { dynamic viewObj = view; viewObj.Next(); return true; } } } return false; } catch (COMException comEx) { var hr = (uint)comEx.HResult; if (hr == 0x8001010E || hr == 0x80004005) { DisconnectFromPPT(); } LogHelper.WriteLogToFile($"ROTPPTManager 切换到下一页失败: {comEx.Message}", LogHelper.LogType.Error); return false; } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 切换到下一页失败: {ex}", LogHelper.LogType.Error); return false; } finally { SafeReleaseComObject(view); SafeReleaseComObject(slideShowWindow); SafeReleaseComObject(slideShowWindows); } } public bool TryNavigatePrevious() { object slideShowWindows = null; object slideShowWindow = null; object view = null; try { if (!IsConnected || !IsInSlideShow || _pptApplication == null) return false; if (!Marshal.IsComObject(_pptApplication)) return false; slideShowWindows = _pptApplication.SlideShowWindows; if (slideShowWindows != null) { dynamic ssw = slideShowWindows; slideShowWindow = ssw[1]; if (slideShowWindow != null) { dynamic sswObj = slideShowWindow; sswObj.Activate(); view = sswObj.View; if (view != null) { dynamic viewObj = view; viewObj.Previous(); return true; } } } return false; } catch (COMException comEx) { var hr = (uint)comEx.HResult; if (hr == 0x8001010E || hr == 0x80004005) { DisconnectFromPPT(); } LogHelper.WriteLogToFile($"ROTPPTManager 切换到上一页失败: {comEx.Message}", LogHelper.LogType.Error); return false; } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 切换到上一页失败: {ex}", LogHelper.LogType.Error); return false; } finally { SafeReleaseComObject(view); SafeReleaseComObject(slideShowWindow); SafeReleaseComObject(slideShowWindows); } } public int GetCurrentSlideNumber() { object slideShowWindows = null; object slideShowWindow = null; object view = null; try { if (!IsConnected || _pptApplication == null) return 0; if (!Marshal.IsComObject(_pptApplication)) return 0; 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; return viewObj.CurrentShowPosition; } } } return 0; } catch (COMException comEx) { var hr = (uint)comEx.HResult; if (hr == 0x8001010E || hr == 0x80004005) { DisconnectFromPPT(); } LogHelper.WriteLogToFile($"ROTPPTManager 获取当前页码失败: {comEx.Message}", LogHelper.LogType.Error); return 0; } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 获取当前页码失败: {ex}", LogHelper.LogType.Error); return 0; } finally { SafeReleaseComObject(view); SafeReleaseComObject(slideShowWindow); SafeReleaseComObject(slideShowWindows); } } public string GetPresentationName() { try { var pres = GetCurrentActivePresentation() as Presentation; return pres?.Name ?? ""; } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 获取演示文稿名称失败: {ex}", LogHelper.LogType.Error); return ""; } } public bool TryShowSlideNavigation() { object slideShowWindows = null; object slideShowWindow = null; object slideNavigation = null; try { LogHelper.WriteLogToFile($"ROTPPTManager 尝试显示幻灯片导航 - 连接状态: {IsConnected}, 放映状态: {IsInSlideShow}", LogHelper.LogType.Trace); if (!IsConnected || !IsInSlideShow || _pptApplication == null) { LogHelper.WriteLogToFile("ROTPPTManager: PPT 未连接或未在放映状态", LogHelper.LogType.Warning); return false; } if (!Marshal.IsComObject(_pptApplication)) { LogHelper.WriteLogToFile("ROTPPTManager: PPT 应用程序 COM 对象无效", LogHelper.LogType.Warning); return false; } slideShowWindows = _pptApplication.SlideShowWindows; if (slideShowWindows != null) { dynamic ssw = slideShowWindows; slideShowWindow = ssw[1]; if (slideShowWindow == null) { LogHelper.WriteLogToFile("ROTPPTManager: 幻灯片放映窗口为空", LogHelper.LogType.Warning); return false; } try { dynamic sswObj = slideShowWindow; slideNavigation = sswObj.SlideNavigation; if (slideNavigation != null) { dynamic sn = slideNavigation; sn.Visible = true; LogHelper.WriteLogToFile("ROTPPTManager: 成功显示幻灯片导航", LogHelper.LogType.Event); return true; } LogHelper.WriteLogToFile("ROTPPTManager: SlideNavigation 对象为空,可能当前环境不支持", LogHelper.LogType.Warning); return false; } catch (COMException comEx) { var hr = (uint)comEx.HResult; if (hr == 0x80020006) { LogHelper.WriteLogToFile("ROTPPTManager: 当前 PPT 实例不支持 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($"ROTPPTManager 显示幻灯片导航失败: {comEx.Message}", LogHelper.LogType.Error); return false; } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 显示幻灯片导航失败: {ex}", LogHelper.LogType.Error); return false; } finally { SafeReleaseComObject(slideNavigation); SafeReleaseComObject(slideShowWindow); SafeReleaseComObject(slideShowWindows); } } public object GetCurrentActivePresentation() { try { if (!IsConnected || _pptApplication == null) return null; if (!Marshal.IsComObject(_pptApplication)) return null; if (IsInSlideShow && _pptApplication.SlideShowWindows.Count > 0) { try { var slideShowWindow = _pptApplication.SlideShowWindows[1]; if (slideShowWindow?.View != null) { return (Presentation)slideShowWindow.View.Slide.Parent; } } catch (COMException comEx) { var hr = (uint)comEx.HResult; if (hr == 0x80048240) { return null; } throw; } } if (_pptApplication.ActiveWindow?.Presentation != null) { return _pptApplication.ActiveWindow.Presentation; } return _currentPresentation; } catch (COMException comEx) { var hr = (uint)comEx.HResult; if (hr == 0x8001010E || hr == 0x80004005) { DisconnectFromPPT(); } LogHelper.WriteLogToFile($"ROTPPTManager 获取当前演示文稿失败: {comEx.Message}", LogHelper.LogType.Error); return null; } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 获取当前演示文稿失败: {ex}", LogHelper.LogType.Error); return null; } } #endregion #region Helpers private void UpdateCurrentPresentationInfo() { try { if (!IsConnected || _pptApplication == null) return; if (!Marshal.IsComObject(_pptApplication)) return; try { _currentPresentation = _pptApplication.ActivePresentation; } catch { _currentPresentation = null; } if (_currentPresentation != null) { try { _currentSlides = _currentPresentation.Slides; SlidesCount = _currentSlides?.Count ?? 0; } catch { _currentSlides = null; SlidesCount = 0; } } else { _currentSlides = null; SlidesCount = 0; } } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager 更新当前演示文稿信息失败: {ex}", LogHelper.LogType.Error); } } /// /// 安全释放 COM 对象。 /// private void SafeReleaseComObject(object comObject) { try { if (comObject != null && Marshal.IsComObject(comObject)) { Marshal.ReleaseComObject(comObject); } } catch { } } #endregion #region IDisposable public void Dispose() { if (_disposed) return; _disposed = true; try { StopMonitoring(); } catch (Exception ex) { LogHelper.WriteLogToFile($"ROTPPTManager Dispose 异常: {ex}", LogHelper.LogType.Error); } } #endregion } }