inno-setuppascalscript

Scripting capabilities in the Registry section


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?


Solution

  • 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;
    

    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