在Windows上如何检查进程是否正在运行?

73

我正在尝试创建一个Python脚本,稍后将其作为服务运行。现在我想仅在iTunes运行时运行代码的特定部分。从一些研究中我了解到轮询整个命令列表,然后搜索该列表中的应用程序是很消耗资源的。

我发现,在基于UNIX的操作系统上,进程会创建一个锁文件来通知程序当前正在运行,此时我们可以使用 os.stat(锁文件的位置) 来检查文件是否存在以确定程序是否正在运行。

在Windows上是否有类似的锁文件呢?

如果没有,那么在Python中有哪些方法可以确定进程是否正在运行?

我正在使用Python 2.7和iTunes COM接口。


如果iTunes没有运行,COM接口会做什么? - Gabe
如果在Python中使用COM接口创建一个对象,COM接口会自动打开iTunes。 - nightf0x
2
说实话,是否创建锁定文件或PID文件取决于个别程序。并非所有的Linux/UNIX程序都会这样做。 - David Z
21个回答

105

在Linux或Windows中,您不能依赖于锁定文件。我建议遍历所有正在运行的程序。我真的不认为这会像你想象的那么“昂贵”。psutil是一个出色的跨平台Python模块,可以枚举系统上运行的所有程序。

import psutil    
"someProgram" in (p.name() for p in psutil.process_iter())

2
尝试运行此程序时,即使我以管理员身份运行它,某些进程也会被拒绝访问。我可能只需使用try和except来忽略这些情况,但这是需要注意的事项。 - trevorKirkby
7
现在看起来是BSD许可证。 - Anthony
1
警告:如果在执行psutil.Process(i).name时,psutil.get_pid_list()返回的进程已经退出,则会引发psutil.NoSuchProcess异常。请务必捕获此异常。 - fnkr
1
为了进一步提高速度,请指定 psutil.process_iter(attrs=['name']) 以仅获取名称属性,这将更快(略微)。https://psutil.readthedocs.io/en/latest/#psutil.process_iter - Chris Collett
1
@ChrisCollett,这也避免了@fnkr提到的“NoSuchProcess”问题,这更加令人烦恼,因此使用您的attrs = ['name']方法是更好的选择。 - ijoseph
显示剩余5条评论

26
尽管@zeller已经在这里说明了如何使用tasklist,但以下是一个例子。因为我刚刚在寻找纯正的Python替代方案...。

虽然@zeller已经在这里说明了如何使用tasklist,但以下是一个例子。因为我刚刚在寻找纯正的Python替代方案...

import subprocess

def process_exists(process_name):
    call = 'TASKLIST', '/FI', 'imagename eq %s' % process_name
    # use buildin check_output right away
    output = subprocess.check_output(call).decode()
    # check in last line for process name
    last_line = output.strip().split('\r\n')[-1]
    # because Fail message could be translated
    return last_line.lower().startswith(process_name.lower())

现在你可以做:

>>> process_exists('eclipse.exe')
True

>>> process_exists('AJKGVSJGSCSeclipse.exe')
False

为了避免多次调用并获得所有进程的概览,你可以这样做:

# get info dict about all running processes
import subprocess
output = subprocess.check_output(('TASKLIST', '/FO', 'CSV')).decode()
# get rid of extra " and split into lines
output = output.replace('"', '').split('\r\n')
keys = output[0].split(',')
proc_list = [i.split(',') for i in output[1:] if i]
# make dict with proc names as keys and dicts with the extra nfo as values
proc_dict = dict((i[0], dict(zip(keys[1:], i[1:]))) for i in proc_list)
for name, values in sorted(proc_dict.items(), key=lambda x: x[0].lower()):
    print('%s: %s' % (name, values))

2
check_output是在Python 2.7中引入的,因此无法在<2.7版本中使用。 - Elo
1
这对你来说真的是个问题吗?2.7版本自2010年以来就存在了,但它很快就会被弃用。https://pythonclock.org - ewerybody
2
这只是信息而已。在我的情况下,这确实是一个问题,因为我们有遗留的Python 2.6代码。 - Elo
@May.D 感谢您的编辑!但我更愿意将 lower() 保留在正确的位置!确实之前是无效的! - ewerybody
3
要在Python 3中工作,请使用output = subprocess.check_output(call).decode() - martineau
1
谢谢@martineau,我已经修复了这两个代码片段。现在在Python 2和3中可以以相同的方式运行。 - ewerybody

10

win32ui.FindWindow(classname, None) 如果找到具有给定类名的窗口,则返回窗口句柄。否则,它会引发 window32ui.error 异常。

import win32ui

