add:PPT联动备用方法

This commit is contained in:
2026-01-10 19:51:00 +08:00
parent d5e5ec8c46
commit 221a0f8e85
2 changed files with 545 additions and 20 deletions
+92 -20
View File
@@ -263,10 +263,8 @@ namespace Ink_Canvas.Helpers
{
var pptApp = (Microsoft.Office.Interop.PowerPoint.Application)Marshal.GetActiveObject("PowerPoint.Application");
// 验证COM对象是否有效
if (pptApp != null && Marshal.IsComObject(pptApp))
{
// 尝试访问一个简单的属性来验证连接
var _ = pptApp.Name;
return pptApp;
}
@@ -275,15 +273,46 @@ namespace Ink_Canvas.Helpers
catch (COMException ex)
{
var hr = (uint)ex.HResult;
if (hr == 0x800401E3 || hr == 0x800401F3 || hr == 0x800401E4)
{
LogHelper.WriteLogToFile($"检测到 COM 注册损坏 (HR: 0x{hr:X8}),尝试使用 ROT 备用方法", LogHelper.LogType.Warning);
return TryConnectToPowerPointViaROT();
}
return null;
}
catch (InvalidCastException)
{
// COM对象类型转换失败
return null;
LogHelper.WriteLogToFile("COM 对象类型转换失败,尝试使用 ROT 备用方法", LogHelper.LogType.Warning);
return TryConnectToPowerPointViaROT();
}
catch (Exception)
catch (Exception ex)
{
LogHelper.WriteLogToFile($"常规连接方法失败: {ex.Message},尝试使用 ROT 备用方法", LogHelper.LogType.Warning);
return TryConnectToPowerPointViaROT();
}
}
private Microsoft.Office.Interop.PowerPoint.Application TryConnectToPowerPointViaROT()
{
try
{
LogHelper.WriteLogToFile("开始使用 ROT 备用方法连接 PowerPoint", LogHelper.LogType.Trace);
var pptApp = PPTROTConnectionHelper.TryConnectViaROT(IsSupportWPS);
if (pptApp != null)
{
LogHelper.WriteLogToFile("ROT 备用方法连接成功", LogHelper.LogType.Event);
}
else
{
LogHelper.WriteLogToFile("ROT 备用方法连接失败", LogHelper.LogType.Warning);
}
return pptApp;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"ROT 备用方法连接异常: {ex}", LogHelper.LogType.Error);
return null;
}
}
@@ -294,10 +323,8 @@ namespace Ink_Canvas.Helpers
{
var wpsApp = (Microsoft.Office.Interop.PowerPoint.Application)Marshal.GetActiveObject("kwpp.Application");
// 验证COM对象是否有效
if (wpsApp != null && Marshal.IsComObject(wpsApp))
{
// 尝试访问一个简单的属性来验证连接
var _ = wpsApp.Name;
return wpsApp;
}
@@ -306,13 +333,12 @@ namespace Ink_Canvas.Helpers
catch (COMException ex)
{
var hr = (uint)ex.HResult;
// 忽略常见的WPS连接错误:
// 0x800401E3: 操作无法使用
// 0x80004005: 未指定错误
// 0x800706B5: RPC服务器不可用
// 0x8001010E: 应用程序调用一个已为另一线程整理的接口
// 0x800401F3: 无效的类字符串(WPS未安装或COM组件未注册)
if (hr != 0x800401E3 && hr != 0x80004005 && hr != 0x800706B5 && hr != 0x8001010E && hr != 0x800401F3)
if (hr == 0x800401E3 || hr == 0x800401F3 || hr == 0x800401E4)
{
LogHelper.WriteLogToFile($"检测到 WPS COM 注册损坏 (HR: 0x{hr:X8}),尝试使用 ROT 备用方法", LogHelper.LogType.Warning);
return TryConnectToWPSViaROT();
}
if (hr != 0x80004005 && hr != 0x800706B5 && hr != 0x8001010E)
{
LogHelper.WriteLogToFile($"连接WPS失败: {ex}", LogHelper.LogType.Warning);
}
@@ -320,12 +346,37 @@ namespace Ink_Canvas.Helpers
}
catch (InvalidCastException)
{
// COM对象类型转换失败
return null;
LogHelper.WriteLogToFile("WPS COM 对象类型转换失败,尝试使用 ROT 备用方法", LogHelper.LogType.Warning);
return TryConnectToWPSViaROT();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"连接WPS时发生意外错误: {ex}", LogHelper.LogType.Warning);
LogHelper.WriteLogToFile($"连接WPS时发生意外错误: {ex},尝试使用 ROT 备用方法", LogHelper.LogType.Warning);
return TryConnectToWPSViaROT();
}
}
private Microsoft.Office.Interop.PowerPoint.Application TryConnectToWPSViaROT()
{
try
{
LogHelper.WriteLogToFile("开始使用 ROT 备用方法连接 WPS", LogHelper.LogType.Trace);
var wpsApp = PPTROTConnectionHelper.TryConnectViaROT(true);
if (wpsApp != null)
{
LogHelper.WriteLogToFile("ROT 备用方法连接 WPS 成功", LogHelper.LogType.Event);
}
else
{
LogHelper.WriteLogToFile("ROT 备用方法连接 WPS 失败", LogHelper.LogType.Warning);
}
return wpsApp;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"ROT 备用方法连接 WPS 异常: {ex}", LogHelper.LogType.Error);
return null;
}
}
@@ -429,19 +480,40 @@ namespace Ink_Canvas.Helpers
LogHelper.WriteLogToFile($"取消PPT事件注册失败: {ex}", LogHelper.LogType.Warning);
}
// 安全释放COM对象
SafeReleaseComObject(CurrentSlide, "CurrentSlide");
SafeReleaseComObject(CurrentSlides, "CurrentSlides");
SafeReleaseComObject(CurrentPresentation, "CurrentPresentation");
SafeReleaseComObject(PPTApplication, "PPTApplication");
if (PPTApplication != null && Marshal.IsComObject(PPTApplication))
{
try
{
int refCount = Marshal.ReleaseComObject(PPTApplication);
while (refCount > 0)
{
refCount = Marshal.ReleaseComObject(PPTApplication);
}
}
catch
{
try
{
Marshal.FinalReleaseComObject(PPTApplication);
}
catch { }
}
}
// 清理引用
PPTApplication = null;
CurrentPresentation = null;
CurrentSlides = null;
CurrentSlide = null;
SlidesCount = 0;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
// 重新启动连接检查定时器
_connectionCheckTimer?.Start();
@@ -0,0 +1,453 @@
using Microsoft.Office.Interop.PowerPoint;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading;
namespace Ink_Canvas.Helpers
{
public static class PPTROTConnectionHelper
{
#region Win32 API Declarations
[DllImport("ole32.dll")]
private static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable prot);
[DllImport("ole32.dll")]
private static extern int CreateBindCtx(int reserved, out IBindCtx ppbc);
[DllImport("user32.dll")]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
#endregion
#region Constants
private static readonly Guid PowerPointApplicationGuid = new Guid("91493441-5A91-11CF-8700-00AA0060263B");
private static readonly string[] PptLikeExtensions = new[]
{
".pptx", ".pptm", ".ppt",
".ppsx", ".ppsm", ".pps",
".potx", ".potm", ".pot",
".dps", ".dpt"
};
#endregion
#region Public Methods
public static Microsoft.Office.Interop.PowerPoint.Application TryConnectViaROT(bool isSupportWPS = false)
{
try
{
LogHelper.WriteLogToFile("开始通过 ROT 查找 PowerPoint 应用程序", LogHelper.LogType.Trace);
object bestApp = GetAnyActivePowerPoint(null, out int bestPriority, out _, isSupportWPS);
if (bestApp != null && bestPriority > 0)
{
try
{
var pptApp = bestApp as Microsoft.Office.Interop.PowerPoint.Application;
if (pptApp != null)
{
var _ = pptApp.Name;
LogHelper.WriteLogToFile($"通过 ROT 成功连接到 PowerPoint (优先级: {bestPriority})", LogHelper.LogType.Event);
return pptApp;
}
else
{
dynamic dynApp = bestApp;
var name = dynApp.Name;
LogHelper.WriteLogToFile($"通过 ROT 成功连接到 PowerPoint (dynamic, 优先级: {bestPriority})", LogHelper.LogType.Event);
return bestApp as Microsoft.Office.Interop.PowerPoint.Application;
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"ROT 连接验证失败: {ex.Message}", LogHelper.LogType.Warning);
SafeReleaseComObject(bestApp);
return null;
}
}
LogHelper.WriteLogToFile("通过 ROT 未找到可用的 PowerPoint 应用程序", LogHelper.LogType.Trace);
return null;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"ROT 连接过程发生异常: {ex}", LogHelper.LogType.Error);
return null;
}
}
#endregion
#region Private Methods
private static object GetAnyActivePowerPoint(object targetApp, out int bestPriority, out int targetPriority, bool isSupportWPS)
{
IRunningObjectTable rot = null;
IEnumMoniker enumMoniker = null;
object bestApp = null;
bestPriority = 0;
targetPriority = 0;
int highestPriority = 0;
List<object> foundAppObjects = new List<object>();
try
{
int hr = GetRunningObjectTable(0, out rot);
if (hr != 0 || rot == null)
{
LogHelper.WriteLogToFile("无法获取 Running Object Table", LogHelper.LogType.Warning);
return null;
}
rot.EnumRunning(out enumMoniker);
if (enumMoniker == null)
{
LogHelper.WriteLogToFile("无法枚举 ROT 中的对象", LogHelper.LogType.Warning);
return null;
}
IMoniker[] moniker = new IMoniker[1];
IntPtr fetched = IntPtr.Zero;
while (enumMoniker.Next(1, moniker, fetched) == 0)
{
IBindCtx bindCtx = null;
object comObject = null;
dynamic candidateApp = null;
string displayName = "Unknown";
dynamic activePres = null;
dynamic ssWindow = null;
bool keepAlive = false;
try
{
CreateBindCtx(0, out bindCtx);
moniker[0].GetDisplayName(bindCtx, null, out displayName);
if (LooksLikePresentationFile(displayName) || displayName == "!{91493441-5A91-11CF-8700-00AA0060263B}")
{
rot.GetObject(moniker[0], out comObject);
if (comObject != null)
{
try
{
object appObj = comObject.GetType().InvokeMember("Application", BindingFlags.GetProperty, null, comObject, null);
candidateApp = appObj;
}
catch
{
candidateApp = comObject;
}
}
}
bool isDuplicate = false;
if (candidateApp != null)
{
foreach (var processedApp in foundAppObjects)
{
if (AreComObjectsEqual((object)candidateApp, processedApp))
{
isDuplicate = true;
break;
}
}
if (!isDuplicate)
{
foundAppObjects.Add(candidateApp);
keepAlive = true;
}
}
if (candidateApp != null && !isDuplicate)
{
int currentPriority = 0;
bool isTarget = false;
if (targetApp != null && AreComObjectsEqual((object)candidateApp, targetApp))
{
isTarget = true;
}
try
{
try
{
activePres = candidateApp.ActivePresentation;
}
catch { }
if (activePres != null)
{
currentPriority = 1;
try
{
ssWindow = activePres.SlideShowWindow;
}
catch { }
if (ssWindow != null)
{
currentPriority = 2;
try
{
bool isActive = false;
try
{
object val = ssWindow.Active;
if (val is int && (int)val == -1) isActive = true;
else if (val is bool && (bool)val == true) isActive = true;
}
catch { }
if (isActive)
{
currentPriority = 3;
}
else
{
if (IsSlideShowWindowActive(ssWindow, isSupportWPS))
{
currentPriority = 3;
}
}
}
catch { }
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"计算优先级时出错: {ex.Message}", LogHelper.LogType.Warning);
}
if (isTarget)
{
targetPriority = currentPriority;
}
if (currentPriority > 0)
{
if (currentPriority > highestPriority)
{
highestPriority = currentPriority;
SafeReleaseComObject(bestApp);
bestApp = candidateApp;
candidateApp = null;
}
}
}
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"ROT 枚举循环中出错: {ex.Message}", LogHelper.LogType.Warning);
}
finally
{
SafeReleaseComObject(ssWindow);
SafeReleaseComObject(activePres);
if (!keepAlive)
{
SafeReleaseComObject(candidateApp);
}
CleanUpLoopObjects(bindCtx, moniker[0], comObject);
}
}
bestPriority = highestPriority;
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"ROT 扫描关键错误: {ex}", LogHelper.LogType.Error);
}
finally
{
if (foundAppObjects != null)
{
foreach (var cachedApp in foundAppObjects)
{
if (bestApp != null && ReferenceEquals(cachedApp, bestApp))
continue;
SafeReleaseComObject(cachedApp);
}
foundAppObjects.Clear();
}
if (enumMoniker != null) Marshal.ReleaseComObject(enumMoniker);
if (rot != null) Marshal.ReleaseComObject(rot);
}
return bestApp;
}
private static bool AreComObjectsEqual(object o1, object o2)
{
if (o1 == null || o2 == null) return false;
if (ReferenceEquals(o1, o2)) return true;
IntPtr pUnk1 = IntPtr.Zero;
IntPtr pUnk2 = IntPtr.Zero;
try
{
pUnk1 = Marshal.GetIUnknownForObject(o1);
pUnk2 = Marshal.GetIUnknownForObject(o2);
return pUnk1 == pUnk2;
}
catch { return false; }
finally
{
if (pUnk1 != IntPtr.Zero) Marshal.Release(pUnk1);
if (pUnk2 != IntPtr.Zero) Marshal.Release(pUnk2);
}
}
private static bool LooksLikePresentationFile(string displayName)
{
if (string.IsNullOrEmpty(displayName))
return false;
string lower = displayName.ToLowerInvariant();
foreach (var ext in PptLikeExtensions)
{
if (lower.Contains(ext))
return true;
}
return false;
}
private static bool IsSlideShowWindowActive(object sswObj, bool isSupportWPS)
{
try
{
IntPtr foregroundHwnd = GetForegroundWindow();
if (foregroundHwnd == IntPtr.Zero) return false;
uint fgPid;
GetWindowThreadProcessId(foregroundHwnd, out fgPid);
IntPtr sswHwnd = IntPtr.Zero;
try
{
sswHwnd = GetPptHwndFromSlideShowWindow(sswObj);
}
catch { return false; }
if (sswHwnd == IntPtr.Zero) return false;
uint sswPid;
GetWindowThreadProcessId(sswHwnd, out sswPid);
if (fgPid == sswPid) return true;
if (isSupportWPS)
{
try
{
using (Process fgProc = Process.GetProcessById((int)fgPid))
using (Process appProc = Process.GetProcessById((int)sswPid))
{
string fgName = fgProc.ProcessName.ToLower();
string appName = appProc.ProcessName.ToLower();
if (fgName.StartsWith("wps") && appName.StartsWith("wpp"))
{
return true;
}
}
}
catch { }
}
return false;
}
catch
{
return false;
}
}
private static IntPtr GetPptHwndFromSlideShowWindow(object pptSlideShowWindowObj)
{
if (pptSlideShowWindowObj == null) return IntPtr.Zero;
try
{
dynamic ssw = pptSlideShowWindowObj;
object hwndObj = ssw.HWND;
if (hwndObj is int)
{
return new IntPtr((int)hwndObj);
}
else if (hwndObj is IntPtr)
{
return (IntPtr)hwndObj;
}
return IntPtr.Zero;
}
catch
{
return IntPtr.Zero;
}
}
private static void SafeReleaseComObject(object comObj)
{
if (comObj == null) return;
if (Marshal.IsComObject(comObj))
{
try
{
Marshal.ReleaseComObject(comObj);
}
catch { }
}
}
private static void CleanUpLoopObjects(IBindCtx bindCtx, IMoniker moniker, object comObject)
{
if (comObject != null && Marshal.IsComObject(comObject))
Marshal.ReleaseComObject(comObject);
if (moniker != null)
Marshal.ReleaseComObject(moniker);
if (bindCtx != null)
Marshal.ReleaseComObject(bindCtx);
}
#endregion
}
}