Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 065d341320 |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "jiangchang-platform-kit"
|
name = "jiangchang-platform-kit"
|
||||||
version = "1.2.9"
|
version = "1.2.10"
|
||||||
description = "匠厂平台共享组件:Skill 实体 SDK + 桌面应用自动化 SDK + 桌面 UIA 暖树"
|
description = "匠厂平台共享组件:Skill 实体 SDK + 桌面应用自动化 SDK + 桌面 UIA 暖树"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# jiangchang_desktop_wake 验收(ensure 主路径,1.2.9+)
|
# jiangchang_desktop_wake 验收(ensure 主路径,1.2.10+)
|
||||||
|
|
||||||
正式对外路径是 **`ensure`**(内部:`activate` → `desktop-wake --mode n2-wake` → `probe`)。
|
正式对外路径是 **`ensure`**(内部:`activate` → `desktop-wake --mode n2-wake` → `probe`)。
|
||||||
`n2-wake`:**仅 OBJID_CLIENT** 走 DuiLib;不做进程内 FindAll、不同步 GETOBJECT、不 SPI 读屏;判暖只看进程外 probe。
|
`n2-wake`:**仅 OBJID_CLIENT** 走 DuiLib;不做进程内 FindAll、不同步 GETOBJECT、不 SPI 读屏;判暖只看进程外 probe。
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
| profile | 窗 | 说明 |
|
| profile | 窗 | 说明 |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `wecom` | `WeWorkWindow` | 主窗(原有) |
|
| `wecom` | `WeWorkWindow` | 主窗(原有) |
|
||||||
| `wecom_search` | `SearchResultWindow2` | 搜索浮层;**不对浮层 Maximize / 不注入**(1.2.9+ 仅软唤醒;注入会崩企微) |
|
| `wecom_search` | `SearchResultWindow2` | 搜索浮层;**不对浮层 Maximize**;n2-wake **子类化浮层本身**(多 HWND;1.2.10+) |
|
||||||
|
|
||||||
## 0. 编译(企微先完全退出,含托盘)
|
## 0. 编译(企微先完全退出,含托盘)
|
||||||
|
|
||||||
@@ -77,11 +77,11 @@ python -m jiangchang_desktop_wake probe --profile wecom
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## C. 搜索浮层 wecom_search(1.2.9+)
|
## C. 搜索浮层 wecom_search(1.2.10+)
|
||||||
|
|
||||||
1. **建议先** `ensure --profile wecom` 暖主窗(可与浮层分步)。
|
1. **建议先** `ensure --profile wecom` 暖主窗(可与浮层分步;验证多 HWND)。
|
||||||
2. 在搜索框输入联系人,**保持搜索结果浮层打开**(类名 `SearchResultWindow2`)。
|
2. 在搜索框输入联系人,**保持搜索结果浮层打开**(类名 `SearchResultWindow2`)。
|
||||||
3. 验收(**禁止**对浮层 n2-wake/注入;profile 仅 `wm_getobject` + `msaa`):
|
3. 验收:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
cd D:\OpenClaw\client-jiangchang\jiangchang-platform-kit
|
cd D:\OpenClaw\client-jiangchang\jiangchang-platform-kit
|
||||||
@@ -93,10 +93,10 @@ python -m jiangchang_desktop_wake probe --profile wecom_search
|
|||||||
|
|
||||||
| 期望 | 说明 |
|
| 期望 | 说明 |
|
||||||
|---|---|
|
|---|---|
|
||||||
| ensure **不**崩企微 | 故障模块不应再是 `wecom_wake.dll` |
|
| ensure 退出码 `0` | 浮层已暖 |
|
||||||
| `warm=true`(或 soft poke 后 findall≫2) | markers 含 `search_result` / `user_item` |
|
| `warm=true` | markers 含 `search_result` / `user_item`;findall 从 ~2 升到几十/上百 |
|
||||||
| 企微 UI | 浮层保持打开、**不** Maximize、不白屏 |
|
| `%TEMP%\jiangchang_desktop_wake_subclass.txt` | 含浮层 hwnd,`ok=1`(主窗已暖后再 ensure 浮层可 `slots>=2`) |
|
||||||
| 若手动对浮层跑 `desktop-wake … n2-wake` | exe/DLL 应走 `popup_soft_only` / `popup_nudge_only`,**不**子类化浮层 |
|
| 企微 UI | 浮层**不**被 Maximize、**不崩**、不无故关闭 |
|
||||||
| `WINDOW_NOT_FOUND` | 浮层未打开——先展开搜索结果再测 |
|
| `WINDOW_NOT_FOUND` | 浮层未打开——先展开搜索结果再测 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ print(rs.warm, rs.probe_after.findall_count, rs.probe_after.markers_found)
|
|||||||
| profile | 窗 | 说明 |
|
| profile | 窗 | 说明 |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `wecom` | `WeWorkWindow` | 主窗;markers=`con_op`,`searchedit` |
|
| `wecom` | `WeWorkWindow` | 主窗;markers=`con_op`,`searchedit` |
|
||||||
| `wecom_search` | `SearchResultWindow2` | 搜索浮层;markers=`search_result`,`user_item`;**禁止注入/n2-wake**(1.2.9+ 仅进程外软唤醒) |
|
| `wecom_search` | `SearchResultWindow2` | 搜索浮层;markers=`search_result`,`user_item`;不 Maximize;**n2-wake 对浮层子类化**(1.2.10+) |
|
||||||
|
|
||||||
| CLI | 退出码 |
|
| CLI | 退出码 |
|
||||||
|---|---|
|
|---|---|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -169,21 +169,8 @@ namespace JiangchangDesktopWake
|
|||||||
if (!doWakeProvider)
|
if (!doWakeProvider)
|
||||||
EnableScreenReaderFlag();
|
EnableScreenReaderFlag();
|
||||||
|
|
||||||
// 1.2.9:SearchResultWindow2 禁止 n2 注入(wecom_wake.dll 子类化/DuiLib 会崩企微)
|
// 1.2.10:允许对 SearchResultWindow2 n2-wake(本机验证可暖且不崩);
|
||||||
if (doN2 && IsSearchResultPopup(hwnd))
|
// 仍靠 ShouldMaximize=false,禁止 Maximize 打掉浮层。
|
||||||
{
|
|
||||||
SendGetObject(hwnd);
|
|
||||||
TryMsaa(hwnd);
|
|
||||||
bool responsivePopup = CheckUiResponsive(hwnd);
|
|
||||||
method = "popup_soft_only";
|
|
||||||
WriteJson(
|
|
||||||
responsivePopup, false, responsivePopup,
|
|
||||||
profile, hwnd, 0, 0, new string[0],
|
|
||||||
"search_popup_no_inject|soft_getobject_msaa",
|
|
||||||
mode, method);
|
|
||||||
return responsivePopup ? 0 : 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (doN2)
|
if (doN2)
|
||||||
{
|
{
|
||||||
string n2Detail;
|
string n2Detail;
|
||||||
@@ -597,17 +584,6 @@ namespace JiangchangDesktopWake
|
|||||||
return new IntPtr(Convert.ToInt64(s, 16));
|
return new IntPtr(Convert.ToInt64(s, 16));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsSearchResultPopup(IntPtr hwnd)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var sb = new StringBuilder(256);
|
|
||||||
if (GetClassName(hwnd, sb, sb.Capacity) <= 0) return false;
|
|
||||||
return string.Equals(sb.ToString(), "SearchResultWindow2", StringComparison.Ordinal);
|
|
||||||
}
|
|
||||||
catch { return false; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 主窗可 Maximize;SearchResultWindow2 等浮层禁止 Maximize(会撑爆/关掉浮层)。
|
/// 主窗可 Maximize;SearchResultWindow2 等浮层禁止 Maximize(会撑爆/关掉浮层)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
* - 不做进程内 UIAutomation FindAll;不做同步 SendMessage(GETOBJECT)
|
* - 不做进程内 UIAutomation FindAll;不做同步 SendMessage(GETOBJECT)
|
||||||
* - 重入守卫;判暖交给进程外 probe
|
* - 重入守卫;判暖交给进程外 probe
|
||||||
* 1.2.8:支持多 HWND 子类化(主窗 + SearchResultWindow2),禁止「全局只装一次」。
|
* 1.2.8:支持多 HWND 子类化(主窗 + SearchResultWindow2),禁止「全局只装一次」。
|
||||||
* 1.2.9:SearchResultWindow2 禁止子类化/DuiLib thiscall(会崩企微);浮层只异步 nudge。
|
* 1.2.10:恢复对 SearchResultWindow2 子类化暖树(本机验证 2→122 且不崩);仍禁止 Maximize(exe 侧)。
|
||||||
*/
|
*/
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
@@ -140,14 +140,6 @@ static void NudgeWinEvents(HWND hwnd)
|
|||||||
PostMessageW(hwnd, WM_NULL, 0, 0);
|
PostMessageW(hwnd, WM_NULL, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsSearchResultPopup(HWND hwnd)
|
|
||||||
{
|
|
||||||
wchar_t cls[256];
|
|
||||||
if (!hwnd || !GetClassNameW(hwnd, cls, 256))
|
|
||||||
return false;
|
|
||||||
return _wcsicmp(cls, L"SearchResultWindow2") == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool ResolveDuiLib()
|
static bool ResolveDuiLib()
|
||||||
{
|
{
|
||||||
if (g_fnGetAccessible && g_fnOnGetObject) return true;
|
if (g_fnGetAccessible && g_fnOnGetObject) return true;
|
||||||
@@ -331,19 +323,6 @@ static void DoSafeWake(HWND hwnd)
|
|||||||
int subclass_ok = 0;
|
int subclass_ok = 0;
|
||||||
int dui_ok = 0;
|
int dui_ok = 0;
|
||||||
|
|
||||||
/* 浮层:DuiLib thiscall / 换 WndProc 在宿主环境会直接崩企微(fault=wecom_wake.dll) */
|
|
||||||
if (IsSearchResultPopup(hwnd)) {
|
|
||||||
NudgeWinEvents(hwnd);
|
|
||||||
NudgeWinEvents(hwnd);
|
|
||||||
sprintf_s(line,
|
|
||||||
"hwnd=%p sta_tid=%lu popup_nudge_only=1 subclass=0 dui=0 "
|
|
||||||
"dui_hits=%ld getobject_hits=%ld done=1\r\n",
|
|
||||||
hwnd, GetCurrentThreadId(),
|
|
||||||
(long)g_duiHits, (long)g_getobjectHits);
|
|
||||||
WriteMarker("jiangchang_desktop_wake_provider.txt", line);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResolveDuiLib();
|
ResolveDuiLib();
|
||||||
dui_ok = (g_fnGetAccessible || g_fnOnGetObject) ? 1 : 0;
|
dui_ok = (g_fnGetAccessible || g_fnOnGetObject) ? 1 : 0;
|
||||||
subclass_ok = InstallDuiSubclass(hwnd) ? 1 : 0;
|
subclass_ok = InstallDuiSubclass(hwnd) ? 1 : 0;
|
||||||
|
|||||||
@@ -44,12 +44,14 @@ _BUILTIN = {
|
|||||||
},
|
},
|
||||||
"wecom_search": {
|
"wecom_search": {
|
||||||
"id": "wecom_search",
|
"id": "wecom_search",
|
||||||
"description": "企业微信搜索结果浮层 SearchResultWindow2;仅进程外软唤醒(禁止注入,1.2.9+)",
|
"description": "企业微信搜索结果浮层 SearchResultWindow2;n2-wake 对浮层子类化(1.2.10+),禁止 Maximize",
|
||||||
"window_class": "SearchResultWindow2",
|
"window_class": "SearchResultWindow2",
|
||||||
"warm_min_findall": 20,
|
"warm_min_findall": 20,
|
||||||
"warm_markers": ["search_result", "user_item"],
|
"warm_markers": ["search_result", "user_item"],
|
||||||
"activate_maximize": False,
|
"activate_maximize": False,
|
||||||
"wake_strategies": ["wm_getobject", "msaa_from_window"],
|
"wake_strategies": ["activate", "native_helper"],
|
||||||
|
"native_helper": "desktop-wake.exe",
|
||||||
|
"native_args": ["wecom", "0x{hwnd_hex}", "--mode", "n2-wake"],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
{
|
{
|
||||||
"id": "wecom_search",
|
"id": "wecom_search",
|
||||||
"description": "企业微信搜索结果浮层 SearchResultWindow2;仅进程外软唤醒(禁止 n2-wake/注入,1.2.9+)",
|
"description": "企业微信搜索结果浮层 SearchResultWindow2;n2-wake 对浮层子类化暖树(1.2.10+),禁止 Maximize",
|
||||||
"window_class": "SearchResultWindow2",
|
"window_class": "SearchResultWindow2",
|
||||||
"warm_min_findall": 20,
|
"warm_min_findall": 20,
|
||||||
"warm_markers": ["search_result", "user_item"],
|
"warm_markers": ["search_result", "user_item"],
|
||||||
"activate_maximize": false,
|
"activate_maximize": false,
|
||||||
"wake_strategies": [
|
"wake_strategies": [
|
||||||
"wm_getobject",
|
"activate",
|
||||||
"msaa_from_window"
|
"native_helper"
|
||||||
]
|
],
|
||||||
|
"native_helper": "desktop-wake.exe",
|
||||||
|
"native_args": ["wecom", "0x{hwnd_hex}", "--mode", "n2-wake"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class Profile:
|
|||||||
# 浮层窗(如搜索结果)禁止 Maximize,避免打乱搜索态
|
# 浮层窗(如搜索结果)禁止 Maximize,避免打乱搜索态
|
||||||
activate_maximize: bool = True
|
activate_maximize: bool = True
|
||||||
# native_helper / n2-wake 目标窗 class;空则用 probe 窗本身。
|
# native_helper / n2-wake 目标窗 class;空则用 probe 窗本身。
|
||||||
# wecom_search 留空;1.2.9+ 浮层禁止 native 注入,勿再设为浮层自身。
|
# 1.2.10+ wecom_search 留空(对浮层自身 n2-wake);勿改注主窗。
|
||||||
native_hwnd_class: Optional[str] = None
|
native_hwnd_class: Optional[str] = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -34,11 +34,10 @@ class ProfileTests(unittest.TestCase):
|
|||||||
self.assertIn("search_result", p.warm_markers)
|
self.assertIn("search_result", p.warm_markers)
|
||||||
self.assertIn("user_item", p.warm_markers)
|
self.assertIn("user_item", p.warm_markers)
|
||||||
self.assertFalse(p.activate_maximize)
|
self.assertFalse(p.activate_maximize)
|
||||||
# 1.2.9+:浮层禁止 n2-wake/注入(会崩企微);仅进程外软唤醒
|
# 1.2.10+:对浮层本身 n2-wake(多窗子类化);禁止 Maximize;不改注主窗
|
||||||
self.assertFalse((p.native_hwnd_class or "").strip())
|
self.assertFalse((p.native_hwnd_class or "").strip())
|
||||||
self.assertNotIn("native_helper", p.wake_strategies)
|
self.assertIn("native_helper", p.wake_strategies)
|
||||||
self.assertIn("wm_getobject", p.wake_strategies)
|
self.assertIn("n2-wake", p.native_args or [])
|
||||||
self.assertIn("msaa_from_window", p.wake_strategies)
|
|
||||||
|
|
||||||
def test_resolve_native_hwnd_fallback_without_class(self):
|
def test_resolve_native_hwnd_fallback_without_class(self):
|
||||||
from jiangchang_desktop_wake.wake import resolve_native_hwnd
|
from jiangchang_desktop_wake.wake import resolve_native_hwnd
|
||||||
|
|||||||
Reference in New Issue
Block a user