Get the path in the wpSelectDir before {app} is set Inno Setup - window

I would like to get the path of the install directory, when the user clicks on Next, when I'm in the wpSelectDir of Inno Setup.
I need to check the path, because I need to verify the path, and if it's not correct, I won't let the user continue.
My problem is that the constant {app} is not set yet, because it will be set after the wpSelectDir and I'm still in.

Use WizardDirValue support function:
Returns the current contents of the edit control on the Select Destination Location page of the wizard.
Unlike ExpandConstant('{app}'), this function will not fail if called after the wizard is shown but prior to the user selecting a directory. Rather, it will return the default directory name.
It's more idiomatic than the WizardForm.DirEdit.Text.
Though internally it does nearly the same:
RemoveBackslashUnlessRoot(WizardForm.DirEdit.Text)
See also How do you find the user-selected install path in Inno Setup?

You can do something like this....
procedure onDirChange(Sender: TObject);
var
currentDir: String;
begin
currentDir := WizardForm.DirEdit.Text;
// your validation goes here....
end;
procedure InitializeWizard;
begin
WizardForm.DirEdit.onChange := #onDirChange;
end;
The WizardForm.DirEdit.Text returns the current value in the DirEdit Text Box. The procedure onDirChange is called everytime the text in the dirEdit text box changes. You can use this value to perform your validations.

Related

How to automatically save the GUI to file? (Paradigm)

The good (and bad) old Delphi taught us the "classic" way of building application because of the way we write code "behind" the IDE.
Based on this paradigm, I built some time ago a library that allows me to save/load the GUI to INI file with a single line of code.
LoadForm(FormName)
BAM! That's it! No more INI files!
The library only saves "relevant" properties. For example, for a TCheckBox it saves only its Checked property not also its Color or Top/Left. But it is more than enough.
Saving the form has no issues. However, I have "problems" during app initialization. They are not quite problems, but the initialization code is not that nice/elegant.
For example when I assign Checkbox1.Checked := True it will trigger the OnClick event (supposing that the checkbox was unchecked at design time). But assigning a False value (naturally) will not trigger the OnClick event.
Therefore, I have to manually call CheckBox1Click(Sender) after SaveForm(FormName) to make sure that whatever code is in CheckBox1Click, it gets initialized. But this raises another problem. The CheckBox1Click(Sender) might be called twice during application start up (once by SaveForm and once my me, manually).
Of course, in theory the logic of the program should be put in individual objects and separated from the GUI. But even if we do this, the initialization problem remains. You load the properties of the object from disk and you assign them to the GUI. When you set whatever value you have in your object to Checkbox1, it will (or not) call CheckBox1Click(Sender) which will set the value back into the object.
On app startup:
procedure TForm.FormCreate (Sender: TObject);
begin
LogicObject.Load(File); // load app logic
Checkbox1.Checked := LogicObject.Checked; // assign object to GUI
end;
procedure TForm.CheckBox1Click(Sender: TObject);
begin
LogicObject.Checked := Checkbox1.Checked;
end;
Probably the solution involves writing stuff like this for EVERY control on the form:
OldEvent := CheckBox1.OnClick;
CheckBox1.OnClick := Nil;
CheckBox1.Checked := something;
CheckBox1.OnClick := OldEvent;
Not elegant.
Question:
How do you solve this specific problem OR what is your approach when saving/restoring your GUI to/from disk?
This is one of the things which botthered me in some components from the beginning. What I know the are 3 options, except separating GUI and the business logic as #David said, which is not always an option.
As you wrote above, always unassign the events so they don't get triggered
Use non-triggered events such as OnMouseDown or OnMouseUp
Or a similar solution that I use and I think is the most elegant
Create a global variable FormPreparing which is set during initialization and check its value at the beginning of the events like below.
procedure TForm.FormCreate (Sender: TObject);
begin
FormPreparing := True;
try
LogicObject.Load(File); // load app logic
Checkbox1.Checked := LogicObject.Checked; // assign object to GUI
finally
FormPreparing := False;
end;
end;
procedure TForm.CheckBox1Click(Sender: TObject);
begin
if FormPreparing then
Exit;
LogicObject.Checked := Checkbox1.Checked;
end;

JumpList to all open forms on Delphi

on the OnCreate event of all my forms I add their names to a JumpList
var
i: Integer;
begin
if JumpList = nil then
JumpList := TJumpList.Create(Application);
JumpList.TaskList.Clear;
for i := 0 to OpenForms.Count - 1 do
JumpList.AddTask(OpenForms[i].Caption);
JumpList.UpdateList;
JumpList.Enabled := true;
end;
I want to show the form clicked when its called in the jumplist.
I know I'm supposed to read the message Windows sends with the new instance of the application but I can't find any documentation telling what type of message it sends.
Just need to know where I can find the message I want.
thanks
When you call AddTask a TJumpListItem instance is returned. You should set the Arguments property of that instance.
Description
String with the command-line arguments for the executable of your
item.
When the user selects your item, Windows calls the executable at Path
and passes that executable the content of Arguments as arguments.
Then, when the user clicks on the jump list item, your executable is started and the arguments that you specified are passed to it. You need to read those command line arguments using ParamCount and ParamStr, and respond to them accordingly.
Because specifying arguments is such a critical part of creating a task, the AddTask method has an optional parameter to do so. So you can do it like this.
JumpList.AddTask(YourTasksFriendlyName, '', YourTasksArguments);
Note that the second parameter specifies the Path and passing '' means that you wish the calling executable's path to be used.
Or you can do it like this:
JumpListItem := JumpList.AddTask(YourTasksFriendlyName);
JumpListItem.Arguments := YourTasksArguments;

