如何从进程ID获取main窗口句柄?
我想把这个窗口放到前面。
它在“Process Explorer”中运行良好。
我检查了.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);
}
我不相信 Windows(相对于 .NET)提供了直接的方法来实现这一点。
我知道的唯一方法是用
EnumWindows()
枚举所有顶级窗口,然后找到每个进程所属的进程 GetWindowThreadProcessID()
。这听起来间接且效率低下,但它并不像您想象的那么糟糕 - 在典型情况下,您可能有十几个顶级窗口需要穿过......
这里可能存在误解。 .Net 中的 WinForms 框架自动将创建的第一个窗口(例如,
Application.Run(new SomeForm())
)指定为 MainWindow
。然而,win32 API 无法识别每个进程的“主窗口”的概念。消息循环完全能够处理系统和进程资源允许您创建的尽可能多的“主”窗口。因此,您的进程没有“主窗口”。在一般情况下,您可以做的最好的事情是使用 EnumWindows()
使给定进程上的所有非子窗口处于活动状态,并尝试使用一些启发式方法来找出您想要的窗口。幸运的是,大多数进程在大部分时间里可能只运行一个“主”窗口,因此在大多数情况下您应该会获得良好的结果。
这是我基于最佳答案使用纯 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)¶ms);
if (!bResult && GetLastError() == -1 && params.first)
{
return params.first;
}
return 0;
}
虽然它可能与您的问题无关,但请看一下GetGUIThreadInfo Function。
作为 Hiale 解决方案的扩展,您可以提供不同的或修改的版本,以支持具有多个主窗口的进程。
首先,修改结构以允许存储多个句柄:
struct handle_data {
unsigned long process_id;
std::vector<HWND> handles;
};
二、修改回调函数:
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;
}
// change these 2 lines to allow storing of handle and loop again
data.handles.push_back(handle);
return TRUE;
}
最后修改主函数的返回值:
std::vector<HWD> find_main_window(unsigned long process_id)
{
handle_data data;
data.process_id = process_id;
EnumWindows(enum_windows_callback, (LPARAM)&data);
return data.handles;
}
如果您有命令行应用程序(而不是 GUI 应用程序),则可以使用 GetConsoleWindow() winapi 函数。
@Hiale 的最佳答案将不起作用,因为
is_main_window
不会将控制台窗口识别为应用程序的主窗口。
只是为了确保您没有混淆 tid(线程 id)和 pid(进程 id):
DWORD pid;
DWORD tid = GetWindowThreadProcessId( this->m_hWnd, &pid);