By Alexey Malistov


2009-12-11 15:34:36 8 Comments

How to get main window handle from process id?

I want to bring this window to the front.

It works well in "Process Explorer".

7 comments

@Benj 2016-09-02 10:25:23

This is my solution using pure Win32/C++ based on the top answer. The idea is to wrap everything required into one function without the need for external callback functions or structures:

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

@Pep 2017-02-18 17:37:55

You may want to double check that the Window has no owner to avoid exiting the loop too early if the app has two windows: with if (GetWindowThreadProcessId(hwnd, &processId) && processId == pParams->second && GetWindow(hwnd, GW_OWNER) == 0)

@Hiale 2014-02-13 23:10:21

I checked how .NET determines the main window.

My finding showed that it also uses EnumWindows().

This code should do it similarly to the .NET way:

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

@Class Skeleton 2015-07-23 15:12:06

Could you explain the logic behind is_main_window? I don't see the difference (in my tests) against using if (data.process_id != process_id || !IsWindowVisible(handle)). Also, this approach needs tweaking to support process ids that have multiple main windows (such as a web browser).

@Jason C 2015-10-29 15:41:32

@CamelCase GetWindow(handle, GW_OWNER) == 0 checks that the window is not an owned window (e.g. a dialog box or something). IsWindowVisible(handle) checks to see that the window is visible and not hidden (quite a few applications with no GUI still have a window that is hidden, or even ones with a hidden GUI like configuration apps that run in the tray). So the window is considered the "main window" if it is visible and has no owner, which is a good-enough description of most "main windows".

@Celess 2017-04-26 00:05:15

This is a really good explanation of parent and owner that might clear a lot of this up for a lot of people, to help with adding or removing from the logic in this code: blogs.msdn.microsoft.com/oldnewthing/20100315-00/?p=14613 in a nutshell a window with no parent could still have an owner and not be a top level window.

@sudo rm -rf slash 2018-08-06 09:59:18

This works for some applications, but it can break down for more complicated things. Some applications (e.g. MSO) have lots of windows that are "main" according to this definition and that cannot be safely closed first. I wound that checking the window classname is a good bet to get the "main" window for most applications. Also, beware of race conditions. A user might close the application while you're looping and break your code

@Class Skeleton 2015-07-23 15:19:42

As an extension to Hiale's solution, you could provide a different or modified version that supports processes that have multiple main windows.

First, amend the structure to allow storing of multiple handles:

struct handle_data {
    unsigned long process_id;
    std::vector<HWND> handles;
};

Second, amend the callback function:

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

Finally, amend the returns on the main function:

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

@Oliver Zendel 2011-08-17 14:16:48

Just to make sure you are not confusing the tid (thread id) and the pid (process id):

DWORD pid;
DWORD tid = GetWindowThreadProcessId( this->m_hWnd, &pid);

@Sebastian 2013-09-02 10:27:57

That is the inverse function of what the OP wants.

@AntonK 2011-04-03 10:08:30

Though it may be unrelated to your question, take a look at GetGUIThreadInfo Function.

@Dathan 2009-12-11 16:01:42

There's the possibility of a mis-understanding here. The WinForms framework in .Net automatically designates the first window created (e.g., Application.Run(new SomeForm())) as the MainWindow. The win32 API, however, doesn't recognize the idea of a "main window" per process. The message loop is entirely capable of handling as many "main" windows as system and process resources will let you create. So, your process doesn't have a "main window". The best you can do in the general case is use EnumWindows() to get all the non-child windows active on a given process and try to use some heuristics to figure out which one is the one you want. Luckily, most processes are only likely to have a single "main" window running most of the time, so you should get good results in most cases.

@IInspectable 2015-07-21 13:41:57

Actually, .NET caches the window handle the first time the System.Diagnostics.Process.MainWindowHandle property is accessed. The window handle isn't stored up front. It is evaluated using the same algorithm outlined by Jerry Coffin in his answer.

@Dathan 2015-07-21 20:18:13

Yes, you're right. I don't know where I got that claim - probably just assuming from experience, even though the enumeration order of windows is unlikely to be guaranteed.

@Jerry Coffin 2009-12-11 15:48:08

I don't believe Windows (as opposed to .NET) provides a direct way to get that.

The only way I know of is to enumerate all the top level windows with EnumWindows() and then find what process each belongs to GetWindowThreadProcessID(). This sounds indirect and inefficient, but it's not as bad as you might expect -- in a typical case, you might have a dozen top level windows to walk through...

@Alexey Malistov 2009-12-11 15:49:52

How do I know that the main window?

@hometoast 2009-12-11 15:50:55

+1. You described exactly what the msdn article link suggested. But in about 1000 words less.

@Jerry Coffin 2009-12-11 15:53:55

@Alexey:From MSDN: "The EnumWindows function does not enumerate child windows."

@Jerry Coffin 2009-12-11 16:03:53

@hometoast:Experience breeds brevity. I published the method four years before that article.

@IInspectable 2015-07-21 13:58:22

For reference, this is how .NET retrieves the main window handle: System.Diagnostics.MainWindowFinder.FindMainWindow and EnumWindowsCallback. So arguably, .NET doesn't provide a "direct way" either.

Related Questions

Sponsored Content

23 Answered Questions

[SOLVED] What is the "-->" operator in C++?

36 Answered Questions

[SOLVED] How to install pip on Windows?

26 Answered Questions

29 Answered Questions

[SOLVED] How can you find out which process is listening on a port on Windows?

17 Answered Questions

[SOLVED] How do I run two commands in one line in Windows CMD?

1 Answered Questions

[SOLVED] The Definitive C++ Book Guide and List

  • 2008-12-23 05:23:56
  • grepsedawk
  • 2250777 View
  • 4246 Score
  • 1 Answer
  • Tags:   c++ c++-faq

9 Answered Questions

[SOLVED] Really killing a process in Windows

  • 2008-09-08 15:48:31
  • Eli Courtwright
  • 582568 View
  • 363 Score
  • 9 Answer
  • Tags:   windows

29 Answered Questions

[SOLVED] How can I update npm on Windows?

9 Answered Questions

[SOLVED] How to determine CPU and memory consumption from inside a process?

  • 2008-09-15 14:04:43
  • Lanzelot
  • 292467 View
  • 567 Score
  • 9 Answer
  • Tags:   c++ c memory cpu

Sponsored Content