2

我会尽量简短而不附上所有相关的源文件。在我的 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()吗?任何帮助表示赞赏。我只是想在提交错误请求之前确定一下。

4

3 回答 3

4

总结一下这个问题的评论线索:

不要卸载

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

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

(仅将其用于这样的子文件夹,并且要谨慎使用;如果删除太多内容,您可能会遇到麻烦。)

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

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

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

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

卸载程序在运行时无法删除自身或它所在的文件夹。虽然 Inno 确实以能够删除卸载程序和文件夹的方式处理了这一点,但这确实意味着您Exec对卸载程序的调用将在删除发生之前和卸载过程实际完成之前返回。

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

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

于 2013-09-24T04:19:45.447 回答
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 硬盘时可能需要几秒钟。

于 2013-09-24T17:44:41.107 回答
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;
于 2014-10-10T20:24:11.883 回答