electron-app-stuck-spinner-triage
Skill2 starsChanged 12 days agoMITView on GitHub
定位 Windows 桌面客户端(尤其 Electron 老客户端、外设配套软件)界面无限转圈且不报错——先按进程有无 ESTABLISHED 对外 TCP 一刀切开云同步与设备握手两类原因,再用界面上的逐字中文串反查遮罩的置位与清除条件与超时兜底,然后主动发只读帧自证发得出去收不回来,最后核对设备主注册 PID 与实际在俗 PID。用于某客户端一直转圈、重启重插换 USB 口都无效、手柄或 2.4G 接收器配置同步失败。HID 收发见 hid-device-protocol-probe,包内代码取证见 electron-asar-code-forensics。
In this repository131 other files it gives its agents.
- agent-client-network-triageSkill
- architecture-designerSkill
- async-python-patternsSkill
- audit-ci-cost-leversSkill
- awesome-design-mdSkill
- byok-model-retrySkill
- cloudbase-wechat-integrationSkill
- concurrent-edit-attribution-checkSkill
- continuation-state-auditSkill
- counterexample-driven-plan-reviewSkill
- deliver-prdSkill
- desktop-update-push-triageSkill
- diagnose-github-actions-quotaSkill
- diagnose-windows-webview2-blank-windowSkill
- disk-junction-migrationSkill
- distinctive-design-systemsSkill
- dsh-session-format-debugSkill
- electron-asar-code-forensicsSkill
- electron-tray-icon-diagnosisSkill
- encrypted-office-visual-inspectionSkill
- frontend-design-ui-uxSkill
- frontend-developmentSkill
- game-designer-toolkitSkill
- game-numeric-designSkill
- github-acceleratorSkill
- global-config-smoke-runnerSkill
- goal-driven-executionSkill
- hid-device-protocol-probeSkill
- i-have-adhdSkill
- injected-context-slimmingSkill
- java-architectSkill
- java-qualitySkill
- kafka-best-practicesSkill
- large-sqlite-text-extractionSkill
- local-api-contractsSkill
- local-browser-e2eSkill
- local-db-migrationsSkill
- local-frontend-a11ySkill
- local-performance-evidenceSkill
- local-reverse-proxy-triageSkill
- local-rust-engineeringSkill
- local-spring-mybatisSkill
- mechanize-rule-into-hook-guardSkill
- memory-bank-divergence-repairSkill
- memory-entry-updateSkill
- merge-inbound-qoder-packageSkill
- minigame-dev-proSkill
- miniprogram-developmentSkill
- mongodb-atlas-stream-processingSkill
- mongodb-connectionSkill
- mongodb-mcp-setupSkill
- mongodb-natural-language-queryingSkill
- mongodb-query-optimizerSkill
- mongodb-schema-designSkill
- mongodb-search-and-aiSkill
- mysqlSkill
- nextjs-app-router-patternsSkill
- nginx-config-builderSkill
- nodejs-backend-patternsSkill
- oauth-login-failure-triageSkill
- The first 60 shown.
The file
Files
Electron 客户端无限转圈定位
Overview
针对「界面一直转圈、不报错、重启重插都无效」的老桌面客户端(Electron 1.x + 原生 .node HID 层那一类),用三个能失败的判据把问题定到唯一一层:网络、遮罩逻辑、还是设备接收路径。本技能只管分诊顺序与判读口径,具体收发/取证机制交给下面两个姊妹技能。
三个必答判据(顺序不能颠倒)
| # | 问题 | 判据 | 分支 |
| 1 | 客户端在等网络吗 | 这些 PID 有无 ESTABLISHED 对外 TCP | 零对外 TCP 只能排除"已建立的长连接",不能排除 UDP/QUIC、DNS、本地代理或瞬时失败;此时网络方向降权但不下结论,继续用判据 2/3 与抓包/DNS 证据区分 |
| 2 | 遮罩靠什么清除 | grep 界面逐字中文串 → i18n key → 置位/清除条件 | 清除条件依赖设备回包且无超时兜底 ⇒ 必然无限转圈、连报错都不给 |
| 3 | 客户端收不到还是设备不回 | 自己发一帧客户端正在轮询的只读请求 | 设备立刻正确应答 ⇒ 传输/固件都好,缺陷在客户端接收路径 |
先按「数据同步 = 云同步」去查网络与证书是最常见的白跑点:判据 1 没出读数之前,不要在任何网络方向上加投入。
1. 现场采集(只读)
Get-Process -Name <exe>拿 PID 树与创建时间;同时Get-NetTCPConnection -OwningProcess <pid>收集对外连接(判据 1)。ExecutablePath为空 ⇒ 进程是提权运行的,后续读它的模块/命令行必然err=5 (Access denied),别把「读不到」当成「没加载」。- 日志常有两处:安装目录
bin\<厂商>\Log\与%APPDATA%\<厂商>\(Cookies / Local Storage / Preferences 能佐证它真走过登录)。 - 看「它今天到底写了哪些文件」(按 mtime 排序)通常比读混淆代码更快定位卡点。
2. 定位安装包与运行时选择逻辑
- 安装目录走注册表
Uninstall的InstallLocation+DisplayVersion,不要从快捷方式猜。 - 老客户端的
resources/app/main.js典型形态:读../../ver,正则/^ver[ ]*=[ ]*(\d*)/m,再用这个值拼出<ver>.asar与<ver>.node;ever字段给出 Electron/Node 版本(1.7.x 配 OpenSSL 1.0.2 ⇒ 现代 TLS/证书链会失败,但只在判据 1 指向网络时才值得查)。 - 比对
ver的 mtime 与进程启动时刻:mtime 晚于启动 ⇒ 进程存活期间切换过版本。 - 「JS 层已换而原生层没换」只当 30 秒排除项,别当结论:launcher 里 asar 与
.node同取一个ver值(require(__dirname+"/../"+betopver)靠扩展名回退命中.node),干净重启后不存在错配;只有「进程存活期间改写ver且 JS 侧重新 require」这一支才可能错配(Node 无法热卸载.node)。要核实进程实际加载哪个.node需管理员权限,拿不到就标「未验证」。
3. 在包里 grep 界面原文(不解包)
用 scripts/asar-inspect.cjs(grep 直接从包内按条目读,不落地;默认拒绝整包解压)。
- 中文串一字之差就全落空:截图「请稍候」与新版代码「请稍后」不是同一处;省略号是
…(U+2026) 不是...。两种写法都要试(请稍候 / 请稍后)。先 grep 短前缀(如「数据同步中」)再核整句;命中多处时按语言/代次目录(gen3/gen4/clientv2)筛。 - 老 minified JS 里中文常存成
\uXXXX转义 ⇒ 同一模式必须同时试「原始 UTF-8」与「\uXXXX(含大小写、单层/双层反斜杠)」,脚本已内置;实测同一个词只有转义形态能命中主进程脚本。 - 拿到 i18n key 后反查使用点,只读三件事:置位条件、清除条件、有无
setTimeout兜底。 - 同一语言包里往往直接写着厂商的前置条件文案(本次同文件就有「请使用有线连接手柄后,再次点击…」两条)——这类文案是低成本高价值的线索。
- 要执行包内代码、按字节偏移取上下文、还原函数:用姊妹技能
electron-asar-code-forensics(asar-peek.cjs)。
asar 头布局(实测钉死;基址写错 8 字节不会报错,只会静默错位):
u32@0 = 4 常量
u32@4 = 对齐后头块长度 头块占 [8, 8+u32@4)
u32@8 = u32@4 - 4
u32@12 = JSON 字节长度
JSON = 字节 [16, 16+u32@12)
数据基址 base = 8 + u32@4 = align4(16 + u32@12) ← 不是 16 + u32@4
文件实读位置 = base + Number(entry.offset) offset 在 JSON 里是字符串必做断言(header 子命令会打印,任一不成立就停手):u32@4 - u32@8 === 4;JSON.parse 对 [16, base) 整段成功;base - (16 + u32@12) <= 3;max(offset+size) <= filesize - base——真包里 maxEnd 与 dataLen 应完全相等,差 8 就是基址按 16+u32@4 算错了。再抽一个 offset="0" 的文件看头 40 字节是不是合法源码。「能读出通顺文本」不能证明基址正确:晚 8 字节照样 grep 得出通顺中文,只是每个文件都少了开头。
4. 设备侧枚举与握手自证
机制写法(枚举每个集合的 UsagePage/Usage/报告长度、joyGetPosEx 证活着、限时收发)见姊妹技能 hid-device-protocol-probe(hid-enum.ps1 / hid-rw-probe.ps1 / joy-check.ps1)。本技能只定这几条判读口径:
- 厂商配置通道通常是独立 Collection(
UsagePage=0x00FF Usage=0x0003,报告长度固定);XInput 手柄会同时枚举成游戏控制器和键鼠设备。 - 按 PID 或产品名筛,不要筛 VID:同一外设可能以另一个厂商 ID 在俗(本次同一接收器从
VID_20BC变成VID_20DD,而固件代码里两个 VID 都注册过),筛 VID 会得到「枚举 41 个、命中 0 个」的假阴性。 - HID 路径必须带
\\?\前缀,被截掉 ⇒CreateFile FAILED err=123。 - 两个必须认得的错误码:
CreateFile带FILE_FLAG_OVERLAPPED却做同步读 ⇒err=87;HidD_SetOutputReport返回 Falseerr=31⇒ 退回WriteFile,并试首字节 report ID 的两种形态(含/不含0x00前缀)。 - 同步句柄上
ReadFile与WriteFile都会永久阻塞(外设关机或无链路),随后CloseHandle跟着挂死。止法:读写各放独立线程 + 窗口到点CancelIoEx(h, NULL)+ 仍有线程卡住时不要CloseHandle。err=995是我方窗口到点取消,不是设备故障,也不能证明帧没送到。 - 零回包不等于设备坏了:关机、无链路、选错集合三者读数一模一样;先复确认
UsagePage=0x00FF再下结论。 - 历史连接证据看注册表
HKLM\SYSTEM\CurrentControlSet\Enum\USB与...\Enum\HID:某个 PID 从未出现说明那种连接方式从没插过。 - 红线:只发客户端本来就在发的只读查询/同步帧;不得下发写配置、改宏、固件升级类帧;确需下发写帧,先向用户点名风险与回滚再动。
关键的第三刀:按客户端帧构造自己发一次它正在轮询的同步帧,然后看帧类型计数——只见状态/心跳帧(如 0x15/0x21)、始终没有同步帧(0x11)⇒ 客户端的同步请求从没完成往返。
5. 查设备注册口径
在包内的型号脚本里 grep 注册调用,比对主注册 PID 与实际在俗 PID:
RegisterDev2(VID, JOYPAD_PID) // 主设备:有线 0x512F
RegisterAlterDev(VID, DONGLE_PID) // 备用:2.4G 接收器 0x5130若回包泵绑在主设备上,则「只用接收器」时写得出去、读不回来,界面就无限转圈且不报错——因为遮罩的清除条件正是那个回包。这条是厂商特异性结论,只作为「最后要查的注册口径」;通用判据是「主注册 PID 从未出现在 USB 枚举里」+「被动监听缺同步帧」两者同时成立。
修复:数据线直连(只能充电的线不算,这是最常见的坑),接收器先拔掉;固件升级、屏幕壁纸类操作通常同样要求有线。
6. 复验(必须能失败)
- 复测枚举:主注册 PID 现在出现在
Enum\USB里。 - 复测链路:被动监听的同步帧计数从 0 变正。
- 界面:遮罩消失并进入配置页——前台改动要真跑 + 截图才算完成。
输出要求
结论分三类逐条给证据:已验证(命令 + 读数)、推断(写明支撑它与推翻它各需要什么)、未验证(写明拿不到什么,例如读提权进程模块需管理员)。本流程两次被证伪的假设(「同步=云同步」、「asar/node 错配」)就是靠这一层暴露的;不要把推断写成结论。
Windows 执行注意
探针脚本一律落 .ps1 文件跑,正文不写中文(PS 5.1 按 GBK 读无 BOM 脚本会把中文字面量炸成「字符串缺少终止符」);其余 Git Bash 驱动 PowerShell 的转义坑见技能 windows-powershell-from-gitbash。产物集中放 %TEMP%/qoder-work/ 或项目 .diag/,解包的大文件不要留在盘上。
Resources
scripts/asar-inspect.cjs— asar 结构层只读取证:头布局自校验(断言不过退 3,并列出8/12/16 + u32@4三个候选基址各自读出的内容)。子命令header、list [--filter S] [--limit N]、grep <文本> [--path S] [--ctx N] [--max N] [--max-bytes N]、cat <包内路径> [--bytes N]、extract <包内路径> [--out DIR] [--force]。grep 对同一模式试 5 种形态(原始 UTF-8 /\uXXXX大小写 / 双层反斜杠),零命中退 2 并提示核对界面用字。
本技能的验证边界
已实测(2026-10-07 本机):asar-inspect.cjs 在两个真包(177MB / 211MB)上 maxEnd == dataLen 全等;两个一字之差的中文变体各自命中;\uXXXX 转义形态命中主进程脚本;非 asar 文件退 3/1、extract . 被拒。HID 侧的判读口径来自同源的一次真实排查(err=123 / err=87 / err=995、同步帧计数 0→正 均由命令读出)。
未做:未在第二个 Electron 客户端上整跑本管线;设备在线时的收发形态本轮未复测(当日外设关机)。这两条要真机复跑才能销账。
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

