Search code examples
pythonwindows

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.


Solution

  • 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:

    [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 - [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.
    
    [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.