Skip to content

Suppress MSI-initiated reboots during Store updates#40079

Merged
benhillis merged 1 commit intomasterfrom
user/benhill/no_reboot_cherrypick
Apr 6, 2026
Merged

Suppress MSI-initiated reboots during Store updates#40079
benhillis merged 1 commit intomasterfrom
user/benhill/no_reboot_cherrypick

Conversation

@benhillis
Copy link
Copy Markdown
Member

@benhillis benhillis commented Apr 2, 2026

Cherry-pick of #40074 onto current master.

When the WSL MSIX package is updated via the Microsoft Store, the WslInstaller service automatically upgrades the MSI package by calling MsiInstallProduct. This call was made with INSTALLUILEVEL_NONE (silent install) but without setting the REBOOT=ReallySuppress property.

Per Windows Installer documentation, when a silent install encounters files in use and REBOOT is not suppressed, the system reboots automatically without any user prompt. This could cause unexpected machine restarts after a Store update when WSL binaries (e.g. wslservice.exe) were in use during the upgrade.

This change:

  • Always appends REBOOT=ReallySuppress to MsiInstallProduct arguments in UpgradeViaMsi, preventing Windows Installer from ever initiating a system restart during install/upgrade.
  • Switches UninstallViaMsi from MsiConfigureProduct to MsiConfigureProductEx so we can pass REBOOT=ReallySuppress during uninstall as well.
  • Propagates ERROR_SUCCESS_REBOOT_REQUIRED (3010) to callers instead of swallowing it. User-facing paths (wsl --update, wsl --uninstall) print a reboot-needed message to stderr. The background WslInstaller service silently treats 3010 as success since it has no console.

Fixes #14536

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR prevents Windows Installer from initiating automatic system reboots during Store-driven WSL MSIX updates by forcing MSI operations to use REBOOT=ReallySuppress, and adjusts handling of the “reboot required” success code (3010) across installer and CLI paths.

Changes:

  • Append REBOOT=ReallySuppress to MSI upgrade/install arguments in UpgradeViaMsi.
  • Switch MSI uninstall to MsiConfigureProductEx(..., "REBOOT=ReallySuppress") so uninstall also suppresses MSI-initiated reboots.
  • Treat ERROR_SUCCESS_REBOOT_REQUIRED (3010) as non-fatal in update/uninstall flows, with user-visible messaging in CLI paths and silent success behavior in the background service.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/windows/wslinstaller/exe/WslInstaller.cpp Treats MSI 3010 as success for the silent background installer service.
src/windows/common/WslClient.cpp Updates wsl --uninstall handling to print a reboot-required message on 3010 instead of treating it as failure.
src/windows/common/install.cpp Ensures MSI upgrade/uninstall operations always suppress MSI-initiated reboot; prints reboot-required message on update when MSI returns 3010.
Comments suppressed due to low confidence (2)

src/windows/common/WslClient.cpp:1294

  • This path treats ERROR_SUCCESS_REBOOT_REQUIRED (3010) as non-fatal, but still returns exitCode to the process. That will make wsl --uninstall exit with 3010 even though the uninstall succeeded, which is inconsistent with other commands that print the reboot-needed message but still return 0 (e.g., install prerequisites). If the intent is “success with reboot required”, consider returning 0 after printing while still surfacing the reboot requirement in output.
    if (exitCode == ERROR_SUCCESS_REBOOT_REQUIRED)
    {
        wsl::windows::common::wslutil::PrintSystemError(ERROR_SUCCESS_REBOOT_REQUIRED);
    }
    else if (exitCode != 0)
    {
        clearLogs.release();
        THROW_HR_WITH_USER_ERROR(
            HRESULT_FROM_WIN32(exitCode),
            wsl::shared::Localization::MessageUninstallFailed(exitCode) + L"\r\n" +
                wsl::shared::Localization::MessageSeeLogFile(logFile.c_str()));
    }

    return exitCode;
}

