Win32 Python: Getting all window titles

This post shows how you can retrieve all window titles in Microsoft Windows using Python’s ctypes module. Moreover, it also acts as a ctypes tutorial, showing how to create and use callback functions.

The following is the full code. Keep reading if you want to understand how it works. (Note: If you are reading this as a ctypes tutorial and are having trouble following the explanation, you may want to go through my previous tutorial first.)

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)

Explanation

First off, we import some Win32 functions from user32.dll. The function names are quite self-explanatory and should give you a picture of how everything is going to work: we call EnumWindows to enumerate all toplevel windows, filter them with IsWindowVisible to weed out most of the junk, and get the window titles with GetWindowText (using GetWindowTextLength to find out the right buffer size to hold the string).

Now let’s see the documentation for EnumWindows:

BOOL WINAPI EnumWindows(
  __in  WNDENUMPROC lpEnumFunc,
  __in  LPARAM lParam
);

Translated into Python (and English), here’s how you call the function:

EnumWindows(some_callback_function, some_data)

The callback function (an EnumWindowsProc) will be called for each topmost window. lParam is some optional, user-defined data that will also be passed to the callback.

BOOL CALLBACK EnumWindowsProc(
  __in  HWND hwnd,
  __in  LPARAM lParam
);

As should be apparent, the function gets passed a window handle (hwnd) and the same data argument that we passed to EnumWindows (lParam). lParam is useful when, for example, we want to store all the window handles in an array that is not otherwise reachable from the callback’s scope. The callback may return false to stop the iteration, or true to continue.

How do we create a callback function in ctypes?

EnumWindowsProc = ctypes.WINFUNCTYPE(ctypes.c_bool,
	ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))

def some_function(hwnd, lParam):
	return True
callback = EnumWindowsProc(some_function)

WINFUNCTYPE‘s first parameter is the return value of the callback (a boolean). The other parameters are the actual function parameters; HWND and LPARAM are both aliases of int *. Note that we use WINFUNCTYPE instead of CFUNCTYPE because that’s what the Win32 API expects (stdcall instead of cdecl)—this is the same reason we use ctypes.windll instead of ctypes.cdll.

Putting together all this information, we have the following code:

hwnds = []
def foreach_window(hwnd, lParam):
	hwnds.append(hwnd)
	return True
EnumWindows(EnumWindowsProc(foreach_window), 0)

Notice how we pass a null (0) as lParam because we’re not using it. In C, we can pass the equivalent to hwnds there to avoid making it a global variable.

Because IsWindowVisible, GetWindowText, and GetWindowTextLength are quite straightforward, I’ll skip the explanation. If you can’t figure out how to use them, refer to my previous tutorial.

Conclusion

This post presents code to retrieve window titles in Windows using ctypes. Most of the post explains the EnumWindows Win32 function and its interaction with the callback function EnumWindowsProc. Of particular interest is ctypes’ WINFUNCTYPE function, which is used to declare a callback function.

Corrections

An earlier version of this post used CFUNCTYPE instead of WINFUNCTYPE. This caused the function to be called using the cdecl calling convention instead of stdcall (which Win32 uses), resulting in an error message: “Procedure probably called with not enough arguments”.

Advertisements

4 thoughts on “Win32 Python: Getting all window titles

  1. Paul Kang

    Hello,

    I tried your codes with Python 2.6.6, and got the error message

    Traceback (most recent call last):
    File “C:/test.py”, line 17, in
    EnumWindows(EnumWindowsProc(foreach_window), 0)
    ValueError: Procedure probably called with not enough arguments (2029202 bytes missing)

    Any help would appreciated.

    -Paul Kang

    Reply
  2. Pingback: Python: Getting the title of windows, getting their processes and their commandlines using ctypes and win32 | Pixomania

Note: By commenting, you grant me permission to freely republish your comment.

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s