Sfoglia il codice sorgente

Add HKA: implement [Registry] and {reg:...}, update whatsnew, update examples.
Also fixed admin install mode side-by-side issues and update sameappnotes help.

Todo: test both.

Martijn Laan 7 anni fa
parent
commit
344a6cd0c3

+ 5 - 5
Examples/CodeDlg.iss

@@ -20,11 +20,11 @@ Source: "MyProg.chm"; DestDir: "{app}"
 Source: "Readme.txt"; DestDir: "{app}"; Flags: isreadme
 Source: "Readme.txt"; DestDir: "{app}"; Flags: isreadme
 
 
 [Registry]
 [Registry]
-Root: HKCU; Subkey: "Software\My Company"; Flags: uninsdeletekeyifempty
-Root: HKCU; Subkey: "Software\My Company\My Program"; Flags: uninsdeletekey
-Root: HKCU; Subkey: "Software\My Company\My Program\Settings"; ValueType: string; ValueName: "Name"; ValueData: "{code:GetUser|Name}"
-Root: HKCU; Subkey: "Software\My Company\My Program\Settings"; ValueType: string; ValueName: "Company"; ValueData: "{code:GetUser|Company}"
-Root: HKCU; Subkey: "Software\My Company\My Program\Settings"; ValueType: string; ValueName: "DataDir"; ValueData: "{code:GetDataDir}"
+Root: HKA; Subkey: "Software\My Company"; Flags: uninsdeletekeyifempty
+Root: HKA; Subkey: "Software\My Company\My Program"; Flags: uninsdeletekey
+Root: HKA; Subkey: "Software\My Company\My Program\Settings"; ValueType: string; ValueName: "Name"; ValueData: "{code:GetUser|Name}"
+Root: HKA; Subkey: "Software\My Company\My Program\Settings"; ValueType: string; ValueName: "Company"; ValueData: "{code:GetUser|Company}"
+Root: HKA; Subkey: "Software\My Company\My Program\Settings"; ValueType: string; ValueName: "DataDir"; ValueData: "{code:GetDataDir}"
 ; etc.
 ; etc.
 
 
 [Dirs]
 [Dirs]

+ 2 - 2
Examples/CodePrepareToInstall.iss

@@ -36,7 +36,7 @@ begin
   Restarted := ExpandConstant('{param:restart|0}') = '1';
   Restarted := ExpandConstant('{param:restart|0}') = '1';
 
 
   if not Restarted then begin
   if not Restarted then begin
-    Result := not RegValueExists(HKLM, 'Software\Microsoft\Windows\CurrentVersion\RunOnce', RunOnceName);
+    Result := not RegValueExists(HKA, 'Software\Microsoft\Windows\CurrentVersion\RunOnce', RunOnceName);
     if not Result then
     if not Result then
       MsgBox(QuitMessageReboot, mbError, mb_Ok);
       MsgBox(QuitMessageReboot, mbError, mb_Ok);
   end else
   end else
@@ -91,7 +91,7 @@ begin
 
 
   //<your code here>
   //<your code here>
   
   
-  RegWriteStringValue(HKLM, 'Software\Microsoft\Windows\CurrentVersion\RunOnce', RunOnceName, RunOnceData);
+  RegWriteStringValue(HKA, 'Software\Microsoft\Windows\CurrentVersion\RunOnce', RunOnceName, RunOnceData);
 end;
 end;
 
 
 function PrepareToInstall(var NeedsRestart: Boolean): String;
 function PrepareToInstall(var NeedsRestart: Boolean): String;

+ 7 - 6
Examples/Example3.iss

@@ -26,9 +26,10 @@ Name: "{group}\My Program"; Filename: "{app}\MyProg.exe"
 ; you don't need a [Registry] section.
 ; you don't need a [Registry] section.
 
 
 [Registry]
 [Registry]
-; Create "Software\My Company\My Program" keys under LOCAL_MACHINE. The flags
-; tell it to always delete the "My Program" key upon uninstall, and delete
-; the "My Company" key if there is nothing left in it.
-Root: HKLM; Subkey: "Software\My Company"; Flags: uninsdeletekeyifempty
-Root: HKLM; Subkey: "Software\My Company\My Program"; Flags: uninsdeletekey
-Root: HKLM; Subkey: "Software\My Company\My Program\Settings"; ValueType: string; ValueName: "Path"; ValueData: "{app}"
+; Create "Software\My Company\My Program" keys under CURRENT_USER or
+; LOCAL_MACHINE depending on administrative or non administrative install
+; mode. The flags tell it to always delete the "My Program" key upon
+; uninstall, and delete the "My Company" key if there is nothing left in it.
+Root: HKA; Subkey: "Software\My Company"; Flags: uninsdeletekeyifempty
+Root: HKA; Subkey: "Software\My Company\My Program"; Flags: uninsdeletekey
+Root: HKA; Subkey: "Software\My Company\My Program\Settings"; ValueType: string; ValueName: "Path"; ValueData: "{app}"

+ 3 - 3
ISHelp/isetup.xml

@@ -763,7 +763,7 @@ For example: If you used <tt>{src}\MYPROG.EXE</tt> on an entry and the user is i
 <li><i>SubkeyName, ValueName,</i> and <i>DefaultValue</i> may include constants. Note that you do <i>not</i> need to escape the closing brace of a constant as described above; that is only necessary when the closing brace is used elsewhere.</li>
 <li><i>SubkeyName, ValueName,</i> and <i>DefaultValue</i> may include constants. Note that you do <i>not</i> need to escape the closing brace of a constant as described above; that is only necessary when the closing brace is used elsewhere.</li>
 </ul>
 </ul>
 <example>
 <example>
-<pre>{reg:HKLM\Software\My Program,Path|{autopf}\My Program}</pre>
+<pre>{reg:HKA\Software\My Program,Path|{autopf}\My Program}</pre>
 </example>
 </example>
 </dd>
 </dd>
 
 
@@ -2552,7 +2552,7 @@ Filename: "{app}\MYPROG.EXE"; Description: "Launch application"; Flags: postinst
 </param>
 </param>
 
 
 <param name="RunOnceId">
 <param name="RunOnceId">
