Several years ago, I was trying to find a window with specified class, so I chose FindWindow
and it worked pretty well. Recently, I've received reports telling me the program grabs wrong window randomly. I realized that FindWindow
seems to be unstable on Windows 11.
Here is a simple demo in C++, searching by class name(or searching a class doesn't exist by default):
#ifndef UNICODE
#define UNICODE
#endif
#include <iostream>
#include <Windows.h>
int wmain(int argc, wchar_t* argv[])
{
LPCWSTR lpszClass;
if (argc > 1) {
lpszClass = argv[1];
}
else {
lpszClass = L"impossible class";
}
wchar_t buffer[1024];
std::wcout << L"Searching for: " << lpszClass << std::endl;
for (int64_t i = 0; true; i ++) {
HWND hWnd = FindWindowW(lpszClass, NULL);
//HWND hWnd = FindWindowEx(NULL, NULL, lpszClass, NULL);
if (hWnd != NULL) {
GetClassNameW(hWnd, buffer, 1024);
if (wcscmp(lpszClass, buffer) != 0) {
std::wcout <<
L"Loop: " << i << std::endl <<
L"Unexpected handle: " << hWnd << std::endl <<
L"Real class: " << buffer << std::endl;
}
}
}
}
After random loops(might takes several seconds to have an hour), what I've got:
Searching for: impossible class
Loop: 123157966
Unexpected handle: 0000000000010130
Real class: Shell_TrayWnd
Loop: 234530658
Unexpected handle: 0000000000010130
Real class: Shell_TrayWnd
Loop: 261192707
Unexpected handle: 0000000000010130
Real class: Shell_TrayWnd
Additionally, the unexpected handle is not always consistent, sometimes another wrong handle with different class name returned. It might take up to an hour to experience this issue, if system doesn't have many processes(or windows/handles?). Also, moving mouse over windows or refreshing windows seems making it happens more frequently.
I've tested:
- both
FindWindow
and FindWindowEx
, broken
- both
W
and A
version API, broken
- both
x86
and x64
platform, broken
- both Intel and AMD platform, broken
- building with both
MinGW
/g++
and MSVC
/cl
, broken
- call API with
DllImport
in a C# program, broken
- both physical system and virtual machine, broken
- binding process to only single/multiple P-Core/E-Core, broken
But when I test it on Windows 10, it works fine.
Here are some environments tested:
Broken:
- Windows 11 Pro 21H2 22000.832, Intel Core i9 12900K
- Windows 11 Pro 21H2 22000.832, Intel Core i9 12900H
- Windows 11 Pro 21H2 22000.795, Intel Core i7 9700
- Windows 11 Pro 21H2 22000.832, AMD Ryzen 9 5950X
OK:
- Windows 10 Pro 21H2 19044.1826, Intel Core i9 12900K
- Windows 10 Pro 21H2 19044.1826, AMD Ryzen 5 5600X
- Windows 10 Enterprise 2009 19042.804, Intel Core i5 1135G7
======
Update 2022/08/08: my own user story
For my own purpose, I'm using FindWindow
and FindWindowEx
in a desktop utility.
There are 2 different use cases:
- Find the window of a music player, grab its title
- Find the window of another process, send message for interprocess communication
Case 1:
A music player process often shows the title and artist of current track in its window title. First, I need to figure out the class name this music player uses with spy++
. Then, I can write something like FindWindow(className, NULL)
in my program to find the proper window of music player.
Usually I would add more filters after FindWindow
to make sure it's the correct one, but that's not the point since FindWindow
didn't do its own job well at first.
Case 2:
Some music player supports plugins, it's possible to establish a connection between player process and my own process, and sending/posting messages is a common way to do so. In the player process, I've created a window with a class name like MyIPCWindowClass
, added some custom messages after WM_USER
. In my own process, I'm trying to find the window created in player process, with FindWindow(_T("MyIPCWindowClass"), NULL)
of course.
I know there are other different ways for IPC, and I could use a special window name instead of a special window class, but that's not the point either.
Beside my own use cases, I have to say this issue is WAY MORE DESTRUCTIVE than what I've said before. FindWindow
and FindWindowEx
have 20+ years history, they have more important use cases, like IPC and daemon services etc.