Home > database >  What's the correct way to bring a window to the front
What's the correct way to bring a window to the front

Time:06-15

I've been trying multiple ways of bringing a window into focus and on top of other windows.

I've tried SetForegroundWindow(), BringWindowToTop(), SetActiveWindow(). None of these work consistently.

How can I simply make it so that a window that I want to be in focus, becomes on top of all other windows and is focused? Why is this so difficult?

CodePudding user response:

SetForegroundWindow() is the correct way to change the foreground window, but the thread that calls SetForegroundWindow() has to meet certain criteria for it to "work". If it does not meet the criteria, the window's taskbar button is flashed instead. This is by design. This is to protect the user from applications stealing focus and you should respect that.

See also:

CodePudding user response:

Your process needs to satisfy a few conditions for it to be able to set the foreground window.
This is to prevent applications from stealing focus - which is a very bad user experience.

Imagine you're writing an email, and halfway through it your application decides now would be a good time to push a window into foreground. As you're typing suddenly the focused window would instantly change and your keypresses would now be sent to your program instead of the mail program. Not only could this cause all sorts of havoc (the keys you pressed are now sent to your program, so hotkeys might get triggered, dialogs dismissed, etc...) - but it would also be a really frustrating experience for the user (especially for less technically-inclined people).

That is the reason why SetForegroundWindow() & similar functions sometimes won't push your window to the foreground, but still report success. Your window will still flash in the task bar though, so users know that something happened in your application.


SetForegroundWindow

The exact list of conditions that need to be met for SetForegroundWindow() to work are detailed in the documentation:

The system restricts which processes can set the foreground window.
A process can set the foreground window only if one of the following conditions is true:

  • The process is the foreground process.
  • The process was started by the foreground process.
  • The process received the last input event.
  • There is no foreground process.
  • The foreground process is being debugged.
  • The foreground is not locked (see LockSetForegroundWindow).
  • The foreground lock time-out has expired (see SPI_GETFOREGROUNDLOCKTIMEOUT in SystemParametersInfo).
  • No menus are active.

An application cannot force a window to the foreground while the user is working with another window ¹. Instead, Windows flashes the taskbar button of the window to notify the user.

1 this is what prevents the mail program example detailed above from happening.

A process that fulfills these criteria can also "share" its permission to set the foreground window with another process by calling AllowSetForegroundWindow()


SetActiveWindow

SetActiveWindow() only works if the targeted window is attached to your message queue and one of your application windows is currently the foreground window.

Activates a window. The window must be attached to the calling thread's message queue.

The window will be brought into the foreground (top of Z-Order) if its application is in the foreground when the system activates the window.


BringWindowToTop

BringWindowToTop() is a convenience function for SetWindowPos(), which again has the same restrictions:

If an application is not in the foreground, and should be in the foreground, it must call the SetForegroundWindow function.

To use SetWindowPos to bring a window to the top, the process that owns the window must have SetForegroundWindow permission.


Using UI Automation

Since you mentioned that you need this functionality for an accessibility tool, here's how you could accomplish this using UI Automation:

This example uses bare-bones COM for simplicity, but if you want you can of course use e.g. wil for a more C -like API.

#include <uiautomation.h>

bool MoveWindowToForeground(IUIAutomation* pAutomation, HWND hWnd) {
    // retrieve an ui automation handle for a given window
    IUIAutomationElement* element = nullptr;
    HRESULT result = pAutomation->ElementFromHandle(hWnd, &element);
    if (FAILED(result))
        return false;

    // move the window into the foreground
    result = element->SetFocus();

    // cleanup
    element->Release();
    return SUCCEEDED(result);
}

int main()
{
    // initialize COM, only needs to be done once per thread
    CoInitialize(nullptr);

    // create the UI automation object
    IUIAutomation* pAutomation = nullptr;
    HRESULT result = CoCreateInstance(CLSID_CUIAutomation, NULL, CLSCTX_INPROC_SERVER, IID_IUIAutomation, reinterpret_cast<LPVOID*>(&pAutomation));
    if (FAILED(result))
        return 1;

    // move the given window into the foreground
    HWND hWnd = FindWindowW(nullptr, L"Calculator");
    MoveWindowToForeground(pAutomation, hWnd);
    
    // cleanup
    pAutomation->Release();
    CoUninitialize();

    return 0;
}
  • Related