fix(picker): cascade thread DPI contexts and harden the round-three review points

- setThreadDpiAwareness checks SetThreadDpiAwarenessContext's return value
  and cascades per-monitor-v2 -> per-monitor -> system-aware; DPI stays a
  deliberate cosmetic best-effort - a host accepting none (or lacking the
  API, pre-1607) still gets the modern dialog instead of a downgrade to the
  legacy fallback chain over a cosmetic concern.
- The mocked-koffi world now uses a distinctive 4-byte pointer width and
  rejects mis-sized out-buffers and mis-divided vtable offsets, so a
  regression to hardcoded 8s fails the suite (the ia32 bug class).
- A keyless built-worker e2e guard loads lib/worker.cjs under plain
  worker_threads on POSIX (the workflow-workerthread shape).
- The 'loaded lazily' module claims are reworded to attribute laziness to
  the dynamic import('koffi') calls, and the discarded close-attempt
  rejection is named at its catch.
This commit is contained in:
Huanqi Cao
2026-08-03 21:42:05 +08:00
parent 8500a21658
commit e182f03230
12 changed files with 128 additions and 35 deletions

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-08-02-win32-in-process-folder-dialog.md
2026-08-02-win32-in-process-folder-dialog.md: fa896d198913f58b22f9186696daec27026bb50f
2026-08-02-win32-in-process-folder-dialog.zh.md: 31077d8d6a3907d955180fda290f92c4cf41e5b9
2026-08-02-win32-in-process-folder-dialog.md: 96bc213ea7cddef6223aa3be69e56688dc9c4724
2026-08-02-win32-in-process-folder-dialog.zh.md: a3dfad2ef73cb28ae8c4c47b6380740aab6de5c3

View File

@@ -10,7 +10,7 @@ The Windows directory picker's primary tier was a spawned PowerShell script arou
## Decision
`packages/host/directory-picker-native` now opens `IFileOpenDialog` (`FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM | FOS_NOCHANGEDIR`) in-process through koffi — already a workspace dependency for the repo's other `win32.ts` surfaces — as the primary win32 tier. The COM conversation runs on a `worker_threads` worker so the modal `Show` never blocks the host event loop; the worker posts its native thread id before blocking, and the driver services aborts by re-posting `WM_CLOSE` to that thread's windows (`EnumThreadWindows`), terminating and unrefing the worker only when the close budget is exhausted (Node cannot interrupt native calls, so an unclosable worker must never hold the process open). The worker thread opts into per-monitor-v2 DPI (`SetThreadDpiAwarenessContext`), a strict upgrade over the script's system-DPI ceiling. The module split keeps coverage honest on every host: `win32-dialog-logic.ts` (pure sequencing) and `win32-dialog.ts` (driver) test against fakes anywhere; `win32-dialog-bindings.ts` tests against a mocked `koffi` COM world (the `dsh-session-persistence-jsonl` technique); POSIX hosts run the real spawn plumbing to its koffi-load rejection; win32 hosts run a real open-and-abort-close smoke. That smoke lives in `processBoundTests`: under the threads pool a worker blocked in a native modal wedges pool teardown, while a fork contains it. The PowerShell chain (see the [DPI note](../bug-fix/2026-08-01-windows-picker-pwsh-dpi.md)) stays as the fallback tier, its trigger widened from `ENOENT` to any pwsh failure, which also closes the PowerShell 6 regression.
`packages/host/directory-picker-native` now opens `IFileOpenDialog` (`FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM | FOS_NOCHANGEDIR`) in-process through koffi — already a workspace dependency for the repo's other `win32.ts` surfaces — as the primary win32 tier. The COM conversation runs on a `worker_threads` worker so the modal `Show` never blocks the host event loop; the worker posts its native thread id before blocking, and the driver services aborts by re-posting `WM_CLOSE` to that thread's windows (`EnumThreadWindows`), terminating and unrefing the worker only when the close budget is exhausted (Node cannot interrupt native calls, so an unclosable worker must never hold the process open). The worker thread opts into the best thread DPI awareness the host accepts (`SetThreadDpiAwarenessContext`, cascading per-monitor-v2 → per-monitor → system-aware with the return value checked), a strict upgrade over the script's system-DPI ceiling; DPI stays a cosmetic best-effort — a host accepting none of them still gets the modern dialog rather than a downgrade to the fallback chain. The module split keeps coverage honest on every host: `win32-dialog-logic.ts` (pure sequencing) and `win32-dialog.ts` (driver) test against fakes anywhere; `win32-dialog-bindings.ts` tests against a mocked `koffi` COM world (the `dsh-session-persistence-jsonl` technique); POSIX hosts run the real spawn plumbing to its koffi-load rejection; win32 hosts run a real open-and-abort-close smoke. That smoke lives in `processBoundTests`: under the threads pool a worker blocked in a native modal wedges pool teardown, while a fork contains it. The PowerShell chain (see the [DPI note](../bug-fix/2026-08-01-windows-picker-pwsh-dpi.md)) stays as the fallback tier, its trigger widened from `ENOENT` to any pwsh failure, which also closes the PowerShell 6 regression.
## Alternatives considered
@@ -21,6 +21,6 @@ The Windows directory picker's primary tier was a spawned PowerShell script arou
## Consequences
- Every Windows machine gets the modern dialog with per-monitor-v2 DPI, PowerShell installed or not; the PowerShell tiers only serve hosts where koffi cannot drive COM.
- Every Windows machine gets the modern dialog with the best DPI awareness it supports (per-monitor-v2 on 1703+), PowerShell installed or not; the PowerShell tiers only serve hosts where koffi cannot drive COM.
- Real dialog rendering and the selection path stay a manual Windows check (the auto-close smoke proves open/abort/unwind); a wedged abort can leak one dialog thread until process exit, documented in the package README.
- The COM vtable slots and GUIDs used are frozen Windows ABI (Vista); a koffi signature mistake is an in-process crash risk contained to the worker thread and caught by the win32 smoke before shipping.

