使用进程名称获取另一个程序窗口的标题

4
这个问题可能相当基础,但我很难理解。我想我需要使用ctypes.windll.user32中的某些东西。请注意,我几乎没有使用这些库甚至整个ctypes的经验。
我使用了这段代码来列出所有窗口标题,但我不知道该如何修改此代码以获取带有进程名称的窗口标题:
import ctypes

EnumWindows = ctypes.windll.user32.EnumWindows
EnumWindowsProc = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
GetWindowText = ctypes.windll.user32.GetWindowTextW
GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
IsWindowVisible = ctypes.windll.user32.IsWindowVisible

titles = []
def foreach_window(hwnd, lParam):
    if IsWindowVisible(hwnd):
        length = GetWindowTextLength(hwnd)
        buff = ctypes.create_unicode_buffer(length + 1)
        GetWindowText(hwnd, buff, length + 1)
        titles.append(buff.value)
    return True
EnumWindows(EnumWindowsProc(foreach_window), 0)

print(titles)

这段代码来源于https://sjohannes.wordpress.com/2012/03/23/win32-python-getting-all-window-titles/ 如果我的问题不够清晰,我想要实现类似这样的功能(仅为示例 - 我并非特别询问Spotify):
getTitleOfWindowbyProcessName("spotify.exe") // returns "Avicii - Waiting For Love" (or whatever the title is)

如果有多个使用相同进程名称(例如多个chrome窗口)的窗口同时运行,可能会出现一个复杂情况。

谢谢。


编辑:为了澄清,我想要一些代码,可以输入一个进程名称,并返回该进程拥有的窗口标题列表(可能为空)作为字符串。


你所提到的复杂性应该清楚地表明,从进程名称获取窗口标题是不可能的。 - TigerhawkT3
@TigerhawkT3 IsWindowVisible 确保它有一个窗口。我只会在我确定一定会有窗口的特定情况下使用它,所以这不是问题。 - Anish Gupta
@CristiFati 没错。我会更新我的问题。 - Anish Gupta
注意:我是ctypes的忠实粉丝,但是当涉及到调用需要复杂结构作为参数的函数时,我建议使用pywin32,这是一个很好的工具,它将大多数WinAPI封装成了Python风格的形式... - CristiFati
很难在评论中写出这个,但我会尝试(这是枚举标题的等效代码;我将使用__\n__文字作为行分隔符):import win32gui\ndef enumWindowsProc(hwnd, lParam):\n print win32gui.GetWindowText(hwnd)\nwin32gui.EnumWindows(enumWindowsProc, 0)。我建议使用此模块使即将到来的工作更简单。 - CristiFati
显示剩余6条评论
1个回答

7

首先,我想指出[SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer)。在使用CTypes之前,请先阅读该文章。

以下是我的评论内容:

import win32gui


def enumWindowsProc(hwnd, lParam):
    print win32gui.GetWindowText(hwnd)


win32gui.EnumWindows(enumWindowsProc, 0)

以下是我复制的全部内容...它在我现在所用的电脑上无法运行,因为我搞砸了安全设置(它是一个XP!!!),我收到了许多拒绝访问(错误代码:5)的错误提示,但这就是它。
code00.py:
#!/usr/bin/env python

import sys
import os
import traceback
import ctypes as ct
from ctypes import wintypes as wt
import win32con as wcon
import win32api as wapi
import win32gui as wgui
import win32process as wproc


def enumWindowsProc(hwnd, lParam):
    if (lParam is None) or ((lParam is not None) and (wproc.GetWindowThreadProcessId(hwnd)[1] == lParam)):
        text = wgui.GetWindowText(hwnd)
        if text:
            wStyle = wapi.GetWindowLong(hwnd, wcon.GWL_STYLE)
            if wStyle & wcon.WS_VISIBLE:
                print("%08X - %s" % (hwnd, text))


def enumProcWnds(pid=None):
    wgui.EnumWindows(enumWindowsProc, pid)


