From 2dea6076f0cd88f312ec3fe120ef08be1d4b4e97 Mon Sep 17 00:00:00 2001 From: CJKmkp <2564608840@qq.com> Date: Mon, 21 Jul 2025 08:37:37 +0800 Subject: [PATCH] =?UTF-8?q?improve:PPT=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ink Canvas/MainWindow_cs/MW_PPT.cs | 517 ++++++++++++++++++++++++++--- 1 file changed, 479 insertions(+), 38 deletions(-) diff --git a/Ink Canvas/MainWindow_cs/MW_PPT.cs b/Ink Canvas/MainWindow_cs/MW_PPT.cs index 6ec9b909..2e14412a 100644 --- a/Ink Canvas/MainWindow_cs/MW_PPT.cs +++ b/Ink Canvas/MainWindow_cs/MW_PPT.cs @@ -1,8 +1,10 @@ using Ink_Canvas.Helpers; using Microsoft.Office.Interop.PowerPoint; using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -37,6 +39,37 @@ namespace Ink_Canvas { [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 IntPtr GetWindow(IntPtr hWnd, uint uCmd); + + [DllImport("user32.dll")] + private static extern bool IsWindow(IntPtr hWnd); + + [DllImport("user32.dll")] + private static extern bool GetWindowRect(IntPtr hWnd, out ForegroundWindowInfo.RECT lpRect); + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + private static extern int GetClassName(IntPtr hWnd, System.Text.StringBuilder lpClassName, int nMaxCount); + + private const int GWL_STYLE = -16; + private const int WS_VISIBLE = 0x10000000; + private const int WS_MINIMIZE = 0x20000000; + private const uint GW_HWNDNEXT = 2; + private const uint GW_HWNDPREV = 3; + + + private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); public static Microsoft.Office.Interop.PowerPoint.Application pptApplication = null; @@ -669,15 +702,62 @@ namespace Ink_Canvas { if (isFloatingBarFolded) await UnFoldFloatingBar(new object()); // 记录 WPP 进程 ID,用于后续检测未关闭的进程 - if (pptApplication != null && pptApplication.Path.Contains("Kingsoft\\WPS Office\\")) + if (pptApplication != null) { - uint processId; - GetWindowThreadProcessId((IntPtr)pptApplication.HWND, out processId); - wppProcess = Process.GetProcessById((int)processId); - hasWppProcessID = true; - wppProcessRecordTime = DateTime.Now; - wppProcessCheckCount = 0; - LogHelper.WriteLogToFile($"记录 WPP 进程 ID: {processId}", LogHelper.LogType.Trace); + try + { + // 尝试多种方式获取WPS进程 + Process wpsProcess = null; + + // 方法1:通过应用程序路径检测 + if (pptApplication.Path.Contains("Kingsoft\\WPS Office\\") || + pptApplication.Path.Contains("WPS Office\\")) + { + uint processId; + GetWindowThreadProcessId((IntPtr)pptApplication.HWND, out processId); + wpsProcess = Process.GetProcessById((int)processId); + LogHelper.WriteLogToFile($"通过路径检测到WPS进程: {processId}", LogHelper.LogType.Trace); + } + + // 方法2:通过前台窗口检测 + if (wpsProcess == null) + { + var foregroundWpsWindow = GetForegroundWpsWindow(); + if (foregroundWpsWindow != null) + { + wpsProcess = Process.GetProcessById((int)foregroundWpsWindow.ProcessId); + LogHelper.WriteLogToFile($"通过前台窗口检测到WPS进程: {foregroundWpsWindow.ProcessId}", LogHelper.LogType.Trace); + } + } + + // 方法3:通过进程名检测 + if (wpsProcess == null) + { + var wpsProcesses = GetWpsProcesses(); + if (wpsProcesses.Count > 0) + { + wpsProcess = wpsProcesses.First(); + LogHelper.WriteLogToFile($"通过进程名检测到WPS进程: {wpsProcess.Id}", LogHelper.LogType.Trace); + } + } + + if (wpsProcess != null) + { + wppProcess = wpsProcess; + hasWppProcessID = true; + wppProcessRecordTime = DateTime.Now; + wppProcessCheckCount = 0; + LogHelper.WriteLogToFile($"成功记录 WPP 进程 ID: {wpsProcess.Id}", LogHelper.LogType.Trace); + } + else + { + LogHelper.WriteLogToFile("未能检测到WPS进程", LogHelper.LogType.Warning); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"记录WPS进程失败: {ex.ToString()}", LogHelper.LogType.Error); + } } LogHelper.WriteLogToFile(string.Format("PowerPoint Slide Show End"), LogHelper.LogType.Event); @@ -1333,15 +1413,22 @@ namespace Ink_Canvas { } else { - // 检查是否有其他WPS窗口打开 - bool hasOtherWpsWindows = CheckForOtherWpsWindows(); + // 使用改进的WPS窗口检测方法 + bool hasActiveWpsWindows = HasActiveWpsWindows(); - if (hasOtherWpsWindows) + if (hasActiveWpsWindows) { - LogHelper.WriteLogToFile("检测到其他WPS窗口打开,停止强制关闭进程", LogHelper.LogType.Trace); + LogHelper.WriteLogToFile("检测到活跃的WPS窗口,停止强制关闭进程", LogHelper.LogType.Trace); StopWppProcessCheckTimer(); return; } + + // 如果检测失败且是第一次检查,输出调试信息 + if (wppProcessCheckCount == 1) + { + LogHelper.WriteLogToFile("首次检测未发现活跃WPS窗口,输出调试信息", LogHelper.LogType.Trace); + DebugAllWindows(); + } // 计算从记录进程开始的时间 var timeSinceRecord = DateTime.Now - wppProcessRecordTime; @@ -1413,34 +1500,16 @@ namespace Ink_Canvas { { if (wppProcess != null) { - bool hasVisibleWppWindow = false; - EnumWindows((hWnd, lParam) => + var wpsWindows = GetWpsWindowsByProcess(wppProcess.Id); + LogHelper.WriteLogToFile($"检测到{wpsWindows.Count}个WPS窗口", LogHelper.LogType.Trace); + + foreach (var window in wpsWindows) { - try - { - uint windowProcessId; - GetWindowThreadProcessId(hWnd, out windowProcessId); - if ((int)windowProcessId == wppProcess.Id) - { - if (IsWindowVisible(hWnd)) - { - var windowTitle = new System.Text.StringBuilder(256); - GetWindowText(hWnd, windowTitle, 256); - var title = windowTitle.ToString().Trim(); - if (!string.IsNullOrEmpty(title)) - { - hasVisibleWppWindow = true; - return false; // 找到一个就停止枚举 - } - } - } - } - catch { } - return true; - }, IntPtr.Zero); - + LogHelper.WriteLogToFile($"WPS窗口: 标题='{window.Title}', 类名='{window.ClassName}', 可见={window.IsVisible}, 最小化={window.IsMinimized}", LogHelper.LogType.Trace); + } + // 只要当前wpp进程没有可见窗口,就允许Kill - return hasVisibleWppWindow; + return wpsWindows.Any(w => w.IsVisible && !w.IsMinimized); } } catch (Exception ex) @@ -1450,6 +1519,378 @@ namespace Ink_Canvas { return false; // 出错时,默认允许Kill } + /// + /// WPS窗口信息结构 + /// + private class WpsWindowInfo + { + public IntPtr Handle { get; set; } + public string Title { get; set; } + public string ClassName { get; set; } + public bool IsVisible { get; set; } + public bool IsMinimized { get; set; } + public bool IsMaximized { get; set; } + public ForegroundWindowInfo.RECT Rect { get; set; } + public uint ProcessId { get; set; } + } + + /// + /// 获取指定进程的所有WPS窗口 + /// + private List GetWpsWindowsByProcess(int processId) + { + var wpsWindows = new List(); + + try + { + EnumWindows((hWnd, lParam) => + { + try + { + if (!IsWindow(hWnd)) return true; + + uint windowProcessId; + GetWindowThreadProcessId(hWnd, out windowProcessId); + + if ((int)windowProcessId == processId) + { + var windowInfo = GetWindowInfo(hWnd); + if (IsWpsWindow(windowInfo)) + { + wpsWindows.Add(windowInfo); + LogHelper.WriteLogToFile($"发现WPS窗口: {windowInfo.Title} ({windowInfo.ClassName})", LogHelper.LogType.Trace); + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"枚举窗口时出错: {ex.ToString()}", LogHelper.LogType.Error); + } + return true; + }, IntPtr.Zero); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"获取WPS窗口失败: {ex.ToString()}", LogHelper.LogType.Error); + } + + return wpsWindows; + } + + /// + /// 获取窗口详细信息 + /// + private WpsWindowInfo GetWindowInfo(IntPtr hWnd) + { + var windowInfo = new WpsWindowInfo + { + Handle = hWnd, + IsVisible = IsWindowVisible(hWnd), + IsMinimized = IsIconic(hWnd), + IsMaximized = IsZoomed(hWnd) + }; + + // 获取窗口标题 + var windowTitle = new System.Text.StringBuilder(256); + GetWindowText(hWnd, windowTitle, 256); + windowInfo.Title = windowTitle.ToString().Trim(); + + // 获取窗口类名 + var className = new System.Text.StringBuilder(256); + GetClassName(hWnd, className, 256); + windowInfo.ClassName = className.ToString().Trim(); + + // 获取窗口位置 + GetWindowRect(hWnd, out ForegroundWindowInfo.RECT rect); + windowInfo.Rect = rect; + + // 获取进程ID + uint processId; + GetWindowThreadProcessId(hWnd, out processId); + windowInfo.ProcessId = processId; + + return windowInfo; + } + + /// + /// 判断是否为WPS窗口 + /// + private bool IsWpsWindow(WpsWindowInfo windowInfo) + { + if (string.IsNullOrEmpty(windowInfo.Title) && string.IsNullOrEmpty(windowInfo.ClassName)) + return false; + + // 检查窗口标题 + var title = windowInfo.Title.ToLower(); + var className = windowInfo.ClassName.ToLower(); + + // WPS相关关键词(扩展版) + var wpsKeywords = new[] + { + "wps", "演示文稿", "presentation", "powerpoint", "ppt", "pptx", + "kingsoft", "金山", "office", "幻灯片", "slide", "presentation", + "wpp", "wps演示", "wps presentation", "wps office", "kingsoft office" + }; + + // 检查标题是否包含WPS相关关键词 + bool hasWpsTitle = wpsKeywords.Any(keyword => title.Contains(keyword)); + + // 检查类名是否包含WPS相关关键词 + bool hasWpsClass = wpsKeywords.Any(keyword => className.Contains(keyword)); + + // 检查是否为WPS特有的窗口类名 + bool isWpsClass = className.Contains("wps") || + className.Contains("kingsoft") || + className.Contains("presentation") || + className.Contains("powerpoint") || + className.Contains("wpp") || + className.Contains("office"); + + // 检查窗口是否有有效尺寸(排除0尺寸窗口) + bool hasValidSize = (windowInfo.Rect.Right - windowInfo.Rect.Left) > 0 && + (windowInfo.Rect.Bottom - windowInfo.Rect.Top) > 0; + + // 检查窗口是否可见且不是最小化状态 + bool isActiveWindow = windowInfo.IsVisible && !windowInfo.IsMinimized; + + // 检查是否为前台窗口 + bool isForegroundWindow = windowInfo.Handle == GetForegroundWindow(); + + // 综合判断是否为WPS窗口 + bool isWpsWindow = (hasWpsTitle || hasWpsClass || isWpsClass) && hasValidSize; + + // 如果是前台窗口且包含相关关键词,更可能是WPS窗口 + if (isForegroundWindow && (hasWpsTitle || hasWpsClass)) + { + isWpsWindow = true; + } + + if (isWpsWindow) + { + var windowType = isForegroundWindow ? "前台" : (isActiveWindow ? "活跃" : "后台"); + LogHelper.WriteLogToFile($"确认WPS窗口: 标题='{windowInfo.Title}', 类名='{windowInfo.ClassName}', 类型={windowType}, 尺寸={windowInfo.Rect.Right - windowInfo.Rect.Left}x{windowInfo.Rect.Bottom - windowInfo.Rect.Top}", LogHelper.LogType.Trace); + } + + return isWpsWindow; + } + + /// + /// 获取前台WPS窗口 + /// + private WpsWindowInfo GetForegroundWpsWindow() + { + try + { + var foregroundHwnd = GetForegroundWindow(); + if (foregroundHwnd != IntPtr.Zero && IsWindow(foregroundHwnd)) + { + var windowInfo = GetWindowInfo(foregroundHwnd); + if (IsWpsWindow(windowInfo)) + { + LogHelper.WriteLogToFile($"前台WPS窗口: {windowInfo.Title}", LogHelper.LogType.Trace); + return windowInfo; + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"获取前台WPS窗口失败: {ex.ToString()}", LogHelper.LogType.Error); + } + return null; + } + + /// + /// 获取顶级WPS窗口(包括前台窗口和最近的活跃窗口) + /// + private List GetTopLevelWpsWindows() + { + var topLevelWindows = new List(); + + try + { + // 获取前台窗口 + var foregroundWindow = GetForegroundWpsWindow(); + if (foregroundWindow != null) + { + topLevelWindows.Add(foregroundWindow); + } + + // 获取所有可见的WPS窗口,按层级排序 + var allWpsWindows = new List(); + var wpsProcesses = GetWpsProcesses(); + + foreach (var process in wpsProcesses) + { + var windows = GetWpsWindowsByProcess(process.Id); + allWpsWindows.AddRange(windows.Where(w => w.IsVisible && !w.IsMinimized)); + } + + // 按窗口位置排序,优先选择屏幕中央的窗口 + var screenCenter = new System.Drawing.Point( + System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width / 2, + System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height / 2 + ); + + var sortedWindows = allWpsWindows + .Where(w => !topLevelWindows.Any(t => t.Handle == w.Handle)) // 排除已添加的前台窗口 + .OrderBy(w => Math.Abs((w.Rect.Left + w.Rect.Right) / 2 - screenCenter.X) + + Math.Abs((w.Rect.Top + w.Rect.Bottom) / 2 - screenCenter.Y)) + .Take(3); // 取最近的3个窗口 + + topLevelWindows.AddRange(sortedWindows); + + LogHelper.WriteLogToFile($"找到{topLevelWindows.Count}个顶级WPS窗口", LogHelper.LogType.Trace); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"获取顶级WPS窗口失败: {ex.ToString()}", LogHelper.LogType.Error); + } + + return topLevelWindows; + } + + /// + /// 检查是否有活跃的WPS窗口(包括前台窗口) + /// + private bool HasActiveWpsWindows() + { + return HasActiveWpsWindowsWithRetry(3); // 重试3次 + } + + /// + /// 带重试机制的WPS窗口检测 + /// + private bool HasActiveWpsWindowsWithRetry(int maxRetries) + { + for (int attempt = 1; attempt <= maxRetries; attempt++) + { + try + { + LogHelper.WriteLogToFile($"第{attempt}次尝试检测WPS窗口", LogHelper.LogType.Trace); + + // 首先检查前台窗口 + var foregroundWpsWindow = GetForegroundWpsWindow(); + if (foregroundWpsWindow != null) + { + LogHelper.WriteLogToFile($"第{attempt}次尝试检测到前台WPS窗口", LogHelper.LogType.Trace); + return true; + } + + // 检查顶级WPS窗口 + var topLevelWindows = GetTopLevelWpsWindows(); + if (topLevelWindows.Any()) + { + LogHelper.WriteLogToFile($"第{attempt}次尝试检测到{topLevelWindows.Count}个顶级WPS窗口", LogHelper.LogType.Trace); + return true; + } + + // 然后检查所有WPS进程的窗口 + var wpsProcesses = GetWpsProcesses(); + foreach (var process in wpsProcesses) + { + var windows = GetWpsWindowsByProcess(process.Id); + if (windows.Any(w => w.IsVisible && !w.IsMinimized)) + { + LogHelper.WriteLogToFile($"第{attempt}次尝试检测到进程{process.Id}的活跃WPS窗口", LogHelper.LogType.Trace); + return true; + } + } + + // 如果还有重试机会,等待一小段时间再重试 + if (attempt < maxRetries) + { + Thread.Sleep(100); // 等待100ms + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"第{attempt}次尝试检查活跃WPS窗口失败: {ex.ToString()}", LogHelper.LogType.Error); + + // 如果还有重试机会,等待一小段时间再重试 + if (attempt < maxRetries) + { + Thread.Sleep(100); // 等待100ms + } + } + } + + LogHelper.WriteLogToFile($"经过{maxRetries}次尝试,未检测到活跃的WPS窗口", LogHelper.LogType.Trace); + return false; + } + + /// + /// 获取所有WPS相关进程 + /// + private List GetWpsProcesses() + { + var wpsProcesses = new List(); + try + { + var allProcesses = Process.GetProcesses(); + foreach (var process in allProcesses) + { + try + { + if (process.ProcessName.ToLower().Contains("wps") || + process.ProcessName.ToLower().Contains("powerpnt") || + process.ProcessName.ToLower().Contains("presentation") || + process.ProcessName.ToLower().Contains("wpp")) + { + wpsProcesses.Add(process); + LogHelper.WriteLogToFile($"发现WPS进程: {process.ProcessName} (PID: {process.Id})", LogHelper.LogType.Trace); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"检查进程{process.ProcessName}失败: {ex.ToString()}", LogHelper.LogType.Error); + } + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"获取WPS进程失败: {ex.ToString()}", LogHelper.LogType.Error); + } + return wpsProcesses; + } + + /// + /// 调试方法:输出所有窗口信息 + /// + private void DebugAllWindows() + { + try + { + LogHelper.WriteLogToFile("开始调试所有窗口信息", LogHelper.LogType.Trace); + var windowCount = 0; + + EnumWindows((hWnd, lParam) => + { + try + { + if (!IsWindow(hWnd)) return true; + + var windowInfo = GetWindowInfo(hWnd); + if (!string.IsNullOrEmpty(windowInfo.Title) || !string.IsNullOrEmpty(windowInfo.ClassName)) + { + windowCount++; + LogHelper.WriteLogToFile($"窗口{windowCount}: 标题='{windowInfo.Title}', 类名='{windowInfo.ClassName}', 进程ID={windowInfo.ProcessId}, 可见={windowInfo.IsVisible}, 最小化={windowInfo.IsMinimized}", LogHelper.LogType.Trace); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"调试窗口时出错: {ex.ToString()}", LogHelper.LogType.Error); + } + return true; + }, IntPtr.Zero); + + LogHelper.WriteLogToFile($"调试完成,共发现{windowCount}个有效窗口", LogHelper.LogType.Trace); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"调试窗口失败: {ex.ToString()}", LogHelper.LogType.Error); + } + } + private bool CheckForWpsWindowsByEnumeration() { try