Inno Setup:删除以前版本安装的文件

Rob*_*ume 3 java installation inno-setup jar upgrade

我正在使用Inno Setup为 Windows 打包 Java 应用程序;应用程序树是这样的:

|   MyApp.jar
\---lib
    |   dependency-A-1.2.3.jar
    |   dependency-B-2.3.4.jar
    |   dependency-Z-x.y.z.jar
Run Code Online (Sandbox Code Playgroud)

我使用Ant预先准备整个树(所有文件和文件夹),包括lib目录(使用*.jar通配符复制依赖项),然后我只需调用ISCC

[Files]
Source: "PreparedFolder\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
Run Code Online (Sandbox Code Playgroud)

现在,每次用户升级应用程序时我都需要清理lib目录,因为我想删除任何过时的依赖项。我可以将以下部分添加到我的.iss文件中:

[InstallDelete]
{app}\lib\*.jar
Run Code Online (Sandbox Code Playgroud)

但我觉得不安全,因为如果用户决定将应用程序安装在包含非空文件夹的现有文件夹中 lib文件夹(罕见但并非不可能),则升级时可能会删除某些用户文件。

是否有一些最佳实践可以避免这种麻烦?其他安装人员是否会解决这些令人头疼的问题?谢谢。

Mar*_*ryl 5

您可以在安装前卸载以前的版本:


如果无法完全卸载,则必须实施部分卸载。

理想的做法是对卸载程序日志 ( unins000.dat)进行逆向工程,仅将安装提取到lib子文件夹并处理(撤消)它们。但由于这是一个未记录的二进制文件,因此可能很难做到。


如果您维护要在该[Files]部分中安装的明确文件列表,例如

[Files]
Source: "lib\dependency-A-1.2.3.jar"; Dest: "{app}\lib"
Source: "lib\dependency-B-2.3.4.jar"; Dest: "{app}\lib"
Run Code Online (Sandbox Code Playgroud)

然后每当依赖项发生变化时,将以前的版本移到该[InstallDelete]部分:

[Files]
Source: "lib\dependency-A-1.3.0.jar"; Dest: "{app}"
Source: "lib\dependency-B-2.3.4.jar"; Dest: "{app}"

[InstallDelete]
{app}\lib\dependency-A-1.2.3.jar
Run Code Online (Sandbox Code Playgroud)

如果使用通配符安装依赖项,

[Files]
Source: "lib\*.jar"; Dest: "{app}\lib"
Run Code Online (Sandbox Code Playgroud)

并且您无法对卸载程序日志进行逆向工程,您必须通过自己的方式复制其功能。

您可以使用预处理器生成已安装依赖项的文件。将该文件安装到该{app}文件夹并在安装前处理该文件。

[Files]
Source: "MyApp.jar"; DestDir: "{app}"
Source: "lib\*.jar"; DestDir: "{app}\lib"

#define ProcessFile(Source, FindResult, FindHandle) \
    Local[0] = FindGetFileName(FindHandle), \
    Local[1] = Source + "\\" + Local[0], \
    Local[2] = FindNext(FindHandle), \
    "'" + Local[0] + "'#13#10" + \
        (Local[2] ? ProcessFile(Source, Local[2], FindHandle) : "")

#define ProcessFolder(Source) \
    Local[0] = FindFirst(Source + "\\*.jar", faAnyFile), \
    ProcessFile(Source, Local[0], Local[0])

#define DepedenciesToInstall ProcessFolder("lib")
#define DependenciesLog "{app}\dependencies.log"

[UninstallDelete]
Type: files; Name: "{#DependenciesLog}"
Run Code Online (Sandbox Code Playgroud)
[Code]

procedure CurStepChanged(CurStep: TSetupStep);
var
  AppPath, DependenciesLogPath: string;
  Dependencies: TArrayOfString;
  Count, I: Integer;
