簡體   English   中英

運行 Inno Setup Installer 時如何修改 PATH 環境變量?

[英]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 文檔所述,有一種方法可以將元素附加到這些元素:

對於stringexpandszmultisz類型的值,您可以在此參數中使用名為{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;

參考: RegQueryStringValueRegWriteStringValue

現在在主 .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 變量”的安裝步驟。

Inno Setup - 添加到 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM