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.

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

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 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)

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 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)

Output:

[[email protected]: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 - [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:\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.

[[email protected]:e:\Work\Dev\StackOverflow\q031278590]>
[[email protected]: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.

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply