Explorar o código

Merge branch 'leserg73-main_regfile_v2'

Martijn Laan hai 1 ano
pai
achega
8b9dd6db9c

+ 3 - 1
Projects/Compil32.dpr

@@ -56,7 +56,9 @@ uses
   FileClass in 'Src\FileClass.pas',
   FileClass in 'Src\FileClass.pas',
   Int64Em in 'Src\Int64Em.pas',
   Int64Em in 'Src\Int64Em.pas',
   Compress in 'Src\Compress.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}
 {$SetPEFlags IMAGE_FILE_RELOCS_STRIPPED}
 {$SETPEOSVERSION 6.1}
 {$SETPEOSVERSION 6.1}

+ 4 - 0
Projects/Compil32.dproj

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

+ 6 - 1
Projects/Src/CompFilesDesigner.pas

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

+ 6 - 0
Projects/Src/CompForm.dfm

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

+ 29 - 1
Projects/Src/CompForm.pas

@@ -190,6 +190,7 @@ type
     DebugCallStackList: TListBox;
     DebugCallStackList: TListBox;
     VDebugCallStack: TMenuItem;
     VDebugCallStack: TMenuItem;
     TMsgBoxDesigner: TMenuItem;
     TMsgBoxDesigner: TMenuItem;
+    TRegistryDesigner: TMenuItem;
     ToolBarPanel: TPanel;
     ToolBarPanel: TPanel;
     HMailingList: TMenuItem;
     HMailingList: TMenuItem;
     MemosTabSet: TNewTabSet; { First tab is the main memo, last tab is the preprocessor output memo }
     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 VDebugCallStackClick(Sender: TObject);
     procedure HMailingListClick(Sender: TObject);
     procedure HMailingListClick(Sender: TObject);
     procedure TMsgBoxDesignerClick(Sender: TObject);
     procedure TMsgBoxDesignerClick(Sender: TObject);
+    procedure TRegistryDesignerClick(Sender: TObject);
     procedure MemosTabSetClick(Sender: TObject);
     procedure MemosTabSetClick(Sender: TObject);
     procedure FSaveAllClick(Sender: TObject);
     procedure FSaveAllClick(Sender: TObject);
     procedure RStepOutClick(Sender: TObject);
     procedure RStepOutClick(Sender: TObject);
@@ -541,7 +543,7 @@ uses
   HtmlHelpFunc, TaskbarProgressFunc,
   HtmlHelpFunc, TaskbarProgressFunc,
   {$IFDEF STATICCOMPILER} Compile, {$ENDIF}
   {$IFDEF STATICCOMPILER} Compile, {$ENDIF}
   CompOptions, CompStartup, CompWizard, CompSignTools, CompTypes, CompInputQueryCombo, CompMsgBoxDesigner,
   CompOptions, CompStartup, CompWizard, CompSignTools, CompTypes, CompInputQueryCombo, CompMsgBoxDesigner,
-  CompFilesDesigner;
+  CompFilesDesigner, CompRegistryDesigner, CompWizardRegistryHelper;
 
 
 {$R *.DFM}
 {$R *.DFM}
 
 
@@ -3080,6 +3082,32 @@ begin
   end;
   end;
 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);
 procedure TCompileForm.TFilesDesignerClick(Sender: TObject);
 begin
 begin
   var FilesDesignerForm := TFilesDesignerForm.Create(Application);
   var FilesDesignerForm := TFilesDesignerForm.Create(Application);

+ 9 - 5
Projects/Src/CompMsgs.pas

@@ -25,7 +25,7 @@ const
   SCompilerScriptBrowseButton = '&Browse...';
   SCompilerScriptBrowseButton = '&Browse...';
   SCompilerStartButton = '&Start';
   SCompilerStartButton = '&Start';
   SCompilerExitButton = 'E&xit';
   SCompilerExitButton = 'E&xit';
