Using Windows Messages for GUI Automation with Python
This article explains how Windows' event‑driven message system can be leveraged for stable GUI automation, covering window concepts, message queues, message types, and providing Python code examples that use win32gui to click buttons, select menus, and manipulate edit controls without relying on screen coordinates.
Windows uses an event‑driven message mechanism that allows applications to communicate with the system and each other.
A window is a screen region that receives input and displays output; windows can contain controls such as buttons, edit boxes, and menus.
The article presents a classic “HelloWin” example (HELLOWIN.C) that demonstrates window class registration, creation, message loop, and window‑procedure handling messages like WM_CREATE , WM_PAINT , WM_DESTROY :
#include
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
static TCHAR szAppName[] = TEXT("HelloWin");
HWND hwnd;
MSG msg;
WNDCLASS wndclass = {0};
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.hInstance = hInstance;
wndclass.lpszClassName = szAppName;
RegisterClass(&wndclass);
hwnd = CreateWindow(szAppName, TEXT("Hello Program"), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
TextOut(hdc, 50, 50, TEXT("Hello, Windows!"), 15);
EndPaint(hwnd, &ps);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}Windows maintains two message queues – a system queue and an application queue – and messages are transferred from the system queue to the appropriate application queue based on the target HWND.
Message types are divided into system‑defined (window, command, notification) and application‑defined (WM_USER, WM_APP, registered messages). System‑defined messages range from 0x0000 to 0x03FF, while user‑defined messages start at 0x0400.
Messages can be sent, posted, or broadcast using functions such as SendMessage , PostMessage , SendMessageTimeout , BroadcastSystemMessage , etc. The most common are PostMessage (asynchronous) and SendMessage (synchronous).
Python code using the pywin32 win32gui and win32con modules shows how to automate GUI actions without relying on mouse coordinates:
def buttonClickFunc1(hwndButton):
"""Click a button by posting WM_LBUTTONDOWN and WM_LBUTTONUP messages."""
win32gui.PostMessage(hwndButton, win32con.WM_LBUTTONDOWN, 0, 0)
win32gui.PostMessage(hwndButton, win32con.WM_LBUTTONUP, 0, 0)
def buttonClickFunc2(hwndParent, buttonID):
"""Click a button via WM_COMMAND (BN_CLICKED)."""
win32gui.PostMessage(hwndParent, win32con.WM_COMMAND,
win32con.BN_CLICKED << 16 | buttonID, buttonID)
def getMenuItemText(menu, idx):
import win32gui_struct
mii, extra = win32gui_struct.EmptyMENUITEMINFO()
win32gui.GetMenuItemInfo(menu, idx, True, mii)
_, _, _, _, _, _, _, text, _ = win32gui_struct.UnpackMENUITEMINFO(mii)
return text
def editSetText(hwndEdit, text):
# WM_SETTEXT must be sent synchronously
result = win32gui.SendMessageTimeout(hwndEdit, win32con.WM_SETTEXT, 0, text,
win32con.SMTO_NORMAL, 1000)
return result[0] == 1 and result[1] == 1A complete script ties these functions together: it launches Dbgview.exe , finds its main window, opens the Filter dialog via a shortcut key, sets a filter string, and clicks the OK button, demonstrating an end‑to‑end automation workflow.
# Example workflow
import win32gui, win32con, subprocess, time
def startProgram(path):
return subprocess.Popen(path, shell=True)
def findParentWindow(className, title=None):
return win32gui.FindWindow(className, title)
if __name__ == '__main__':
proc = startProgram(r'C:\Users\Administrator\Desktop\Dbgview.exe')
time.sleep(3)
phwnd = findParentWindow('dbgviewClass')
# Open Filter (Ctrl+L) – virtual ID 40022
win32gui.PostMessage(phwnd, win32con.WM_COMMAND, 40022, 0)
time.sleep(3)
hwndFilter = findParentWindow('#32770', 'DebugView Filter')
hwndEdit = win32gui.FindWindowEx(hwndFilter, None, 'ComboBox', None)
editSetText(hwndEdit, 'testStr')
hwndOk = win32gui.FindWindowEx(hwndFilter, None, 'Button', '&OK')
buttonClickFunc1(hwndOk)Reference links to Microsoft documentation and community resources are provided for further reading.
360 Quality & Efficiency
360 Quality & Efficiency focuses on seamlessly integrating quality and efficiency in R&D, sharing 360’s internal best practices with industry peers to foster collaboration among Chinese enterprises and drive greater efficiency value.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.