def WindowExists(classname):
    try:
        win32ui.FindWindow(classname, None)
    except win32ui.error:
        return False
    else:
        return True

if WindowExists("DropboxTrayIcon"):
    print "Dropbox is running, sir."
else:
    print "Dropbox is running..... not."

我发现使用Autohotkey窗口监视器,Dropbox托盘图标的窗口类名为DropboxTrayIcon。
另请参阅: MSDN FindWindow

谢谢,我真的很喜欢这种方法。我的唯一反对意见是,即使使用像AHK Window Spy这样的程序,我也找不到我的应用程序的类名。最终我使用了WMIC import subprocess cmd = 'WMIC PROCESS get Caption,Commandline,Processid' proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) for line in proc.stdout: print line - TechDude
1
你可以不使用子进程获取它 --> procList = wmi.WMI().Win32_Process() print "\n".join(proc.Caption for proc in procList) - user3885927

7

在Windows系统中通常不使用锁文件(Unix上也很少用)。通常,当Windows程序想要查看是否已经运行了另一个实例时,它会使用已知的标题或类名调用FindWindow

def iTunesRunning():
    import win32ui
    # may need FindWindow("iTunes", None) or FindWindow(None, "iTunes")
    # or something similar
    if FindWindow("iTunes", "iTunes"):
        print "Found an iTunes window"
        return True

1
这个方法可能适用于iTunes,但对于可以作为无界面实例运行的进程(例如Excel),这种方法将不起作用。 - anijhaw
我认为问题非常明确,是想要检查iTunes。 - Clare Macrae
1
@anijhaw:您是在暗示Excel将在没有窗口的情况下运行,还是FindWindow无法找到Excel的隐藏窗口? - Gabe
1
谢谢,win32ui.FindWindow('iTunes,'iTunes') 将会起作用 (Y) - nightf0x
是的,Excel可以在没有窗口的无界面实例中运行。 - anijhaw
显示剩余4条评论

7

出于历史目的,我希望将此解决方案添加到列表中。它允许您基于.exe文件而不是窗口标题查找,并返回使用的内存和PID。

processes = subprocess.Popen('tasklist', stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE).communicate()[0]
# Put a regex for exact matches, or a simple 'in' for naive matches

一个示例输出的片段:
notepad.exe                  13944 Console                    1     11,920 K
python.exe                    5240 Console                    1     28,616 K
conhost.exe                   9796 Console                    1      7,812 K
svchost.exe                   1052 Services                   0     18,524 K
iTunes.exe                    1108 Console                    1    157,764 K

7

试试这段代码:

import subprocess
def process_exists(process_name):
    progs = str(subprocess.check_output('tasklist'))
    if process_name in progs:
        return True
    else:
        return False

检查进程是否正在运行:

if process_exists('example.exe'):
    #do something

4
import psutil

for p in psutil.process_iter(attrs=['pid', 'name']):
    if "itunes.exe" in (p.info['name']).lower():
        print("yes", (p.info['name']).lower())

适用于Python 3.7版本


import psutil

for p in psutil.process_iter(attrs=['pid', 'name']):
    if p.info['name'] == "itunes.exe":
        print("yes", (p.info['name']))

此方法适用于Python 3.8和psutil 5.7.0,Windows操作系统。


3

1
只是一个警告:tasklist 在处理长名称的进程时会截断进程名称。例如,它在这里截断了 exevmware-usbarbitrator64.ex 2076 Services 0 9,348 K。运行 wmic process 不会有这个问题。 - twasbrillig

3
根据ewerybody的帖子:https://dev59.com/XGsz5IYBdhLWcg3wmpBa#29275361 可能会出现多个问题:
  • 相同名称的多个进程
  • 长进程名称
如果进程名称过长,则'ewerybody'的代码将无法正常工作,因此这一行存在问题:
last_line.lower().startswith(process_name.lower())

由于最后一行比进程名称短,因此如果您只想知道一个或多个进程是否处于活动状态:

from subprocess import check_output

def process_exists(process_name):
    call = 'TASKLIST', '/FI', 'imagename eq %s' % process_name
    if check_output(call).splitlines()[3:]:
        return True

为了获取一个或多个进程的全部信息

from subprocess import check_output

def process_exists(process_name):
    call = 'TASKLIST', '/FI', 'imagename eq %s' % process_name
    processes = []
    for process in check_output(call).splitlines()[3:]:
        process = process.decode()
        processes.append(process.split())
    return processes

这很棒。我还会建议使用subprocess.run,因为它是自Python 3.5以来调用子进程的较新、更简洁且更易读的方式。文档指出 callcheck_callcheck_output是旧的API,尽管它们尚未被弃用。这个答案很好地概括了run - Antrikshy

2

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