【问题标题】:Get the title of a window of another program using the process name使用进程名获取另一个程序的窗口标题
【发布时间】:2015-09-25 12:46:57
【问题描述】:

这个问题可能很基本,但我很难破解它。我假设我将不得不在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 确保它有一个窗口。我只会将它用于我确定会有一个窗口的特定窗口,所以无论如何这都不是问题。
  • 所以,基本上你想要的是一段代码,它将一个字符串(进程名称)作为输入并输出一个字符串列表(可能为空)(该进程拥有的窗口标题)。

标签: python windows


【解决方案1】:

首先,我想指出[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)

下面,我粘贴了整个内容...它在我现在使用的 PC 上不起作用,因为我搞砸了安全设置(它是一个 XP!!!)我收到一堆 Access denied(错误代码: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)

不用说:

  • 为了让这段代码工作,您需要以特权用户(Administrator)的身份运行它;至少需要 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.') 的错误我以管理员身份运行它。
  • 修复了代码。它现在是一个混合的窗口处理它使用pywin32,而进程处理使用ctypes(因为pywin32不导出一些需要的功能)。
  • 成功了吗?当然会有一些进程在尝试打开时会返回Access denied
  • @CristiFabi 当我在没有 args 的情况下运行它时,它工作得非常好,但是当我尝试使用 arg 运行它时,例如 chrome.exe 这是我得到的错误:Error getting process name: Traceback (most recent call last): File "ez.py", line 39, in enumProcs name = buf.value.split(os.path.sep)[-1] TypeError: 'str' does not support the buffer interface
  • 另外,我不需要它来列出所有窗口标题,只需一个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-03
  • 1970-01-01
  • 2012-06-19
  • 2013-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多