Inno Setup-安装前停止运行中的程序

前言

Inno Setup打包的程序在运行的时候会监测程序是否正在运行,会提示终止运行的程序,但是不知什么原因,自带的终止功能有时不能正常终止,所以这里直接添加了检测代码来自行终止。

检测代码

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[Code]  
// 自定义函数,判断软件是否运行,参数为需要判断的软件的exe名称
function CheckSoftRun(strExeName: String): Boolean;
// 变量定义
var ErrorCode: Integer;
var strCmdKill: String; // 终止软件命令
begin
strCmdKill := Format('/c taskkill /f /t /im %s', [strExeName]);
// 终止程序
ShellExec('open', ExpandConstant('{cmd}'), strCmdKill, '', SW_HIDE, ewNoWait, ErrorCode);
Result:= true;// 继续安装
end;

// 开始页下一步时判断软件是否运行
function NextButtonClick(CurPageID: Integer): Boolean;
begin
if 1=CurPageID then begin
Result := CheckSoftRun('{#MyAppExeName}');
end
else begin
Result:= true;
end;
end;

// 卸载时关闭软件
function InitializeUninstall(): Boolean;
begin
Result := CheckSoftRun('{#MyAppExeName}');
end;

这里我全局定义了程序的exe名称

1
#define MyAppExeName "我的程序.exe"

注意

一定要保证安装的名称不要和程序本身的名称一样,否则会把安装的程序也关闭掉。

与C#程序结合的问题

会出问题的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
try
{
using (Process process = new Process())
{
process.StartInfo.FileName = filepath;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.Start();
}
}
catch (Exception)
{
}
Environment.Exit(0);

如果我们用上面的方法来打开安装包的时候,有时正常,

有时候会出现这么一种情况

安装包打开的一瞬间,程序和安装包的程序都退出了。

这是因为process.Start()创建的进程为子进程,当父进程死后,子进程就会成为父进程,因为安装包会关闭源程序的父子进程,如果在安装包启动后源程序还没退出,就会导致上面的情况。

建议使用下面的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
try
{
using (var managementClass = new ManagementClass("Win32_Process"))
{
var processInfo = new ManagementClass("Win32_ProcessStartup");
processInfo.Properties["CreateFlags"].Value = 0x00000008;

var inParameters = managementClass.GetMethodParameters("Create");
inParameters["CommandLine"] = filepath;
inParameters["ProcessStartupInformation"] = processInfo;

var result = managementClass.InvokeMethod("Create", inParameters, null);
if ((result != null) && ((uint)result.Properties["ReturnValue"].Value != 0))
{
Console.WriteLine("Process ID: {0}", result.Properties["ProcessId"].Value);
}
}
}
catch (Exception)
{
}
Environment.Exit(0);

所以解决方法有两种

  1. 用新的进程非子进程打开安装程序。
  2. 源程序中添加退出代码,而打包脚本中去掉关闭运行中程序的逻辑。

其实,我更推荐使用第2种方式,因为关闭程序是自己的事情,自己的事情自己做,才不会出问题。

taskkill

1
taskkill [/s <computer> [/u [<domain>\]<username> [/p [<password>]]]] {[/fi <filter>] [...] [/pid <processID> | /im <imagename>]} [/f] [/t]

参数

参数 说明
/s <computer> 指定远程计算机的名称或 IP 地址, (不要使用反斜杠) 。 默认为本地计算机。
/u <domain>\<username> 使用由或 <domain>\<username> 指定 <username> 的用户的帐户权限运行该命令。
只有在同时指定了/s参数时,才能指定/u参数。 默认值是当前登录到发出命令的计算机的用户的权限。
/p <password> 指定在 /u 参数中指定的用户帐户的密码。
/fi <filter> 应用筛选器以选择一组任务。 可以使用多个筛选器,也可以使用通配符 (*) 来指定所有任务或映像名称。
/pid <processID> 指定要终止的进程的进程 ID。
/im <imagename> 指定要终止的进程的映像名称。 使用通配符 (*) 指定所有映像名称。
/f 指定强制结束进程。 对于远程进程,此参数将被忽略。所有远程进程都被强制结束。
/t 结束指定进程以及由该进程启动的任何子进程。