Search code examples
pythonpython-3.xwindowsctypespywin32

GetGUIThreadInfo() with pywin32


I am trying to follow this answer and i have reached the point where a should call

GetGUIThreadInfo()

but i cannot find that in the pywin32 docomentation i am using.

What i have done so far is

import win32api
import win32gui
import win32process

test1 = win32gui.FindWindowEx(0, 0, 0, "notepad")
(test1tid, test1pid) = win32process.GetWindowThreadProcessId(test1)
test1hwndFocus = win32process.GetGUIThreadInfo(test1tid)

but the last line is compleatly made up as i cannot find the right way to call the function.

Update1:

Think i made some progress but now my struct just returns 0 when i expect some hwnd... so maybe my struct is not writen to, i think this could be because of the types in my struct, but how do i find the right types?

import win32api
import win32gui
import win32process
import ctypes

class RECT(ctypes.Structure):
    _fields_ = [
    ("left", ctypes.c_ulong),
    ("top", ctypes.c_ulong),
    ("right", ctypes.c_ulong),
    ("bottom", ctypes.c_ulong)
    ]


class GUITHREADINFO(ctypes.Structure):
    _fields_ = [
    ("cbSize", ctypes.c_ulong),
    ("flags", ctypes.c_ulong),
    ("hwndActive", ctypes.c_ulong),
    ("hwndFocus", ctypes.c_ulong),
    ("hwndCapture", ctypes.c_ulong),
    ("hwndMenuOwner", ctypes.c_ulong),
    ("hwndMoveSize", ctypes.c_ulong),
    ("hwndCaret", ctypes.c_ulong),
    ("rcCaret", RECT)
    ]

guiThreadInfoStruct = GUITHREADINFO()


ctypes.sizeof(gtitest)

test1 = win32gui.FindWindowEx(0, 0, 0, "notepad")
(test1tid, test1pid) = win32process.GetWindowThreadProcessId(test1)
ctypes.windll.user32.GetGUIThreadInfo(test1tid, guiThreadInfoStruct)
print (guiThreadInfoStruct.hwndFocus)

Update2:

I found the types here

update3:

If anyone wanna see what i used this for go look here


Solution

  • Apparently, [MS.Docs]: GetGUIThreadInfo function is not wrapped by PyWin32, so alternative ways must be used. One of them is calling it via [Python 3.Docs]: ctypes - A foreign function library for Python (involves writing a lot of extra code).

    code00.py:

    #!/usr/bin/env python
    
    import sys
    import win32gui as wgui
    import win32process as wproc
    import win32con as wcon
    
    import ctypes as ct
    from ctypes import wintypes as wt
    
    
    class GUITHREADINFO(ct.Structure):
        _fields_ = [
            ("cbSize", wt.DWORD),
            ("flags", wt.DWORD),
            ("hwndActive", wt.HWND),
            ("hwndFocus", wt.HWND),
            ("hwndCapture", wt.HWND),
            ("hwndMenuOwner", wt.HWND),
            ("hwndMoveSize", wt.HWND),
            ("hwndCaret", wt.HWND),
            ("rcCaret", wt.RECT),
    
        ]
    
        def __str__(self):
            ret = "\n" + self.__repr__()
            start_format = "\n  {0:s}: "
            for field_name, _ in self. _fields_[:-1]:
                field_value = getattr(self, field_name)
                field_format = start_format + ("0x{1:016X}" if field_value else "{1:}")
                ret += field_format.format(field_name, field_value)
            rc_caret = getattr(self, self. _fields_[-1][0])
            ret += (start_format + "({1:d}, {2:d}, {3:d}, {4:d})").format(self. _fields_[-1][0], rc_caret.top, rc_caret.left, rc_caret.right, rc_caret.bottom)
            return ret
    
    
    def main(*argv):
        window_name = "Untitled - Notepad"
        hwnd = wgui.FindWindowEx(wcon.NULL, 0, wcon.NULL, window_name)
        print("'{0:s}' window handle: 0x{1:016X}".format(window_name, hwnd))
        tid, pid = wproc.GetWindowThreadProcessId(hwnd)
        print("PId: {0:d}, TId: {1:d}".format(pid, tid))
    
        user32_dll = ct.WinDLL("user32.dll")
        GetGUIThreadInfo = getattr(user32_dll, "GetGUIThreadInfo")
        GetGUIThreadInfo.argtypes = [wt.DWORD, ct.POINTER(GUITHREADINFO)]
        GetGUIThreadInfo.restype = wt.BOOL
    
        gti = GUITHREADINFO()
        gti.cbSize = ct.sizeof(GUITHREADINFO)
        res = GetGUIThreadInfo(tid, ct.byref(gti))
        print("{0:s} returned: {1:d}".format(GetGUIThreadInfo.__name__, res))
        if res:
            print(gti)
    
    
    if __name__ == "__main__":
        print("Python {0:s} {1:d}bit on {2:s}\n".format(" ".join(item.strip() for item in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform))
        main(*sys.argv[1:])
        print("\nDone.")
    

    Output:

    e:\Work\Dev\StackOverflow\q059884688>"e:\Work\Dev\VEnvs\py_pc064_03.07.06_test0\Scripts\python.exe" code00.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
    
    'Untitled - Notepad' window handle: 0x00000000042B20D8
    PId: 37192, TId: 53072
    GetGUIThreadInfo returned: 1
    
    <__main__.GUITHREADINFO object at 0x0000022649436648>
      cbSize: 0x0000000000000048
      flags: 0
      hwndActive: None
      hwndFocus: None
      hwndCapture: None
      hwndMenuOwner: None
      hwndMoveSize: None
      hwndCaret: None
      rcCaret: (0, 0, 0, 0)
    
    Done.
    

    Notes:

    • print data that you are working with, as it might be different what you'd expect. For example, the Notepad window title is not "notepad" like your code expects it to be, and in that case win32gui.FindWindowEx would return NULL (0).
    • I also use [ActiveState.Docs]: PyWin32 Documentation (it's old and outdated, but in most cases it's extremely helpful)