在安装开始时卸载先前版本的产品会导致 Inno Setup 中的安装损坏

时间:2021-06-24 10:17:16

标签: inno-setup

我想使用 Inno Setup 删除旧的程序实例。 该程序不必安装在系统的特定部分才能运行,但我想制定一个标准并通过复制文件来删除就在那里的旧实例。由于发生了一些更改,并且我想从注册表中删除以前安装的条目(32 位和 64 位安装是可能的,因此如果您想要两个安装程序,同一程序可能存在两个条目),我在假设中写了这个在 PrepareToInstall 部分运行卸载程序并删除文件后,安装将开始。

{ This is the part that is executed after you started the install }
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
  ResultCode: integer;
  UninstallString: String;
  AppId: String;
begin
  AppId := '{#SetupSetting("AppId")}';
  AppId := Copy(AppId, 2,  Length(AppId) - 1);
  if (IsWin64 And RegQueryStringValue(HKEY_LOCAL_MACHINE_64, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\'+AppId+'_is1', 'UninstallString', UninstallString)) then begin
    Exec(Copy(UninstallString, 2,  Length(UninstallString) - 2), '/VERYSILENT', '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
    if RegKeyExists(HKEY_LOCAL_MACHINE_64, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\'+AppId+'_is1') then begin
      RegDeleteKeyIncludingSubkeys(HKEY_LOCAL_MACHINE_64, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\'+AppId+'_is1')
    end;
  end;
  if (RegQueryStringValue(HKEY_LOCAL_MACHINE_32, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\'+AppId+'_is1', 'UninstallString', UninstallString)) then begin
    Exec(Copy(UninstallString, 2,  Length(UninstallString) - 2), '/VERYSILENT', '', SW_HIDE, ewWaitUntilTerminated, ResultCode)
    if RegKeyExists(HKEY_LOCAL_MACHINE_32, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\'+AppId+'_is1') then begin
      RegDeleteKeyIncludingSubkeys(HKEY_LOCAL_MACHINE_32, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\'+AppId+'_is1')
    end;
  end;

  If (DirIsSysLink(ExpandConstant('{app}')) = True) Then begin
    RenameFile(ExpandConstant('{app}'), ExpandConstant('{app}_link'));
    CreateDir(ExpandConstant('{app}'));
    DirectoryCopy(ExpandConstant('{app}_link'), ExpandConstant('{app}'));
    DelTree(ExpandConstant('{app}_link'), True, True, True);
  End;

  If (IsWin64) Then Begin
    If (DirExists('C:\Program Files (x86)\TargetProgram')) then begin 
      CleanupFolder('C:\Program Files (x86)\TargetProgram', 'X86') 
      If (DirIsSysLink('C:\Program Files (x86)\TargetProgram') = False) Then begin
        DelTree('C:\Program Files (x86)\TargetProgram', True, True, True)
      end
    end;
  End;
  
  If (DirExists('C:\Program Files\TargetProgram')) then begin
    CleanupFolder('C:\Program Files\TargetProgram', 'X64')
  end;
end;

{ Tries to remove existing installations and additional files in the folders that don't belong here before install starts }
function CleanupFolder(Folder: String; Num: String): Boolean;
var
  BolTmpVal: Boolean;
  FindRec: TFindRec;
  FileList: String;
begin
  BolCopyFailed := False;
  If (DirExists(Folder)) then begin
    FileList := '|a.file|b.dll|...|'
    if (FindFirst(Folder+'\*', FindRec)) then begin
      try
        repeat
          if (Pos('|'+LowerCase(FindRec.Name)+'|', FileList) <> 0) then begin//this is a file known to be installed later
            DeleteFile(Folder+'\'+FindRec.Name);
          end Else if ((Pos('unins0', FindRec.Name) = 1) And ((Pos('.exe', FindRec.Name) = 9) OR (Pos('.dat', FindRec.Name) = 9))) Then begin
            {Deleting the uninstall files seems to remove the actual installer itself...}
            //DeleteFile(Folder+'\'+FindRec.Name);
          End Else If ((FindRec.Name = '.') OR (FindRec.Name = '..')  OR (LowerCase(FindRec.Name) = 'backup')) Then Begin
            // do nothing with main directories or backup folder
          end Else begin
            //... some copy and paste backup of possible user-files not involved in the problem
          end;
        until not FindNext(FindRec);
      finally
        FindClose(FindRec);
      end;
    end;
  end;
end;

如果我用它来清除旧的安装,安装有时只包含 15 个应该安装的文件中的 8 个(在 [Files] 中定义,我可以看到它们被添加到编译器输出和通常安装)。所以我唯一的猜测为什么我得到的文件少于 15 个是应该在预安装过程中删除的文件在安装过程中被删除。因此,如果我没有记错的话,所需的清理工作必须删除安装文件。但我找不到原因。

我认为安装本身在准备部分之后将内容放在目标文件夹中。那么我错了/如何修改代码以使安装有效?我知道我可以通过在安装程序结束前检查来测试这一点,但我想这不是一个神的练习......

我之前卸载的安装是这样的:

[Files]
Source: "Input\*"; DestDir: "{app}"; \
    Flags: ignoreversion recursesubdirs createallsubdirs 

[UninstallDelete]
Type: dirifempty; Name: "{app}" Type: dirifempty; Name: "C:\Program Files\A"

[UninstallRun]
Filename: "{sys}\taskkill.exe"; Parameters: "/F /IM a.exe"; Flags:runhidden ; \
    StatusMsg: "Closing process"

1 个答案:

答案 0 :(得分:0)

主卸载程序进程仅在临时文件夹中创建自己的副本,并运行子进程进行实际卸载。

因此,您代码中的 Exec,尽管有 ewWaitUntilTerminated,但实际上并不等待安装完成。因此可能会发生卸载程序和安装程序同时运行的情况。这自然会导致安装损坏。

您必须等待子卸载程序完成才能解决您的问题。快速而肮脏的解决方案是查找名称包含 _iu 的任何进程。见Inno Setup Pascal Script to search for running process

相关问题