inno-setup的磁盘缓存问题?

时间:2013-09-19 18:13:44

标签: caching inno-setup

我会尝试尽可能简短而不附加所有相关的源文件。我已经跟踪了这个问题,就像我的Pascal知识允许我一样......

我发现了一个磁盘缓存问题,对于我的情况,在步骤 ssInstall 。我有一个应用程序的安装程序,如果它发现安装了较旧的应用程序版本,它将调用这样的卸载:

procedure CurStepChanged(CurStep: TSetupStep);
var
  uninstallStr: String;
  ResultCode: Integer;
begin
  if (CurStep = ssInstall) and IsUpdatableApplicationInstalled() then
  begin
  uninstallStr := GetUninstallString();
  uninstallStr := RemoveQuotes(uninstallStr);
  Result := Exec(uninstallStr, '/SILENT /NORESTART /SUPPRESSMSGBOXES', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
  if Result and (ResultCode = 0) then
    Log('CurStepChanged = ssInstall; uninstall OK');
  //-------------
  //Sleep(30000);
  //-------------
end;

此外,文件夹/文件的定义如下:

[Dirs]
Name: "{app}\db"; Flags: uninsalwaysuninstall

[Files]
Source: "..\bin\*"; DestDir: "{app}\bin"; Flags: ignoreversion createallsubdirs recursesubdirs
Source: "..\java\jre\*"; DestDir: "{app}\jre"; Flags: ignoreversion recursesubdirs createallsubdirs
blah...

测试用例1; 正常安装:一切顺利。 日志文件部分:

Starting the installation process.
Creating directory: C:\Program Files                               <---
Creating directory: C:\Program Files\MyApp                         <---
Creating directory: C:\Program Files\MyApp\db                      <---
Creating directory: C:\Program Files\MyApp\jre                     <---
Creating directory: C:\Program Files\MyApp\jre\lib
Creating directory: C:\Program Files\MyApp\jre\lib\applet
Directory for uninstall files: C:\Program Files\MyApp
Creating new uninstall log: C:\Program Files\MyApp\unins000.dat    <--- !!!
-- File entry --
Dest filename: C:\Program Files\MyApp\unins000.exe                 <--- !!!
blah...

测试用例2; 更新旧版本:当进入步骤 ssInstall 时,卸载程序启动,完成然后安装开始。 日志文件部分:

CurStepChanged = ssInstall; uninstall OK
Starting the installation process.
Creating directory: C:\Program Files\MyApp\jre\lib
Creating directory: C:\Program Files\MyApp\jre\lib\applet
Directory for uninstall files: C:\Program Files\MyApp
Creating new uninstall log: C:\Program Files\MyApp\unins001.dat    <--- !!!
-- File entry --
Dest filename: C:\Program Files\MyApp\unins001.exe                 <--- !!!
blah...

正如您所见,某些文件夹未创建,我的应用程序稍后在尝试写入'db'文件夹时失败。

如果我取消注释 Sleep()命令,一切都会顺利运行,两个日志文件都是相同的。

磁盘似乎有足够的时间来刷新更改!不知何故,必须在inno-setup中缺少flush()命令。

任何创新大师都能以某种方式发表评论或帮助吗? 我可以调用flush()而不是sleep()吗? 任何帮助表示赞赏。我只想在提交错误请求之前确定。

3 个答案:

答案 0 :(得分:4)

这就是我实际解决这个问题的方法。我发布了我的解决方案,希望能帮助其他人解决同样的问题。

非常感谢所有帮助过的人,特别是 Miral 让我指向了正确的方向!

解决方案相当简单;等到删除卸载程序exe!

const
  DELAY_MILLIS = 250;
  MAX_DELAY_MILLIS = 30000;


function GetUninstallString(): String;
var
  uninstallPath: String;
  uninstallStr: String;
begin
  uninstallPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#emit SetupSetting("AppId")}_is1');
  uninstallStr := '';
  if not RegQueryStringValue(HKLM, uninstallPath, 'UninstallString', uninstallStr) then
    RegQueryStringValue(HKCU, uninstallPath, 'UninstallString', uninstallStr);
  Result := RemoveQuotes(uninstallStr);
end;


function ForceUninstallApplication(): Boolean;
var
  ResultCode: Integer;
  uninstallStr: String;
  delayCounter: Integer;
begin
  // 1) Uninstall the application
  Log('forcing uninstall of application);
  uninstallStr := GetUninstallString();
  Result := Exec(uninstallStr, '/SILENT /NORESTART /SUPPRESSMSGBOXES /LOG', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) and (ResultCode = 0);
  if not Result then
  begin
    Log('application uninstall failed!');
    Exit
  end;
  Log('application uninstalled!');

  // 2) Be sure to wait a while, until the actual uninstaller is deleted!
  Log('waiting a while until uninstaller changes are flushed in the filesystem...');
  delayCounter := 0;
  repeat
    Sleep(DELAY_MILLIS);
    delayCounter := delayCounter + DELAY_MILLIS;
  until not FileExists(uninstallStr) or (delayCounter >= MAX_DELAY_MILLIS);
  if (delayCounter >= MAX_DELAY_MILLIS) then
    RaiseException('Timeout exceeded trying to delete uninstaller: ' + uninstallStr);
  Log('waited ' + IntToStr(delayCounter) + ' milliseconds');
end;

可以从ForceUninstallApplication()PrepareToInstall成功调用函数CurStepChanged(ssInstall)。对于我的情况,当我使用我的SSD硬盘时需要大约500毫秒,而当我使用外部USB硬盘时可能需要几秒钟。

答案 1 :(得分:3)

总结一下这个问题的评论记录:

不要卸载

最好的解决方案是根本不运行卸载程序。您可以通过[InstallDelete]部分删除多余的文件;例如。要完全删除“jre”子文件夹(作为安装的一部分进行更换),请执行以下操作:

[InstallDelete]
Type: filesandordirs; Name: "{app}\jre"

(只对这样的子文件夹使用此文件并且只是谨慎使用;如果删除太多东西,可能会遇到麻烦。)

对于以前版本安装的现在是多余的普通单个应用程序文件,您可以删除它们,如下所示:

[InstallDelete]
Type: files; Name: "{app}\redundant.dll"

(如果他们在安装时有“regserver”或“sharedfile”,你需要做一些稍微好一点的事情。)

如果您无论如何卸载,请等待更长时间

卸载程序无法删除自身或它仍在运行时所在的文件夹。虽然Inno确实以能够删除卸载程序和文件夹的方式处理此问题,但它确实意味着在删除发生之前以及卸载过程实际完成之前,对{1}​​}调用卸载程序将会返回

卸载的Exec实际完成后,您需要等待更长时间才能继续安装。使用Sleep很简单,并且在大多数情况下都可以使用,但是如果你想要最好的结果,你需要调用WinAPI来检查正在运行的进程列表。

此外,您应该使用Exec事件函数来执行实际的卸载。这样可以更好地处理卸载错误或卸载和重新安装之间需要重新启动等情况。 (因为它在安装过程中的“正确”时间执行。)

答案 2 :(得分:0)

感谢fubar这个答案!我只是做了一点修改,以避免在卸载过程中删除安装文件夹的同样问题。它可以在调用ForceUninstallApplication()后删除,并避免创建安装文件夹。

为了避免这个问题,我只是在安装文件夹中创建一个临时文件,并在安装完成后删除。当存在未知文件文件夹时,unins000.exe不会删除该文件夹。

此处更新了fubar代码:

const
  DELAY_MILLIS = 250;
  MAX_DELAY_MILLIS = 30000;

var
  tempUninstallFilename: String;


function GetUninstallString(): String;
var
  uninstallPath: String;
  uninstallStr: String;
begin
  uninstallPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#emit SetupSetting("AppId")}_is1');
  uninstallStr := '';
  if not RegQueryStringValue(HKLM, uninstallPath, 'UninstallString', uninstallStr) then
    RegQueryStringValue(HKCU, uninstallPath, 'UninstallString', uninstallStr);
  Result := RemoveQuotes(uninstallStr);
end;


function ForceUninstallApplication(): Boolean;
var
  ResultCode: Integer;
  uninstallStr: String;
  delayCounter: Integer;
begin
  // 1) Create a temporary file to avoid destruction of install folder during uninstall.
  uninstallStr := RemoveQuotes(GetUninstallString());
  tempUninstallFilename := ExtractFileDir(uninstallStr) + '\uninstall.log';
  SaveStringToFile(tempUninstallFilename, '...', False);
  Log('Create temp file: ' + tempUninstallFilename);

  // 2) Uninstall the application
  Log('forcing uninstall of application);
  uninstallStr := GetUninstallString();
  Result := Exec(uninstallStr, '/SILENT /NORESTART /SUPPRESSMSGBOXES /LOG', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) and (ResultCode = 0);
  if not Result then
  begin
    Log('application uninstall failed!');
    Exit
  end;
  Log('application uninstalled!');

  // 2) Be sure to wait a while, until the actual uninstaller is deleted!
  Log('waiting a while until uninstaller changes are flushed in the filesystem...');
  delayCounter := 0;
  repeat
    Sleep(DELAY_MILLIS);
    delayCounter := delayCounter + DELAY_MILLIS;
  until not FileExists(uninstallStr) or (delayCounter >= MAX_DELAY_MILLIS);
  if (delayCounter >= MAX_DELAY_MILLIS) then
    RaiseException('Timeout exceeded trying to delete uninstaller: ' + uninstallStr);
  Log('waited ' + IntToStr(delayCounter) + ' milliseconds');
end;


//============================================================================================================
// SUMMARY
// You can use this event function to perform your own pre-install and post-install tasks.
procedure CurStepChanged(CurStep: TSetupStep);
begin
  if (CurStep = ssInstall) then
  begin
    ForceUninstallApplication();
  end ;

  if (CurStep = ssPostInstall) then
  begin
    DeleteFile(tempUninstallFilename);
  end;
end;