How to use Inno Setup preprocessor directive in [Code] section? - inno-setup

I feel like this has got to be something simple I'm missing - in Inno Setup, if I have passed in a directive variable, how do I use it inside the [Code] section?
Say I pass in /DMYVAR=1 to the Inno Setup engine.
In my .iss file I can have something like:
[Setup]
AppName=MyApp v{#MYVAR}
Down in my [Code] section I'd like to be able to use it like this:
function IsVersionOne(param: String): boolean;
begin
Result := {#MYVAR} == "1";
end;
This doesn't work :(

Preprocessor does not care, where its directives are expanded.
So {#name} syntax (inline preprocessor directive call) works everywhere, including Pascal Script.
Though, if you want to compare a define value as a string, you have to, of course, enclose it to quotes to make it a string. Also note that your == "1" syntax is wrong, Pascal uses single = and single quotes.
function IsVersionOne(param: String): boolean;
begin
Result := '{#MYVAR}' = '1';
end;
#expr SaveToFile(AddBackslash(SourcePath) + "Preprocessed.iss")
Run the compiler with /DMYVAR=1, and check the generated Preprocessed.iss. It will show:
function IsVersionOne(param: String): boolean;
begin
Result := '1' = '1';
end;
As the value is a number, you can, of course, use numerical comparison too:
function IsVersionOne(param: String): boolean;
begin
Result := {#MYVAR} = 1;
end;
For a related question with more detailed information, see Evaluate preprocessor macro on run time in Inno Setup Pascal Script.
While the above answered your literal question, your function signature actually suggests that you want to implement a Check function to test a value of a compile-time directive, like:
[Files]
Source: "MYPROG.EXE"; DestDir: "{app}"; Check: IsVersionOne
That's an inefficient overkill.
Use a preprocessor #if directive instead:
[Files]
#if MYVAR == "1"
Source: "MYPROG.EXE"; DestDir: "{app}"
#endif
#expr SaveToFile(AddBackslash(SourcePath) + "Preprocessed.iss")
If you run the compiler with /DMYVAR=1, Preprocessed.iss will show:
[Files]
Source: "MYPROG.EXE"; DestDir: "{app}"
If you run the compiler with a different value of DMYVAR, Preprocessed.iss will show:
[Files]
If you need the script to compile even without /DMYVAR= switch, define a default value at the top of the script, like:
#ifndef MYVAR
#define MYVAR "0"
#endif

Related

Evaluate a collection of data from preprocessor on run time in Inno Setup Pascal Script

I am trying to get Inno Setup define value in Code section but not with {#VersionTool1}. I need to pass defined name dynamically, because there are a lot of them (I want to avoid large switch case). I tried SetupSetting but it's not in Setup section (it's before it). Is there any way to do this?
#define VersionTool1 2019.01.1111
#define VersionTool2 2020.02.2111
...
[Code]
procedure SetSelectedTool(ToolName: String);
var
CurrentTool: string;
begin
...
CurrentTool := 'Version' + ToolName;
CurrentToolVersion := {#CurrentTool};
...
end;
Value of local variable CurrentTool wil for example be 'VersionTool1' and I want to get value of VersionTool1 preprocessor variable which is 2020.02.2111.
It's not possible, see Evaluate preprocessor macro on run time in Inno Setup Pascal Script.
But there are other solutions.
For example:
[Code]
var
ToolNames: TStringList;
ToolVersions: TStringList;
function InitializeSetup(): Boolean;
begin
ToolNames := TStringList.Create;
ToolVersions := TStringList.Create;
#define AddToolVersion(Name, Version) \
"ToolNames.Add('" + Name + "'); ToolVersions.Add('" + Version +"');"
#emit AddToolVersion('Tool1', '2019.01.1111')
#emit AddToolVersion('Tool2', '2020.02.2111')
{ ... }
Result := True;
end;
(of course, the above makes sense only if you actually do not hardcode the version numbers, but use a code that only a preprocessor can do – something like GetStringFileInfo, what I've understood from your comments that you plan to)
And then you can have a function like:
function GetToolVersion(ToolName: string): string;
var
I: Integer;
begin
I := ToolNames.IndexOf(ToolName);
if I >= 0 then Result := ToolVersions[I];
end;
Another similar questions:
Array Variables and dynamic access in [Code] section
Scripting capabilities in the Registry section

How do I copy a source file from a directory specified by the user? [duplicate]

So given this function, I get the error "Identifier Expected" on the GetRoot := ROOTPage.Values[0]; line. I expect it is telling me that the ROOTPage is not defined?
const
DefaultRoot = 'C:\IAmGRoot';
Var
ROOTPage : TInputQueryWizardPage;
procedure SetupRoot;
begin
ROOTPage := CreateInputQueryPage(wpUserInfo,
ExpandConstant('{cm:RootTitle}'),
ExpandConstant('{cm:RootInstructions}'),
ExpandConstant('{cm:RootDescription}') + ' "' + DefaultRoot + '"'
);
ROOTPage.Add(ExpandConstant('{cm:SSRoot}') + ':', False);
ROOTPage.Values[0] := ExpandConstant('{DefaultRoot}');
// add SSROOT to path
end;
function GetRoot : string;
begin
GetRoot := ROOTPage.Values[0];
end;
How should I interpret this error. What is an identifier in Pascal?
This page tells me that identifiers are variable names. Perhaps I need to expand the ROOTPage.Values[0] in some way since I am referencing an array from an Inno Setup object?
Or maybe I need to return the value differently. I saw one page on Pascal that said that you needed to avoid assigning to the function value on parameter less functions to avoid a recursive loop. Does this mean I should pass in a dummy value? or is there a different syntax? That page did not explain.
I secretly think my real issue is that I am not defining my function correctly... but well. That much compiles at least. This question could become: How do you handle a parameter-less function in Pascal?
I do not think Inno Setup is part of the issue, but I am working with Inno Setup in case that is important.
Update:
It doesn't seem to be the array because this gets the same error:
const
DefaultRoot = 'C:\IAmGRoot';
function GetRoot : string;
begin
GetRoot := DefaultRoot;
end;
Update:
This link has said that the function name can be replaced/ should be replaced with the key word Result such as the following code. I actually knew this, but the Inno Setup compiler does not recognize this as valid syntax. It then tells me my function is an invalid prototype.
function GetRoot : string;
begin
Result := DefaultRoot;
end;
Update:
If I do this I get "Invalid Prototype for GetRoot"
function GetRoot : boolean;
begin
Result := False;
end;
Update for #Martin Prikryl:
Well I use it a few places but the typical use would be like this:
[Files]
Source: "C:\ValidPath\Release\*"; DestDir: "{app}\bin"; Components: DefinedComponent
Source: "C:\ValidPath\Deployment\*"; DestDir: "{code:GetRoot}\"; Flags: ignoreversion recursesubdirs; Components: DefinedComponent
Identifier expected
Your code would be correct in a Pascal, but it does not compile in Pascal Script.
In Pascal, when you want to assign a return value of a function, you either assign the value to a "variable" with a name of the function or to Result variable.
So this is correct:
function GetRoot: string;
begin
GetRoot := ROOTPage.Values[0];
end;
And this too (both are equivalent):
function GetRoot: string;
begin
Result := ROOTPage.Values[0];
end;
In the Pascal Script, only the Result works. When you use the name of the function, you get the "Identifier expected."
Invalid prototype
You get this when the function is called from outside of the Code section and a specific parameter list/return value is required. But you didn't tell us, what you use the GetRoot function for.
There are two places, where you can use a custom function in Inno Setup:
Check parameter: For this the function must return a Boolean and take either no parameter or one parameter (the parameter type is determined by a value you provide in the Check parameter).
function MyProgCheck(): Boolean;
function MyDirCheck(DirName: String): Boolean;
Scripted Constants: The function must return a string and take one string parameter, even if no parameter is provided in the scripted constant. I assume this is your use case. If you do not need any parameter, just declare it, but do not use it:
function GetRoot(Param: String): string;
begin
Result := ROOTPage.Values[0];
end;

Using a standard Inno Setup message in the Run entry description

There is a related question for those who are interested here. I was encouraged to ask one of the questions separately.
So I have this [Run] code:
Filename: "{cmd}"; Parameters: "/C exit"; Description: "Exit Setup"; \
Flags: nowait postinstall runasoriginaluser unchecked skipifsilent; Check: IsWin64
It works well. It shows the following window at the end of the installation:
But the problem is that the wording Exit Setup is hard coded in English. Now, I have looked at the default.isl file and located:
ExitSetupTitle=Exit Setup
ClickFinish=Click Finish to exit Setup.
I don't know if I should be using either of these messages. It makes sense to me to use the ClickFinish because that is what the user will be doing.
But I can't work out how to use that message in the run statement.
Update
I have modified the supplied answer to:
function GetClickFinishSetupMessage(Param: string): string;
var
I: integer;
S1: string;
S2: string;
begin
S1 := SetupMessage(msgClickFinish);
I := Length(S1);
S2 := Copy(S1, I-1, 1);
if(S2 = '.') then
S1 := Delete(S1, I-1, 1);
end;
result := S1;
end;
So I could remove the final period. But it will not compiled. It says there is a mismatch for the third parameter of Delete.
Use SetupMessage support function from a scripted constant:
[Run]
Filename: ...; Description: {code:GetClickFinishSetupMessage}
[Code]
function GetClickFinishSetupMessage(Param: string): string;
begin
Result := SetupMessage(msgClickFinish);
{ Drop trailing dot, if any }
if Copy(Result, Length(Result), 1) = '.' then
SetLength(Result, Length(Result) - 1);
end;

Inno Setup define a hexadecimal color constant

I am trying to use the [ISPP] section to define a hexadecimal color which will later be used in the [Code] section as a spot color, the value of which may change in the future, but I am getting a type mismatch error when running. Here are the relevant sections from the code:
[ISPP]
#define ColorPetrol "$C8C264"
[Code]
procedure InitializeWizard();
var
PortLabel: TNewStaticText;
begin
PortLabel := TNewStaticText.Create(WizardForm);
PortLabel.Caption := 'Port';
PortLabel.Top := ScaleY(78);
PortLabel.Parent := Page.Surface;
PortLabel.Font.Color := ExpandConstant('{#ColorPetrol}');
end;
I am assuming the error is caused by the define constant being a string and the PortLabel.Font.Color requiring a hex value. How can the constant be defined in the [ISPP] section and used in this way correctly?
Just use PortLabel.Font.Color := {#ColorPetrol};. ExpandConstant() is for expanding built-in Inno Setup constants, not for ISPP defines. The latter really are just about textual replacements.
As a side note, I'm not aware of an [ISPP] section. IMO you should just move the define to the [Code] section.

Conditional DisableProgramGroupPage in Inno Setup

I'm trying to create a single installer for both normal and portable installs. For the portable install I am disabling all icons and uninstaller creation.
The only problem I am stuck at is how to disable program group page when running the portable install. Am I misunderstanding something here?
[Setup]
;This works as expected
Uninstallable=not IsPortable()
;This does NOT work, can't compile (DisableProgramGroupPage=yes alone compiles fine)
DisableProgramGroupPage=yes IsPortable()
Compilation fails with error
Value of [Setup] section directive ... is invalid.
This is the IsPortable() function:
function IsPortable(): Boolean;
begin
if(StandardRadioButton.Checked = True) then
Result := False
else
Result := True;
end;
(Elaborating on #TLama's comment)
The DisableProgramGroupPage does not support "boolean expression":
[Setup]: DisableProgramGroupPage
Valid values: auto, yes, or no
Contrary to Uninstallable:
[Setup]: Uninstallable
Valid values: yes or no, or a boolean expression
You can use ShouldSkipPage event function instead:
function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := False;
if PageID = wpSelectProgramGroup then
begin
Result := IsPortable;
end;
end;

Resources