在Inno Setup中[Run]程序无法识别[不可用]环境变量

时间:2014-02-11 16:59:32

标签: environment-variables inno-setup

我有一个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]部分?

3 个答案:

答案 0 :(得分:7)

为执行[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;

答案 1 :(得分:2)

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

runasoriginaluser flag [Run]任务对runasoriginaluser任务不起作用(postinstall flag隐含的内容)。即该变量不会传播到运行的应用程序,该应用程序使用常见的"运行我的程序" 复选框"已完成"页。

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

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

这样的中间过程很容易成为[Run] Filename: "{cmd}"; Parameters: "/C set MYVAR=MyValue & ""{app}\MyProg.exe"""; \ Description: "Run My Program"; Flags: postinstall runhidden 及其set command

cmd.exe

runhidden flag隐藏aobj = new ByteArray(); aobj.writeObject(Obj); //Obj is a usual as3 Object {param1:123,......} 控制台窗口,而不是应用程序(假设它是GUI应用程序)。如果它是控制台应用程序,请使用start在自己的(可见)控制台窗口中启动它。

答案 2 :(得分:1)

经过以下一些修改,效果很好:

[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 ///////////////////////////////////