fix(launch): route WindowsApps UWP reparse-point stubs through ShellExecute (closes #30) - #31
Open
cioinside wants to merge 1 commit into
Open
fix(launch): route WindowsApps UWP reparse-point stubs through ShellExecute (closes #30)#31cioinside wants to merge 1 commit into
cioinside wants to merge 1 commit into
Conversation
…xecute da_launch's `child_process.spawn` -> `CreateProcessW` path on Windows silently fails for Microsoft Store apps (mspaint, msedge, etc.) because the AppX activation context is not established. The new process exits cleanly within ~1s of the tool call returning, with no diagnostic surface (no crash, no Application Error, no WER, no prefetch). Detection post-resolution via `isWindowsAppsPath`, dispatch via the already-installed `open` package (which uses `cmd /c start` + `ShellExecuteExW` under the hood). Returns a fire-and-forget handle shaped like `launchUrl` (pid:null) because the spawned UWP process is owned by the AppX runtime and not trackable via the ChildProcess API. Reproduction and full analysis in issue #30. Closes #30
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes da_launch's silent failure mode for WindowsApps Store-app reparse-point executables (mspaint, msedge, etc.). Before this PR, calling
da_launch({ argv: ["mspaint"] })returned{ pid, killed: false }but the spawned process exited within ~1 second becauseCreateProcessWdoes not establish the AppX activation context. There was no diagnostic surface — no crash, noApplication Errorevent, no WER report, no crash dump.After this PR, WindowsApps paths are detected post-resolution and dispatched via
ShellExecuteExW(through theopenpackage, which usescmd /c start "" /b <target> <args...>withwindowsVerbatimArguments). The UWP app now launches normally and stays alive.Detection —
isWindowsAppsPath(p)Case-insensitive substring check on the normalized path, looking for
\windowsapps\as a directory component (not arbitrary substring — guards againstmywindowsappsbackup). No-op on non-Windows. AfterresolveProgram()so PATH/PATHEXT matching still wins first.Dispatch —
launchViaShellExecute(resolvedPath, args)Routes through the
openpackage (already independenciesforlaunchUrl). On Windows the package shells out tocmd /s /c start "" /b <target> <args...>withwindowsVerbatimArguments— the only documented way to invokeShellExecuteExWfrom Node.js without a native binding.Returns a fire-and-forget
SpawnHandleshaped likelaunchUrl(pid: null,killed: false,exited: Promise.resolve(0),kill()is a no-op). Rationale:open()returns the transientcmd.exechild process, which exits within milliseconds and is NOT the spawned UWP app. Exposing that PID would be misleading. Callers that need to terminate the spawned UWP app should usetaskkill /im <name>.exevia a separateda_launch, orda_window_list+ a window-close gesture.Behaviour change summary
["mspaint"]["cmd", "/c", "start", "mspaint.exe"]["notepad.exe"],["C:\\Windows\\System32\\notepad.exe"]["echo", "hello"]on POSIXhttp://...)launchUrlrouteTests added (
test/unit/launch.test.ts)9 new test cases:
isWindowsAppsPath:C:\Program Files\WindowsApps\Microsoft.Paint_...\mspaint.exe)%USERPROFILE%\AppData\Local\Microsoft\WindowsApps\mspaint.exe)windowsappsin a file name (not a directory) returns false (regression guard)launchProgramUWP dispatch:launchProgram(['mspaint'])returnspid: nulland the spawnedmspaint.exeprocess is observable viaGet-CimInstance Win32_Process— gates onaccessSync(F_OK)(which succeeds on reparse stubs whereexistsSync/statSyncreport EACCES), skips on macOS/Linux or hosts without mspaint installedlaunchProgram(['C:\\Windows\\System32\\notepad.exe'])still returns a numericpid(i.e. still usesspawn, not the new ShellExecute path)Why the workaround in the issue still works (and is preserved)
The PR does not remove the existing
child_process.spawnpath. Callers that already work around viaargv: ["cmd", "/c", "start", "mspaint.exe"]continue to usespawn—cmd.exeitself is not under WindowsApps, so the newisWindowsAppsPathcheck returns false and the legacy code path is taken. The new ShellExecute route is purely additive.Reproduction (from issue #30)
5 control methods that already worked (Start-Process,
cmd /c start mspaint.exe,cmd /c start ms-paint:,explorer.exe ms-paint:,shell:AppsFolder\Microsoft.Paint_...!App) all go through ShellExecuteExW — confirming the diagnosis.Checklist
npm run typecheckcleanisWindowsAppsPathunit tests pass on Windowsspawnpath still used for non-UWP)echo/sleep/POSIX paths on Windows hosts) unchanged — unrelated to this fixCloses #30