feat: Windows v1.1.1 — 修复与改进(DSH 工作区归档)

This commit is contained in:
jbjf6666 committed 2026-10-29 15:37:17 +08:00
1 parent 4a5d676c7b
commit e6880efdc6
21 files changed
+807 -106

No files matched your search

+176
View File
@@ -0,0 +1,176 @@
# v1.1.1 变更清单(Windows)
> 本次只在 Windows 端更新。macOS 端尚未重新构建,等你更新 mac 后一起发布。
> 版本号 `1.1.0` → `1.1.1`(bug 修复 + 交互改进)。
---
## 一、修复的 Bug
### 严重(会导致功能错误 / 给错设备下发定位)
**1. 切换设备时复用了上一台设备的 RSD → 给错误的手机下发定位**
`core/server.py: provider_for()`
原逻辑只在「没有任何隧道」时才调 `tunnel.ensure(device.udid)`。如果先连 A 再选 B,
隧道状态仍是 connected,于是 B 的 provider 拿到的是 **A 的 RSD 对象** ——
DVT 流量全发到 A,而界面显示的是 B,没有任何地方会报错。
修复:无条件调用 `tunnel.ensure(device.udid)`,由它内部比对 UDID 并决定是否重建。
**2. `tunnel.stop()` 竞态:释放期间 `ready` 仍为 True 但 `rsd_object()` 已是 None**
`core/tunnel.py: stop()`
原逻辑先清 `_rsd`,最后才清 `host/port/state`。中间那段时间(最长 25 秒)并发的
`provider_for` 会走到 `address()` 回退分支,拿用户态隧道地址去做**内核 TCP 连接**,
必然报 `OSError [WinError 1231] 网络位置不可达`。
修复:在锁内一次性把 state / host / port / rsd 全部清掉再释放资源。
另外新增 `invalidate(reason)`,用于把隧道标记为失效但不释放资源。
**3. 隧道静默断开后仍然报告 connected,持续注入已死的 RSD**
`core/tunnel.py: ready`
pymobiledevice3 的 `UserspaceRsdTunnel` 在 USB 拔出/传输中断时会自己 `aclose()`
并把内部 `rsd` 置空,但 `TunnelManager` 还留着旧引用。表现为界面显示「已连接」,
但怎么操作都不生效,只能手动断开重连。
修复:`ready` 增加健康检查 —— `self._userspace_tunnel.rsd is None` 时判定为不可用,
下一次 `ensure()` 会重建隧道。新增 `is_alive()` 供上层判断。
**4. AMDS 已装好、只是 27015 探针失败时,会去下 193MB 的 iTunes**
`core/apple_driver.py: ensure()`
服务装着且在运行、但 usbmuxd 协议往返失败时,原逻辑直接进入安装阶梯:
先跑 38MB 的 AMDS MSI(返回 1638「已安装」被当成成功),复检仍不通,
于是继续下载 193MB 的完整 iTunes 包,再试 winget。整个过程纯属浪费。
修复:**服务存在时先重启服务**(最多两次,带退避),成功即返回;
仍不通则给出明确的排查建议(防火墙/安全软件拦截 127.0.0.1:27015、重插数据线),
而不是盲目重装。新增 `restart_service()`。
### 中等
**5. `clear_location()` 之后保活任务被永久停掉**
`core/location_provider.py`
`clear_location()` 里调了 `_stop_keepalive()`,之后再次 `set_location` 也**不会重启**
保活任务 —— 等于「设了点但暂停/静止」这个最需要保活的场景彻底失去保护,
而保活正是 Windows 用户态隧道防「静默断线导致手机恢复真实定位」的关键。
修复:`clear_location` 不再停保活(循环本来就会在 `_last_location is None` 时空转);
`set_location` 幂等地重新拉起任务兜底。
**6. DDI 暂存选错目录 → 可能拷进残缺素材,挂载必失败**
`core/server.py: _stage_personalized_ddi()`
原逻辑「找到第一个有 `Image.dmg` 的目录就用」,但只有三件齐全(Image.dmg /
BuildManifest.plist / Image.dmg.trustcache)才能挂载。如果打包内置目录缺 trustcache,
会拷一份残缺素材进缓存,然后挂载失败。
修复:只挑选**三件齐全**的目录(与 `apple_driver.ddi_status()` 判定一致),
拷贝后再校验一次落地完整性,缺任何一件就明确报错并返回 False。
**7. 「进入程序」被可选项拖住 / 「管理员权限」在不需要时也阻塞**
`core/setup_window.py`
- 底部按钮原本只在全部检查结束后才刷新,可选项(网络探测 3×6s、PnP 最长 45s)
没跑完时,即使必需项全绿按钮也是灰的。修复:每完成一项就刷新底部。
- 「管理员权限」原本恒为必需。但 userspace 隧道本身**不需要管理员**,
只有「需要安装 Apple 驱动」时才真的需要。源码模式 / 非管理员启动会被锁死。
修复:`_needs_item()` —— 驱动已就绪时,权限项不再阻塞「进入程序」。
**8. 连接失败时 provider 泄漏 + 启动后重复探测驱动**
`core/server.py` / `main.py`
- `provider_for` 里会话建立失败后,新建的 provider 未关闭(DVT/lockdown 泄漏)。
修复:失败时先关闭再抛出。
- 环境窗口刚做过完整驱动检查,`main()` 启动后又跑一遍深度探测
(sc + 27015 + Get-PnpDevice + 注册表)并可能再次进入安装阶梯。
修复:`setup_result is True` 时跳过重复探测。
**9. keep-alive 连接上「不读 body 的接口」会污染下一个请求**
`core/server.py`
`/api/devices/connect`、`/api/devices/disconnect`、`/api/tunnel`
这些 handler 从不读请求体。在 HTTP/1.1 keep-alive 连接上,没被读走的 `{}`
留在 socket 缓冲区,被 `BaseHTTPRequestHandler` 当成下一个请求的请求行:
```
501 Unsupported method ('{}POST')
```
**间歇性**出现(取决于客户端是否复用连接),最终用户看到的是"偶发操作失败"。
这是我在打包版冒烟测试里实际撞到的。
修复:`do_POST` 进入路由前先把请求体读完并缓存;`handle_one_request` 每请求重置缓存
(否则第二个请求会读到上一个请求的 body —— 这是修复第一版引入的新隐患,已一并处理)。
新增回归脚本 `tools/keepalive_probe.py`(同连接连发 3 个 POST 验证不串)。
### 轻微
**9. 窗口销毁后 Tk 报 `invalid command name "..._pump"`**
`core/setup_window.py`
`_pump` 的 `after` 回调在窗口销毁后仍在队列里。命令行模式下会刷错误。
修复:记录 after id,销毁前取消;并在 `_pump` 开头检查 `winfo_exists()`。
**10. 日志文件无限增长**
`core/server.py`
`app.log` 只追加不滚动,长期使用会越来越大。
修复:启动时若超过 5MB 则滚动为 `app.log.1`(保留一份旧日志)。
**11. 死代码清理**
- `setup_window._update_footer` 里的 `self.state.get("__x__")`(无意义表达式)
- `apple_driver.ensure` 里自相矛盾的 `if result.get("ok"): if name=="msi" and not result.get("ok")`
- `apple_driver.ensure` 里 `service_state()` 的空调用
- `main.py` 的 `driver_state` 字典(写入但从未读取)
- `server._stage_personalized_ddi` 尾部整段不可达代码(残留的 `_cmd_mount_status` 函数体)
---
## 二、功能改进
| 改进 | 说明 |
| --- | --- |
| 环境窗口显示版本号 | 标题旁显示 `v1.1.1 · Windows`,便于确认版本 |
| 环境窗口「打开日志目录」 | 一键打开 `%LOCALAPPDATA%\iOSLocationTrack\logs`,装驱动失败时可自行取证 |
| 网络检查更准确 | 分成「Apple TSS 签名」(必须)和「镜像下载源」(可选)两条,不再把 GitHub 被墙误报成"连不上 Apple" |
| 「重启提权」按钮 | 权限项右侧按钮,点击后通过 UAC 拉起管理员实例,本实例自动退出 |
| `--check-driver` 更完整 | 增加开发者镜像状态与目录、日志目录信息 |
| 驱动安装失败提示 | 明确提示「需要重启 Windows 后生效」而不是笼统的失败 |
---
## 三、验收
```
tests/selftest.py 83/83 PASS
tests/drift_sim.py 40/40 PASS
tests/offline_tiles.py 13/13 PASS
tests/setup_window_selftest.py 7/7 PASS(新增:环境窗口检测→确认→放行全流程)
tests/win_e2e.py 7/7 PASS(真机 iPhone 11 / iOS 26.6.1)
tests/win_app_e2e.py 9/9 PASS(真机,走 App 真实装配路径)
```
打包版 exe 实测:发现设备 → 0.3s 隧道 → DVT → 两次下发 → 恢复真实定位 → 干净退出。
---
## 四、已知未修(留待后续)
审查还提了几项,本轮未动(风险或成本较高,且不影响当前功能):
| 项 | 说明 |
| --- | --- |
| 保活取消的竞态 | `_stop_keepalive()` 只 `cancel()` 不 await,理论上可能与 `clear()/close()` 抢同一个 DTX 通道;窗口极小 |
| `probe()` 失败路径泄漏 lockdown | iOS<17 老路径才有,且失败即报错,影响有限 |
| `provider_for` 长时间持锁 | 连接期间最长持锁约 2 分钟,并发 disconnect/shutdown 会阻塞(无死锁) |
| DDI 下载无 SHA 校验 | 镜像源可变,目前只按文件大小判断完整性 |
| `console=False` 隐藏 CLI 输出 | 打包版 `--check-driver` 的输出看不到,需要另开控制台版本 |
| Win32 调用缺 argtypes | ctypes 未声明原型,当前 64 位下实测正常,但属未定义行为 |
其中建议**下一轮优先**处理:隧道断线自动重连(目前仍需手动断开重连)、
打包版 CLI 输出可见性。
+2 -2
View File
@@ -10,7 +10,7 @@
## 1. Quick start (3 minutes)
1. Run `iOSLocationTrack-1.1.0_win-setup.exe`, or unzip the portable build and
1. Run `iOSLocationTrack-1.1.1_win-setup.exe`, or unzip the portable build and
double-click `iOSLocationTrack.exe`.
2. **The app requests administrator rights** — click *Yes* on the UAC prompt.
This is only needed so it can auto-install the Apple driver on first run.
@@ -199,7 +199,7 @@ Outputs:
| File | Notes |
| --- | --- |
| `dist\iOSLocationTrack\iOSLocationTrack.exe` | Portable onedir build — copy the whole folder |
| `dist\installer\iOSLocationTrack-1.1.0_win-setup.exe` | Installer (Inno Setup 6) |
| `dist\installer\iOSLocationTrack-1.1.1_win-setup.exe` | Installer (Inno Setup 6) |
**Prerequisites:**
+2 -2
View File
@@ -9,7 +9,7 @@
## 1. 三分钟上手
1. 运行 `iOSLocationTrack-1.1.0_win-setup.exe` 安装,或直接双击绿色版
1. 运行 `iOSLocationTrack-1.1.1_win-setup.exe` 安装,或直接双击绿色版
`dist\iOSLocationTrack\iOSLocationTrack.exe`。
2. **程序会请求管理员权限**(UAC 弹窗点「是」)—— 用于首次自动安装 Apple 驱动。
3. 弹出**「环境准备」**窗口,逐项列出所需组件:
@@ -185,7 +185,7 @@ Windows 上这样行不通:
| 文件 | 说明 |
| --- | --- |
| `dist\iOSLocationTrack\iOSLocationTrack.exe` | 绿色版 onedir,整个目录一起拷走 |
| `dist\installer\iOSLocationTrack-1.1.0_win-setup.exe` | 安装包(Inno Setup 6) |
| `dist\installer\iOSLocationTrack-1.1.1_win-setup.exe` | 安装包(Inno Setup 6) |
**前置条件:**