View File

@@ -10,7 +10,7 @@ Windows 目录选择器的主层此前是围绕 WinForms `FolderBrowserDialog`
## 决策
`packages/host/directory-picker-native` 现在经 koffi——它已是仓库其他 `win32.ts` 面的工作区依赖——在进程内打开 `IFileOpenDialog``FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM | FOS_NOCHANGEDIR`),作为 win32 主层。COM 会话运行在 `worker_threads` worker 上,模态 `Show` 永不阻塞宿主事件循环worker 在阻塞前上报其原生线程 iddriver 通过向该线程的窗口反复投递 `WM_CLOSE``EnumThreadWindows`)来服务中止,仅当关闭预算耗尽时才 terminate 并 unref workerNode 无法打断原生调用,关不掉的 worker 决不能拖住进程退出。worker 线程启用 per-monitor-v2 DPI`SetThreadDpiAwarenessContext`),严格优于脚本的系统 DPI 上限。模块切分让覆盖率在任何主机上都诚实:`win32-dialog-logic.ts`(纯时序)与 `win32-dialog.ts`driver在任何平台对假件测试`win32-dialog-bindings.ts` 对 mock 的 `koffi` COM 世界测试(`dsh-session-persistence-jsonl` 的技法POSIX 主机把真实 spawn 管道跑到 koffi 加载失败的拒绝win32 主机跑真实的"打开并中止关闭"冒烟。该冒烟位于 `processBoundTests`threads 池下阻塞在原生模态中的 worker 会卡死池的收尾fork 则能容纳它。PowerShell 链(见 [DPI note](../bug-fix/2026-08-01-windows-picker-pwsh-dpi.md))保留为回退层,触发条件从 `ENOENT` 拓宽为 pwsh 的任何失败,同时关闭了 PowerShell 6 回归。
`packages/host/directory-picker-native` 现在经 koffi——它已是仓库其他 `win32.ts` 面的工作区依赖——在进程内打开 `IFileOpenDialog``FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM | FOS_NOCHANGEDIR`),作为 win32 主层。COM 会话运行在 `worker_threads` worker 上,模态 `Show` 永不阻塞宿主事件循环worker 在阻塞前上报其原生线程 iddriver 通过向该线程的窗口反复投递 `WM_CLOSE``EnumThreadWindows`)来服务中止,仅当关闭预算耗尽时才 terminate 并 unref workerNode 无法打断原生调用,关不掉的 worker 决不能拖住进程退出。worker 线程启用宿主接受的最佳线程 DPI 感知`SetThreadDpiAwarenessContext`,按 per-monitor-v2 → per-monitor → system-aware 级联并检查返回值),严格优于脚本的系统 DPI 上限DPI 保持为纯外观的 best-effort——全部不被接受的宿主仍得到现代对话框而不会降级到回退链。模块切分让覆盖率在任何主机上都诚实:`win32-dialog-logic.ts`(纯时序)与 `win32-dialog.ts`driver在任何平台对假件测试`win32-dialog-bindings.ts` 对 mock 的 `koffi` COM 世界测试(`dsh-session-persistence-jsonl` 的技法POSIX 主机把真实 spawn 管道跑到 koffi 加载失败的拒绝win32 主机跑真实的"打开并中止关闭"冒烟。该冒烟位于 `processBoundTests`threads 池下阻塞在原生模态中的 worker 会卡死池的收尾fork 则能容纳它。PowerShell 链(见 [DPI note](../bug-fix/2026-08-01-windows-picker-pwsh-dpi.md))保留为回退层,触发条件从 `ENOENT` 拓宽为 pwsh 的任何失败,同时关闭了 PowerShell 6 回归。
## 考虑过的替代方案
@@ -21,6 +21,6 @@ Windows 目录选择器的主层此前是围绕 WinForms `FolderBrowserDialog`
## 后果
- 每台 Windows 机器都得到带 per-monitor-v2 DPI 的现代对话框,无论是否安装 PowerShellPowerShell 层只服务 koffi 无法驱动 COM 的主机。
- 每台 Windows 机器都得到带其所支持的最佳 DPI 感知1703+ 为 per-monitor-v2的现代对话框,无论是否安装 PowerShellPowerShell 层只服务 koffi 无法驱动 COM 的主机。
- 真实对话框渲染与选中路径仍是手动 Windows 检查(自动关闭冒烟证明打开/中止/收尾);卡死的中止可能泄漏一个对话框线程直到进程退出,已记录于包 README。
- 所用 COM vtable 槽位与 GUID 是冻结的 Windows ABIVista 起koffi 签名错误是被限制在 worker 线程内的进程内崩溃风险,并在交付前被 win32 冒烟捕获。