diff --git a/Ink Canvas/FloatingWindowInterceptorManager.cs b/Ink Canvas/FloatingWindowInterceptorManager.cs new file mode 100644 index 00000000..3618f09f --- /dev/null +++ b/Ink Canvas/FloatingWindowInterceptorManager.cs @@ -0,0 +1,393 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using Ink_Canvas.Helpers; + +namespace Ink_Canvas +{ + /// + /// 悬浮窗拦截管理器 + /// + public class FloatingWindowInterceptorManager : IDisposable + { + #region 私有字段 + + private FloatingWindowInterceptor _interceptor; + private bool _isInitialized; + private bool _disposed; + private FloatingWindowInterceptorSettings _settings; + + #endregion + + #region 事件 + + public event EventHandler WindowIntercepted; + public event EventHandler WindowRestored; + + #endregion + + #region 公共属性 + + public bool IsEnabled => _interceptor != null && _settings != null && _settings.IsEnabled; + public bool IsRunning => _interceptor != null && _isInitialized; + + #endregion + + #region 公共方法 + + /// + /// 初始化拦截器 + /// + public void Initialize(FloatingWindowInterceptorSettings settings) + { + if (_isInitialized) return; + + try + { + _settings = settings ?? new FloatingWindowInterceptorSettings(); + _interceptor = new FloatingWindowInterceptor(); + + // 订阅事件 + _interceptor.WindowIntercepted += OnWindowIntercepted; + _interceptor.WindowRestored += OnWindowRestored; + + // 应用配置 + ApplySettings(); + + _isInitialized = true; + + // 如果设置了自动启动,则启动拦截器 + if (_settings.AutoStart && _settings.IsEnabled) + { + Start(); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"初始化悬浮窗拦截器失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 启动拦截器 + /// + public void Start() + { + if (!_isInitialized || _settings == null) return; + + if (_interceptor == null) return; + + try + { + _interceptor.Start(_settings.ScanIntervalMs); + LogHelper.WriteLogToFile("悬浮窗拦截器已启动", LogHelper.LogType.Event); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"启动悬浮窗拦截器失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 停止拦截器 + /// + public void Stop() + { + if (_interceptor == null) return; + + try + { + _interceptor.Stop(); + LogHelper.WriteLogToFile("悬浮窗拦截器已停止", LogHelper.LogType.Event); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"停止悬浮窗拦截器失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 设置拦截规则 + /// + public void SetInterceptRule(FloatingWindowInterceptor.InterceptType type, bool enabled) + { + if (_interceptor == null || _settings == null) return; + + try + { + _interceptor.SetInterceptRule(type, enabled); + + // 更新设置 + var ruleName = type.ToString(); + if (_settings.InterceptRules.ContainsKey(ruleName)) + { + _settings.InterceptRules[ruleName] = enabled; + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"设置拦截规则失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 获取拦截规则 + /// + public FloatingWindowInterceptor.InterceptRule GetInterceptRule(FloatingWindowInterceptor.InterceptType type) + { + return _interceptor?.GetInterceptRule(type); + } + + /// + /// 获取所有拦截规则 + /// + public Dictionary GetAllRules() + { + return _interceptor?.GetAllRules() ?? new Dictionary(); + } + + /// + /// 手动扫描一次 + /// + public void ScanOnce() + { + if (_interceptor == null) return; + + try + { + _interceptor.ScanOnce(); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"手动扫描失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 调试:列出所有可见窗口 + /// + public void DebugListAllWindows() + { + if (_interceptor == null) + { + LogHelper.WriteLogToFile("拦截器未初始化,无法进行调试", LogHelper.LogType.Warning); + return; + } + + try + { + LogHelper.WriteLogToFile("开始调试扫描所有可见窗口...", LogHelper.LogType.Event); + + // 直接调用扫描方法,在扫描过程中会输出调试信息 + _interceptor.ScanOnce(); + + LogHelper.WriteLogToFile("调试扫描完成,请查看日志文件获取详细信息", LogHelper.LogType.Event); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"调试列出窗口失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 恢复所有被拦截的窗口 + /// + public void RestoreAllWindows() + { + if (_interceptor == null) return; + + try + { + _interceptor.RestoreAllWindows(); + LogHelper.WriteLogToFile("已恢复所有被拦截的窗口", LogHelper.LogType.Event); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"恢复窗口失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 应用设置 + /// + public void ApplySettings() + { + if (_interceptor == null || _settings == null) return; + + try + { + // 应用拦截规则设置 + foreach (var kvp in _settings.InterceptRules) + { + if (Enum.TryParse(kvp.Key, out var type)) + { + _interceptor.SetInterceptRule(type, kvp.Value); + } + } + + // 如果启用了拦截器,则启动 + if (_settings.IsEnabled && !IsRunning) + { + Start(); + } + // 如果禁用了拦截器,则停止 + else if (!_settings.IsEnabled && IsRunning) + { + Stop(); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"应用设置失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 更新扫描间隔 + /// + public void UpdateScanInterval(int intervalMs) + { + if (_interceptor == null || _settings == null) return; + + try + { + _settings.ScanIntervalMs = intervalMs; + + // 如果正在运行,重启以应用新间隔 + if (IsRunning) + { + Stop(); + Start(); + } + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"更新扫描间隔失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + /// + /// 获取拦截统计信息 + /// + public InterceptStatistics GetStatistics() + { + if (_interceptor == null || _settings == null) return new InterceptStatistics(); + + try + { + var rules = GetAllRules(); + var enabledRules = rules.Count(r => r.Value.IsEnabled); + var totalRules = rules.Count; + + return new InterceptStatistics + { + TotalRules = totalRules, + EnabledRules = enabledRules, + IsRunning = IsRunning, + ScanIntervalMs = _settings.ScanIntervalMs + }; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"获取统计信息失败: {ex.Message}", LogHelper.LogType.Error); + return new InterceptStatistics(); + } + } + + #endregion + + #region 私有方法 + + private void OnWindowIntercepted(object sender, FloatingWindowInterceptor.WindowInterceptedEventArgs e) + { + try + { + // 记录日志 + LogHelper.WriteLogToFile($"拦截窗口: {e.WindowTitle} ({e.InterceptType})", LogHelper.LogType.Event); + + // 显示通知(如果启用) + if (_settings != null && _settings.ShowNotifications) + { + ShowNotification($"已拦截悬浮窗: {e.Rule.Description}"); + } + + // 触发事件 + WindowIntercepted?.Invoke(this, e); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"处理窗口拦截事件失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + private void OnWindowRestored(object sender, FloatingWindowInterceptor.WindowRestoredEventArgs e) + { + try + { + // 记录日志 + LogHelper.WriteLogToFile($"恢复窗口: {e.InterceptType}", LogHelper.LogType.Event); + + // 触发事件 + WindowRestored?.Invoke(this, e); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"处理窗口恢复事件失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + private void ShowNotification(string message) + { + try + { + // 这里可以集成系统通知或自定义通知 + // 暂时使用调试输出 + System.Diagnostics.Debug.WriteLine($"通知: {message}"); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"显示通知失败: {ex.Message}", LogHelper.LogType.Error); + } + } + + #endregion + + #region 辅助类 + + public class InterceptStatistics + { + public int TotalRules { get; set; } + public int EnabledRules { get; set; } + public bool IsRunning { get; set; } + public int ScanIntervalMs { get; set; } + } + + #endregion + + #region IDisposable + + public void Dispose() + { + if (_disposed) return; + + try + { + Stop(); + _interceptor?.Dispose(); + _interceptor = null; + _isInitialized = false; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"释放悬浮窗拦截器失败: {ex.Message}", LogHelper.LogType.Error); + } + finally + { + _disposed = true; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/Ink Canvas/Helpers/FloatingWindowInterceptor.cs b/Ink Canvas/Helpers/FloatingWindowInterceptor.cs new file mode 100644 index 00000000..eedd031a --- /dev/null +++ b/Ink Canvas/Helpers/FloatingWindowInterceptor.cs @@ -0,0 +1,775 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Threading; + +namespace Ink_Canvas.Helpers +{ + /// + /// 悬浮窗拦截器 - 检测和隐藏指定的悬浮窗 + /// + public class FloatingWindowInterceptor : IDisposable + { + #region Windows API Declarations + + [DllImport("user32.dll")] + private static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam); + + [DllImport("user32.dll")] + private static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out uint processId); + + [DllImport("user32.dll")] + private static extern bool IsWindowVisible(IntPtr hWnd); + + [DllImport("user32.dll")] + private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); + + [DllImport("user32.dll")] + private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + + [DllImport("user32.dll")] + private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); + + [DllImport("user32.dll")] + private static extern bool IsIconic(IntPtr hWnd); + + [DllImport("user32.dll")] + private static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd); + + [DllImport("user32.dll")] + private static extern uint GetWindowLong(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); + + [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("kernel32.dll")] + private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId); + + [DllImport("kernel32.dll")] + private static extern bool CloseHandle(IntPtr hObject); + + [DllImport("kernel32.dll")] + private static extern int GetProcessImageFileName(IntPtr hProcess, StringBuilder lpImageFileName, int nSize); + + private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); + + private const int SW_HIDE = 0; + private const int SW_SHOW = 1; + private const int SW_MINIMIZE = 6; + private const int SW_RESTORE = 9; + + private const int GWL_EXSTYLE = -20; + private const uint WS_EX_TOOLWINDOW = 0x00000080; + private const uint WS_EX_APPWINDOW = 0x00040000; + + private const uint SWP_NOMOVE = 0x0002; + private const uint SWP_NOSIZE = 0x0001; + private const uint SWP_NOZORDER = 0x0004; + private const uint SWP_HIDEWINDOW = 0x0080; + + private const uint PROCESS_QUERY_INFORMATION = 0x0400; + private const uint PROCESS_VM_READ = 0x0010; + + #endregion + + #region 拦截规则定义 + + /// + /// 拦截规则类型 + /// + public enum InterceptType + { + /// + /// 希沃白板3 桌面悬浮窗 + /// + SeewoWhiteboard3Floating, + /// + /// 希沃白板5 桌面悬浮窗 + /// + SeewoWhiteboard5Floating, + /// + /// 希沃白板5C 桌面悬浮窗 + /// + SeewoWhiteboard5CFloating, + /// + /// 希沃品课教师端 桌面悬浮窗 + /// + SeewoPincoSideBarFloating, + /// + /// 希沃品课教师端 画笔悬浮窗(包括PPT控件) + /// + SeewoPincoDrawingFloating, + /// + /// 希沃PPT小工具 + /// + SeewoPPTFloating, + /// + /// AiClass 桌面悬浮窗 + /// + AiClassFloating, + /// + /// 鸿合屏幕书写 + /// + HiteAnnotationFloating, + /// + /// 畅言智慧课堂 桌面悬浮窗 + /// + ChangYanFloating, + /// + /// 畅言智慧课堂 PPT悬浮窗 + /// + ChangYanPptFloating, + /// + /// 天喻教育云互动课堂 桌面悬浮窗(包括PPT控件) + /// + IntelligentClassFloating, + /// + /// 希沃桌面 画笔悬浮窗 + /// + SeewoDesktopAnnotationFloating, + /// + /// 希沃桌面 侧栏悬浮窗 + /// + SeewoDesktopSideBarFloating + } + + /// + /// 拦截规则 + /// + public class InterceptRule + { + public InterceptType Type { get; set; } + public string ProcessName { get; set; } + public string WindowTitlePattern { get; set; } + public string ClassNamePattern { get; set; } + public bool IsEnabled { get; set; } + public bool RequiresAdmin { get; set; } + public string Description { get; set; } + } + + #endregion + + #region 私有字段 + + private readonly Dictionary _interceptRules; + private readonly Dictionary _interceptedWindows; + private readonly Timer _scanTimer; + private readonly Dispatcher _dispatcher; + private bool _isRunning; + private bool _disposed; + + #endregion + + #region 事件 + + public event EventHandler WindowIntercepted; + public event EventHandler WindowRestored; + + #endregion + + #region 构造函数 + + public FloatingWindowInterceptor() + { + _interceptRules = new Dictionary(); + _interceptedWindows = new Dictionary(); + _dispatcher = Dispatcher.CurrentDispatcher; + + InitializeRules(); + _scanTimer = new Timer(ScanForWindows, null, Timeout.Infinite, Timeout.Infinite); + } + + #endregion + + #region 初始化 + + private void InitializeRules() + { + // 希沃白板3 桌面悬浮窗 + _interceptRules[InterceptType.SeewoWhiteboard3Floating] = new InterceptRule + { + Type = InterceptType.SeewoWhiteboard3Floating, + ProcessName = "EasiNote3", + WindowTitlePattern = "", + ClassNamePattern = "", + IsEnabled = true, + RequiresAdmin = false, + Description = "希沃白板3 桌面悬浮窗" + }; + + // 希沃白板5 桌面悬浮窗 + _interceptRules[InterceptType.SeewoWhiteboard5Floating] = new InterceptRule + { + Type = InterceptType.SeewoWhiteboard5Floating, + ProcessName = "EasiNote5", + WindowTitlePattern = "", + ClassNamePattern = "", + IsEnabled = true, + RequiresAdmin = false, + Description = "希沃白板5 桌面悬浮窗" + }; + + // 希沃白板5C 桌面悬浮窗 + _interceptRules[InterceptType.SeewoWhiteboard5CFloating] = new InterceptRule + { + Type = InterceptType.SeewoWhiteboard5CFloating, + ProcessName = "EasiNote5C", + WindowTitlePattern = "希沃白板", + ClassNamePattern = "EasiNote5C", + IsEnabled = true, + RequiresAdmin = false, + Description = "希沃白板5C 桌面悬浮窗" + }; + + // 希沃品课教师端 桌面悬浮窗 + _interceptRules[InterceptType.SeewoPincoSideBarFloating] = new InterceptRule + { + Type = InterceptType.SeewoPincoSideBarFloating, + ProcessName = "SeewoPinco", + WindowTitlePattern = "品课", + ClassNamePattern = "SeewoPinco", + IsEnabled = true, + RequiresAdmin = false, + Description = "希沃品课教师端 桌面悬浮窗" + }; + + // 希沃品课教师端 画笔悬浮窗 + _interceptRules[InterceptType.SeewoPincoDrawingFloating] = new InterceptRule + { + Type = InterceptType.SeewoPincoDrawingFloating, + ProcessName = "SeewoPinco", + WindowTitlePattern = "画笔", + ClassNamePattern = "SeewoPinco", + IsEnabled = true, + RequiresAdmin = false, + Description = "希沃品课教师端 画笔悬浮窗(包括PPT控件)" + }; + + // 希沃PPT小工具 + _interceptRules[InterceptType.SeewoPPTFloating] = new InterceptRule + { + Type = InterceptType.SeewoPPTFloating, + ProcessName = "SeewoPPT", + WindowTitlePattern = "希沃PPT", + ClassNamePattern = "SeewoPPT", + IsEnabled = true, + RequiresAdmin = false, + Description = "希沃PPT小工具" + }; + + // AiClass 桌面悬浮窗 + _interceptRules[InterceptType.AiClassFloating] = new InterceptRule + { + Type = InterceptType.AiClassFloating, + ProcessName = "AiClass", + WindowTitlePattern = "AiClass", + ClassNamePattern = "AiClass", + IsEnabled = true, + RequiresAdmin = false, + Description = "AiClass 桌面悬浮窗" + }; + + // 鸿合屏幕书写 + _interceptRules[InterceptType.HiteAnnotationFloating] = new InterceptRule + { + Type = InterceptType.HiteAnnotationFloating, + ProcessName = "HiteAnnotation", + WindowTitlePattern = "鸿合", + ClassNamePattern = "HiteAnnotation", + IsEnabled = true, + RequiresAdmin = false, + Description = "鸿合屏幕书写" + }; + + // 畅言智慧课堂 桌面悬浮窗 + _interceptRules[InterceptType.ChangYanFloating] = new InterceptRule + { + Type = InterceptType.ChangYanFloating, + ProcessName = "ChangYan", + WindowTitlePattern = "畅言", + ClassNamePattern = "ChangYan", + IsEnabled = true, + RequiresAdmin = true, + Description = "畅言智慧课堂 桌面悬浮窗" + }; + + // 畅言智慧课堂 PPT悬浮窗 + _interceptRules[InterceptType.ChangYanPptFloating] = new InterceptRule + { + Type = InterceptType.ChangYanPptFloating, + ProcessName = "ChangYanPPT", + WindowTitlePattern = "畅言PPT", + ClassNamePattern = "ChangYanPPT", + IsEnabled = true, + RequiresAdmin = true, + Description = "畅言智慧课堂 PPT悬浮窗" + }; + + // 天喻教育云互动课堂 桌面悬浮窗 + _interceptRules[InterceptType.IntelligentClassFloating] = new InterceptRule + { + Type = InterceptType.IntelligentClassFloating, + ProcessName = "IntelligentClass", + WindowTitlePattern = "天喻", + ClassNamePattern = "IntelligentClass", + IsEnabled = true, + RequiresAdmin = false, + Description = "天喻教育云互动课堂 桌面悬浮窗(包括PPT控件)" + }; + + // 希沃桌面 画笔悬浮窗 + _interceptRules[InterceptType.SeewoDesktopAnnotationFloating] = new InterceptRule + { + Type = InterceptType.SeewoDesktopAnnotationFloating, + ProcessName = "SeewoDesktop", + WindowTitlePattern = "希沃桌面", + ClassNamePattern = "SeewoDesktop", + IsEnabled = true, + RequiresAdmin = false, + Description = "希沃桌面 画笔悬浮窗" + }; + + // 希沃桌面 侧栏悬浮窗 + _interceptRules[InterceptType.SeewoDesktopSideBarFloating] = new InterceptRule + { + Type = InterceptType.SeewoDesktopSideBarFloating, + ProcessName = "SeewoDesktop", + WindowTitlePattern = "希沃桌面", + ClassNamePattern = "SeewoDesktop", + IsEnabled = true, + RequiresAdmin = true, + Description = "希沃桌面 侧栏悬浮窗" + }; + + // 测试规则 - 拦截所有小窗口 + _interceptRules[InterceptType.SeewoWhiteboard3Floating] = new InterceptRule + { + Type = InterceptType.SeewoWhiteboard3Floating, + ProcessName = "", // 空字符串表示匹配所有进程 + WindowTitlePattern = "", + ClassNamePattern = "", + IsEnabled = false, // 默认关闭,需要手动开启 + RequiresAdmin = false, + Description = "测试规则 - 拦截所有小窗口" + }; + } + + #endregion + + #region 公共方法 + + /// + /// 启动拦截器 + /// + public void Start(int scanIntervalMs = 5000) + { + if (_isRunning) return; + + _isRunning = true; + _scanTimer.Change(0, scanIntervalMs); + } + + /// + /// 停止拦截器 + /// + public void Stop() + { + if (!_isRunning) return; + + _isRunning = false; + _scanTimer.Change(Timeout.Infinite, Timeout.Infinite); + } + + /// + /// 设置拦截规则 + /// + public void SetInterceptRule(InterceptType type, bool enabled) + { + if (_interceptRules.ContainsKey(type)) + { + _interceptRules[type].IsEnabled = enabled; + } + } + + /// + /// 获取拦截规则 + /// + public InterceptRule GetInterceptRule(InterceptType type) + { + return _interceptRules.ContainsKey(type) ? _interceptRules[type] : null; + } + + /// + /// 获取所有拦截规则 + /// + public Dictionary GetAllRules() + { + return new Dictionary(_interceptRules); + } + + /// + /// 手动扫描一次 + /// + public void ScanOnce() + { + ScanForWindows(null); + } + + /// + /// 恢复所有被拦截的窗口 + /// + public void RestoreAllWindows() + { + var windowsToRestore = new List(_interceptedWindows.Keys); + foreach (var hWnd in windowsToRestore) + { + RestoreWindow(hWnd); + } + } + + /// + /// 恢复指定窗口 + /// + public bool RestoreWindow(IntPtr hWnd) + { + if (!_interceptedWindows.ContainsKey(hWnd)) return false; + + if (IsWindow(hWnd)) + { + ShowWindow(hWnd, SW_RESTORE); + _interceptedWindows.Remove(hWnd); + + WindowRestored?.Invoke(this, new WindowRestoredEventArgs + { + WindowHandle = hWnd, + InterceptType = _interceptedWindows[hWnd] + }); + + return true; + } + + _interceptedWindows.Remove(hWnd); + return false; + } + + #endregion + + #region 私有方法 + + private void ScanForWindows(object state) + { + if (!_isRunning) return; + + try + { + EnumWindows(EnumWindowsCallback, IntPtr.Zero); + } + catch (Exception ex) + { + // 记录错误但不中断扫描 + LogHelper.WriteLogToFile($"扫描窗口时发生错误: {ex.Message}", LogHelper.LogType.Error); + } + } + + private bool EnumWindowsCallback(IntPtr hWnd, IntPtr lParam) + { + try + { + // 基本检查 + if (!IsWindow(hWnd) || !IsWindowVisible(hWnd)) return true; + + // 检查是否已经被拦截 + if (_interceptedWindows.ContainsKey(hWnd)) return true; + + // 获取窗口信息 + var windowInfo = GetWindowInfo(hWnd); + if (windowInfo == null) return true; + + // 输出调试信息到日志 + LogHelper.WriteLogToFile($"检测到窗口: '{windowInfo.WindowTitle}' | 进程: '{windowInfo.ProcessName}' | 类名: '{windowInfo.ClassName}'", LogHelper.LogType.Event); + + // 检查窗口样式,过滤掉系统窗口和主窗口 + var exStyle = GetWindowLong(hWnd, GWL_EXSTYLE); + var style = GetWindowLong(hWnd, -16); // GWL_STYLE + + // 跳过工具窗口 + if ((exStyle & WS_EX_TOOLWINDOW) != 0) + { + LogHelper.WriteLogToFile($"跳过工具窗口: {windowInfo.WindowTitle}", LogHelper.LogType.Event); + return true; + } + + // 跳过主窗口(有标题栏和系统菜单的窗口) + const uint WS_CAPTION = 0x00C00000; + const uint WS_SYSMENU = 0x00080000; + if ((style & WS_CAPTION) != 0 && (style & WS_SYSMENU) != 0) + { + LogHelper.WriteLogToFile($"跳过主窗口: {windowInfo.WindowTitle}", LogHelper.LogType.Event); + return true; + } + + // 检查窗口大小,跳过过大的窗口 + var rect = new ForegroundWindowInfo.RECT(); + GetWindowRect(hWnd, out rect); + var width = rect.Right - rect.Left; + var height = rect.Bottom - rect.Top; + + if (width > 600 || height > 400) + { + LogHelper.WriteLogToFile($"跳过大窗口 ({width}x{height}): {windowInfo.WindowTitle}", LogHelper.LogType.Event); + return true; + } + + // 检查是否匹配拦截规则 + foreach (var rule in _interceptRules.Values) + { + if (!rule.IsEnabled) continue; + + if (MatchesRule(windowInfo, rule)) + { + LogHelper.WriteLogToFile($"匹配规则 '{rule.Description}',开始拦截窗口: {windowInfo.WindowTitle}", LogHelper.LogType.Event); + InterceptWindow(hWnd, rule); + break; + } + } + + return true; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"处理窗口时发生错误: {ex.Message}", LogHelper.LogType.Error); + return true; + } + } + + private WindowInfo GetWindowInfo(IntPtr hWnd) + { + try + { + // 获取进程ID + GetWindowThreadProcessId(hWnd, out uint processId); + if (processId == 0) return null; + + // 获取进程信息 + var process = Process.GetProcessById((int)processId); + if (process == null) return null; + + // 获取窗口标题 + var titleBuilder = new StringBuilder(256); + GetWindowText(hWnd, titleBuilder, titleBuilder.Capacity); + + // 获取窗口类名 + var classBuilder = new StringBuilder(256); + GetClassName(hWnd, classBuilder, classBuilder.Capacity); + + return new WindowInfo + { + Handle = hWnd, + ProcessId = processId, + ProcessName = process.ProcessName, + WindowTitle = titleBuilder.ToString(), + ClassName = classBuilder.ToString(), + Process = process + }; + } + catch + { + return null; + } + } + + private bool MatchesRule(WindowInfo windowInfo, InterceptRule rule) + { + try + { + // 检查进程名(如果指定了进程名) + if (!string.IsNullOrEmpty(rule.ProcessName)) + { + if (!windowInfo.ProcessName.ToLower().Contains(rule.ProcessName.ToLower())) + { + return false; + } + } + + // 检查窗口标题(如果指定了模式) + if (!string.IsNullOrEmpty(rule.WindowTitlePattern)) + { + if (!windowInfo.WindowTitle.ToLower().Contains(rule.WindowTitlePattern.ToLower())) + { + return false; + } + } + + // 检查类名(如果指定了模式) + if (!string.IsNullOrEmpty(rule.ClassNamePattern)) + { + if (!windowInfo.ClassName.ToLower().Contains(rule.ClassNamePattern.ToLower())) + { + return false; + } + } + + // 如果所有检查都通过,就认为是目标窗口 + return true; + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"匹配规则时发生错误: {ex.Message}", LogHelper.LogType.Error); + return false; + } + } + + private void InterceptWindow(IntPtr hWnd, InterceptRule rule) + { + try + { + // 使用多种方法隐藏窗口 + // 方法1:移动到屏幕外 + SetWindowPos(hWnd, IntPtr.Zero, -2000, -2000, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_HIDEWINDOW); + + // 方法2:最小化窗口 + ShowWindow(hWnd, SW_MINIMIZE); + + // 方法3:隐藏窗口 + ShowWindow(hWnd, SW_HIDE); + + // 记录拦截的窗口 + _interceptedWindows[hWnd] = rule.Type; + + LogHelper.WriteLogToFile($"成功拦截窗口: {GetWindowTitle(hWnd)}", LogHelper.LogType.Event); + + // 触发事件 + WindowIntercepted?.Invoke(this, new WindowInterceptedEventArgs + { + WindowHandle = hWnd, + InterceptType = rule.Type, + Rule = rule, + WindowTitle = GetWindowTitle(hWnd) + }); + } + catch (Exception ex) + { + LogHelper.WriteLogToFile($"拦截窗口时发生错误: {ex.Message}", LogHelper.LogType.Error); + } + } + + private string GetWindowTitle(IntPtr hWnd) + { + try + { + var titleBuilder = new StringBuilder(256); + GetWindowText(hWnd, titleBuilder, titleBuilder.Capacity); + return titleBuilder.ToString(); + } + catch + { + return "Unknown"; + } + } + + private bool IsMainWindow(IntPtr hWnd) + { + try + { + // 检查是否有父窗口 + var parent = GetWindow(hWnd, 4); // GW_OWNER + if (parent != IntPtr.Zero) return false; + + // 检查窗口样式 + var style = GetWindowLong(hWnd, -16); // GWL_STYLE + var exStyle = GetWindowLong(hWnd, GWL_EXSTYLE); + + // 主窗口通常有 WS_CAPTION 和 WS_SYSMENU + const uint WS_CAPTION = 0x00C00000; + const uint WS_SYSMENU = 0x00080000; + const uint WS_MINIMIZEBOX = 0x00020000; + const uint WS_MAXIMIZEBOX = 0x00010000; + + if ((style & WS_CAPTION) != 0 && (style & WS_SYSMENU) != 0) + { + return true; // 这可能是主窗口 + } + + // 检查窗口大小,主窗口通常比较大 + var rect = new ForegroundWindowInfo.RECT(); + GetWindowRect(hWnd, out rect); + var width = rect.Right - rect.Left; + var height = rect.Bottom - rect.Top; + + // 如果窗口很大,可能是主窗口 + if (width > 800 && height > 600) + { + return true; + } + + return false; + } + catch + { + return false; + } + } + + #endregion + + #region 辅助类 + + private class WindowInfo + { + public IntPtr Handle { get; set; } + public uint ProcessId { get; set; } + public string ProcessName { get; set; } + public string WindowTitle { get; set; } + public string ClassName { get; set; } + public Process Process { get; set; } + } + + #endregion + + #region 事件参数类 + + public class WindowInterceptedEventArgs : EventArgs + { + public IntPtr WindowHandle { get; set; } + public InterceptType InterceptType { get; set; } + public InterceptRule Rule { get; set; } + public string WindowTitle { get; set; } + } + + public class WindowRestoredEventArgs : EventArgs + { + public IntPtr WindowHandle { get; set; } + public InterceptType InterceptType { get; set; } + } + + #endregion + + #region IDisposable + + public void Dispose() + { + if (_disposed) return; + + Stop(); + _scanTimer?.Dispose(); + + // 恢复所有被拦截的窗口 + RestoreAllWindows(); + + _disposed = true; + } + + #endregion + } +} \ No newline at end of file diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml index d73b4879..a0b6dedf 100644 --- a/Ink Canvas/MainWindow.xaml +++ b/Ink Canvas/MainWindow.xaml @@ -2745,6 +2745,255 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +