Skip to content

Instantly share code, notes, and snippets.

@blewert
Last active July 22, 2024 02:43
Show Gist options
  • Save blewert/b6e7b11c565cf82e7d700c609f22d023 to your computer and use it in GitHub Desktop.
Save blewert/b6e7b11c565cf82e7d700c609f22d023 to your computer and use it in GitHub Desktop.
[windows.h] Iterating over all desktop windows in C++, by z-order

Enumerating all visible desktop windows by z-order

Do you want to traverse all visible desktop windows, but with respect to z-order? Well, you might be able to do this via EnumDesktopWindows, but I'm not too sure if this is enumerated in a specified z-order. Plus if you don't want to use a callback, maybe this approach isn't for you. Instead, the windows api has some functions built into it to iterate over windows with respect to z-order. However, there's not many examples of how to do this online, which has inspired the creation of this gist.

Getting the top-most window

Getting the top-most window is really easy. First, you can just do something like:

//Window handle of the desktop
HWND desktopWindow = GetDesktopWindow();

//Get the top-most window in the desktop
HWND topMostWindow = GetTopWindow(desktopWindow);

Finding the window with the lowest z-index

Using the top-most window as a reference point, you can easily find the window which is the lowest z-order by using GetWindow(hwnd, GW_HWNDLAST). The GW_HWNDLAST parameter indicates that we want the window whose z-index is the lowest. Similarly, we could specify GW_HWNDFIRST to get the window whose z-index is the highest. So for example, you could use:

//Get the window with the lowest z-index, with regards to the top-most window
HWND window = GetWindow(window, GW_HWNDLAST);

Using GetWindow for iteration

GetWindow is really useful because it allows you to go to the next/previous window in the z-order by specifying either GW_HWNDNEXT (for the next window with a lower z-index) or GW_HWNDLAST (for the next window with a higher z-index). Iteration is achieved by saving the result of GetWindow into a variable, and then using that result to get the next window relative to the result. Here's an illustration of what I mean:

//Get the top-most window
HWND window = GetTopWindow(GetDesktopWindow());

//Find the window below this one, iteratively
window = GetWindow(window, GW_HWNDNEXT); // Maybe this is the window with z-index 5
window = GetWindow(window, GW_HWNDNEXT); // z-index 4
window = GetWindow(window, GW_HWNDNEXT); // z-index 3
window = GetWindow(window, GW_HWNDNEXT); // z-index 2

//etc..

So you can see how easy this would be to put into a loop, and just check whilst window is not null, e.g. while(window = GetWindow(window, GW_HWNDNEXT);

Iterating over windows from highest to lowest z-order

Putting together everything we've covered, this is really easy:

//Find the top-most window.
HWND window = GetTopWindow(GetDesktopWindow());

//Run from the highest z-order to the lowest by using GW_HWNDNEXT. We're using a do-while
//here because we don't want to skip the top-most window in this loop.
//..

do
{
  //Your code here..
}
while (window = GetWindow(window, GW_HWNDNEXT));

For example, if you wanted to print out the titles of all 'visible' windows, here's some code to do that:

do
{
  //Not visible? skip
  if(!IsWindowVisible(window))
    continue;
  
  //Get the title of the window
  char titleString[64];
  GetWindowTextA(window, titleString, 64);

  //Print it out
  printf("%s\r\n", titleString, 64);
}
while (window = GetWindow(window, GW_HWNDNEXT));

Iterating over windows from lowest z-order to highest

This is a bit trickier, but still very much achievable. The only difference we really have to make is to replace GW_HWNDFIRST with GW_HWNDLAST, so that we start from the lowest z-index in the loop. We also need to use GW_HWNDLAST instead of GW_HWNDNEXT.

GW_HWNDLAST is like GW_HWNDNEXT, but instead of going to a lower z-value (top -> bottom-most), it goes to higher z-values (bottom -> top-most). It's basically just used to iterate over the windows in a reverse order. So, by modifying our code a bit, we can use something like:

//Find the top-most window. We need the Z-order to be
//relative to this
HWND window = GetTopWindow(GetDesktopWindow());

//Then, find the window with the lowest Z-order value (GW_HWNDLAST not GW_HWNDFIRST)
window = GetWindow(window, GW_HWNDLAST);

do
{
  //Your code here
} 
while (window = GetWindow(window, GW_HWNDPREV));

Both types of iteration (top -> bottom, bottom -> top) are listed as separate files in this gist.

//Find the top-most window. We need the Z-order to be
//relative to this
HWND window = GetTopWindow(GetDesktopWindow());
do
{
//Your code
}
while (window = GetWindow(window, GW_HWNDNEXT));
//Find the top-most window. We need the Z-order to be
//relative to this
HWND window = GetTopWindow(GetDesktopWindow());
//Then, find the window with the lowest Z-order value (GW_HWNDLAST not GW_HWNDFIRST)
window = GetWindow(window, GW_HWNDLAST);
do
{
//Your code
}
while (window = GetWindow(window, GW_HWNDPREV));
@Rillwz
Copy link

Rillwz commented Oct 29, 2021

Awesome man, I need this.

@Prauwer
Copy link

Prauwer commented Jul 21, 2022

Thank you so much

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment