Skip to content

fix(installer): warn + offer relaunch when TEMP is on a RAM disk#15

Merged
hooyao merged 4 commits into
mainfrom
fix-installer-temp-on-target-drive
May 5, 2026
Merged

fix(installer): warn + offer relaunch when TEMP is on a RAM disk#15
hooyao merged 4 commits into
mainfrom
fix-installer-temp-on-target-drive

Conversation

@hooyao
Copy link
Copy Markdown
Owner

@hooyao hooyao commented May 5, 2026

Summary

If a user has `%TEMP%` on a RAM disk (their own, or any WinFsp mount), the installer self-destructs: `InitializeSetup` stops the running RamDrive service to swap binaries → service unmount erases `{tmp}` → installer crashes with new exe not in place.

This PR adds, as the very first user-visible action, a Vista-style TaskDialog with three command-link buttons:

  1. Continue installation — TEMP is NOT on a RAM disk, safe to proceed.
  2. Pick a different TEMP folder... — opens BrowseForFolder. After the user selects a non-RAM-disk folder, the installer execs `cmd /c` to set `TEMP`/`TMP` env vars in a child shell, then `start`s a fresh `setup.exe` that inherits those vars. The current instance exits cleanly. The relaunched installer's Inno bootstrap creates `{tmp}` under the chosen folder.
  3. Cancel — exit.

If the user picks another RAM-disk path, they see the same dialog again (self-correcting loop).

Why not in NextButtonClick?

Earlier iteration of this PR put the check on the drive-letter wizard page. Reachability bug: `InitializeSetup` runs first; if it stops the service the wizard never gets to render. Removed in this revision.

What this PR does NOT try to do

  • It doesn't detect whether TEMP is on a RAM disk — there's no reliable user-mode IOCTL for that on a generic NTFS-reporting WinFsp filesystem. It asks the user (with the current TEMP path shown for context) and gives a one-click escape hatch.
  • It doesn't avoid the second UAC prompt on relaunch — `PrivilegesRequired=admin` makes that unavoidable.

Test plan

  • Inno Setup compiles (`ISCC.exe` locally on Windows)
  • Manual test: TaskDialog renders correctly on Win11 (yellow triangle + 3 command-link buttons)
  • Manual test: "Pick a different TEMP folder..." → BrowseForFolder → relaunch flows end-to-end into successful install
  • Manual test: "Cancel" exits cleanly with no system state changes
  • Manual test: "Continue installation" runs original install flow

🤖 Generated with Claude Code

hooyao and others added 2 commits May 5, 2026 19:57
…rive

If the user's TEMP (or whatever directory Inno Setup is using as {tmp}) is
on the same drive they just selected for the RAM disk, installation breaks
catastrophically: the installer must stop the running RamDrive service to
swap binaries; stopping the service unmounts the drive; that erases {tmp}
mid-install; the installer then crashes with the new exe not in place and
the service stopped — leaving the system in a hard-to-recover state.

Detect this in NextButtonClick on the config page (after the user picks
the drive letter). If {tmp}'s drive matches the chosen mount letter,
show an actionable error explaining the problem and pointing to the
/T= command-line option:

    setup.exe /T=C:\Windows\Temp

(or any directory on a different physical drive). The installer cancels
rather than proceeding.

This intentionally only catches the common case via the wizard's drive
dropdown — it doesn't try to detect WinFsp mounts in general. That covers
the user-reported failure mode (TEMP on RamDrive) without P/Invoke into
fsutil/IOCTL territory.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The first version of this PR added the temp-on-target check inside
NextButtonClick on the wizard's drive-letter page. That check is
unreachable in practice: InitializeSetup runs first, sees the existing
RamDrive service, prompts the user to stop it, and on Yes immediately
unmounts the drive — by the time the user reaches the drive-letter page,
{tmp} either still works (TEMP wasn't on the RAM disk) or the installer
has already crashed (TEMP was). Either way NextButtonClick never gets to
run the check.

Replaced with the right-place fix:

* In InitializeSetup (very first user-visible action), show a Vista-style
  TaskDialog with three command-link buttons:
    - "Continue installation"          (TEMP isn't on a RAM disk)
    - "Pick a different TEMP folder..."
    - "Cancel"
* "Pick" opens BrowseForFolder defaulting to {sd}\Windows\Temp.
* On a chosen folder, the installer exec's cmd /c to set TEMP / TMP env
  vars in a child shell, then `start` a fresh setup.exe that inherits
  those vars. Current instance Result := False; exit cleanly.
* The relaunched setup.exe is the same .exe on disk — only the env it
  inherits is changed, so its Inno bootstrap creates {tmp} under the
  user-chosen folder instead of the RAM disk.
* If the user picks another RAM-disk path, they'll see the same dialog
  again (self-correcting loop).

Edge cases handled:
* User cancels BrowseForFolder -> abort cleanly.
* User picks a non-existent path -> error + abort.
* Relaunch goes through UAC again (admin required for service install);
  this is unavoidable given PrivilegesRequired=admin.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hooyao hooyao changed the title fix(installer): refuse install when {tmp} is on the target RAM disk drive fix(installer): warn + offer relaunch when TEMP is on a RAM disk May 5, 2026
hooyao and others added 2 commits May 5, 2026 21:20
…ded lines

The previous WriteAppSettings hand-built the file with `Lines[0..N] := '...'`,
so the installed jsonc was missing every field added since the helper was
last touched (FileInfoTimeoutMs, EnableNotifications) and all of the inline
JSONC comments. It also took the opposite of the intended upgrade behavior:
[Files] copied the template with `onlyifdoesntexist` (skipped on upgrade),
then WriteAppSettings unconditionally overwrote with a stale shape.

Replaced with a template-driven flow:

* [Files] now drops the published appsettings.jsonc into {tmp} (always).
* New helpers PatchScalarValue / PatchInitialDirectories / ReadScalarValue /
  ReadInitialDirectories / CountSubstr operate on the in-memory line array.
* WriteAppSettings loads {tmp}\appsettings.jsonc as the base, then patches
  exactly three user-controlled values: MountPoint, CapacityMb, and
  InitialDirectories. Every other line — including all comments and any new
  field added in future releases — flows through unchanged.
* On upgrade, MountPoint / CapacityMb / InitialDirectories are pre-loaded
  from the existing config (read by ReadScalarValue / ReadInitialDirectories)
  into the wizard controls in CurPageChanged, so the user sees their current
  drive letter / capacity / initial-directory tree pre-filled and can either
  keep or change them. The selected values are then patched into the *new*
  template, picking up new defaults and schema additions automatically.
* On fresh install, the wizard defaults drive the patch.
* All other tunables (PageSizeKb, PreAllocate, EnableKernelCache,
  FileInfoTimeoutMs, EnableNotifications, VolumeLabel, Logging) come from
  the new template — so an upgrading user gets the latest default values
  for those, without having to manually edit jsonc.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The release workflow rewrites #define MyAppVersion before invoking ISCC, so
the value committed in the script never ships. "1.0.0" looked like a real
release tag — local builds produced RamDrive-1.0.0-setup.exe which is
indistinguishable from a public 1.0 build at a glance. Switch to 0.0.0-dev
so accidental local artifacts are obviously not release artifacts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hooyao hooyao merged commit 5bda856 into main May 5, 2026
2 checks passed
@hooyao hooyao deleted the fix-installer-temp-on-target-drive branch May 5, 2026 13:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant