如何使用Inno Setup检查Windows 2008 R2 64位操作系统上是否正在运行进程?

16
我已阅读了以下帖子,我的代码与其完全相同,但不起作用:
Inno Setup 检查正在运行的进程 我复制了来自http://www.vincenzo.net/isxkb/index.php?title=PSVince 的示例,但即使我像这样更改代码,示例仍然无法工作:
[Code]
function IsModuleLoaded(modulename: AnsiString):  Boolean;
external 'IsModuleLoaded@files:psvince.dll stdcall';

无论程序是否运行,该代码始终返回false(即程序未运行)。 在Windows 2008 R2和Windows 7上进行过测试。

实际上,我想检查tomcat5.exe是否正在运行。所以我认为我不能使用AppMutex

我也看到过https://code.google.com/p/psvince/source/detail?r=5
但是我找不到有关该DLL兼容性的任何事实。

完整的代码:

[Files]
Source: psvince.dll; Flags: dontcopy

[Code]
function IsModuleLoaded(modulename: AnsiString ):  Boolean;
external 'IsModuleLoaded@files:psvince.dll stdcall';

function InitializeSetup(): Boolean;
begin
  if(IsModuleLoaded( 'notepad.exe' )) then
    begin
      MsgBox('Running', mbInformation, MB_OK);
      Result := false;
    end
  else
    begin
      MsgBox('Not running', mbInformation, MB_OK);
      Result := true;
    end
end;

1
也在Inno Setup新闻组上提问过。 - Deanna
是的,没错。感谢您在新闻组中的回复。我已经看到了psvince的r5-commit,但是我找不到关于兼容性的事实。 - Christian Kuetbach
Deanna,我尝试了psvince的r5-commit中的Bugfix。实际上,我使用了“IsModuleLoaded(“app.exe”)或IsModuleLoaded2(“app.exe”)”,看起来它可以工作。如果您将其发布为答案,我会接受它。 - Christian Kuetbach
5个回答

57

您可以使用WMI和Win32_Process来实现。

尝试将此函数添加到您的Inno Setup脚本中。

function IsAppRunning(const FileName : string): Boolean;
var
    FSWbemLocator: Variant;
    FWMIService   : Variant;
    FWbemObjectSet: Variant;
begin
    Result := false;
    FSWbemLocator := CreateOleObject('WBEMScripting.SWBEMLocator');
    FWMIService := FSWbemLocator.ConnectServer('', 'root\CIMV2', '', '');
    FWbemObjectSet :=
      FWMIService.ExecQuery(
        Format('SELECT Name FROM Win32_Process Where Name="%s"', [FileName]));
    Result := (FWbemObjectSet.Count > 0);
    FWbemObjectSet := Unassigned;
    FWMIService := Unassigned;
    FSWbemLocator := Unassigned;
end;

是的,这对我有效!它还可以在多用户场景下工作(例如在运行安装程序的服务器上,有更多的用户使用RDP登录)- 在这种情况下不需要“以管理员身份运行”! - UnDiUdin
要使用RRUZ发布的代码,请在安装程序脚本中添加一个代码部分。添加一个名为InitializeSetup的函数,该函数调用IsAppRunning("appname.exe")。当安装程序启动时,将调用InitializeSetup。 请访问我的网站,了解如何使用此代码的示例:http://www.andrew-seaford.co.uk/check-program-running-installing-inno/ - Andrew Seaford
你让我的一天变得美好!谢谢 :) - Jay Patel
我遇到了运行时错误。由于应用正在调度输入同步调用,因此无法进行呼出调用。在某些设备上会出现此问题。 - cool_php
这种方法已经使用了很多年,但今天我收到了一个错误报告:SWbemLocator: The specified service does not exist as an installed service - Edwin Yip

9

我没有足够的声望点数来对RRUZ的优秀回答添加评论,因此我将在此处添加。 一定要捕获异常,否则对于无法访问服务的用户,安装程序将失败。

try
      FSWbemLocator := CreateOleObject('WBEMScripting.SWBEMLocator');
      FWMIService := FSWbemLocator.ConnectServer('', 'root\CIMV2', '', '');
      FWbemObjectSet := FWMIService.ExecQuery(Format('SELECT Name FROM Win32_Process Where Name="%s"',[FileName]));
      Result := (FWbemObjectSet.Count > 0);
except
end;

1
吞咽异常并不是一个好的实践,因为你不会知道任何函数调用失败的原因。例如,我几乎总是让异常被抛出,让它们的调用者来处理。 - TLama
1
当然,开发人员需要捕获异常并采取相应措施。我只是指出这些方法可能会失败,这将导致安装程序退出而无法完成其工作。 - Jedao

2
这个问题有一个更简单的解决方案;使用RRUZ建议的代码依赖于您知道安装路径,如果您在安装程序初始化时运行代码,则不知道这个路径。
最好的解决方案是使用FindWindowByClassName。它有一个小先决条件,您必须有一个始终打开的主窗体,但如果您有多种可能打开的表单,则可以运行多个检查。当然,您需要尽可能使类名独特!
示例函数:
function IsAppRunning(): Boolean;
begin                                                                
  Result := (FindWindowByClassName( '{#AppWndClassName}') <> 0) or (FindWindowByClassName( '{#AltAppWndClassName}') <> 0);
end;

# 预编译参考是在安装脚本的早期定义的...
#define AppWndClassName "TMySplashScreen"
#define AltAppWndClassName "TMyMainForm"

在代码部分,你需要按照以下方式调用它:
function InitializeUninstall(): Boolean;
begin
  // check if application is running
  if IsAppRunning() then
  begin
    MsgBox( 'An Instance of MyFantasticApp is already running. - Please close it and run the uninstall again.', mbError, MB_OK );
    Result := false;
  end
  else 
    Result := true;
End;

如果您需要比这更复杂的内容,那么您需要研究互斥锁,但上述代码的美妙之处在于它简单、快速,并且只要您有相对独特的类名,就可以和其他任何东西一样好。
(尽管诚然,如果您正在运行多用户系统,那么如果窗口在另一个用户的会话中,这可能不会找到该窗口。但正如我所说,对于大多数简单情况,这将是可行的。)

我认为OP在这里不能使用互斥锁,因为tomcat5.exe听起来不像是自制应用程序。但是如果有扩展应用程序的方法,那么互斥锁是最好的选择!在InnoSetup中,您可以使用CheckForMutexes,这比您的代码更简单。[+1] - TLama

1
一个简单的解决方案是尝试删除exe文件。我假设您无论如何都要替换或卸载它。如果该文件存在且删除失败,则可能正在运行。

不确定我是该笑还是该哭。这不是解决方案。 - cool_php

1

谢谢,我会测试一下。但是安装程序和进程都是32位的。只有操作系统是64位的。 - Christian Kuetbach
是否可以直接将代码嵌入到安装程序中(而无需部署另一个可执行文件)? - Christian Kuetbach
这取决于一个不太简单的单元,https://github.com/lextm/processviewer/blob/master/uDGProcessList.pas,因此我认为它不容易移植到Pascal脚本。@RRUZ发布的WMI方法可能更简单。 - Lex Li

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接