fix(picker): pointer-width vtable offsets, COM apartment pairing, unconditional abort budget, and the full failure chain
Review round two on the in-process dialog:
- Vtable slots and out-pointers use koffi.sizeof('void *') instead of a
hardcoded 8 - win32-ia32 (which Node and koffi both ship) would have read
method pointers from the wrong address and crashed in-process before any
fallback could run.
- runFolderDialog pairs every successful (incl. S_FALSE) CoInitializeEx
with CoUninitialize in the outermost finally, releasing the dialog first;
a failed init is deliberately unpaired. Pinned across fake-bindings and
mocked-koffi suites.
- The abort close budget starts unconditionally: a worker hung before the
showing notice (koffi import or COM init) now ends in terminate instead
of a dangling promise; WM_CLOSE posting still waits for the thread id.
- A triple miss (dialog + pwsh + 5.1) surfaces an AggregateError carrying
all three causes - the in-process tier's reason was previously
unrecoverable from the final PowerShell error.
- The stray '=>{ ' formatter artifacts are normalized to real blocks.
Both stale note claims from the review are fixed: the DPI note's
Consequences no longer claims an ENOENT classification or zero new
dependencies, and the 2026-07-27 picker note's Windows bullet now names
the in-process primary and keeps the PowerShell chain as fallback (both
languages, pairings re-recorded).
This commit is contained in:
@@ -72,10 +72,12 @@ export async function pickNativeDirectory(
|
||||
// support. Any non-abort failure (koffi unavailable, ancient Windows, COM
|
||||
// refusal) falls back to the PowerShell chain below.
|
||||
const pickDialog = internals.pickWin32Dialog ?? pickWin32Directory
|
||||
let dialogError: unknown
|
||||
try {
|
||||
return await pickDialog(signal)
|
||||
} catch (error: unknown) {
|
||||
rethrowIfAborted(signal, error)
|
||||
dialogError = error
|
||||
}
|
||||
|
||||
// PowerShell fallback: PowerShell 7 renders the modern IFileDialog folder
|
||||
@@ -100,14 +102,27 @@ export async function pickNativeDirectory(
|
||||
' [Console]::WriteLine($dialog.SelectedPath)',
|
||||
'}',
|
||||
].join('; ')
|
||||
let pwshError: unknown
|
||||
try {
|
||||
const result = await run('pwsh.exe', ['-NoProfile', '-STA', '-Command', script], signal)
|
||||
return outputPath(result.stdout)
|
||||
} catch (error: unknown) {
|
||||
rethrowIfAborted(signal, error)
|
||||
pwshError = error
|
||||
}
|
||||
try {
|
||||
const result = await run('powershell.exe', ['-NoProfile', '-STA', '-Command', script], signal)
|
||||
return outputPath(result.stdout)
|
||||
} catch (error: unknown) {
|
||||
rethrowIfAborted(signal, error)
|
||||
// Triple miss: every tier failed. Surface all three causes — the
|
||||
// in-process dialog's reason is otherwise unrecoverable from the last
|
||||
// PowerShell error alone.
|
||||
throw new AggregateError(
|
||||
[dialogError, pwshError, error],
|
||||
'native directory picker failed: the in-process dialog and both PowerShell hosts failed',
|
||||
)
|
||||
}
|
||||
const result = await run('powershell.exe', ['-NoProfile', '-STA', '-Command', script], signal)
|
||||
return outputPath(result.stdout)
|
||||
}
|
||||
|
||||
if (platform === 'linux') {
|
||||
|
||||
Reference in New Issue
Block a user