Specify the registry uninstall key location/hive via [Code]

前端 未结 2 939
旧时难觅i
旧时难觅i 2020-12-18 13:33

Inno Setup by default looks at the PrivilegesRequired setup variable, if this is set to admin or poweruser, the installer installs the

相关标签:
2条回答
  • 2020-12-18 13:56

    Inno Setup 6 has a built-in support for selecting between "Install for all users" and "Install for me only".

    Basically, you can simply set PrivilegesRequiredOverridesAllowed:

    [Setup]
    PrivilegesRequiredOverridesAllowed=commandline dialog
    


    For Inno Setup 5: As you found yourself, the logic is hard-coded. You cannot really control that.

    The closest you can get is by using the undocumented (deprecated) PrivilegesRequired=none.

    With this value (and with a help of installer-autodetection in Windows):

    • When you start the installer with an un-privileged account, it starts without prompting you for elevation. If you decide you need to elevate during the installation, you can restart the installer elevated.
    • When you start the installer with a privileged account, it always prompts you for elevation and won't start, if you reject that. So the installer always runs elevated. Again, you would have to restart the installer, if you decide you to proceed un-elevated. See How to Start a Process Unelevated or maybe Run un-elevated command from an elevated prompt?.

    It's not exactly, what you want, but I do not think you can get any closer.


    You can of course copy (move) the registry key between the HKCU and HKLM yourself by a code:

    function MoveHKCUUninstallKeyToHKLM: Boolean;
    var
      UninstallKey: string;
      AppId: string;
      I: Integer;
      ValueNames: TArrayOfString;
      ValueName: string;
      ValueStr: string;
      ValueDWord: Cardinal;
    begin
      if '{#emit SetupSetting("AppId")}' <> '' then
      begin
        AppId := '{#emit SetupSetting("AppId")}';
      end
        else
      begin
        AppId := '{#emit SetupSetting("AppName")}';
      end;
    
      Result := False;
      if AppId = '' then
      begin
        Log('Cannot identify AppId');
      end
        else
      begin
        UninstallKey :=
          'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + AppId + '_is1';
        Log(Format(
          'AppId identified as "%s", using uninstall key "%s"', [AppId, UninstallKey]));
        if not RegKeyExists(HKCU, UninstallKey) then
        begin
          Log('HKCU uninstall key not found');
        end
          else
        if RegKeyExists(HKLM, UninstallKey) then
        begin
          Log('HKLM uninstall key exists already');
        end
          else
        begin
          Log('HKCU uninstall key found and HKLM key not exists yet');
    
          if not RegGetValueNames(HKCU, UninstallKey, ValueNames) then
          begin
            Log('Cannot list uninstall key values');
          end
            else
          begin
            I := 0;
            Result := True;
            while (I < GetArrayLength(ValueNames)) and Result do
            begin
              ValueName := ValueNames[I];
              if RegQueryStringValue(HKCU, UninstallKey, ValueName, ValueStr) then
              begin
                if not RegWriteStringValue(HKLM, UninstallKey, ValueName, ValueStr) then
                begin
                  Log(Format('Error moving "%s" string value', [ValueName]));
                  Result := False;
                end
                  else
                begin
                  Log(Format('Moved "%s" string value', [ValueName]));
                end;
              end
                else
              if RegQueryDWordValue(HKCU, UninstallKey, ValueName, ValueDWord) then
              begin
                if not RegWriteDWordValue(HKLM, UninstallKey, ValueName, ValueDWord) then
                begin
                  Log(Format('Error moving "%s" dword value', [ValueName]));
                  Result := False;
                end
                  else
                begin
                  Log(Format('Moved "%s" dword value', [ValueName]));
                end;
              end
                else
              begin
                { All uninstall values written by Inno Setup are either string or dword }
                Log(Format('Value "%s" is neither string nor dword', [ValueName]));
                Result := False;
              end;
              Inc(I);
            end;
    
            if Result then
            begin
              if not RegDeleteKeyIncludingSubkeys(HKCU, UninstallKey) then
              begin
                Log('Error removing HKCU uninstall key');
                Result := False;
              end
                else
              begin
                Log('Removed HKCU uninstall key');
              end;
            end;
    
            if not Result then
            begin
              if not RegDeleteKeyIncludingSubkeys(HKCU, UninstallKey) then
              begin
                Log('Failed to move uninstall key to HKLM, ' +
                    'and also failed to rollback the changes');
              end
                else
              begin
                Log('Failed to move uninstall key to HKLM, rolled back the changes');
              end;
            end;
          end;
        end;
      end;
    end;
    
    procedure CurStepChanged(CurStep: TSetupStep);
    begin
      if CurStep = ssPostInstall then
      begin
        Log('Post install');
        MoveHKCUUninstallKeyToHKLM;
      end;
    end;
    
    0 讨论(0)
  • 2020-12-18 14:10

    The PrivilegesRequired=none solution was not what I wanted. In some cases, it still prompts for elevation on administrator accounts and also the registry destination was still not reflective of the users selection.

    Since I was already using a native helper DLL in my Inno Setup project, I coded this in C++ as I'm more comfortable there. I'm calling this method is called in CurStepChanged where CurPage=ssDoneInstall. Just call this method with the [Setup] AppId and whether or not the registry keys should be installed locally or not.

    #include <shlwapi.h>
    extern "C" __declspec(dllexport)
    bool DetectAndMoveRegKeyW(LPCWSTR app_id, bool install_local)
    {
        std::wstring s_app = app_id;
        std::wstring path =
            L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + s_app + L"_is1";
        LPCWSTR c_path = path.c_str();
    
        LRESULT res;
        HKEY source = nullptr, subKey = nullptr;
    
        // try to find source in HKLM
        source = HKEY_LOCAL_MACHINE;
        res = RegOpenKeyExW(source, c_path, 0, KEY_READ, &subKey);
        if (subKey != nullptr)
            RegCloseKey(subKey);
    
        // try to find source in HKCU
        if (res != ERROR_SUCCESS)
        {
            subKey = nullptr;
            source = HKEY_CURRENT_USER;
            res = RegOpenKeyExW(source, c_path, 0, KEY_READ, &subKey);
            if (subKey != nullptr)
                RegCloseKey(subKey);
        }
    
        if (res != ERROR_SUCCESS)
            return false; // cant find the registry key
    
        HKEY dest = install_local ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
        if (source == dest)
            return true; // registry already in the right place
    
    
        // copy registry key to correct destination
        HKEY hOldKey;
        HKEY hNewKey;
        bool bResult = false;
        if (RegOpenKeyW(source, c_path, &hOldKey) == 0)
        {
            if (RegCreateKeyW(dest, c_path, &hNewKey) == 0)
            {
                bResult = (SHCopyKeyW(hOldKey, nullptr, hNewKey, 0) == 0);
                RegCloseKey(hNewKey);
            }
            RegCloseKey(hOldKey);
    
            if (bResult)
            {
                RegDeleteKeyW(source, c_path);
            }
        }
    
        return bResult;
    }
    

    I'm exporting this method as cdecl instead of stdcall, this is because VC++ ignores the C extern and mangles method names anyways when using stdcall. You'll need to import this as cdecl in inno (see inno docs for this). Also, of course this is the Unicode-only implementation, if you require an Ansi version it should be simple enough.

    IMPORTANT NOTICE:
    This code is incomplete, it doesn't account for 64bit registry redirection. Inno-Setup completely ignores windows registry redirection and this code doesn't search the 64 bit registry at all since Inno and itself are running in 32bit.

    0 讨论(0)
提交回复
热议问题