Inno Setup - 卸载程序时从 PATH 环境变量中删除路径
Inno Setup - Remove path from PATH environment variable while uninstalling a program
我写了一个 Inno Setup 脚本来安装程序并更新
PATH
带有程序安装目录的环境变量。
我想更新 PATH
环境变量,以恢复其之前的安装状态。
安装路径由用户选择,安装程序为运行ning。
这是脚本,它使用来自 How do I modify the PATH environment variable when running an Inno Setup Installer?
的代码
[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
变量都删除了,但是我需要将 PATH
环境变量恢复到之前的安装值。
我认为可以在 运行 安装程序之前读取它的值,但我不知道如何在卸载阶段使用它。
有人可以帮我举个代码示例吗?
当仅使用 [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;
如果之前的版本已经安装,包含错误的卸载例程,然后使用新的安装程序更新,问题似乎仍然存在。无论如何,路径都被删除了,即使注册表部分不再有标志。所以我的任务是编写一个新的安装程序,在更新有问题的版本时确保路径在卸载后仍然存在。
我最接近 Martin Prikryl 的方法,但日志记录显示我需要在卸载开始时保存路径 (usUninstall) 并在结束时重写它 (usPostUninstall),否则它不会坚持。此外,在处理 usPostUninstall 步骤时,环境已经在没有 Path 的情况下进行了错误刷新,因此我添加了从 another post 获取的手动环境刷新。
这是对我有用的最终结果:
var
Paths: string;
const
EnvironmentKey = 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment';
SMTO_ABORTIFHUNG = 2;
WM_WININICHANGE = [=10=]1A;
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;
这些从 PATH 中删除变量的解决方案很有帮助,但对我来说,当我的安装程序路径位于开头时,Delete(Paths, P - 1, Length(Path) + 1);
中出现了问题。那么删除功能将不起作用。
我的简单修改对我有用 Delete(Paths, P, Length(Path) + 1);
我写了一个 Inno Setup 脚本来安装程序并更新
PATH
带有程序安装目录的环境变量。
我想更新 PATH
环境变量,以恢复其之前的安装状态。
安装路径由用户选择,安装程序为运行ning。
这是脚本,它使用来自 How do I modify the PATH environment variable when running an Inno Setup Installer?
的代码[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
变量都删除了,但是我需要将 PATH
环境变量恢复到之前的安装值。
我认为可以在 运行 安装程序之前读取它的值,但我不知道如何在卸载阶段使用它。
有人可以帮我举个代码示例吗?
当仅使用 [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;
如果之前的版本已经安装,包含错误的卸载例程,然后使用新的安装程序更新,问题似乎仍然存在。无论如何,路径都被删除了,即使注册表部分不再有标志。所以我的任务是编写一个新的安装程序,在更新有问题的版本时确保路径在卸载后仍然存在。
我最接近 Martin Prikryl 的方法,但日志记录显示我需要在卸载开始时保存路径 (usUninstall) 并在结束时重写它 (usPostUninstall),否则它不会坚持。此外,在处理 usPostUninstall 步骤时,环境已经在没有 Path 的情况下进行了错误刷新,因此我添加了从 another post 获取的手动环境刷新。
这是对我有用的最终结果:
var
Paths: string;
const
EnvironmentKey = 'SYSTEM\CurrentControlSet\Control\Session Manager\Environment';
SMTO_ABORTIFHUNG = 2;
WM_WININICHANGE = [=10=]1A;
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;
这些从 PATH 中删除变量的解决方案很有帮助,但对我来说,当我的安装程序路径位于开头时,Delete(Paths, P - 1, Length(Path) + 1);
中出现了问题。那么删除功能将不起作用。
我的简单修改对我有用 Delete(Paths, P, Length(Path) + 1);