Add another destination behind path read from registry - inno-setup

How is possible to add two subfolders behind a path read from registry?
[Code]
function GetDirName(Value: string): string;
var
InstallPath: string;
begin
Result := 'C:\Program Files (x86)\myapp\subfolder1\subfolder2';
if RegQueryStringValue(HKLM, 'SOFTWARE\Wow6432Node\myapp', 'RootPath', InstallPath) then
Result := InstallPath
else
// query the second registry value; if it succeeds, return the obtained value
if RegQueryStringValue(HKLM, '\SOFTWARE\myapp', 'RootPath', InstallPath) then
Result := InstallPath;
end;
When I got the destination from register I need to add behind it \subfolder1\subfolder2 and complete destination get to function GetDirName.
Can someone guide me?
Thanks a lot.

Just concatenate the two strings at the end of the GetDirName function:
function GetDirName(Value: string): string;
var
InstallPath: string;
begin
// expand path to the 32-bit Program Files folder with appended 'myapp' subfolder
Result := ExpandConstant('{pf32}\myapp');
// query value from 64-bit registry node (notice the used HKLM64 root key)
if RegQueryStringValue(HKLM64, 'SOFTWARE\myapp', 'RootPath', InstallPath) or
// query value from 32-bit registry node (notice the used HKLM32 root key)
RegQueryStringValue(HKLM32, 'SOFTWARE\myapp', 'RootPath', InstallPath) then
begin
Result := InstallPath;
end;
// ensure the path will have backslash and append the final subdirectory string
Result := AddBackslash(Result) + 'subfolder1\subfolder2';
end;

Related

Downloading files defined in start arguments with Inno Setup Script [duplicate]

