1

Our software supports a range (~70) of file associations that a user can select to associate with our application. It is straight forward to set this up in the Registry section of the installer and use flags to control behavior during uninstalling as well as the Check flag to control if it should be written to the registry or not. The users can control which associations to setup via a custom page with a CheckListBox.

What I'm wondering now is if there is a method to loop over the CheckBox items of the CheckListBox inside the Registry section? Currently I can only imagine that I would have to create all the registry entries via the Code section but that would require me to also write the code for the uninstaller as the flags controlling the uninstall behavior are not available here?

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Markus Heckmann
  • 307
  • 1
  • 6

2 Answers2

1

You cannot use Pascal code to generate Registry section entries.

But you can use Inno Setup preprocessor to generate both your Registry sections as well as the code for adding the extensions to the CheckListBox. Something like this:

#define Extension(Mode, Ext) \
  Mode == "Registry" ? \
    "Root: HKCU; Subkey: ""Software\My Company""; ValueType: string; ValueName: """ + \
      Ext + """; ValueData: ""yes""; Check: RegisterExtension('" + Ext + "')" + NewLine \
  : Mode == "Check" ? \
    "  if (Ext = '" + Ext + "') and CheckListBox.Checked[I] then Exit;" + NewLine + \
    "  Inc(I); " + NewLine \
  : Mode == "AddCheckbox" ? \
    "  CheckListBox.AddCheckBox('" + Ext + "', '', 0, True, True, False, True, nil);" + \
      NewLine \
  : ""


#define Extensions(Mode) \
  Extension(Mode, 'jpg') + \
  Extension(Mode, 'gif') + \
  Extension(Mode, 'png') + \
  ""
[Registry]
#emit Extensions("Registry")
[Code]

var
  CheckListBox: TNewCheckListBox;

function RegisterExtension(Ext: string): Boolean;
var
  I: Integer;
begin
  I := 0;
  Result := True;
  #emit Extensions("Check")
  Result := False;
end;

procedure AddExtensionsToCheckListBox;
begin
  #emit Extensions("AddCheckbox")
end;
  • Registry entry is just a (useless) example.
  • Call AddExtensionsToCheckListBox somewhere from the code, where you are creating the CheckListBox.

See my other answer for a less elegant, but more readable solution.


To see what this does, add this to the end of your script:

#expr SaveToFile(AddBackslash(SourcePath) + "Preprocessed.iss")

Check the the Preprocessed.iss after compilation. It should give you something like (empty lines and line wrapping added for readability):

[Registry]
Root: HKCU; Subkey: "Software\My Company"; ValueType: string; ValueName: "jpg"; \
  ValueData: "yes"; Check: RegisterExtension('jpg')
Root: HKCU; Subkey: "Software\My Company"; ValueType: string; ValueName: "gif"; \
  ValueData: "yes"; Check: RegisterExtension('gif')
Root: HKCU; Subkey: "Software\My Company"; ValueType: string; ValueName: "png"; \
  ValueData: "yes"; Check: RegisterExtension('png')

[Code]
var
  CheckListBox: TNewCheckListBox;

function RegisterExtension(Ext: string): Boolean;
var
  I: Integer;
begin
  I := 0;
  Result := True;
  if (Ext = 'jpg') and CheckListBox.Checked[I] then Exit;
  Inc(I); 
  if (Ext = 'gif') and CheckListBox.Checked[I] then Exit;
  Inc(I); 
  if (Ext = 'png') and CheckListBox.Checked[I] then Exit;
  Inc(I); 
  Result := False;
end;

procedure AddExtensionsToCheckListBox;
begin
  CheckListBox.AddCheckBox('jpg', '', 0, True, True, False, True, nil);
  CheckListBox.AddCheckBox('gif', '', 0, True, True, False, True, nil);
  CheckListBox.AddCheckBox('png', '', 0, True, True, False, True, nil);
end;

For somewhat different approach that makes use of event attributes that were not available, at the time I wrote this answer:
Array Variables and dynamic access in [Code] section

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
0

I've came up with an alternative solution to my first answer. It is less elegant. But probably a way more readable, particularly to someone not experienced with Inno Setup preprocessor syntax.

It uses the preprocessor #sub directive, which allows using native Inno Setup syntax to generate the Registry section, instead of constructing it using a preprocessor expression. The preprocessor code generates a poll of indexed entries, which are conditionally used on runtime. You just need to make sure that MaxExt is large enough to fit all your extensions.

I didn't test it, so there might be minor bugs. But the concept will work.

#define MaxExt 100

[Registry]
#define I

#sub RegistryEntry
Root: HKCU; Subkey: "Software\My Company"; ValueType: string; \
  ValueName: "{code:GetExt|{#I}}"; ValueData: "yes"; Check: RegisterExt({#I})
#endsub

#for {I = 0; I < MaxExt; I++} RegistryEntry
[Code]

var
  Exts: TStrings;
  CheckListBox: TNewCheckListBox;

function RegisterExt(I: Integer): Boolean;
begin
  Result := (I < CheckListBox.Items.Count) and CheckListBox.Checked[I];
end;

function GetExt(Param: string): string;
var
  I: Integer;
begin
  I := StrToInt(Param);
  if I < Exts.Count then Result := Exts[I]
    else Result := 'unused';
end;

procedure AddExt(Ext: string);
begin
  { Separate list of extensions is needed only in case that the list box items caption }
  { is more complex than a mere extension name. Otherwise you could read the extension }
  { name from checked item caption in GetExt. }
  Exts.Add(Ext);
  CheckListBox.AddCheckBox(Ext, '', 0, True, True, False, True, nil);
end;

procedure AddExtensionsToCheckListBox;
begin
  Exts := TStringList.Create;
  AddExt('jpg');
  AddExt('gif');
  AddExt('png');
  { ... }
end;
  • Registry entry is just a (useless) example.
  • Call AddExtensionsToCheckListBox somewhere from the code, where you are creating the CheckListBox.

To see what the preprocesor part does, add this to the end of your script:

#expr SaveToFile(AddBackslash(SourcePath) + "Preprocessed.iss")

Check the the Preprocessed.iss after compilation. It should contain lie of 100 generated entries like this:

[Registry]
Root: HKCU; Subkey: "Software\My Company"; ValueType: string; ValueName: "{code:GetExt|0}"; ValueData: "yes"; Check: RegisterExt(0)
Root: HKCU; Subkey: "Software\My Company"; ValueType: string; ValueName: "{code:GetExt|1}"; ValueData: "yes"; Check: RegisterExt(1)
Root: HKCU; Subkey: "Software\My Company"; ValueType: string; ValueName: "{code:GetExt|2}"; ValueData: "yes"; Check: RegisterExt(2)
...
Root: HKCU; Subkey: "Software\My Company"; ValueType: string; ValueName: "{code:GetExt|99}"; ValueData: "yes"; Check: RegisterExt(99)
Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
  • 1
    After a bit of trial and error to get the double quotes right in your initial, I got it all to work and added the option to also have groups in the CheckListBox. Writing the long expressions is a bit cumbersome when looking for errors but I'm very happy with your first solution. – Markus Heckmann Mar 03 '20 at 17:35