InnoSetup: don't uninstall changed files

后端 未结 2 2082
滥情空心
滥情空心 2021-02-06 03:17

How to tell InnoSetup to not uninstall (text) files which had been changed by the user (== are different from those installed by InnoSetup)?

Or maybe more difficult: whe

2条回答
  •  忘掉有多难
    2021-02-06 03:53

    I recently had a similar problem. This was my solution to detect if a text file (profile) has been changed from the one installed during the last installation run:

    Use ISPP (Inno Setup Pre-Processor) to create the list of text files and their hashes at compile time:

    [Files]
    ; ...
    #define FindHandle
    #define FindResult
    #define Mask "Profiles\*.ini"
    #sub ProcessFoundFile
       #define FileName "Profiles\" + FindGetFileName(FindHandle)
       #define FileMd5 GetMd5OfFile(FileName)
       Source: {#FileName}; DestDir: {app}\Profiles; Components: profiles; \
          Check: ProfileCheck('{#FileMd5}'); AfterInstall: ProfileAfterInstall('{#FileMd5}');
    #endsub
    #for {FindHandle = FindResult = FindFirst(Mask, 0); FindResult; FindResult = FindNext(FindHandle)} ProcessFoundFile
    

    At the top of the "Code" section I define some useful things:

    [Code]
    var
       PreviousDataCache : tStringList;
    
    function InitializeSetup() : boolean;
    begin
       // Initialize global variable
       PreviousDataCache := tStringList.Create();
       result := TRUE;
    end;
    
    function BoolToStr( Value : boolean ) : string;
    begin
       if ( not Value ) then
          result := 'false'
       else
          result := 'true';
    end;
    

    In the "Check" event handler I compare the hashes of previous install and current file:

    function ProfileCheck( FileMd5 : string ) : boolean;
    var
       TargetFileName, TargetFileMd5, PreviousFileMd5 : string;
       r : integer;
    begin
       result := FALSE;
       TargetFileName := ExpandConstant(CurrentFileName());
       Log('Running check procedure for file: ' + TargetFileName);
    
       if not FileExists(TargetFileName) then
       begin
          Log('Check result: Target file does not exist yet.');
          result := TRUE;
          exit;
       end;
    
       try
          TargetFileMd5 := GetMd5OfFile(TargetFileName);
       except
          TargetFileMd5 := '(error)';
       end;
       if ( CompareText(TargetFileMd5, FileMd5) = 0 ) then
       begin
          Log('Check result: Target matches file from setup.');
          result := TRUE;
          exit;
       end;
    
       PreviousFileMd5 := GetPreviousData(ExtractFileName(TargetFileName), '');
       if ( PreviousFileMd5 = '' ) then
       begin
          r := MsgBox(TargetFileName + #10#10 + 
             'The existing file is different from the one Setup is trying to install. ' + 
             'It is recommended that you keep the existing file.' + #10#10 +
             'Do you want to keep the existing file?', mbConfirmation, MB_YESNO);
          result := (r = idNo);
          Log('Check result: ' + BoolToStr(result));
       end
       else if ( CompareText(PreviousFileMd5, TargetFileMd5) <> 0 ) then
       begin
          r := MsgBox(TargetFileName + #10#10 + 
             'The existing file has been modified since the last run of Setup. ' +
             'It is recommended that you keep the existing file.' + #10#10 +
             'Do you want to keep the existing file?', mbConfirmation, MB_YESNO);
          result := (r = idNo);
          Log('Check result: ' + BoolToStr(result));
       end
       else
       begin
          Log('Check result: Existing target has no local modifications.');
          result := TRUE;
       end;
    end;
    

    In the "AfterInstall" event handler I mark the file hash to be stored in Registry later. Because in my tests the event was triggered even if the file move failed (target file is read-only) I compare the hash again to find out if the file move was successful:

    procedure ProfileAfterInstall( FileMd5 : string );
    var
       TargetFileName, TargetFileMd5 : string;
    begin
       TargetFileName := ExpandConstant(CurrentFileName());
       try
          TargetFileMd5 := GetMd5OfFile(TargetFileName);
       except
          TargetFileMd5 := '(error)';
       end;
       if ( CompareText(TargetFileMd5, FileMd5) = 0 ) then
       begin
          Log('Storing hash of installed file: ' + TargetFileName);
          PreviousDataCache.Add(ExtractFileName(TargetFileName) + '=' + FileMd5);
       end;
    end;
    
    procedure RegisterPreviousData( PreviousDataKey : integer );
    var
       Name, Value : string;
       i, n : integer;
    begin
       for i := 0 to PreviousDataCache.Count-1 do
       begin
          Value := PreviousDataCache.Strings[i];
          n := Pos('=', Value);
          if ( n > 0 ) then
          begin
             Name := Copy(Value, 1, n-1);
             Value := Copy(Value, n+1, MaxInt);
             SetPreviousData(PreviousDataKey, Name, Value);
          end;
       end;
    end;
    

提交回复
热议问题