[英]How do I modify the PATH environment variable when running an Inno Setup Installer?
Inno Setup 允許您通過 [Registry] 部分設置環境變量(通過設置與環境變量對應的注冊表項)
但是,有時您不只是想設置環境變量。 通常,您想修改它。 例如:安裝時,您可能希望在 PATH 環境變量中添加/刪除目錄。
如何從 InnoSetup 中修改 PATH 環境變量?
您提供的注冊表項中的路徑是REG_EXPAND_SZ
類型的值。 正如[Registry]部分的 Inno Setup 文檔所述,有一種方法可以將元素附加到這些元素:
對於
string
、expandsz
或multisz
類型的值,您可以在此參數中使用名為{olddata}
的特殊常量。{olddata}
替換為注冊表值的先前數據。 如果您需要將字符串附加到現有值,例如{olddata};{app}
,則{olddata}
常量會很有用。 如果該值不存在或現有值不是字符串類型,則{olddata}
常量將被靜默刪除。
因此,可以使用與此類似的注冊表部分附加到路徑:
[Registry]
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; \
ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};C:\foo"
這會將“C:\\foo”目錄附加到路徑中。
不幸的是,當您第二次安裝時,這會重復,這也應該是固定的。 使用 Pascal 腳本中編碼的函數的Check
參數可用於檢查路徑是否確實需要擴展:
[Registry]
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; \
ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};C:\foo"; \
Check: NeedsAddPath('C:\foo')
此函數讀取原始路徑值並檢查給定目錄是否已包含在其中。 為此,它會預先添加並附加用於分隔路徑中的目錄的分號字符。 考慮到搜索的目錄可能是第一個或最后一個元素的事實,分號字符也被前置並附加到原始值:
[Code]
function NeedsAddPath(Param: string): boolean;
var
OrigPath: string;
begin
if not RegQueryStringValue(HKEY_LOCAL_MACHINE,
'SYSTEM\CurrentControlSet\Control\Session Manager\Environment',
'Path', OrigPath)
then begin
Result := True;
exit;
end;
{ look for the path with leading and trailing semicolon }
{ Pos() returns 0 if not found }
Result := Pos(';' + Param + ';', ';' + OrigPath + ';') = 0;
end;
請注意,在將常量作為參數傳遞給檢查函數之前,您可能需要展開常量,有關詳細信息,請參閱文檔。
在卸載期間從路徑中刪除此目錄可以以類似的方式完成,並留給讀者作為練習。
我遇到了同樣的問題,但盡管有上述答案,我還是得到了一個自定義解決方案,我想與您分享。
首先,我使用兩種方法創建了environment.iss
文件 - 一種用於向環境的Path變量添加路徑,第二種用於刪除它:
[Code]
const EnvironmentKey = 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment';
procedure EnvAddPath(Path: string);
var
Paths: string;
begin
{ Retrieve current path (use empty string if entry not exists) }
if not RegQueryStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths)
then Paths := '';
{ Skip if string already found in path }
if Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';') > 0 then exit;
{ App string to the end of the path variable }
Paths := Paths + ';'+ Path +';'
{ Overwrite (or create if missing) path environment variable }
if RegWriteStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths)
then Log(Format('The [%s] added to PATH: [%s]', [Path, Paths]))
else Log(Format('Error while adding the [%s] to PATH: [%s]', [Path, Paths]));
end;
procedure EnvRemovePath(Path: string);
var
Paths: string;
P: Integer;
begin
{ Skip if registry entry not exists }
if not RegQueryStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths) then
exit;
{ Skip if string not found in path }
P := Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';');
if P = 0 then exit;
{ Update path variable }
Delete(Paths, P - 1, Length(Path) + 1);
{ Overwrite path environment variable }
if RegWriteStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths)
then Log(Format('The [%s] removed from PATH: [%s]', [Path, Paths]))
else Log(Format('Error while removing the [%s] from PATH: [%s]', [Path, Paths]));
end;
參考: RegQueryStringValue
、 RegWriteStringValue
現在在主 .iss 文件中,我可以包含此文件並偵聽 2 個事件(有關事件的更多信息,您可以在文檔中的事件函數部分了解), CurStepChanged
在安裝后添加路徑, CurUninstallStepChanged
在用戶卸載應用程序時將其刪除。 在下面的示例腳本中添加/刪除bin
目錄(相對於安裝目錄):
#include "environment.iss"
[Setup]
ChangesEnvironment=true
; More options in setup section as well as other sections like Files, Components, Tasks...
[Code]
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssPostInstall
then EnvAddPath(ExpandConstant('{app}') +'\bin');
end;
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
if CurUninstallStep = usPostUninstall
then EnvRemovePath(ExpandConstant('{app}') +'\bin');
end;
參考: ExpandConstant
注意#1 :安裝步驟只添加一次路徑(確保安裝的可重復性)。
注意#2 :卸載步驟僅從變量中刪除一次出現的路徑。
獎勵:帶有復選框“添加到 PATH 變量”的安裝步驟。
要使用復選框“添加到 PATH 變量”添加安裝步驟,請在[Tasks]
部分定義新任務(默認選中):
[Tasks]
Name: envPath; Description: "Add to PATH variable"
然后你可以在CurStepChanged
事件中檢查它:
procedure CurStepChanged(CurStep: TSetupStep);
begin
if (CurStep = ssPostInstall) and IsTaskSelected('envPath')
then EnvAddPath(ExpandConstant('{app}') +'\bin');
end;
您可以在 InnoSetup 腳本文件中使用 LegRoom.net 的modpath.iss腳本:
#define MyTitleName "MyApp"
[Setup]
ChangesEnvironment=yes
[CustomMessages]
AppAddPath=Add application directory to your environmental path (required)
[Files]
Source: "install\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs;
[Icons]
Name: "{group}\{cm:UninstallProgram,{#MyTitleName}}"; Filename: "{uninstallexe}"; Comment: "Uninstalls {#MyTitleName}"
Name: "{group}\{#MyTitleName}"; Filename: "{app}\{#MyTitleName}.EXE"; WorkingDir: "{app}"; AppUserModelID: "{#MyTitleName}"; Comment: "Runs {#MyTitleName}"
Name: "{commondesktop}\{#MyTitleName}"; Filename: "{app}\{#MyTitleName}.EXE"; WorkingDir: "{app}"; AppUserModelID: "{#MyTitleName}"; Comment: "Runs {#MyTitleName}"
[Registry]
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}"
[Tasks]
Name: modifypath; Description:{cm:AppAddPath};
[Code]
const
ModPathName = 'modifypath';
ModPathType = 'system';
function ModPathDir(): TArrayOfString;
begin
setArrayLength(Result, 1)
Result[0] := ExpandConstant('{app}');
end;
#include "modpath.iss"
NeedsAddPath
的答案中的 NeedsAddPath不檢查尾隨的\\
和字母大小寫。 修理它。
function NeedsAddPath(Param: string): boolean;
var
OrigPath: string;
begin
if not RegQueryStringValue(
HKEY_LOCAL_MACHINE,
'SYSTEM\CurrentControlSet\Control\Session Manager\Environment',
'Path', OrigPath)
then begin
Result := True;
exit;
end;
{ look for the path with leading and trailing semicolon }
{ Pos() returns 0 if not found }
Result :=
(Pos(';' + UpperCase(Param) + ';', ';' + UpperCase(OrigPath) + ';') = 0) and
(Pos(';' + UpperCase(Param) + '\;', ';' + UpperCase(OrigPath) + ';') = 0);
end;
這是忽略大小寫、檢查是否存在以\\
結尾的路徑並擴展參數中的常量的問題的完整解決方案:
function NeedsAddPath(Param: string): boolean;
var
OrigPath: string;
ParamExpanded: string;
begin
//expand the setup constants like {app} from Param
ParamExpanded := ExpandConstant(Param);
if not RegQueryStringValue(HKEY_LOCAL_MACHINE,
'SYSTEM\CurrentControlSet\Control\Session Manager\Environment',
'Path', OrigPath)
then begin
Result := True;
exit;
end;
// look for the path with leading and trailing semicolon and with or without \ ending
// Pos() returns 0 if not found
Result := Pos(';' + UpperCase(ParamExpanded) + ';', ';' + UpperCase(OrigPath) + ';') = 0;
if Result = True then
Result := Pos(';' + UpperCase(ParamExpanded) + '\;', ';' + UpperCase(OrigPath) + ';') = 0;
end;
我要感謝大家對這個問題的貢獻。 我已經將 Wojciech Mleczek 發布的大約 95% 的代碼合並到我的應用程序安裝程序中。 我確實對該代碼進行了一些更正,可能對其他人有用。 我的變化:
將形式參數Path
重命名為instlPath
。 減少代碼中“路徑”的多次使用(更容易閱讀,IMO)。
安裝/卸載時,為以\\;
結尾的instlPath
添加存在性檢查\\;
.
在安裝過程中,不要加倍;
在當前%PATH%
。
在安裝過程中處理丟失或空的%PATH%
。
在卸載期間,請確保未將起始索引 0 傳遞給Delete()
。
這是我的EnvAddPath()
更新版本:
const EnvironmentKey = 'Environment';
procedure EnvAddPath(instlPath: string);
var
Paths: string;
begin
{ Retrieve current path (use empty string if entry not exists) }
if not RegQueryStringValue(HKEY_CURRENT_USER, EnvironmentKey, 'Path', Paths) then
Paths := '';
if Paths = '' then
Paths := instlPath + ';'
else
begin
{ Skip if string already found in path }
if Pos(';' + Uppercase(instlPath) + ';', ';' + Uppercase(Paths) + ';') > 0 then exit;
if Pos(';' + Uppercase(instlPath) + '\;', ';' + Uppercase(Paths) + ';') > 0 then exit;
{ Append App Install Path to the end of the path variable }
Log(Format('Right(Paths, 1): [%s]', [Paths[length(Paths)]]));
if Paths[length(Paths)] = ';' then
Paths := Paths + instlPath + ';' { don't double up ';' in env(PATH) }
else
Paths := Paths + ';' + instlPath + ';' ;
end;
{ Overwrite (or create if missing) path environment variable }
if RegWriteStringValue(HKEY_CURRENT_USER, EnvironmentKey, 'Path', Paths)
then Log(Format('The [%s] added to PATH: [%s]', [instlPath, Paths]))
else Log(Format('Error while adding the [%s] to PATH: [%s]', [instlPath, Paths]));
end;
以及EnvRemovePath()
的更新版本:
procedure EnvRemovePath(instlPath: string);
var
Paths: string;
P, Offset, DelimLen: Integer;
begin
{ Skip if registry entry not exists }
if not RegQueryStringValue(HKEY_CURRENT_USER, EnvironmentKey, 'Path', Paths) then
exit;
{ Skip if string not found in path }
DelimLen := 1; { Length(';') }
P := Pos(';' + Uppercase(instlPath) + ';', ';' + Uppercase(Paths) + ';');
if P = 0 then
begin
{ perhaps instlPath lives in Paths, but terminated by '\;' }
DelimLen := 2; { Length('\;') }
P := Pos(';' + Uppercase(instlPath) + '\;', ';' + Uppercase(Paths) + ';');
if P = 0 then exit;
end;
{ Decide where to start string subset in Delete() operation. }
if P = 1 then
Offset := 0
else
Offset := 1;
{ Update path variable }
Delete(Paths, P - Offset, Length(instlPath) + DelimLen);
{ Overwrite path environment variable }
if RegWriteStringValue(HKEY_CURRENT_USER, EnvironmentKey, 'Path', Paths)
then Log(Format('The [%s] removed from PATH: [%s]', [instlPath, Paths]))
else Log(Format('Error while removing the [%s] from PATH: [%s]', [instlPath, Paths]));
end;
在嘗試添加HKEY_CURRENT_USER的路徑時,@ mghie的答案有些麻煩。 這個解決方案對我有用,所以我想我可能會和其他人分享。
我不確定為什么頂級解決方案需要添加半冒號,因為如果找到子串,則inno設置中的“pos”函數應返回非零,並且RegQueryStringValue將返回存儲在該路徑變量中的所有內容,包括所有分號。 也許有人可以向我解釋為什么這是必要的。
無論如何,如果您使用向導設置路徑進行安裝,這應該是一個很好的解決方案。 我不確定這對其他形式的安裝是否安全。
[Registry]
Root: HKCU; Subkey: "Environment"; \
ValueType: string; ValueName: "Path"; ValueData: "{olddata}{app};"; \
Check: NeedsAddPath()
[Code]
function NeedsAddPath(): boolean;
var
OrigPath: String;
begin
if not RegQueryStringValue(HKEY_CURRENT_USER,
'Environment',
'Path', OrigPath)
then begin
Result := True;
exit;
end;
Result := (Pos(WizardDirValue(), OrigPath) = 0);
end;
如果您可以使用外部 DLL, PathMgr.dll也可以是一個選項。
有一個示例 .iss 腳本演示了如何在 Inno Setup 6 或更高版本中使用 DLL。
PathMgr.dll 受 LPGL 許可保護。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.