Check the IniFile descriptor is open

I have a small app, that stores it's settings in INI file. When another form opens, I close INI file descriptor:
procedure TIndexForm.startButtonClick(Sender: TObject);
var
workForm : TForm;
begin
workForm := TworkingForm.Create(nil);
workForm.Show();
configIni.Free; // freeing IniFile desciptor
IndexForm.Hide();
end;
And how I can check the descriptor is still open? Like this(code not working):
if not Assigned configIni then
configIni := TIniFile.Create(configPath);
Thanks in advance.
You're welcome to use Assigned, but it doesn't check whether a variable refers to a valid object instance. Nothing does. Rather, Assigned just checks whether a variable is null. To make that a valid check, make your variable null when you free the object it refers to:
configIni.Free;
configIni := nil;
You can use the FreeAndNil helper function if you want; it does little more than what I've written above.

Switch to the finish page in Inno Setup installation

I added a few custom pages to my setup. In one of this custom pages I do some checks. If this checks failed, I want switch to finish page. How can I do this?
I can not do this with ShouldSkipPage event function because:
function ShouldSkipPage(PageID: Integer): Boolean;
begin
// this will NEVER happened - see documentation below
if (PageID = wpInstalling) or (PageID = wpPreparing) or (PageID = wpWelcome) then
begin
// skip install - simply for example
result := True;
exit;
end;
resutl := false;
end;
From Inno Setup documentation:
The wizard calls this event function
to determine whether or not a
particular page (specified by PageID)
should be shown at all. If you return
True, the page will be skipped; if you
return False, the page may be shown.
Note: This event function isn't called
for the wpWelcome, wpPreparing, and
wpInstalling pages, nor for pages that
Setup has already determined should be
skipped (for example,
wpSelectComponents in an install
containing no components).
I'm sorry, i did not understand why you cannot use ShouldSkipPage.
The usual way of doing it is in ShouldSkipPage:
function ShouldSkipPage(curPageId : Integer) : Boolean;
begin
{For a certain condition, skip to wpFinished}
if (SomeCondition and (curPageId <> wpFinished)) then
Result := True
{Probably more checks}
else
Result := False
end;
If I understand you correctly, you are performing a check, and if it fails, you want to skip wpWelcome, wpPreparing, and wpInstalling (and perhaps more custom pages).
I assume that you also want to skip the actions performed when these pages are shown, specifically, you don't want to perform the installation step.
If that is the case, you should cancel the installation, not continue it without performing the actual installation steps. You can do it in InitializeSetup, for example:
Procedure InitializeSetup();
VAR
Check: Integer;
BEGIN
// perform you check here, set the variable according to it's result
IF (Check <> 0) THEN
// abort installation
Return False;
ELSE
Return True;
END;
Edit
In response to your comment: The easiest solution would be to show a message box with the result of your check, instead of a complete wizard page. If that is not enough for your purposes I would suggest the following approach:
Perform your check in InitializeSetup and store the result in a global variable.
Create a new wizard page (lets call it CheckResult) to display the results of your check, it should be displayed directly after wpWelcome.
In that page's OnNextButtonClick just call CancelButtonClick, that way the installation is always aborted when this page is displayed
Now is the time to modify ShouldSkipPage ;-) If the global check variable indicates that everything is ok, skip your CheckResult page, so that the installation is not aborted automatically
This should work, but if you somehow can, follow the KISS principle and go with the message box approach.

Determine if flash OCX is installed?

What is the best way to determine if the flash ocx is installed in Innosetup (or any installer for that matter). I don't want to attempt to install it myself, I will simply force the user to go to the flash site and install, I just want to make sure that the flash.ocx (version 9+) is installed.
Is it enough to check for HKEY_CLASSES_ROOT\ShockwaveFlash.ShockwaveFlash and check that CurVer >= 9? Is there a better way to test for this?
Add a function in the code section to check whether you can create an instance of the Flash control, like so:
function IsFlashInstalled(): boolean;
var
V: Variant;
begin
try
V := CreateOleObject('ShockwaveFlash.ShockwaveFlash.9');
Result := True;
except
Result := False;
end;
end;
Check out the various examples in the Inno Setup package on how to use your own function to show a message box to the user, cancel the installation, open the Flash site in the default browser or whatever you want to do.
Easy way without Try/Except
function IsFlashInstalled: Boolean;
var ClassID : TCLSID;
begin
Result := Succeeded(CLSIDFromProgID('ShockwaveFlash.ShockwaveFlash', ClassID));//Use CreateComObject() instead...
end;

Resources