Introduction
Within its lifecycle, an app can create files and folders which may not be of any value to the user once uninstalled. Common practice is to simply ignore the extras and just uninstall what has been installed. Some of the pitfalls of proceeding any further are mentioned in the comments to this answer, yet the code at ISXKB prompted this author to further expand upon the scenario.
Requirements
The reader is best served by having or at least intending to build an active installation with Inno Setup. Very friendly and intuitive interface, along with extensive documentation. Determine where and how files relating to your app are created, and what purpose they will have once the user is no longer using your software.
Using the Code
Download and paste the code in the code section of the .iss script. The dialog driven script is run when the app is uninstalled.
The chief point of interest is at the bottom of the code where paths containing Inno defined constants can be inserted or removed:
FileX:= ExpandConstant({Take care these are exactly what are required!}
PrepDeleteOneFile(FileB, False, True, false); // 2nd parm: dir/file,
// 3rd parm confirm, 4th parm: confirm for many
DeleteManyFiles(FileX, False); // 2nd Parm: dir/file
The following snippet is for a VB compilation:
FileA:= ExpandConstant('{userprograms}\{#MyAppName}\*');
FileB:= ExpandConstant('{userprograms}\{#MyAppName}');
FileC:= ExpandConstant('{app}\{#MyAppName}.log');
FileD:= ExpandConstant('{app}\Process.log');
FileE:= ExpandConstant('{app}\*');
FileF:= ExpandConstant('{app}');
ExistsB:= FileExists(FileB);
ExistsC:= FileExists(FileC);
ExistsD:= DirExists(FileD);
ExistsF:= DirExists(FileF);
DeleteManyFiles(FileA, False);
if isEmptyDir(FileA, False) = False And
ExistsB = true then PrepDeleteOneFile(FileB, True, False, False);
if ExistsC = true then PrepDeleteOneFile(FileC, False, False, False);
if ExistsD = true then PrepDeleteOneFile(FileD, False, False, False);
DeleteManyFiles(+FileF+ '??????XX.mdb', False);
DeleteManyFiles(+FileF+ '{app}\??????YY.mdb', False);
DeleteManyFiles(+FileF+ '{app}\??????ZZ.mdb', False);
if isEmptyDir(FileF, True) = False Then
Begin
DeleteManyFiles(FileE, True);
end;
The Inno Delete Functions work very well, however they only return false on fail, so we can assume in most situations, the reason is either in-use or not accessible.
If an Updater is used (see below), also consider replicating the above code in the Updater, as depending on the Setup & Updater settings, the code can be removed from the setup uninstall binary (typically unins000.exe) when the Updater executes.
Some of the language dialogs were made with Google Translate so any corrections to them are welcome.
Apologies to non Spanish/German/English subscribers, as those languages were the only ones on offer at ISXKB.
Addendum
The following section is for anyone interested using Inno to update an application. An updater is a separate iss script typically run from {tmp} (inno's temp sub-directory for MyApp prefixed is-.) to update the installation {App} from an external source -e.g. an http server.
Updaters do not have an uninstall as such as they are defined to operate on MyApp
only, but each time an updater is run from {tmp}, any temporary files related to the install session are created in that folder.
In the usual course of installation, these temporary folders are removed after install, but in the case of updating MyApp, Inno (5.59) generates an (Abort-Ignore-Retry) error when attempting to remove an Updater {srcexe} from {tmp} in the ssPostInstall
step with the deleteafterinstall flag thus:
Source: "{tmp}\{#MyAppUpdaterExeName}"; DestDir: "{tmp}"; DestName: "Deletethis{#MyAppUpdaterExeName}";
Flags: external skipifsourcedoesntexist ignoreversion replacesameversion deleteafterinstall
So here's a method to silently remove it after reboot:
[code]
const
MOVEFILE_DELAY_UNTIL_REBOOT = $00000004;
...
//Decls
function MoveFileEx (lpExistingFileName : string; lpNewFileName : string; dwFlags: Integer): Integer;
external 'MoveFileExW@kernel32.dll stdcall';
...
procedure CurPageChanged(CurPageID: Integer);
var
iResultCode : Integer;
// MovefileEx writes the pathname of {scrcexe} to...
// HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations
// {srcexe} is equivalent to our running updater: {tmp}\{#MyAppUpdaterExeName}
if (CurPageID=wpFinished) then
begin
iResultCode := MoveFileEx(ExpandConstant('{srcexe}'), '', MOVEFILE_DELAY_UNTIL_REBOOT);
if iResultCode = 0 then
MsgBox(ExpandConstant('{srcexe}') + NL + ' is not able to be deleted at next reboot!
Reason code: ' + IntToStr(iResultCode) + '.', mbError, MB_OK);
end;