From 81b291f2e61cae7cf999e0d6b9d96c9bb902d15c Mon Sep 17 00:00:00 2001 From: CJKmkp <2564608840@qq.com> Date: Sat, 2 May 2026 14:52:46 +0800 Subject: [PATCH] =?UTF-8?q?improve:=E6=82=AC=E6=B5=AE=E7=AA=97=E6=8B=A6?= =?UTF-8?q?=E6=88=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Helpers/FloatingWindowInterceptor.cs | 1472 ++++++++--------- .../MW_FloatingWindowInterceptor.cs | 49 + Ink Canvas/Resources/Settings.cs | 20 +- 3 files changed, 717 insertions(+), 824 deletions(-) diff --git a/Ink Canvas/Helpers/FloatingWindowInterceptor.cs b/Ink Canvas/Helpers/FloatingWindowInterceptor.cs index ec745625..d94004cb 100644 --- a/Ink Canvas/Helpers/FloatingWindowInterceptor.cs +++ b/Ink Canvas/Helpers/FloatingWindowInterceptor.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Text; +using System.Text.RegularExpressions; using System.Threading; using System.Windows.Threading; @@ -53,13 +54,13 @@ namespace Ink_Canvas.Helpers private static extern bool IsWindow(IntPtr hWnd); [DllImport("user32.dll")] - private static extern bool GetWindowRect(IntPtr hWnd, out ForegroundWindowInfo.RECT lpRect); + private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); [DllImport("user32.dll")] private static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); [DllImport("dwmapi.dll")] - private static extern int DwmGetWindowAttribute(IntPtr hWnd, int dwAttribute, out ForegroundWindowInfo.RECT pvAttribute, int cbAttribute); + private static extern int DwmGetWindowAttribute(IntPtr hWnd, int dwAttribute, out RECT pvAttribute, int cbAttribute); [DllImport("user32.dll")] private static extern IntPtr GetDC(IntPtr hWnd); @@ -71,10 +72,7 @@ namespace Ink_Canvas.Helpers private static extern int GetDeviceCaps(IntPtr hdc, int nIndex); [DllImport("user32.dll")] - private static extern bool SetForegroundWindow(IntPtr hWnd); - - [DllImport("user32.dll")] - private static extern bool BringWindowToTop(IntPtr hWnd); + private static extern int GetSystemMetrics(int nIndex); [DllImport("kernel32.dll")] private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId); @@ -82,193 +80,135 @@ namespace Ink_Canvas.Helpers [DllImport("kernel32.dll")] private static extern bool CloseHandle(IntPtr hObject); - [DllImport("kernel32.dll")] - private static extern int GetProcessImageFileName(IntPtr hProcess, StringBuilder lpImageFileName, int nSize); + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + private static extern bool QueryFullProcessImageNameW(IntPtr hProcess, uint dwFlags, StringBuilder lpExeName, ref uint lpdwSize); + + [StructLayout(LayoutKind.Sequential)] + private struct RECT + { + public int Left, Top, Right, Bottom; + } private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); private const int SW_HIDE = 0; - private const int SW_SHOW = 1; - private const int SW_SHOWNORMAL = 1; - private const int SW_MINIMIZE = 6; - private const int SW_RESTORE = 9; + private const int SW_SHOWNOACTIVATE = 4; private const int GWL_STYLE = -16; - 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 SWP_SHOWWINDOW = 0x0040; + private const uint SWP_NOACTIVATE = 0x0010; - private const uint PROCESS_QUERY_INFORMATION = 0x0400; - private const uint PROCESS_VM_READ = 0x0010; + private const uint PROCESS_QUERY_LIMITED_INFORMATION = 0x1000; private const uint WM_CLOSE = 0x0010; private const int DWMWA_EXTENDED_FRAME_BOUNDS = 9; private const int LOGPIXELSX = 88; private const int LOGPIXELSY = 90; - + private const int SM_CXSCREEN = 0; + private const int SM_CYSCREEN = 1; #endregion #region 拦截规则定义 - /// - /// 拦截规则类型 - /// public enum InterceptType { - /// - /// 希沃白板3 桌面悬浮窗 - /// + // 希沃系列 SeewoWhiteboard3Floating, - /// - /// 希沃白板5 桌面悬浮窗 - /// SeewoWhiteboard5Floating, - /// - /// 希沃白板5C 桌面悬浮窗 - /// SeewoWhiteboard5CFloating, - /// - /// 希沃品课教师端 桌面悬浮窗 - /// SeewoPincoSideBarFloating, - /// - /// 希沃品课教师端 画笔悬浮窗(包括PPT控件) - /// SeewoPincoDrawingFloating, - /// - /// 希沃品课教师端 桌面画板 - /// SeewoPincoBoardService, - /// - /// 希沃PPT小工具 - /// SeewoPPTFloating, - /// - /// AiClass 桌面悬浮窗 - /// + SeewoIwbAssistantFloating, + SeewoDesktopSideBarFloating, + SeewoDesktopDrawingFloating, + // 欧帝 + YiouBoardFloating, + // AiClass AiClassFloating, - /// - /// 鸿合屏幕书写 - /// + // ClassIn X + ClassInXFloating, + // 鸿合 HiteAnnotationFloating, - /// - /// 畅言智慧课堂 主栏悬浮窗 - /// + // 畅言4.0 ChangYanFloating, - /// - /// 畅言智慧课堂 画笔设置 - /// ChangYanBrushSettings, - /// - /// 畅言智慧课堂 滑动清除 - /// ChangYanSwipeClear, - /// - /// 畅言智慧课堂 互动 - /// ChangYanInteraction, - /// - /// 畅言智慧课堂 学科应用 - /// ChangYanSubjectApp, - /// - /// 畅言智慧课堂 管控 - /// ChangYanControl, - /// - /// 畅言智慧课堂 通用工具 - /// ChangYanCommonTools, - /// - /// 畅言智慧课堂 场景工具栏 - /// ChangYanSceneToolbar, - /// - /// 畅言智慧课堂 绘制窗口 - /// ChangYanDrawWindow, - /// - /// 畅言智慧课堂 PPT悬浮窗 - /// ChangYanPptFloating, - /// - /// 畅言智慧课堂 PPT页面控制 - /// ChangYanPptPageControl, - /// - /// 畅言智慧课堂 PPT返回 - /// ChangYanPptGoBack, - /// - /// 畅言智慧课堂 PPT预览 - /// ChangYanPptPreview, - /// - /// 天喻教育云互动课堂 桌面悬浮窗(包括PPT控件) - /// + // 畅言5.0 + ChangYan5Floating, + // 天喻 IntelligentClassFloating, - /// - /// 天喻教育云互动课堂 PPT悬浮窗 - /// IntelligentClassPptFloating, - /// - /// 希沃桌面 画笔悬浮窗 - /// + // C30智能教学 + Iclass30SidebarFloating, + Iclass30Floating, + // 保留(已被SeewoDesktopDrawingFloating替代,但保持向后兼容) SeewoDesktopAnnotationFloating, - /// - /// 希沃桌面 侧栏悬浮窗 - /// - SeewoDesktopSideBarFloating } - /// - /// 拦截规则 - /// + public enum StyleMatchType { Exact, Subset } + + public enum SizeMatchType { None, Exact, DPIScale, Scale, FullScreen, FullHeight, FullWidth } + public class InterceptRule { public InterceptType Type { get; set; } public string ProcessName { get; set; } public string WindowTitlePattern { get; set; } + public bool TitleIsRegex { get; set; } = false; public string ClassNamePattern { get; set; } + public bool ClassNameIsRegex { get; set; } = true; public bool IsEnabled { get; set; } public bool RequiresAdmin { get; set; } public string Description { get; set; } public InterceptType? ParentType { get; set; } public List ChildTypes { get; set; } = new List(); - // 新增的精确匹配字段 public bool HasWindowStyle { get; set; } public uint WindowStyle { get; set; } - public bool HasWindowSize { get; set; } + public StyleMatchType StyleMatchType { get; set; } = StyleMatchType.Exact; + + public SizeMatchType SizeMatchType { get; set; } = SizeMatchType.None; public int WindowWidth { get; set; } public int WindowHeight { get; set; } - public bool ExactTitleMatch { get; set; } = false; - public bool ExactClassNameMatch { get; set; } = false; - // 运行时状态字段 + // Action type (Close vs Hide vs Move) + public InterceptActionType ActionType { get; set; } = InterceptActionType.Hide; + + // 运行时扫描状态(每次扫描重置) public bool foundHwnd { get; set; } = false; public IntPtr outHwnd { get; set; } = IntPtr.Zero; } + public enum InterceptActionType { Hide, Close, Move } + #endregion #region 私有字段 private readonly Dictionary _interceptRules; private readonly Dictionary _interceptedWindows; + private readonly Dictionary _movedWindowPositions; private readonly Timer _scanTimer; private readonly Dispatcher _dispatcher; private bool _isRunning; private bool _disposed; - // 简化的性能统计 private int _consecutiveEmptyScans = 0; private DateTime _lastSuccessfulScan = DateTime.Now; @@ -293,6 +233,7 @@ namespace Ink_Canvas.Helpers { _interceptRules = new Dictionary(); _interceptedWindows = new Dictionary(); + _movedWindowPositions = new Dictionary(); _dispatcher = Dispatcher.CurrentDispatcher; InitializeRules(); @@ -301,171 +242,281 @@ namespace Ink_Canvas.Helpers #endregion - #region 初始化 + #region 初始化规则 private void InitializeRules() { - // 希沃白板3 桌面悬浮窗 - _interceptRules[InterceptType.SeewoWhiteboard3Floating] = new InterceptRule + // ─── 希沃白板3 ─────────────────────────────────────────────────────────── + AddRule(new InterceptRule { Type = InterceptType.SeewoWhiteboard3Floating, - ProcessName = "EasiNote", - WindowTitlePattern = "Note", - ClassNamePattern = "HwndWrapper[EasiNote.exe;;", - IsEnabled = true, - RequiresAdmin = false, - Description = "希沃白板3 桌面悬浮窗", + ProcessName = "EasiNote.exe", + WindowTitlePattern = "^Note$", + TitleIsRegex = true, + ClassNamePattern = @"HwndWrapper\[EasiNote\.exe;;[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\]", + ClassNameIsRegex = true, HasWindowStyle = true, - WindowStyle = 370081792, - HasWindowSize = true, - WindowWidth = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width, - WindowHeight = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height, - ExactTitleMatch = true, - ExactClassNameMatch = false - }; + WindowStyle = 0x16CF0000, // WS_CLIPSIBLINGS|WS_CLIPCHILDREN|WS_SYSMENU|WS_THICKFRAME|WS_GROUP|WS_TABSTOP|WS_MINIMIZEBOX|WS_MAXIMIZEBOX + StyleMatchType = StyleMatchType.Exact, + SizeMatchType = SizeMatchType.FullScreen, + IsEnabled = true, + Description = "希沃白板3 桌面悬浮窗" + }); - // 希沃白板5 桌面悬浮窗 - _interceptRules[InterceptType.SeewoWhiteboard5Floating] = new InterceptRule + // ─── 希沃白板5 ─────────────────────────────────────────────────────────── + AddRule(new InterceptRule { Type = InterceptType.SeewoWhiteboard5Floating, - ProcessName = "EasiNote", + ProcessName = "EasiNote.exe", WindowTitlePattern = "", - ClassNamePattern = "HwndWrapper[EasiNote;;", - IsEnabled = true, - RequiresAdmin = false, - Description = "希沃白板5 桌面悬浮窗", + ClassNamePattern = @"HwndWrapper\[EasiNote;;[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\]", + ClassNameIsRegex = true, HasWindowStyle = true, - WindowStyle = 369623040, - HasWindowSize = true, - WindowWidth = 550, - WindowHeight = 200, - ExactTitleMatch = false, - ExactClassNameMatch = false - }; + WindowStyle = 0x16080000, // WS_CLIPSIBLINGS|WS_CLIPCHILDREN|WS_SYSMENU + StyleMatchType = StyleMatchType.Exact, + SizeMatchType = SizeMatchType.FullScreen, + IsEnabled = true, + Description = "希沃白板5 桌面悬浮窗" + }); - // 希沃白板5C 桌面悬浮窗 - _interceptRules[InterceptType.SeewoWhiteboard5CFloating] = new InterceptRule + // ─── 希沃白板5C ────────────────────────────────────────────────────────── + AddRule(new InterceptRule { Type = InterceptType.SeewoWhiteboard5CFloating, - ProcessName = "EasiNote5C", + ProcessName = "EasiNote5C.exe", WindowTitlePattern = "", - ClassNamePattern = "HwndWrapper[EasiNote5C;;", - IsEnabled = true, - RequiresAdmin = false, - Description = "希沃白板5C 桌面悬浮窗", + ClassNamePattern = @"HwndWrapper\[EasiNote5C;;[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\]", + ClassNameIsRegex = true, HasWindowStyle = true, - WindowStyle = 369623040, - HasWindowSize = true, - WindowWidth = 550, - WindowHeight = 200, - ExactTitleMatch = false, - ExactClassNameMatch = false - }; + WindowStyle = 0x16080000, + StyleMatchType = StyleMatchType.Subset, + SizeMatchType = SizeMatchType.FullScreen, + IsEnabled = true, + Description = "希沃白板5C 桌面悬浮窗" + }); - // 希沃品课教师端 桌面悬浮窗(父规则) - _interceptRules[InterceptType.SeewoPincoSideBarFloating] = new InterceptRule + // ─── 希沃品课 侧栏(父规则)──────────────────────────────────────────── + AddRule(new InterceptRule { Type = InterceptType.SeewoPincoSideBarFloating, - ProcessName = "ClassIn", - WindowTitlePattern = "希沃品课——appBar", - ClassNamePattern = "Chrome_WidgetWin_1", - IsEnabled = true, - RequiresAdmin = false, - Description = "希沃品课教师端 桌面悬浮窗", - ParentType = null, - ChildTypes = new List { InterceptType.SeewoPincoDrawingFloating, InterceptType.SeewoPincoBoardService }, + ProcessName = "seewoPincoTeacher.exe", + WindowTitlePattern = "^希沃品课——appBar$", + TitleIsRegex = true, + ClassNamePattern = "^Chrome_WidgetWin_1$", + ClassNameIsRegex = true, HasWindowStyle = true, - WindowStyle = 0x16CF0000, - ExactTitleMatch = true, - ExactClassNameMatch = true - }; + WindowStyle = 0x04440000, // WS_CLIPSIBLINGS|WS_GROUP|WS_MINIMIZEBOX + StyleMatchType = StyleMatchType.Exact, + SizeMatchType = SizeMatchType.None, + ActionType = InterceptActionType.Close, + IsEnabled = true, + Description = "希沃品课教师端 侧栏悬浮窗", + ChildTypes = new List { InterceptType.SeewoPincoDrawingFloating, InterceptType.SeewoPincoBoardService } + }); - // 希沃品课教师端 画笔悬浮窗(子规则) - _interceptRules[InterceptType.SeewoPincoDrawingFloating] = new InterceptRule + // ─── 希沃品课 画笔(子规则)──────────────────────────────────────────── + AddRule(new InterceptRule { Type = InterceptType.SeewoPincoDrawingFloating, - ProcessName = "ClassIn", - WindowTitlePattern = "希沃品课——integration", - ClassNamePattern = "Chrome_WidgetWin_1", - IsEnabled = true, - RequiresAdmin = false, - Description = "希沃品课教师端 画笔悬浮窗(包括PPT控件)", - ParentType = InterceptType.SeewoPincoSideBarFloating, - ChildTypes = new List(), + ProcessName = "seewoPincoTeacher.exe", + WindowTitlePattern = "^希沃品课——integration$", + TitleIsRegex = true, + ClassNamePattern = "^Chrome_WidgetWin_1$", + ClassNameIsRegex = true, HasWindowStyle = true, - WindowStyle = 335675392, - ExactTitleMatch = true, - ExactClassNameMatch = true - }; + WindowStyle = 0x04440000, + StyleMatchType = StyleMatchType.Exact, + SizeMatchType = SizeMatchType.FullScreen, + ActionType = InterceptActionType.Close, + IsEnabled = true, + Description = "希沃品课教师端 画笔悬浮窗(包括PPT控件)", + ParentType = InterceptType.SeewoPincoSideBarFloating + }); - // 希沃品课教师端 桌面画板(子规则) - _interceptRules[InterceptType.SeewoPincoBoardService] = new InterceptRule + // ─── 希沃品课 桌面画板(子规则)───────────────────────────────────────── + AddRule(new InterceptRule { Type = InterceptType.SeewoPincoBoardService, - ProcessName = "BoardService", + ProcessName = "BoardService.exe", WindowTitlePattern = "", - ClassNamePattern = "HwndWrapper[BoardService;;", - IsEnabled = true, - RequiresAdmin = false, - Description = "希沃品课教师端 桌面画板", - ParentType = InterceptType.SeewoPincoSideBarFloating, - ChildTypes = new List(), + ClassNamePattern = @"HwndWrapper\[BoardService;;[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\]", + ClassNameIsRegex = true, HasWindowStyle = true, - WindowStyle = 369623040, - HasWindowSize = true, - WindowWidth = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Width, - WindowHeight = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height, - ExactTitleMatch = false, - ExactClassNameMatch = false - }; + WindowStyle = 0x16080000, + StyleMatchType = StyleMatchType.Exact, + SizeMatchType = SizeMatchType.FullScreen, + ActionType = InterceptActionType.Close, + IsEnabled = true, + Description = "希沃品课教师端 桌面画板", + ParentType = InterceptType.SeewoPincoSideBarFloating + }); - // 希沃PPT小工具 - _interceptRules[InterceptType.SeewoPPTFloating] = new InterceptRule + // ─── 希沃PPT小工具 ─────────────────────────────────────────────────────── + AddRule(new InterceptRule { Type = InterceptType.SeewoPPTFloating, - ProcessName = "PPTService", + ProcessName = "PPTService.exe", WindowTitlePattern = "", - ClassNamePattern = "HwndWrapper[PPTService.exe;;", + ClassNamePattern = @"HwndWrapper\[PPTService\.exe;;[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\]", + ClassNameIsRegex = true, + HasWindowStyle = true, + WindowStyle = 0x16080000, + StyleMatchType = StyleMatchType.Subset, + SizeMatchType = SizeMatchType.FullScreen, IsEnabled = true, - RequiresAdmin = false, Description = "希沃PPT小工具" - }; + }); - // AiClass 桌面悬浮窗 - _interceptRules[InterceptType.AiClassFloating] = new InterceptRule + // ─── 希沃课堂助手 ──────────────────────────────────────────────────────── + AddRule(new InterceptRule { - Type = InterceptType.AiClassFloating, - ProcessName = "ClassIn", - WindowTitlePattern = "TransparentWindow", - ClassNamePattern = "UIWndTransparent", + Type = InterceptType.SeewoIwbAssistantFloating, + ProcessName = "SeewoIwbAssistant.exe", + WindowTitlePattern = "", + ClassNamePattern = @"HwndWrapper\[SeewoIwbAssistant;;[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\]", + ClassNameIsRegex = true, + HasWindowStyle = true, + WindowStyle = 0x16080000, + StyleMatchType = StyleMatchType.Subset, + SizeMatchType = SizeMatchType.FullScreen, IsEnabled = true, - RequiresAdmin = false, - Description = "AiClass 桌面悬浮窗" - }; + Description = "希沃课堂助手" + }); - // 鸿合屏幕书写 - _interceptRules[InterceptType.HiteAnnotationFloating] = new InterceptRule + // ─── 希沃桌面 侧栏 ─────────────────────────────────────────────────────── + AddRule(new InterceptRule { - Type = InterceptType.HiteAnnotationFloating, - ProcessName = "HiteVision", - WindowTitlePattern = "HiteAnnotation", - ClassNamePattern = "Qt5QWindowToolSaveBits", - IsEnabled = true, - RequiresAdmin = false, - Description = "鸿合屏幕书写" - }; - - // 畅言智慧课堂 主栏悬浮窗(父规则) - _interceptRules[InterceptType.ChangYanFloating] = new InterceptRule - { - Type = InterceptType.ChangYanFloating, - ProcessName = "ClassIn", - WindowTitlePattern = "ifly", - ClassNamePattern = "Qt5QWindowOwnDCIcon", + Type = InterceptType.SeewoDesktopSideBarFloating, + ProcessName = "ResidentSideBar.exe", + WindowTitlePattern = "^ResidentSideBar$", + TitleIsRegex = true, + ClassNamePattern = @"HwndWrapper\[ResidentSideBar\.exe;;[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\]", + ClassNameIsRegex = true, + HasWindowStyle = true, + WindowStyle = 0x17080000, // WS_CLIPSIBLINGS|WS_CLIPCHILDREN|WS_MAXIMIZE|WS_SYSMENU + StyleMatchType = StyleMatchType.Exact, + SizeMatchType = SizeMatchType.FullScreen, IsEnabled = true, RequiresAdmin = true, - Description = "畅言智慧课堂 主栏悬浮窗", - ParentType = null, + Description = "希沃桌面 侧栏悬浮窗" + }); + + // ─── 希沃桌面 画笔 ─────────────────────────────────────────────────────── + // (旧名 SeewoDesktopAnnotationFloating 已合并到此条,保留向后兼容枚举) + AddRule(new InterceptRule + { + Type = InterceptType.SeewoDesktopDrawingFloating, + ProcessName = "DesktopAnnotation.exe", + WindowTitlePattern = "", + ClassNamePattern = @"HwndWrapper\[DesktopAnnotation(\.exe)?;;[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\]", + ClassNameIsRegex = true, + HasWindowStyle = true, + WindowStyle = 0x16080000, + StyleMatchType = StyleMatchType.Exact, + SizeMatchType = SizeMatchType.FullScreen, + IsEnabled = true, + Description = "希沃桌面 画笔悬浮窗" + }); + + // 向后兼容:SeewoDesktopAnnotationFloating 指向相同规则逻辑,默认禁用(使用新条目) + AddRule(new InterceptRule + { + Type = InterceptType.SeewoDesktopAnnotationFloating, + ProcessName = "DesktopAnnotation.exe", + WindowTitlePattern = "", + ClassNamePattern = @"HwndWrapper\[DesktopAnnotation(\.exe)?;;[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\]", + ClassNameIsRegex = true, + HasWindowStyle = true, + WindowStyle = 0x16080000, + StyleMatchType = StyleMatchType.Exact, + SizeMatchType = SizeMatchType.FullScreen, + IsEnabled = false, + Description = "希沃桌面 画笔悬浮窗(旧)" + }); + + // ─── 欧帝白板 ───────────────────────────────────────────────────────────── + AddRule(new InterceptRule + { + Type = InterceptType.YiouBoardFloating, + ProcessName = "WriteBoard.exe", + WindowTitlePattern = "^WinYODesktop$", + TitleIsRegex = true, + ClassNamePattern = @"HwndWrapper\[WriteBoard\.exe;;[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\]", + ClassNameIsRegex = true, + HasWindowStyle = true, + WindowStyle = 0x16080000, + StyleMatchType = StyleMatchType.Subset, + SizeMatchType = SizeMatchType.DPIScale, + WindowWidth = 780, + WindowHeight = 60, + IsEnabled = true, + RequiresAdmin = true, + Description = "欧帝白板 悬浮窗" + }); + + // ─── AiClass ────────────────────────────────────────────────────────────── + AddRule(new InterceptRule + { + Type = InterceptType.AiClassFloating, + ProcessName = "wisdom_class.exe", + WindowTitlePattern = "^TransparentWindow$", + TitleIsRegex = true, + ClassNamePattern = "^UIWndTransparent$", + ClassNameIsRegex = true, + HasWindowStyle = true, + WindowStyle = 0x82000000, // WS_POPUP|WS_CLIPSIBLINGS + StyleMatchType = StyleMatchType.Subset, + SizeMatchType = SizeMatchType.FullScreen, + IsEnabled = true, + Description = "AiClass 桌面悬浮窗" + }); + + // ─── ClassIn X ──────────────────────────────────────────────────────────── + AddRule(new InterceptRule + { + Type = InterceptType.ClassInXFloating, + ProcessName = "ClassIn X.exe", + WindowTitlePattern = "^ClassIn X$", + TitleIsRegex = true, + ClassNamePattern = "^Qt5151QWindowIcon$", + ClassNameIsRegex = true, + HasWindowStyle = true, + WindowStyle = 0x86000000, // WS_POPUP|WS_CLIPSIBLINGS|WS_CLIPCHILDREN + StyleMatchType = StyleMatchType.Subset, + SizeMatchType = SizeMatchType.FullScreen, + IsEnabled = true, + Description = "ClassIn X 桌面悬浮窗" + }); + + // ─── 鸿合屏幕书写 ──────────────────────────────────────────────────────── + AddRule(new InterceptRule + { + Type = InterceptType.HiteAnnotationFloating, + ProcessName = "HiteVision.exe", + WindowTitlePattern = "^HiteAnnotation$", + TitleIsRegex = true, + ClassNamePattern = "^Qt5QWindowToolSaveBits$", + ClassNameIsRegex = true, + IsEnabled = true, + Description = "鸿合屏幕书写" + }); + + // ─── 畅言4.0 父规则 ────────────────────────────────────────────────────── + AddRule(new InterceptRule + { + Type = InterceptType.ChangYanFloating, + ProcessName = "ifly.qzk.Toolbar.exe", + WindowTitlePattern = "^DrawWindow$", + TitleIsRegex = true, + ClassNamePattern = "^Qt5QWindowToolSaveBits$", + ClassNameIsRegex = true, + HasWindowStyle = true, + WindowStyle = 0x86000000, + StyleMatchType = StyleMatchType.Subset, + SizeMatchType = SizeMatchType.FullScreen, + IsEnabled = true, + RequiresAdmin = true, + Description = "畅言智慧课堂4.0 绘制窗口", ChildTypes = new List { InterceptType.ChangYanBrushSettings, @@ -475,401 +526,246 @@ namespace Ink_Canvas.Helpers InterceptType.ChangYanControl, InterceptType.ChangYanCommonTools, InterceptType.ChangYanSceneToolbar, - InterceptType.ChangYanDrawWindow + InterceptType.ChangYanDrawWindow, } - }; + }); - // 畅言智慧课堂 画笔设置(子规则) - _interceptRules[InterceptType.ChangYanBrushSettings] = new InterceptRule - { - Type = InterceptType.ChangYanBrushSettings, - ProcessName = "ClassIn", - WindowTitlePattern = "画笔设置", - ClassNamePattern = "Qt5QWindowOwnDCIcon", - IsEnabled = true, - RequiresAdmin = true, - Description = "畅言智慧课堂 画笔设置", - ParentType = InterceptType.ChangYanFloating, - ChildTypes = new List() - }; + // 畅言4.0 子规则 + AddChangYan4ChildRule(InterceptType.ChangYanBrushSettings, "^画笔设置$", "^Qt5QWindowOwnDCIcon$", "畅言4.0 画笔设置"); + AddChangYan4ChildRule(InterceptType.ChangYanSwipeClear, "^滑动清除$", "^Qt5QWindowOwnDCIcon$", "畅言4.0 滑动清除"); + AddChangYan4ChildRule(InterceptType.ChangYanInteraction, "^互动$", "^Qt5QWindowOwnDCIcon$", "畅言4.0 互动"); + AddChangYan4ChildRule(InterceptType.ChangYanSubjectApp, "^学科应用$", "^Qt5QWindowOwnDCIcon$", "畅言4.0 学科应用"); + AddChangYan4ChildRule(InterceptType.ChangYanControl, "^管控$", "^Qt5QWindowOwnDCIcon$", "畅言4.0 管控"); + AddChangYan4ChildRule(InterceptType.ChangYanCommonTools, "^通用工具$", "^Qt5QWindowOwnDCIcon$", "畅言4.0 通用工具"); + AddChangYan4ChildRule(InterceptType.ChangYanSceneToolbar, "^SceneToolbar$", "^Qt5QWindowOwnDCIcon$", "畅言4.0 场景工具栏"); + AddChangYan4ChildRule(InterceptType.ChangYanDrawWindow, "^DrawWindow$", "^Qt5QWindowToolSaveBits$", "畅言4.0 绘制窗口(子)"); - // 畅言智慧课堂 滑动清除(子规则) - _interceptRules[InterceptType.ChangYanSwipeClear] = new InterceptRule - { - Type = InterceptType.ChangYanSwipeClear, - ProcessName = "ClassIn", - WindowTitlePattern = "滑动清除", - ClassNamePattern = "Qt5QWindowOwnDCIcon", - IsEnabled = true, - RequiresAdmin = true, - Description = "畅言智慧课堂 滑动清除", - ParentType = InterceptType.ChangYanFloating, - ChildTypes = new List() - }; - - // 畅言智慧课堂 互动(子规则) - _interceptRules[InterceptType.ChangYanInteraction] = new InterceptRule - { - Type = InterceptType.ChangYanInteraction, - ProcessName = "ClassIn", - WindowTitlePattern = "互动", - ClassNamePattern = "Qt5QWindowOwnDCIcon", - IsEnabled = true, - RequiresAdmin = true, - Description = "畅言智慧课堂 互动", - ParentType = InterceptType.ChangYanFloating, - ChildTypes = new List() - }; - - // 畅言智慧课堂 学科应用(子规则) - _interceptRules[InterceptType.ChangYanSubjectApp] = new InterceptRule - { - Type = InterceptType.ChangYanSubjectApp, - ProcessName = "ClassIn", - WindowTitlePattern = "学科应用", - ClassNamePattern = "Qt5QWindowOwnDCIcon", - IsEnabled = true, - RequiresAdmin = true, - Description = "畅言智慧课堂 学科应用", - ParentType = InterceptType.ChangYanFloating, - ChildTypes = new List() - }; - - // 畅言智慧课堂 管控(子规则) - _interceptRules[InterceptType.ChangYanControl] = new InterceptRule - { - Type = InterceptType.ChangYanControl, - ProcessName = "ClassIn", - WindowTitlePattern = "管控", - ClassNamePattern = "Qt5QWindowOwnDCIcon", - IsEnabled = true, - RequiresAdmin = true, - Description = "畅言智慧课堂 管控", - ParentType = InterceptType.ChangYanFloating, - ChildTypes = new List() - }; - - // 畅言智慧课堂 通用工具(子规则) - _interceptRules[InterceptType.ChangYanCommonTools] = new InterceptRule - { - Type = InterceptType.ChangYanCommonTools, - ProcessName = "ClassIn", - WindowTitlePattern = "通用工具", - ClassNamePattern = "Qt5QWindowOwnDCIcon", - IsEnabled = true, - RequiresAdmin = true, - Description = "畅言智慧课堂 通用工具", - ParentType = InterceptType.ChangYanFloating, - ChildTypes = new List() - }; - - // 畅言智慧课堂 场景工具栏(子规则) - _interceptRules[InterceptType.ChangYanSceneToolbar] = new InterceptRule - { - Type = InterceptType.ChangYanSceneToolbar, - ProcessName = "ClassIn", - WindowTitlePattern = "SceneToolbar", - ClassNamePattern = "Qt5QWindowOwnDCIcon", - IsEnabled = true, - RequiresAdmin = true, - Description = "畅言智慧课堂 场景工具栏", - ParentType = InterceptType.ChangYanFloating, - ChildTypes = new List() - }; - - // 畅言智慧课堂 绘制窗口(子规则) - _interceptRules[InterceptType.ChangYanDrawWindow] = new InterceptRule - { - Type = InterceptType.ChangYanDrawWindow, - ProcessName = "ClassIn", - WindowTitlePattern = "DrawWindow", - ClassNamePattern = "Qt5QWindowToolSaveBits", - IsEnabled = true, - RequiresAdmin = true, - Description = "畅言智慧课堂 绘制窗口", - ParentType = InterceptType.ChangYanFloating, - ChildTypes = new List() - }; - - // 畅言智慧课堂 PPT悬浮窗 - _interceptRules[InterceptType.ChangYanPptFloating] = new InterceptRule + // ─── 畅言4.0 PPT 悬浮窗 ───────────────────────────────────────────────── + AddRule(new InterceptRule { Type = InterceptType.ChangYanPptFloating, - ProcessName = "ClassIn", + ProcessName = "ifly.qzk.Toolbar.exe", WindowTitlePattern = "Exch", - ClassNamePattern = "Qt5QWindowToolSaveBitsOwnDC", + ClassNamePattern = "^Qt5QWindowToolSaveBitsOwnDC$", + ClassNameIsRegex = true, IsEnabled = true, RequiresAdmin = true, - Description = "畅言智慧课堂 PPT悬浮窗", - ParentType = null, + Description = "畅言4.0 PPT悬浮窗", ChildTypes = new List { InterceptType.ChangYanPptPageControl, InterceptType.ChangYanPptGoBack, InterceptType.ChangYanPptPreview } - }; + }); + AddChangYan4PptChildRule(InterceptType.ChangYanPptPageControl, "PageCtl", "畅言4.0 PPT页面控制"); + AddChangYan4PptChildRule(InterceptType.ChangYanPptGoBack, "Goback", "畅言4.0 PPT返回"); + AddChangYan4PptChildRule(InterceptType.ChangYanPptPreview, "Preview", "畅言4.0 PPT预览"); - // 畅言智慧课堂 PPT页面控制(子规则) - _interceptRules[InterceptType.ChangYanPptPageControl] = new InterceptRule + // ─── 畅言5.0 ───────────────────────────────────────────────────────────── + AddRule(new InterceptRule { - Type = InterceptType.ChangYanPptPageControl, - ProcessName = "ClassIn", - WindowTitlePattern = "PageCtl", - ClassNamePattern = "Qt5QWindowToolSaveBitsOwnDC", + Type = InterceptType.ChangYan5Floating, + ProcessName = "ifly.qzk.Toolbar.exe", + WindowTitlePattern = "^DrawWindow$", + TitleIsRegex = true, + ClassNamePattern = "^Qt5152QWindowIcon$", + ClassNameIsRegex = true, + HasWindowStyle = true, + WindowStyle = 0x86000000, + StyleMatchType = StyleMatchType.Subset, + SizeMatchType = SizeMatchType.FullScreen, IsEnabled = true, RequiresAdmin = true, - Description = "畅言智慧课堂 PPT页面控制", - ParentType = InterceptType.ChangYanPptFloating, - ChildTypes = new List() - }; + Description = "畅言智慧课堂5.0 绘制窗口" + }); - // 畅言智慧课堂 PPT返回(子规则) - _interceptRules[InterceptType.ChangYanPptGoBack] = new InterceptRule - { - Type = InterceptType.ChangYanPptGoBack, - ProcessName = "ClassIn", - WindowTitlePattern = "Goback", - ClassNamePattern = "Qt5QWindowToolSaveBitsOwnDC", - IsEnabled = true, - RequiresAdmin = true, - Description = "畅言智慧课堂 PPT返回", - ParentType = InterceptType.ChangYanPptFloating, - ChildTypes = new List() - }; - - // 畅言智慧课堂 PPT预览(子规则) - _interceptRules[InterceptType.ChangYanPptPreview] = new InterceptRule - { - Type = InterceptType.ChangYanPptPreview, - ProcessName = "ClassIn", - WindowTitlePattern = "Preview", - ClassNamePattern = "Qt5QWindowToolSaveBitsOwnDC", - IsEnabled = true, - RequiresAdmin = true, - Description = "畅言智慧课堂 PPT预览", - ParentType = InterceptType.ChangYanPptFloating, - ChildTypes = new List() - }; - - // 天喻教育云互动课堂 桌面悬浮窗(父规则) - _interceptRules[InterceptType.IntelligentClassFloating] = new InterceptRule + // ─── 天喻互动课堂 父规则 ────────────────────────────────────────────────── + AddRule(new InterceptRule { Type = InterceptType.IntelligentClassFloating, - ProcessName = "IntelligentClassApp", - WindowTitlePattern = "桌面小工具 - 互动课堂", - ClassNamePattern = "HwndWrapper[IntelligentClassApp.exe;;", + ProcessName = "IntelligentClassApp.exe", + WindowTitlePattern = "^桌面小工具 - 互动课堂$", + TitleIsRegex = true, + ClassNamePattern = @"HwndWrapper\[IntelligentClassApp\.exe;;[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\]", + ClassNameIsRegex = true, + HasWindowStyle = true, + WindowStyle = 0x17080000, + StyleMatchType = StyleMatchType.Subset, + SizeMatchType = SizeMatchType.FullScreen, IsEnabled = true, - RequiresAdmin = false, - Description = "天喻教育云互动课堂 桌面悬浮窗(包括PPT控件)", - ParentType = null, + Description = "天喻互动课堂 桌面悬浮窗", ChildTypes = new List { InterceptType.IntelligentClassPptFloating } - }; + }); - // 天喻教育云互动课堂 PPT悬浮窗(子规则) - _interceptRules[InterceptType.IntelligentClassPptFloating] = new InterceptRule + // ─── 天喻互动课堂 PPT 子规则 ────────────────────────────────────────────── + AddRule(new InterceptRule { Type = InterceptType.IntelligentClassPptFloating, - ProcessName = "IntelligentClass", + ProcessName = "IntelligentClass.Office.PowerPoint.vsto|vstolocal", WindowTitlePattern = "", - ClassNamePattern = "HwndWrapper[IntelligentClass.Office.PowerPoint.vsto|vstolocal;VSTA_Main;", + ClassNamePattern = @"HwndWrapper\[IntelligentClass\.Office\.PowerPoint\.vsto\|vstolocal;VSTA_Main;[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\]", + ClassNameIsRegex = true, + HasWindowStyle = true, + WindowStyle = 0x86000000, + StyleMatchType = StyleMatchType.Subset, + SizeMatchType = SizeMatchType.FullScreen, IsEnabled = true, - RequiresAdmin = false, - Description = "天喻教育云互动课堂 PPT悬浮窗", - ParentType = InterceptType.IntelligentClassFloating, - ChildTypes = new List() - }; + Description = "天喻互动课堂 PPT悬浮窗", + ParentType = InterceptType.IntelligentClassFloating + }); - // 希沃桌面 画笔悬浮窗 - _interceptRules[InterceptType.SeewoDesktopAnnotationFloating] = new InterceptRule + // ─── C30 侧栏(默认关闭)──────────────────────────────────────────────── + AddRule(new InterceptRule { - Type = InterceptType.SeewoDesktopAnnotationFloating, - ProcessName = "DesktopAnnotation", - WindowTitlePattern = "", - ClassNamePattern = "HwndWrapper[DesktopAnnotation.exe;;", + Type = InterceptType.Iclass30SidebarFloating, + ProcessName = "teach.exe", + WindowTitlePattern = "^控制工具条$", + TitleIsRegex = true, + ClassNamePattern = "^ctrltool$", + ClassNameIsRegex = true, + HasWindowStyle = true, + WindowStyle = 0x86880000, // WS_POPUP|WS_CLIPSIBLINGS|WS_CLIPCHILDREN|WS_SYSMENU + StyleMatchType = StyleMatchType.Subset, + ActionType = InterceptActionType.Move, + IsEnabled = false, + Description = "C30智能教学 侧栏悬浮窗" + }); + + // ─── C30 画笔工具 ───────────────────────────────────────────────────────── + AddRule(new InterceptRule + { + Type = InterceptType.Iclass30Floating, + ProcessName = "teach.exe", + WindowTitlePattern = "^工具栏$", + TitleIsRegex = true, + ClassNamePattern = "^toolview$", + ClassNameIsRegex = true, + HasWindowStyle = true, + WindowStyle = 0x86880000, + StyleMatchType = StyleMatchType.Subset, + ActionType = InterceptActionType.Move, IsEnabled = true, - RequiresAdmin = false, - Description = "希沃桌面 画笔悬浮窗" - }; + Description = "C30智能教学 画笔工具栏" + }); + } - // 希沃桌面 侧栏悬浮窗 - _interceptRules[InterceptType.SeewoDesktopSideBarFloating] = new InterceptRule + private void AddRule(InterceptRule rule) + { + _interceptRules[rule.Type] = rule; + } + + private void AddChangYan4ChildRule(InterceptType type, string titlePattern, string classPattern, string desc) + { + AddRule(new InterceptRule { - Type = InterceptType.SeewoDesktopSideBarFloating, - ProcessName = "ResidentSideBar", - WindowTitlePattern = "ResidentSideBar", - ClassNamePattern = "HwndWrapper[ResidentSideBar.exe;;", + Type = type, + ProcessName = "ifly.qzk.Toolbar.exe", + WindowTitlePattern = titlePattern, + TitleIsRegex = true, + ClassNamePattern = classPattern, + ClassNameIsRegex = true, IsEnabled = true, RequiresAdmin = true, - Description = "希沃桌面 侧栏悬浮窗" - }; + Description = desc, + ParentType = InterceptType.ChangYanFloating + }); + } + private void AddChangYan4PptChildRule(InterceptType type, string titlePattern, string desc) + { + AddRule(new InterceptRule + { + Type = type, + ProcessName = "ifly.qzk.Toolbar.exe", + WindowTitlePattern = titlePattern, + ClassNamePattern = "^Qt5QWindowToolSaveBitsOwnDC$", + ClassNameIsRegex = true, + IsEnabled = true, + RequiresAdmin = true, + Description = desc, + ParentType = InterceptType.ChangYanPptFloating + }); } #endregion #region 公共方法 - /// - /// 启动拦截器 - /// public void Start(int scanIntervalMs = 5000) { if (_isRunning) return; - _isRunning = true; _scanTimer.Change(0, Math.Max(scanIntervalMs, 2000)); } - /// - /// 停止拦截器 - /// public void Stop() { if (!_isRunning) return; - _isRunning = false; _scanTimer.Change(Timeout.Infinite, Timeout.Infinite); - - // 恢复所有被拦截的窗口 RestoreAllWindows(); } - /// - /// 设置拦截规则 - /// public void SetInterceptRule(InterceptType type, bool enabled) { - if (_interceptRules.ContainsKey(type)) + if (!_interceptRules.ContainsKey(type)) return; + + var rule = _interceptRules[type]; + rule.IsEnabled = enabled; + + if (!enabled) RestoreWindowsByType(type); + + if (!enabled && rule.ChildTypes.Count > 0) { - var rule = _interceptRules[type]; - rule.IsEnabled = enabled; - - // 如果规则被禁用,恢复相关的被拦截窗口 - if (!enabled) - { - RestoreWindowsByType(type); - } - - // 如果是父规则被禁用,则禁用所有子规则 - if (!enabled && rule.ChildTypes.Count > 0) - { - foreach (var childType in rule.ChildTypes) + foreach (var childType in rule.ChildTypes) + if (_interceptRules.ContainsKey(childType)) { - if (_interceptRules.ContainsKey(childType)) - { - _interceptRules[childType].IsEnabled = false; - RestoreWindowsByType(childType); - } + _interceptRules[childType].IsEnabled = false; + RestoreWindowsByType(childType); } - } - // 如果是父规则被启用,则启用所有子规则 - else if (enabled && rule.ChildTypes.Count > 0) - { - foreach (var childType in rule.ChildTypes) - { - if (_interceptRules.ContainsKey(childType)) - { - _interceptRules[childType].IsEnabled = true; - } - } - } - // 如果是子规则被禁用,检查是否需要禁用父规则 - else if (!enabled && rule.ParentType.HasValue) - { - var parentRule = _interceptRules[rule.ParentType.Value]; - // 检查是否还有其他启用的子规则 - bool hasEnabledChildren = parentRule.ChildTypes.Any(childType => - _interceptRules.ContainsKey(childType) && _interceptRules[childType].IsEnabled); - - // 如果没有启用的子规则,则禁用父规则 - if (!hasEnabledChildren) - { - parentRule.IsEnabled = false; - } - } - // 如果是子规则被启用,则启用父规则 - else if (enabled && rule.ParentType.HasValue) - { - var parentRule = _interceptRules[rule.ParentType.Value]; - parentRule.IsEnabled = true; - } + } + else if (enabled && rule.ChildTypes.Count > 0) + { + foreach (var childType in rule.ChildTypes) + if (_interceptRules.ContainsKey(childType)) + _interceptRules[childType].IsEnabled = true; + } + else if (!enabled && rule.ParentType.HasValue) + { + var parentRule = _interceptRules[rule.ParentType.Value]; + bool hasEnabledChildren = parentRule.ChildTypes.Any(ct => + _interceptRules.ContainsKey(ct) && _interceptRules[ct].IsEnabled); + if (!hasEnabledChildren) + parentRule.IsEnabled = false; + } + else if (enabled && rule.ParentType.HasValue) + { + _interceptRules[rule.ParentType.Value].IsEnabled = true; } } - /// - /// 获取拦截规则 - /// public InterceptRule GetInterceptRule(InterceptType type) { return _interceptRules.ContainsKey(type) ? _interceptRules[type] : null; } - /// - /// 获取所有拦截规则 - /// public Dictionary GetAllRules() { return new Dictionary(_interceptRules); } - /// - /// 获取当前被拦截的窗口数量 - /// - public int GetInterceptedWindowsCount() - { - return _interceptedWindows.Count; - } + public int GetInterceptedWindowsCount() => _interceptedWindows.Count; - /// - /// 手动扫描一次 - /// - public void ScanOnce() - { - ScanForWindows(null); - } + public void ScanOnce() => ScanForWindows(null); - /// - /// 恢复所有被拦截的窗口 - /// public void RestoreAllWindows() { - var windowsToRestore = new List(_interceptedWindows.Keys); - var restoredCount = 0; - - foreach (var hWnd in windowsToRestore) - { - if (RestoreWindow(hWnd)) - { - restoredCount++; - } - } - + foreach (var hWnd in new List(_interceptedWindows.Keys)) + RestoreWindow(hWnd); } - /// - /// 恢复指定类型的被拦截窗口 - /// public void RestoreWindowsByType(InterceptType type) { - var windowsToRestore = new List(); - foreach (var kvp in _interceptedWindows) - { - if (kvp.Value == type) - { - windowsToRestore.Add(kvp.Key); - } - } - - var restoredCount = 0; - foreach (var hWnd in windowsToRestore) - { - if (RestoreWindow(hWnd)) - { - restoredCount++; - } - } - + foreach (var hWnd in _interceptedWindows.Where(kvp => kvp.Value == type).Select(kvp => kvp.Key).ToList()) + RestoreWindow(hWnd); } - /// - /// 恢复指定窗口 - /// public bool RestoreWindow(IntPtr hWnd) { if (!_interceptedWindows.ContainsKey(hWnd)) return false; @@ -878,31 +774,28 @@ namespace Ink_Canvas.Helpers if (IsWindow(hWnd)) { - // 使用多种方法确保窗口恢复显示 - ShowWindow(hWnd, SW_RESTORE); - ShowWindow(hWnd, SW_SHOW); - ShowWindow(hWnd, SW_SHOWNORMAL); - - // 将窗口置于前台并显示 - SetWindowPos(hWnd, IntPtr.Zero, 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW); - - // 强制将窗口带到前台 - BringWindowToTop(hWnd); - SetForegroundWindow(hWnd); + if (_movedWindowPositions.TryGetValue(hWnd, out var pos)) + { + SetWindowPos(hWnd, IntPtr.Zero, pos.x, pos.y, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + _movedWindowPositions.Remove(hWnd); + } + else + { + ShowWindow(hWnd, SW_SHOWNOACTIVATE); + } _interceptedWindows.Remove(hWnd); - WindowRestored?.Invoke(this, new WindowRestoredEventArgs { WindowHandle = hWnd, InterceptType = interceptType }); - return true; } _interceptedWindows.Remove(hWnd); + _movedWindowPositions.Remove(hWnd); return false; } @@ -910,24 +803,12 @@ namespace Ink_Canvas.Helpers #region 私有方法 - /// - /// 清理无效的窗口句柄 - /// private void CleanupInvalidWindows() { - var invalidWindows = new List(); - - foreach (var kvp in _interceptedWindows) - { - var hWnd = kvp.Key; - if (!IsWindow(hWnd)) - { - invalidWindows.Add(hWnd); - } - } - foreach (var hWnd in invalidWindows) + foreach (var hWnd in _interceptedWindows.Keys.Where(h => !IsWindow(h)).ToList()) { _interceptedWindows.Remove(hWnd); + _movedWindowPositions.Remove(hWnd); } } @@ -937,43 +818,38 @@ namespace Ink_Canvas.Helpers try { - // 简化的扫描逻辑 - var interceptedCount = 0; CleanupInvalidWindows(); - // 重置所有规则的发现状态 + var foundWindows = new List<(IntPtr hwnd, InterceptRule rule)>(); + + // Reset per-scan state foreach (var rule in _interceptRules.Values) + rule.foundHwnd = false; + + EnumWindows((hWnd, lParam) => { - if (rule.IsEnabled) + EnumChildWindows(hWnd, (childHwnd, _) => { - rule.foundHwnd = false; + CheckWindow(childHwnd, foundWindows); + return true; + }, IntPtr.Zero); + CheckWindow(hWnd, foundWindows); + return true; + }, IntPtr.Zero); + + var interceptedCount = 0; + foreach (var (hWnd, rule) in foundWindows) + { + bool shouldAct = !_interceptedWindows.ContainsKey(hWnd) || + (_interceptedWindows.ContainsKey(hWnd) && IsWindowVisible(hWnd)); + if (shouldAct) + { + ApplyIntercept(hWnd, rule); + interceptedCount++; } } - // 枚举所有窗口 - EnumWindows(EnumWindowsCallback, IntPtr.Zero); - - // 处理找到的窗口 - foreach (var rule in _interceptRules.Values) - { - if (rule.IsEnabled && rule.foundHwnd && rule.outHwnd != IntPtr.Zero) - { - bool shouldIntercept = !_interceptedWindows.ContainsKey(rule.outHwnd) || - (_interceptedWindows.ContainsKey(rule.outHwnd) && IsWindowVisible(rule.outHwnd)); - - if (shouldIntercept) - { - InterceptWindow(rule.outHwnd, rule); - interceptedCount++; - } - } - } - - // 更新统计 - if (interceptedCount == 0) - { - _consecutiveEmptyScans++; - } + if (interceptedCount == 0) _consecutiveEmptyScans++; else { _consecutiveEmptyScans = 0; @@ -987,67 +863,165 @@ namespace Ink_Canvas.Helpers } } + private void CheckWindow(IntPtr hWnd, List<(IntPtr, InterceptRule)> found) + { + if (!IsWindow(hWnd) || !IsWindowVisible(hWnd)) return; - private bool EnumWindowsCallback(IntPtr hWnd, IntPtr lParam) + foreach (var rule in _interceptRules.Values) + { + if (!rule.IsEnabled || rule.foundHwnd) continue; + if (MatchesRule(hWnd, rule)) + { + rule.outHwnd = hWnd; + rule.foundHwnd = true; + found.Add((hWnd, rule)); + } + } + } + + private bool MatchesRule(IntPtr hWnd, InterceptRule rule) { try { - // 递归枚举子窗口 - EnumChildWindows(hWnd, EnumWindowsCallback, lParam); - - // 基本检查 - if (!IsWindow(hWnd) || !IsWindowVisible(hWnd)) return true; - - // 检查每个启用的规则 - foreach (var rule in _interceptRules.Values) + // 检查类名 + if (!string.IsNullOrEmpty(rule.ClassNamePattern)) { - if (!rule.IsEnabled || rule.foundHwnd) continue; - - if (MatchesRulePrecise(hWnd, rule)) + var sb = new StringBuilder(512); + GetClassName(hWnd, sb, sb.Capacity); + var className = sb.ToString(); + if (rule.ClassNameIsRegex) { - rule.outHwnd = hWnd; - rule.foundHwnd = true; + if (!Regex.IsMatch(className, rule.ClassNamePattern)) return false; + } + else + { + if (!className.Contains(rule.ClassNamePattern)) return false; + } + } + + // 检查窗口标题 + if (!string.IsNullOrEmpty(rule.WindowTitlePattern)) + { + var sb = new StringBuilder(512); + GetWindowText(hWnd, sb, sb.Capacity); + var title = sb.ToString(); + if (rule.TitleIsRegex) + { + if (!Regex.IsMatch(title, rule.WindowTitlePattern)) return false; + } + else + { + if (!title.Contains(rule.WindowTitlePattern)) return false; + } + } + + // 检查进程名 + if (!string.IsNullOrEmpty(rule.ProcessName)) + { + GetWindowThreadProcessId(hWnd, out uint processId); + if (processId == 0) return false; + + var processName = GetProcessFileName(processId); + if (processName == null) return false; + + // ProcessName 可能是不含路径的文件名(如 "EasiNote.exe") + if (!processName.Equals(rule.ProcessName, StringComparison.OrdinalIgnoreCase)) + return false; + } + + // 检查窗口样式 + if (rule.HasWindowStyle) + { + var style = GetWindowLong(hWnd, GWL_STYLE); + if (rule.StyleMatchType == StyleMatchType.Exact) + { + if (style != rule.WindowStyle) return false; + } + else // Subset + { + if ((style & rule.WindowStyle) != rule.WindowStyle) return false; + } + } + + // 检查窗口尺寸 + if (rule.SizeMatchType != SizeMatchType.None) + { + RECT rect; + if (DwmGetWindowAttribute(hWnd, DWMWA_EXTENDED_FRAME_BOUNDS, out rect, Marshal.SizeOf(typeof(RECT))) != 0) + GetWindowRect(hWnd, out rect); + + int w = rect.Right - rect.Left; + int h = rect.Bottom - rect.Top; + if (w == 0 && h == 0) return false; + + const int tolerance = 2; + + switch (rule.SizeMatchType) + { + case SizeMatchType.FullScreen: + if (w != GetSystemMetrics(SM_CXSCREEN) || h != GetSystemMetrics(SM_CYSCREEN)) + return false; + break; + case SizeMatchType.FullHeight: + if (h != GetSystemMetrics(SM_CYSCREEN)) return false; + break; + case SizeMatchType.FullWidth: + if (w != GetSystemMetrics(SM_CXSCREEN)) return false; + break; + case SizeMatchType.Exact: + if (w != rule.WindowWidth || h != rule.WindowHeight) return false; + break; + case SizeMatchType.DPIScale: + { + var hdc = GetDC(IntPtr.Zero); + float scaleX = GetDeviceCaps(hdc, LOGPIXELSX) / 96.0f; + float scaleY = GetDeviceCaps(hdc, LOGPIXELSY) / 96.0f; + ReleaseDC(IntPtr.Zero, hdc); + if (Math.Abs(rule.WindowWidth * scaleX - w) > tolerance || + Math.Abs(rule.WindowHeight * scaleY - h) > tolerance) + return false; + break; + } + case SizeMatchType.Scale: + { + if (rule.WindowWidth == 0 || rule.WindowHeight == 0) return false; + float wr = (float)w / rule.WindowWidth; + float hr = (float)h / rule.WindowHeight; + if (Math.Abs(wr - hr) > 0.03f) return false; + break; + } } } return true; } - catch (Exception ex) + catch { - LogHelper.WriteLogToFile($"枚举窗口回调错误: {ex.Message}", LogHelper.LogType.Error); - return true; + return false; } } - private WindowInfo GetWindowInfo(IntPtr hWnd) + private string GetProcessFileName(uint processId) { 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 + var hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, processId); + if (hProcess == IntPtr.Zero) return null; + try { - Handle = hWnd, - ProcessId = processId, - ProcessName = process.ProcessName, - WindowTitle = titleBuilder.ToString(), - ClassName = classBuilder.ToString(), - Process = process - }; + var sb = new StringBuilder(1024); + uint size = (uint)sb.Capacity; + if (QueryFullProcessImageNameW(hProcess, 0, sb, ref size)) + { + var fullPath = sb.ToString(0, (int)size); + return System.IO.Path.GetFileName(fullPath); + } + return null; + } + finally + { + CloseHandle(hProcess); + } } catch { @@ -1055,123 +1029,40 @@ namespace Ink_Canvas.Helpers } } - /// - /// 精确匹配规则 - /// - private bool MatchesRulePrecise(IntPtr hWnd, InterceptRule rule) - { - try - { - // 检查类名 - if (!string.IsNullOrEmpty(rule.ClassNamePattern)) - { - var className = new StringBuilder(256); - GetClassName(hWnd, className, className.Capacity); - var classNameStr = className.ToString(); - - if (rule.ExactClassNameMatch) - { - if (!classNameStr.Equals(rule.ClassNamePattern, StringComparison.OrdinalIgnoreCase)) - return false; - } - else - { - if (!classNameStr.Contains(rule.ClassNamePattern)) - return false; - } - } - - // 检查窗口标题 - if (!string.IsNullOrEmpty(rule.WindowTitlePattern)) - { - var windowTitle = new StringBuilder(256); - GetWindowText(hWnd, windowTitle, windowTitle.Capacity); - var titleStr = windowTitle.ToString(); - - if (rule.ExactTitleMatch) - { - if (!titleStr.Equals(rule.WindowTitlePattern, StringComparison.OrdinalIgnoreCase)) - return false; - } - else - { - if (!titleStr.Contains(rule.WindowTitlePattern)) - return false; - } - } - - // 检查窗口样式 - if (rule.HasWindowStyle) - { - var style = GetWindowLong(hWnd, GWL_STYLE); - if (style != rule.WindowStyle) - return false; - } - - // 检查窗口尺寸 - if (rule.HasWindowSize) - { - var rect = new ForegroundWindowInfo.RECT(); - if (DwmGetWindowAttribute(hWnd, DWMWA_EXTENDED_FRAME_BOUNDS, out rect, Marshal.SizeOf(rect)) == 0) - { - var width = rect.Right - rect.Left; - var height = rect.Bottom - rect.Top; - - // 检查精确匹配 - if (rule.WindowWidth == width && rule.WindowHeight == height) - return true; - - // 检查缩放匹配 - var hdc = GetDC(IntPtr.Zero); - var horizontalDPI = GetDeviceCaps(hdc, LOGPIXELSX); - var verticalDPI = GetDeviceCaps(hdc, LOGPIXELSY); - ReleaseDC(IntPtr.Zero, hdc); - - var scale = (horizontalDPI + verticalDPI) / 2.0f / 96.0f; - var scaledWidth = (int)(rule.WindowWidth * scale); - var scaledHeight = (int)(rule.WindowHeight * scale); - - if (Math.Abs(scaledWidth - width) <= 1 && Math.Abs(scaledHeight - height) <= 1) - return true; - - return false; - } - } - - return true; - } - catch (Exception ex) - { - LogHelper.WriteLogToFile($"精确匹配规则时发生错误: {ex.Message}", LogHelper.LogType.Error); - return false; - } - } - - private bool MatchesRule(WindowInfo windowInfo, InterceptRule rule) - { - return MatchesRulePrecise(windowInfo.Handle, rule); - } - - private void InterceptWindow(IntPtr hWnd, InterceptRule rule) + private void ApplyIntercept(IntPtr hWnd, InterceptRule rule) { try { if (!IsWindow(hWnd) || !IsWindowVisible(hWnd)) { - if (_interceptedWindows.ContainsKey(hWnd)) - { - _interceptedWindows.Remove(hWnd); - } + _interceptedWindows.Remove(hWnd); return; } - // 直接隐藏窗口,不发送关闭消息 - ShowWindow(hWnd, SW_HIDE); + switch (rule.ActionType) + { + case InterceptActionType.Hide: + ShowWindow(hWnd, SW_HIDE); + _interceptedWindows[hWnd] = rule.Type; + break; - // 记录拦截的窗口 - _interceptedWindows[hWnd] = rule.Type; + case InterceptActionType.Close: + PostMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero); + break; + + case InterceptActionType.Move: + GetWindowRect(hWnd, out var rect); + int x = rect.Left, y = rect.Top; + if (x != -32000 || y != -32000) + { + _movedWindowPositions[hWnd] = (x, y); + SetWindowPos(hWnd, IntPtr.Zero, -32000, -32000, 0, 0, + SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + _interceptedWindows[hWnd] = rule.Type; + } + break; + } - // 触发事件 WindowIntercepted?.Invoke(this, new WindowInterceptedEventArgs { WindowHandle = hWnd, @@ -1179,7 +1070,6 @@ namespace Ink_Canvas.Helpers Rule = rule, WindowTitle = GetWindowTitle(hWnd) }); - } catch (Exception ex) { @@ -1191,69 +1081,11 @@ namespace Ink_Canvas.Helpers { try { - var titleBuilder = new StringBuilder(256); - GetWindowText(hWnd, titleBuilder, titleBuilder.Capacity); - return titleBuilder.ToString(); + var sb = new StringBuilder(256); + GetWindowText(hWnd, sb, sb.Capacity); + return sb.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; - - 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; } + catch { return "Unknown"; } } #endregion @@ -1281,13 +1113,9 @@ namespace Ink_Canvas.Helpers public void Dispose() { if (_disposed) return; - Stop(); _scanTimer?.Dispose(); - - // 恢复所有被拦截的窗口 RestoreAllWindows(); - _disposed = true; } diff --git a/Ink Canvas/MainWindow_cs/MW_FloatingWindowInterceptor.cs b/Ink Canvas/MainWindow_cs/MW_FloatingWindowInterceptor.cs index 486c61a0..d53d761a 100644 --- a/Ink Canvas/MainWindow_cs/MW_FloatingWindowInterceptor.cs +++ b/Ink Canvas/MainWindow_cs/MW_FloatingWindowInterceptor.cs @@ -169,6 +169,55 @@ namespace Ink_Canvas if (toggle != null) SetInterceptRule(FloatingWindowInterceptor.InterceptType.SeewoDesktopSideBarFloating, toggle.IsOn); } + private void ToggleSwitchSeewoIwbAssistantFloating_Toggled(object sender, RoutedEventArgs e) + { + if (!isLoaded) return; + var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch; + if (toggle != null) SetInterceptRule(FloatingWindowInterceptor.InterceptType.SeewoIwbAssistantFloating, toggle.IsOn); + } + + private void ToggleSwitchSeewoDesktopDrawingFloating_Toggled(object sender, RoutedEventArgs e) + { + if (!isLoaded) return; + var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch; + if (toggle != null) SetInterceptRule(FloatingWindowInterceptor.InterceptType.SeewoDesktopDrawingFloating, toggle.IsOn); + } + + private void ToggleSwitchYiouBoardFloating_Toggled(object sender, RoutedEventArgs e) + { + if (!isLoaded) return; + var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch; + if (toggle != null) SetInterceptRule(FloatingWindowInterceptor.InterceptType.YiouBoardFloating, toggle.IsOn); + } + + private void ToggleSwitchClassInXFloating_Toggled(object sender, RoutedEventArgs e) + { + if (!isLoaded) return; + var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch; + if (toggle != null) SetInterceptRule(FloatingWindowInterceptor.InterceptType.ClassInXFloating, toggle.IsOn); + } + + private void ToggleSwitchChangYan5Floating_Toggled(object sender, RoutedEventArgs e) + { + if (!isLoaded) return; + var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch; + if (toggle != null) SetInterceptRule(FloatingWindowInterceptor.InterceptType.ChangYan5Floating, toggle.IsOn); + } + + private void ToggleSwitchIclass30SidebarFloating_Toggled(object sender, RoutedEventArgs e) + { + if (!isLoaded) return; + var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch; + if (toggle != null) SetInterceptRule(FloatingWindowInterceptor.InterceptType.Iclass30SidebarFloating, toggle.IsOn); + } + + private void ToggleSwitchIclass30Floating_Toggled(object sender, RoutedEventArgs e) + { + if (!isLoaded) return; + var toggle = sender as iNKORE.UI.WPF.Modern.Controls.ToggleSwitch; + if (toggle != null) SetInterceptRule(FloatingWindowInterceptor.InterceptType.Iclass30Floating, toggle.IsOn); + } + public void SetInterceptRule(FloatingWindowInterceptor.InterceptType type, bool enabled) { try diff --git a/Ink Canvas/Resources/Settings.cs b/Ink Canvas/Resources/Settings.cs index 2c3ebed1..6743e3ae 100644 --- a/Ink Canvas/Resources/Settings.cs +++ b/Ink Canvas/Resources/Settings.cs @@ -627,6 +627,7 @@ namespace Ink_Canvas [JsonProperty("interceptRules")] public Dictionary InterceptRules { get; set; } = new Dictionary { + // 希沃系列 { "SeewoWhiteboard3Floating", false }, { "SeewoWhiteboard5Floating", false }, { "SeewoWhiteboard5CFloating", false }, @@ -634,8 +635,19 @@ namespace Ink_Canvas { "SeewoPincoDrawingFloating", false }, { "SeewoPincoBoardService", false }, { "SeewoPPTFloating", false }, + { "SeewoIwbAssistantFloating", false }, + { "SeewoDesktopSideBarFloating", false }, + { "SeewoDesktopDrawingFloating", false }, + { "SeewoDesktopAnnotationFloating", false }, + // 欧帝 + { "YiouBoardFloating", false }, + // AiClass { "AiClassFloating", false }, + // ClassIn X + { "ClassInXFloating", false }, + // 鸿合 { "HiteAnnotationFloating", false }, + // 畅言4.0 { "ChangYanFloating", false }, { "ChangYanBrushSettings", false }, { "ChangYanSwipeClear", false }, @@ -649,10 +661,14 @@ namespace Ink_Canvas { "ChangYanPptPageControl", false }, { "ChangYanPptGoBack", false }, { "ChangYanPptPreview", false }, + // 畅言5.0 + { "ChangYan5Floating", false }, + // 天喻 { "IntelligentClassFloating", false }, { "IntelligentClassPptFloating", false }, - { "SeewoDesktopAnnotationFloating", false }, - { "SeewoDesktopSideBarFloating", false } + // C30智能教学 + { "Iclass30SidebarFloating", false }, + { "Iclass30Floating", false }, }; }