I am preparing an installer with Inno Setup. But I'd like to add an additional custom (none of the available parameters) command line parameters and would like to get the value of the parameter, like:
setup.exe /do something
Check if /do is given, then get the value of something. Is it possible? How can I do this?
With InnoSetup 5.5.5 (and perhaps other versions), just pass whatever you want as a parameter, prefixed by a /
c:\> myAppInstaller.exe /foo=wiggle
and in your myApp.iss:
[Setup]
AppName = {param:foo|waggle}
The |waggle provides a default value if no parameter matches. Inno setup is not case sensitive. This is a particularly nice way to handle command line options: They just spring into existence. I wish there was as slick a way to let users know what command line parameters the installer cares about.
BTW, this makes both #knguyen's and #steve-dunn's answers somewhat redundant. The utility functions do exactly what the built-in {param: } syntax does.
Further to #DanLocks' answer, the {param:*ParamName|DefaultValue*} constant is documented near the bottom of the Constants page:
http://www.jrsoftware.org/ishelp/index.php?topic=consts
I found it quite handy for optionally suppressing the license page. Here is all I needed to add (using Inno Setup 5.5.6(a)):
[code]
{ If there is a command-line parameter "skiplicense=true", don't display license page }
function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := False
if PageId = wpLicense then
if ExpandConstant('{param:skiplicense|false}') = 'true' then
Result := True;
end;
Inno Setup directly supports switches with syntax /Name=Value using {param} constant.
You can use the constant directly in sections, though this use is quite limited.
An example:
[Registry]
Root: HKCU; Subkey: "Software\My Company\My Program\Settings"; ValueType: string; \
ValueName: "Mode"; ValueData: "{param:Mode|DefaultMode}"
You will more likely want to use switches in Pascal Script.
If your switch has the syntax /Name=Value, the easiest way to read its value is using ExpandConstant function.
For example:
if ExpandConstant('{param:Mode|DefaultMode}') = 'DefaultMode' then
begin
Log('Installing for default mode');
end
else
begin
Log('Installing for different mode');
end;
If you want to use a switch value to toggle entries in sections, you can use Check parameter and a auxiliary function, like:
[Files]
Source: "Client.exe"; DestDir: "{app}"; Check: SwitchHasValue('Mode', 'Client')
Source: "Server.exe"; DestDir: "{app}"; Check: SwitchHasValue('Mode', 'Server')
[Code]
function SwitchHasValue(Name: string; Value: string): Boolean;
begin
Result := CompareText(ExpandConstant('{param:' + Name + '}'), Value) = 0;
end;
Ironically it is more difficult to check for a mere presence of switch (without a value).
Use can use a function CmdLineParamExists from #TLama's answer to Passing conditional parameter in Inno Setup.
function CmdLineParamExists(const Value: string): Boolean;
var
I: Integer;
begin
Result := False;
for I := 1 to ParamCount do
if CompareText(ParamStr(I), Value) = 0 then
begin
Result := True;
Exit;
end;
end;
You can obviously use the function in Pascal Script:
if CmdLineParamExists('/DefaultMode') then
begin
Log('Installing for default mode');
end
else
begin
Log('Installing for different mode');
end;
But you can even use it in sections, most typically using Check parameter:
[Files]
Source: "MyProg.hlp"; DestDir: "{app}"; Check: CmdLineParamExists('/InstallHelp')
A related problem:
Add user defined command line parameters to /? window
If you want to parse command line arguments from code in inno, then use a method similar to this. Just call the inno script from the command line as follows:
c:\MyInstallDirectory>MyInnoSetup.exe -myParam parameterValue
Then you can call the GetCommandLineParam like this wherever you need it:
myVariable := GetCommandLineParam('-myParam');
{ ================================================================== }
{ Allows for standard command line parsing assuming a key/value organization }
function GetCommandlineParam (inParam: String):String;
var
LoopVar : Integer;
BreakLoop : Boolean;
begin
{ Init the variable to known values }
LoopVar :=0;
Result := '';
BreakLoop := False;
{ Loop through the passed in arry to find the parameter }
while ( (LoopVar < ParamCount) and
(not BreakLoop) ) do
begin
{ Determine if the looked for parameter is the next value }
if ( (ParamStr(LoopVar) = inParam) and
( (LoopVar+1) <= ParamCount )) then
begin
{ Set the return result equal to the next command line parameter }
Result := ParamStr(LoopVar+1);
{ Break the loop }
BreakLoop := True;
end;
{ Increment the loop variable }
LoopVar := LoopVar + 1;
end;
end;
This is the function I wrote, which is an improvement of Steven Dunn's answer. You can use it as:
c:\MyInstallDirectory>MyInnoSetup.exe /myParam="parameterValue"
myVariable := GetCommandLineParam('/myParam');
{ util method, equivalent to C# string.StartsWith }
function StartsWith(SubStr, S: String): Boolean;
begin
Result:= Pos(SubStr, S) = 1;
end;
{ util method, equivalent to C# string.Replace }
function StringReplace(S, oldSubString, newSubString: String): String;
var
stringCopy: String;
begin
stringCopy := S; { Prevent modification to the original string }
StringChange(stringCopy, oldSubString, newSubString);
Result := stringCopy;
end;
{ ================================================================== }
function GetCommandlineParam(inParamName: String): String;
var
paramNameAndValue: String;
i: Integer;
begin
Result := '';
for i := 0 to ParamCount do
begin
paramNameAndValue := ParamStr(i);
if (StartsWith(inParamName, paramNameAndValue)) then
begin
Result := StringReplace(paramNameAndValue, inParamName + '=', '');
break;
end;
end;
end;
Yes it is possible, you can use the ParamStr function in PascalScript to access all the commandline parameters. The ParamCount function will give you the number of commandline parameters.
Another possibility is to use GetCmdTail
In response to:
"With InnoSetup 5.5.5 (and perhaps other versions), just pass whatever you want as a parameter, prefixed by a /"
"#NickG, yes, every constant you can expand by the ExpandConstant function"
This is not the case. Trying to use a command line parameter in ExpandConstant in InnoSetup 5.5.6 results in a runtime error.
PS: I would have added a comment directly but apparently I dont have enough "reputation"
I've modified a little bit knguyen's answer. Now it's case insensitive (you can write en console /myParam or /MYPARAM) and it can accept default value. Also I fixed the case when you receive larger parameter then expected (for ex: /myParamOther="parameterValue" in place of /myParam="parameterValue". Now myParamOther doesn't match).
function GetCommandlineParam(inParamName: String; defaultParam: String): String;
var
paramNameAndValue: String;
i: Integer;
begin
Result := defaultParam;
for i := 0 to ParamCount do
begin
paramNameAndValue := ParamStr(i);
if (Pos(Lowercase(inParamName)+'=', AnsiLowercase(paramNameAndValue)) = 1) then
begin
Result := Copy(paramNameAndValue, Length(inParamName)+2, Length(paramNameAndValue)-Length(inParamName));
break;
end;
end;
end;
I found the answer: GetCmdTail.
You can pass parameters to your installer scripts. Install the Inno Setup Preprocessor and read the documentation on passing custom command-line parameters.

Inno Setup avoiding duplicate registry entries for path? [duplicate]

Inno Setup lets you set environment variables via the [Registry] sections (by setting registry key which correspond to environment variable)
However, sometimes you don't just wanna set an environment variable. Often, you wanna modify it. For example: upon installation, one may want to add/remove a directory to/from the PATH environment variable.
How can I modify the PATH environment variable from within InnoSetup?
The path in the registry key you gave is a value of type REG_EXPAND_SZ. As the Inno Setup documentation for the [Registry] section states there is a way to append elements to those:
On a string, expandsz, or multisz type value, you may use a special constant called {olddata} in this parameter. {olddata} is replaced with the previous data of the registry value. The {olddata} constant can be useful if you need to append a string to an existing value, for example, {olddata};{app}. If the value does not exist or the existing value isn't a string type, the {olddata} constant is silently removed.
So to append to the path a registry section similar to this may be used:
[Registry]
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; \
ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};C:\foo"
which would append the "C:\foo" directory to the path.
Unfortunately this would be repeated when you install a second time, which should be fixed as well. A Check parameter with a function coded in Pascal script can be used to check whether the path does indeed need to be expanded:
[Registry]
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; \
ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};C:\foo"; \
Check: NeedsAddPath('C:\foo')
This function reads the original path value and checks whether the given directory is already contained in it. To do so it prepends and appends semicolon chars which are used to separate directories in the path. To account for the fact that the searched for directory may be the first or last element semicolon chars are prepended and appended to the original value as well:
[Code]
function NeedsAddPath(Param: string): boolean;
var
OrigPath: string;
begin
if not RegQueryStringValue(HKEY_LOCAL_MACHINE,
'SYSTEM\CurrentControlSet\Control\Session Manager\Environment',
'Path', OrigPath)
then begin
Result := True;
exit;
end;
{ look for the path with leading and trailing semicolon }
{ Pos() returns 0 if not found }
Result := Pos(';' + Param + ';', ';' + OrigPath + ';') = 0;
end;
Note that you may need to expand constants before you pass them as parameter to the check function, see the documentation for details.
Removing this directory from the path during uninstallation can be done in a similar fashion and is left as an exercise for the reader.
I had the same problem but despite the answers above I've ended up with a custom solution and I'd like to share it with you.
First of all I've created the environment.iss file with 2 methods - one for adding path to the environment's Path variable and second to remove it:
[Code]
const EnvironmentKey = 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment';
procedure EnvAddPath(Path: string);
var
Paths: string;
begin
{ Retrieve current path (use empty string if entry not exists) }
if not RegQueryStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths)
then Paths := '';
{ Skip if string already found in path }
if Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';') > 0 then exit;
{ App string to the end of the path variable }
Paths := Paths + ';'+ Path +';'
{ Overwrite (or create if missing) path environment variable }
if RegWriteStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths)
then Log(Format('The [%s] added to PATH: [%s]', [Path, Paths]))
else Log(Format('Error while adding the [%s] to PATH: [%s]', [Path, Paths]));
end;
procedure EnvRemovePath(Path: string);
var
Paths: string;
P: Integer;
begin
{ Skip if registry entry not exists }
if not RegQueryStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths) then
exit;
{ Skip if string not found in path }
P := Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';');
if P = 0 then exit;
{ Update path variable }
Delete(Paths, P - 1, Length(Path) + 1);
{ Overwrite path environment variable }
if RegWriteStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths)
then Log(Format('The [%s] removed from PATH: [%s]', [Path, Paths]))
else Log(Format('Error while removing the [%s] from PATH: [%s]', [Path, Paths]));
end;
Reference: RegQueryStringValue, RegWriteStringValue
Now in main .iss file I could include this file and listen for the 2 events (more about events you can learn in Event Functions section in documentation), CurStepChanged to add path after installation and CurUninstallStepChanged to remove it when user uninstall an application. In below example script add/remove the bin directory (relative to the installation directory):
#include "environment.iss"
[Setup]
ChangesEnvironment=true
; More options in setup section as well as other sections like Files, Components, Tasks...
[Code]
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssPostInstall
then EnvAddPath(ExpandConstant('{app}') +'\bin');
end;
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
if CurUninstallStep = usPostUninstall
then EnvRemovePath(ExpandConstant('{app}') +'\bin');
end;
Reference: ExpandConstant
Note #1: Install step add path only once (ensures repeatability of the installation).
Note #2: Uninstall step remove only one occurrence of the path from variable.
Bonus: Installation step with checkbox "Add to PATH variable".
To add installation step with checkbox "Add to PATH variable" define new task in [Tasks] section (checked by default):
[Tasks]
Name: envPath; Description: "Add to PATH variable"
Then you can check it in CurStepChanged event:
procedure CurStepChanged(CurStep: TSetupStep);
begin
if (CurStep = ssPostInstall) and IsTaskSelected('envPath')
then EnvAddPath(ExpandConstant('{app}') +'\bin');
end;
You can use LegRoom.net's modpath.iss script in your InnoSetup script file:
#define MyTitleName "MyApp"
[Setup]
ChangesEnvironment=yes
[CustomMessages]
AppAddPath=Add application directory to your environmental path (required)
[Files]
Source: "install\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs;
[Icons]
Name: "{group}\{cm:UninstallProgram,{#MyTitleName}}"; Filename: "{uninstallexe}"; Comment: "Uninstalls {#MyTitleName}"
Name: "{group}\{#MyTitleName}"; Filename: "{app}\{#MyTitleName}.EXE"; WorkingDir: "{app}"; AppUserModelID: "{#MyTitleName}"; Comment: "Runs {#MyTitleName}"
Name: "{commondesktop}\{#MyTitleName}"; Filename: "{app}\{#MyTitleName}.EXE"; WorkingDir: "{app}"; AppUserModelID: "{#MyTitleName}"; Comment: "Runs {#MyTitleName}"
[Registry]
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}"
[Tasks]
Name: modifypath; Description:{cm:AppAddPath};
[Code]
const
ModPathName = 'modifypath';
ModPathType = 'system';
function ModPathDir(): TArrayOfString;
begin
setArrayLength(Result, 1)
Result[0] := ExpandConstant('{app}');
end;
#include "modpath.iss"
The NeedsAddPath in the answer by #mghie doesn't check trailing \ and letter case. Fix it.
function NeedsAddPath(Param: string): boolean;
var
OrigPath: string;
begin
if not RegQueryStringValue(
HKEY_LOCAL_MACHINE,
'SYSTEM\CurrentControlSet\Control\Session Manager\Environment',
'Path', OrigPath)
then begin
Result := True;
exit;
end;
{ look for the path with leading and trailing semicolon }
{ Pos() returns 0 if not found }
Result :=
(Pos(';' + UpperCase(Param) + ';', ';' + UpperCase(OrigPath) + ';') = 0) and
(Pos(';' + UpperCase(Param) + '\;', ';' + UpperCase(OrigPath) + ';') = 0);
end;
I want to thank everyone for their contributions to this question. I've incorporated about 95% of the code posted by Wojciech Mleczek into my app's installer. I do have some corrections to that code that may prove useful to others. My changes:
Renamed formal argument Path to instlPath. Cuts down on multiple uses of "Path" in code (easier to read, IMO).
When installing/uninstalling, add an existence check for an instlPath that ends with \;.
During installation, don't double up ; in the current %PATH%.
Handle missing or empty %PATH% during installation.
During uninstall, make sure that a starting index of 0 is not passed to Delete().
Here's my updated version of EnvAddPath():
const EnvironmentKey = 'Environment';
procedure EnvAddPath(instlPath: string);
var
Paths: string;
begin
{ Retrieve current path (use empty string if entry not exists) }
if not RegQueryStringValue(HKEY_CURRENT_USER, EnvironmentKey, 'Path', Paths) then
Paths := '';
if Paths = '' then
Paths := instlPath + ';'
else
begin
{ Skip if string already found in path }
if Pos(';' + Uppercase(instlPath) + ';', ';' + Uppercase(Paths) + ';') > 0 then exit;
if Pos(';' + Uppercase(instlPath) + '\;', ';' + Uppercase(Paths) + ';') > 0 then exit;
{ Append App Install Path to the end of the path variable }
Log(Format('Right(Paths, 1): [%s]', [Paths[length(Paths)]]));
if Paths[length(Paths)] = ';' then
Paths := Paths + instlPath + ';' { don't double up ';' in env(PATH) }
else
Paths := Paths + ';' + instlPath + ';' ;
end;
{ Overwrite (or create if missing) path environment variable }
if RegWriteStringValue(HKEY_CURRENT_USER, EnvironmentKey, 'Path', Paths)
then Log(Format('The [%s] added to PATH: [%s]', [instlPath, Paths]))
else Log(Format('Error while adding the [%s] to PATH: [%s]', [instlPath, Paths]));
end;
And an updated version of EnvRemovePath():
procedure EnvRemovePath(instlPath: string);
var
Paths: string;
P, Offset, DelimLen: Integer;
begin
{ Skip if registry entry not exists }
if not RegQueryStringValue(HKEY_CURRENT_USER, EnvironmentKey, 'Path', Paths) then
exit;
{ Skip if string not found in path }
DelimLen := 1; { Length(';') }
P := Pos(';' + Uppercase(instlPath) + ';', ';' + Uppercase(Paths) + ';');
if P = 0 then
begin
{ perhaps instlPath lives in Paths, but terminated by '\;' }
DelimLen := 2; { Length('\;') }
P := Pos(';' + Uppercase(instlPath) + '\;', ';' + Uppercase(Paths) + ';');
if P = 0 then exit;
end;
{ Decide where to start string subset in Delete() operation. }
if P = 1 then
Offset := 0
else
Offset := 1;
{ Update path variable }
Delete(Paths, P - Offset, Length(instlPath) + DelimLen);
{ Overwrite path environment variable }
if RegWriteStringValue(HKEY_CURRENT_USER, EnvironmentKey, 'Path', Paths)
then Log(Format('The [%s] removed from PATH: [%s]', [instlPath, Paths]))
else Log(Format('Error while removing the [%s] from PATH: [%s]', [instlPath, Paths]));
end;
Here is a complete solution to the problem that ignores casing, checks for existence of path ending with \ and also expands the constants in the param:
function NeedsAddPath(Param: string): boolean;
var
OrigPath: string;
ParamExpanded: string;
begin
//expand the setup constants like {app} from Param
ParamExpanded := ExpandConstant(Param);
if not RegQueryStringValue(HKEY_LOCAL_MACHINE,
'SYSTEM\CurrentControlSet\Control\Session Manager\Environment',
'Path', OrigPath)
then begin
Result := True;
exit;
end;
// look for the path with leading and trailing semicolon and with or without \ ending
// Pos() returns 0 if not found
Result := Pos(';' + UpperCase(ParamExpanded) + ';', ';' + UpperCase(OrigPath) + ';') = 0;
if Result = True then
Result := Pos(';' + UpperCase(ParamExpanded) + '\;', ';' + UpperCase(OrigPath) + ';') = 0;
end;
If you are ok with using an external DLL, PathMgr.dll could also be an option.
There is a sample .iss script that demonstrates how to use the DLL in Inno Setup 6 or later.
PathMgr.dll is covered by the LPGL license.

How to check if a string ends with another (EndsWith) in Inno Setup

I need to write a logic in some Inno Setup function that checks if a string ends with another.
Can I use StrUtils Pascal functions (EndsWith) to do so?
function NextButtonClick(CurPageID: Integer): Boolean;
var
dir_value: String; app_name: String;
begin
if CurPageID = wpSelectDir then
begin
dir_value := "C:\work\ABC"
app_name := "ABC"
{ I need to write a logic here to check if dir_value ends with app_name }
end;
end;
There's no EndsWith in Inno Setup.
But you can easily implement it:
function EndsWith(SubText, Text: string): Boolean;
var
EndStr: string;
begin
EndStr := Copy(Text, Length(Text) - Length(SubText) + 1, Length(SubText));
{ Use SameStr, if you need a case-sensitive comparison }
Result := SameText(SubText, EndStr);
end;
Though in your case, you actually need something like this:
function EndsWithFileName(FileName, Path: string): Boolean;
begin
Result := SameText(FileName, ExtractFileName(Path));
end;
For SameText (and SameStr), you need Inno Setup 6. On older versions, you can replace them with CompareText (and CompareStr).

How to find a component index by it's name?

Why can't I get one of my component's index by using this line:
WizardForm.ComponentsList.FindComponent('core').ComponentIndex
If I'm wrong, can anyone point out for me a way to get that index of component?
Currently, there's no way to find index of a component item in the ComponentsList by the component Name parameter. The Name parameter is stored in the TSetupComponentEntry structure, which is held by the ComponentsList.ItemObject[i] object collection, but you can't access it due to a lack of missing pointer support in Inno Setup Pascal Script.
The only way how to uniquely identify a component in the ComponentsList is by its Description parameter. To find index of a certain component by its description in the ComponentsList, you can use this:
[Components]
Name: "mycomponent"; Description: "Component description"; Types: full
[Code]
...
var
ItemIndex: Integer;
begin
ItemIndex := WizardForm.ComponentsList.Items.IndexOf('Component description');
...
end;
I found a way to do this through WizardSelectedComponents.
Define in var this array of String in order to save components name:
ComponentsName: array of String;
Define this function and this procedure:
Function StringToArray(const Text: String; const Cut: String): array of String;
var
i: Integer;
k: Integer;
Begin
SetArrayLength(Result, 0);
if Cut = '' then Cut:= #1310;
Repeat
k:= Pos(Cut,Text);
if k = 1 then
begin
Delete(Text, 1, Length(Cut));
CONTINUE
end;
SetArrayLength(Result, GetArrayLength(Result) +1); i:= GetArrayLength(Result) -1;
if k = 0 then
Result[i]:=Text
else begin
Result[i]:= Copy(Text, 1, k -1); Delete(Text, 1, Length(Result[i]) + Length(Cut));
end;
Until Length(Text) * k = 0;
End;
procedure InitializeComponentsName();
var
I: Integer;
CheckBack: array of boolean;
begin
SetArrayLength(CheckBack, WizardForm.ComponentsList.ItemCount);
for I:=0 to WizardForm.ComponentsList.ItemCount-1 do
begin
//Saves state in CheckBack.
CheckBack[I]:=WizardForm.ComponentsList.Checked[I];
//Only checks non checked components.
if not CheckBack[I] then WizardForm.ComponentsList.Checked[I]:=true;
end;
//Saves components names in ComponentsName array
ComponentsName:=StringToArray(WizardSelectedComponents(false), ',');
//Unchecks components that was uncheck previouly.
//If we try to check a checked component it may crash the Inno program (tested)
for I:=0 to WizardForm.ComponentsList.ItemCount-1 do
begin
if not CheckBack[I] then WizardForm.ComponentsList.Checked[I]:=false;
end;
//LOG components name.
log('COMPONENTS NAME:');
for I:=0 to GetArrayLength(ComponentsName) -1 do
begin
log(ComponentsName[I]);
end;
end;
You must call this procedure on InitializeWizard.
Now we have all components name in ComponentsName array.
You can use getComponentName to get your component name:
//Returns component name by Index.
function getComponentName(Index: Integer): String;
begin
if ((Index>=0) and (Index<GetArrayLength(ComponentsName))) then Result:=ComponentsName[Index];
end;
and GetIndexComponent to get your Index component by name.
//Returns index component by Name. -1 if it doesn't exist.
function GetIndexComponent(ComponentName: String): Integer;
var
J: Integer;
begin
Result:= -1;
for J:=0 to GetArrayLength(ComponentsName)-1 do
begin
if (ComponentName=ComponentsName[J]) then
begin
Result:=J;
break;
end;
end;
end;
If you add another component to ComponentsList you must call InitializeComponentsName in order to update ComponentsList array.

How to convert the contents of AppID in [setup] into a string in [code]?

When I use the INNO wizard I get an *.iss file that contains in its setup section:
[Setup]
AppId={87E1AD40-F32B-4EF7-A2FF-5B508814068A}
<statements not included here}
I then add a procedure in the code section for the generation of an *.ini file to be used as input to my application. The code section contains the following:
[Code]
procedure CurStepChanged(CurStep: TSetupStep);
// Purpose: write an *.ini file
// Used as input to the program to be executed
var
S: string;
begin
if CurStep = ssPostInstall then
begin
//* Output language entered
S := Format('[%s]'+#13#10, ['LANGUAGE']);
SaveStringToFile(ExpandConstant('{app}\UserInputs.ini'), S, False);
S := Format('language = %s'+#13#10, [ActiveLanguage]);
SaveStringToFile(ExpandConstant('{app}\UserInputs.ini'), S, True);
<code not included here>
//* Output AppId code generated by INNO
S := Format('[%s]'+#13#10, ['REGISTRATION']); // key word
SaveStringToFile(ExpandConstant('{app}\UserInputs.ini'), S, True);
// S := Format(??)
//SaveStringToFile(ExpandConstant('{app}\UserInputs.ini'), S, True);
end;
end;
How can I Format AppId so that S will contain "87E1AD40-F32B-4EF7-A2FF-5B508814068A" [i.e., S := Format(??)]?
If you want to expand certain [Setup] section setting in your code, you can use the SetupSetting preprocessor function. In your question you've mentioned you want to get the AppId directive value, which you've set to a GUID value including {} chars, which you want to have stripped on your output. The following script shows how to get the AppId directive value and how to copy just the part without those enclosing {} chars:
[Setup]
AppId={{87E1AD40-F32B-4EF7-A2FF-5B508814068A}
AppName=My Program
AppVersion=1.5
DefaultDirName=My Program
[Code]
procedure InitializeWizard;
var
S: string;
begin
S := '{#SetupSetting("AppId")}';
S := Copy(S, 3, Length(S) - 3);
MsgBox(S, mbInformation, MB_OK);
end;

Resources