I'm currently getting into the ctypes module and I'm trying to call the user32 function GetWindowText with a HWND handle I already received by using FindWindow. This time though i wanted to process a step further and use a function prototype instead of calling the function with ctypes.windll.user32.GetWindowText. Although I´m having problems declaring the lpString arguement as output parameter.
My first attempt looked this way:
GetWindowText = cfunc("GetWindowTextA",windll.user32,c_int,
("hWnd",HWND,1),
("lpString",LPCSTR,2),
("nMaxCount",c_int,1)
)
(cfunc is a little wrapper that I found here)
This prototype yields the following exception as soon as it is called:
chars,name = user32.GetWindowText(handle,255)
TypeError: c_char_p 'out' parameter must be passed as default value
I thought that any output variables must be a POINTER(...) type, so I changed my definition to:
GetWindowText = cfunc("GetWindowTextA",windll.user32,c_int,
("hWnd",HWND,1),
("lpString",POINTER(c_char),2),
("nMaxCount",c_int,1)
)
But this yields an exception too:
chars,name = user32.GetWindowText(handle,255)
ctypes.ArgumentError: argument 2: <type 'exceptions.TypeError'>: wrong type
I hope somebody knows how to call the GetWindowText function correctly using ctypes prototyping.
Edit:
Through further research I could get it to work, at least somehow. The first issue I fixed was the usage of cfunc() which had wrong calling specifiers. I defined a exact copy of that function and named it winfunc() and replaced return CFUNCTYPE(result, *atypes)((name, dll), tuple(aflags)) with return WINFUNCTYPE(result, *atypes)((name, dll), tuple(aflags)).
Then I inspected prototyping further. As it seems if you pass somewhat like ("someParameter",POINTER(aType),2) to WINFUNCTYPE it will create a aType object on call and passes a pointer to that object to the function. In the returned tuple you can then access the aType object. This brings up another problem. A cstring is a array of chars; so one needs to tell ctypes to create a c_char array. This means that something like:
GetWindowText = winfunc("GetWindowTextA",windll.user32,c_int,
("hWnd",HWND,1),
("lpString",POINTER(c_char*255),2),
("nMaxCount",c_int,1)
)
works just fine. But unfortunately, ctypes will now pass a pointer to a cstring which is ALWAYS 255 chars long ignoring the size specified by nMaxCount.
In my opinion, I think theres no way one could get that function to work with a dynamically sized cstring defined as output parameter. The only possibility seems to be simply going without the output parameter feature and defining a LPCSTR as input parameter. The callee then needs to create a buffer by his own with ctypes.create_string_buffer() and pass it to the function (just as in C).
You have to create a string buffer for out parameters. You can wrap the function to make it somewhat transparent:
# python3
import ctypes as ct
import ctypes.wintypes as w
def zerocheck(result, func, args):
if result == 0:
raise ct.WinError(ct.get_last_error())
return result
user32 = ct.WinDLL('user32', use_last_error=True)
# Use Unicode(W) APIs since Windows uses Unicode internally
# and Python 3 `str` type is Unicode as well.
_GetWindowText = user32.GetWindowTextW
_GetWindowText.argtypes = w.HWND, w.LPWSTR, ct.c_int
_GetWindowText.restype = ct.c_int
_GetWindowText.errcheck = zerocheck
FindWindow = user32.FindWindowW
FindWindow.argtypes = w.LPCWSTR, w.LPCWSTR
FindWindow.restype = w.HWND
def GetWindowText(handle, *, bufsize=256):
buf = ct.create_unicode_buffer(bufsize) # Allocate wchar buffer
_GetWindowText(handle, buf, bufsize)
return buf.value
handle = FindWindow(None, 'Calculator') # Run Windows 11 Calculator first
print(GetWindowText(handle))
print(GetWindowText(handle, bufsize=5))
try:
GetWindowText(-123)
except OSError as e:
print(e)
Output:
Calculator
Calc
[WinError 1400] Invalid window handle.
Or in this case you can just use pywin32:
import win32gui
h = win32gui.FindWindow(None,'Calculator')
print(win32gui.GetWindowText(h))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With