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

2024-01-13

我在用着创新设置 http://www.jrsoftware.org/isinfo.php打包适用于 Windows 的 Java 应用程序;应用程序树是这样的:

|   MyApp.jar
\---lib
    |   dependency-A-1.2.3.jar
    |   dependency-B-2.3.4.jar
    |   dependency-Z-x.y.z.jar

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

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

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

[InstallDelete]
{app}\lib\*.jar

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

有没有最佳实践可以避免此类麻烦?其他安装人员可以解决这些令人头痛的问题吗?谢谢。


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

  • Inno Setup:如何自动卸载以前安装的版本? https://stackoverflow.com/q/2000296/850848
  • 如何检测旧安装并提供删除? https://stackoverflow.com/q/11739317/850848

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

理想的情况是对卸载程序日志进行逆向工程(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"

然后每当依赖项发生变化时,将以前的版本移动到[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

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

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

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

您可以使用预处理器 https://jrsoftware.org/ispphelp/生成一个包含已安装依赖项的文件。将该文件安装到{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}"
[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;

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

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

您可以使用[InstallDelete] section https://jrsoftware.org/ishelp/index.php?topic=installdeletesection为了那个原因。但是,如果您在安装文件夹中有一些带有配置的文件夹/文件,则不允许您排除它们。

您可以改为编写 Pascal 脚本。看Inno Setup - 删除除数据子目录之外的整个应用程序文件夹 https://stackoverflow.com/q/36491213/850848。您可以致电DelTreeExceptSavesDir从我对这个问题的回答来看事件函数:

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

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

#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;
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐

  • 拍摄嵌入式 Linux 帧缓冲区的屏幕截图

    我在评估套件 Zoom OMAP35x Torpedo 开发套件 上运行嵌入式 Linux 该板有一个 LCD 我希望能够拍摄屏幕截图并将其转换为 gif 或 png 我可以通过执行以下操作来获取原始数据 cp dev fb0 screen
  • jqgrid搜索/过滤器

    这与我的最新问题有关link https stackoverflow com questions 5775022 edited how to display the searched data on the jgrid 我已经弄清楚错误是什
  • 在 Angular UI Bootstrap 中打开模式时出现 404 Not Found 错误

    我在使用 UI Bootstrap 时遇到一个非常奇怪的问题 即使是最简单的例子也不起作用 这就是我的情况 我有一个使用 yeoman 插件的 Play Framework 应用程序 https github com tuplejump p
  • Python:无法捕获 IndexError

    免责声明 这看起来像是重复的 但找到这个特定问题的答案不仅仅是微不足道的 我希望其他人能更轻松地找到这个问题 答案 当我运行以下代码时 它无法捕获第二个 IndexError 而是引发它 try raise ValueError excep
  • 如何向 Xbox Live Restful API 发送请求?

    我想向 Xbox Live Restful API 的配置文件 URI 和人员 URI 发送请求 我无法理解这两个接口的授权部分 POST 用户 批 配置文件 设置 https learn microsoft com en us windo
  • 在azure构建管道中,一旦推送,如何从自托管代理清理docker镜像(作为管道的一部分创建)?

    我正在使用自托管代理来运行我的构建管道 代理机器已下载基础 docker 映像 管道使用第一个任务构建并推送新的 docker 映像 基于基础映像的顶部 如以下屏幕截图所示 一旦构建了映像并将其推送到容器注册表 我希望从自托管代理中清理映像
  • 覆盖 Django Forms 中的标签

    我有 3 个具有相同字段的部分 除了 标题 字段上的标签 对于所有这些 我都使用相同的 Django 表单 在我的观点中 def get self context self CONTEXT CLASS self MODEL CLASS co
  • 使用 Microsoft 认知语音 API 和非麦克风实时音频流进行语音识别

    Problem 我的项目由一个实时录制音频的桌面应用程序组成 我打算为此接收来自 API 的实时识别反馈 与一个麦克风 使用 Microsoft 新的 Speech to Text API 进行实时实现是微不足道的 我的场景与此的不同之处仅
  • 如何防止 Angular 中的内存泄漏

    我们有一个具有复杂且冗长视图的应用程序 一个视图大约有 50 个具有自己的服务 订阅和行为的组件 组件有自己的变量 包括数组和表单组 问题 从 View2 Component2 导航回 View1 Component1 后 我仍然在 chr
  • 我在 python 中遇到关键错误

    在我的 python 程序中 我收到此错误 KeyError variablename 从这段代码来看 path meta entry path strip 任何人都可以解释为什么会发生这种情况吗 A KeyError https docs
  • 在 scrapy/python 中创建循环来解析表数据

    使用 scrapy 编写 python 脚本 从网站抓取数据 将其分配给 3 个字段 然后生成 csv 工作正常 但有一个主要问题 所有字段都包含所有数据 而不是针对每个表行将其分开 我确信这是由于我的循环不起作用 当它找到 xpath 时
  • 为什么我不能在 ASP.NET 中的 HTML 属性内使用 ContentPlaceholder?

    我正在使用 ASP NET MVC2 和 C 但这个问题通常适用于 ASP NET 这打破了 gt 智能感知强调body标签和紧随其后的开头报价id 并抱怨 验证 HTML 4 01 元素 body 的开始标记中缺少 gt 字符 asp 元
  • Cloud Foundry 上的 HTTPS 节点应用程序

    是否可以在 Cloud Foundry 上部署一个 node js 应用程序来侦听端口 443 上的 HTTPS 请求 我可以在 Cloud Foundry 论坛中找到有关 SSL 支持的各种参考 但没有 HTTPS 应用程序的实际示例 文
  • 在 Python/Win32 中打开文档时如何关闭 MS Word 显示的对话框

    我正在尝试编写一个脚本 该脚本将遍历文件夹中所有 ms word 文档的内容并收集一些信息 我使用 Python 2 7 3 和 Ms Office 2007 我遇到的问题是 有时打开文档时 MS Word 会弹出警告 错误窗口 错误消息的
  • 如何从预训练模型加载保存的分词器

    我使用 Huggingface 转换器在 Pytorch 中微调了预训练的 BERT 模型 所有训练 验证都是在云中的 GPU 上完成的 训练结束时 我保存模型和分词器 如下所示 best model save pretrained sav
  • 我可以随机化 MultipleChoiceItem 中的项目吗? [复制]

    这个问题在这里已经有答案了 我是 GoogleAppsScript 的新手 现在使用 GAS 以 Google 表单和电子表格进行测验 我想在重新加载 google 表单时随机排列 MultipleChoiceItem 中的项目 我当前脚本
  • 何时在 Packer 与 Terraform 中进行配置?

    我遇到的情况是 我需要在启动时为 EC2 实例配置一些软件包 存在一些 企业 公司 约束 我需要在特定 AMI 之上进行配置 这会添加 LDAP AD 访问等企业内容 这些更改旨在用于所有内部开发机器 主要由于第二个限制 我想知道放置配置的
  • 在现有的 apache Web 服务器上运行 slim 框架

    我是 slim 框架的新手 目前 在我现有的 centos 7 网络服务器上 我正在运行其他基于 php 的应用程序 所以目前我的目录结构是这样的 var www html phpapp1 phpapp2 apislim 我创建的 apis
  • 如何在C++中检查Qt中文件是否存在

    Qt 中如何检查给定路径中是 否存在文件 我当前的代码如下 QFile Fout Users Hans Desktop result txt if Fout exists eh handleError 8 else 但是当我运行代码时 它没
  • Inno Setup:删除以前版本安装的文件

    我在用着创新设置 http www jrsoftware org isinfo php打包适用于 Windows 的 Java 应用程序 应用程序树是这样的 MyApp jar lib dependency A 1 2 3 jar depe