Overview
BCU uses a comprehensive multi-source detection system to find installed applications. Unlike Windows’ built-in “Apps & Features”, BCU looks beyond the registry to detect applications from Windows Store, Steam, portable apps, and orphaned installations.
Detection Architecture
BCU employs a factory pattern with specialized detectors for different application types:
public static IList < ApplicationUninstallerEntry > GetUninstallerEntries (
ListGenerationProgress . ListGenerationCallback callback )
{
const int totalStepCount = 8 ;
var currentStep = 1 ;
// 1. Find MSI products
var msiProducts = MsiTools . MsiEnumProducts (). ToList ();
// 2. Find stuff mentioned in registry
var registryFactory = new RegistryFactory ( msiProducts );
var registryResults = registryFactory . GetUninstallerEntries ( callback );
// 3. Look for entries on drives
var driveFactory = new DirectoryFactory ( registryResults );
var driveResults = driveFactory . GetUninstallerEntries ( callback );
// 4. Find apps from stores and other sources
var otherResults = GetMiscUninstallerEntries ( callback );
// 5. Merge and deduplicate
MergeResults ( mergedResults , otherResults , callback );
MergeResults ( mergedResults , driveResults , callback );
return mergedResults ;
}
Detection Sources
Windows Registry Primary source for traditionally installed applications
Windows Store UWP and modern Windows Store applications
Steam Games and applications from Steam platform
Chocolatey Package manager installed applications
Scoop Portable applications managed by Scoop
Oculus VR applications from Oculus platform
Windows Features Optional Windows features and components
Directory Scan Orphaned apps found by scanning Program Files
Registry Detection
The primary source for most applications is the Windows registry:
BCU scans multiple registry paths: private static readonly string RegUninstallersKeyDirect =
@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" ;
private static readonly string RegUninstallersKeyWow =
@"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" ;
32-bit Applications on 64-bit Windows : Found in the Wow6432Node path64-bit Applications : Found in the direct pathPer-User Installations : Scanned in both HKLM and HKCU hives
MSI Product Detection
Windows Installer (MSI) applications require special handling:
// Enumerate all MSI products
var msiProducts = MsiTools . MsiEnumProducts (). ToList ();
// Check if entry is MSI-based
if ( ApplicationEntryTools . PathPointsToMsiExec ( uninstallString ) &&
MsiTools . MsiEnumProducts (). All ( g => ! g . Equals ( entry . BundleProviderKey )))
{
// Product was uninstalled
CurrentStatus = UninstallStatus . Completed ;
}
MSI Special Handling : BCU re-enumerates MSI products during batch operations to detect when MSI applications are removed by other uninstallers.
Windows Store Apps
Modern Windows Store applications are detected using PowerShell integration:
public class StoreAppFactory : IIndependantUninstallerFactory
{
private static string HelperPath { get ; } =
Path . Combine ( UninstallToolsGlobalConfig . AssemblyLocation ,
@"StoreAppHelper.exe" );
public IList < ApplicationUninstallerEntry > GetUninstallerEntries (
ListGenerationProgress . ListGenerationCallback progressCallback )
{
var results = new List < ApplicationUninstallerEntry >();
if ( ! IsHelperAvailable ()) return results ;
var output = FactoryTools . StartHelperAndReadOutput ( HelperPath , "/query" );
foreach ( var data in FactoryTools . ExtractAppDataSetsFromHelperOutput ( output ))
{
var fullName = data [ "FullName" ];
var result = new ApplicationUninstallerEntry
{
Comment = fullName ,
RatingId = fullName . Substring ( 0 , fullName . IndexOf ( "_" )),
UninstallString = $" \" { HelperPath } \" /uninstall \" { fullName } \" " ,
RawDisplayName = data [ "DisplayName" ],
Publisher = data [ "PublisherDisplayName" ],
UninstallerKind = UninstallerType . StoreApp ,
InstallLocation = data [ "InstalledLocation" ],
IsProtected = Convert . ToBoolean ( data [ "IsProtected" ])
};
results . Add ( result );
}
return results ;
}
}
FullName : Complete package identity including version and architecture
DisplayName : User-friendly application name (may be localized)
PublisherDisplayName : Application publisher
InstalledLocation : Installation directory path
IsProtected : Whether the app is a system component
Logo : Application icon/logo path
Steam Applications
Steam games and applications are detected through Steam’s API:
public class SteamFactory : IIndependantUninstallerFactory
{
private static bool GetSteamInfo ( out string steamLocation )
{
if ( File . Exists ( SteamHelperPath ))
{
var output = FactoryTools . StartHelperAndReadOutput (
SteamHelperPath , "steam" );
if ( ! string . IsNullOrEmpty ( output ) && Directory . Exists ( output ))
{
steamLocation = output . Trim (). TrimEnd ( ' \\ ' , '/' );
return true ;
}
}
return false ;
}
public IList < ApplicationUninstallerEntry > GetUninstallerEntries (
ListGenerationProgress . ListGenerationCallback progressCallback )
{
var results = new List < ApplicationUninstallerEntry >();
if ( ! GetSteamInfo ( out var steamLocation )) return results ;
var output = FactoryTools . StartHelperAndReadOutput (
SteamHelperPath , "l /i" );
foreach ( var data in FactoryTools . ExtractAppDataSetsFromHelperOutput ( output ))
{
if ( ! int . TryParse ( data [ "AppId" ], out var appId )) continue ;
var entry = new ApplicationUninstallerEntry
{
DisplayName = data [ "Name" ],
UninstallString = data [ "UninstallString" ],
InstallLocation = data [ "InstallDirectory" ],
UninstallerKind = UninstallerType . Steam ,
RatingId = "Steam App " + appId . ToString ( "G" )
};
if ( long . TryParse ( data [ "SizeOnDisk" ], out var bytes ))
entry . EstimatedSize = FileSize . FromBytes ( bytes );
results . Add ( entry );
}
return results ;
}
}
Directory Scanner
BCU can detect orphaned applications by scanning installation directories:
Scanning Process
Orphan Detection
public class DirectoryFactory : IUninstallerFactory
{
public IList < ApplicationUninstallerEntry > GetUninstallerEntries (
ListGenerationProgress . ListGenerationCallback progressCallback )
{
var results = new List < ApplicationUninstallerEntry >();
// Scan common installation directories
foreach ( var programFilesDir in GetProgramFilesDirs ())
{
foreach ( var subDir in Directory . GetDirectories ( programFilesDir ))
{
// Check if already known from registry
if ( AlreadyKnown ( subDir )) continue ;
// Try to create entry from directory
var entry = TryCreateFromDirectory (
new DirectoryInfo ( subDir ));
if ( entry != null )
results . Add ( entry );
}
}
return results ;
}
}
Applications are marked as orphaned when:
Found in Program Files but not in registry
Contain executable files but no uninstaller
Have characteristics of an application (folder structure, files)
Not matched to any registered application
Orphaned Flag : IsOrphaned = trueSimple Delete : Uninstaller type is set to SimpleDelete for folder removal
Supported Installer Types
BCU detects and handles various installer technologies:
Windows Installer (MSI) Standard Windows Installer packages with GUID-based identification
NSIS (Nullsoft) Nullsoft Scriptable Install System with quiet uninstall detection
Inno Setup Inno Setup installers with silent parameter generation
InstallShield InstallShield installers with response file support
Wise Installer Wise Installation System packages
Custom Uninstallers Application-specific uninstall executables
Simple Delete Direct folder deletion for portable/orphaned apps
PowerShell PowerShell-based uninstall scripts (Store apps)
Uninstaller Type Detection
BCU automatically identifies the installer type:
public enum UninstallerType
{
Unknown = 0 ,
Msiexec ,
Nsis ,
InnoSetup ,
InstallShield ,
SteamApp ,
StoreApp ,
Chocolatey ,
Scoop ,
WindowsFeature ,
WindowsUpdate ,
SimpleDelete ,
PowerShell
}
After initial detection, BCU enriches entries with additional information:
Extracts digital signature information: public X509Certificate2 GetCertificate ()
{
if ( ! _certificateGotten )
{
_certificateGotten = true ;
_certificate = CertificateGetter . TryGetCertificate ( this );
if ( _certificate != null )
_certificateValid = _certificate . Verify ();
}
return _certificate ;
}
Determines installation size:
Reads EstimatedSize from registry (in KB)
Scans installation directory if not available
Uses fast size calculation for large directories
Caches results for performance
Finds main application executables: internal string [] SortedExecutables { get ; set ; }
public IEnumerable < string > GetSortedExecutables ()
{
if ( SortedExecutables == null )
return Enumerable . Empty < string >();
var output = SortedExecutables . AsEnumerable ();
if ( ! string . IsNullOrEmpty ( UninstallerFullFilename ))
output = output . OrderBy ( x =>
x . Equals ( UninstallerFullFilename ,
StringComparison . InvariantCultureIgnoreCase ));
return output ;
}
Quiet Uninstall String Generation
Generates silent uninstall commands:
NSIS : Adds /S parameter
Inno Setup : Adds /VERYSILENT /SUPPRESSMSGBOXES
MSI : Uses msiexec /x {GUID} /qn
Custom : Checks for common silent switches
Deduplication and Merging
Multiple sources may detect the same application:
internal static void MergeResults (
ICollection < ApplicationUninstallerEntry > baseEntries ,
ICollection < ApplicationUninstallerEntry > newResults ,
ListGenerationProgress . ListGenerationCallback progressCallback )
{
var newToAdd = new List < ApplicationUninstallerEntry >();
foreach ( var entry in newResults )
{
var matchedEntry = baseEntries
. Select ( x => new {
x ,
score = ApplicationEntryTools . AreEntriesRelated ( x , entry )
})
. Where ( x => x . score >= 1 )
. OrderByDescending ( x => x . score )
. Select ( x => x . x )
. FirstOrDefault ();
if ( matchedEntry != null )
{
// Merge information into existing entry
InfoAdder . CopyMissingInformation ( matchedEntry , entry );
continue ;
}
// Add as new entry
newToAdd . Add ( entry );
}
}
Matching Algorithm
Applications are matched based on:
Registry Key Name : Exact match on GUID or key name
Install Location : Same installation directory
Display Name + Publisher : Similar name and same publisher
Bundle Provider Key : Matching MSI product GUID
Caching System
BCU caches detected application information:
if ( UninstallToolsGlobalConfig . UninstallerFactoryCache != null )
{
ApplyCache ( registryResults ,
UninstallToolsGlobalConfig . UninstallerFactoryCache ,
InfoAdder );
}
// After enrichment, update cache
foreach ( var entry in mergedResults )
UninstallToolsGlobalConfig . UninstallerFactoryCache . TryCacheItem ( entry );
Performance : The cache significantly speeds up subsequent scans by storing expensive-to-calculate information like file sizes and certificates.
Bulk Uninstall Uninstalling detected applications in bulk
Export Lists Exporting detected application lists