Skip to content

fix: check what is installed again before removing what an uninstall left - #3229

Merged
laurentiu021 merged 1 commit into
mainfrom
fix/leftovers-recheck-before-removal
Nov 10, 2026
Merged

laurentiu021 merged 1 commit into
mainfrom
fix/leftovers-recheck-before-removal

Conversation

@laurentiu021

Copy link
Copy Markdown
Owner

The Left behind card stays on the Uninstaller until it is dismissed, so between finding what an uninstall left and pressing Remove ticked… the app can be installed again, or another app into one of the listed folders. The removal re-checked each folder's location and links and nothing else, so it could send a freshly installed app's folder, its AppData folder and its settings key to the Recycle Bin or delete them. The pending list read back in an administrator session already applied the full rules; the same-session removal now does too.

What changes

  • LeftoverService.RemoveAsync reads the uninstall entries Windows holds once per removal, and every folder and key is held to the search's rules again before it goes: refused when an installed app lives in it, inside it or around it (SharedWith), when it was left by an app whose name is registered again, and, for what was found by a name, when an installed app answers to that name (IsShared, by the publisher for a Guess). While that list cannot be read whole, nothing found by a name is removed, as in the search; the install folder came from the app's own entry and is held to the rules that need no name. Each refusal is one sentence on the status line, like the others.
  • LeftoverItem.AppName records which app left each item, set by the search and by the pending list.
  • SafeWalkOptions.Unreadable collects the folders the walk could not list. MeasureFolder reports whether it listed everything and throws when its token was cancelled, instead of reading an unfinished walk as "no link inside". A folder it could not look through whole is not offered by the search, not removed, and not offered again from the pending list.
  • The once-per-folder refusal in LeftoverFinder.AddFolder is kept by path. Matching the start of the sentences already written also matched a sibling such as Acme after Acme Notes, whose refusal was then never shown.
  • README: the removal checks every item again against the same rules and what is installed by then.

How it was checked

Each new test was run against the code with its rule taken out, and failed for that reason:

  • Without the re-check in RemoveFolder, AFolderAnAppWasInstalledIntoSinceTheSearch_IsNotRemoved, AFolderFoundByItsName_IsNotRemoved_OnceAnInstalledAppAnswersToThatName, APublisherFolder_IsNotRemoved_OnceAnotherAppOfThatPublisherIsInstalled, WhatAnAppInstalledAgainLeft_IsKept_FolderAndKeyAlike and WhileTheUninstallListCannotBeReadWhole_OnlyTheInstallFolderCanGo failed. Taking out only the name-registered-again rule, only the key's re-check, or only the incomplete-list rule failed the tests for that rule.
  • Without the cancellation check after the walk, CancelledWhileAFolderIsCheckedAgain_TheFolderStays sent a folder holding a junction to the recorder standing in for the Recycle Bin. The token is cancelled from inside the location check, so the walk sees it before its first folder.
  • With the walk's verdict forced to "whole", AFolderWithAFolderInsideThatCannotBeOpened_IsNotRemoved and Find_DoesNotOfferAFolderItCouldNotLookThroughWhole failed; without the walk recording unreadable folders, the two SafeFileWalkTests cases failed too. The unopenable folder's name ends in a dot, made through a \\?\ path, so an ordinary path cannot list it whatever the account's privileges; a deny entry would not do, since the walk opens folders with backup semantics and an elevated run passes it by.
  • With the old prefix match back, AFolderWhoseNameStartsAnotherRefusedOne_HasItsOwnRefusalShown failed while AFolderRefusedAsTheInstallFolder_IsMentionedOnce_WhenItsNameFindsItAgain still passed. Without AppName set by the search, EveryItem_SaysWhichAppLeftIt failed.
  • Unit suite twice, the format check, and all four projects built with no warnings.

…left

The Left behind card stays until it is dismissed, so an app can be
installed again, or another app into one of its folders, before Remove
ticked is pressed. The removal checked each folder's location and links
again and nothing else, so the install folder, the AppData folder and
the settings key of an app installed again since were removed. Each
item is now held to the search's rules again, against the uninstall
entries Windows holds at that moment: nothing an installed app lives in,
inside or around; nothing left by an app of the same name that is
installed again; nothing found by a name an installed app answers to.
While that list cannot be read whole, only the install folder can go,
as in the search. Every item records the app that left it.

The link check walked the folder again and read a walk it had not
finished as "no link inside": a folder it could not open was skipped,
and a Cancel ended the walk early. The walk now reports the folders it
could not list, a folder it could not look through whole is neither
offered nor removed, and a Cancel during the check stops the removal
before that folder.

A refusal was said once per folder by matching the start of the
sentences already written, which also matched a sibling whose name
starts the same way, so its refusal was never shown. Refusals are now
kept by the folder's path.
@laurentiu021
laurentiu021 merged commit b147234 into main Nov 10, 2026
7 checks passed
@laurentiu021
laurentiu021 deleted the fix/leftovers-recheck-before-removal branch November 10, 2026 08:22
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