-<p>Valid only in an <tt>[UninstallRun]</tt> section. If the same application is installed more than once, "run" entries will be duplicated in the uninstall log file. By assigning a string to <tt>RunOnceId</tt>, you can ensure that a particular <tt>[UninstallRun]</tt> entry will only be executed once during uninstallation. For example, if two or more "run" entries in the uninstall log have a <tt>RunOnceId</tt> setting of "DelService", only the latest entry with a <tt>RunOnceId</tt> setting of "DelService" will be executed; the rest will be ignored. Note that <tt>RunOnceId</tt> comparisons are case-sensitive.</p>
+<p>Valid only in an <tt>[UninstallRun]</tt> section. If the <link topic="sameappnotes">same application</link> is installed more than once, "run" entries will be duplicated in the uninstall log file. By assigning a string to <tt>RunOnceId</tt>, you can ensure that a particular <tt>[UninstallRun]</tt> entry will only be executed once during uninstallation. For example, if two or more "run" entries in the uninstall log have a <tt>RunOnceId</tt> setting of "DelService", only the latest entry with a <tt>RunOnceId</tt> setting of "DelService" will be executed; the rest will be ignored. Note that <tt>RunOnceId</tt> comparisons are case-sensitive.</p>
 <example>
 <example>
 <pre>RunOnceId: "DelService"</pre>
 <pre>RunOnceId: "DelService"</pre>
 </example>
 </example>
@@ -5321,7 +5321,7 @@ SignTool=byparam format c:
 <topic name="sameappnotes" title="Same Application">
 <topic name="sameappnotes" title="Same Application">
 <keyword value="Same Application" />
 <keyword value="Same Application" />
 <body>
 <body>
-<p>"Same application" refers to two separate installations that share the same <link topic="setup_appid">AppId</link> setting (or if <tt>AppId</tt> is not set, the same <link topic="setup_appname">AppName</link> setting), and the same <link topic="32vs64bitinstalls">32-bit or 64-bit install mode</link>.</p>
+<p>"Same application" refers to two separate installations that share the same <link topic="setup_appid">AppId</link> setting (or if <tt>AppId</tt> is not set, the same <link topic="setup_appname">AppName</link> setting), and the same <link topic="admininstallmode">administrative or non administrative install mode</link>. In administrative install mode the installations additionally need to share the same <link topic="32vs64bitinstalls">32-bit or 64-bit install mode</link>.</p>
 </body>
 </body>
 </topic>
 </topic>
 
 

+ 6 - 6
ISHelp/isxfunc.xml

@@ -1854,7 +1854,7 @@ end;</pre>
         <prototype>function RegKeyExists(const RootKey: Integer; const SubKeyName: String): Boolean;</prototype>
         <prototype>function RegKeyExists(const RootKey: Integer; const SubKeyName: String): Boolean;</prototype>
         <description><p>Returns True if the specified registry key exists.</p></description>
         <description><p>Returns True if the specified registry key exists.</p></description>
         <example><pre>begin
         <example><pre>begin
-  if RegKeyExists(HKEY_CURRENT_USER, 'Software\Jordan Russell\Inno Setup') then
+  if RegKeyExists(HKEY_AUTO, 'Software\Jordan Russell\Inno Setup') then
   begin
   begin
     // The key exists
     // The key exists
   end;
   end;
@@ -1967,7 +1967,7 @@ end;</pre></example>
         <description><p>Writes the specified REG_SZ-type registry value. Returns True if successful, False otherwise.</p></description>
         <description><p>Writes the specified REG_SZ-type registry value. Returns True if successful, False otherwise.</p></description>
         <remarks><p>If the value already exists and is of type REG_EXPAND_SZ, the new value will also be of type REG_EXPAND_SZ. Otherwise, a REG_SZ-type value will be created.</p></remarks>
         <remarks><p>If the value already exists and is of type REG_EXPAND_SZ, the new value will also be of type REG_EXPAND_SZ. Otherwise, a REG_SZ-type value will be created.</p></remarks>
         <example><pre>begin
         <example><pre>begin
-  RegWriteStringValue(HKEY_CURRENT_USER, 'Software\My Company\My Program',
+  RegWriteStringValue(HKEY_AUTO, 'Software\My Company\My Program',
     'UserName', ExpandConstant('{sysuserinfoname}'));
     'UserName', ExpandConstant('{sysuserinfoname}'));
 end;</pre></example>
 end;</pre></example>
       </function>
       </function>
@@ -1976,7 +1976,7 @@ end;</pre></example>
         <prototype>function RegWriteExpandStringValue(const RootKey: Integer; const SubKeyName, ValueName, Data: String): Boolean;</prototype>
         <prototype>function RegWriteExpandStringValue(const RootKey: Integer; const SubKeyName, ValueName, Data: String): Boolean;</prototype>
         <description><p>Writes the specified REG_EXPAND_SZ-type registry value. Returns True if successful, False otherwise.</p></description>
         <description><p>Writes the specified REG_EXPAND_SZ-type registry value. Returns True if successful, False otherwise.</p></description>
         <example><pre>begin
         <example><pre>begin
