I will try to be as brief as possible without attaching all the related source files. I have tracked down the issue as much as my Pascal knowledge allows me...
I thing I found a disk caching problem that occurs, for my case, at step ssInstall. I have an installer for an app that, if it finds an older app version installed, it will invoke an uninstallation like this:
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;
Also the folders/files are defined like this:
[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...
Test case1; Normal installation: Everything goes smoothly. Log file part:
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...
Test case2; Update old version: When getting to step ssInstall, the uninstaller launches, it finishes then the installation begins. Log file part:
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...
As you can see some folders do not get created and my app fails later on when it tries to write to 'db' folder.
If I uncomment the Sleep() command, everything runs smoothly and both the log files are identical.
It seems the disk has enough time to flush the changes! Somehow there must be a flush() command missing in inno-setup.
Can any of the inno-gurus comment or help somehow? Is there a flush() i could call, instead of the sleep()? Any help is appreciated. I just want to be sure before I file a bug request.
Just to sum up the comment trail on the question:
The best solution is to not run the uninstaller at all. You can remove redundant files via the [InstallDelete] section; eg. to remove a "jre" subfolder completely (to be replaced as part of your installation) then do this:
[InstallDelete]
Type: filesandordirs; Name: "{app}\jre"
(Only use this for subfolders like this and only sparingly; you can get yourself in trouble if you delete too much stuff.)
For normal individual application files installed by a previous version that are now redundant, you can remove them like so:
[InstallDelete]
Type: files; Name: "{app}\redundant.dll"
(You'll need to do something slightly fancier if they had "regserver" or "sharedfile" when installed.)
The uninstaller cannot delete itself or the folder where it is located while it is still running. While Inno does take care of this in such a way that it is able to delete the uninstaller and folder, it does mean that your Exec call to the uninstaller will return before this deletion has occurred and before the uninstall process actually finishes.
You will need to wait longer after the Exec for the uninstall to actually finish before you let it continue with the installation. Using a Sleep is simple enough and will work in most cases but if you want the best results you'll need to call into the WinAPI to check the running processes list.
Additionally, you should use the PrepareToInstall event function to perform the actual uninstallation. This will better allow you to handle cases such as uninstall errors or when a reboot is required between uninstall and reinstall. (And because it executes at the "right" time in the installation process.)
This is how I actually worked around this issue. I am posting my solution in hope to help others with the same problem.
Big thanks to all that helped out and especially to Miral for pointing me in the right direction!
The solution is rather simple; wait until the uninstaller exe is deleted!
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;
Function ForceUninstallApplication() can be successfuly called from either PrepareToInstall or CurStepChanged(ssInstall). For my case it takes about 500 millis when I use my SSD hard disk and maybe a couple of seconds when I use my external usb hard disk.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With