如何从进程ID获取主窗口句柄?

Ale*_*tov 55 c++ windows winapi windows-7

如何从进程ID 获取窗口句柄?

我想把这个窗口带到前面.

它在"Process Explorer"中运行良好.

小智 54

我检查了.NET如何确定主窗口.

我的发现表明它也使用了EnumWindows().

此代码应该与.NET方式类似:

struct handle_data {
    unsigned long process_id;
    HWND window_handle;
};

HWND find_main_window(unsigned long process_id)
{
    handle_data data;
    data.process_id = process_id;
    data.window_handle = 0;
    EnumWindows(enum_windows_callback, (LPARAM)&data);
    return data.window_handle;
}

BOOL CALLBACK enum_windows_callback(HWND handle, LPARAM lParam)
{
    handle_data& data = *(handle_data*)lParam;
    unsigned long process_id = 0;
    GetWindowThreadProcessId(handle, &process_id);
    if (data.process_id != process_id || !is_main_window(handle))
        return TRUE;
    data.window_handle = handle;
    return FALSE;   
}

BOOL is_main_window(HWND handle)
{   
    return GetWindow(handle, GW_OWNER) == (HWND)0 && IsWindowVisible(handle);
}
Run Code Online (Sandbox Code Playgroud)

  • @CamelCase`GetWindow(handle,GW_OWNER)== 0`检查窗口是否不是[拥有的窗口](https://msdn.microsoft.com/zh-cn/library/windows/desktop/ms632599(v = vs .85).aspx#owned_windows)(例如对话框或其他内容)。IsWindowVisible(handle)检查窗口是否可见并且未被隐藏(一些没有GUI的应用程序仍然具有被隐藏的窗口,甚至具有隐藏GUI的应用程序,例如运行在托盘中的配置应用程序)。因此,如果该窗口可见并且没有所有者,则将其视为“主窗口”,这是对大多数“主窗口”的充分描述。 (2认同)

Jer*_*fin 37

我不相信Windows(而不是.NET)提供了直接的方法来实现它.

我知道的唯一方法是枚举所有顶级窗口,EnumWindows()然后找到每个窗口所属的进程GetWindowThreadProcessID().这听起来是间接的和低效的,但它并没有你想象的那么糟糕 - 在典型的情况下,你可能会有十几个顶级窗口可以走过......

  • 我怎么知道主窗口? (6认同)
  • +1.您准确描述了msdn文章链接建议的内容.但在约1000字以内. (6认同)
  • @Alexey:来自MSDN:"EnumWindows函数不枚举子窗口." (5认同)
  • @hometoast:经验孕育简洁。我在那篇文章前四年发表了这个方法。 (2认同)

Dat*_*han 11

这里有可能误解..Net中的WinForms框架自动将创建的第一个窗口(例如Application.Run(new SomeForm()))指定为MainWindow.但是,win32 API无法识别每个进程的"主窗口"的概念.消息循环完全能够处理尽可能多的"主"窗口,系统和进程资源将允许您创建.因此,您的流程没有"主窗口".在一般情况下,您可以做的最好的事情是EnumWindows()使所有非子窗口在给定进程上处于活动状态,并尝试使用一些启发式方法来确定哪一个是您想要的那个.幸运的是,大多数进程在大多数情况下只能运行一个"主"窗口,因此在大多数情况下您应该获得良好的结果.

  • 实际上,.NET 第一次缓存窗口句柄 [System.Diagnostics.Process.MainWindowHandle](http://referencesource.microsoft.com/#System/services/monitoring/system/diagnosticts/Process.cs,4a6008b8d55b354b) 属性被访问。窗户把手没有存储在前面。它使用 Jerry Coffin 在[他的答案](http://stackoverflow.com/a/1888944/1889329) 中概述的相同算法进行评估。 (3认同)

Ben*_*enj 7

这是我使用基于最佳答案的纯Win32/C++的解决方案.我们的想法是将所需的所有内容包装到一个函数中,而无需外部回调函数或结构:

#include <utility>

HWND FindTopWindow(DWORD pid)
{
    std::pair<HWND, DWORD> params = { 0, pid };

    // Enumerate the windows using a lambda to process each window
    BOOL bResult = EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL 
    {
        auto pParams = (std::pair<HWND, DWORD>*)(lParam);

        DWORD processId;
        if (GetWindowThreadProcessId(hwnd, &processId) && processId == pParams->second)
        {
            // Stop enumerating
            SetLastError(-1);
            pParams->first = hwnd;
            return FALSE;
        }

        // Continue enumerating
        return TRUE;
    }, (LPARAM)&params);

    if (!bResult && GetLastError() == -1 && params.first)
    {
        return params.first;
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • 如果应用程序有两个窗口,您可能需要仔细检查窗口是否没有所有者,以避免过早退出循环:使用 `if (GetWindowThreadProcessId(hwnd, &amp;processId) &amp;&amp; processId == pParams-&gt;second &amp;&amp; GetWindow(hwnd, GW_OWNER ) == 0)` (3认同)