Inno Setup - 卸载程序时从PATH环境变量中删除路径

时间:2016-02-15 13:18:35

标签: windows environment-variables installer inno-setup

我写了一个Inno安装脚本,安装程序并更新 PATH环境变量,其中包含安装程序的目录。

我想更新PATH环境变量,以恢复其先前的安装状态。

安装程序运行时,用户会选择安装路径。

这是使用How do I modify the PATH environment variable when running an Inno Setup Installer?

代码的脚本
[Setup]
AppName=Pandoc_x64
AppVersion=1.16.0.2
AppPublisher=Hitachi Systems CBT
DefaultDirName={pf64}\pandoc
UninstallDisplayName=Pandoc_x64
DisableDirPage=no
UninstallFilesDir={app}\uninstall

[Files]
Source: "pandoc.exe"; DestDir: "{app}";
Source: "pandoc-citeproc.exe"; DestDir: "{app}";


[Setup]
ChangesEnvironment=yes

[Registry]
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; \
    ValueType: expandsz; ValueName: "PATH"; ValueData: "{olddata};{app}"; \
    Check: NeedsAddPath('{app}')
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment";  ValueName: "PATH"; ValueData: "{app}"; Flags: uninsdeletevalue

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

看一下代码,可以注意以下说明:

Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment";  ValueName: "PATH"; ValueData: "{app}"; Flags: uninsdeletevalue

我使用了那个指令(我认为)适用于我的例子,阅读Inno Setup. How to uninstall registry value?

使用uninsdeletevalue应该在卸载程序时删除该值,事实上,当我运行卸载程序时,整个PATH变量被删除,但我需要恢复{ {1}}环境变量到先前的安装值。 我认为在运行安装程序之前可以读取它的价值,但我不知道如何在卸载阶段使用它。

有人可以用代码示例帮我吗?

3 个答案:

答案 0 :(得分:6)

当仅使用[Registry]部分条目卸载时,您无法让Inno Setup记住安装时的值并将其恢复。

虽然您可以对其进行编码,但无论如何它都不是好方法,因为安装后PATH可能会发生变化,您将放弃任何此类更改。

您必须在PATH搜索您的路径并仅删除路径。

const
  EnvironmentKey = 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment';

procedure RemovePath(Path: string);
var
  Paths: string;
  P: Integer;
begin
  if not RegQueryStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths) then
  begin
    Log('PATH not found');
  end
    else
  begin
    Log(Format('PATH is [%s]', [Paths]));

    P := Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';');
    if P = 0 then
    begin
      Log(Format('Path [%s] not found in PATH', [Path]));
    end
      else
    begin
      if P > 1 then P := P - 1;
      Delete(Paths, P, Length(Path) + 1);
      Log(Format('Path [%s] removed from PATH => [%s]', [Path, Paths]));

      if RegWriteStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths) then
      begin
        Log('PATH written');
      end
        else
      begin
        Log('Error writing PATH');
      end;
    end;
  end;
end;

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
  if CurUninstallStep = usUninstall then
  begin
    RemovePath(ExpandConstant('{app}'));
  end;
end;

答案 1 :(得分:2)

如果已经安装了以前的版本,包含错误的卸载例程,然后使用新的安装程序进行了更新,则问题似乎仍然存在。无论如何,路径都被删除了,即使注册表部分已经没有Flags了。所以我的任务是编写一个新的安装程序,在更新有故障的版本时确保卸载后路径仍然存在。

我最接近Martin Prikryl的方法,但是日志显示我需要在卸载开始时保存Path(usUninstall)并在最后重写它(usPostUninstall),否则它不会棒。此外,在处理usPostUninstall Step时,环境已经在没有Path的情况下刷新了错误,所以我添加了一个手动环境刷新,我从another post获取。

这是对我有用的最终结果:

var
  Paths: string;

const
  EnvironmentKey = 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment';
  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 SaveOldPath();
begin
  if not RegQueryStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths) then
  begin
    Log('PATH not found');
  end else begin
    Log(Format('Old Path saved as [%s]', [Paths]));
  end;
end;

procedure RemovePath(Path: string);
var
  P: Integer;
begin
  Log(Format('Prepare to remove from Old PATH [%s]', [Paths]));

  P := Pos(';' + Uppercase(Path) + ';', ';' + Uppercase(Paths) + ';');
  if P = 0 then
  begin
    Log(Format('Path [%s] not found in PATH', [Path]));
  end
    else
  begin
    Delete(Paths, P - 1, Length(Path) + 1);
    Log(Format('Path [%s] removed from PATH => [%s]', [Path, Paths]));

    if RegWriteExpandStringValue(HKEY_LOCAL_MACHINE, EnvironmentKey, 'Path', Paths) then
    begin
      Log('PATH written');
    end
      else
    begin
      Log('Error writing PATH');
    end;
  end;
end;

procedure RefreshEnvironment;
var
  S: AnsiString;
  MsgResult: DWORD;
begin
  S := 'Environment';
  SendTextMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
    PAnsiChar(S), SMTO_ABORTIFHUNG, 5000, MsgResult);
end;

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
  if CurUninstallStep =  usUninstall then
  begin
    SaveOldPath();
  end;
  if CurUninstallStep = usPostUninstall then
  begin
    RemovePath(ExpandConstant('{app}'));
    RefreshEnvironment();
  end;
end;

答案 2 :(得分:1)

这些从PATH中删除变量的解决方案帮助很大,但对于我来说,Delete(Paths, P - 1, Length(Path) + 1);当我的安装程序的路径在开头时出现了问题。然后删除功能将无效。

我的简单修改是Delete(Paths, P, Length(Path) + 1);