begin
  DependenciesLogPath := ExpandConstant('{#DependenciesLog}');

  if CurStep = ssInstall then
  begin
    { If dependencies log already exists, remove the previously installed dependencies }
    if LoadStringsFromFile(DependenciesLogPath, Dependencies) then
    begin
      Count := GetArrayLength(Dependencies);
      Log(Format('Loaded %d dependencies, deleting...', [Count]));
      for I := 0 to Count - 1 do
        DeleteFile(ExpandConstant('{app}\lib\' + Dependencies[I]));
    end;
  end
    else
  if CurStep = ssPostInstall then
  begin
    { Now that the app folder already exists, }
    { save dependencies log (to be processed by future upgrade) }
    if SaveStringToFile(DependenciesLogPath, {#DepedenciesToInstall}, False) then
    begin
      Log('Created dependencies log');
    end
      else
    begin
      Log('Failed to create dependencies log');
    end;
  end;
end;
Run Code Online (Sandbox Code Playgroud)

另一种方法是删除安装文件夹中所有不是最新安装程序安装的文件。

最简单的解决方法是在安装前删除安装文件夹中的所有文件。

您可以使用[InstallDelete]部分。但是,如果您在安装文件夹中有一些带有配置的文件夹/文件,则不允许您排除它们。

您可以改为编写 Pascal 脚本。请参阅Inno Setup - 删除除数据子目录之外的整个应用程序文件夹。您可以DelTreeExceptSavesDir从我对CurStepChanged(ssInstall)事件函数的那个问题的回答中调用该函数:

procedure CurStepChanged(CurStep: TSetupStep);
begin
  if CurStep = ssInstall then
  begin
    DelTreeExceptSavesDir(WizardDirValue); 
  end;
end;
Run Code Online (Sandbox Code Playgroud)

如果您真的只想删除过时的文件,以避免删除和重新创建现有文件,您可以使用预处理器生成要为 Pascal Scripting 安装的文件列表,并使用它来仅删除真正过时的文件。

#pragma parseroption -p-

#define FileEntry(DestDir) \
    "  FilesNotToBeDeleted.Add('" + LowerCase(DestDir) + "');\n"

#define ProcessFile(Source, Dest, FindResult, FindHandle) \
    FindResult \
        ? \
            Local[0] = FindGetFileName(FindHandle), \
            Local[1] = Source + "\\" + Local[0], \
            Local[2] = Dest + "\\" + Local[0], \
            (Local[0] != "." && Local[0] != ".." \
                ? FileEntry(Local[2]) + \
                  (DirExists(Local[1]) ? ProcessFolder(Local[1], Local[2]) : "") \
                : "") + \
            ProcessFile(Source, Dest, FindNext(FindHandle), FindHandle) \
        : \
            ""

#define ProcessFolder(Source, Dest) \
    Local[0] = FindFirst(Source + "\\*", faAnyFile), \
    ProcessFile(Source, Dest, Local[0], Local[0])

#pragma parseroption -p+

[Code]

var
  FilesNotToBeDeleted: TStringList;

function InitializeSetup(): Boolean;
begin
  FilesNotToBeDeleted := TStringList.Create;
  FilesNotToBeDeleted.Add('\data');
  {#Trim(ProcessFolder('build\exe.win-amd64-3.6', ''))}
  FilesNotToBeDeleted.Sorted := True;

  Result := True;
end;

procedure DeleteObsoleteFiles(Path: string; RelativePath: string);
var
  FindRec: TFindRec;
  FilePath: string;
  FileRelativePath: string;
begin
  if FindFirst(Path + '\*', FindRec) then
  begin
    try
      repeat
        if (FindRec.Name <> '.') and (FindRec.Name <> '..') then
        begin
          FilePath := Path + '\' + FindRec.Name;
          FileRelativePath := RelativePath + '\' + FindRec.Name;
          if FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY <> 0 then
          begin
            DeleteObsoleteFiles(FilePath, FileRelativePath);
          end;

          if FilesNotToBeDeleted.IndexOf(Lowercase(FileRelativePath)) < 0 then
          begin
            if FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY <> 0 then
            begin
              if RemoveDir(FilePath) then
              begin
                Log(Format('Deleted obsolete directory %s', [FilePath]));
              end
                else
              begin
                Log(Format('Failed to delete obsolete directory %s', [FilePath]));
              end;
            end
              else
            begin
              if DeleteFile(FilePath) then
              begin
                Log(Format('Deleted obsolete file %s', [FilePath]));
              end
                else
              begin
                Log(Format('Failed to delete obsolete file %s', [FilePath]));
              end;
            end;
          end;
        end;
      until not FindNext(FindRec);
    finally
      FindClose(FindRec);
    end;
  end
    else
  begin
    Log(Format('Failed to list %s', [Path]));
  end;
end;

procedure CurStepChanged(CurStep: TSetupStep);
begin
  if CurStep = ssInstall then
  begin
    Log('Looking for obsolete files...');
    DeleteObsoleteFiles(WizardDirValue, '');
  end;
end;
Run Code Online (Sandbox Code Playgroud)