Inno Setup:删除以前版本安装的文件 [英] Inno Setup: Removing files installed by previous version

查看:213
本文介绍了Inno Setup:删除以前版本安装的文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  | MyApp.jar 
\ --- lib
| dependency-A-1.2.3.jar
| dependency-B-2.3.4.jar
| dependency-Zx.yzjar

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

  [Files] 
来源:PreparedFolder \ *; DestDir:{app};标志:ignoreversion recursesubdirs

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

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

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



是否有一些最佳做法可以避免这种麻烦?其他安装人员是否会处理这些令人头疼的问题?谢谢。

解决方案

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








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



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






如果你维护一个明确的文件列表安装在 [Files] 部分,例如

  [Files] 
来源:lib\dependency-A-1.2.3.jar;目的地:{app} \ lib
来源:lib\dependency-B-2.3.4.jar;目的地:{app} \ lib

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

  [Files] 
来源:lib\dependency-A-1.3.0.jar;目的地:{app}
来源:lib\dependency-B-2.3.4.jar;目的地:{app}

[InstallDelete]
{app} \lib \ dependency-A-1.2.3.jar






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

  [Files] 
来源:lib\ * .jar;目的地:{app} \ lib

你无法对卸载程序日志进行反向工程,你必须通过你自己的方式复制它的功能。



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

  [Files] 
来源:MyApp.jar; DestDir:{app}
来源:lib \ * .jar; DestDir:{app} \ lt

#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]
类型:files;名称:{#DependenciesLog}



  [代码] 

程序CurStepChanged(CurStep:TSetupStep);
var
AppPath,DependenciesLogPath:string;
依赖关系:TArrayOfString;
伯爵,I:整数;
begin
DependenciesLogPath:= ExpandConstant('{#DependenciesLog}');

如果CurStep = ssInstall然后
开始
{如果依赖项日志已存在,则删除以前安装的依赖项}
如果LoadStringsFromFile(DependenciesLogPath,Dependencies)则
开始
计数:= GetArrayLength(依赖关系);
Log(格式化('加载%d个依赖项,删除...',[计数]));
for I:= 0 to Count - 1 do
DeleteFile(ExpandConstant('{app} \lib \'+ Dependencies [I]));
结束;
结束
否则
如果CurStep = ssPostInstall然后
开始
{现在app文件夹已经存在,}
{save dependencies log(要处理)通过将来的升级)}
如果SaveStringToFile(DependenciesLogPath,{#DeistenSoInstall},False)则
开始
Log('Created dependencies log');
end
else
begin
Log('创建依赖项日志失败');
结束;
结束;
结束;






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



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



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



您可以编写Pascal脚本代码。请参阅 Inno Setup - 删除除数据子目录以外的整个应用程序文件夹。您可以从我对 DelTreeExceptSavesDir 函数scriptevents& anchor = CurStepChangedrel =nofollow noreferrer> CurStepChanged(ssInstall) 事件函数:

  procedure CurStepChanged(CurStep:TSetupStep); 
如果CurStep = ssInstall则开始
然后
开始
DelTreeExceptSavesDir(WizardDirValue);
结束;
结束;






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



< pre class =lang-pascal prettyprint-override> #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 +

[代码]

var
FilesNotToBeDeleted:TStringList;

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

结果:=真;
结束;

过程DeleteObsoleteFiles(Path:string; RelativePath:string);
var
FindRec:TFindRec;
FilePath:string;
FileRelativePath:string;
开始
如果FindFirst(路径+'\ *',FindRec)然后
开始
尝试
重复
if(FindRec.Name<> ;'。')和(FindRec.Name<>'..')然后
开始
FilePath:= Path +'\'+ FindRec.Name;
FileRelativePath:= RelativePath +'\'+ FindRec.Name;
如果FindRec.Attributes和FILE_ATTRIBUTE_DIRECTORY<> 0然后
开始
DeleteObsoleteFiles(FilePath,FileRelativePath);
结束;如果FilesNotToBeDeleted.IndexOf(小写(FileRelativePath))<

0然后
如果FindRec.Attributes和FILE_ATTRIBUTE_DIRECTORY<> ;,则开始
0然后
开始
如果RemoveDir(FilePath)然后
开始
Log(格式化('删除过时目录%s',[FilePath]));
end
else
begin
Log(格式('无法删除过时目录%s',[FilePath]));
结束;
end
else
begin
如果DeleteFile(FilePath)然后
开始
Log(格式化('删除过时文件%s',[FilePath]) );
end
else
begin
Log(格式('无法删除过时的文件%s',[FilePath]));
结束;
结束;
结束;
结束;
直到不是FindNext(FindRec);
最后
FindClose(FindRec);
结束;
end
else
begin
Log(格式('无法列出%s',[路径]));
结束;
结束;

程序CurStepChanged(CurStep:TSetupStep);
如果CurStep = ssInstall则开始
然后
开始
Log('寻找过时的文件......');
DeleteObsoleteFiles(WizardDirValue,'');
结束;
结束;


I'm using Inno Setup to package a Java application for Windows; the application tree is like this:

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

I use Ant to prepare the whole tree (all the files and folders) beforehand, including the lib directory (using *.jar wildcard to copy the dependencies), then I simply call ISCC with:

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

Now, I need to cleanup the lib directory everytime the user upgrades the application because I want to remove any obsolete dependencies. I could add the following section to my .iss file:

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

but I'm not feeling safe because if a user decides to install the application in an existing folder that contains a not-empty lib subfolder (rare but not impossible), there is a chance that some user files are deleted on upgrade.

Are there some best practices to avoid this kind of troubles? Do other installers take care of these headaches? Thanks.

解决方案

You can uninstall the previous version before the installation:


If you cannot do a complete uninstallation, you would have to implement a partial uninstallation.

Ideal would be to reverse-engineer the uninstaller log (unins000.dat), extract only installations to the lib subfolder and process (undo) them. But as that is an undocumented binary file, it can be difficult to do.


If you maintain an explicit list of files to be installed in the [Files] section, like

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

then whenever a dependency changes, move the previous version to the [InstallDelete] section:

[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


If you install the dependencies using a wildcard,

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

and you cannot reverse-engineer the uninstaller log, you would have to replicate its functionality by your own means.

You can use a preprocessor to generate a file with installed dependencies. Install that file to the {app} folder and process the file before installation.

[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;


Another approach is to delete all files in the installation folder that is not installed by the latest installer.

The easiest solution is to delete all files in the installation folder before the installation.

You can use [InstallDelete] section for that. But if you have some folder/files with configuration in the installation folder, it won't allow you to exclude them.

You can code that Pascal Scripting instead. See Inno Setup - Delete whole application folder except for data subdirectory. You can call the DelTreeExceptSavesDir function from my answer to the that question from CurStepChanged(ssInstall) event function:

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


If you really want to delete only obsolete files, to avoid deleting and re-creating existing files, you can use preprocessor to generate a list of files to be installed for the Pascal Scripting and use that to delete only really obsolete files.

#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;

这篇关于Inno Setup:删除以前版本安装的文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