目录
inno setup 安装时判断并关闭已经运行的应用程序exe
inno setup 安装时判断并关闭已经运行的应用程序exe
就很简单的一个功能,Inno setup制作的安装包,在安装时候,判断是否之前有本程序的实力在运行,运行的话需要终止他,貌似很简单,折腾了我1小时。
网上找的好多方案,不理想不理想!我就给大家列举下这些我认为不理想的原因!
网上方案1: 在Code段用FindWindowByWindowName、FindWindowByClassName……对不起我的客户端的标题是会变的啊!我的客户端主窗口是对话框啊,窗口类名都一样(#32770)啊!
网上方案2: 用个叫IsTask.dll的外部扩展,Inno setup没,自己网上下,说白了就是个用c++写的一个dll,自己也能写,回头用Code pascal语言调用……,RunTask、KillTask用的这两个函数……,蛋疼的事情发生了,本来秒开的安装包,要卡个5秒以上在打开……怀疑是IsTask.dll破吧,自己写个,c++中char对应pascal中PAnsiChar,函数导出调用方案方法注意stdcall,弄好,还是通过进程名加载dll……结果……卡……5秒以上……原来是inno setup自己low……升级inno setup到最新5.5.9,一样有问题。
网上方案3: 找啊找老半天,发现SETUP段里可以添加一个APPMUTEX,不错,我的exe里为了不能多开也弄了个非匿名的mutex,马上加上,点开安装……我了个去,结果只是提示你有你的exe在运行,自己去关闭吧……啥体验啊……
我自己的方案,很简单通过checkmutex判断是否在运行,是的话通过ShellExec 打开cmd用taskkill杀死进程……完美解决,也不会加载dll卡的5秒,体验棒棒哒。
//安装时判断并关闭已经运行的应用exe
// 定义一个整数类型的变量 ErrorCode,用于存储执行 ShellExec 命令后的错误代码
var ErrorCode: Integer;
// 定义 InitializeSetup 函数,该函数返回一个布尔值
function InitializeSetup(): Boolean;
begin
if CheckForMutexes('MyAppMutex') then
begin
if MsgBox('检测到你的DocWatch.exe正在运行中!'#13'点击 "是" 关闭Doc.Watch.exe继续安装;'#13'点击 "否" 退出安装程序!', mbConfirmation, MB_YESNO) = IDYES then
begin
ShellExec('open', ExpandConstant('{cmd}'), '/c taskkill /f /t /im DocWatch.exe', '', SW_HIDE, ewNoWait, ErrorCode);
Result:= true;
end
else
Result:= false;
end
else
Result:=true;
end;
Inno Setup打包exe卸载旧版本
// 获取旧版本的卸载字符串
function GetUninstallString(): String;
var
sUnInstPath: String;
sUnInstallString: String;
begin
// 构建注册表路径
sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{{#MyUid}}_is1');
// 初始化卸载字符串为空
sUnInstallString := '';
// 从 HKLM 注册表项中获取 UninstallString 值
if not RegQueryStringValue(HKLM, sUnInstPath, 'UninstallString', sUnInstallString) then
// 如果在 HKLM 中未找到,尝试从 HKCU 中获取
RegQueryStringValue(HKCU, sUnInstPath, 'UninstallString', sUnInstallString);
// 返回获取到的卸载字符串
Result := sUnInstallString;
end;
// 判断是否需要升级(是否存在旧版本)
function IsUpgrade(): Boolean;
begin
// 如果获取到的卸载字符串不为空,则存在旧版本
Result := (GetUninstallString() <> '');
end;
// 卸载旧版本的软件
function UnInstallOldVersion(): Integer;
var
sUnInstallString: String;
iResultCode: Integer;
begin
// 默认返回值为 0
Result := 0;
// 获取旧版本的卸载字符串
sUnInstallString := GetUninstallString();
// 如果卸载字符串不为空
if sUnInstallString <> '' then
begin
// 去除卸载字符串中的引号
sUnInstallString := RemoveQuotes(sUnInstallString);
// 执行卸载命令,并等待执行完毕
if Exec(sUnInstallString, '/SILENT /NORESTART /SUPPRESSMSGBOXES', '', SW_HIDE, ewWaitUntilTerminated, iResultCode) then
Result := 3 // 卸载成功
else
Result := 2; // 卸载失败
end
else
Result := 1; // 卸载字符串为空
end;
// 当安装步骤变化时触发的事件处理函数
procedure CurStepChanged(CurStep: TSetupStep);
begin
// 当进入安装步骤时
if CurStep = ssInstall then
begin
// 检查是否需要升级(是否存在旧版本)
if IsUpgrade() then
begin
// 尝试卸载旧版本
UnInstallOldVersion();
end;
end;
end;
功能描述
- 比较版本号,如果版本号高于已安装的旧版本则卸载旧版本;
注意
- 只有AppId相同才会对比,AppId不同继续安装;
获取历史安装路径的代码如下:
//获取历史安装路径,Inno Setup保存的一些信息可自己在注册表中查看
//64位会映射到:计算机\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall
function GetInstallString(): String;
var
InstallPath: String;
begin
InstallPath := '{#MyAppInstallPath}';
if RegValueExists(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{#MyUid}}_is1', 'Inno Setup: App Path') then
begin
RegQueryStringValue(HKLM, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{#MyUid}}_is1', 'Inno Setup: App Path', InstallPath)
end;
result := InstallPath;
end;
业务场景:有次客户反馈说,卸载应用程序没有卸载干净,安装目录下残留很多文件,特别是一些配置文件和数据库文件,涉及到一些数据安全机密。卸载程序应该把安装目录都要清除。操作如下:
卸载应用程序,删除整个安装目录
//卸载应用程序没有卸载干净,安装目录下残留很多文件,特别是一些配置文件和数据库文件,涉及到一些数据安全机密。卸载程序应该把安装目录都要清除。以下代码将卸载应用程序,并删除整个安装目录
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
var
InstallDir: String;
begin
// 获取安装目录
InstallDir := ExpandConstant('{app}');
case CurUninstallStep of
usUninstall:
begin
// 如果安装目录存在,则删除整个目录及其内容
if DirExists(InstallDir) then
DelTree(InstallDir, True, True, True);
// 删除安装根目录
RemoveDir(ExtractFileDir(InstallDir));
end;
end;
end;
综上完整的脚本代码如下所示:
; 脚本由 Inno Setup 脚本向导 生成!
; 有关创建 Inno Setup 脚本文件的详细资料请查阅帮助文档!
#define MyAppName "DocWatch"
//程序图标路径,相对于iss脚本的相对路径
#define MyAppIcon "Install\bin\Gmep.ico"
#define MyAppVersion "3.2"
#define MyAppPublisher "CEEC"
#define MyAppExeName "CADSetup.exe"
#define docWatch "DocWatch.exe"
#define InstallWindowsService "InstallWindowsService.exe"
//待打包的文件路径,相对于iss脚本的相对路径
#define MyAppExePath "Install"
//程序默认安装目录
#define MyAppInstallPath "D:\GEDI\DocWatch"
//程序uuid,点工具生成唯一的GUID
#define MyUid "{A626D5BF-BB90-471F-BA24-06F77DB4F9C4}"
[Setup]
; 注: AppId的值为单独标识该应用程序。
; 不要为其他安装程序使用相同的AppId值。
; (若要生成新的 GUID,可在菜单中点击 "工具|生成 GUID"。)
AppId={{#MyUid}}
//程序安装后的名字
AppName={#MyAppName}
//程序版本号
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
//默认安装目录
DefaultDirName={#MyAppInstallPath}
//使用已安装版本的目录安装,为yes则默认选择已有的目录且不能选择
UsePreviousAppDir=no
//不显示选择开始菜单文件夹 向导页面
DisableProgramGroupPage=yes
; 以下行取消注释,以在非管理安装模式下运行(仅为当前用户安装)。
;PrivilegesRequired=lowest
//以管理权限运行安装
PrivilegesRequired=admin
//安装包生成后所在文件夹和文件名
OutputDir=C:\Users\user\Desktop\DocWatch
OutputBaseFilename={#MyAppName} V{#MyAppVersion}
SetupIconFile=F:\bigDataResultUp\DocWatch\Install\bin\Gmep.ico
//选择压缩方法,lzma为7z
Compression=lzma
//启用固态压缩,详见文档
SolidCompression=yes
//互斥体
APPMUTEX=MyAppMutex
//安装和卸载程序现代外观
WizardStyle=modern
[Languages]
Name: "chinesesimp"; MessagesFile: "compiler:Default.isl"
[Files]
Source: "{#MyAppExePath}\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#MyAppExePath}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; 注意: 不要在任何共享系统文件上使用“Flags: ignoreversion”
; 安装前判断进程,dll文件放在inno的安装目录中
[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
[Run]
// [Run]- 用来指定在程序完成安装后,在安装程序显示最终对话框前要执行的一些程序
Filename: "{app}\bin\{#docWatch}"; Description: "{cm:LaunchProgram,{#StringChange(docWatch, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppExeName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
[UninstallRun]
Filename: "{cmd}"; Parameters: "/C ""taskkill /im DocWatch.exe /f /t"
[Code]
//安装时判断并关闭已经运行的应用exe
// 定义一个整数类型的变量 ErrorCode,用于存储执行 ShellExec 命令后的错误代码
var ErrorCode: Integer;
// 定义 InitializeSetup 函数,该函数返回一个布尔值
function InitializeSetup(): Boolean;
begin
if CheckForMutexes('MyAppMutex') then
begin
if MsgBox('检测到你的DocWatch.exe正在运行中!'#13'点击 "是" 关闭Doc.Watch.exe继续安装;'#13'点击 "否" 退出安装程序!', mbConfirmation, MB_YESNO) = IDYES then
begin
ShellExec('open', ExpandConstant('{cmd}'), '/c taskkill /f /t /im DocWatch.exe', '', SW_HIDE, ewNoWait, ErrorCode);
Result:= true;
end
else
Result:= false;
end
else
Result:=true;
end;
//卸载应用程序没有卸载干净,安装目录下残留很多文件,特别是一些配置文件和数据库文件,涉及到一些数据安全机密。卸载程序应该把安装目录都要清除。以下代码将卸载应用程序,并删除整个安装目录
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
var
InstallDir: String;
begin
// 获取安装目录
InstallDir := ExpandConstant('{app}');
case CurUninstallStep of
usUninstall:
begin
// 如果安装目录存在,则删除整个目录及其内容
if DirExists(InstallDir) then
DelTree(InstallDir, True, True, True);
// 删除安装根目录
RemoveDir(ExtractFileDir(InstallDir));
end;
end;
end;
// 获取旧版本的卸载字符串
function GetUninstallString(): String;
var
sUnInstPath: String;
sUnInstallString: String;
begin
// 构建注册表路径
sUnInstPath := ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{{#MyUid}}_is1');
// 初始化卸载字符串为空
sUnInstallString := '';
// 从 HKLM 注册表项中获取 UninstallString 值
if not RegQueryStringValue(HKLM, sUnInstPath, 'UninstallString', sUnInstallString) then
// 如果在 HKLM 中未找到,尝试从 HKCU 中获取
RegQueryStringValue(HKCU, sUnInstPath, 'UninstallString', sUnInstallString);
// 返回获取到的卸载字符串
Result := sUnInstallString;
end;
// 判断是否需要升级(是否存在旧版本)
function IsUpgrade(): Boolean;
begin
// 如果获取到的卸载字符串不为空,则存在旧版本
Result := (GetUninstallString() <> '');
end;
// 卸载旧版本的软件
function UnInstallOldVersion(): Integer;
var
sUnInstallString: String;
iResultCode: Integer;
begin
// 默认返回值为 0
Result := 0;
// 获取旧版本的卸载字符串
sUnInstallString := GetUninstallString();
// 如果卸载字符串不为空
if sUnInstallString <> '' then
begin
// 去除卸载字符串中的引号
sUnInstallString := RemoveQuotes(sUnInstallString);
// 执行卸载命令,并等待执行完毕
if Exec(sUnInstallString, '/SILENT /NORESTART /SUPPRESSMSGBOXES', '', SW_HIDE, ewWaitUntilTerminated, iResultCode) then
Result := 3 // 卸载成功
else
Result := 2; // 卸载失败
end
else
Result := 1; // 卸载字符串为空
end;
// 当安装步骤变化时触发的事件处理函数
procedure CurStepChanged(CurStep: TSetupStep);
begin
// 当进入安装步骤时
if CurStep = ssInstall then
begin
// 检查是否需要升级(是否存在旧版本)
if IsUpgrade() then
begin
// 尝试卸载旧版本
UnInstallOldVersion();
end;
end;
end;