def enumProcs(procName=None):
    pids = wproc.EnumProcesses()
    if procName is not None:
        bufLen = 0x100

        _OpenProcess = ct.windll.kernel32.OpenProcess
        _OpenProcess.argtypes = (wt.DWORD, wt.BOOL, wt.DWORD)
        _OpenProcess.restype = wt.HANDLE

        _GetProcessImageFileName = ct.windll.psapi.GetProcessImageFileNameA
        _GetProcessImageFileName.argtypes = (wt.HANDLE, wt.LPSTR, wt.DWORD)
        _GetProcessImageFileName.restype = wt.DWORD

        _CloseHandle = ct.windll.kernel32.CloseHandle
        _CloseHandle.argtypes = (wt.HANDLE,)
        _CloseHandle.restype = wt.BOOL

        filteredPids = ()
        for pid in pids:
            try:
                hProc = _OpenProcess(wcon.PROCESS_ALL_ACCESS, 0, pid)
            except:
                print("Process [%d] couldn't be opened: %s" % (pid, traceback.format_exc()))
                continue
            try:
                buf = ct.create_string_buffer(bufLen)
                _GetProcessImageFileName(hProc, buf, bufLen)
                if buf.value:
                    name = buf.value.decode().split(os.path.sep)[-1]
                    #print("proc name:", name)
                    if name.lower() == procName.lower():
                        filteredPids += (pid,)
                else:
                    _CloseHandle(hProc)
                    continue
            except:
                print("Error getting process name: %s" % traceback.format_exc())
                _CloseHandle(hProc)
                continue
            _CloseHandle(hProc)
        return filteredPids
    else:
        return pids


def main(*argv):
    if argv:
        procName = argv[0]
    else:
        procName = None
    pids = enumProcs(procName)
    #print(pids)
    for pid in pids:
        enumProcWnds(pid)


if __name__ == "__main__":
    print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                   64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.")
    sys.exit(rc)

无需多言:

  • 为了让这段代码正常运行,您需要以特权用户(管理员)身份运行它; 至少需要SeDebugPrivilege ([MS.Docs]: Privilege Constants)。
  • 当进程以32/64位模式运行时(执行此代码的Python进程和代码枚举的目标进程),可能会有一些意外情况发生。


更新#0

  • 将所有CTypes调用替换为PyWin32调用
  • 改进算法
  • 修复之前版本中的错误

code01.py:

#!/usr/bin/env python

import sys
import os
import traceback
import win32con as wcon
import win32api as wapi
import win32gui as wgui
import win32process as wproc


# Callback
def enum_windows_proc(wnd, param):
    pid = param.get("pid", None)
    data = param.get("data", None)
    if pid is None or wproc.GetWindowThreadProcessId(wnd)[1] == pid:
        text = wgui.GetWindowText(wnd)
        if text:
            style = wapi.GetWindowLong(wnd, wcon.GWL_STYLE)
            if style & wcon.WS_VISIBLE:
                if data is not None:
                    data.append((wnd, text))
                #else:
                    #print("%08X - %s" % (wnd, text))


def enum_process_windows(pid=None):
    data = []
    param = {
        "pid": pid,
        "data": data,
    }
    wgui.EnumWindows(enum_windows_proc, param)
    return data


def _filter_processes(processes, search_name=None):
    if search_name is None:
        return processes
    filtered = []
    for pid, _ in processes:
        try:
            proc = wapi.OpenProcess(wcon.PROCESS_ALL_ACCESS, 0, pid)
        except:
            #print("Process {0:d} couldn't be opened: {1:}".format(pid, traceback.format_exc()))
            continue
        try:
            file_name = wproc.GetModuleFileNameEx(proc, None)
        except:
            #print("Error getting process name: {0:}".format(traceback.format_exc()))
            wapi.CloseHandle(proc)
            continue
        base_name = file_name.split(os.path.sep)[-1]
        if base_name.lower() == search_name.lower():
            filtered.append((pid, file_name))
        wapi.CloseHandle(proc)
    return tuple(filtered)


def enum_processes(process_name=None):
    procs = [(pid, None) for pid in wproc.EnumProcesses()]
    return _filter_processes(procs, search_name=process_name)


def main(*argv):
    proc_name = argv[0] if argv else None
    procs = enum_processes(process_name=proc_name)
    for pid, name in procs:
        data = enum_process_windows(pid)
        if data:
            proc_text = "PId {0:d}{1:s}windows:".format(pid, " (File: [{0:s}]) ".format(name) if name else " ")
            print(proc_text)
            for handle, text in data:
                print("    {0:d}: [{1:s}]".format(handle, text))


if __name__ == "__main__":
    print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                   64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    rc = main(*sys.argv[1:])
    print("\nDone.")
    sys.exit(rc)

输出:

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q031278590]> "e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe" code01.py
Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 064bit on win32

PId 8048 windows:
    131462: [Program Manager]
PId 10292 windows:
    133738: [Skype]
