Skip to content

Inno setup – restore files on uninstall

September 9, 2013

Suppose you are using an Inno Setup project to replace existing files. When running the uninstaller it will delete the new files, instead of the expected behavior of restoring the previous ones.
Here is my implementation of a backup & restore mechanism:

1. For each file to be backed up, add a ‘Check’ parameter with the name of your backup function. This function will be called just before installing the file so we can use it to perform the backup.

2. Implement the backup function to perform the backup. Make sure to return True if everything’s OK and the file can be replaced.

3. Implement the restore function by copying all files from backup to the original location.

4. Call the restore function during uninstall (post-uninstall, after the new files have been removed).

Here is a sample Inno Setup script that implements this mechanism. In this example, the backup function supports backing up the entire folder structure, so restoring them to the original location is easy (using xcopy).

; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define MyAppName "TestBackupRestore"
#define MyAppVersion "1.0"
#define MyAppPublisher "yossiz74"
#define MyAppURL "https://adventuresinscm.wordpress.com/"
[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{DA07C52A-E107-4F08-897E-9394690B7C7F}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={pf}\{#MyAppName}
DisableDirPage=yes
DefaultGroupName={#MyAppName}
DisableProgramGroupPage=yes
OutputBaseFilename=setup
Compression=lzma
SolidCompression=yes
SetupLogging=yes
CreateUninstallRegKey=no

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
[Dirs]
Name: "{app}\log"
Name: "{app}\conf"
[Files]
Source: "F:\Temp\StructuredQuery.log"; DestDir: "{app}\log"; Flags: ignoreversion; Check: FileBackup
Source: "F:\Temp\browserinfo.ini"; DestDir: "{app}\conf"; Flags: ignoreversion; Check: FileBackup
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons]
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
[Code]
var BackupDir: String;
function FileBackup(): Boolean; 
  var FileName,Source,Target,TargetDir: String; 
begin 
  Result := True; 
  Source := ExpandConstant(CurrentFileName);
  if not FileExists(Source) then begin
    Result := True;
  end else begin
    FileName := ExtractRelativePath(ExpandConstant('{app}') + '\',Source); 
    Target := BackupDir + '\' + FileName;
    TargetDir := ExtractFilePath(Target);
    if not DirExists(TargetDir) then
      if not CreateDir(TargetDir) then
        Result := False 
    if Result then begin
      if FileExists(Target) then // Keep original backup
        Result := True
      else
        Result := FileCopy(Source,Target,False); 
    end; 
  end;
end;
function RestoreFiles(): Boolean;
 var ResultCode: Integer;
 var CommandLine: String;
begin
  CommandLine := '/sry "' + BackupDir + '"\* "' + ExpandConstant('{app}') + '"';
  Exec('xcopy', CommandLine, '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
  Result := (ResultCode = 0);
end;
procedure CurStepChanged(CurStep: TSetupStep);
begin
  if CurStep = ssInstall then 
  begin
    BackupDir := ExpandConstant('{app}\Backup');
    CreateDir(BackupDir);
  end;
end;
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
  if CurUninstallStep = usUninstall then
    BackupDir := ExpandConstant('{app}\Backup');
  if CurUninstallStep = usPostUninstall then
    RestoreFiles();
end;
Advertisements

From → Inno Setup

6 Comments
  1. when i try to use your code.. it shows an error on line ” if Result then begin”..
    can you please help me with it? thank a lot..

    • Note sure, I’ve just copied the code from the post to a completely new empty script and it builds successfully.
      I’ve sent you an email so we can take it offline.

  2. Sampan Kumar permalink

    Many thanks for this wonderful code block, It works great. Just a small question though, to make a backup of files, the code looks for the filename existing in the Source and then look for same file in {app} directory. How to deal with cases where {app} directories has extra files (probably the files which were deleted in Source in new installation), as those filenames are not present in Source, hence the backup does not get those files.

    Many thanks again, looking forward to your suggestion.

    • If I understand your question correctly, that depends on your upgrade policy.
      The mechanism I suggested is intended for an ‘in-place’ upgrade – so files which existed beforehand, but are not in Source, would simply remain there, and nothing got lost.
      However if you delete the {app} folder before installing the updated files, then instead of this method you will need to backup the entire folder (and restore it upon rollback), since you cannot know in advance which files currently exist there. Of course, if you know that you should only backup certain files (e.g. based on specified wildcards) you can write a code to scan the existing folders, and backup all those files.
      I hope this answers your question.

  3. Hi, I would like to know how to delete the backup after it has been restored.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: