Răsfoiți Sursa

Merge branch 'leserg73-main_regfile_v2'

Martijn Laan 1 an în urmă
părinte
comite
8b9dd6db9c

+ 3 - 1
Projects/Compil32.dpr

@@ -56,7 +56,9 @@ uses
   FileClass in 'Src\FileClass.pas',
   Int64Em in 'Src\Int64Em.pas',
   Compress in 'Src\Compress.pas',
-  TaskDialog in 'Src\TaskDialog.pas';
+  TaskDialog in 'Src\TaskDialog.pas',
+  CompRegistryDesigner in 'Src\CompRegistryDesigner.pas' {RegistryDesignerForm},
+  CompWizardRegistryHelper in 'Src\CompWizardRegistryHelper.pas';
 
 {$SetPEFlags IMAGE_FILE_RELOCS_STRIPPED}
 {$SETPEOSVERSION 6.1}

+ 4 - 0
Projects/Compil32.dproj

@@ -138,6 +138,10 @@
         <DCCReference Include="Src\Int64Em.pas"/>
         <DCCReference Include="Src\Compress.pas"/>
         <DCCReference Include="Src\TaskDialog.pas"/>
+        <DCCReference Include="Src\CompRegistryDesigner.pas">
+            <Form>RegistryDesignerForm</Form>
+        </DCCReference>
+        <DCCReference Include="Src\CompWizardRegistryHelper.pas"/>
         <BuildConfiguration Include="Base">
             <Key>Base</Key>
         </BuildConfiguration>

+ 6 - 1
Projects/Src/CompFilesDesigner.pas

@@ -44,9 +44,14 @@ implementation
 
 {$R *.dfm}
 
+uses
+  CompFunc;
+
 procedure TFilesDesignerForm.FormCreate(Sender: TObject);
 begin