PId 5716 windows:
    89659824: [python - Get the title of a window of another program using the process name - Stack Overflow - Google Chrome]
    132978: [Service Name and Transport Protocol Port Number Registry - Google Chrome]
    329646: [CristiFati/Prebuilt-Binaries: Various software built on various platforms. - Google Chrome]
    133078: [unittest — Unit testing framework — Python 3.8.2 documentation - Google Chrome]
    263924: [libssh2/libssh2 at libssh2-1.9.0 - Google Chrome]
    264100: [WNetAddConnection2A function (winnetwk.h) - Win32 apps | Microsoft Docs - Google Chrome]
    525390: [Understanding 4D -- The Tesseract - YouTube - Google Chrome]
    198398: [Workaround for virtual environments (VirtualEnv) by CristiFati · Pull Request #1442 · mhammond/pywin32 - Google Chrome]
    591586: [struct — Interpret bytes as packed binary data — Python 3.8.2 documentation - Google Chrome]
    263982: [Simulating an epidemic - YouTube - Google Chrome]
    329312: [SetHandleInformation function (handleapi.h) - Win32 apps | Microsoft Docs - Google Chrome]
    263248: [studiu functie faze - Google Search - Google Chrome]
    198364: [Lambda expressions (since C++11) - cppreference.com - Google Chrome]
PId 13640 windows:
    984686: [Total Commander (x64) 9.22a - NOT REGISTERED]
    44046462: [Lister - [c:\c\pula.txt]]
    4135542: [Lister - [e:\Work\Dev\CristiFati\Builds\Win\OPSWpython27\src\pywin32-b222\win32\src\win32process.i]]
    3873800: [Lister - [e:\Work\Dev\CristiFati\Builds\Win\OPSWpython27\src\pywin32-b222\win32\src\PyHANDLE.cpp]]
    29825332: [Lister - [E:\Work\Dev\Projects\DevTel\ifm\yosemite\GitLabA\yose\issues\buildnr\dependencies_200412.json]]
    8329240: [Lister - [e:\Work\Dev\Projects\DevTel\ifm\yosemite\src\svn\yosemite\CFATI_TRUNK_2\src\res\icpVerAppX.h]]
    985026: [Lister - [e:\Work\Dev\CristiFati\Builds\Win\OPSWpython27\src\pywin32-b222\win32\src\win32apimodule.cpp]]
PId 10936 windows:
    264744: [Junk - cristian.fati@devtelsoftware.com - Mozilla Thunderbird]
PId 10848 windows:
    1115842: [Registry Editor]
PId 6164 windows:
    264756: [Rocket.Chat]
PId 2716 windows:
    854508: [Skype]
    199310: [New tab and 5 more pages ‎- Microsoft Edge]
PId 14936 windows:
    655466: [Administrator: C:\Windows\System32\cmd.exe]
PId 15132 windows:
    526852: [Administrator: Cmd (064) - "e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe"]
PId 15232 windows:
    133918: [Microsoft Edge]
PId 9748 windows:
    68492: [Microsoft Edge]
PId 14968 windows:
    134146: [Microsoft Edge]
    68634: [Microsoft Edge]
PId 15636 windows:
    134208: [Microsoft Edge]
PId 16348 windows:
    1379450: [Microsoft Edge]
PId 15568 windows:
    68828: [Microsoft Edge]
    68788: [Microsoft Edge]
PId 16040 windows:
    265406: [Administrator: Cmd (032)]
PId 5792 windows:
    2034532: [e:\Work\Dev\StackOverflow\q031278590\code00.py - Notepad++ [Administrator]]
PId 12032 windows:
    69134: [Microsoft Edge]
PId 16200 windows:
    69146: [Microsoft Text Input Application]
PId 16224 windows:
    69184: [Microsoft Edge]
PId 5288 windows:
    265806: [Administrator: Cmd (064) - "e:\Work\Dev\VEnvs\py_pc032_03.07.06_test0\Scripts\python.exe"]
PId 16476 windows:
    265814: [Administrator: Cmd (064) - "e:\Work\Dev\VEnvs\py_pc064_03.08.01_test0\Scripts\python.exe"]
PId 16612 windows:
    331388: [Administrator: Cmd (064) - python]
PId 16796 windows:
    592540: [Administrator: Cmd (064)]
PId 16880 windows:
    264894: [Administrator: C:\Windows\System32\cmd.exe]
PId 17156 windows:
    69284: [Console1 - Microsoft Visual Studio (Administrator)]
PId 16636 windows:
    69396: [QtConsole0 - Microsoft Visual Studio  (Administrator)]
PId 18380 windows:
    69522: [Console0 - Microsoft Visual Studio (Administrator)]
PId 18108 windows:
    200528: [BlackBird - Microsoft Visual Studio (Administrator)]
PId 19476 windows:
    1052868: [pcbuild - Microsoft Visual Studio  (Administrator)]
PId 16680 windows:
    200924: [Yosemite - Microsoft Visual Studio  (Administrator)]
PId 16020 windows:
    201030: [-bash]
PId 6532 windows:
    200996: [-bash]
PId 13140 windows:
    266602: [-bash]
PId 6032 windows:
    790834: [-bash]
PId 8496 windows:
    8130950: [-bash]
PId 3208 windows:
    4198878: [-bash]
PId 19088 windows:
    528856: [-bash]
PId 12744 windows:
    266770: [-bash]
PId 3896 windows:
    201370: [-bash]
PId 11512 windows:
    1315422: [Yosemite - Microsoft Visual Studio  (Administrator)]
PId 20660 windows:
    267028: [Yosemite - Microsoft Visual Studio  (Administrator)]
PId 20684 windows:
    70554: [Microsoft Visual Studio  (Administrator)]
PId 14808 windows:
    201692: [Dependency Walker]
PId 13056 windows:
    5509836: [Oracle VM VirtualBox Manager]
PId 17756 windows:
    70802: [TIC2Vone.pdf - Adobe Acrobat Reader DC]
PId 14572 windows:
    267868: [Select Administrator: Powershell (064)]
PId 25588 windows:
    332550: [Administrator: Cmd (064)]
PId 20504 windows:
    463448: [Administrator: C:\Windows\System32\cmd.exe]
PId 24740 windows:
    2298466: [Administrator: C:\Windows\System32\cmd.exe - "e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe"]
PId 25960 windows:
    2430020: [Utils [E:\Work\Dev\Utils] - ...\current\ifm\pe_ver.py - PyCharm (Administrator)]
PId 28836 windows:
    332582: [E:\Work\Dev\Projects\DevTel\ifm\yosemite\src\svn\yosemite - Log Messages - TortoiseSVN]
PId 29796 windows:
    4724788: [Administrator: C:\Windows\System32\cmd.exe]
PId 26344 windows:
    2883688: [Dependency Walker]
PId 34124 windows:
    242746876: [Administrator: C:\Windows\System32\cmd.exe]
PId 21972 windows:
    1317748: [Administrator: Cmd (064)]
PId 35060 windows:
    2563162: [Administrator: C:\Windows\System32\cmd.exe - "e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe"  code00.py]
PId 14692 windows:
    102695792: [Device Manager]
PId 35776 windows:
    990338: [Process Explorer - Sysinternals: www.sysinternals.com [CFATI-5510-0\cfati] (Administrator)]
PId 33524 windows:
    656408: [PE Explorer - 30 day evaluation version]
    25368616: [PE Explorer]
PId 29488 windows:
    3218206: [Microsoft Edge]
PId 13184 windows:
    267896: [cfati-ubtu16x64-0 [Running] - Oracle VM VirtualBox]
PId 33716 windows:
    3932934: [Cheat Engine 7.0]
    73098: [Cheat Engine 7.0]

Done.

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q031278590]>
[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q031278590]> "e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe" code01.py "notepad++.exe"
Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 064bit on win32

