Inno Setup:删除以前版本安装的文件 [英] Inno Setup: Removing files installed by previous version
问题描述
我正在使用 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:
- Inno Setup: How to automatically uninstall previous installed version?
- How to detect old installation and offer removal?
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屋!