-  SCompilerOpenFilter = 'Inno Setup Scripts (*.iss)|*.iss|All Files|*.*';
+  SCompilerOpenFilter = 'Inno Setup Script files (*.iss)|*.iss|All files|*.*';
   SCompilerExampleScripts = 'Example scripts...';
   SCompilerExampleScripts = 'Example scripts...';
   SCompilerMoreFiles = 'More files...';
   SCompilerMoreFiles = 'More files...';
 
 
@@ -40,7 +40,7 @@ const
   SWizardAppFiles2 = 'Please specify the files that are part of your application.';
   SWizardAppFiles2 = 'Please specify the files that are part of your application.';
   SWizardAppFiles3 = 'Please specify the source folder.';
   SWizardAppFiles3 = 'Please specify the source folder.';
   SWizardAppFilesSubDirsMessage = 'Should files in subfolders of "%s" also be included?';
   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';
   SWizardAppExeDefaultExt = 'exe';
   SWizardAppAssoc = 'Application File Association';
   SWizardAppAssoc = 'Application File Association';
   SWizardAppAssoc2 = 'Please specify which file association should be created for your application.';
   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.';
   SWizardAppIcons2 = 'Please specify which shortcuts should be created for your application.';
   SWizardAppDocs = 'Application Documentation';
   SWizardAppDocs = 'Application Documentation';
   SWizardAppDocs2 = 'Please specify which documentation files should be shown by Setup during installation.';
   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';
   SWizardAppDocsDefaultExt = 'rtf';
+  SWizardAppRegFilter = 'Registry files (*.reg)|*.reg|All files|*.*';
+  SWizardAppRegDefaultExt = 'reg';
   SWizardPrivilegesRequired = 'Setup Install Mode';
   SWizardPrivilegesRequired = 'Setup Install Mode';
   SWizardPrivilegesRequired2 = 'Please specify in which install mode Setup should run.';
   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';
   SWizardLanguages = 'Setup Languages';
   SWizardLanguages2 = 'Please specify which Setup languages should be included.';
   SWizardLanguages2 = 'Please specify which Setup languages should be included.';
   SWizardCompiler = 'Compiler Settings';
   SWizardCompiler = 'Compiler Settings';
   SWizardCompiler2 = 'Please specify some basic 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';
   SWizardCompilerSetupIconFileDefaultExt = 'ico';
   SWizardCompilerOutputDir = 'Please specify the folder.';
   SWizardCompilerOutputDir = 'Please specify the folder.';
   SWizardISPP = 'Inno Setup Preprocessor';
   SWizardISPP = 'Inno Setup Preprocessor';
@@ -69,7 +73,7 @@ const
   SWizardFinishButton = '&Finish';
   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]?';
   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.';
   SWizardAppNameError = 'Please specify the application name.';
   SWizardAppVersionError = 'Please specify the application version.';
   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=BIN
Projects/Src/CompRegistryDesignerTest.reg


+ 89 - 0
Projects/Src/CompWizard.dfm

