97

I'm using Inno Setup to create an installer.

I want the installer to automatically uninstall the previous installed version, instead of overwriting it. How can I do that?

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Delta76
  • 13,931
  • 30
  • 95
  • 128
  • 2
    Note that as mlaan [said](http://stackoverflow.com/a/2514693/588306) there is not normally any need to do this with an Inno based setup unless you're upgrading from a non Inno version. – Deanna Jul 31 '12 at 12:17
  • 7
    Deanna: it depends on the case. For some programs with automatic plugin systems, which read anything in a folder, removal of old files is an absolute must when installing a new version, and simply running the uninstall is usually the cleanest way to do this. – Nyerguds Apr 03 '13 at 07:02
  • 1
    @Nyerguds But InnoSetup caters for that by having an option to delete certain files/folders before installation starts ("InstallDelete" flag) so you still wouldn't need to uninstall the old version first. – NickG Feb 19 '15 at 14:51
  • 3
    @NickG: Again, depends on the case. That would be the ideal situation, yes, and by far the preferred one, but in reality, there are quite a lot of non-ideal situations. One such example are registered dll files, on many possible target versions. – Nyerguds Feb 24 '15 at 12:54
  • See also [Detect and uninstall old version of application in Inno Setup using its version number stored in registry](https://stackoverflow.com/q/60821439/850848). – Martin Prikryl Mar 25 '20 at 08:24
  • For correct way to parse `UninstallString`, see [Executing UninstallString in Inno Setup](https://stackoverflow.com/q/42222356/850848). – Martin Prikryl May 26 '22 at 13:21

13 Answers13

119

I have used the following. I'm not sure it's the simplest way to do it but it works.

This uses {#emit SetupSetting("AppId")} which relies on the Inno Setup Preprocessor. If you don't use that, cut-and-paste your App ID in directly.

[Code]

{ ///////////////////////////////////////////////////////////////////// }
function GetUninstallString(): String;
var
  sUnInstPath: String;
  sUnInstallString: String;
begin
  sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#emit SetupSetting("AppId")}_is1');
  sUnInstallString := '';
  if not RegQueryStringValue(HKLM, sUnInstPath, 'UninstallString', sUnInstallString) then
    RegQueryStringValue(HKCU, sUnInstPath, 'UninstallString', sUnInstallString);
  Result := sUnInstallString;
end;


{ ///////////////////////////////////////////////////////////////////// }
function IsUpgrade(): Boolean;
begin
  Result := (GetUninstallString() <> '');
end;


{ ///////////////////////////////////////////////////////////////////// }
function UnInstallOldVersion(): Integer;
var
  sUnInstallString: String;
  iResultCode: Integer;
begin
{ Return Values: }
{ 1 - uninstall string is empty }
{ 2 - error executing the UnInstallString }
{ 3 - successfully executed the UnInstallString }

  { default return value }
  Result := 0;

  { get the uninstall string of the old app }
  sUnInstallString := GetUninstallString();
  if sUnInstallString <> '' then begin
    sUnInstallString := RemoveQuotes(sUnInstallString);
    if Exec(sUnInstallString, '/SILENT /NORESTART /SUPPRESSMSGBOXES','', SW_HIDE, ewWaitUntilTerminated, iResultCode) then
      Result := 3
    else
      Result := 2;
  end else
    Result := 1;
end;

{ ///////////////////////////////////////////////////////////////////// }
procedure CurStepChanged(CurStep: TSetupStep);
begin
  if (CurStep=ssInstall) then
  begin
    if (IsUpgrade()) then
    begin
      UnInstallOldVersion();
    end;
  end;
end;

Alternatives

See also this blog post "Inno Setup Script Sample for Version Comparison" which goes one step further, and reads the version number of any previously installed version, and compares that version number with that of the current installation package.

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Craig McQueen
  • 41,871
  • 30
  • 130
  • 181
  • 3
    thanks for referring to my blog post. The full sample for that post is available here, http://code.google.com/p/lextudio/source/browse/trunk/trunk/setup/CBC2Exe.iss – Lex Li Mar 07 '11 at 06:38
  • 2
    Great solution, works fine. However it opens a window during installation showing "Uninstalling [software name]". Is it possible to prevent this window from popping? Because my software's installation is so fast that the install window closes before the uninstall window and it looks weird... – André Santaló Dec 18 '13 at 18:02
  • 5
    @AndréSantaló Use /VERYSILENT instead of /SILENT – Gautam Jain Mar 01 '16 at 12:02
  • 2
    ewWaitUntilTerminated does not work. The uninstaller copies itself to a temporary folder and restarts itself from the temporary folder. – Максим Румянцев Aug 22 '19 at 15:25
  • If you want to uninstall only older versions of the application, see also [Detect and uninstall old version of application in Inno Setup using its version number stored in registry](https://stackoverflow.com/q/60821439/850848). – Martin Prikryl Mar 25 '20 at 08:26
  • 1
    @МаксимРумянцев Yes, indeed the `ewWaitUntilTerminated` does not help on its own, what can lead to corrupted installations. See [Uninstalling previous version of product at the beginning of the installation results in corrupted install in Inno Setup](https://stackoverflow.com/q/68113854/850848). – Martin Prikryl Jul 01 '21 at 05:27
  • @МаксимРумянцев , Hi. There is no restart. According to the source code, I found that the original uninstaller launched by the user will wait for the copy launched from the temp folder. When the uninstallation is almost complete, the copy will send a `WM_KillFirstPhase` message to the original one and wait for it to exit. After that, the copy will wait a while and try to delete the original uninstaller. This is why `ewWaitUntilTerminated` does not help. These operations occur after logging "Deleting Uninstall data files", you can search for that signal or this string in the source code. (edit) – li ki Aug 26 '21 at 22:41
  • I sincerely urge you to use Bill Stewart's excellent DLL. It solves the problem of the installation process triggered before the end of the uninstallation process and has many options. None of the options provided in various threads worked for me, but this one did. https://github.com/Bill-Stewart/UninsIS/pulse – Kostas Markakis Jan 25 '23 at 11:08
30

You should be able to read the uninstall string from the registry, given the AppId (i.e. the value you used for AppID in the [Setup]-section). It could be found under Software\Microsoft\Windows\CurrentVersion\Uninstall\{AppId}\ (could be either HKLM or HKCU, so best check both) where {AppId} should be substituted with the actual value you used. Look for the UninstallString or QuietUninstallString values and use the Exec function to run it from your InitializeSetup() event function.

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Oliver Giesen
  • 9,129
  • 6
  • 46
  • 82
  • Don't even think about doing this from InitializeSetup. PrepareToInstall is the correct place. (But not doing it at all is the best solution.) – Miral Jul 17 '12 at 09:15
  • Using AppId, you can even have one uninstall for mulptiple programs (i.e. client and database): [AppId Inno Setup Help](http://www.jrsoftware.org/ishelp/index.php?topic=setup_appid) – Bojan Hrnkas Jul 08 '13 at 08:17
  • Note that using simply using `Exec` to run the uninstaller will not wait for it to complete before the installer continues (even if `ewWaitUntilTerminated` is used), what can lead to corrupted installations. See [Uninstalling previous version of product at the beginning of the installation results in corrupted install in Inno Setup](https://stackoverflow.com/q/68113854/850848). – Martin Prikryl Jul 01 '21 at 05:25
9

If you "just want to remove the old icons" (because yours have changed/updated) you can use this:

; attempt to remove previous versions' icons
[InstallDelete]
Type: filesandordirs; Name: {group}\*;

This is run "at the beginning of installation" so basically removes the old icons, and your new ones will still be installed there after this is completely done.

I just do this with every install "in case anything has changed" icon wise (it all gets reinstalled anyway).

rogerdpack
  • 62,887
  • 36
  • 269
  • 388
  • If you have update for your icons, just let them overwrite. There's no need to remove them. Well, if you want to remove them, you can use this option. That is correct way. Anyway, the guy you were talking to (mlaan, Martijn Laan) is the Inno Setup author and I think he knows what he's talking about :-) – TLama Mar 31 '14 at 20:36
  • 1
    Yeah, it's when you want to rename or move an icon that you need this. For instance if v5 has one named "run" and v6 has renamed it to "run basic" if a user installs v5 then v6, they'll end up with 2 icons, when really you wanted 1 ("run basic"). So this trick ends up being necessary (@mlaan +1 for changing innosetup's default behavior to be to remove old icons and not need this...) – rogerdpack Apr 01 '14 at 16:36
7

When using Inno Setup, there's no reason to uninstall a previous version unless that version was installed by a different installer program. Otherwise upgrades are handled automatically.

mlaan
  • 670
  • 4
  • 13
  • 7
    Our program got a change in structure, so old version need to be uninstalled. – Delta76 Jun 02 '10 at 07:26
  • 12
    No it doesnt, you can add entries to your script to handle the structure change during an update. – mlaan Jun 10 '10 at 14:15
  • 1
    @mlaan And what entries would those be? – mythofechelon Sep 02 '13 at 22:21
  • 1
    You could simply use an [`[InstallDelete]`](http://www.jrsoftware.org/ishelp/index.php?topic=installdeletesection) section to remove old files/directories. The new files will then be placed in the correct locations during the installation. – daiscog Feb 28 '14 at 16:23
  • unfortunately inno setup doesn't, for instance, update your icons if you choose to rename some of them. You have to do what daiscog suggested and [basically] manually delete the old icons http://stackoverflow.com/a/22568945/32453 – rogerdpack Mar 21 '14 at 20:31
  • 4
    If you upgrade a third party library like DevExpress, which has version numbers in DLL names (like 15.1 installed before and 15.2 now) then you want to remove the old version. IMHO that's a good reason. – Thomas Weller Dec 07 '16 at 07:23
  • If the old version was 32-bit only and is installed in `\Program Files (x86)` and new version supports 64-bit install mode (`\Program Files`), it might be preferable to uninstall the previous version before installing the new version. (In fact, Windows Installer package upgrades do exactly that.) – Bill_Stewart Jun 26 '19 at 14:21
  • My installer created with Inno Setup installs a Windows Service which then in turn also starts a small HTTP server. These are *not* shut down if running the installer without uninstalling first, which causes the installer to crash (can't overwrite files that are currently running). Running the uninstaller *seems* to be the best way to resolve this issue. – Aaron Cicali Aug 25 '20 at 01:25
  • Inno Setup uses Windows' Restart Manager which will automatically shut down the services before installation, and restart them afterwards. Also, you can (and should) use restartreplace flags. – mlaan Aug 26 '20 at 04:20
3

Here is a simplified version based on answer from Craig McQueen:

const
    UninstallRegisterPath = 'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + '{#emit SetupSetting("AppName")}' + '_is1';

function GetUninstallerPath(): String;
begin
    result := '';
    if (not RegQueryStringValue(HKLM, UninstallRegisterPath, 'UninstallString', result)) then
        RegQueryStringValue(HKCU, UninstallRegisterPath, 'UninstallString', result);
end;

procedure UninstallOldVersion();
var
    UninstallerPath: String;
    ResultCode: Integer;
begin
    UninstallerPath := GetUninstallerPath();
    if (UninstallerPath <> '') then begin
        Exec(UninstallerPath, '/VERYSILENT /NORESTART /SUPPRESSMSGBOXES', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
    end;
end;

procedure CurStepChanged(CurStep: TSetupStep);
begin
    if (CurStep = ssInstall) then
    begin
        UninstallOldVersion();
    end;
end;

Note: in my case, I use AppName instead of AppId.

Xavier Lamorlette
  • 1,152
  • 1
  • 12
  • 20
2

The answer provided by Craig McQueen is totally viable. Although, I would add those comments:

  • The {#emit SetupSetting("AppId")} code does not work for me, so I just add my App ID.
  • I didn't want to execute my uninstallation program, because I have a INI config file stored in the AppData/ folder which is removed by the uninstaller, and I don't want it to be erased when installing a new version. So, I modified a bit the code provided by Craig McQueen to remove the directory where is installed the program, after retrieving its path.

So, regarding the code of Craig McQueen, changes are:

  • Retrieve the InstallLocation key instead of the UninstallString key.
  • Use the DelTree function instead of the Exec(sUnInstallString, ...)
1

For anyone that uses the GetUninstallString() suggested above to force an uninstall inside CurStepChanged() and has disk caching issues, see below for a related solution that actually waits a while after unistallation for the uninstaller exe to be deleted!

Disk caching issue with inno-setup?

Community
  • 1
  • 1
fubar
  • 338
  • 1
  • 6
  • 20
1

For those interested, I wrote a DLL for Inno Setup 6 and later that provides a simple mechanism for supporting automatic uninstall.

The DLL provides a way to detect if the package you are installing is already installed (via AppId) and to decide, based on the installed version, if you want to automatically uninstall it (for example, you might want to automatically uninstall if user is downgrading).

https://github.com/Bill-Stewart/UninsIS

Bill_Stewart
  • 22,916
  • 4
  • 51
  • 62
0

i got edited @Crain Mc-Queen code , i think this code is better because not need to modified in different project :

[Code]
function GetNumber(var temp: String): Integer;
var
  part: String;
  pos1: Integer;
begin
  if Length(temp) = 0 then
  begin
    Result := -1;
    Exit;
  end;
    pos1 := Pos('.', temp);
    if (pos1 = 0) then
    begin
      Result := StrToInt(temp);
    temp := '';
    end
    else
    begin
    part := Copy(temp, 1, pos1 - 1);
      temp := Copy(temp, pos1 + 1, Length(temp));
      Result := StrToInt(part);
    end;
end;

function CompareInner(var temp1, temp2: String): Integer;
var
  num1, num2: Integer;
begin
    num1 := GetNumber(temp1);
  num2 := GetNumber(temp2);
  if (num1 = -1) or (num2 = -1) then
  begin
    Result := 0;
    Exit;
  end;
      if (num1 > num2) then
      begin
        Result := 1;
      end
      else if (num1 < num2) then
      begin
        Result := -1;
      end
      else
      begin
        Result := CompareInner(temp1, temp2);
      end;
end;

function CompareVersion(str1, str2: String): Integer;
var
  temp1, temp2: String;
begin
    temp1 := str1;
    temp2 := str2;
    Result := CompareInner(temp1, temp2);
end;

function InitializeSetup(): Boolean;
var
  oldVersion: String;
  uninstaller: String;
  ErrorCode: Integer;
  vCurID      :String;
  vCurAppName :String;
begin
  vCurID:= '{#SetupSetting("AppId")}';
  vCurAppName:= '{#SetupSetting("AppName")}';
  //remove first "{" of ID
  vCurID:= Copy(vCurID, 2, Length(vCurID) - 1);
  //
  if RegKeyExists(HKEY_LOCAL_MACHINE,
    'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\' + vCurID + '_is1') then
  begin
    RegQueryStringValue(HKEY_LOCAL_MACHINE,
      'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\' + vCurID + '_is1',
      'DisplayVersion', oldVersion);
    if (CompareVersion(oldVersion, '{#SetupSetting("AppVersion")}') < 0) then      
    begin
      if MsgBox('Version ' + oldVersion + ' of ' + vCurAppName + ' is already installed. Continue to use this old version?',
        mbConfirmation, MB_YESNO) = IDYES then
      begin
        Result := False;
      end
      else
      begin
          RegQueryStringValue(HKEY_LOCAL_MACHINE,
            'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\' + vCurID + '_is1',
            'UninstallString', uninstaller);
          ShellExec('runas', uninstaller, '/SILENT', '', SW_HIDE, ewWaitUntilTerminated, ErrorCode);
          Result := True;
      end;
    end
    else
    begin
      MsgBox('Version ' + oldVersion + ' of ' + vCurAppName + ' is already installed. This installer will exit.',
        mbInformation, MB_OK);
      Result := False;
    end;
  end
  else
  begin
    Result := True;
  end;
end;
MohsenB
  • 1,669
  • 18
  • 29
0

You can exec an uninstaller in the [code] section. You have to figure out how to get the path to the existing uninstaller. For simplicity when I install my apps I add a registry string value that points to the folder containing the uninstaller, and just exec the uninstaller in the InitializeWizard callback.

Keep in mind that Inno setup uninstaller names are all of the form uninsnnn.exe, you need to take that into account in your code.

Jim In Texas
  • 1,524
  • 4
  • 20
  • 31
-2

I must be missing something. The new files are copied to the target directory before the removal of the old installation occurs. Then comes the uninstaller deletes them and remove the directory.

Shaul
  • 437
  • 5
  • 17
  • 2
    I'm not sure what you're trying to say, but note that it is not always just about copying files. Imagine that you'd have installed your product, which with the next release comes with totally changed file structure, where many of the original files were removed and new files have different names and are stored in different directories. What would be the easiest way to upgrade ? Wouldn't that be uninstalling previous version ? – TLama Apr 02 '14 at 08:10
  • I use INNO to install a driver and it's accompanying applications. Naturally, the actual driver Installation/Removal is not done directly by INNO. Rather, INNO copies a driver installer/remover app then runs it. Uninstalling done similarly: INNO runs the driver remover, then deletes files. – Shaul Apr 02 '14 at 12:36
-8

Do not use the [Run] section, but the [UninstallRun]. Infact, the program under [Run] are executed after the installation, causing to uninstall your program immediately after the installation :-| Instead, the [UninstallRun] section is evaluated before the installation.

-9

Follow this link: http://news.jrsoftware.org/news/innosetup/msg55323.html

In InitializeSetup() function, you can call "MSIEXEC /x {your program ID}" after user prompt to uninstall old old version