Inno setup asking for remove folder only if contain files [duplicate] - inno-setup

I have written a setup. In this setup nothing happens, because I only concentrated on one area: The "
wpSelectDir", where the user can choose a directory the setup should be installed in.
Now my code snippet should check, if ANYTHING exist in the chosen directory (any other folders, files, etc.). If so, the user gets warned if he still wants to continue, because everything in this directory will be removed then.
If the user only created a new empty folder he should not get a warning, because nothing will be lost.
I have the code snippet already finished excepting the check if the directory is empty (I replaced it with "if 1=1 then".
Please just have a look:
[Setup]
AppName=Testprogramm
AppVerName=Example
AppPublisher=Exxample
DefaultDirName={pf}\C
DefaultGroupName=C
Compression=lzma
SolidCompression=yes
[Code]
function NextButtonClick(CurPageID: Integer): Boolean;
begin
if CurPageID = wpSelectDir then // if user is clicked the NEXT button ON the select directory window; if1 begins here;
begin
if 1=1 then // if the directory is not empty; thx 4 help stackoverflow
begin // warning with yes and no
if MsgBox('The file contains data. This data will be removed permanently by continuing the setup?', mbConfirmation, MB_YESNO) = IDYES then //if 3 begins here
begin
Result := True;
end
else
begin
Result := False;
end;
end; // if2 ends here
end // not CurPageID but any other begins here
else
begin
Result := True;
end;
end;
I have already tried to use functions like "if FileExists( ...", but there I can not say " . " for any file. Also I was not successful using WizardDirValue and its properties.
I would really appreciate if someone could help me or give me a hint.
Thanks a lot,
Regards C.

Use FindFirst/FindNext.
Example:
function isEmptyDir(dirName: String): Boolean;
var
FindRec: TFindRec;
FileCount: Integer;
begin
Result := False;
if FindFirst(dirName+'\*', FindRec) then begin
try
repeat
if (FindRec.Name <> '.') and (FindRec.Name <> '..') then begin
FileCount := 1;
break;
end;
until not FindNext(FindRec);
finally
FindClose(FindRec);
if FileCount = 0 then Result := True;
end;
end;
end;
Note: This function also returns False if directory doesn't exists

Related

Deleting all files with some exceptions after installation with Inno Setup

How can I delete a series of files once the Run has been executed?
I want to delete a lot of DLL files and some other files because they are now in separate sub folders. But some of the DLL files are registered COM etc. Once Run has finished the old DLLs will not be registered anymore. So it will be safe for me to delete them.
InstallDelete is too early:
https://jrsoftware.org/ishelp/index.php?topic=installorder
So I want ability to delete *.dll but exclude pattern*.dll ONCE install finished.
I've seen a similar question:
Delete a file AFTER installation in Inno Setup
It seems I need to use something like:
[Code]
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssPostInstall then begin
DeleteFile(ExpandConstant('path'));
..
Ideally I want to delete:
{app}\*.dll
{app}\*.config
{app}\GoogleAuthAndSync.exe
But, I want to KEEP these files in the {app} folder:
{app}\MeetSchedAssist*.dll
{app}\VclStylesinno.dll
That is what I want to achieve. This is because the files are now being installed into distinct sub-folders and being managed there, and not all mixed up in the app folder.
The CurStepChanged(ssPostInstall) seems like the the right approach to me. So all you need on top of that is a way to delete all files, with some exceptions, right?
Start here:
Delete whole application folder except for "data" subdirectory in Inno Setup
If you need to know how to test for partial filename (MeetSchedAssist*.dll):
If you need just one-off test, an ad-hoc condition like this will do:
(CompareText(Copy(FindRec.Name, 1, 15), 'MeetSchedAssist') = 0) and
(CompareText(Copy(FindRec.Name, Length(FindRec.Name) - 3, 4), '.dll') = 0)
If you have many tests, you can use MatchesMaskEx function from Inno Setup - Integer or Set/Range wildcard?:
MatchesMaskEx('MeetSchedAssist*.dll', FindRec.Name)
I thought I would add my final code:
(* Cannot use built-in DeleteFile directly in AfterInstall as it's a function,
not a procedure. And this way we can add some error handling too. *)
procedure DoDeleteFile(FileName: string);
begin
if (FileExists(FileName)) then
begin
if DeleteFile(FileName) then
begin
Log(Format('"%s" deleted', [FileName]));
end
else begin
MsgBox(Format('Failed to delete "%s"', [FileName]), mbError, MB_OK);
end;
end;
end;
{ Deletes obsolete DLL files etc. }
procedure DelObsoleteFiles(Path: string);
var
FindRec: TFindRec;
FilePath: string;
begin
if FindFirst(Path + '\*.dll', FindRec) then
begin
try
repeat
FilePath := Path + '\' + FindRec.Name;
if CompareText(FindRec.Name, 'VclStylesinno.dll') = 0 then
begin
Log(Format('Keeping DLL %s', [FilePath]));
end else if CompareText(Copy(FindRec.Name, 1, 15), 'MeetSchedAssist') = 0 then
begin
Log(Format('Keeping DLL %s', [FilePath]));
end else begin
DoDeleteFile(FilePath);
end;
until not FindNext(FindRec);
finally
FindClose(FindRec);
end;
end else
begin
Log(Format('Failed to list %s', [Path]));
end;
DoDeleteFile(Path + '\GoogleAuthAndSync.exe')
DoDeleteFile(Path + '\GoogleAuthAndSync.exe.config')
end;
procedure CurStepChanged(CurStep: TSetupStep);
var
ResultCode: integer;
begin
if (CurStep = ssPostInstall) then
begin
(* Delete obsolete DLL files etc. because they are now maintained in
separate sub-folders *)
DelObsoleteFiles(ExpandConstant('{app}'));
end;
end;

How to set creation time of existing file in Inno Setup Pascal Script

When copying a file by FileCopy (or also RenameFile) from a directory to another one an original creation time changes to the current date. I would like to set the creation time to the original one.
I can get the original time values by FindFirst, but how to get a handle of a file to use when calling SetFileTime?
In the [Code] section of Inno Setup, I have this code:
If FileCopy(F1, F2,False) then
If FindFirst(F1,FindRec) then
Try
Fhandle := ??????????? (FindRec.FindHandle don't works)
SetFileTime(
Fhandle, FindRec.CreationTime, FindRec.LastAccessTime, FindRec.LastWriteTime)
finally
FindClose(FindRec);
end
EDIT:
After the answer of Martin I have modified the code as follow (sorry if is far than perfect... I am a VB.NET programmer, not a Pascal programmer):
{ C1 and C2 are full Paths }
if Not FileCopy(C1, C2, False) then
begin
MsgBox('Data reading error 01. Setup will be aborted.', mbError, MB_OK);
Result := false;
exit;
end;
if FindFirst(C2, FindRec) then
try
begin
MyTime := FindRec.LastWriteTime //remains the original one
end;
finally
FindClose(FindRec);
end
else
begin
MsgBox('Data reading error 02. Setup will be aborted.', mbError, MB_OK);
Result := false;
exit;
end;
end;
FileStream := TFileStream.Create(C2, fmOpenReadWrite);
Try
if not SetFileTime(FileStream.Handle, MyTime, MyTime, MyTime) Then
begin
MsgBox('Data reading error 03. Setup will be aborted.', mbError, MB_OK);
Result := false;
exit;
end;
Finally
FileStream.Free;
end;
To obtain a handle of a file, you can use TFileStream class:
var
FileStream: TFileStream;
begin
{ ... }
FileStream := TFileStream.Create(FileName, fmOpenReadWrite);
try
SetFileTime(FileStream.Handle, CreationTime, LastAccessTime, LastWriteTime);
finally
FileStream .Free;
end;
end;
Though as #Ken wrote, in most cases, it would be easier to use [Files] section entry with an external flag.

Inno Setup - Check if multiple folders exist

I have a custom uninstall page, which is invoked with this line:
UninstallProgressForm.InnerNotebook.ActivePage := UninstallConfigsPage;
Now, this just shows the page every time the uninstaller is run, but I need it to show only if certain folders exist (there's 6 of them). I could make an if statement with a bunch of or's, but I'm wondering if there's a neater way to do it.
In general, there's no better way than calling DirExists for each folder:
if DirExists('C:\path1') or
DirExists('C:\path2') or
DirExists('C:\path3') then
begin
{ ... }
end;
Though, when processing a set of files/folders, it's advisable to have their list stored in some container (like TStringList or array of string), to allow their (repeated) bulk-processing. You already have that (Dirs: TStringList) from my solution to your other question.
var
Dirs: TStringList;
begin
Dirs := TStringList.Create();
Dirs.Add('C:\path1');
Dirs.Add('C:\path2');
Dirs.Add('C:\path2');
end;
function AnyDirExists(Dirs: TStringList): Boolean;
var
I: Integer;
begin
for I := 0 to Dirs.Count - 1 do
begin
if DirExists(Dirs[I]) then
begin
Result := True;
Exit;
end;
end;
Result := False;
end;
But I know from your other question, that you map all the paths to checkboxes. Hence, all you need to do, is to check, if there's any checkbox:
if CheckListBox.Items.Count > 0 then
begin
UninstallProgressForm.InnerNotebook.ActivePage := UninstallConfigssPage;
{ ... }
if UninstallProgressForm.ShowModal = mrCancel then Abort;
{ ... }
UninstallProgressForm.InnerNotebook.ActivePage := UninstallProgressForm.InstallingPage;
end;

Inno Setup disable Installing Wizard Pages

Is it possible to disable the the Preparing To Install wpPreparing and the Installing wpInstalling Wizard Pages (i.e. the ones with the progress bar) so that they do not show during installation? There does not appear to be a built-in directive or method (e.g. for the Ready Wizard Page you can use DisableReadyPage=yes to do so). Am I missing something, or is it, as I suspect, simply not possible?
I have already tried using:
function ShouldSkipPage(CurPageID: Integer): Boolean;
begin
if CurPageID = wpPreparing then
Result := True;
if CurPageID = wpInstalling then
Result := True;
end;
Have you tried this - DisableReadyPage=yes in the [Setup] section.
Seems the only other option is to use the "install silently" command line switch. I'd be careful though this essentially installs a potentially destructive program without the users knowledge.
It is not possible to skip the wpPreparing or wpInstalling Wizard Pages. However, if the installer is not actually installing anything and is being used to return something, say an unlock code, as is the use case here, the following could be done instead:
//Disable the Exit confirmation prompt
procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean);
begin
Cancel := True;
Confirm := False;
end;
function NextButtonClick(CurPageID: Integer): Boolean;
begin
//Get the unlock code
if CurPageID = UnlockCodePage.ID then
begin
if UnlockCodePage.Values[0] = '' then
begin
MsgBox('You must enter an installation ID to generate an unlock code.',
mbError, MB_OK);
end
else
begin
UnlockCodePage.Values[1] := GetUnlockCode;
end;
Result := False;
end;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
//Define button visibility, whether the Back button is enabled and change the Next and Cancel button labels
WizardForm.CancelButton.Caption := '&Close';
if CurPageID = UnlockCodePage.ID then
begin
WizardForm.BackButton.Enabled := False;
WizardForm.NextButton.Caption := '&Generate';
end;
end;
Hopefully this may help someone looking to do something similar.

How do I check if registry key exists and exit setup

I try to making setup file to patch previous program. The setup must be able to check if previous program is installed or not.
This is my unusable code
[Code]
function GetHKLM() : Integer;
begin
if IsWin64 then
begin
Result := HKLM64;
end
else
begin
Result := HKEY_LOCAL_MACHINE;
end;
end;
function InitializeSetup(): Boolean;
var
V: string;
begin
if RegKeyExists(GetHKLM(), 'SOFTWARE\ABC\Option\Settings')
then
MsgBox('please install ABC first!!',mbError,MB_OK);
end;
My condition is
must check windows 32- or 64-bits in for RegKeyExists
if previous program is not installed, show error message and exit setup program, else continue process.
How to modify the code?
Thank you in advance.
**Update for fix Wow6432Node problem. I try to modify my code
[Code]
function InitializeSetup: Boolean;
begin
// allow the setup to continue initially
Result := True;
// if the registry key based on current OS bitness doesn't exist, then...
if IsWin64 then
begin
if not RegKeyExists(HKLM, 'SOFTWARE\Wow6432Node\ABC\Option\Settings') then
begin
// return False to prevent installation to continue
Result := False;
// and display a message box
MsgBox('please install ABC first!!', mbError, MB_OK);
end
else
if not RegKeyExists(HKLM, 'SOFTWARE\ABC\Option\Settings' then
begin
// return False to prevent installation to continue
Result := False;
// and display a message box
MsgBox('please install ABC first!!', mbError, MB_OK);
end
end;
end;
The updated code in the question is incorrect -- you should never ever use Wow6432Node anywhere other than when looking at paths in RegEdit.
From the behaviour you have described, you are actually looking for a 32-bit application. In this case you can use the same code regardless of whether Windows is 32-bit or 64-bit; you were overthinking your original question.
Here is the corrected code from your updated question:
[Code]
function InitializeSetup: Boolean;
begin
// allow the setup to continue initially
Result := True;
if not RegKeyExists(HKLM, 'SOFTWARE\ABC\Option\Settings') then
begin
// return False to prevent installation to continue
Result := False;
// and display a message box
MsgBox('please install ABC first!!', mbError, MB_OK);
end;
end;

Resources