-  RegWriteStringValue(HKEY_CURRENT_USER, 'Software\My Company\My Program',
+  RegWriteStringValue(HKEY_AUTO, 'Software\My Company\My Program',
     'UserName', '%UserName%);
     'UserName', '%UserName%);
 end;</pre></example>
 end;</pre></example>
       </function>
       </function>
@@ -1986,7 +1986,7 @@ end;</pre></example>
         <description><p>Writes the specified REG_MULTI_SZ-type registry value. Returns True if successful, False otherwise.</p></description>
         <description><p>Writes the specified REG_MULTI_SZ-type registry value. Returns True if successful, False otherwise.</p></description>
         <remarks><p>In a REG_MULTI_SZ-type value, each string is separated by a null character (<tt>#0</tt>).</p></remarks>
         <remarks><p>In a REG_MULTI_SZ-type value, each string is separated by a null character (<tt>#0</tt>).</p></remarks>
         <example><pre>begin
         <example><pre>begin
-  RegWriteMultiStringValue(HKEY_CURRENT_USER, 'Software\My Company\My Program',
+  RegWriteMultiStringValue(HKEY_AUTO, 'Software\My Company\My Program',
     'MultiStringTest', 'String1' + #0 + 'String2' + #0 + 'String3');
     'MultiStringTest', 'String1' + #0 + 'String2' + #0 + 'String3');
 end;</pre></example>
 end;</pre></example>
       </function>
       </function>
@@ -1995,7 +1995,7 @@ end;</pre></example>
         <prototype>function RegWriteDWordValue(const RootKey: Integer; const SubKeyName, ValueName: String; const Data: Cardinal): Boolean;</prototype>
         <prototype>function RegWriteDWordValue(const RootKey: Integer; const SubKeyName, ValueName: String; const Data: Cardinal): Boolean;</prototype>
         <description><p>Writes the specified REG_DWORD-type registry value. Returns True if successful, False otherwise.</p></description>
         <description><p>Writes the specified REG_DWORD-type registry value. Returns True if successful, False otherwise.</p></description>
         <example><pre>begin
         <example><pre>begin
-  RegWriteDWordValue(HKEY_CURRENT_USER, 'Software\My Company\My Program',
+  RegWriteDWordValue(HKEY_AUTO, 'Software\My Company\My Program',
     'ShowToolbar', 1);
     'ShowToolbar', 1);
 end;</pre></example>
 end;</pre></example>
       </function>
       </function>
@@ -2004,7 +2004,7 @@ end;</pre></example>
         <prototype>function RegWriteBinaryValue(const RootKey: Integer; const SubKeyName, ValueName: String; const Data: AnsiString): Boolean;</prototype>
         <prototype>function RegWriteBinaryValue(const RootKey: Integer; const SubKeyName, ValueName: String; const Data: AnsiString): Boolean;</prototype>
         <description><p>Writes the specified REG_BINARY-type registry value. Returns True if successful, False otherwise.</p></description>
         <description><p>Writes the specified REG_BINARY-type registry value. Returns True if successful, False otherwise.</p></description>
         <example><pre>begin
         <example><pre>begin
-  RegWriteBinaryValue(HKEY_CURRENT_USER, 'Software\My Company\My Program',
+  RegWriteBinaryValue(HKEY_AUTO, 'Software\My Company\My Program',
     'BinaryTest', 'Whatever' + #1#2#3#4);
     'BinaryTest', 'Whatever' + #1#2#3#4);
 end;</pre></example>
 end;</pre></example>
       </function>
       </function>

+ 5 - 2
Projects/Compile.pas

@@ -2655,7 +2655,8 @@ function TSetupCompiler.CheckConst(const S: String; const MinVersion: TSetupVers
       KeyConst: HKEY;
       KeyConst: HKEY;
     end;
     end;
   const
   const
-    KeyNameConsts: array[0..4] of TKeyNameConst = (
+    KeyNameConsts: array[0..5] of TKeyNameConst = (
+      (KeyName: 'HKA';  KeyConst: HKEY_AUTO),
       (KeyName: 'HKCR'; KeyConst: HKEY_CLASSES_ROOT),
       (KeyName: 'HKCR'; KeyConst: HKEY_CLASSES_ROOT),
       (KeyName: 'HKCU'; KeyConst: HKEY_CURRENT_USER),
       (KeyName: 'HKCU'; KeyConst: HKEY_CURRENT_USER),
       (KeyName: 'HKLM'; KeyConst: HKEY_LOCAL_MACHINE),
       (KeyName: 'HKLM'; KeyConst: HKEY_LOCAL_MACHINE),
@@ -5505,7 +5506,9 @@ begin
           SetLength(S, Length(S)-2);
           SetLength(S, Length(S)-2);
         end;
         end;
       end;
       end;
-      if S = 'HKCR' then
+      if S = 'HKA' then
+        RootKey := HKEY_AUTO
+      else if S = 'HKCR' then
         RootKey := HKEY_CLASSES_ROOT
         RootKey := HKEY_CLASSES_ROOT
       else if S = 'HKCU' then begin
       else if S = 'HKCU' then begin
         UsedUserAreas.Add(S);
         UsedUserAreas.Add(S);

+ 2 - 1
Projects/InstFunc.pas

@@ -117,7 +117,7 @@ function ForceDirectories(const DisableFsRedir: Boolean; Dir: String): Boolean;
 implementation
 implementation
 
 
 uses
 uses
-  Messages, ShellApi, PathFunc, Msgs, MsgIDs, FileClass, RedirFunc;
+  Messages, ShellApi, PathFunc, Msgs, MsgIDs, FileClass, RedirFunc, SetupTypes;
 
 
 procedure InternalError(const Id: String);
 procedure InternalError(const Id: String);
 begin
 begin
@@ -155,6 +155,7 @@ end;
 function GetRegRootKeyName(const RootKey: HKEY): String;
 function GetRegRootKeyName(const RootKey: HKEY): String;
 begin
 begin
   case RootKey of
   case RootKey of
+    HKEY_AUTO: InternalError('GetRegRootKeyName called for HKEY_AUTO');
     HKEY_CLASSES_ROOT: Result := 'HKEY_CLASSES_ROOT';
     HKEY_CLASSES_ROOT: Result := 'HKEY_CLASSES_ROOT';
     HKEY_CURRENT_USER: Result := 'HKEY_CURRENT_USER';
     HKEY_CURRENT_USER: Result := 'HKEY_CURRENT_USER';
     HKEY_LOCAL_MACHINE: Result := 'HKEY_LOCAL_MACHINE';
     HKEY_LOCAL_MACHINE: Result := 'HKEY_LOCAL_MACHINE';

+ 24 - 26
Projects/Install.pas

@@ -509,17 +509,12 @@ var
     MajorVersion, MinorVersion, I: Integer;
     MajorVersion, MinorVersion, I: Integer;
     EstimatedSize: Integer64;
     EstimatedSize: Integer64;
   begin
   begin
-    if IsAdminInstallMode then
-      RootKey := HKEY_LOCAL_MACHINE
-    else
-      RootKey := HKEY_CURRENT_USER;
+    RootKey := InstallModeRootKey;
     SubkeyName := NEWREGSTR_PATH_UNINSTALL + '\' + UninstallRegKeyBaseName + '_is1';
     SubkeyName := NEWREGSTR_PATH_UNINSTALL + '\' + UninstallRegKeyBaseName + '_is1';
  
  
-    Log('Deleting any uninstall keys left over from previous installs');
+    Log('Deleting any uninstall key left over from previous install');
 
 
-    RegDeleteKeyIncludingSubkeys(InstallDefaultRegView, HKEY_CURRENT_USER, PChar(SubkeyName));
-    if IsAdmin then
-      RegDeleteKeyIncludingSubkeys(InstallDefaultRegView, HKEY_LOCAL_MACHINE, PChar(SubkeyName));
+    RegDeleteKeyIncludingSubkeys(InstallDefaultRegView, RootKey, PChar(SubkeyName));
 
 
     LogFmt('Creating new uninstall key: %s\%s', [GetRegRootKeyName(RootKey), SubkeyName]);
     LogFmt('Creating new uninstall key: %s\%s', [GetRegRootKeyName(RootKey), SubkeyName]);
 
 
@@ -2099,7 +2094,7 @@ var
   const
   const
     REG_QWORD = 11;
     REG_QWORD = 11;
   var
   var
-    K: HKEY;
+    RK, K: HKEY;
     Disp: DWORD;
     Disp: DWORD;
     N, V, ExistingData: String;
     N, V, ExistingData: String;
     ExistingType, NewType, DV: DWORD;
     ExistingType, NewType, DV: DWORD;
@@ -2120,8 +2115,11 @@ var
           DebugNotifyEntry(seRegistry, CurRegNumber);
           DebugNotifyEntry(seRegistry, CurRegNumber);
           NotifyBeforeInstallEntry(BeforeInstall);
           NotifyBeforeInstallEntry(BeforeInstall);
           Log('-- Registry entry --');
           Log('-- Registry entry --');
+          RK := RootKey;
+          if RK = HKEY_AUTO then
+            RK := InstallModeRootKey;
           S := ExpandConst(Subkey);
           S := ExpandConst(Subkey);
-          LogFmt('Key: %s\%s', [GetRegRootKeyName(RootKey), Subkey]);
+          LogFmt('Key: %s\%s', [GetRegRootKeyName(RK), Subkey]);
           N := ExpandConst(ValueName);
           N := ExpandConst(ValueName);
           if N <> '' then
           if N <> '' then
             LogFmt('Value name: %s', [N]);
             LogFmt('Value name: %s', [N]);
@@ -2147,7 +2145,7 @@ var
               if roDeleteKey in Options then begin
               if roDeleteKey in Options then begin
                 if IsDeletableSubkey(S) then begin
                 if IsDeletableSubkey(S) then begin
                   Log('Deleting the key.');
                   Log('Deleting the key.');
-                  RegDeleteKeyIncludingSubkeys(RV, RootKey, PChar(S));
+                  RegDeleteKeyIncludingSubkeys(RV, RK, PChar(S));
                   DidDeleteKey := True;
                   DidDeleteKey := True;
                 end else
                 end else
                   Log('Key to delete is not deletable.');
                   Log('Key to delete is not deletable.');
@@ -2160,7 +2158,7 @@ var
               end else if (roDeleteValue in Options) and (Typ = rtNone) then begin
               end else if (roDeleteValue in Options) and (Typ = rtNone) then begin
                 { We're going to delete a value with no intention of creating
                 { We're going to delete a value with no intention of creating
                   another, so don't create the key if it didn't exist. }
                   another, so don't create the key if it didn't exist. }
-                if RegOpenKeyExView(RV, RootKey, PChar(S), 0, KEY_SET_VALUE, K) = ERROR_SUCCESS then begin
+                if RegOpenKeyExView(RV, RK, PChar(S), 0, KEY_SET_VALUE, K) = ERROR_SUCCESS then begin
                   Log('Deleting the value.');
                   Log('Deleting the value.');
                   RegDeleteValue(K, PChar(N));
                   RegDeleteValue(K, PChar(N));
                   RegCloseKey(K);
                   RegCloseKey(K);
@@ -2174,33 +2172,33 @@ var
                   RegOpenKeyExView, since we may (in a rather unlikely scenario)
                   RegOpenKeyExView, since we may (in a rather unlikely scenario)
                   need those permissions in order for those calls to succeed }
                   need those permissions in order for those calls to succeed }
                 if PermissionsEntry <> -1 then
                 if PermissionsEntry <> -1 then
-                  ApplyPermissions(RV, RootKey, S, PermissionsEntry);
+                  ApplyPermissions(RV, RK, S, PermissionsEntry);
                 { Create or open the key }
                 { Create or open the key }
                 if not(roDontCreateKey in Options) then begin
                 if not(roDontCreateKey in Options) then begin
                   Log('Creating or opening the key.');
                   Log('Creating or opening the key.');
-                  ErrorCode := RegCreateKeyExView(RV, RootKey, PChar(S), 0, nil,
+                  ErrorCode := RegCreateKeyExView(RV, RK, PChar(S), 0, nil,
                     REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE or KEY_SET_VALUE,
                     REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE or KEY_SET_VALUE,
                     nil, K, @Disp);
                     nil, K, @Disp);
                   if ErrorCode = ERROR_SUCCESS then begin
                   if ErrorCode = ERROR_SUCCESS then begin
                     { Apply permissions again if a new key was created }
                     { Apply permissions again if a new key was created }
                     if (Disp = REG_CREATED_NEW_KEY) and (PermissionsEntry <> -1) then begin
                     if (Disp = REG_CREATED_NEW_KEY) and (PermissionsEntry <> -1) then begin
                       Log('New key created, need to set permissions again.');
                       Log('New key created, need to set permissions again.');
-                      ApplyPermissions(RV, RootKey, S, PermissionsEntry);
+                      ApplyPermissions(RV, RK, S, PermissionsEntry);
                     end;
                     end;
                   end
                   end
                   else begin
                   else begin
                     if not(roNoError in Options) then
                     if not(roNoError in Options) then
-                      RegError(reRegCreateKeyEx, RootKey, S, ErrorCode);
+                      RegError(reRegCreateKeyEx, RK, S, ErrorCode);
                   end;
                   end;
                 end
                 end
                 else begin
                 else begin
                   if Typ <> rtNone then begin
                   if Typ <> rtNone then begin
                     Log('Opening the key.');
                     Log('Opening the key.');
-                    ErrorCode := RegOpenKeyExView(RV, RootKey, PChar(S), 0,
+                    ErrorCode := RegOpenKeyExView(RV, RK, PChar(S), 0,
                       KEY_QUERY_VALUE or KEY_SET_VALUE, K);
                       KEY_QUERY_VALUE or KEY_SET_VALUE, K);
                     if (ErrorCode <> ERROR_SUCCESS) and (ErrorCode <> ERROR_FILE_NOT_FOUND) then
                     if (ErrorCode <> ERROR_SUCCESS) and (ErrorCode <> ERROR_FILE_NOT_FOUND) then
                       if not(roNoError in Options) then
                       if not(roNoError in Options) then
-                        RegError(reRegOpenKeyEx, RootKey, S, ErrorCode);
+                        RegError(reRegOpenKeyEx, RK, S, ErrorCode);
                   end
                   end
                   else begin
                   else begin
                     { We're not creating a value, and we're not just deleting a
                     { We're not creating a value, and we're not just deleting a
@@ -2261,7 +2259,7 @@ var
                             PChar(V), (Length(V)+1)*SizeOf(V[1]));
                             PChar(V), (Length(V)+1)*SizeOf(V[1]));
                           if (ErrorCode <> ERROR_SUCCESS) and
                           if (ErrorCode <> ERROR_SUCCESS) and
                              not(roNoError in Options) then
                              not(roNoError in Options) then
-                            RegError(reRegSetValueEx, RootKey, S, ErrorCode);
+                            RegError(reRegSetValueEx, RK, S, ErrorCode);
                         end;
                         end;
                       rtDWord: begin
                       rtDWord: begin
                           DV := StrToInt(ExpandConst(ValueData));
                           DV := StrToInt(ExpandConst(ValueData));
@@ -2269,7 +2267,7 @@ var
                             @DV, SizeOf(DV));
                             @DV, SizeOf(DV));
                           if (ErrorCode <> ERROR_SUCCESS) and
                           if (ErrorCode <> ERROR_SUCCESS) and
                              not(roNoError in Options) then
                              not(roNoError in Options) then
-                            RegError(reRegSetValueEx, RootKey, S, ErrorCode);
+                            RegError(reRegSetValueEx, RK, S, ErrorCode);
                         end;
                         end;
                       rtQWord: begin
                       rtQWord: begin
                           if not StrToInteger64(ExpandConst(ValueData), QV) then
                           if not StrToInteger64(ExpandConst(ValueData), QV) then
@@ -2278,7 +2276,7 @@ var
                             @TLargeInteger(QV), SizeOf(TLargeInteger(QV)));
                             @TLargeInteger(QV), SizeOf(TLargeInteger(QV)));
                           if (ErrorCode <> ERROR_SUCCESS) and
                           if (ErrorCode <> ERROR_SUCCESS) and
                              not(roNoError in Options) then
                              not(roNoError in Options) then
-                            RegError(reRegSetValueEx, RootKey, S, ErrorCode);
+                            RegError(reRegSetValueEx, RK, S, ErrorCode);
                         end;
                         end;
                       rtBinary: begin
                       rtBinary: begin
 {$IFDEF UNICODE}
 {$IFDEF UNICODE}
@@ -2293,7 +2291,7 @@ var
 {$ENDIF}
 {$ENDIF}
                           if (ErrorCode <> ERROR_SUCCESS) and
                           if (ErrorCode <> ERROR_SUCCESS) and
                              not(roNoError in Options) then
                              not(roNoError in Options) then
-                            RegError(reRegSetValueEx, RootKey, S, ErrorCode);
+                            RegError(reRegSetValueEx, RK, S, ErrorCode);
                         end;
                         end;
                       end;
                       end;
                     Log('Successfully created or set the value.');
                     Log('Successfully created or set the value.');
@@ -2316,17 +2314,17 @@ var
           
           
           if roUninsDeleteEntireKey in Options then
           if roUninsDeleteEntireKey in Options then
             if IsDeletableSubkey(S) then
             if IsDeletableSubkey(S) then
-              UninstLog.AddReg(utRegDeleteEntireKey, RV, RootKey, [S]);
+              UninstLog.AddReg(utRegDeleteEntireKey, RV, RK, [S]);
           if roUninsDeleteEntireKeyIfEmpty in Options then
           if roUninsDeleteEntireKeyIfEmpty in Options then
             if IsDeletableSubkey(S) then
             if IsDeletableSubkey(S) then
-              UninstLog.AddReg(utRegDeleteKeyIfEmpty, RV, RootKey, [S]);
+              UninstLog.AddReg(utRegDeleteKeyIfEmpty, RV, RK, [S]);
           if roUninsDeleteValue in Options then
           if roUninsDeleteValue in Options then
-            UninstLog.AddReg(utRegDeleteValue, RV, RootKey, [S, N]);
+            UninstLog.AddReg(utRegDeleteValue, RV, RK, [S, N]);
             { ^ must add roUninsDeleteValue after roUninstDeleteEntireKey*
             { ^ must add roUninsDeleteValue after roUninstDeleteEntireKey*
               since the entry may have both the roUninsDeleteValue and
               since the entry may have both the roUninsDeleteValue and
               roUninsDeleteEntireKeyIfEmpty options }
               roUninsDeleteEntireKeyIfEmpty options }
           if roUninsClearValue in Options then
           if roUninsClearValue in Options then
-            UninstLog.AddReg(utRegClearValue, RV, RootKey, [S, N]);
+            UninstLog.AddReg(utRegClearValue, RV, RK, [S, N]);
 
 
           NotifyAfterInstallEntry(AfterInstall);
           NotifyAfterInstallEntry(AfterInstall);
         end;
         end;

+ 18 - 16
Projects/Main.pas

@@ -177,6 +177,7 @@ var
 {$IFDEF IS_D12}
 {$IFDEF IS_D12}
   TaskbarButtonHidden: Boolean;
   TaskbarButtonHidden: Boolean;
 {$ENDIF}
 {$ENDIF}
+  InstallModeRootKey: HKEY;
 
 
   CodeRunner: TScriptRunner;
   CodeRunner: TScriptRunner;
 
 
@@ -327,26 +328,20 @@ end;
 
 
 { Based on FindPreviousData in Wizard.pas }
 { Based on FindPreviousData in Wizard.pas }
 function GetPreviousData(const ExpandedAppID, ValueName, DefaultValueData: String): String;
 function GetPreviousData(const ExpandedAppID, ValueName, DefaultValueData: String): String;
-const
-  RootKeys: array[0..1] of HKEY = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE);
 var
 var
-  I: Integer;
   H: HKEY;
   H: HKEY;
   UninstallRegKeyBaseName: String;
   UninstallRegKeyBaseName: String;
 begin
 begin
   Result := DefaultValueData;
   Result := DefaultValueData;
   if ExpandedAppId <> '' then begin
   if ExpandedAppId <> '' then begin
     UninstallRegKeyBaseName := GetUninstallRegKeyBaseName(ExpandedAppId);
     UninstallRegKeyBaseName := GetUninstallRegKeyBaseName(ExpandedAppId);
-    for I := 0 to 1 do begin
-      if RegOpenKeyExView(InstallDefaultRegView, RootKeys[I],
-         PChar(Format('%s\%s_is1', [NEWREGSTR_PATH_UNINSTALL, UninstallRegKeyBaseName])),
-         0, KEY_QUERY_VALUE, H) = ERROR_SUCCESS then begin
-        try
-          RegQueryStringValue (H, PChar(ValueName), Result);
-        finally
-          RegCloseKey (H);
-        end;
-        Break;
+    if RegOpenKeyExView(InstallDefaultRegView, InstallModeRootKey,
+       PChar(Format('%s\%s_is1', [NEWREGSTR_PATH_UNINSTALL, UninstallRegKeyBaseName])),
+       0, KEY_QUERY_VALUE, H) = ERROR_SUCCESS then begin
+      try
+        RegQueryStringValue (H, PChar(ValueName), Result);
+      finally
+        RegCloseKey (H);
       end;
       end;
     end;
     end;
   end;
   end;
@@ -710,7 +705,7 @@ function ExpandIndividualConst(Cnst: String;
         Delete(Cnst, Length(Cnst)-1, 2);
         Delete(Cnst, Length(Cnst)-1, 2);
     end;
     end;
   end;
   end;
-
+  
   procedure NoUninstallConstError(const C: String);
   procedure NoUninstallConstError(const C: String);
   begin
   begin
     InternalError(Format('Cannot evaluate "%s" constant during Uninstall', [C]));
     InternalError(Format('Cannot evaluate "%s" constant during Uninstall', [C]));
@@ -744,7 +739,8 @@ function ExpandIndividualConst(Cnst: String;
       KeyConst: HKEY;
       KeyConst: HKEY;
     end;
     end;
   const
   const
-    KeyNameConsts: array[0..4] of TKeyNameConst = (
+    KeyNameConsts: array[0..5] of TKeyNameConst = (
+      (KeyName: 'HKA';  KeyConst: HKEY_AUTO),
       (KeyName: 'HKCR'; KeyConst: HKEY_CLASSES_ROOT),
       (KeyName: 'HKCR'; KeyConst: HKEY_CLASSES_ROOT),
       (KeyName: 'HKCU'; KeyConst: HKEY_CURRENT_USER),
       (KeyName: 'HKCU'; KeyConst: HKEY_CURRENT_USER),
       (KeyName: 'HKLM'; KeyConst: HKEY_LOCAL_MACHINE),
       (KeyName: 'HKLM'; KeyConst: HKEY_LOCAL_MACHINE),
@@ -781,6 +777,8 @@ function ExpandIndividualConst(Cnst: String;
         for J := Low(KeyNameConsts) to High(KeyNameConsts) do
         for J := Low(KeyNameConsts) to High(KeyNameConsts) do
           if CompareText(KeyNameConsts[J].KeyName, Z) = 0 then begin
           if CompareText(KeyNameConsts[J].KeyName, Z) = 0 then begin
             RootKey := KeyNameConsts[J].KeyConst;
             RootKey := KeyNameConsts[J].KeyConst;
+            if RootKey = HKEY_AUTO then
+              RootKey := InstallModeRootKey;
             Break;
             Break;
           end;
           end;
         if RootKey <> 0 then begin
         if RootKey <> 0 then begin
@@ -2463,10 +2461,14 @@ begin
 end;
 end;
 
 
 procedure InitializeAdminInstallMode(const AAdminInstallMode: Boolean);
 procedure InitializeAdminInstallMode(const AAdminInstallMode: Boolean);
-{ Initializes IsAdminInstallMode }
+{ Initializes IsAdminInstallMode and other global variables that depend on it }
+const
+  RootKeys: array[Boolean] of HKEY = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE);
 begin
 begin
   LogFmt('Administrative install mode: %s', [SYesNo[AAdminInstallMode]]);
   LogFmt('Administrative install mode: %s', [SYesNo[AAdminInstallMode]]);
   IsAdminInstallMode := AAdminInstallMode;
   IsAdminInstallMode := AAdminInstallMode;
+  InstallModeRootKey := RootKeys[AAdminInstallMode];
+  LogFmt('Install mode root key: %s', [GetRegRootKeyName(InstallModeRootKey)]);
 end;
 end;
 
 
 procedure Initialize64BitInstallMode(const A64BitInstallMode: Boolean);
 procedure Initialize64BitInstallMode(const A64BitInstallMode: Boolean);

+ 2 - 6
Projects/ScriptFunc_R.pas

@@ -387,16 +387,12 @@ end;
 { CmnFunc2 }
 { CmnFunc2 }
 function CmnFunc2Proc(Caller: TPSExec; Proc: TPSExternalProcRec; Global, Stack: TPSStack): Boolean;
 function CmnFunc2Proc(Caller: TPSExec; Proc: TPSExternalProcRec; Global, Stack: TPSStack): Boolean;
 
 
-  procedure CrackCodeRootKey(CodeRootKey: Longint; var RegView: TRegView;
+  procedure CrackCodeRootKey(CodeRootKey: HKEY; var RegView: TRegView;
     var RootKey: HKEY);
     var RootKey: HKEY);
   begin
   begin
     if (CodeRootKey and not CodeRootKeyValidFlags) = HKEY_AUTO then begin
     if (CodeRootKey and not CodeRootKeyValidFlags) = HKEY_AUTO then begin
       { Change HKA to HKLM or HKCU, keeping our special flag bits. }
       { Change HKA to HKLM or HKCU, keeping our special flag bits. }
-      CodeRootKey := CodeRootKey and CodeRootKeyValidFlags;
-      if IsAdminInstallMode then
-        CodeRootKey := CodeRootKey or HKEY_LOCAL_MACHINE
-      else
-        CodeRootKey := CodeRootKey or HKEY_CURRENT_USER;
+      CodeRootKey := (CodeRootKey and CodeRootKeyValidFlags) or InstallModeRootKey;
     end else begin
     end else begin
       { Allow only predefined key handles (8xxxxxxx). Can't accept handles to
       { Allow only predefined key handles (8xxxxxxx). Can't accept handles to
         open keys because they might have our special flag bits set.
         open keys because they might have our special flag bits set.

+ 1 - 1
Projects/SetupTypes.pas

@@ -51,7 +51,7 @@ const
   CodeRootKeyFlag64Bit = $02000000;
   CodeRootKeyFlag64Bit = $02000000;
   CodeRootKeyValidFlags = CodeRootKeyFlag32Bit or CodeRootKeyFlag64Bit;
   CodeRootKeyValidFlags = CodeRootKeyFlag32Bit or CodeRootKeyFlag64Bit;
 
 
-  HKEY_AUTO = 0; { Any value will work as long as it doesn't match a predefined key handle (8xxxxxxx) nor includes any of the CodeRootKeyValidFlags flags. }
+  HKEY_AUTO = 1; { Any value will work as long as it isn't 0 and doesn't match a predefined key handle (8xxxxxxx) nor includes any of the CodeRootKeyValidFlags flags. }
 
 
 function StringsToCommaString(const Strings: TStrings): String;
 function StringsToCommaString(const Strings: TStrings): String;
 procedure SetStringsFromCommaString(const Strings: TStrings; const Value: String);
 procedure SetStringsFromCommaString(const Strings: TStrings; const Value: String);

+ 32 - 40
Projects/Wizard.pas

@@ -1225,55 +1225,47 @@ end;
 
 
 { Also see GetPreviousData in Main.pas }
 { Also see GetPreviousData in Main.pas }
 procedure TWizardForm.FindPreviousData;
 procedure TWizardForm.FindPreviousData;
-const
-  RootKeys: array[0..1] of HKEY = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE);
 var
 var
-  I: Integer;
   H: HKEY;
   H: HKEY;
   S, ExpandedAppId, UninstallRegKeyBaseName: String;
   S, ExpandedAppId, UninstallRegKeyBaseName: String;
 begin
 begin
   ExpandedAppId := ExpandConst(SetupHeader.AppId);
   ExpandedAppId := ExpandConst(SetupHeader.AppId);
   if ExpandedAppId <> '' then begin
   if ExpandedAppId <> '' then begin
-    { First look in HKEY_CURRENT_USER. That's where Inno Setup creates
-      the uninstall key for non-administrators. If the key doesn't exist
-      under HKEY_CURRENT_USER, check HKEY_LOCAL_MACHINE. }
     UninstallRegKeyBaseName := GetUninstallRegKeyBaseName(ExpandedAppId);
     UninstallRegKeyBaseName := GetUninstallRegKeyBaseName(ExpandedAppId);
-    for I := 0 to 1 do
-      if RegOpenKeyExView(InstallDefaultRegView, RootKeys[I],
-         PChar(Format('%s\%s_is1', [NEWREGSTR_PATH_UNINSTALL, UninstallRegKeyBaseName])),
-         0, KEY_QUERY_VALUE, H) = ERROR_SUCCESS then begin
-        try
-          { do not localize or change the following strings }
-          if shUsePreviousAppDir in SetupHeader.Options then
-            RegQueryStringValue(H, 'Inno Setup: App Path', PrevAppDir);
-          if shUsePreviousGroup in SetupHeader.Options then begin
-            RegQueryStringValue(H, 'Inno Setup: Icon Group', PrevGroup);
-            if RegValueExists(H, 'Inno Setup: No Icons') then
-              PrevNoIcons := True;
-          end;
-          if shUsePreviousSetupType in SetupHeader.Options then begin
-            RegQueryStringValue(H, 'Inno Setup: Setup Type', PrevSetupType);
-            if RegQueryStringValue(H, 'Inno Setup: Selected Components', S) then
-              SetStringsFromCommaString(PrevSelectedComponents, S);
-            if RegQueryStringValue(H, 'Inno Setup: Deselected Components', S) then
-              SetStringsFromCommaString(PrevDeselectedComponents, S);
-          end;
-          if shUsePreviousTasks in SetupHeader.Options then begin
-            if RegQueryStringValue(H, 'Inno Setup: Selected Tasks', S) then
-              SetStringsFromCommaString(PrevSelectedTasks, S);
-            if RegQueryStringValue(H, 'Inno Setup: Deselected Tasks', S) then
-              SetStringsFromCommaString(PrevDeselectedTasks, S);
-          end;
-          if shUsePreviousUserInfo in SetupHeader.Options then begin
-            RegQueryStringValue(H, 'Inno Setup: User Info: Name', PrevUserInfoName);
-            RegQueryStringValue(H, 'Inno Setup: User Info: Organization', PrevUserInfoOrg);
-            RegQueryStringValue(H, 'Inno Setup: User Info: Serial', PrevUserInfoSerial);
-          end;
-        finally
-          RegCloseKey(H);
+    if RegOpenKeyExView(InstallDefaultRegView, InstallModeRootKey,
+       PChar(Format('%s\%s_is1', [NEWREGSTR_PATH_UNINSTALL, UninstallRegKeyBaseName])),
+       0, KEY_QUERY_VALUE, H) = ERROR_SUCCESS then begin
+      try
+        { do not localize or change the following strings }
+        if shUsePreviousAppDir in SetupHeader.Options then
+          RegQueryStringValue(H, 'Inno Setup: App Path', PrevAppDir);
+        if shUsePreviousGroup in SetupHeader.Options then begin
+          RegQueryStringValue(H, 'Inno Setup: Icon Group', PrevGroup);
+          if RegValueExists(H, 'Inno Setup: No Icons') then
+            PrevNoIcons := True;
         end;
         end;
-        Break;
+        if shUsePreviousSetupType in SetupHeader.Options then begin
+          RegQueryStringValue(H, 'Inno Setup: Setup Type', PrevSetupType);
+          if RegQueryStringValue(H, 'Inno Setup: Selected Components', S) then
+            SetStringsFromCommaString(PrevSelectedComponents, S);
+          if RegQueryStringValue(H, 'Inno Setup: Deselected Components', S) then
+            SetStringsFromCommaString(PrevDeselectedComponents, S);
+        end;
+        if shUsePreviousTasks in SetupHeader.Options then begin
+          if RegQueryStringValue(H, 'Inno Setup: Selected Tasks', S) then
+            SetStringsFromCommaString(PrevSelectedTasks, S);
+          if RegQueryStringValue(H, 'Inno Setup: Deselected Tasks', S) then
+            SetStringsFromCommaString(PrevDeselectedTasks, S);
+        end;
+        if shUsePreviousUserInfo in SetupHeader.Options then begin
+          RegQueryStringValue(H, 'Inno Setup: User Info: Name', PrevUserInfoName);
+          RegQueryStringValue(H, 'Inno Setup: User Info: Organization', PrevUserInfoOrg);
+          RegQueryStringValue(H, 'Inno Setup: User Info: Serial', PrevUserInfoSerial);
+        end;
+      finally
+        RegCloseKey(H);
       end;
       end;
+    end;
   end;
   end;
 end;
 end;
 
 

+ 9 - 1
whatsnew.htm

@@ -30,14 +30,22 @@ For conditions of distribution and use, see <a href="http://www.jrsoftware.org/f
 <p>A number of changes have been made to the administrative vs. non administrative install mode functionality:</p>
 <p>A number of changes have been made to the administrative vs. non administrative install mode functionality:</p>
 <ul>
 <ul>
    <li>Added a separate <a href="http://www.jrsoftware.org/is6help/index.php?topic=admininstallmode">Non Administrative Install Mode</a> topic to the help file.</li>
    <li>Added a separate <a href="http://www.jrsoftware.org/is6help/index.php?topic=admininstallmode">Non Administrative Install Mode</a> topic to the help file.</li>
+   <li>Two installations that do not share the same administrative or non administrative install mode no longer count as the <a href="http://www.jrsoftware.org/is6help/index.php?topic=sameappnotes">same application</a>. This means you can now install in these modes side-by-side even if the installers share the same <tt>AppId</tt> setting.</li>
+</ul>
+<ul>
    <li>Added new &quot;auto&quot; constants which automatically map to their &quot;common&quot; form unless the installation is running in non administrative install mode, in which case they map to their &quot;user&quot; form. It is recommended to update your scripts to use these "auto" constants as much as possible to avoid mistakes. The list of added &quot;auto&quot; constants is: <tt>{autoappdata}</tt>, <tt>{autocf}</tt>, <tt>{autocf32}</tt>, <tt>{autocf64}</tt>, <tt>{autodesktop}</tt>, <tt>{autodocs}</tt>, <tt>{autopf}</tt>, <tt>{autopf32}</tt>, <tt>{autopf64}</tt>, <tt>{autoprograms}</tt>, <tt>{autostartmenu}</tt><tt>, {autostartup}</tt>, and <tt>{autotemplates}</tt>.</li>
    <li>Added new &quot;auto&quot; constants which automatically map to their &quot;common&quot; form unless the installation is running in non administrative install mode, in which case they map to their &quot;user&quot; form. It is recommended to update your scripts to use these "auto" constants as much as possible to avoid mistakes. The list of added &quot;auto&quot; constants is: <tt>{autoappdata}</tt>, <tt>{autocf}</tt>, <tt>{autocf32}</tt>, <tt>{autocf64}</tt>, <tt>{autodesktop}</tt>, <tt>{autodocs}</tt>, <tt>{autopf}</tt>, <tt>{autopf32}</tt>, <tt>{autopf64}</tt>, <tt>{autoprograms}</tt>, <tt>{autostartmenu}</tt><tt>, {autostartup}</tt>, and <tt>{autotemplates}</tt>.</li>
    <li><b>Change in default behavior:</b> The &quot;common&quot; Shell Folder contants are no longer mapped to their "user" form if the installation is running in non administrative install mode. Instead they simply return the requested common directory. The list of affected &quot;common&quot; constants is: <tt>{commonappdata}</tt>, <tt>{commondesktop}</tt>, <tt>{commondocs}</tt>, <tt>{commonprograms}</tt>, <tt>{commonstartmenu}</tt><tt>, {commonstartup}</tt>, and <tt>{commontemplates}</tt>.</li>
    <li><b>Change in default behavior:</b> The &quot;common&quot; Shell Folder contants are no longer mapped to their "user" form if the installation is running in non administrative install mode. Instead they simply return the requested common directory. The list of affected &quot;common&quot; constants is: <tt>{commonappdata}</tt>, <tt>{commondesktop}</tt>, <tt>{commondocs}</tt>, <tt>{commonprograms}</tt>, <tt>{commonstartmenu}</tt><tt>, {commonstartup}</tt>, and <tt>{commontemplates}</tt>.</li>
    <li>The <tt>{commonfavorites}</tt> constant has been removed: this directory doesn't exist anymore in Windows.</li>
    <li>The <tt>{commonfavorites}</tt> constant has been removed: this directory doesn't exist anymore in Windows.</li>
    <li>The <tt>{pf}</tt> and <tt>{cf}</tt> constants have been renamed to <tt>{commonpf}</tt> and <tt>{commoncf}</tt>. This includes <tt>{pf32}</tt> to <tt>{commonpf32}</tt>, etc. The old names are still supported, but it is recommended to update your scripts to the new names.</tt>
    <li>The <tt>{pf}</tt> and <tt>{cf}</tt> constants have been renamed to <tt>{commonpf}</tt> and <tt>{commoncf}</tt>. This includes <tt>{pf32}</tt> to <tt>{commonpf32}</tt>, etc. The old names are still supported, but it is recommended to update your scripts to the new names.</tt>
    <li>The <tt>{userpf}</tt> and <tt>{usercf}</tt> constants and the <tt>AlwaysUsePersonalGroup</tt> [Setup] section directive can now correctly trigger a <a href="http://www.jrsoftware.org/files/is5-whatsnew.htm#useduserareaswarnings">used user areas warning</a>.</li>
    <li>The <tt>{userpf}</tt> and <tt>{usercf}</tt> constants and the <tt>AlwaysUsePersonalGroup</tt> [Setup] section directive can now correctly trigger a <a href="http://www.jrsoftware.org/files/is5-whatsnew.htm#useduserareaswarnings">used user areas warning</a>.</li>
-   <li>Updated all examples to use <tt>{autopf}</tt> instead of <tt>{pf}</tt>.</li>
+   <li>Updated all examples to use <tt>{autopf}</tt> instead of <tt>{pf}</tt>, etc.</li>
    <li>Pascal Scripting change: Removed the <tt>GetShellFolder</tt> support function. As the help file already recommended, use <tt>ExpandConstant</tt> instead.</li>
    <li>Pascal Scripting change: Removed the <tt>GetShellFolder</tt> support function. As the help file already recommended, use <tt>ExpandConstant</tt> instead.</li>
 </ul>
 </ul>
+<ul>
+   <li>Added new [Registry] section <tt>Root</tt> value: <tt>HKA</tt>. Equals <tt>HKLM</tt> in administrative install mode, and <tt>HKCU</tt> otherwise. Also supported by <tt>{reg:...}</tt> constants. It is recommended to update your scripts to use <tt>HKA</tt> as much as possible to avoid mistakes. <tt>HKA32</tt> and <tt>HKA64</tt> are also supported.</tt></li>
+   <li>Updated all examples to use <tt>HKA</tt> instead of <tt>HKLM</tt> or <tt>HKCU</tt> as much as possible.</li>
+   <li>Pascal Scripting change: Added <tt>HKA</tt> and <tt>HKEY_AUTO</tt> constants. <tt>HKA32</tt>, <tt>HKA64</tt>, <tt>HKEY_AUTO_32</tt>, and <tt>HKEY_AUTO_64</tt> are also supported.</li>
+</ul>
 <p>Other changes:</p>
 <p>Other changes:</p>
 <ul>
 <ul>
 <li>Added new [Setup] section directive: <tt>VersionInfoOriginalFileName</tt>, which sets the original filename version value.</li>
 <li>Added new [Setup] section directive: <tt>VersionInfoOriginalFileName</tt>, which sets the original filename version value.</li>