-  FFilesHelper := TWizardFormFilesHelper.Create(Handle,
+  InitFormFont(Self);
+
+  FFilesHelper := TWizardFormFilesHelper.Create(Self,
     NotCreateAppDirCheck, AppFilesListBox, AppFilesAddButton, AppFilesAddDirButton,
     AppFilesEditButton, AppFilesRemoveButton);
 end;

+ 6 - 0
Projects/Src/CompForm.dfm

@@ -702,6 +702,12 @@ object CompileForm: TCompileForm
         ShortCut = 24649
         OnClick = TFilesDesignerClick
       end
+      object TRegistryDesigner: TMenuItem
+        Caption = 'Generate [&Registry] Entries...'
+        ImageIndex = 66
+        ShortCut = 24658
+        OnClick = TRegistryDesignerClick
+      end
       object TMsgBoxDesigner: TMenuItem
         Caption = '&Generate MsgBox/TaskDialogMsgBox Call...'
         ShortCut = 24653

+ 29 - 1
Projects/Src/CompForm.pas

@@ -190,6 +190,7 @@ type
     DebugCallStackList: TListBox;
     VDebugCallStack: TMenuItem;
     TMsgBoxDesigner: TMenuItem;
+    TRegistryDesigner: TMenuItem;
     ToolBarPanel: TPanel;
     HMailingList: TMenuItem;
     MemosTabSet: TNewTabSet; { First tab is the main memo, last tab is the preprocessor output memo }
@@ -303,6 +304,7 @@ type
     procedure VDebugCallStackClick(Sender: TObject);
     procedure HMailingListClick(Sender: TObject);
     procedure TMsgBoxDesignerClick(Sender: TObject);
+    procedure TRegistryDesignerClick(Sender: TObject);
     procedure MemosTabSetClick(Sender: TObject);
     procedure FSaveAllClick(Sender: TObject);
     procedure RStepOutClick(Sender: TObject);
@@ -541,7 +543,7 @@ uses
   HtmlHelpFunc, TaskbarProgressFunc,
   {$IFDEF STATICCOMPILER} Compile, {$ENDIF}
   CompOptions, CompStartup, CompWizard, CompSignTools, CompTypes, CompInputQueryCombo, CompMsgBoxDesigner,
-  CompFilesDesigner;
+  CompFilesDesigner, CompRegistryDesigner, CompWizardRegistryHelper;
 
 {$R *.DFM}
 
@@ -3080,6 +3082,32 @@ begin
   end;
 end;
 
+procedure TCompileForm.TRegistryDesignerClick(Sender: TObject);
+begin
+  var RegistryDesignerForm := TRegistryDesignerForm.Create(Application);
+  try
+    var PrivilegesRequired := FindSetupDirectiveValue('PrivilegesRequired', 'admin');
+    var PrivilegesRequiredOverridesAllowed := FindSetupDirectiveValue('PrivilegesRequiredOverridesAllowed', '');
+    if PrivilegesRequiredOverridesAllowed = '' then begin
+      if SameText(PrivilegesRequired, 'admin') then
+        RegistryDesignerForm.PrivilegesRequired := prAdmin
+      else
+        RegistryDesignerForm.PrivilegesRequired := prLowest
+    end else
+      RegistryDesignerForm.PrivilegesRequired := prDynamic;
+    if RegistryDesignerForm.ShowModal = mrOk then
+    begin
+      FActiveMemo.CaretColumn := 0;
+      var Text := RegistryDesignerForm.Text;
+      if FMemosStyler.GetSectionFromLineState(FActiveMemo.Lines.State[FActiveMemo.CaretLine]) <> scRegistry then
+        Text := '[Registry]' + SNewLine + Text;
+      FActiveMemo.SelText := Text;
+    end;
+  finally
+    RegistryDesignerForm.Free;
+  end;
+end;
+
 procedure TCompileForm.TFilesDesignerClick(Sender: TObject);
 begin
   var FilesDesignerForm := TFilesDesignerForm.Create(Application);

+ 9 - 5
Projects/Src/CompMsgs.pas

@@ -25,7 +25,7 @@ const
   SCompilerScriptBrowseButton = '&Browse...';
   SCompilerStartButton = '&Start';
   SCompilerExitButton = 'E&xit';
-  SCompilerOpenFilter = 'Inno Setup Scripts (*.iss)|*.iss|All Files|*.*';
+  SCompilerOpenFilter = 'Inno Setup Script files (*.iss)|*.iss|All files|*.*';
   SCompilerExampleScripts = 'Example scripts...';
   SCompilerMoreFiles = 'More files...';
 
@@ -40,7 +40,7 @@ const
   SWizardAppFiles2 = 'Please specify the files that are part of your application.';
   SWizardAppFiles3 = 'Please specify the source folder.';
   SWizardAppFilesSubDirsMessage = 'Should files in subfolders of "%s" also be included?';
-  SWizardAppExeFilter = 'Application files (*.exe)|*.exe|All Files|*.*';
+  SWizardAppExeFilter = 'Application files (*.exe)|*.exe|All files|*.*';
   SWizardAppExeDefaultExt = 'exe';
   SWizardAppAssoc = 'Application File Association';
   SWizardAppAssoc2 = 'Please specify which file association should be created for your application.';
@@ -48,15 +48,19 @@ const
   SWizardAppIcons2 = 'Please specify which shortcuts should be created for your application.';
   SWizardAppDocs = 'Application Documentation';
   SWizardAppDocs2 = 'Please specify which documentation files should be shown by Setup during installation.';
-  SWizardAppDocsFilter = 'Documentation files (*.rtf,*.txt)|*.rtf;*.txt|All Files|*.*';
+  SWizardAppDocsFilter = 'Documentation files (*.rtf,*.txt)|*.rtf;*.txt|All files|*.*';
   SWizardAppDocsDefaultExt = 'rtf';
+  SWizardAppRegFilter = 'Registry files (*.reg)|*.reg|All files|*.*';
+  SWizardAppRegDefaultExt = 'reg';
   SWizardPrivilegesRequired = 'Setup Install Mode';
   SWizardPrivilegesRequired2 = 'Please specify in which install mode Setup should run.';
+  SWizardAppRegistry = 'Application Registry Keys And Values';
+  SWizardAppRegistry2 = 'Please specify the registry keys and values that are part of your application.';
   SWizardLanguages = 'Setup Languages';
   SWizardLanguages2 = 'Please specify which Setup languages should be included.';
   SWizardCompiler = 'Compiler Settings';
   SWizardCompiler2 = 'Please specify some basic compiler settings.';
-  SWizardCompilerSetupIconFileFilter = 'Icon files (*.ico)|*.ico|All Files|*.*';
+  SWizardCompilerSetupIconFileFilter = 'Icon files (*.ico)|*.ico|All files|*.*';
   SWizardCompilerSetupIconFileDefaultExt = 'ico';
   SWizardCompilerOutputDir = 'Please specify the folder.';
   SWizardISPP = 'Inno Setup Preprocessor';
@@ -69,7 +73,7 @@ const
   SWizardFinishButton = '&Finish';
   SWizardCancelMessage = 'The [name] is not complete. If you quit now, the new script file will not be generated.'#13#13'Exit the [name]?';
 
-  SWizardAllFilesFilter = 'All Files|*.*';
+  SWizardAllFilesFilter = 'All files|*.*';
 
   SWizardAppNameError = 'Please specify the application name.';
   SWizardAppVersionError = 'Please specify the application version.';

+ 149 - 0
Projects/Src/CompRegistryDesigner.dfm

@@ -0,0 +1,149 @@
+object RegistryDesignerForm: TRegistryDesignerForm
+  Left = 298
+  Top = 273
+  BorderStyle = bsDialog
+  Caption = '[Registry] Entries Designer'
+  ClientHeight = 348
+  ClientWidth = 500
+  Color = clBtnFace
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'MS Sans Serif'
+  Font.Style = []
+  Position = poScreenCenter
+  OnCreate = FormCreate
+  OnDestroy = FormDestroy
+  TextHeight = 13
+  object Panel1: TPanel
+    Left = 0
+    Top = 320
+    Width = 500
+    Height = 42
+    Align = alBottom
+    BevelOuter = bvNone
+    TabOrder = 9
+    object Bevel1: TBevel
+      Left = 0
+      Top = 0
+      Width = 500
+      Height = 3
+      Align = alTop
+      Shape = bsBottomLine
+    end
+    object InsertButton: TButton
+      Left = 330
+      Top = 9
+      Width = 75
+      Height = 25
+      Anchors = [akRight, akBottom]
+      Caption = 'Insert'
+      Default = True
+      ModalResult = 1
+      TabOrder = 0
+      OnClick = InsertButtonClick
+    end
+    object CancelButton: TButton
+      Left = 414
+      Top = 9
+      Width = 75
+      Height = 25
+      Anchors = [akRight, akBottom]
+      Cancel = True
+      Caption = 'Cancel'
+      ModalResult = 2
+      TabOrder = 1
+      ExplicitLeft = 410
+    end
+    object PrivilegesRequiredLabel: TNewStaticText
+      Left = 8
+      Top = 15
+      Width = 8
+      Height = 17
+      Caption = '*'
+      Enabled = False
+      TabOrder = 2
+    end
+  end
+  object AppRegistryFileLabel: TNewStaticText
+    Left = 8
+    Top = 18
+    Width = 316
+    Height = 17
+    Caption = '&Windows registry file (.reg) to import:'
+    FocusControl = AppRegistryFileEdit
+    TabOrder = 0
+  end
+  object AppRegistryFileEdit: TEdit
+    Left = 8
+    Top = 38
+    Width = 392
+    Height = 21
+    Anchors = [akLeft, akTop, akRight]
+    TabOrder = 1
+  end
+  object AppRegistryFileButton: TButton
+    Left = 414
+    Top = 36
+    Width = 75
+    Height = 25
+    Anchors = [akTop, akRight]
+    Caption = '&Browse...'
+    TabOrder = 2
+  end
+  object AppRegistrySettingsLabel: TNewStaticText
+    Left = 8
+    Top = 69
+    Width = 392
+    Height = 230
+    Anchors = [akLeft, akTop, akRight]
+    Caption = 'Settings (for all keys and values):'
+    TabOrder = 3
+  end
+  object AppRegistryUninsDeleteKeyCheck: TCheckBox
+    Left = 16
+    Top = 109
+    Width = 225
+    Height = 17
+    Caption = 'Also delete keys which are not empty'
+    TabOrder = 5
+  end
+  object AppRegistryUninsDeleteKeyIfEmptyCheck: TCheckBox
+    Left = 8
+    Top = 89
+    Width = 225
+    Height = 17
+    Caption = 'Delete keys which are empty on uninstall'
+    Checked = True
+    State = cbChecked
+    TabOrder = 4
+  end
+  object AppRegistryUninsDeleteValueCheck: TCheckBox
+    Left = 8
+    Top = 139
+    Width = 225
+    Height = 17
+    Caption = 'Delete values on uninstall'
+    Checked = True
+    State = cbChecked
+    TabOrder = 6
+    WordWrap = True
+  end
+  object AppRegistryMinVerCheck: TCheckBox
+    Left = 8
+    Top = 169
+    Width = 245
+    Height = 17
+    Caption = 'Create only if Windows'#39' version is at least:'
+    TabOrder = 7
+  end
+  object AppRegistryMinVerEdit: TEdit
+    Left = 259
+    Top = 168
+    Width = 140
+    Height = 21
+    Enabled = False
+    TabOrder = 8
+    Text = '6.2'
+  end
+end

+ 94 - 0
Projects/Src/CompRegistryDesigner.pas

@@ -0,0 +1,94 @@
+unit CompRegistryDesigner;
+
+{
+  Inno Setup
+  Copyright (C) 1997-2024 Jordan Russell
+  Portions by Martijn Laan
+  For conditions of distribution and use, see LICENSE.TXT.
+
+  Registry Designer form
+
+  Originally contributed by leserg73
+}
+
+interface
+
+uses
+  SysUtils, Classes,
+  Forms, Controls, StdCtrls, ExtCtrls,
+  CompWizardRegistryHelper, NewStaticText;
+
+type
+  TRegistryDesignerForm = class(TForm)
+    Panel1: TPanel;
+    Bevel1: TBevel;
+    InsertButton: TButton;
+    CancelButton: TButton;
+    AppRegistryFileLabel: TNewStaticText;
+    AppRegistryFileEdit: TEdit;
+    AppRegistryFileButton: TButton;
+    AppRegistrySettingsLabel: TNewStaticText;
+    AppRegistryUninsDeleteKeyCheck: TCheckBox;
+    AppRegistryUninsDeleteKeyIfEmptyCheck: TCheckBox;
+    AppRegistryUninsDeleteValueCheck: TCheckBox;
+    AppRegistryMinVerCheck: TCheckBox;
+    AppRegistryMinVerEdit: TEdit;
+    PrivilegesRequiredLabel: TNewStaticText;
+    procedure InsertButtonClick(Sender: TObject);
+    procedure FormCreate(Sender: TObject);
+    procedure FormDestroy(Sender: TObject);
+  private
+    FRegistryHelper: TWizardFormRegistryHelper;
+    procedure SetPrivilegesRequired(const Value: TPrivilegesRequired);
+    function GetText: String;
+  public
+    property PrivilegesRequired: TPrivilegesRequired write SetPrivilegesRequired;
+    property Text: string read GetText;
+  end;
+
+implementation
+
+{$R *.dfm}
+
+uses
+  CompFunc;
+
+procedure TRegistryDesignerForm.SetPrivilegesRequired(
+  const Value: TPrivilegesRequired);
+begin
+  if Value = prAdmin then
+    PrivilegesRequiredLabel.Caption := 'Script has PrivilegesRequired=admin'
+  else if Value = prLowest then
+    PrivilegesRequiredLabel.Caption := 'Script has PrivilegesRequired=lowest'
+  else
+    PrivilegesRequiredLabel.Caption := 'Script has PrivilegesRequiredOverridesAllowed set';
+end;
+
+procedure TRegistryDesignerForm.FormCreate(Sender: TObject);
+begin
+  InitFormFont(Self);
+
+  FRegistryHelper := TWizardFormRegistryHelper.Create(Self, AppRegistryFileEdit,
+    AppRegistryFileButton, AppRegistryUninsDeleteKeyCheck,
+    AppRegistryUninsDeleteKeyIfEmptyCheck, AppRegistryUninsDeleteValueCheck,
+    AppRegistryMinVerCheck, AppRegistryMinVerEdit);
+end;
+
+procedure TRegistryDesignerForm.FormDestroy(Sender: TObject);
+begin
+  FRegistryHelper.Free;
+end;
+
+function TRegistryDesignerForm.GetText: String;
+begin
+  Result := '';
+  FRegistryHelper.AddScript(Result, True);
+end;
+
+procedure TRegistryDesignerForm.InsertButtonClick(Sender: TObject);
+begin
+  if not FileExists(AppRegistryFileEdit.Text) then
+    ModalResult := mrCancel;
+end;
+
+end.

BIN
Projects/Src/CompRegistryDesignerTest.reg


+ 89 - 0
Projects/Src/CompWizard.dfm

@@ -896,6 +896,95 @@ object WizardForm: TWizardForm
             OnClick = PrivilegesRequiredOverridesAllowedDialogCheckboxClick
           end
         end
+        object AppRegistryPage: TNewNotebookPage
+          DesignSize = (
+            485
+            245)
+          object AppRegistryFileLabel: TNewStaticText
+            Left = 36
+            Top = 8
+            Width = 413
+            Height = 16
+            Anchors = [akLeft, akTop, akRight]
+            AutoSize = False
+            Caption = '&Windows registry file (.reg) to import:'
+            FocusControl = AppRegistryFileEdit
+            TabOrder = 0
+            WordWrap = True
+          end
+          object AppRegistryFileEdit: TEdit
+            Left = 36
+            Top = 28
+            Width = 309
+            Height = 21
+            Anchors = [akLeft, akTop, akRight]
+            TabOrder = 1
+          end
+          object AppRegistryFileButton: TButton
+            Left = 360
+            Top = 27
+            Width = 89
+            Height = 23
+            Anchors = [akTop, akRight]
+            Caption = 'B&rowse...'
+            TabOrder = 2
+          end
+          object AppRegistrySettingsLabel: TNewStaticText
+            Left = 36
+            Top = 60
+            Width = 392
+            Height = 230
+            Anchors = [akLeft, akTop, akRight]
+            Caption = 'Settings (for all keys and values):'
+            TabOrder = 3
+          end
+          object AppRegistryUninsDeleteKeyCheck: TCheckBox
+            Left = 44
+            Top = 100
+            Width = 225
+            Height = 17
+            Caption = 'Also delete keys which are not empty'
+            TabOrder = 5
+          end
+          object AppRegistryUninsDeleteKeyIfEmptyCheck: TCheckBox
+            Left = 36
+            Top = 80
+            Width = 225
+            Height = 17
+            Caption = 'Delete keys which are empty on uninstall'
+            Checked = True
+            State = cbChecked
+            TabOrder = 4
+          end
+          object AppRegistryUninsDeleteValueCheck: TCheckBox
+            Left = 36
+            Top = 130
+            Width = 225
+            Height = 17
+            Caption = 'Delete values on uninstall'
+            Checked = True
+            State = cbChecked
+            TabOrder = 6
+            WordWrap = True
+          end
+          object AppRegistryMinVerCheck: TCheckBox
+            Left = 36
+            Top = 160
+            Width = 245
+            Height = 17
+            Caption = 'Create only if Windows'#39' version is at least:'
+            TabOrder = 7
+          end
+          object AppRegistryMinVerEdit: TEdit
+            Left = 287
+            Top = 159
+            Width = 140
+            Height = 21
+            Enabled = False
+            TabOrder = 8
+            Text = '6.2'
+          end
+        end
         object LanguagesPage: TNewNotebookPage
           DesignSize = (
             485

+ 37 - 9
Projects/Src/CompWizard.pas

@@ -14,11 +14,11 @@ interface
 uses
   Windows, Forms, Classes, Graphics, StdCtrls, ExtCtrls, Controls, Dialogs, pngimage,
   UIStateForm, NewStaticText, DropListBox, NewCheckListBox, NewNotebook,
-  CompWizardFilesHelper;
+  CompWizardFilesHelper, CompWizardRegistryHelper;
 
 type
   TWizardPage = (wpWelcome, wpAppInfo, wpAppDir, wpAppFiles, wpAppAssoc, wpAppIcons,
-                 wpAppDocs, wpPrivilegesRequired, wpLanguages, wpCompiler,
+                 wpAppDocs, wpPrivilegesRequired, wpAppRegistry, wpLanguages, wpCompiler,
                  wpISPP, wpFinished);
 
   TWizardFormResult = (wrNone, wrEmpty, wrComplete);
@@ -37,6 +37,7 @@ type
     AppIconsPage: TNewNotebookPage;
     AppDocsPage: TNewNotebookPage;
     PrivilegesRequiredPage: TNewNotebookPage;
+    AppRegistryPage: TNewNotebookPage;
     LanguagesPage: TNewNotebookPage;
     CompilerPage: TNewNotebookPage;
     ISPPPage: TNewNotebookPage;
@@ -129,6 +130,15 @@ type
     CreateAssocCheck: TCheckBox;
     AppAssocExtLabel: TNewStaticText;
     AppAssocExtEdit: TEdit;
+    AppRegistryFileLabel: TNewStaticText;
+    AppRegistryFileEdit: TEdit;
+    AppRegistryFileButton: TButton;
+    AppRegistrySettingsLabel: TNewStaticText;
+    AppRegistryUninsDeleteKeyCheck: TCheckBox;
+    AppRegistryUninsDeleteKeyIfEmptyCheck: TCheckBox;
+    AppRegistryUninsDeleteValueCheck: TCheckBox;
+    AppRegistryMinVerCheck: TCheckBox;
+    AppRegistryMinVerEdit: TEdit;
     procedure FormCreate(Sender: TObject);
     procedure FormShow(Sender: TObject);
     procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
@@ -152,6 +162,7 @@ type
     CurPage: TWizardPage;
     FWizardName: String;
     FFilesHelper: TWizardFormFilesHelper;
+    FRegistryHelper: TWizardFormRegistryHelper;
     FLanguages: TStringList;
     FResult: TWizardFormResult;
     FResultScript: String;
@@ -187,20 +198,20 @@ const
   NotebookPages: array[TWizardPage, 0..1] of Integer =
     ((0, -1), (1, 0), (1, 1), (1, 2),
      (1, 3), (1, 4), (1, 5), (1, 6),
-     (1, 7), (1, 8), (1, 9), (2, -1));
+     (1, 7), (1, 8), (1, 9), (1, 10), (2, -1));
 
   PageCaptions: array[TWizardPage] of String =
     (SWizardWelcome, SWizardAppInfo, SWizardAppDir, SWizardAppFiles, SWizardAppAssoc,
-     SWizardAppIcons, SWizardAppDocs, SWizardPrivilegesRequired, SWizardLanguages,
-     SWizardCompiler, SWizardISPP, SWizardFinished);
+     SWizardAppIcons, SWizardAppDocs, SWizardPrivilegesRequired, SWizardAppRegistry,
+     SWizardLanguages, SWizardCompiler, SWizardISPP, SWizardFinished);
 
   PageDescriptions: array[TWizardPage] of String =
     ('', SWizardAppInfo2, SWizardAppDir2, SWizardAppFiles2, SWizardAppAssoc2,
-         SWizardAppIcons2, SWizardAppDocs2, SWizardPrivilegesRequired2, SWizardLanguages2,
-         SWizardCompiler2, SWizardISPP2, '');
+         SWizardAppIcons2, SWizardAppDocs2, SWizardPrivilegesRequired2, SWizardAppRegistry2,
+         SWizardLanguages2, SWizardCompiler2, SWizardISPP2, '');
 
   RequiredLabelVisibles: array[TWizardPage] of Boolean =
