public static IEnumerable <ApplicationUninstallerEntry> GetApplicationsFromDrive(
            IEnumerable <ApplicationUninstallerEntry> existingUninstallerEntries, GetUninstallerListCallback callback)
        {
            var existingUninstallers = existingUninstallerEntries as IList <ApplicationUninstallerEntry> ??
                                       existingUninstallerEntries.ToList();

            var pfDirectories = UninstallToolsGlobalConfig.GetProgramFilesDirectories(true).ToList();

            // Get directories which are already used and should be skipped
            var directoriesToSkip = existingUninstallers.SelectMany(x =>
            {
                if (!string.IsNullOrEmpty(x.DisplayIcon))
                {
                    try
                    {
                        var iconFilename = x.DisplayIcon.Contains('.')
                            ? ProcessTools.SeparateArgsFromCommand(x.DisplayIcon).FileName
                            : x.DisplayIcon;

                        return(new[] { x.InstallLocation, x.UninstallerLocation, PathTools.GetDirectory(iconFilename) });
                    }
                    catch
                    {
                        // Ignore invalid DisplayIcon paths
                    }
                }
                return(new[] { x.InstallLocation, x.UninstallerLocation });
            }).Where(x => x.IsNotEmpty()).Select(PathTools.PathToNormalCase)
                                    .Where(x => !pfDirectories.Any(pfd => pfd.Key.FullName.Contains(x, StringComparison.InvariantCultureIgnoreCase)))
                                    .Distinct().ToList();

            // Get sub directories which could contain user programs
            var directoriesToCheck = pfDirectories.Aggregate(Enumerable.Empty <KeyValuePair <DirectoryInfo, bool?> >(),
                                                             (a, b) => a.Concat(b.Key.GetDirectories().Select(x => new KeyValuePair <DirectoryInfo, bool?>(x, b.Value))));

            // Get directories that can be relatively safely checked
            var inputs = directoriesToCheck.Where(x => !directoriesToSkip.Any(y =>
                                                                              x.Key.FullName.Contains(y, StringComparison.InvariantCultureIgnoreCase) ||
                                                                              y.Contains(x.Key.FullName, StringComparison.InvariantCultureIgnoreCase))).ToList();

            var results = new List <ApplicationUninstallerEntry>();
            var itemId  = 0;

            foreach (var directory in inputs)
            {
                itemId++;

                var progress = new GetUninstallerListProgress(inputs.Count)
                {
                    CurrentCount = itemId
                };
                callback(progress);

                if (UninstallToolsGlobalConfig.IsSystemDirectory(directory.Key) ||
                    directory.Key.Name.StartsWith("Windows", StringComparison.InvariantCultureIgnoreCase))
                {
                    continue;
                }

                //Try to get the main executable from the filtered folders. If no executables are present check subfolders.
                var detectedEntries = ApplicationUninstallerFactory.TryCreateFromDirectory(directory.Key,
                                                                                           directory.Value);

                results.AddRange(detectedEntries.Where(detected => !existingUninstallers.Any(existing =>
                {
                    if (!string.IsNullOrEmpty(existing.DisplayName) && !string.IsNullOrEmpty(detected.DisplayNameTrimmed) &&
                        existing.DisplayName.Contains(detected.DisplayNameTrimmed))
                    {
                        return(!existing.IsInstallLocationValid() ||
                               detected.InstallLocation.Contains(existing.InstallLocation,
                                                                 StringComparison.CurrentCultureIgnoreCase));
                    }
                    return(false);
                })));

                //if (result != null && !existingUninstallers.Any(x => x.DisplayName.Contains(result.DisplayNameTrimmed)))
                //    results.Add(result);
            }

            return(results);
        }
        /// <summary>
        ///     Search the system for valid uninstallers, parse them into coherent objects and return the resulting list.
        /// </summary>
        /// <exception cref="System.Security.SecurityException">
        ///     The user does not have the permissions required to read the
        ///     registry key.
        /// </exception>
        /// <exception cref="ObjectDisposedException">
        ///     The <see cref="T:Microsoft.Win32.RegistryKey" /> is closed (closed keys
        ///     cannot be accessed).
        /// </exception>
        /// <exception cref="UnauthorizedAccessException">The user does not have the necessary registry rights.</exception>
        /// <exception cref="IOException">A system error occurred, for example the current key has been deleted.</exception>
        public static IEnumerable <ApplicationUninstallerEntry> GetUninstallerList(GetUninstallerListCallback callback)
        {
            PopulateWindowsInstallerValidGuids();

            var keysToCheck          = GetRegistryKeys();
            var uninstallersToCreate = new List <KeyValuePair <RegistryKey, bool> >();

            foreach (var kvp in keysToCheck.Where(kvp => kvp.Key != null))
            {
                uninstallersToCreate.AddRange(from subkeyName in kvp.Key.GetSubKeyNames()
                                              select kvp.Key.OpenSubKey(subkeyName)
                                              into subkey
                                              where subkey != null
                                              select new KeyValuePair <RegistryKey, bool>(subkey, kvp.Value));

                kvp.Key.Close();
            }

            var itemId = 0;
            var applicationUninstallers = new List <ApplicationUninstallerEntry>();

            foreach (var uninstallerToCreate in uninstallersToCreate)
            {
                try
                {
                    var entry = ApplicationUninstallerFactory.TryCreateFromRegistry(uninstallerToCreate.Key,
                                                                                    uninstallerToCreate.Value);
                    if (entry != null)
                    {
                        applicationUninstallers.Add(entry);
                    }
                }
                catch
                {
                    //Uninstaller is invalid or there is no uninstaller in the first place. Skip it to avoid problems.
                }
                finally
                {
                    uninstallerToCreate.Key.Close();

                    itemId++;
                    var progress = new GetUninstallerListProgress(uninstallersToCreate.Count)
                    {
                        CurrentCount = itemId
                    };
                    callback(progress);
                }
            }

            applicationUninstallers.AddRange(ApplicationUninstallerFactory.GetStoreApps());

            if (ApplicationUninstallerFactory.SteamHelperIsAvailable)
            {
                var steamAppsOnDisk = ApplicationUninstallerFactory.GetSteamApps().ToList();

                foreach (var steamApp in applicationUninstallers.Where(x => x.UninstallerKind == UninstallerType.Steam))
                {
                    var toRemove = steamAppsOnDisk.FindAll(x => x.InstallLocation.Equals(steamApp.InstallLocation, StringComparison.InvariantCultureIgnoreCase));
                    steamAppsOnDisk.RemoveAll(toRemove);
                    ApplicationUninstallerFactory.ChangeSteamAppUninstallStringToHelper(steamApp);

                    if (steamApp.EstimatedSize.IsDefault() && toRemove.Any())
                    {
                        steamApp.EstimatedSize = toRemove.First().EstimatedSize;
                    }
                }

                foreach (var steamApp in steamAppsOnDisk)
                {
                    ApplicationUninstallerFactory.ChangeSteamAppUninstallStringToHelper(steamApp);
                }

                applicationUninstallers.AddRange(steamAppsOnDisk);
            }

            applicationUninstallers.AddRange(ApplicationUninstallerFactory.GetSpecialUninstallers(applicationUninstallers));

            // Fill in missing information
            foreach (var applicationUninstaller in applicationUninstallers)
            {
                if (applicationUninstaller.IconBitmap == null)
                {
                    string iconPath;
                    applicationUninstaller.IconBitmap = ApplicationUninstallerFactory.TryGetIcon(
                        applicationUninstaller, out iconPath);
                    applicationUninstaller.DisplayIcon = iconPath;
                }

                if (applicationUninstaller.InstallDate.IsDefault() &&
                    Directory.Exists(applicationUninstaller.InstallLocation))
                {
                    applicationUninstaller.InstallDate =
                        Directory.GetCreationTime(applicationUninstaller.InstallLocation);
                }
            }

            return(applicationUninstallers);
        }