Files
deepseek-harness/.agents/notes/implemented/feature/2026-08-02-win32-in-process-folder-dialog.md
T
Huanqi Cao 089f4dfad8 feat(picker): open the Win32 folder dialog in-process over koffi
The modern IFileOpenDialog becomes the primary win32 tier: a koffi-driven
COM conversation on a worker_threads worker (the modal Show never blocks
the host event loop), per-monitor-v2 DPI via SetThreadDpiAwarenessContext,
and abort service by re-posting WM_CLOSE to the dialog thread's windows,
with terminate+unref as the last resort (Node cannot interrupt a thread
blocked in native code, and such a worker must never hold the process open).

The PowerShell chain stays as the fallback tier with its trigger widened
from ENOENT to any pwsh failure, closing the review-flagged PowerShell 6
regression (no WinForms: exit 1, not ENOENT, so 5.1 never ran).

Layering keeps per-file coverage honest on every host: pure sequencing and
the driver test against fakes anywhere; the bindings run against a mocked
koffi COM world (the session-persistence-jsonl technique); POSIX hosts
drive the real spawn plumbing to its koffi-load rejection; win32 hosts run
a real open-and-abort-close smoke. The smoke joins processBoundTests: a
worker blocked in a native modal wedges the threads pool's teardown, while
a fork contains it. The worker bundles as its own CJS tsdown entry
(workflow-workerthread's pattern; no TLA), and the host module is imported
statically so the node-half bundle stays chunk-free.

Built-plane and real-COM behavior verified on native Windows: standalone
probes for the source worker, the built CJS worker, and the driver's abort
path all open and close the real dialog.

Agent Notes: new implemented/feature/2026-08-02-win32-in-process-folder-dialog
(bilingual) owns the decision; the DPI note is re-scoped to the fallback tier
it now describes and its AutoUpgradeEnabled attribution corrected (.NET Core
3.0 rewrote FolderBrowserDialog; the opt-out arrived in .NET 6).
2026-08-05 00:31:43 +08:00

3.7 KiB

Agent Note: Win32 folder picker moves in-process over koffi

Status: implemented

English | 中文

Problem

The Windows directory picker's primary tier was a spawned PowerShell script around WinForms FolderBrowserDialog: the modern dialog only where PowerShell 7 happens to be installed, a review-flagged regression where PowerShell 6 resolves but has no WinForms (exit 1 is not ENOENT, so the 5.1 fallback never ran), a SetProcessDPIAware ceiling of system DPI, and a picker whose behavior depended on which shells a machine ships rather than on Windows itself.

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) stays as the fallback tier, its trigger widened from ENOENT to any pwsh failure, which also closes the PowerShell 6 regression.

Alternatives considered

  • A prebuilt native helper (native/ family like node-addon-landlock-run). Rejected: a mirror repository, an npm package family, MSVC provisioning, and a release handoff — all to ship ~150 lines of C the repository cannot exercise on CI (no real-Windows lane); koffi delivers the same COM surface with zero new supply chain.
  • An N-API in-process addon. Rejected for the same CI/toolchain reasons plus owned C++ for STA threading and message pumping that worker_threads + koffi express in TypeScript.
  • Keep PowerShell primary and probe versions. Rejected: the picker stays hostage to shell packaging (6 vs 7, Store aliases, profiles), and 5.1's legacy dialog remains the floor wherever pwsh is absent; the fallback-trigger widening alone was accepted into the fallback tier instead.
  • Blocking the main thread for the modal call. Rejected outright: the web host must keep serving RPC while the dialog is open.

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.
  • 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.