-    (False, True, True, True, True, True, False, True, True, False, False, False);
+    (False, True, True, True, True, True, False, True, False, True, False, False, False);
 
   AppRootDirs: array[0..0] of TConstant =
   (
@@ -272,9 +283,13 @@ begin
   FResult := wrNone;
 
   FWizardName := SWizardDefaultName;
-  FFilesHelper := TWizardFormFilesHelper.Create(Handle,
+  FFilesHelper := TWizardFormFilesHelper.Create(Self,
     NotCreateAppDirCheck, AppFilesListBox, AppFilesAddButton, AppFilesAddDirButton,
     AppFilesEditButton, AppFilesRemoveButton);
+  FRegistryHelper := TWizardFormRegistryHelper.Create(Self, AppRegistryFileEdit,
+    AppRegistryFileButton, AppRegistryUninsDeleteKeyCheck,
+    AppRegistryUninsDeleteKeyIfEmptyCheck, AppRegistryUninsDeleteValueCheck,
+    AppRegistryMinVerCheck, AppRegistryMinVerEdit);
 
   FLanguages := TStringList.Create;
   FLanguages.Sorted := True;
@@ -387,6 +402,7 @@ end;
 procedure TWizardForm.FormDestroy(Sender: TObject);
 begin
   FLanguages.Free;
+  FRegistryHelper.Free;
   FFilesHelper.Free;
 end;
 
@@ -446,6 +462,7 @@ begin
         else
           ActiveControl := PrivilegesRequiredLowestRadioButton;
       end;
+    wpAppRegistry: ActiveControl := AppRegistryFileEdit;
     wpLanguages: ActiveControl := LanguagesList;
     wpCompiler: ActiveControl := OutputDirEdit;
     wpISPP: ActiveControl := ISPPCheck;
@@ -548,6 +565,14 @@ begin
     if CurPage = wpAppAssoc then begin
       if (AppAssocExtEdit.Text <> '') and (AppAssocExtEdit.Text[1] <> '.') then
         AppAssocExtEdit.Text := '.' + AppAssocExtEdit.Text;
+    end else if CurPage = wpPrivilegesRequired then begin
+      if not PrivilegesRequiredOverridesAllowedCommandLineCheckbox.Checked then begin
+        if PrivilegesRequiredAdminRadioButton.Checked then
+          FRegistryHelper.PrivilegesRequired := prAdmin
+        else
+          FRegistryHelper.PrivilegesRequired := prLowest
+      end else
+        FRegistryHelper.PrivilegesRequired := prDynamic;
     end else if CurPage = wpFinished then begin
       GenerateScript;
       ModalResult := mrOk;
@@ -951,6 +976,9 @@ begin
       Setup := Setup + 'PrivilegesRequiredOverridesAllowed=dialog' + SNewLine
     else if PrivilegesRequiredOverridesAllowedCommandLineCheckbox.Checked then
       Setup := Setup + 'PrivilegesRequiredOverridesAllowed=commandline' + SNewLine;
+      
+    { AppRegistry }
+    FRegistryHelper.AddScript(Registry, False);
 
     { Languages }
     if FLanguages.Count > 1 then begin

+ 8 - 8
Projects/Src/CompWizardFilesHelper.pas

@@ -12,14 +12,14 @@ unit CompWizardFilesHelper;
 interface
 
 uses
-  Windows, Classes, StdCtrls,
+  Windows, Classes, Forms, StdCtrls,
   DropListBox;
 
 type
   TWizardFormFilesHelper = class
     private
       FWizardFiles: TList;
-      FHandle: HWND;
+      FForm: TForm;
       FNotCreateAppDirCheck: TCheckBox;
       FFilesListBox: TDropListBox;
       FEditButton: TButton;
@@ -36,7 +36,7 @@ type
       procedure EditButtonClick(Sender: TObject);
       procedure RemoveButtonClick(Sender: TObject);
     public
-      constructor Create(const Handle: HWND;
+      constructor Create(const Form: TForm;
         const NotCreateAppDirCheck: TCheckBox; const FilesListBox: TDropListBox;
         const AddButton, AddDirButton, EditButton, RemoveButton: TButton);
       destructor Destroy; override;
@@ -47,11 +47,11 @@ type
 implementation
 
 uses
-  SysUtils, Forms, UITypes,
+  SysUtils, UITypes,
   CmnFunc, CmnFunc2, BrowseFunc, PathFunc,
   CompMsgs, CompWizardFile;
 
-constructor TWizardFormFilesHelper.Create(const Handle: HWND;
+constructor TWizardFormFilesHelper.Create(const Form: TForm;
   const NotCreateAppDirCheck: TCheckBox; const FilesListBox: TDropListBox;
   const AddButton, AddDirButton, EditButton, RemoveButton: TButton);
 begin
@@ -59,7 +59,7 @@ begin
 
   FWizardFiles := TList.Create;
 
-  FHandle := Handle;
+  FForm := Form;
   FNotCreateAppDirCheck := NotCreateAppDirCheck;
   FFilesListBox := FilesListBox;
   FEditButton := EditButton;
@@ -158,7 +158,7 @@ var
 begin
   FileList := TStringList.Create;
   try
-    if NewGetOpenFileNameMulti('', FileList, '', SWizardAllFilesFilter, '', FHandle) then begin
+    if NewGetOpenFileNameMulti('', FileList, '', SWizardAllFilesFilter, '', FForm.Handle) then begin
       FileList.Sort;
       for I := 0 to FileList.Count-1 do
         AddWizardFile(FileList[I], False, False);
@@ -175,7 +175,7 @@ var
   Recurse: Boolean;
 begin
   Path := '';
-  if BrowseForFolder(SWizardAppFiles3, Path, FHandle, False) then begin
+  if BrowseForFolder(SWizardAppFiles3, Path, FForm.Handle, False) then begin
     case MsgBox(Format(SWizardAppFilesSubDirsMessage, [Path]), '', mbConfirmation, MB_YESNOCANCEL) of
       IDYES: Recurse := True;
       IDNO: Recurse := False;

+ 445 - 0
Projects/Src/CompWizardRegistryHelper.pas

@@ -0,0 +1,445 @@
+unit CompWizardRegistryHelper;
+
+{
+  Inno Setup
+  Copyright (C) 1997-2024 Jordan Russell
+  Portions by Martijn Laan
+  For conditions of distribution and use, see LICENSE.TXT.
+
+  Helper to avoid duplicate code between CompWizard and CompRegistryDesigner
+}
+
+interface
+
+uses
+  Forms, StdCtrls;
+
+type
+  TPrivilegesRequired = (prAdmin, prLowest, prDynamic);
+
+  TWizardFormRegistryHelper = class
+    private
+      FForm: TForm;
+      FFileEdit: TEdit;
+      FUninsDeleteKeyCheck, FUninsDeleteKeyIfEmptyCheck,
+      FUninsDeleteValueCheck, FMinVerCheck: TCheckBox;
+      FMinVerEdit: TEdit;
+      FPrivilegesRequired: TPrivilegesRequired;
+      procedure SetPrivilegesRequired(const Value: TPrivilegesRequired);
+      procedure FileButtonClick(Sender: TObject);
+      procedure UninsDeleteKeyIfEmptyCheckClick(Sender: TObject);
+      procedure MinVerCheckClick(Sender: TObject);
+    public
+      constructor Create(const Form: TForm; const FileEdit: TEdit;
+        const FileButton: TButton; const UninsDeleteKeyCheck,
+        UninsDeleteKeyIfEmptyCheck, UninsDeleteValueCheck, MinVerCheck: TCheckBox;
+        const MinVerEdit: TEdit);
+      procedure AddScript(var Registry: String; const AllowException: Boolean);
+      property PrivilegesRequired: TPrivilegesRequired write SetPrivilegesRequired;
+    end;
+
+implementation
+
+uses
+  Classes, SysUtils, StrUtils, TypInfo,
+  CompMsgs, BrowseFunc, CmnFunc2;
+
+{ TWizardFormRegistryHelper }
+
+procedure TWizardFormRegistryHelper.SetPrivilegesRequired(
+  const Value: TPrivilegesRequired);
+begin
+  FPrivilegesRequired := Value;
+end;
+
+constructor TWizardFormRegistryHelper.Create(const Form: TForm;
+  const FileEdit: TEdit; const FileButton: TButton; const UninsDeleteKeyCheck,
+  UninsDeleteKeyIfEmptyCheck, UninsDeleteValueCheck, MinVerCheck: TCheckBox;
+  const MinVerEdit: TEdit);
+begin
+  FForm := Form;
+  FFileEdit := FileEdit;
+  FUninsDeleteKeyCheck := UninsDeleteKeyCheck;
+  FUninsDeleteKeyIfEmptyCheck := UninsDeleteKeyIfEmptyCheck;
+  FUninsDeleteValueCheck := UninsDeleteValueCheck;
+  FMinVerCheck := MinVerCheck;
+  FMinVerEdit := MinVerEdit;
+
+  FileButton.OnClick := FileButtonClick;
+  UninsDeleteKeyIfEmptyCheck.OnClick := UninsDeleteKeyIfEmptyCheckClick;
+  MinVerCheck.OnClick := MinVerCheckClick;
+
+  TryEnableAutoCompleteFileSystem(FileEdit.Handle);
+end;
+
+procedure TWizardFormRegistryHelper.FileButtonClick(Sender: TObject);
+begin
+  var FileName: String := FFileEdit.Text;
+  if NewGetOpenFileName('', FileName, '', SWizardAppRegFilter, SWizardAppRegDefaultExt, FForm.Handle) then
+    FFileEdit.Text := FileName;
+end;
+
+procedure TWizardFormRegistryHelper.UninsDeleteKeyIfEmptyCheckClick(Sender: TObject);
+begin
+  FUninsDeleteKeyCheck.Enabled := FUninsDeleteKeyIfEmptyCheck.Checked;
+  if not FUninsDeleteKeyCheck.Enabled then
+    FUninsDeleteKeyCheck.Checked := False;
+end;
+
+procedure TWizardFormRegistryHelper.MinVerCheckClick(Sender: TObject);
+begin
+  FMinVerEdit.Enabled := FMinVerCheck.Checked;
+  if FMinVerEdit.Enabled then
+    FForm.ActiveControl := FMinVerEdit;
+end;
+
+procedure TWizardFormRegistryHelper.AddScript(var Registry: String;
+  const AllowException: Boolean);
+
+  function NextLine(const Lines: TStrings; var LineIndex: Integer): String;
+  begin
+    Inc(LineIndex);
+    if LineIndex < Lines.Count then
+      Result := Lines[LineIndex]
+    else
+      Result := ''; { Official .reg files must end with a blank line so should never get here but we support ones without }
+  end;
+
+  function CutStrBeginEnd(S: String; CharCount: Integer): String;
+  begin
+    Result := Copy(S, CharCount + 1, S.Length - 2 * CharCount);
+  end;
+
+  function StrRootRename(S: String): String;
+  type
+    TStrings = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_CLASSES_ROOT, HKEY_USERS, HKEY_CURRENT_CONFIG);
+  begin
+    var ARoot := TStrings(GetEnumValue(TypeInfo(TStrings), S));
+    case ARoot of
+      HKEY_CURRENT_USER:   Result := 'HKCU';
+      HKEY_LOCAL_MACHINE:  Result := 'HKLM';
+      HKEY_CLASSES_ROOT:   Result := 'HKCR';
+      HKEY_USERS:          Result := 'HKU';
+      HKEY_CURRENT_CONFIG: Result := 'HKCC';
+    else
+      raise Exception.CreateFmt('Unknown root %s', [S]);
+    end;
+  end;
+
+  function UTF16LEHexStrToStr(HexStr: String): String;
+  begin
+    if HexStr.Length mod 4 <> 0 then
+      HexStr := HexStr + '00'; { RegEdit does this as well on import }
+    var UTF16LEBytes: TBytes;
+    SetLength(UTF16LEBytes, HexStr.Length div 2);
+    var i := 1;
+    var idx := 0;
+    while i <= HexStr.Length do
+    begin
+      UTF16LEBytes[idx] := StrToInt('$' + HexStr[i] + HexStr[i + 1]);
+      i := i + 2;
+      idx := idx + 1;
+    end;
+    Result := TEncoding.Unicode.GetString(UTF16LEBytes);
+  end;
+
+  type
+    TValueType = (vtSz, vtSzAsList, vtExpandSz, vtMultiSz, vtBinary, vtDWord, vtDWordAsList, vtQWord, vtNone, vtDelete, vtUnsupported);
+
+  function GetValueType(AStr: String): TValueType;
+  { See https://en.wikipedia.org/wiki/Windows_Registry#.REG_files
+
+    Value formats: (we don't support I/K/L and just ignore those)
+
+    "Value A"="<REG_SZ String value data with escape characters>"
+    "Value B"=hex:<REG_BINARY Binary data (as comma-delimited list of hexadecimal values)>
+    "Value C"=dword:<REG_DWORD DWORD value integer>
+    "Value D"=hex(0):<REG_NONE (as comma-delimited list of hexadecimal values)>
+    "Value E"=hex(1):<REG_SZ (as comma-delimited list of hexadecimal values representing a UTF-16LE NUL-terminated string)>
+    "Value F"=hex(2):<REG_EXPAND_SZ Expandable string value data  (as comma-delimited list of hexadecimal values representing a UTF-16LE NUL-terminated string)>
+    "Value G"=hex(3):<REG_BINARY Binary data (as comma-delimited list of hexadecimal values)> ; equal to "Value B"
+    "Value H"=hex(4):<REG_DWORD DWORD value (as comma-delimited list of 4 hexadecimal values, in little endian byte order)>
+    "Value I"=hex(5):<REG_DWORD_BIG_ENDIAN DWORD value (as comma-delimited list of 4 hexadecimal values, in big endian byte order)>
+    "Value J"=hex(7):<RED_MULTISZ Multi-string value data (as comma-delimited list of hexadecimal values representing UTF-16LE NUL-terminated strings)>
+    "Value K"=hex(8):<REG_RESOURCE_LIST (as comma-delimited list of hexadecimal values)>
+    "Value L"=hex(a):<REG_RESOURCE_REQUIREMENTS_LIST (as comma-delimited list of hexadecimal values)>
+    "Value M"=hex(b):<REG_QWORD QWORD value (as comma-delimited list of 8 hexadecimal values, in little endian byte order)>
+
+    Other notes from the article:
+    To remove a key (and all subkeys, values and data), the key name must be preceded by a minus sign ("-")
+    To remove a value (and its data), the values to be removed must have a minus sign ("-") after the equal sign ("=")
+    The Default Value of a key can be edited by using "@" instead of "Value Name"
+    Lines beginning with a semicolon are considered comments
+
+    BTW: Missing from the article is a note about multiline lists, these use "\" to continue }
+  begin
+    if Pos('"', AStr) <> 0 then
+      Result := vtSz //Value A
+    else if (Pos('hex:', AStr) <> 0) or
+            (Pos('hex(3):', AStr) <> 0) then
+      Result := vtBinary //Value B or G
+    else if Pos('dword:', AStr) <> 0 then
+      Result := vtDWord //Value C
+    else if Pos('hex(0):', AStr) <> 0 then
+      Result := vtNone //Value D
+    else if Pos('hex(1):', AStr) <> 0 then
+      Result := vtSzAsList //Value E
+    else if Pos('hex(2):', AStr) <> 0 then
+      Result := vtExpandSz //Value F
+    else if Pos('hex(4):', AStr) <> 0 then
+      Result := vtDWordAsList //Value H
+    else if Pos('hex(7):', AStr) <> 0 then
+      Result := vtMultiSz //Value J
+    else if Pos('hex(b):', AStr) <> 0 then
+      Result := vtQWord //Value M
+    else if AStr.StartsWith('-') then
+      Result := vtDelete
+    else
+      Result := vtUnsupported;
+  end;
+
+  type
+    TRegistryEntry = record
+      Root, Subkey, ValueName, ValueData, ValueType: String;
+    end;
+
+  function RequiresAdminInstallMode(AEntry: TRegistryEntry): Boolean;
+  begin
+    Result := (AEntry.Root = 'HKLM') or (AEntry.Root = 'HKCC') or
+              ((AEntry.Root = 'HKU') and SameText(AEntry.Subkey, '.Default'));
+  end;
+
+   function RequiresNotAdminInstallMode(AEntry: TRegistryEntry): Boolean;
+  begin
+    Result := (AEntry.Root = 'HKCU');
+  end;
+
+  function TextCommon(AEntry: TRegistryEntry): String;
+  begin
+    Result := '';
+    if FMinVerCheck.Checked then
+      Result := Result + '; MinVersion: ' + FMinVerEdit.Text;
+    if (FPrivilegesRequired <> prAdmin) and RequiresAdminInstallMode(AEntry) then
+      Result := Result + '; Check: IsAdminInstallMode'
+    else if (FPrivilegesRequired <> prLowest) and RequiresNotAdminInstallMode(AEntry) then
+      Result := Result + '; Check: not IsAdminInstallMode';
+  end;
+
+  function TextKeyEntry(AEntry: TRegistryEntry; ADeleteKey: Boolean): String;
+  begin
+    Result := 'Root: ' + AEntry.Root +
+              '; Subkey: ' + AEntry.Subkey;
+    if ADeleteKey then
+      Result := Result + '; ValueType: none' +
+                         '; Flags: deletekey'
+    else begin
+      if FUninsDeleteKeyCheck.Checked then
+        Result := Result + '; Flags: uninsdeletekey'
+      else if FUninsDeleteKeyIfEmptyCheck.Checked then
+        Result := Result + '; Flags: uninsdeletekeyifempty';
+    end;
+    Result := Result + TextCommon(AEntry);
+  end;
+
+  function TextValueEntry(AEntry: TRegistryEntry; AValueType: TValueType): String;
+  begin
+    Result := 'Root: ' + AEntry.Root +
+              '; Subkey: ' + AEntry.Subkey +
+              '; ValueType: ' + AEntry.ValueType +
+              '; ValueName: ' + AEntry.ValueName;
+    if AValueType = vtDelete then
+      Result := Result + '; Flags: deletevalue'
+    else begin
+      if AValueType <> vtNone then
+        Result := Result + '; ValueData: ' + AEntry.ValueData;
+      if FUninsDeleteValueCheck.Checked then
+        Result := Result + '; Flags: uninsdeletevalue';
+    end;
+    Result := Result + TextCommon(AEntry);
+  end;
+
+  function TextHeader: String;
+  begin
+    Result := ';Registry data from file ' + ExtractFileName(FFileEdit.Text);
+  end;
+
+  function TextBadHeader: String;
+  begin
+    Result := ';COULD NOT IMPORT ' + ExtractFileName(FFileEdit.Text);
+  end;
+
+  function TextFooter(const HadFilteredKeys, HadUnsupportedValueTypes: Boolean): String;
+  begin
+    Result := ';End of registry data from file ' + ExtractFileName(FFileEdit.Text);
+    if HadFilteredKeys then
+      Result := Result + SNewLine + ';SOME KEYS FILTERED DUE TO PRIVILEGESREQUIRED SETTINGS!';
+    if HadUnsupportedValueTypes then
+      Result := Result + SNewLine + ';SOME VALUES WITH UNSUPPORTED TYPES SKIPPED!'
+  end;
+
+begin
+  if FFileEdit.Text = '' then
+    Exit;
+
+  var Lines := TStringList.Create;
+  var OutLines := TStringList.Create;
+  try
+    Lines.LoadFromFile(FFileEdit.Text);
+
+    { Official .reg files must have blank lines as second and last lines but we
+      don't require that so we just check for the header on the first line }
+    const Header = 'Windows Registry Editor Version 5.00'; { don't localize }
+    if (Lines.Count = 0) or (Lines[0] <> Header) then begin
+      if AllowException then
+        raise Exception.Create('Invalid file format.')
+      else begin
+        Registry := Registry + TextBadHeader + SNewLine;
+        Exit;
+      end;
+    end;
+
+    var LineIndex := 1;
+    var HadFilteredKeys := False;
+    var HadUnsupportedValueTypes := False;
+    while LineIndex <= Lines.Count-1 do
+    begin
+      var Line := Lines[LineIndex];
+      if (Length(Line) > 2) and (Line[1] = '[') and (Line[Line.Length] = ']') then
+      begin
+        { Got a new section, first handle the key }
+        Line := CutStrBeginEnd(Line, 1);
+        var DeleteKey := Line.StartsWith('-');
+        if DeleteKey then
+          Delete(Line, 1, 1);
+        var P := Pos('\', Line);
+
+        var Entry: TRegistryEntry;
+        Entry.Root := StrRootRename(Copy(Line, 1, P - 1));
+        Entry.Subkey := Copy(Line, P + 1, MaxInt);
+        if Entry.Root = 'HKCR' then begin
+          Entry.Root := 'HKA';
+          Entry.Subkey := 'Software\Classes\' + Entry.Subkey;
+        end;
+        Entry.Subkey := Entry.Subkey.Replace('\WOW6432Node', '')
+                                            .Replace('{', '{{')
+                                            .QuotedString('"');
+
+        var FilterKey := ((FPrivilegesRequired = prAdmin) and RequiresNotAdminInstallMode(Entry)) or
+                         ((FPrivilegesRequired = prLowest) and RequiresAdminInstallMode(Entry));
+
+        if not FilterKey then
+          OutLines.Add(TextKeyEntry(Entry, DeleteKey))
+        else
+          HadFilteredKeys := True;
+
+        { Key done, handle values }
+        Line := NextLine(Lines, LineIndex);
+
+        while Line <> '' do begin
+          if not FilterKey and not DeleteKey and (Line[1] <> ';') then begin
+            P := Pos('=', Line);
+            if (P = 2) and (Line[1] = '@') then
+              Entry.ValueName := '""'
+            else begin
+              Entry.ValueName := CutStrBeginEnd(Copy(Line, 1, P - 1), 1);
+              Entry.ValueName := Entry.ValueName.Replace('\\', '\')
+                                                .Replace('{', '{{')
+                                                .QuotedString('"');
+            end;
+            var ValueTypeAndData := Copy(Line, P + 1, MaxInt);
+            var ValueType := GetValueType(ValueTypeAndData);
+            case ValueType of
+              vtSz:
+                begin
+                  Entry.ValueData := CutStrBeginEnd(ValueTypeAndData, 1);
+                  Entry.ValueData := Entry.ValueData.Replace('\\', '\')
+                                                    .Replace('{', '{{')
+                                                    .QuotedString('"');
+                  Entry.ValueType := 'string';
+                end;
+              vtSzAsList, vtExpandSz, vtMultiSz, vtBinary:
+                begin
+                  P := Pos(':', ValueTypeAndData);
+                  var ValueData := Copy(ValueTypeAndData, P + 1, MaxInt);
+
+                  var HasMoreLines := ValueData[ValueData.Length] = '\';
+                  if HasMoreLines then
+                    Delete(ValueData, ValueData.Length, 1);
+                  Entry.ValueData := ValueData;
+
+                  while HasMoreLines do
+                  begin
+                    ValueData := NextLine(Lines, LineIndex).TrimLeft;
+                    HasMoreLines := ValueData[ValueData.Length] = '\';
+                    if HasMoreLines then
+                      Delete(ValueData, ValueData.Length, 1);
+                    Entry.ValueData := Entry.ValueData + ValueData;
+                  end;
+
+                  Entry.ValueData := Entry.ValueData.Replace(',', ' ');
+                  if ValueType <> vtBinary then
+                  begin
+                    Entry.ValueData := Entry.ValueData.Replace(' ', '');
+                    Entry.ValueData := UTF16LEHexStrToStr(Entry.ValueData);
+                  end;
+
+                  if ValueType in [vtSzAsList, vtExpandSz] then
+                  begin
+                    Entry.ValueData := Entry.ValueData.Replace(#0, '');
+                    Entry.ValueType := IfThen(ValueType = vtSzAsList, 'string', 'expandsz');
+                  end else if ValueType = vtMultiSz then
+                  begin
+                    Entry.ValueData := Entry.ValueData.Replace(#0, '{break}');
+                    Entry.ValueType := 'multisz';
+                  end else
+                    Entry.ValueType := 'binary';
+
+                  Entry.ValueData := Entry.ValueData.QuotedString('"');
+               end;
+              vtDWord, vtDWordAsList, vtQWord:
+                begin
+                  P := Pos(':', ValueTypeAndData);
+                  Entry.ValueData := Copy(ValueTypeAndData, P + 1, MaxInt);
+
+                  if ValueType in [vtDWordAsList, vtQWord] then
+                  begin
+                    { ValueData is in reverse order, fix this }
+                    var ReverseValueData := Entry.ValueData.Replace(',', '');
+                    Entry.ValueData := '';
+                    for var I := 0 to ReverseValueData.Length div 2 do
+                      Entry.ValueData := Copy(ReverseValueData, (I * 2) + 1, 2) + Entry.ValueData;
+
+                    Entry.ValueType := IfThen(ValueType = vtDWordAsList, 'dword', 'qword');
+                  end else
+                    Entry.ValueType := 'dword';
+
+                  Entry.ValueData := '$' + Entry.ValueData;
+                end;
+              vtNone, vtDelete:
+                begin
+                  Entry.ValueType := 'none';
+                  Entry.ValueData := ''; { value doesn't matter }
+                end;
+            end;
+
+            if ValueType <> vtUnsupported then
+              OutLines.Add(TextValueEntry(Entry, ValueType))
+            else
+              HadUnsupportedValueTypes := True;
+          end;
+
+          Line := NextLine(Lines, LineIndex); { Go to the next line - should be the next value or a comment }
+        end; { Out of values }
+      end;
+      Inc(LineIndex); { Go to the next line - should be the next key section or a comment }
+    end;
+    OutLines.Insert(0, TextHeader);
+    OutLines.Add(TextFooter(HadFilteredKeys, HadUnsupportedValueTypes));
+    Registry := Registry + OutLines.Text;
+  finally
+    Lines.Free;
+    OutLines.Free;
+  end;
+end;
+
+end.

+ 4 - 2
whatsnew.htm

@@ -61,8 +61,10 @@ For conditions of distribution and use, see <a href="https://jrsoftware.org/file
 <ul>
   <li>Compiler IDE changes: 
   <ul>
+    <li>The New Script Wizard now offers an option to import a Windows registry .reg file.</li>
+    <li>Added new <i>Generate [Registry] Entries... (Ctrl+Shift+R)</i> menu item to the <i>Tools</i> menu to Windows registry .reg file as extra entries to the [Registry] section at the cursor position, or to a new section.</li>
     <li>Added new <i>Generate [Files] Entries... (Ctrl+Shift+I)</i> menu item to the <i>Tools</i> menu to design and insert extra entries to the [Files] section at the cursor position, or to a new section.</li>
-    <li>The <i>Generate MsgBox/TaskDialogMsgBox Call (Ctrl+Shift+M)</i> tool (previously named <i>MsgBox/TaskDialogMsgBox Designer</i>) now respects the tab width and tab character settings, indents the generated Pascal script one extra level, and warns if the cursor position is not in the [Code] section.</li>
+    <li>The <i>Generate MsgBox/TaskDialogMsgBox Call... (Ctrl+Shift+M)</i> tool (previously named <i>MsgBox/TaskDialogMsgBox Designer</i>) now respects the tab width and tab character settings, indents the generated Pascal script one extra level, and warns if the cursor position is not in the [Code] section.</li>
   </ul>
   </li>
   <li>Added new [Setup] section directive <tt>UninstallLogging</tt>, which defaults to <tt>no</tt>. If set to <tt>yes</tt>, the uninstaller will always create a log file if it is launched from the <i>Add/Remove Programs</i> Control Panel applet. Equivalent to passing /LOG on the command line.</li>
@@ -75,7 +77,7 @@ For conditions of distribution and use, see <a href="https://jrsoftware.org/file
   <li>Minor tweaks and documentation improvements.</li>
 </ul>
 
-<p>Contributions via <a href="https://github.com/jrsoftware/issrc" target="_blank">GitHub</a>: Thanks to Achim Stuy, ser163, and Jens Geyer for their contributions.</p>
+<p>Contributions via <a href="https://github.com/jrsoftware/issrc" target="_blank">GitHub</a>: Thanks to Achim Stuy, ser163, Jens Geyer, and Sergii Leonov for their contributions.</p>
 
 <p><a href="https://jrsoftware.org/files/is6.2-whatsnew.htm">Inno Setup 6.2 Revision History</a></p >