@@ -896,6 +896,95 @@ object WizardForm: TWizardForm
             OnClick = PrivilegesRequiredOverridesAllowedDialogCheckboxClick
             OnClick = PrivilegesRequiredOverridesAllowedDialogCheckboxClick
           end
           end
         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
         object LanguagesPage: TNewNotebookPage
           DesignSize = (
           DesignSize = (
             485
             485

+ 37 - 9
Projects/Src/CompWizard.pas

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

+ 8 - 8
Projects/Src/CompWizardFilesHelper.pas

@@ -12,14 +12,14 @@ unit CompWizardFilesHelper;
 interface
 interface
 
 
 uses
 uses
-  Windows, Classes, StdCtrls,
+  Windows, Classes, Forms, StdCtrls,
   DropListBox;
   DropListBox;
 
 
 type
 type
   TWizardFormFilesHelper = class
   TWizardFormFilesHelper = class
     private
     private
       FWizardFiles: TList;
       FWizardFiles: TList;
-      FHandle: HWND;
+      FForm: TForm;
       FNotCreateAppDirCheck: TCheckBox;
       FNotCreateAppDirCheck: TCheckBox;
       FFilesListBox: TDropListBox;
       FFilesListBox: TDropListBox;
       FEditButton: TButton;
       FEditButton: TButton;
@@ -36,7 +36,7 @@ type
       procedure EditButtonClick(Sender: TObject);
       procedure EditButtonClick(Sender: TObject);
       procedure RemoveButtonClick(Sender: TObject);
       procedure RemoveButtonClick(Sender: TObject);
     public
     public
-      constructor Create(const Handle: HWND;
+      constructor Create(const Form: TForm;
         const NotCreateAppDirCheck: TCheckBox; const FilesListBox: TDropListBox;
         const NotCreateAppDirCheck: TCheckBox; const FilesListBox: TDropListBox;
         const AddButton, AddDirButton, EditButton, RemoveButton: TButton);
         const AddButton, AddDirButton, EditButton, RemoveButton: TButton);
       destructor Destroy; override;
       destructor Destroy; override;
@@ -47,11 +47,11 @@ type
 implementation
 implementation
 
 
 uses
 uses
-  SysUtils, Forms, UITypes,
+  SysUtils, UITypes,
   CmnFunc, CmnFunc2, BrowseFunc, PathFunc,
   CmnFunc, CmnFunc2, BrowseFunc, PathFunc,
   CompMsgs, CompWizardFile;
   CompMsgs, CompWizardFile;
 
 
-constructor TWizardFormFilesHelper.Create(const Handle: HWND;
+constructor TWizardFormFilesHelper.Create(const Form: TForm;
   const NotCreateAppDirCheck: TCheckBox; const FilesListBox: TDropListBox;
   const NotCreateAppDirCheck: TCheckBox; const FilesListBox: TDropListBox;
   const AddButton, AddDirButton, EditButton, RemoveButton: TButton);
   const AddButton, AddDirButton, EditButton, RemoveButton: TButton);
 begin
 begin
@@ -59,7 +59,7 @@ begin
 
 
   FWizardFiles := TList.Create;
   FWizardFiles := TList.Create;
 
 
-  FHandle := Handle;
+  FForm := Form;
   FNotCreateAppDirCheck := NotCreateAppDirCheck;
   FNotCreateAppDirCheck := NotCreateAppDirCheck;
   FFilesListBox := FilesListBox;
   FFilesListBox := FilesListBox;
   FEditButton := EditButton;
   FEditButton := EditButton;
@@ -158,7 +158,7 @@ var
 begin
 begin
   FileList := TStringList.Create;
   FileList := TStringList.Create;
   try
   try
-    if NewGetOpenFileNameMulti('', FileList, '', SWizardAllFilesFilter, '', FHandle) then begin
+    if NewGetOpenFileNameMulti('', FileList, '', SWizardAllFilesFilter, '', FForm.Handle) then begin
       FileList.Sort;
       FileList.Sort;
       for I := 0 to FileList.Count-1 do
       for I := 0 to FileList.Count-1 do
         AddWizardFile(FileList[I], False, False);
         AddWizardFile(FileList[I], False, False);
@@ -175,7 +175,7 @@ var
   Recurse: Boolean;
   Recurse: Boolean;
 begin
 begin
   Path := '';
   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
     case MsgBox(Format(SWizardAppFilesSubDirsMessage, [Path]), '', mbConfirmation, MB_YESNOCANCEL) of
       IDYES: Recurse := True;
       IDYES: Recurse := True;
       IDNO: Recurse := False;
       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>
 <ul>
   <li>Compiler IDE changes: 
   <li>Compiler IDE changes: 
   <ul>
   <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>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>
   </ul>
   </li>
   </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>
   <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>
   <li>Minor tweaks and documentation improvements.</li>
 </ul>
 </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 >
 <p><a href="https://jrsoftware.org/files/is6.2-whatsnew.htm">Inno Setup 6.2 Revision History</a></p >