소스 검색

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 년 전
부모
커밋
344a6cd0c3
13개의 변경된 파일116개의 추가작업 그리고 115개의 파일을 삭제
  1. 5 5
      Examples/CodeDlg.iss
  2. 2 2
      Examples/CodePrepareToInstall.iss
  3. 7 6
      Examples/Example3.iss
  4. 3 3
      ISHelp/isetup.xml
  5. 6 6
      ISHelp/isxfunc.xml
  6. 5 2
      Projects/Compile.pas
  7. 2 1
      Projects/InstFunc.pas
  8. 24 26
      Projects/Install.pas
  9. 18 16
      Projects/Main.pas
  10. 2 6
      Projects/ScriptFunc_R.pas
  11. 1 1
      Projects/SetupTypes.pas
  12. 32 40
      Projects/Wizard.pas
  13. 9 1
      whatsnew.htm

+ 5 - 5
Examples/CodeDlg.iss

@@ -20,11 +20,11 @@ Source: "MyProg.chm"; DestDir: "{app}"
 Source: "Readme.txt"; DestDir: "{app}"; Flags: isreadme
 
 [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.
 
 [Dirs]

+ 2 - 2
Examples/CodePrepareToInstall.iss

@@ -36,7 +36,7 @@ begin
   Restarted := ExpandConstant('{param:restart|0}') = '1';
 
   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
       MsgBox(QuitMessageReboot, mbError, mb_Ok);
   end else
@@ -91,7 +91,7 @@ begin
 
   //<your code here>
   
-  RegWriteStringValue(HKLM, 'Software\Microsoft\Windows\CurrentVersion\RunOnce', RunOnceName, RunOnceData);
+  RegWriteStringValue(HKA, 'Software\Microsoft\Windows\CurrentVersion\RunOnce', RunOnceName, RunOnceData);
 end;
 
 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.
 
 [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>
 </ul>
 <example>
-<pre>{reg:HKLM\Software\My Program,Path|{autopf}\My Program}</pre>
+<pre>{reg:HKA\Software\My Program,Path|{autopf}\My Program}</pre>
 </example>
 </dd>
 
@@ -2552,7 +2552,7 @@ Filename: "{app}\MYPROG.EXE"; Description: "Launch application"; Flags: postinst
 </param>
 
 <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>
 <pre>RunOnceId: "DelService"</pre>
 </example>
@@ -5321,7 +5321,7 @@ SignTool=byparam format c:
 <topic name="sameappnotes" title="Same Application">
 <keyword value="Same Application" />
 <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>
 </topic>
 

+ 6 - 6
ISHelp/isxfunc.xml

@@ -1854,7 +1854,7 @@ end;</pre>
         <prototype>function RegKeyExists(const RootKey: Integer; const SubKeyName: String): Boolean;</prototype>
         <description><p>Returns True if the specified registry key exists.</p></description>
         <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
     // The key exists
   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>
         <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
-  RegWriteStringValue(HKEY_CURRENT_USER, 'Software\My Company\My Program',
+  RegWriteStringValue(HKEY_AUTO, 'Software\My Company\My Program',
     'UserName', ExpandConstant('{sysuserinfoname}'));
 end;</pre></example>
       </function>
@@ -1976,7 +1976,7 @@ end;</pre></example>
         <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>
         <example><pre>begin
-  RegWriteStringValue(HKEY_CURRENT_USER, 'Software\My Company\My Program',
+  RegWriteStringValue(HKEY_AUTO, 'Software\My Company\My Program',
     'UserName', '%UserName%);
 end;</pre></example>
       </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>
         <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
-  RegWriteMultiStringValue(HKEY_CURRENT_USER, 'Software\My Company\My Program',
+  RegWriteMultiStringValue(HKEY_AUTO, 'Software\My Company\My Program',
     'MultiStringTest', 'String1' + #0 + 'String2' + #0 + 'String3');
 end;</pre></example>
       </function>
@@ -1995,7 +1995,7 @@ end;</pre></example>
         <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>
         <example><pre>begin
-  RegWriteDWordValue(HKEY_CURRENT_USER, 'Software\My Company\My Program',
+  RegWriteDWordValue(HKEY_AUTO, 'Software\My Company\My Program',
     'ShowToolbar', 1);
 end;</pre></example>
       </function>
@@ -2004,7 +2004,7 @@ end;</pre></example>
         <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>
         <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);
 end;</pre></example>
       </function>

+ 5 - 2
Projects/Compile.pas

@@ -2655,7 +2655,8 @@ function TSetupCompiler.CheckConst(const S: String; const MinVersion: TSetupVers
       KeyConst: HKEY;
     end;
   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: 'HKCU'; KeyConst: HKEY_CURRENT_USER),
       (KeyName: 'HKLM'; KeyConst: HKEY_LOCAL_MACHINE),
@@ -5505,7 +5506,9 @@ begin
           SetLength(S, Length(S)-2);
         end;
       end;
-      if S = 'HKCR' then
+      if S = 'HKA' then
+        RootKey := HKEY_AUTO
+      else if S = 'HKCR' then
         RootKey := HKEY_CLASSES_ROOT
       else if S = 'HKCU' then begin
         UsedUserAreas.Add(S);

+ 2 - 1
Projects/InstFunc.pas

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

+ 24 - 26
Projects/Install.pas

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

+ 18 - 16
Projects/Main.pas

@@ -177,6 +177,7 @@ var
 {$IFDEF IS_D12}
   TaskbarButtonHidden: Boolean;
 {$ENDIF}
+  InstallModeRootKey: HKEY;
 
   CodeRunner: TScriptRunner;
 
@@ -327,26 +328,20 @@ end;
 
 { Based on FindPreviousData in Wizard.pas }
 function GetPreviousData(const ExpandedAppID, ValueName, DefaultValueData: String): String;
-const
-  RootKeys: array[0..1] of HKEY = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE);
 var
-  I: Integer;
   H: HKEY;
   UninstallRegKeyBaseName: String;
 begin
   Result := DefaultValueData;
   if ExpandedAppId <> '' then begin
     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;
@@ -710,7 +705,7 @@ function ExpandIndividualConst(Cnst: String;
         Delete(Cnst, Length(Cnst)-1, 2);
     end;
   end;
-
+  
   procedure NoUninstallConstError(const C: String);
   begin
     InternalError(Format('Cannot evaluate "%s" constant during Uninstall', [C]));
@@ -744,7 +739,8 @@ function ExpandIndividualConst(Cnst: String;
       KeyConst: HKEY;
     end;
   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: 'HKCU'; KeyConst: HKEY_CURRENT_USER),
       (KeyName: 'HKLM'; KeyConst: HKEY_LOCAL_MACHINE),
@@ -781,6 +777,8 @@ function ExpandIndividualConst(Cnst: String;
         for J := Low(KeyNameConsts) to High(KeyNameConsts) do
           if CompareText(KeyNameConsts[J].KeyName, Z) = 0 then begin
             RootKey := KeyNameConsts[J].KeyConst;
+            if RootKey = HKEY_AUTO then
+              RootKey := InstallModeRootKey;
             Break;
           end;
         if RootKey <> 0 then begin
@@ -2463,10 +2461,14 @@ begin
 end;
 
 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
   LogFmt('Administrative install mode: %s', [SYesNo[AAdminInstallMode]]);
   IsAdminInstallMode := AAdminInstallMode;
+  InstallModeRootKey := RootKeys[AAdminInstallMode];
+  LogFmt('Install mode root key: %s', [GetRegRootKeyName(InstallModeRootKey)]);
 end;
 
 procedure Initialize64BitInstallMode(const A64BitInstallMode: Boolean);

+ 2 - 6
Projects/ScriptFunc_R.pas

@@ -387,16 +387,12 @@ end;
 { CmnFunc2 }
 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);
   begin
     if (CodeRootKey and not CodeRootKeyValidFlags) = HKEY_AUTO then begin
       { 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
       { Allow only predefined key handles (8xxxxxxx). Can't accept handles to
         open keys because they might have our special flag bits set.

+ 1 - 1
Projects/SetupTypes.pas

@@ -51,7 +51,7 @@ const
   CodeRootKeyFlag64Bit = $02000000;
   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;
 procedure SetStringsFromCommaString(const Strings: TStrings; const Value: String);

+ 32 - 40
Projects/Wizard.pas

@@ -1225,55 +1225,47 @@ end;
 
 { Also see GetPreviousData in Main.pas }
 procedure TWizardForm.FindPreviousData;
-const
-  RootKeys: array[0..1] of HKEY = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE);
 var
-  I: Integer;
   H: HKEY;
   S, ExpandedAppId, UninstallRegKeyBaseName: String;
 begin
   ExpandedAppId := ExpandConst(SetupHeader.AppId);
   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);
-    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;
-        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;
 

+ 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>
 <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>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><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>{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>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>
 </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>
 <ul>
 <li>Added new [Setup] section directive: <tt>VersionInfoOriginalFileName</tt>, which sets the original filename version value.</li>