【问题标题】:Why do GetParent(hwnd) and (HWND)::GetWindow(hwnd, GW_OWNER) for top level windows give different results?为什么顶级窗口的 GetParent(hwnd) 和 (HWND)::GetWindow(hwnd, GW_OWNER) 会给出不同的结果?
【发布时间】:2018-11-25 19:55:13
【问题描述】:

我一直在研究 Window 层次结构的工作原理,并发现了不一致之处。两个函数调用 GetParent(hwnd)(HWND)::GetWindow(hwnd, GW_OWNER) 返回的值虽然大部分都同意,但并不总是顶级窗口

我假设这些是顶级窗口的原因是因为它们是使用EnumWindows() 函数找到的,用于枚举顶级窗口。还使用hWnd==GetAncestor(hWnd,GA_ROOT) 的答案中指定的测试What's the best way do determine if an HWND represents a top-level window? 进行了确认。

我在AVGUI.exe 中的窗口类#32770explorer.exenotepad++.exenotepad++.exeTeamViewer.exePrivacyIconClient.exedevenv.exe 中的窗口类devenv.exe 中看到了这个名单还在继续。

GetParent(hwnd) 将返回 HWNDGetDesktopWindow(),但 (HWND)::GetWindow(hwnd, GW_OWNER) 将返回 nullptr。所以如果GetParent() 应该返回一个顶级窗口的所有者,那么当(HWND)::GetWindow(hwnd, GW_OWNER) 返回一个nullptr 时它从哪里得到呢?

它确实与(HWND)::GetWindowLongPtr(hwnd, GWLP_HWNDPARENT) 一致,但这表明它是一个子窗口,这是有道理的,因为许多窗口类被列为ComboLBox。但是,我已经看到其他 HWND 具有它们应该存在的值,并且可能只是根据上下文忽略了该值。这些可能是非顶级窗口的另一个原因是!(GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD) 返回false

通过我完成的这项额外分析,似乎某些应用程序以某种方式将非顶级窗口提升为顶级窗口,这表明存在一些错误或正在使用一些未记录/未定义的行为并导致在奇怪的 HWND 链接中。

任何人都可以确认这些是由错误引起的,还是出于某种正当原因正在做的事情?

编辑

最小、完整和可验证的示例:

#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;
}

示例输出:

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,

【问题讨论】:

  • GW_OWNERGetWindow 函数的有效参数,而不是GetWindowLongPtr 函数。
  • @JonathanPotter,谢谢。错字。固定
  • 对我来说看起来不像是错字(而且标题仍然有那个 "typo")。如果您将GW_OWNER(值4)传递给GetWindowLongPtr,它将简单地读出窗口额外数据中偏移量4处的值。对于不在该偏移量中分配额外数据的窗口,该值是不确定的。对于这样做的窗口,该值的含义对于窗口类实现是私有的。目前尚不清楚,这个问题在问什么。
  • @IInspectable,错过了标题,谢谢。这是一个错字。你可以查看我的代码。

标签: windows winapi win32gui


【解决方案1】:

具有父级但没有所有者的窗口不是顶级窗口。这是对您描述的场景的明显解释。

更新

看来情况并非如此,因为您现在解释了窗口来自EnumWindows。它的文档说:

EnumWindows 函数不枚举子窗口,系统拥有的少数具有 WS_CHILD 样式的顶级窗口除外。

因此,我认为这些一定是您描述的窗户。那是系统拥有的顶级窗口,它给出了 WS_CHILD 样式。

【讨论】:

  • 另见Five Things Every Win32 Developer Should Know,有一节关于“父窗口和所有者窗口(审查)”(幻灯片#28​​-33)。特别是幻灯片 #31 有一张图表,显示了父母和所有者之间的区别。
  • 这是霰弹枪的回答? ? 是的,我知道这一切,但仍然没有回答我的问题。这些是顶层窗口,所以GetParent() 应该给所有者。但是,如果所有者设置为 null,那么它从哪里获取所有者句柄?
  • 由于该项目是使用EnumWindows() 找到的,它必须是顶级窗口
  • 在 EnumWindows 的文档中,它说“EnumWindows 函数不枚举子窗口,系统拥有的一些具有 WS_CHILD 样式的顶级窗口除外。”这些是窗户吗?他们有 WS_CHILD 风格吗?
  • 另请注意,直到最近的编辑,该问题才提到 EnumWindows。我们不知道这些窗口句柄是从哪里来的,而且仍然没有minimal reproducible example 这很容易解释。据我们所知,您正在根据问题标题调用 GetWindowLongPtr。把这一切清理干净怎么样?
猜你喜欢
  • 1970-01-01
  • 2013-07-13
  • 2019-03-28
  • 2013-06-03
  • 2013-01-17
  • 2020-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多