/** * Pure sequencing of the Win32 `IFileOpenDialog` folder-picker COM * conversation over an injectable bindings seam, so every outcome path * (selection, cancellation, HRESULT failure, cleanup ordering) is testable on * any platform. The koffi-backed bindings live in * `win32-dialog-bindings.ts`, which only a real win32 process ever loads. */ /** `HRESULT_FROM_WIN32(ERROR_CANCELLED)`: the user dismissed the dialog. */ export const HRESULT_CANCELLED = 0x800704c7 | 0 /** `FOS_PICKFOLDERS`: the dialog selects directories, not files. */ export const FOS_PICKFOLDERS = 0x20 /** `FOS_FORCEFILESYSTEM`: only results with a filesystem path can be chosen. */ export const FOS_FORCEFILESYSTEM = 0x40 /** `FOS_NOCHANGEDIR`: never mutate the process working directory. */ export const FOS_NOCHANGEDIR = 0x8 /** One created folder dialog: the vtable calls the sequencing needs. */ export interface Win32FolderDialog { /** * `IFileDialog::SetOptions`. * @param options - the `FOS_*` flag union to apply. * @returns the call's HRESULT. */ setOptions(options: number): number /** * `IFileDialog::SetTitle`. * @param title - the dialog title text. * @returns the call's HRESULT. */ setTitle(title: string): number /** * `IModalWindow::Show` with no owner window; blocks the calling thread * until the user selects or dismisses. * @returns the call's HRESULT (`HRESULT_CANCELLED` on dismissal). */ show(): number /** * `IFileDialog::GetResult` + `IShellItem::GetDisplayName(SIGDN_FILESYSPATH)`, * releasing the shell item and freeing the COM string. * @returns the call chain's HRESULT and, on success, the selected path. */ resultPath(): { hr: number; path?: string } /** Release the dialog's COM reference. */ release(): void } /** The thread-level native surface the dialog sequencing runs against. */ export interface Win32DialogBindings { /** * Opt the calling thread into the best supported DPI awareness * (per-monitor-v2, then per-monitor, then system-aware), checking each * call's result. Best-effort on purpose: a host accepting none of them * (or lacking the API, pre-1607) still shows the modern dialog — possibly * blurry above 100 % scaling — because a cosmetic degradation must not * cost the tier. */ setThreadDpiAwareness(): void /** * `CoInitializeEx(COINIT_APARTMENTTHREADED)` on the calling thread. * @returns the call's HRESULT (`S_FALSE` re-entry is still a success). */ coInitializeSta(): number /** * `CoUninitialize` on the calling thread — COM requires one pairing call * for every successful (including `S_FALSE`) `CoInitializeEx`, even on a * thread that exits right after the conversation. */ coUninitialize(): void /** * `CoCreateInstance(CLSID_FileOpenDialog)`. * @returns the created dialog surface; throws when creation fails. */ createFolderDialog(): Win32FolderDialog /** * `GetCurrentThreadId` — the native id a driver needs to close this * thread's windows from outside. * @returns the calling thread's native id. */ currentThreadId(): number } /** * Throw when an HRESULT signals failure. * @param hr - the HRESULT to check. * @param what - the failing call's name for the error message. * @returns the (successful) HRESULT unchanged. */ function check(hr: number, what: string): number { if (hr < 0) throw new Error(`${what} failed: HRESULT 0x${(hr >>> 0).toString(16)}`) return hr } /** * Run one modal folder-picker conversation on the calling thread: DPI opt-in, * STA init, dialog creation, `Show`, and result extraction, releasing the * dialog on every path. * @param bindings - the native surface (koffi-backed in production, fakes in tests). * @param title - the dialog title text. * @param onShowing - called with the native thread id immediately before the * blocking `Show`, so a driver on another thread can close the dialog. * @returns the selected filesystem path, or null when the user cancels. */ export function runFolderDialog( bindings: Win32DialogBindings, title: string, onShowing: (threadId: number) => void, ): string | null { bindings.setThreadDpiAwareness() check(bindings.coInitializeSta(), 'CoInitializeEx') // From here the apartment is initialized (S_OK or S_FALSE) and must be // uninitialized exactly once on every path. try { const dialog = bindings.createFolderDialog() try { check(dialog.setOptions(FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM | FOS_NOCHANGEDIR), 'SetOptions') check(dialog.setTitle(title), 'SetTitle') onShowing(bindings.currentThreadId()) const shown = dialog.show() if (shown === HRESULT_CANCELLED) return null check(shown, 'Show') const result = dialog.resultPath() check(result.hr, 'GetResult') return result.path as string } finally { dialog.release() } } finally { bindings.coUninitialize() } }