【问题标题】:Environment variable not recognized [not available] for [Run] programs in Inno SetupInno Setup 中的 [Run] 程序无法识别环境变量 [不可用]
【发布时间】:2014-02-11 16:59:32
【问题描述】:

我有一个 license.exe 文件,我最后在我的设置代码中调用它,

代码需要设置环境变量才能正常工作,

代码如下:

[Registry]
; set PATH
Root: HKLM; \
    Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; \
    ValueType: string; ValueName: "PATH"; ValueData: "{app}"

[Setup]
; Tell Windows Explorer to reload the environment
ChangesEnvironment=yes

[Run]
Filename: "{app}\temp\installation_files\license.exe";

这里代码执行,但没有找到正确的路径。

当我检查系统环境变量时,它设置正确,

当我之后手动运行license.exe 代码时,它可以正常工作并看到环境变量。

谁能告诉我如何解决这个问题?

或者如何延迟[Run]部分直到系统识别环境变量?

【问题讨论】:

    标签: environment-variables inno-setup


    【解决方案1】:

    为执行[Run] 部分中的条目而创建的进程会继承其父进程的环境块,即安装程序本身。因此,您必须将环境变量设置为安装程序并让它继承到您执行的应用程序。如何做到这一点在下面的脚本中显示:

    [Run]
    Filename: "{app}\temp\installation_files\license.exe"; BeforeInstall: SetEnvPath
    
    [Code]
    #ifdef UNICODE
      #define AW "W"
    #else
      #define AW "A"
    #endif
    function SetEnvironmentVariable(lpName: string; lpValue: string): BOOL;
      external 'SetEnvironmentVariable{#AW}@kernel32.dll stdcall';
     
    procedure SetEnvPath;
    begin
      if not SetEnvironmentVariable('PATH', ExpandConstant('{app}')) then
        MsgBox(SysErrorMessage(DLLGetLastError), mbError, MB_OK);
    end;
    

    通知系统其余部分变量更改的先前答案:

    正如@Jerry 在他的评论中指出的那样,在处理[Run] 部分之后会执行有关环境更改的通知。其实就是安装程序执行的one of the last things

    因此,要在处理 [Run] 部分之前通知系统环境变化,您需要有一个解决方法。我将 Inno Setup 代码中的 RefreshEnvironment 过程重写为脚本。如果您将ChangesEnvironment 指令设置为yes,则它与执行的功能相同。

    在以下脚本中,我删除了ChangesEnvironment 指令,并从您的注册表项的AfterInstall 参数函数中添加了RefreshEnvironment 过程的执行:

    [Registry]
    Root: HKLM; \
        Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; \
        ValueType: string; ValueName: "PATH"; ValueData: "{app}"; \
        AfterInstall: RefreshEnvironment;
    
    [Run]
    Filename: "{app}\temp\installation_files\license.exe";
    
    [Code]
    const
      SMTO_ABORTIFHUNG = 2;
      WM_WININICHANGE = $001A;
      WM_SETTINGCHANGE = WM_WININICHANGE;
    
    type
      WPARAM = UINT_PTR;
      LPARAM = INT_PTR;
      LRESULT = INT_PTR;
    
    function SendTextMessageTimeout(hWnd: HWND; Msg: UINT;
      wParam: WPARAM; lParam: PAnsiChar; fuFlags: UINT;
      uTimeout: UINT; out lpdwResult: DWORD): LRESULT;
      external 'SendMessageTimeoutA@user32.dll stdcall';  
    
    procedure RefreshEnvironment;
    var
      S: AnsiString;
      MsgResult: DWORD;
    begin
      S := 'Environment';
      SendTextMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
        PAnsiChar(S), SMTO_ABORTIFHUNG, 5000, MsgResult);
    end;
    

    【讨论】:

      【解决方案2】:

      TLama's answer 中的SetEnvironmentVariable 的解决方案在许多情况下都是正确的。

      但它不适用于带有runasoriginaluser flag[Run] 任务(postinstall flag 暗示的内容)。 IE。该变量不会传播到使用“已完成”页面上的常见“运行我的程序”复选框运行的应用程序。

      原因是runasoriginaluser 的任务是由 Inno Setup 安装程序的未提升隐藏父进程执行的。 SetEnvironmentVariable 将更改安装程序的环境,但不会更改其父进程。不幸的是,安装程序的父进程无法控制(imo)。

      作为一种解决方法,要为runasoriginaluser 任务设置变量,您必须在安装程序父进程和任务之间注入一个中间进程,并让中间进程设置变量。

      这样的中间过程很容易就是cmd.exe及其set command

      [Run]
      Filename: "{cmd}"; Parameters: "/C set PATH=%PATH%;{app} & ""{app}\MyProg.exe"""; \
          Description: "Run My Program"; Flags: postinstall runhidden
      

      runhidden flag 隐藏了cmd.exe 控制台窗口,而不是应用程序(假设它是一个 GUI 应用程序)。如果它是一个控制台应用程序并且您希望输出可见,请删除 runhidden 标志。或者,您可以使用start 命令在其自己的控制台窗口中启动应用程序。

      【讨论】:

        【解决方案3】:

        经过以下一些修改,完美运行:

        [Run]
        Filename: "{app}\{#MyAppExeName}"; BeforeInstall: AppendToPathAndRefresh;Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}";Flags: nowait postinstall shellexec skipifsilent
        
        [Code]
        ////////////////////////////////////////////////////////////
        const
        SMTO_ABORTIFHUNG = 2;
        WM_WININICHANGE = $001A;
        
        type
        WPARAM = UINT_PTR;
        LPARAM = INT_PTR;
        LRESULT = INT_PTR;
        
        function SendTextMessageTimeout(hWnd: HWND; Msg: UINT;
        wParam: WPARAM; lParam: PAnsiChar; fuFlags: UINT;
        uTimeout: UINT; out lpdwResult: DWORD): LRESULT;
        external 'SendMessageTimeoutA@user32.dll stdcall';  
        
        procedure RefreshEnvironment;
        var
        S: AnsiString;
        MsgResult: DWORD;
        begin
        S := 'Environment';
        SendTextMessageTimeout(HWND_BROADCAST, WM_WININICHANGE, 0,
            PAnsiChar(S), SMTO_ABORTIFHUNG, 5000, MsgResult);
        end;
        ///PATH ENVINRONMENT//////////////////////////////////////////
        function Replace(Dest, SubStr, Str: string): string;
        var
        Position: Integer;
        Ok: Integer;
        begin
        Ok := 1;
        while Ok > 0 do
        begin
            Position:=Pos(SubStr, Dest);
            if Position > 0 then
            begin
            Delete(Dest, Position, Length(SubStr));
            Insert(Str, Dest, Position);
            end else
            Ok := 0;
        end;
        Result:=Dest;
        end;
        
        procedure AppendToPath();
        var
        V: string;
        Str: string;
        begin
        RegQueryStringValue(HKLM, 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', 'Path', V)
        Str := ExpandConstant('{app}\libav');
        V := Replace(V, Str, '');
        V := V + ';' + Str;
        V := Replace(V,';;',';');
        RegWriteStringValue(HKLM, 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', 'Path', V)
        
        // MsgBox(V, mbInformation, MB_OK); 
        end;
        
        procedure RemoveFromPath();
        var
        V: string;
        Str: string;
        begin
        RegQueryStringValue(HKLM, 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', 'Path', V)
        Str := ExpandConstant('{app}\dlls');
        V := Replace(V, Str, '');
        V := Replace(V,';;',';');
        RegWriteStringValue(HKLM, 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', 'Path', V)
        //MsgBox(V, mbInformation, MB_OK);
        end;
        
        procedure AppendToPathAndRefresh;
        begin
        AppendToPath;
        RefreshEnvironment;
        end;
        
        
        procedure DeinitializeUninstall();
        begin
        RemoveFromPath();
        end;
        ///END OF PATH ENVIRONMENT ///////////////////////////////////
        

        【讨论】:

        • 如果我要在 WIX 等其他安装程序中执行此操作,该怎么做?我可以使用上述代码在 C++ 中创建 CA 吗?我会调用哪个函数?我不是 C++ 人,如果有人能解释正在发生的事情将会很有用。谢谢
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-01-01
        • 2016-12-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多