PId 5792 (File: [C:\Install\pc064\NP++\NP++\Version\notepad++.exe]) windows:
    2034532: [e:\Work\Dev\StackOverflow\q031278590\code00.py - Notepad++ [Administrator]]

Done.

我收到了一堆错误信息,看起来像是 Error getting process name: Traceback (most recent call last): File "D:\Desktop\procs.py", line 27, in enumProcs name = win32api.GetModuleFileName(hProc) pywintypes.error: (126, 'GetModuleFileNameW', 'The specified module could not be found.')Process [5248] couldn't be opened: Traceback (most recent call last): File "D:\Desktop\procs.py", line 22, in enumProcs hProc = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION, False, pid) pywintypes.error: (5, 'OpenProcess', 'Access is denied.') 我以管理员身份运行它。 - Anish Gupta
修复了代码。现在它是一个混合体,用于窗口处理使用 pywin32,而用于进程处理则使用 ctypes(因为 pywin32 没有导出一些所需的函数)。 - CristiFati
它起作用了吗?当尝试打开某些进程时,肯定会返回“访问被拒绝”的错误信息。 - CristiFati
另外,我不需要它列出所有窗口标题,只需要一个即可。 - Anish Gupta
额外的窗口可以从 enumWindowsProc 中过滤掉(我看到许多无用的“默认 IME”窗口被显示出来)。关于 TypeError,请将第 39 行替换为 name = buf.value.decode().split(os.path.sep)[-1](我已经在 Python2 中测试了该脚本)。 - CristiFati
显示剩余5条评论

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