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() コマンドがあるに違いありません。

インノグルの誰かがコメントしたり、何らかの形で助けたりできますか? sleep() の代わりに呼び出すことができる flush() はありますか? どんな助けでも大歓迎です。バグリクエストを提出する前に確認したいだけです。

4

3 に答える 3

4

質問に関するコメント トレイルを要約すると、次のようになります。

アンインストールしないでください

最善の解決策は、アンインストーラーをまったく実行しないことです。[InstallDelete]セクションを介して冗長ファイルを削除できます。例えば。「jre」サブフォルダーを完全に削除するには (インストールの一部として置き換えます)、次のようにします。

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

(これは、このようなサブフォルダーにのみ使用し、慎重に使用してください。あまりにも多くのものを削除すると、問題が発生する可能性があります。)

以前のバージョンでインストールされた通常の個々のアプリケーション ファイルが冗長になっている場合は、次のように削除できます。

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

(インストール時に「regserver」または「sharedfile」があった場合は、少し複雑なことをする必要があります。)

それでもアンインストールする場合は、もう少し待ちます

アンインストーラーは、実行中は自分自身またはアンインストーラーが配置されているフォルダーを削除できません。Inno はアンインストーラーとフォルダーを削除できるようにこれを処理しますExecが、アンインストーラーへの呼び出しは、この削除が行われる前、およびアンインストール プロセスが実際に終了する前に返されることを意味します。

Execインストールを続行する前に、アンインストールが実際に完了するまでしばらく待つ必要があります。Sleep の使用は簡単で、ほとんどの場合に機能しますが、最良の結果が必要な場合は、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()から正常に呼び出すことができます。私の場合、SSD ハードディスクを使用する場合は約 500 ミリ秒かかり、外付け USB ハードディスクを使用する場合はおそらく数秒かかります。PrepareToInstallCurStepChanged(ssInstall)

于 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 に答える