Inno Setup インストーラーには PrivilegesRequired
ディレクティブ があり、インストーラーの起動時に特権の昇格が必要な場合に制御できます。管理者以外のユーザーでもインストーラーを機能させたい(Program Files
ではなく、ユーザーフォルダーにアプリをインストールしても問題ありません)。そのため、PrivilegesRequired
をnone
(文書化されていない値)に設定します。これにより、UACプロンプトポップアップは管理ユーザーのみに表示されるため、Program Files
にもインストールできます。管理者以外のユーザーにはUACプロンプトが表示されないため、ユーザーも(ユーザーフォルダーに)アプリケーションをインストールできます。
ただし、これにはいくつかの欠点があります。
Inno Setupが必要なときにのみ特権を昇格させる方法はありますか(ユーザーが管理者アカウントのみが書き込み可能なインストールフォルダーを選択した場合)
Inno Setupにはこの設定がないと思います。しかし、おそらく、プログラムによる解決策(Inno Setup Pascalスクリプト)または何らかのプラグイン/ DLLがあります。
Inno Setup 6には 非管理インストールモード のサポートが組み込まれていることに注意してください。
Inno Setup 6には 非管理インストールモード のサポートが組み込まれています。
基本的に、単純に PrivilegesRequiredOverridesAllowed
を設定できます:
[Setup]
PrivilegesRequiredOverridesAllowed=commandline dialog
以下は、 @ TLamaの回答 に基づいた、Inno Setup 5の私の(現在は廃止された)ソリューションです。
昇格せずにセットアップを開始すると、いくつかの例外を除き、昇格が要求されます。
ユーザーが新しいインストールで昇格を拒否した場合、インストーラーは自動的に「ローカルアプリケーションデータ」フォルダーにフォールバックします。つまりC:\Users\standard\AppData\Local\AppName
。
その他の改善:
PrivilegesRequired=none
を使用すると、インストーラーはアンインストール情報を昇格時にHKLM
ではなくHKCU
に書き込みます。#define AppId "myapp"
#define AppName "MyApp"
#define InnoSetupReg \
"Software\Microsoft\Windows\CurrentVersion\Uninstall\" + AppId + "_is1"
#define InnoSetupAppPathReg "Inno Setup: App Path"
[Setup]
AppId={#AppId}
PrivilegesRequired=none
...
[Code]
function IsWinVista: Boolean;
begin
Result := (GetWindowsVersion >= $06000000);
end;
function HaveWriteAccessToApp: Boolean;
var
FileName: string;
begin
FileName := AddBackslash(WizardDirValue) + 'writetest.tmp';
Result := SaveStringToFile(FileName, 'test', False);
if Result then
begin
Log(Format(
'Have write access to the last installation path [%s]', [WizardDirValue]));
DeleteFile(FileName);
end
else
begin
Log(Format('Does not have write access to the last installation path [%s]', [
WizardDirValue]));
end;
end;
procedure ExitProcess(uExitCode: UINT);
external '[email protected] stdcall';
function ShellExecute(hwnd: HWND; lpOperation: string; lpFile: string;
lpParameters: string; lpDirectory: string; nShowCmd: Integer): THandle;
external '[email protected] stdcall';
function Elevate: Boolean;
var
I: Integer;
RetVal: Integer;
Params: string;
S: string;
begin
{ Collect current instance parameters }
for I := 1 to ParamCount do
begin
S := ParamStr(I);
{ Unique log file name for the elevated instance }
if CompareText(Copy(S, 1, 5), '/LOG=') = 0 then
begin
S := S + '-elevated';
end;
{ Do not pass our /SL5 switch }
if CompareText(Copy(S, 1, 5), '/SL5=') <> 0 then
begin
Params := Params + AddQuotes(S) + ' ';
end;
end;
{ ... and add selected language }
Params := Params + '/LANG=' + ActiveLanguage;
Log(Format('Elevating setup with parameters [%s]', [Params]));
RetVal := ShellExecute(0, 'runas', ExpandConstant('{srcexe}'), Params, '', SW_SHOW);
Log(Format('Running elevated setup returned [%d]', [RetVal]));
Result := (RetVal > 32);
{ if elevated executing of this setup succeeded, then... }
if Result then
begin
Log('Elevation succeeded');
{ exit this non-elevated setup instance }
ExitProcess(0);
end
else
begin
Log(Format('Elevation failed [%s]', [SysErrorMessage(RetVal)]));
end;
end;
procedure InitializeWizard;
var
S: string;
Upgrade: Boolean;
begin
Upgrade :=
RegQueryStringValue(HKLM, '{#InnoSetupReg}', '{#InnoSetupAppPathReg}', S) or
RegQueryStringValue(HKCU, '{#InnoSetupReg}', '{#InnoSetupAppPathReg}', S);
{ elevate }
if not IsWinVista then
begin
Log(Format('This version of Windows [%x] does not support elevation', [
GetWindowsVersion]));
end
else
if IsAdminLoggedOn then
begin
Log('Running elevated');
end
else
begin
Log('Running non-elevated');
if Upgrade then
begin
if not HaveWriteAccessToApp then
begin
Elevate;
end;
end
else
begin
if not Elevate then
begin
WizardForm.DirEdit.Text := ExpandConstant('{localappdata}\{#AppName}');
Log(Format('Falling back to local application user folder [%s]', [
WizardForm.DirEdit.Text]));
end;
end;
end;
end;
Inno Setupのライフタイム中にセットアッププロセスを条件付きで昇格させる組み込みの方法はありません。ただし、runas
動詞を使用してセットアッププロセスを実行し、昇格していない動詞を強制終了できます。私が書いたスクリプトは少しトリッキーですが、それを行う方法の可能性を示しています。
警告:
ここで使用するコードは、常に昇格したセットアップインスタンスを実行しようとします。標高が実際に必要かどうかのチェックはありません(標高が必要かどうかを判断する方法は、オプションで別の質問で尋ねてください)。また、現時点では、そのような手動による昇格が安全かどうかはわかりません。 Inno SetupがPrivilegesRequired
ディレクティブの値に何らかの方法で依存していない(または依存しない)かどうかはわかりません。そして最後に、この昇格は、関連するWindowsバージョンでのみ実行する必要があります。このスクリプトでは、これのチェックは行われません。
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
PrivilegesRequired=lowest
[Code]
#ifdef UNICODE
#define AW "W"
#else
#define AW "A"
#endif
type
HINSTANCE = THandle;
procedure ExitProcess(uExitCode: UINT);
external '[email protected] stdcall';
function ShellExecute(hwnd: HWND; lpOperation: string; lpFile: string;
lpParameters: string; lpDirectory: string; nShowCmd: Integer): HINSTANCE;
external 'ShellExecute{#AW}@Shell32.dll stdcall';
var
Elevated: Boolean;
PagesSkipped: Boolean;
function CmdLineParamExists(const Value: string): Boolean;
var
I: Integer;
begin
Result := False;
for I := 1 to ParamCount do
if CompareText(ParamStr(I), Value) = 0 then
begin
Result := True;
Exit;
end;
end;
procedure InitializeWizard;
begin
{ initialize our helper variables }
Elevated := CmdLineParamExists('/ELEVATE');
PagesSkipped := False;
end;
function ShouldSkipPage(PageID: Integer): Boolean;
begin
{ if we've executed this instance as elevated, skip pages unless we're }
{ on the directory selection page }
Result := not PagesSkipped and Elevated and (PageID <> wpSelectDir);
{ if we've reached the directory selection page, set our flag variable }
if not Result then
PagesSkipped := True;
end;
function NextButtonClick(CurPageID: Integer): Boolean;
var
Params: string;
RetVal: HINSTANCE;
begin
Result := True;
{ if we are on the directory selection page and we are not running the }
{ instance we've manually elevated, then... }
if not Elevated and (CurPageID = wpSelectDir) then
begin
{ pass the already selected directory to the executing parameters and }
{ include our own custom /ELEVATE parameter which is used to tell the }
{ setup to skip all the pages and get to the directory selection page }
Params := ExpandConstant('/DIR="{app}" /ELEVATE');
{ because executing of the setup loader is not possible with ShellExec }
{ function, we need to use a WinAPI workaround }
RetVal := ShellExecute(WizardForm.Handle, 'runas',
ExpandConstant('{srcexe}'), Params, '', SW_SHOW);
{ if elevated executing of this setup succeeded, then... }
if RetVal > 32 then
begin
{ exit this non-elevated setup instance }
ExitProcess(0);
end
else
{ executing of this setup failed for some reason; one common reason may }
{ be simply closing the UAC dialog }
begin
{ handling of this situation is upon you, this line forces the wizard }
{ stay on the current page }
Result := False;
{ and possibly show some error message to the user }
MsgBox(Format('Elevating of this setup failed. Code: %d', [RetVal]),
mbError, MB_OK);
end;
end;
end;