pythonctypes

How to use ctypes' errcheck?


The Python Library Reference, Release 3.6.5, paragraph 16.16 ctypes - A foreign function library for Python, gives this example, demonstrating output parameters, in section Function prototypes:

The win32 GetWindowRect function:

WINUSERAPI BOOL WINAPI GetWindowRect(HWND hWnd, LPCRECT lprect);

The ctypes wrapper:

from ctypes             import POINTER, WINFUNCTYPE, windll, WinError
from ctypes.wintypes    import BOOL, HWND, RECT

prototype = WINFUNCTYPE(BOOL, HWND, POINTER(RECT))
paramflags = (1, 'hwnd'), (2, 'lprect')
GetWindowRect = prototype(('GetWindowRect', windll.user32), paramflags)

Now, how do I actually use GetWindowRect?

Trying

from ctypes import byref, cast
r = RECT()
h = cast(65552, HWND)   # _65552_ is the return value of _GetDesktopWindow_ on my system
result = GetWindowRect([h, byref(r)])

returns ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type.


Solution

  • [Python.Docs]: ctypes - A foreign function library for Python doesn't provide a complete example.

    code00.py:

    #!/usr/bin/env python
    
    import ctypes as cts
    import sys
    from ctypes import wintypes as wts
    
    
    def errcheck(result, func, args):
        if not result:
            raise cts.WinError()
        rc = args[1]
        return rc.left, rc.top, rc.bottom, rc.right
    
    
    def test_get_window_rect(desktop_wnd_handle):
        print("\n{:s}\n".format(test_get_window_rect.__name__))
        prototype = cts.WINFUNCTYPE(wts.BOOL, wts.HWND, cts.POINTER(wts.RECT))
        paramflags = (1, "hwnd"), (2, "lprect")
        GetWindowRect = prototype(("GetWindowRect", cts.windll.user32), paramflags)
    
        print("Without errcheck:\n")
        result = GetWindowRect(cts.windll.user32.GetDesktopWindow())
        print("Left: {:d}, Top: {:d}, Right: {:d}, Bottom: {:d}".format(result.left, result.top, result.right, result.bottom))
        result = GetWindowRect(0)
        print("Left: {:d}, Top: {:d}, Right: {:d}, Bottom: {:d}".format(result.left, result.top, result.right, result.bottom))
    
        GetWindowRect.errcheck = errcheck
    
        print("\nWith errcheck:\n")
        result = GetWindowRect(desktop_wnd_handle)
        print("Left: {:d}, Top: {:d}, Bottom: {:d}, Right: {:d}".format(*result))
        result = GetWindowRect(0)
        print("Left: {:d}, Top: {:d}, Bottom: {:d}, Right: {:d}".format(*result))
    
    
    def test_get_window_rect_old_style(desktop_wnd_handle):
        print("\n{:s}\n".format(test_get_window_rect_old_style.__name__))
        user32_dll = cts.WinDLL("user32")
        get_windows_rect_func = user32_dll.GetWindowRect
        get_windows_rect_func.argtypes = (wts.HWND, cts.POINTER(wts.RECT))
        get_windows_rect_func.restype = wts.BOOL
        rect = wts.RECT()
        result = get_windows_rect_func(desktop_wnd_handle, cts.byref(rect))
        print("Result: {:d}\nLeft: {:d}, Top: {:d}, Right: {:d}, Bottom: {:d}".format(result, rect.left, rect.top, rect.right, rect.bottom))
        rect = wts.RECT()
        result = get_windows_rect_func(0, cts.byref(rect))
        print("Result: {:d}\nLeft: {:d}, Top: {:d}, Right: {:d}, Bottom: {:d}".format(result, rect.left, rect.top, rect.right, rect.bottom))
    
    
    def main(*argv):
        hwnd = cts.windll.user32.GetDesktopWindow()
        test_get_window_rect_old_style(hwnd)
        test_get_window_rect(hwnd)
    
    
    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.\n")
        sys.exit(rc)
    

    Notes:

    • Function call happens normally (no argument processing - placing them in a list), and only input arguments specified

    • errcheck "magic" illustrated

    • Also included the old fashioned way of calling the function

    Output:

    (py35x64_test) e:\Work\Dev\StackOverflow\q050669907> "e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" ./code00.py
    Python 3.5.4 (v3.5.4:3f56838, Aug  8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] 064bit on win32
    
    
    test_get_window_rect_old_style
    
    Result: 1
    Left: 0, Top: 0, Right: 1920, Bottom: 1080
    Result: 0
    Left: 0, Top: 0, Right: 0, Bottom: 0
    
    test_get_window_rect
    
    Without errcheck:
    
    Left: 0, Top: 0, Right: 1920, Bottom: 1080
    Left: 0, Top: 0, Right: 0, Bottom: 0
    
    With errcheck:
    
    Left: 0, Top: 0, Bottom: 1080, Right: 1920
    Traceback (most recent call last):
      File "code.py", line 58, in <module>
        main()
      File "code.py", line 53, in main
        test_get_window_rect(hwnd)
      File "code.py", line 30, in test_get_window_rect
        result = GetWindowRect(0)
      File "code.py", line 9, in errcheck
        raise cts.WinError()
    OSError: [WinError 1400] Invalid window handle.