Compare commits

...

3 Commits

Author SHA1 Message Date
Boliang Zhang (from Dev Box)
39e24f6bbf Make cleanup_updates exception-safe to prevent startup crashes
Switch std::filesystem::exists and directory_iterator to error_code
overloads in cleanup_updates() so filesystem errors do not throw.
Wrap the detached cleanup thread in try/catch to prevent
std::terminate if any unexpected exception escapes.

Since cleanup now runs at every startup (not just upToDate), the blast
radius of an unhandled exception is wider — a broken reparse point,
access-denied, or IO error would crash PowerToys on launch.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-09 21:40:43 +08:00
copilot-swe-agent[bot]
bb3ce7595a Clean up old update executables on disk at every startup
Previously, cleanup_updates() only ran at startup when the update state
was 'upToDate'. When updates were installed via external means (winget,
Microsoft Store, manual download), the state never transitioned through
the auto-update path, so old installers accumulated indefinitely.

Now cleanup runs at every startup regardless of update state. When a
pending installer exists (readyToInstall state), it is preserved while
all other old installers are deleted.

Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/232217ea-a5f8-4a51-ac0d-0bf1750f97ce

Co-authored-by: LegendaryBlair <122517415+LegendaryBlair@users.noreply.github.com>
2026-04-09 03:26:56 +00:00
copilot-swe-agent[bot]
ccba1c2846 Initial plan 2026-04-09 03:17:35 +00:00
3 changed files with 42 additions and 7 deletions

View File

@@ -194,19 +194,33 @@ namespace updating
co_return download_success ? installer_download_path : std::nullopt;
}
void cleanup_updates()
void cleanup_updates(const std::wstring& preserveFileName)
{
auto update_dir = updating::get_pending_updates_path();
if (std::filesystem::exists(update_dir))
std::error_code ec;
if (std::filesystem::exists(update_dir, ec) && !ec)
{
std::wstring preserveLower = preserveFileName;
std::transform(preserveLower.begin(), preserveLower.end(), preserveLower.begin(), ::towlower);
// Msi and exe files
for (const auto& entry : std::filesystem::directory_iterator(update_dir))
for (const auto& entry : std::filesystem::directory_iterator(update_dir, ec))
{
auto entryPath = entry.path().wstring();
std::transform(entryPath.begin(), entryPath.end(), entryPath.begin(), ::towlower);
if (entryPath.ends_with(L".msi") || entryPath.ends_with(L".exe"))
{
if (!preserveLower.empty())
{
auto entryFilename = entry.path().filename().wstring();
std::transform(entryFilename.begin(), entryFilename.end(), entryFilename.begin(), ::towlower);
if (entryFilename == preserveLower)
{
continue;
}
}
std::error_code err;
std::filesystem::remove(entry, err);
if (err.value())

View File

@@ -41,7 +41,7 @@ namespace updating
wil::task<github_version_result> get_github_version_info_async(bool prerelease = false);
wil::task<std::optional<std::filesystem::path>> download_new_version_async(new_version_download_info new_version);
std::filesystem::path get_pending_updates_path();
void cleanup_updates();
void cleanup_updates(const std::wstring& preserveFileName = {});
// non-localized
constexpr inline std::wstring_view INSTALLER_FILENAME_PATTERN = L"powertoyssetup";

View File

@@ -550,10 +550,31 @@ int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR l
modules();
std::thread{ [] {
auto state = UpdateState::read();
if (state.state == UpdateState::upToDate)
try
{
updating::cleanup_updates();
auto state = UpdateState::read();
if (state.state == UpdateState::readyToInstall && !state.downloadedInstallerFilename.empty())
{
// Preserve the pending installer but clean up all other old update files
updating::cleanup_updates(state.downloadedInstallerFilename);
}
else
{
if (state.state == UpdateState::readyToInstall)
{
Logger::warn("Update state is readyToInstall but downloadedInstallerFilename is empty");
}
updating::cleanup_updates();
}
}
catch (const std::exception& e)
{
Logger::error("Failed to clean up old update files: {}", e.what());
}
catch (...)
{
Logger::error("Failed to clean up old update files: unknown exception");
}
} }.detach();