C++ 如何从进程id获取主窗口句柄?

C++ 如何从进程id获取主窗口句柄?,c++,windows,winapi,windows-7,C++,Windows,Winapi,Windows 7,如何从进程id获取主窗口句柄 我想把这扇窗户搬到前面去 它在“Process Explorer”中运行良好。我不认为Windows(与.NET相反)提供了一种直接的方法来实现这一点 我知道的唯一方法是使用EnumWindows()枚举所有顶级窗口,然后找到每个窗口所属的进程GetWindowThreadProcessID()。这听起来间接且低效,但并不像你想象的那么糟糕——在一个典型的例子中,你可能有十几个顶层窗口要穿过……这里可能存在误解。Net中的WinForms framework自动将创

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

我想把这扇窗户搬到前面去

它在“Process Explorer”中运行良好。

我不认为Windows(与.NET相反)提供了一种直接的方法来实现这一点


我知道的唯一方法是使用
EnumWindows()
枚举所有顶级窗口,然后找到每个窗口所属的进程
GetWindowThreadProcessID()
。这听起来间接且低效,但并不像你想象的那么糟糕——在一个典型的例子中,你可能有十几个顶层窗口要穿过……

这里可能存在误解。Net中的WinForms framework自动将创建的第一个窗口(例如,
Application.Run(new SomeForm())
)指定为
主窗口。然而,win32 API不承认每个进程都有“主窗口”的概念。消息循环完全能够处理系统和进程资源允许您创建的尽可能多的“主”窗口。因此,您的流程没有“主窗口”。在一般情况下,最好使用
EnumWindows()
使给定进程上的所有非子窗口都处于活动状态,并尝试使用一些启发式方法来确定哪个是您想要的。幸运的是,大多数进程在大多数情况下可能只有一个“主”窗口在运行,因此在大多数情况下,您应该会得到很好的结果。

尽管这可能与您的问题无关,但请查看。

只是为了确保您不会混淆tid(线程id)和pid(进程id):


我检查了.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);
}

作为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;
}
std::向量查找主窗口(无符号长进程id)
{
处理数据;
data.process\u id=process\u id;
枚举窗口(枚举窗口回调(LPRAM)和数据);
返回数据.handles;
}

这是我的解决方案,基于上面的答案,使用纯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;
}
#包括
HWND FindTopWindow(DWORD pid)
{
std::pair params={0,pid};
//使用lambda枚举窗口以处理每个窗口
BOOL-bResult=EnumWindows([](HWND-HWND,LPARAM-LPARAM)->BOOL
{
自动PPARAM=(标准::对*)(LPRAM);
DWORD processId;
if(GetWindowThreadProcessId(hwnd,&processId)&&processId==pParams->second)
{
//停止枚举
SetLastError(-1);
pParams->first=hwnd;
返回FALSE;
}
//继续枚举
返回TRUE;
},(LPARAM)和参数);
如果(!bResult&&GetLastError()==-1&¶ms.first)
{
首先返回参数;
}
返回0;
}

在这里,我想补充一点,如果您正在读取一个进程的HWND窗口句柄,那么该进程不应该在调试中运行,否则它将无法使用FindWindowEx找到窗口句柄。

旧问题,但似乎有很多流量,下面是一个简单的解决方案:

IntPtr GetMainWindowHandle(IntPtr aHandle) {
        return System.Diagnostics.Process.GetProcessById(aHandle.ToInt32()).MainWindowHandle;
}

我怎么知道主窗口?+1。您准确地描述了msdn文章链接所建议的内容。但是大约少了1000个字。@Alexey:From MSDN:“EnumWindows函数不枚举子窗口。”@hometoast:Experience孕育简洁。在那篇文章发表四年前,我发布了这个方法。为了便于参考,.NET是如何检索主窗口句柄的:and。所以可以说,.NET也不提供“直接方式”。如果有两个Firefox窗口打开,哪一个是“主”窗口?他们是平等的。Process Explorer似乎选择了最新的关注点。Windows不保留“主窗口”的概念。有顶级窗口、子窗口和自有窗口。任何进程都可以有零个或多个顶级窗口。除非您提供一个简洁的规范来确定“主窗口”,否则这个问题无法回答。这是OP想要的相反函数。实际上,.NET在第一次访问属性时缓存窗口句柄。车窗把手没有放在前面。它是用Jerry Coffin在年提出的相同算法计算的。是的,你是对的。我不知道我是从哪里得到这种说法的——可能只是根据经验假设,即使窗口的枚举顺序不太可能得到保证。你能解释一下is_main_窗口背后的逻辑吗?在我的测试中,我看不到使用
if(data.process\u id!=process\u id|124;!IsWindowVisible(handle))
的区别。此外,此方法需要调整以支持具有多个主窗口(如web浏览器)的进程ID。@CamelCase
GetWindow(handle,GW_OWNER)==0
检查该窗口是否为非主窗口(例如对话框或其他)
IsWindowVisible(handle)
检查窗口是否可见而未隐藏(相当多没有GUI的应用程序仍然有一个隐藏的窗口,甚至有一个隐藏的GUI类配置应用程序在任务栏中运行)。因此,如果窗口可见且没有所有者,则该窗口被视为“主窗口”,这是对大多数“主窗口”的一个足够好的描述。这是对父窗口和所有者的一个非常好的解释,可能会为很多人澄清这一点,帮助添加或删除此代码中的逻辑:简而言之,没有父窗口的窗口可能仍然有所有者,而不是顶级窗口。这适用于某些应用程序,但可能会因更复杂的事情而崩溃。一些苹果
IntPtr GetMainWindowHandle(IntPtr aHandle) {
        return System.Diagnostics.Process.GetProcessById(aHandle.ToInt32()).MainWindowHandle;
}