Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
301 views
in Technique[技术] by (71.8m points)

python - Get the title of a window of another program using the process name

This question is probably quite basic but I'm having difficulty cracking it. I assume that I will have to use something in ctypes.windll.user32. Bear in mind that I have little to no experience using these libraries or even ctypes as a whole.

I have used this code to list all the window titles, but I have no idea how I am supposed to change this code to get a window title with a process name:

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)

This code is from https://sjohannes.wordpress.com/2012/03/23/win32-python-getting-all-window-titles/

If my question is unclear, I would like to achieve something like this (just an example - I'm not asking specifically about Spotify):

getTitleOfWindowbyProcessName("spotify.exe") // returns "Avicii - Waiting For Love" (or whatever the title is)

A complication that may arise, if there are multiple windows running with the same process name (e.g. multiple chrome windows)

Thank you.


EDIT: To clarify, I want some code that takes a process name and returns a (possibly empty) list of window titles owned by that process as strings.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Before everything, I want to point out [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati's answer). Read it before working with CTypes.

Here's what I meant in the comment:

import win32gui


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

win32gui.EnumWindows(enumWindowsProc, 0)

Below, I pasted the whole thing...it doesn't work on the PC that I am at right now, since I messed up with security settings (it's an XP!!!) and I get a bunch of Access denied (error code: 5) errors, but here it is.

code00.py:

#!/usr/bin/env python3

import sys
import os
import traceback
import ctypes
from ctypes import wintypes
import win32con
import win32api
import win32gui
import win32process


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


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


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

        _OpenProcess = ctypes.windll.kernel32.OpenProcess
        _OpenProcess.argtypes = [wintypes.DWORD, wintypes.BOOL, wintypes.DWORD]
        _OpenProcess.restype = wintypes.HANDLE

        _GetProcessImageFileName = ctypes.windll.psapi.GetProcessImageFileNameA
        _GetProcessImageFileName.argtypes = [wintypes.HANDLE, wintypes.LPSTR, wintypes.DWORD]
        _GetProcessImageFileName.restype = wintypes.DWORD

        _CloseHandle = ctypes.windll.kernel32.CloseHandle
        _CloseHandle.argtypes = [wintypes.HANDLE] 
        _CloseHandle.restype = wintypes.BOOL

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


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


if __name__ == "__main__":
    print("Python {:s} on {:s}
".format(sys.version, sys.platform))
    main(sys.argv[1:])

Needless to say that:

  • In order for this code to work, you need to run it as a privileged user (Administrator); at least SeDebugPrivilege ([MS.Docs]: Privilege Constants) is required.
  • There might be surprises when the processes are running in 32 / 64 bit modes (the python process that you execute this code from, and the target processes enumerated by the code)



Update #0

  • Replaced all CTypes calls by PyWin32 ones
  • Improved algorithm
  • Fixed error in previous version

code01.py:

#!/usr/bin/env python3

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(*args):
    proc_name = args[0] if args 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 {0:s} {1:d}bit on {2:s}
".format(" ".join(item.strip() for item in sys.version.split("
")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
    main(*sys.argv[1:])
    print("
Done.")

Output:

[cfati@CFATI-5510-0:e:WorkDevStackOverflowq031278590]> "e:WorkDevVEnvspy_pc064_03.07.06_test0Scriptspython.exe" code01.py
Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)] 64bit 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:cpula.txt]]
    4135542: [Lister - [e:WorkDevCristiFatiBuildsWinOPSWpython27srcpywin32-b222win32srcwin32process.i]]
    3873800: [Lister - [e:WorkDevCristiFatiBuildsWinOPSWpython27srcpywin32-b222win32srcPyHANDLE.cpp]]
    29825332: [Lister - [E:WorkDevProjectsDevTelifmyosemiteGitLabAyoseissuesuildnrdependencies_200412.json]]
    8329240: [Lister - [e:WorkDevProjectsDevTelifmyosemitesrcsvnyosemiteCFATI_TRUNK_2src
esicpVerAppX.h]]
    985026: [Lister - [e:WorkDevCristiFatiBuildsWinOPSWpython27srcpywin32-b222win32srcwin32apimodule.cpp]]
PId 10936 windows:
    264744: [Junk - [email protected] - 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:WindowsSystem32cmd.exe]
PId 15132 windows:
    526852: [Administrator: Cmd (064) - "e:WorkDevVEnvspy_pc064_03.07.06_test0Scriptspython.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:WorkDevStackOverflowq031278590code00.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:WorkDevVEnvspy_pc032_03.07.06_test0Scriptspython.exe"]
PId 16476 windows:
    265814: [Administrator: Cmd (064) - "e:WorkDevVEnvspy_pc064_03.08.01_test0Scriptspython.exe"]
PId 16612 windows:
    331388: [Administrator: Cmd (064) - python]
PId 16796 windows:
    592540: [Administrator: Cmd (064)]
PId 16880 windows:
    264894: [Administrator: C:WindowsSystem32cmd.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  (Adm

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...