From 17ac1b4e1108d073938004fd0375bfc16592f6f0 Mon Sep 17 00:00:00 2001 From: chendelian <116870791@qq.com> Date: Sun, 2 Aug 2026 21:10:15 +0800 Subject: [PATCH] fix(desktop-wake): multi-hwnd subclass + no maximize on search popup (1.2.8) SearchResultWindow2 can be warmed after main is already warm; desktop-wake no longer maximizes the popup. Co-authored-by: Cursor --- pyproject.toml | 2 +- src/jiangchang_desktop_wake/ACCEPTANCE.md | 14 ++-- src/jiangchang_desktop_wake/README.md | 2 +- .../native/desktop-wake.exe | Bin 18432 -> 18944 bytes .../native/wecom_wake.dll | Bin 118272 -> 118784 bytes .../native_src/DesktopWake/Program.cs | 26 ++++++- .../native_src/N2/payload/wecom_wake.cpp | 66 +++++++++++++++--- src/jiangchang_desktop_wake/profiles.py | 3 +- .../profiles/wecom_search.json | 3 +- src/jiangchang_desktop_wake/types.py | 4 +- tests/desktop_wake/test_profiles_and_types.py | 3 +- 11 files changed, 96 insertions(+), 27 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2da8195..be12128 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "jiangchang-platform-kit" -version = "1.2.7" +version = "1.2.8" description = "匠厂平台共享组件:Skill 实体 SDK + 桌面应用自动化 SDK + 桌面 UIA 暖树" readme = "README.md" requires-python = ">=3.10" diff --git a/src/jiangchang_desktop_wake/ACCEPTANCE.md b/src/jiangchang_desktop_wake/ACCEPTANCE.md index 4386f9a..3f0ceea 100644 --- a/src/jiangchang_desktop_wake/ACCEPTANCE.md +++ b/src/jiangchang_desktop_wake/ACCEPTANCE.md @@ -1,4 +1,4 @@ -# jiangchang_desktop_wake 验收(ensure 主路径,1.2.7+) +# jiangchang_desktop_wake 验收(ensure 主路径,1.2.8+) 正式对外路径是 **`ensure`**(内部:`activate` → `desktop-wake --mode n2-wake` → `probe`)。 `n2-wake`:**仅 OBJID_CLIENT** 走 DuiLib;不做进程内 FindAll、不同步 GETOBJECT、不 SPI 读屏;判暖只看进程外 probe。 @@ -7,7 +7,7 @@ | profile | 窗 | 说明 | |---|---|---| | `wecom` | `WeWorkWindow` | 主窗(原有) | -| `wecom_search` | `SearchResultWindow2` | 搜索浮层;不 Maximize;**n2-wake 注入同 PID 主窗**(`native_hwnd_class=WeWorkWindow`),probe 仍对浮层 | +| `wecom_search` | `SearchResultWindow2` | 搜索浮层;**不对浮层 Maximize**;n2-wake **子类化浮层本身**(DLL 支持多窗;主窗已暖后仍可再装浮层) | ## 0. 编译(企微先完全退出,含托盘) @@ -77,10 +77,11 @@ python -m jiangchang_desktop_wake probe --profile wecom --- -## C. 搜索浮层 wecom_search(1.2.7+) +## C. 搜索浮层 wecom_search(1.2.8+) -1. 打开企微主窗并登录;在搜索框输入联系人,**保持搜索结果浮层打开**(类名 `SearchResultWindow2`)。 -2. 验收: +1. **建议先** `ensure --profile wecom` 暖主窗(可与浮层分步)。 +2. 在搜索框输入联系人,**保持搜索结果浮层打开**(类名 `SearchResultWindow2`)。 +3. 验收: ```powershell cd D:\OpenClaw\client-jiangchang\jiangchang-platform-kit @@ -94,7 +95,8 @@ python -m jiangchang_desktop_wake probe --profile wecom_search |---|---| | ensure 退出码 `0` | 浮层已暖 | | `warm=true` | markers 含 `search_result` / `user_item`(或 findall 足够) | -| 企微 UI | **不白屏**、无红叹号错误页(1.2.6 对浮层 hwnd 注入曾导致) | +| `%TEMP%\jiangchang_desktop_wake_subclass.txt` | 含浮层 hwnd,`ok=1`(主窗已暖后再 ensure 浮层也会再写一条) | +| 企微 UI | 浮层**不**被 Maximize 撑满、**不白屏**、不无故关闭 | | `WINDOW_NOT_FOUND` | 浮层未打开——先展开搜索结果再测 | --- diff --git a/src/jiangchang_desktop_wake/README.md b/src/jiangchang_desktop_wake/README.md index d07b159..6e5c9d0 100644 --- a/src/jiangchang_desktop_wake/README.md +++ b/src/jiangchang_desktop_wake/README.md @@ -42,7 +42,7 @@ print(rs.warm, rs.probe_after.findall_count, rs.probe_after.markers_found) | profile | 窗 | 说明 | |---|---|---| | `wecom` | `WeWorkWindow` | 主窗;markers=`con_op`,`searchedit` | -| `wecom_search` | `SearchResultWindow2` | 搜索浮层;markers=`search_result`,`user_item`;不 Maximize;**n2-wake 注入同 PID 主窗**(1.2.7+) | +| `wecom_search` | `SearchResultWindow2` | 搜索浮层;markers=`search_result`,`user_item`;不 Maximize;**n2-wake 对浮层子类化**(1.2.8+ 支持主窗+浮层多窗) | | CLI | 退出码 | |---|---| diff --git a/src/jiangchang_desktop_wake/native/desktop-wake.exe b/src/jiangchang_desktop_wake/native/desktop-wake.exe index 591b3051748e8d9987fdeac4dfcdaa53c98b0b96..97545974ca560b96ccc2ded10595eefb83058dd5 100644 GIT binary patch delta 5649 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 qqUE&~ZML>tTdi%zUM$h^I!h4Fx0`k4x + /// 主窗可 Maximize;SearchResultWindow2 等浮层禁止 Maximize(会撑爆/关掉浮层)。 + /// + private static bool ShouldMaximize(IntPtr hwnd) + { + try + { + var sb = new StringBuilder(256); + if (GetClassName(hwnd, sb, sb.Capacity) <= 0) return true; + string cls = sb.ToString(); + if (string.Equals(cls, "SearchResultWindow2", StringComparison.Ordinal)) + return false; + if (cls.IndexOf("Shadow", StringComparison.OrdinalIgnoreCase) >= 0) + return false; + } + catch { } + return true; + } + private static void Activate(IntPtr hwnd) { try { if (IsIconic(hwnd)) ShowWindow(hwnd, SW_RESTORE); - else ShowWindow(hwnd, SW_MAXIMIZE); + else if (ShouldMaximize(hwnd)) ShowWindow(hwnd, SW_MAXIMIZE); + else ShowWindow(hwnd, SW_SHOW); SetForegroundWindow(hwnd); Thread.Sleep(250); } diff --git a/src/jiangchang_desktop_wake/native_src/N2/payload/wecom_wake.cpp b/src/jiangchang_desktop_wake/native_src/N2/payload/wecom_wake.cpp index 3178c09..12f3ae3 100644 --- a/src/jiangchang_desktop_wake/native_src/N2/payload/wecom_wake.cpp +++ b/src/jiangchang_desktop_wake/native_src/N2/payload/wecom_wake.cpp @@ -11,6 +11,7 @@ * - UiaRootObjectId 等原样交给旧 WndProc(禁止用 MSAA Lresult 冒充 UIA root) * - 不做进程内 UIAutomation FindAll;不做同步 SendMessage(GETOBJECT) * - 重入守卫;判暖交给进程外 probe + * 1.2.8:支持多 HWND 子类化(主窗 + SearchResultWindow2),禁止「全局只装一次」。 */ #define WIN32_LEAN_AND_MEAN #include @@ -40,16 +41,25 @@ #define WM_JC_WAKE (WM_APP + 0x51) #define WM_JC_QUIT (WM_APP + 0x52) #define JC_SUBCLASS_ID 0x4A435701u +/* 主窗 + 搜索浮层等可能各装一次;禁止全局「只装一次」漏掉 SearchResultWindow2 */ +#define JC_MAX_SUBCLASS 8 static HMODULE g_self = NULL; static HWND g_hwnd = NULL; static WNDPROC g_oldProc = NULL; -static WNDPROC g_duiOldProc = NULL; static volatile LONG g_getobjectHits = 0; static volatile LONG g_hookHits = 0; static volatile LONG g_duiHits = 0; static volatile LONG g_reenterSkip = 0; -static volatile LONG g_subclassInstalled = 0; + +typedef struct JcSubEntry { + HWND hwnd; + WNDPROC oldProc; /* SetWindowLongPtr 路径;SetWindowSubclass 时为 NULL */ + int useSubclassApi; +} JcSubEntry; + +static JcSubEntry g_subs[JC_MAX_SUBCLASS]; +static volatile LONG g_subCount = 0; static HANDLE g_staThread = NULL; static HANDLE g_staReady = NULL; @@ -209,6 +219,16 @@ static LRESULT HandleGetObject( return r; } +static JcSubEntry* FindSubEntry(HWND hwnd) +{ + LONG n = g_subCount; + for (LONG i = 0; i < n && i < JC_MAX_SUBCLASS; i++) { + if (g_subs[i].hwnd == hwnd) + return &g_subs[i]; + } + return NULL; +} + static LRESULT CALLBACK DuiSubclassProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) @@ -222,27 +242,46 @@ static LRESULT CALLBACK DuiSubclassProc( static LRESULT CALLBACK DuiWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + JcSubEntry* e = FindSubEntry(hwnd); + WNDPROC old = e ? e->oldProc : NULL; if (msg == WM_GETOBJECT) - return HandleGetObject(hwnd, wParam, lParam, g_duiOldProc, false); - if (g_duiOldProc) - return CallWindowProcW(g_duiOldProc, hwnd, msg, wParam, lParam); + return HandleGetObject(hwnd, wParam, lParam, old, false); + if (old) + return CallWindowProcW(old, hwnd, msg, wParam, lParam); return DefWindowProcW(hwnd, msg, wParam, lParam); } static bool InstallDuiSubclass(HWND hwnd) { if (!hwnd || !IsWindow(hwnd)) return false; - if (InterlockedCompareExchange(&g_subclassInstalled, 1, 0) != 0) + if (FindSubEntry(hwnd)) return true; + if (g_subCount >= JC_MAX_SUBCLASS) { + WriteMarker("jiangchang_desktop_wake_subclass.txt", + "err=subclass_slots_full\r\n"); + return false; + } ResolveDuiLib(); void* userdata = (void*)GetWindowLongPtrW(hwnd, GWLP_USERDATA); + /* 先占槽再换 WndProc,避免 GETOBJECT 抢在 Remember 之前 */ + LONG idx = InterlockedIncrement(&g_subCount) - 1; + if (idx < 0 || idx >= JC_MAX_SUBCLASS) { + InterlockedDecrement(&g_subCount); + WriteMarker("jiangchang_desktop_wake_subclass.txt", + "err=subclass_slots_full\r\n"); + return false; + } + g_subs[idx].hwnd = hwnd; + g_subs[idx].oldProc = NULL; + g_subs[idx].useSubclassApi = 0; + SetLastError(0); - g_duiOldProc = (WNDPROC)SetWindowLongPtrW( + WNDPROC oldProc = (WNDPROC)SetWindowLongPtrW( hwnd, GWLP_WNDPROC, (LONG_PTR)DuiWndProc); DWORD gle = GetLastError(); - BOOL ok = (g_duiOldProc != NULL); + BOOL ok = (oldProc != NULL); const char* method = "SetWindowLongPtr"; if (!ok) { @@ -255,17 +294,22 @@ static bool InstallDuiSubclass(HWND hwnd) ok = SetWindowSubclass(hwnd, DuiSubclassProc, JC_SUBCLASS_ID, 0); gle = GetLastError(); method = "SetWindowSubclass"; + g_subs[idx].useSubclassApi = 1; + oldProc = NULL; + } else { + g_subs[idx].oldProc = oldProc; } char line[512]; sprintf_s(line, "hwnd=%p ok=%d gle=%lu method=%s userdata=%p " - "duilib=%p getacc=%p onget=%p\r\n", + "duilib=%p getacc=%p onget=%p slots=%ld\r\n", hwnd, (int)ok, gle, method, userdata, - g_duilib, g_fnGetAccessible, g_fnOnGetObject); + g_duilib, g_fnGetAccessible, g_fnOnGetObject, (long)g_subCount); WriteMarker("jiangchang_desktop_wake_subclass.txt", line); if (!ok) { - InterlockedExchange(&g_subclassInstalled, 0); + g_subs[idx].hwnd = NULL; + InterlockedDecrement(&g_subCount); return false; } return true; diff --git a/src/jiangchang_desktop_wake/profiles.py b/src/jiangchang_desktop_wake/profiles.py index b2668b5..dd4144c 100644 --- a/src/jiangchang_desktop_wake/profiles.py +++ b/src/jiangchang_desktop_wake/profiles.py @@ -44,12 +44,11 @@ _BUILTIN = { }, "wecom_search": { "id": "wecom_search", - "description": "企业微信搜索结果浮层 SearchResultWindow2(n2-wake 注入主窗)", + "description": "企业微信搜索结果浮层 SearchResultWindow2(n2-wake 对浮层子类化,1.2.8+)", "window_class": "SearchResultWindow2", "warm_min_findall": 20, "warm_markers": ["search_result", "user_item"], "activate_maximize": False, - "native_hwnd_class": "WeWorkWindow", "wake_strategies": ["activate", "native_helper"], "native_helper": "desktop-wake.exe", "native_args": ["wecom", "0x{hwnd_hex}", "--mode", "n2-wake"], diff --git a/src/jiangchang_desktop_wake/profiles/wecom_search.json b/src/jiangchang_desktop_wake/profiles/wecom_search.json index 67442e4..35dd482 100644 --- a/src/jiangchang_desktop_wake/profiles/wecom_search.json +++ b/src/jiangchang_desktop_wake/profiles/wecom_search.json @@ -1,11 +1,10 @@ { "id": "wecom_search", - "description": "企业微信搜索结果浮层 SearchResultWindow2(联系人列表 user_item);n2-wake 注入同 PID 主窗 WeWorkWindow,避免对浮层 hwnd 注入导致白屏", + "description": "企业微信搜索结果浮层 SearchResultWindow2(联系人列表 user_item);n2-wake 对浮层本身子类化(1.2.8+ 支持多窗),禁止 Maximize", "window_class": "SearchResultWindow2", "warm_min_findall": 20, "warm_markers": ["search_result", "user_item"], "activate_maximize": false, - "native_hwnd_class": "WeWorkWindow", "wake_strategies": [ "activate", "native_helper" diff --git a/src/jiangchang_desktop_wake/types.py b/src/jiangchang_desktop_wake/types.py index 0b66322..3b2b306 100644 --- a/src/jiangchang_desktop_wake/types.py +++ b/src/jiangchang_desktop_wake/types.py @@ -29,8 +29,8 @@ class Profile: native_args: list[str] = field(default_factory=list) # 浮层窗(如搜索结果)禁止 Maximize,避免打乱搜索态 activate_maximize: bool = True - # native_helper / n2-wake 注入目标窗 class;空则用 probe 窗本身。 - # 搜索浮层应对主窗 WeWorkWindow 注入,避免对 SearchResultWindow2 注入导致白屏。 + # native_helper / n2-wake 目标窗 class;空则用 probe 窗本身。 + # 1.2.8+ wecom_search 留空(对浮层子类化);若需改注其它窗可再设。 native_hwnd_class: Optional[str] = None @classmethod diff --git a/tests/desktop_wake/test_profiles_and_types.py b/tests/desktop_wake/test_profiles_and_types.py index cee9e4c..794a09d 100644 --- a/tests/desktop_wake/test_profiles_and_types.py +++ b/tests/desktop_wake/test_profiles_and_types.py @@ -34,7 +34,8 @@ class ProfileTests(unittest.TestCase): self.assertIn("search_result", p.warm_markers) self.assertIn("user_item", p.warm_markers) self.assertFalse(p.activate_maximize) - self.assertEqual(p.native_hwnd_class, "WeWorkWindow") + # 1.2.8+:对浮层本身 n2-wake(多窗子类化);不再改注主窗 + self.assertFalse((p.native_hwnd_class or "").strip()) self.assertIn("native_helper", p.wake_strategies) self.assertIn("n2-wake", p.native_args or [])