I've been looking into how the Window hierarchy works and have found an inconsistency. The values returned by the two function calls GetParent(hwnd)
and (HWND)::GetWindow(hwnd, GW_OWNER)
though, mostly agree, don't always for top level windows.
The reason that I am assuming that these are top level windows, is because they were found using the EnumWindows() function, which are only to enumerate top level windows. It was also confirmed using the test hWnd==GetAncestor(hWnd,GA_ROOT)
as specified in the answer to What's the best way do determine if an HWND represents a top-level window?.
I've seen this in window class #32770
in AVGUI.exe
and window class ComboLBox
in explorer.exe
, notepad++.exe
, TeamViewer.exe
, PrivacyIconClient.exe
, devenv.exe
, ... and the list goes on.
GetParent(hwnd)
would return a HWND
of GetDesktopWindow()
, but (HWND)::GetWindow(hwnd, GW_OWNER)
would return nullptr
. So if GetParent()
should return the owner of a top level window, where is it getting that from when (HWND)::GetWindow(hwnd, GW_OWNER)
is returning a nullptr
?
It does agree with (HWND)::GetWindowLongPtr(hwnd, GWLP_HWNDPARENT)
, but that would indicate that it is a child window, which sort of makes sense as the window class for many are listed as ComboLBox
. However, I've seen other HWNDs that have values where they should be, and it could be that the value is just being ignored, based on context. Another reason that these could have been non-top level windows at one point is that !(GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD)
returns false
.
With this extra analysis I've done, it would appear that some application is somehow promoting non-top level windows to top level windows, which would indicate some bug or there is using some undocumented/undefined behaviour going on and is resulting in odd HWND linkages.
Can anyone confirm that these are caused by bugs or is something that is being done for some legitimate reason?
Edit
Minimal, Complete, and Verifiable example:
#include <AtlBase.h> // Conversion routines (CW2A)
#include <Windows.h> // Windows stuff
#include <assert.h>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <set>
#include <psapi.h>
#include <regex>
auto FIELD_SEPERATOR = L",";
auto& output_window_class_and_title(std::wostream & os, const HWND &hWnd)
{
wchar_t window_class[1024], window_title[1024];
window_class[0] = window_title[0] = 0;
::GetClassNameW(hWnd, window_class, _countof(window_class));
::GetWindowTextW(hWnd, window_title, _countof(window_title));
// replacing any CRLFs with field separators
auto wc
= std::regex_replace(window_class, std::wregex(L"(\r\n?|\n\r?)")
, L" " );
auto wt
= std::regex_replace(window_title, std::wregex(L"(\r\n?|\n\r?)")
, L" " );
os << CW2A(wc.c_str()) << FIELD_SEPERATOR << CW2A(wt.c_str());
return os;
}
// Store exe names
std::set<std::wstring> exe_names;
// Map pid to exe name
std::map<DWORD, std::wstring const*> pid_to_exe_name;
// Get exe name (from cache if possible)
const std::wstring * GetProcessName(DWORD pid)
{
const std::wstring * pProcess_name = nullptr;
auto it_found_pid = pid_to_exe_name.find(pid);
if (it_found_pid == pid_to_exe_name.end()) {
wchar_t exe_name[MAX_PATH]; exe_name[0] = 0;
if (HANDLE hProcess = ::OpenProcess(
PROCESS_ALL_ACCESS | PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ,
FALSE, pid))
{
auto chars_copied = ::GetProcessImageFileNameW(hProcess, exe_name, _countof(exe_name));
assert(chars_copied > 0);
exe_name[chars_copied] = 0;
::CloseHandle(hProcess);
auto found = exe_names.emplace(exe_name);
pProcess_name = &*found.first;
}
else
{
auto found = exe_names.emplace(L"* Couldn't open process handle *");
pProcess_name = &*found.first;
}
pid_to_exe_name.try_emplace(pid, pProcess_name);
}
else {
pProcess_name = it_found_pid->second;
}
}
int main()
{
//auto* filename = "window-tree.txt";
//static std::wfstream os(filename, std::ios_base::out | std::ios_base::trunc);
static auto& os = std::wcout;
os.exceptions(os.badbit | os.failbit | os.eofbit);
os << std::hex;
try {
static HWND hDesktop = GetDesktopWindow();
EnumWindows([](_In_ HWND hwnd, _In_ LPARAM lParam) -> BOOL
{
assert(hwnd);
HWND hParent = ::GetParent(hwnd);
if (hParent == hDesktop) {
auto hOwner = (HWND)::GetWindow(hwnd, GW_OWNER);
auto hParent = (HWND)::GetWindowLongPtr(hwnd, GWLP_HWNDPARENT);
auto hParent_from_GetParent = ::GetParent(hwnd);
auto hParent_from_GetAncestor = ::GetAncestor(hwnd, GA_PARENT);
bool is_top_level = (hwnd == GetAncestor(hwnd, GA_ROOT));
bool is_top_level2 = !(GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD);
DWORD pid;
auto tid = ::GetWindowThreadProcessId(hwnd, &pid);
std::wstring const* pProcess_name = GetProcessName(pid);
os
<< std::setw(8) << hwnd
<< FIELD_SEPERATOR << ::IsWindowVisible(hwnd)
<< FIELD_SEPERATOR << is_top_level
<< FIELD_SEPERATOR << is_top_level2
<< FIELD_SEPERATOR << std::setw(8) << hOwner
<< FIELD_SEPERATOR << std::setw(8) << hParent_from_GetParent
<< FIELD_SEPERATOR << std::setw(8) << hParent
<< FIELD_SEPERATOR << std::setw(8) << hParent_from_GetAncestor
<< FIELD_SEPERATOR << std::setw(4) << pid
<< FIELD_SEPERATOR << std::setw(4) << tid
<< FIELD_SEPERATOR << pProcess_name->c_str()
<< FIELD_SEPERATOR;
output_window_class_and_title(os, hwnd);
os
<< std::endl;
}
return TRUE;
}
, 0);
}
catch (std::ios_base::failure& e) {
std::cerr << "Exception: " << e.what() << std::endl;
}
return 0;
}
Example output:
000A0FF6,0,1,0,00000000,00010010,00000000,00010010,8520,6d44,\Device\HarddiskVolume2\Program Files (x86)\Notepad++\notepad++.exe,ComboLBox,
0094150C,0,1,0,00000000,00010010,00000000,00010010, 3ac,3f14,\Device\HarddiskVolume4\Windows\explorer.exe,ComboLBox,
0078181E,0,1,0,00000000,00010010,00000000,00010010,5e58,5068,\Device\HarddiskVolume4\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\Tools\spyxx_amd64.exe,ComboLBox,
00FA16AA,0,1,0,00000000,00010010,00000000,00010010, 3ac,242c,\Device\HarddiskVolume4\Windows\explorer.exe,ComboLBox,
01121B00,0,1,0,00000000,00010010,00000000,00010010,4440,7b98,\Device\HarddiskVolume4\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\devenv.exe,ComboLBox,
021304D0,0,1,0,00000000,00010010,00000000,00010010,5e1c,5b5c,\Device\HarddiskVolume4\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\devenv.exe,ComboLBox,
011A11EE,0,1,0,00000000,00010010,00000000,00010010,5e1c,5b5c,\Device\HarddiskVolume4\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\devenv.exe,ComboLBox,
018D1B7A,0,1,0,00000000,00010010,00000000,00010010,5e1c,5b5c,\Device\HarddiskVolume4\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\devenv.exe,ComboLBox,
0137042A,0,1,0,00000000,00010010,00000000,00010010,5e1c,5b5c,\Device\HarddiskVolume4\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\devenv.exe,ComboLBox,
0028065A,0,1,0,00000000,00010010,00000000,00010010,5e1c,5b5c,\Device\HarddiskVolume4\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\devenv.exe,ComboLBox,
005B0472,0,1,0,00000000,00010010,00000000,00010010,8520,6d44,\Device\HarddiskVolume2\Program Files (x86)\Notepad++\notepad++.exe,ComboLBox,
00421248,0,1,0,00000000,00010010,00000000,00010010,8520,6d44,\Device\HarddiskVolume2\Program Files (x86)\Notepad++\notepad++.exe,ComboLBox,
00BA10F8,0,1,0,00000000,00010010,00000000,00010010,8520,6d44,\Device\HarddiskVolume2\Program Files (x86)\Notepad++\notepad++.exe,ComboLBox,
009E0EE2,0,1,0,00000000,00010010,00000000,00010010,8520,6d44,\Device\HarddiskVolume2\Program Files (x86)\Notepad++\notepad++.exe,ComboLBox,
00040822,0,1,0,00000000,00010010,00000000,00010010, 3ac,3d94,\Device\HarddiskVolume4\Windows\explorer.exe,ComboLBox,
000404A4,0,1,0,00000000,00010010,00000000,00010010, 7e0, 7dc,\Device\HarddiskVolume4\Program Files (x86)\Intel\Intel(R) Management Engine Components\IMSS\PrivacyIconClient.exe,ComboLBox,
000404A0,0,1,0,00000000,00010010,00000000,00010010, 7e0, 7dc,\Device\HarddiskVolume4\Program Files (x86)\Intel\Intel(R) Management Engine Components\IMSS\PrivacyIconClient.exe,ComboLBox,
000102FE,0,1,0,00000000,00010010,00000000,00010010,19dc, 4c4,\Device\HarddiskVolume2\Program Files (x86)\TeamViewer\TeamViewer.exe,ComboLBox,
00010290,0,1,0,00000000,00010010,00000000,00010010,19dc, 4c4,\Device\HarddiskVolume2\Program Files (x86)\TeamViewer\TeamViewer.exe,ComboLBox,
00EF16B4,0,1,0,00000000,00010010,00000000,00010010,4440,7b98,\Device\HarddiskVolume4\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\devenv.exe,ComboLBox,
00840D20,0,1,0,00000000,00010010,00000000,00010010,4440,7b98,\Device\HarddiskVolume4\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\devenv.exe,ComboLBox,
016A0E64,0,1,0,00000000,00010010,00000000,00010010,4440,7b98,\Device\HarddiskVolume4\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\devenv.exe,ComboLBox,
021B11F2,0,1,0,00000000,00010010,00000000,00010010,4440,7b98,\Device\HarddiskVolume4\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\devenv.exe,ComboLBox,