Search code examples
registryinno-setup

Uninstall auto-run registry entries for all users


Consider this scenario:

  • Inno Setup installs program named XYZ to Program Files, to be accessed by all users.
  • A configuration option within program XYZ allows installation of a registry value to HKCU\Software\Microsoft\Windows\CurrentVersion\Run on a per-user basis, to allow users to configure application auto-start to their own preferences.
  • When uninstalling XYZ, if any user other than the current user has auto-run registry keys set, they will be left over and cause errors next time they log-in.

Questions

  • What would be the correct way to remove the appropriate registry values from all user accounts in Inno Setup?
  • Would it be appropriate to enumerate over the profiles in HKU and check for the keys and delete them? How would this be done in Inno Setup?
  • Lastly, what issues might doing this cause with roaming profiles?

The program XYZ in question is in C#, and can enumerate through the HKU's with the following code, but I'd like to handle the uninstallation completely via Inno Setup and not have to call into a separate executable on uninstall.

private static string GetSIDFromUserName(string userName)
{
    var account = new System.Security.Principal.NTAccount(userName);
    var identifier = (System.Security.Principal.SecurityIdentifier)account.Translate(typeof(System.Security.Principal.SecurityIdentifier));
    var sid = identifier.Value;
    return sid;
}

private static string[] GetAllSystemUsers()
{
    List<string> names = new List<string>();
    SelectQuery query = new SelectQuery("Win32_UserAccount");
    ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
    foreach (ManagementObject envVar in searcher.Get())
    {
        names.Add((string)envVar["Name"]);
    }
    return names.ToArray();
}

Solution

  • To delete an autorun entry from all users, use:

    procedure DeleteAutoRunEntryFromAllUsers(AutoRunValueName: string);
    var
      Names: TArrayOfString;
      UserKey: string;
      AutoRunKey: string;
      I: Integer;
    begin
      Log('Enumerating user keys');
      RegGetSubkeyNames(HKEY_USERS, '', Names);
      Log(Format('Found %d user keys', [GetArrayLength(Names)]));
    
      for I := 0 to GetArrayLength(Names)-1 do
      begin
        UserKey := Names[I];
        Log(Format('User %s', [UserKey]));
        AutoRunKey := Format('%s\SOFTWARE\Microsoft\Windows\CurrentVersion\Run', [UserKey]);
    
        if RegValueExists(HKEY_USERS, AutoRunKey, AutoRunValueName) then
        begin
          Log(Format('Deleting auto-run entry from user %s', [UserKey]));
    
          if RegDeleteValue(HKEY_USERS, AutoRunKey, AutoRunValueName) then
          begin
            Log(Format('Deleted auto-run entry from user %s', [UserKey]));
          end
            else
          begin
            Log(Format('Failed to delete auto-run entry from user %s', [UserKey]));
          end;
        end;
      end;
    end;
    

    Not sure about the roaming profiles.


    Did you consider adding the autorun entry to the HKEY_LOCAL_MACHINE, but make the application to exit immediately based on a setting in the HKEY_CURRENT_USER (per user preference)?

    This way you could just uninstall a single HKEY_LOCAL_MACHINE value. The setting in HKEY_CURRENT_USER might be left behind.