src/windows/common/install.cpp:137

  • PR description says ERROR_SUCCESS_REBOOT_REQUIRED (3010) is “propagated to callers”, but this update path prints the reboot-needed message and then still returns 0 from UpdatePackageImpl(). If the desired behavior is to propagate 3010 to the wsl --update exit code (so callers can detect reboot-needed), this implementation doesn’t do that; otherwise, the PR description should be updated to reflect that wsl --update still exits 0 on 3010.
        const auto exitCode = UpgradeViaMsi(downloadPath.c_str(), L"", logFile.c_str(), &MsiMessageCallback);

        if (exitCode == ERROR_SUCCESS_REBOOT_REQUIRED)
        {
            PrintSystemError(ERROR_SUCCESS_REBOOT_REQUIRED);
        }
        else if (exitCode != 0)
        {
            clearLogs.release();
            THROW_HR_WITH_USER_ERROR(
                HRESULT_FROM_WIN32(exitCode),
                wsl::shared::Localization::MessageUpdateFailed(exitCode) + L"\r\n" +
                    wsl::shared::Localization::MessageSeeLogFile(logFile.c_str()));
        }
    }
    else
    {
        // Set FILE_FLAG_DELETE_ON_CLOSE on the file to make sure it's deleted when the installation completes.
        const wil::unique_hfile package{CreateFileW(
            downloadPath.c_str(), DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, nullptr)};

        THROW_LAST_ERROR_IF(!package);

        const winrt::Windows::Management::Deployment::PackageManager packageManager;
        const auto result = packageManager.AddPackageAsync(
            Uri{downloadPath.c_str()}, nullptr, DeploymentOptions::ForceApplicationShutdown | DeploymentOptions::ForceTargetApplicationShutdown);

        THROW_IF_FAILED(result.get().ExtendedErrorCode());

        // Note: If the installation is successful, this process is expected to receive and Ctrl-C and exit
    }

    return 0;

When the WSL MSIX package is updated via the Microsoft Store, the
WslInstaller service automatically upgrades the MSI package by calling
MsiInstallProduct. This call was made with INSTALLUILEVEL_NONE (silent
install) but without setting the REBOOT=ReallySuppress property.

Per Windows Installer documentation, when a silent install encounters
files in use and REBOOT is not suppressed, the system reboots
automatically without any user prompt. This could cause unexpected
machine restarts after a Store update when WSL binaries (e.g.
wslservice.exe) were in use during the upgrade.

Every deployment script in the repo already passes /norestart to
msiexec (deploy-to-host.ps1, deploy-to-vm.ps1, install-latest-wsl.ps1,
test-setup.ps1), but the programmatic MsiInstallProduct path used by
the WslInstaller service lacked the equivalent property.

This change:
- Always appends REBOOT=ReallySuppress to MsiInstallProduct arguments
  in UpgradeViaMsi, preventing Windows Installer from ever initiating
  a system restart during install/upgrade.
- Switches UninstallViaMsi from MsiConfigureProduct to
  MsiConfigureProductEx so we can pass REBOOT=ReallySuppress during
  uninstall as well.
- Propagates ERROR_SUCCESS_REBOOT_REQUIRED (3010) to callers instead
  of swallowing it. User-facing paths (wsl --update, wsl --uninstall)
  print a reboot-needed message to stderr. The background WslInstaller
  service silently treats 3010 as success since it has no console.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@benhillis benhillis force-pushed the user/benhill/no_reboot_cherrypick branch from 6fd02e3 to da73f0e Compare April 2, 2026 21:16
@benhillis benhillis enabled auto-merge (squash) April 3, 2026 03:51
@benhillis benhillis disabled auto-merge April 3, 2026 21:34
@benhillis benhillis merged commit 7ee07db into master Apr 6, 2026
6 checks passed
benhillis added a commit that referenced this pull request Apr 7, 2026
When the WSL MSIX package is updated via the Microsoft Store, the
WslInstaller service automatically upgrades the MSI package by calling
MsiInstallProduct. This call was made with INSTALLUILEVEL_NONE (silent
install) but without setting the REBOOT=ReallySuppress property.

Per Windows Installer documentation, when a silent install encounters
files in use and REBOOT is not suppressed, the system reboots
automatically without any user prompt. This could cause unexpected
machine restarts after a Store update when WSL binaries (e.g.
wslservice.exe) were in use during the upgrade.

Every deployment script in the repo already passes /norestart to
msiexec (deploy-to-host.ps1, deploy-to-vm.ps1, install-latest-wsl.ps1,
test-setup.ps1), but the programmatic MsiInstallProduct path used by
the WslInstaller service lacked the equivalent property.

This change:
- Always appends REBOOT=ReallySuppress to MsiInstallProduct arguments
  in UpgradeViaMsi, preventing Windows Installer from ever initiating
  a system restart during install/upgrade.
- Switches UninstallViaMsi from MsiConfigureProduct to
  MsiConfigureProductEx so we can pass REBOOT=ReallySuppress during
  uninstall as well.
- Propagates ERROR_SUCCESS_REBOOT_REQUIRED (3010) to callers instead
  of swallowing it. User-facing paths (wsl --update, wsl --uninstall)
  print a reboot-needed message to stderr. The background WslInstaller
  service silently treats 3010 as success since it has no console.

Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants