How do you determine if an object has be constructed in Inno Setup Pascal Script? - inno-setup

How do I check that my checkbox has been created / constructed and can be used to check if checked?
[Code]
var
MyCheckBoxThatMayExistOrNot: TNewCheckBox;
procedure Whatever();
begin
{ Check if MyCheckBoxThatMayExistOrNot exists and checked }
if ????? and MyCheckBoxThatMayExistOrNot.Checked then
begin
...
end;
end;
TIA!!

Compare the variable value against nil:
if (MyCheckBoxThatMayExistOrNot <> nil) and
MyCheckBoxThatMayExistOrNot.Checked then
An equivalent is use of Assigned function:
if Assigned(MyCheckBoxThatMayExistOrNot) and
MyCheckBoxThatMayExistOrNot.Checked then
You might want to explicitly initialize the variable to nil in InitializeSetup or InitializeWizard, but it should not be necessary: Are global variables in Pascal Script zero-initialized?

Related

Inno Setup - Check if a component is installed

What I really want to do is have Inno Setup uninstall a component, if it's unchecked in a subsequent run. But, if I'm not mistaken, that is not possible in Inno Setup (actually, correct me, if I'm wrong on this).
So, instead I want to make check function to see if a component is installed, so I can hide it during subsequent runs. I'm not sure where else to get that info other than the Inno Setup: Selected Components under HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\[AppName]_is1.
Now the problem is my Inno Setup: Selected Components is as,as2,as3,bs,bs2,bs3.
How can I detect as, without detecting as2 or as3?
Indeed, Inno Setup does not support uninstalling components.
For a similar question (and maybe better), see:
Inno Setup: Disable already installed components on upgrade
For checking of installed components, I'd rather suggest you to check for existence of files corresponding to the component.
Anyway, to answer your actual question: If you want to scan the Inno Setup: Selected Components entry, you can use this function:
function ItemExistsInList(Item: string; List: string): Boolean;
var
S: string;
P: Integer;
begin
Result := False;
while (not Result) and (List <> '') do
begin
P := Pos(',', List);
if P > 0 then
begin
S := Copy(List, 1, P - 1);
Delete(List, 1, P);
end
else
begin
S := List;
List := '';
end;
Result := (CompareText(S, Item) = 0);
end;
end;
Note that the uninstall key can be present in HKCU (not in HKLM) under certain circumstances.

Inno Setup : Create Uninstall Registry Key only when needed

I need to force the directive CreateUninstallRegKey in the [Setup] Section to only create the Registry Key for uninstall when needed.
For example , if I set a condition to create Uninstall Registry Key,
it must only be created when the condition goes True.
Otherwise the Uninstall Registry Key must not be created.
How can I do this in Inno Setup?
UPDATED QUESTION
The code I wrote is:
[Setup]
CreateUninstallRegKey=RegKeyDeterminer
[Code]
function RegKeyDeterminer(): Boolean;
begin
Result:= ISDoneError = True;
if ISDoneError = True then Result:= True;
end;
With this code , The Uninstall Registry Key is always creating. (It should be something wrong in the code I wrote.)
The Uninstall Registry Key must not be created if ISDoneError = True.
The Uninstall Registry Key must be created if ISDoneError = False.
ISDoneError only has True or False values.(It is a Boolean Function in ISDone.dll which is a Dynamic Link Library that used to extract files from 7-Zip,RAR,Binary etc. archives in Inno Setup.)
These are the conditions.
If you can see any mistakes or condition setting errors , then correct my code.
Thank You.
The CreateUninstallRegKey directive can take a boolean expression/function as its value.
So just implement the function to return True when you need to create the key and False otherwise.
[Setup]
CreateUninstallRegKey=CreateKey
[Code]
function CreateKey: Boolean;
begin
Result := condition;
end;

Get strings from ini file and use multiple times

I want to store text from ini file into a variable and use the var many times along my setup pages,
For instace lets say I have ini file named-"datatxt", ini section -[txtdata], section key-"htxt", key value - "hello world".
I want to store this text value in a var named - "hiVar" and use it here-
Title :=
TNewStaticText.Create(MOPage);
Title.Parent := MOPage.Surface;
Title.Font.Name := 'Verdana';
Title.Caption := hiVar;
For declaring variables there are two scopes available. Local and global. Locally declared variables are visible only within the body of a procedure or method where they are declared. They are widely used as a temporary storage for intermediate operations, or for holding object references (as you already do):
procedure DoSomething;
var
S: string; // <- this is a locally declared variable
begin
// only inside this procedure the S variable can be accessed
end;
Globally declared variables (which is your question about) are visible in the scope of all procedures and methods within the whole code scripting section. They are used for holding references of objects whose are used across script code, passing results of some operations between event methods, or for holding some permanent values (which is your case):
var
S: string; // <- this is a globally declared variable
procedure DoSomething;
begin
// inside this procedure the S variable can be accessed
end;
procedure DoSomethingElse;
begin
// as well as inside this procedure the S variable can be accessed
end;
Answer your question with an example is quite hard, since you haven't desribed the context in which you want to read that INI file, so it's hard to tell in which event you should read it. In the following example the INI file value is read when the wizard form is initialized. You can see there an access to a global variable from another method as well:
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
[Files]
Source: "datatxt"; Flags: dontcopy
[Code]
var
hiVar: string; // <- this is a globally declared variable
procedure InitializeWizard;
begin
// extract the file into the setup temporary folder
ExtractTemporaryFile('datatxt');
// assign the read value into a global variable
hiVar := GetIniString('txtdata', 'htxt', '', ExpandConstant('{tmp}\datatxt'));
// from now on the variable should contain the key value
end;
procedure CurPageChanged(CurPageID: Integer);
begin
// just a random code showing that you can access global
// variables across the methods
if CurPageID = wpWelcome then
MsgBox(hiVar, mbInformation, MB_OK);
end;

innosetup semicolon expected in code section

I have an error while compiling the code section of my inno script.
The code section
var
ServerID: String;
EditServerID: TEdit;
PageIDServer: TWizardPage;
function getServerID(Param: String): String;
begin
Result := ServerID.Text; <--- Error here
end;
And the procedure section:
if InstallService(ExpandConstant('"{app}\Client.exe -{code:GetServerID}" Client'),'Client','Client','Client',SERVICE_WIN32_OWN_PROCESS,SERVICE_AUTO_START) = true then
begin
StartService('Client');
Sleep(500);
end
else
MsgBox('Client service could not be installed',mbInformation, MB_OK);
I read that this error may be linked to the {code:} but don't know why.
Thanks for your help.
You were trying to access a Text property of a string variable ServerID, but you certainly wanted to get that value from the EditServerID edit box. If that is so, write it this way:
function GetServerID(Param: string): string;
begin
Result := EditServerID.Text;
end;
The same applies for the code in your NextButtonClick event method. Btw. the ServerID variable seems to be unused in your script, you're just assigning its value to the EditServerID.Text property when the edit box is created, but at that time the variable is empty, so I think you can just remove it from your script to not mislead you anymore.

Hook standard Inno Setup checkbox

I added an InputOptionWizardPage for selecting tasks. This works fine, but I would like to add some custom functionality. One task is dependent on the other, so if the second checkbox is checked, the first should be checked and grayed out.
To do this, I need to access the properties of a checkbox. I found ways to do this using a completely custom page, where I would explicitly create the checkbox myself, but that would be a lot of work, since most of what I have so far is satisfactory.
How can I hook a checkbox that was created by Inno Setup, using MyInputOptionWizardPage.Add('This will add a checkbox with this caption')?
In attempt to answer your question directly.
I suspect you have used CreateInputOptionPage() which returns a TInputOptionWizardPage
This has the '.Add('Example')` method that you mention.
TInputOptionWizard descends TWizardPage which descends from TComponent which has the methods you need.
Update: Replaced original Code, this example is based on a review of options available in the InnoSetup source code of ScriptClasses_C.pas My original example I thought
that TRadioButton and TCheckBox where individual controls. They instead its one control called TNewCheckListBox. There is a couple of ways someone could pull this off but the safest way is to use.
This example is a complete Inno Setup Script.
[Setup]
AppName='Test Date Script'
AppVerName='Test Date Script'
DefaultDirName={pf}\test
[Code]
const
cCheckBox = false;
cRadioButton = true;
var
Opt : TInputOptionWizardPage;
function BoolToStr(Value : Boolean) : String;
begin
if Value then
result := 'true'
else
result := 'false';
end;
procedure ClickEvent(Sender : TObject);
var
Msg : String;
I : Integer;
begin
// Click Event, allowing inspection of the Values.
Msg := 'The Following Items are Checked' +#10#13;
Msg := Msg + 'Values[0]=' + BoolToStr(Opt.Values[0]) +#10#13;
Msg := Msg + 'Values[1]=' + BoolToStr(Opt.Values[1]) +#10#13;
Msg := Msg + 'Values[2]=' + BoolToStr(Opt.Values[2]);
MsgBox(Msg,mbInformation,MB_OK);
end;
procedure InitializeWizard();
var
I : Integer;
ControlType : Boolean;
begin
ControlType := cCheckBox;
Opt := CreateInputOptionPage(1,'Caption','Desc','SubCaption',ControlType, false);
Opt.Add('Test1');
Opt.Add('Test2');
Opt.Add('Test3');
// Assign the Click Event.
Opt.CheckListBox.OnClickCheck := #ClickEvent;
end;
You can also control tasks by parent relationships, it gives you a similar behavior to what your asking for but is not 100% the same. I know this does not answer your question directly, but intends to give you an option that maybe easier to implement. Doing it this way you don't have to worry about managing a custom dialog at all.
[Setup]
;This allows you to show Lines showing parent / Child Relationships
ShowTasksTreeLines=yes
[Tasks]
;Parent Tasks don't use "\"
Name: p1; Description: P1 Test;
;Child Tasks are named ParentTaskName\ChildTaskName
;Flag don't inheritcheck:Specifies that the task
;should not automatically become checked when its parent is checked
Name: p1\c1; Description: C1 Test; Flags: dontinheritcheck;
Name: p1\c2; Description: C2 Test;
;Default behavior is that child must be selected
;when a parent is selected
;this can be overridden using the:
;doninheritcheck flag and the checkablealone flag.
Name: p2; Description: P2 Test; Flags: checkablealone;
Name: p2\c1; Description: P2-C1 Test; Flags: dontinheritcheck;
Name: p2\c2; Description: P2-C2 Test; Flags: dontinheritcheck;

Resources