Win32 ::SetForegroundWindow() not working all the time - c++

I'm working on a messenging tool. The messaging window is part of a whole application. I need the window to go to the front when there are some messages coming. I'am using this code :
if( m_hwnd == NULL || !::IsWindow(m_hwnd) )
return E_UNEXPECTED;
if(::IsIconic(m_hwnd))
{
::ShowWindowAsync( m_hwnd, SW_RESTORE );
}
::SetWindowPos(m_hwnd, HWND_TOP, 0, 0, 0, 0, SWP_ASYNCWINDOWPOS | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
::SetForegroundWindow(m_hwnd);
if( pvbProcessed != NULL )
*pvbProcessed = VARIANT_TRUE;
return S_OK;
I even tried to do a TOPMOST but still in some cases it does not work.
I also Tried a ::BringToFront().
Anyone can help or give an explanation on why it doen not work ? Is it a known microsoft limitation.

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.
See the SetForegroundWindow() docs for more details.

In the official document of Win32 API, there are remarks:
Remarks 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 process is being debugged.
The foreground process is not a Modern Application or the Start Screen.
The foreground is not locked (see LockSetForegroundWindow).
The foreground lock time-out has expired (see SPI_GETFOREGROUNDLOCKTIMEOUT in SystemParametersInfo).
No menus are active.
But there is a trick to force a window to foreground:
The trick is to make windows ‘think’ that our process and the target window (hwnd) are related by attaching the threads (using AttachThreadInput API) and using an alternative API: BringWindowToTop.
void CommonHelpers::forceForegroundWindow(HWND hwnd) {
DWORD windowThreadProcessId = GetWindowThreadProcessId(GetForegroundWindow(),LPDWORD(0));
DWORD currentThreadId = GetCurrentThreadId();
DWORD CONST_SW_SHOW = 5;
AttachThreadInput(windowThreadProcessId, currentThreadId, true);
BringWindowToTop(hwnd);
ShowWindow(hwnd, CONST_SW_SHOW);
AttachThreadInput(windowThreadProcessId,currentThreadId, false);
}
The original answer is here
P.S: I also dont think you should bring your message app to top if some message is coming up, but still ... this is the solution that working for my Qt App on Windows 10

Related

What's the correct way to bring a window to the front

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?
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:
Foreground activation permission is like love: You can’t steal it, it has to be given to you
What if two programs did this?
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;
}

Pin window to desktop / Glue window to desktop / "Always-on-bottom" window

I'm working on a basic desktop app in C++ / Win32.
My goal right now is to create a basic "sticky note" app that would be pinned / glued to the desktop, i.e always in front of the desktop but always behind of any other application.
Really a personal project there, just to fight my bad memory and have my tasks/notes always visible on the desktop so I couldn't miss them when starting the computer & so on.
The behaviour I'm aiming for would be similar to Stardock Fences ("kind of" because I'm not going to store any desktop icon in there, but you hopefully get the idea)
I started with the sample code from the Get Started with Win32 and C++ docs to have the most basic Win32 minimal window setup.
What I got so far :
I managed to keep my window on bottom of every other app and in front of the desktop by calling SetWindowPos in the window procedure (WindowProc), when handling the event WM_SETFOCUS (I first tried with the event WM_WINDOWPOSCHANGING as suggested in this answer but this resulted in an annoying flickering when dragging the window).
case WM_SETFOCUS:
SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
return 0;
The problem : my window stays in front of the desktop, except when I click on the "Show Desktop" button in the taskbar (or hit the Windows + D shortcut). As I often use this shortcut myself, I'd like my window to stay over the desktop no matter what.
A not-satisfying-enough-but-still-something I managed to do is to bring back my window in front of the desktop when clicking on any other window after hitting Windows + D (this mostly makes sense with multiple monitors, as opening a random app on the first one for example, will toggle back my own app in front of the desktop on another screen).
I could do this using this time the event WM_SIZE and calling ShowWindow then SetWindowPos, still in the WindowProc
case WM_SIZE:
ShowWindow(hwnd, SW_SHOWNORMAL);
SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
return 0;
Not ideal though, as I'd really want my app to always remain in front of the desktop and "survive" to the Show Desktop action.
What I tried :
I checked out those answers but couldn't figure out how to achieve what I want.
How to make 'always-on-bottom'-window
Window “on desktop” : Note on this one, I tried the trick with SetParent like this in the wWinMain
HWND desktop = FindWindow(L"ProgMan", L"Program Manager");
if (desktop == NULL)
{
return 0;
}
ShowWindow(hwnd, nCmdShow);
SetParent(hwnd, desktop);
However, my app isn't visible at all anymore with this, even though the FindWindow didn't return NULL but an actual handle.
Make aplication always on Bottom (pinned to desktop, behind all other apps) in C++/WinAPI [duplicate]
Disable Minimize, Maximize, Close buttons in Win32 I tried those to "intercept" the Show Desktop event but it seems this event doesn't get fired with the Show Desktop action.
Did I miss something ?
As #JonathanPotter pointed out, when hitting Windows + D or the Show Desktop button, the event WM_WINDOWPOSCHANGING gets fired, and the window gets moved to -32 000, -32 000 (its size also gets changed)
NOTE : a window without the style WS_MINIMIZEBOX seems not receiving WINDOWPOSCHANGING event when hitting Windows + D. Thus, no -32 000 coordinates detection in that case... Also noticed the same issue when using the ex style WS_EX_TOOLWINDOW (as this one gets rid of the minimize box, even if you set the style flag WS_MINIMIZEBOX).
Didn't find a solution for that case, so I'm sticking to an overlapped window.
To prevent this movement, just set the flags SWP_NOMOVE and SWP_NOSIZE on the WINDOWPOS structure passed in the lParam
So as a final result, to achieve the wanted behaviour (i.e always behind every other window but always in front of the desktop), the only needed code to add to the doc's sample is the following, placed in the window procedure WindowProc's switch statement :
EDIT : the best place to force the Z order with HWND_BOTTOM thus ensuring the window is always on bottom is also in the WM_WINDOWPOSCHANGING event. Indeed, calling SetWindowPos to force it in the WM_SIZE event when dragging the window over, as I was doing previously, causes some flickering on the window when resizing it, whereas no flickering occurs when setting directly the hwndInsertAfter property of the WINDOWPOS structure in WM_WINDOWPOSCHANGING.
case WM_WINDOWPOSCHANGING:
{
WINDOWPOS* pos = (WINDOWPOS*)lParam;
// Show desktop (Windows + D) results in the window moved to -32000, -32000 and size changed
if (pos->x == -32000) {
// Set the flags to prevent this and "survive" to the desktop toggle
pos->flags |= SWP_NOMOVE | SWP_NOSIZE;
}
// Also force the z order to ensure the window is always on bottom
pos->hwndInsertAfter = HWND_BOTTOM;
return 0;
}
I had the same problem and found a solution via using "autohotkey". autohotkey is a powerful free scripting language for automation in windows that you can download from here. Below scripts are an instance of making sticky notes always on top when you execute it inside a .ahk file (you can copy these codes to a simple notepad and then change .txt to .ahk if you already had installed autohotkey on your computer). by right clicking on .ahk file, you can also see option of Compiling .ahk into .exe which enables you to share it with your other friends too:
#SingleInstance Force
GroupAdd, ontop, Sticky Notes ; you can replace sticky note by any application,
; if you know the name of that application window
; (and its ahk_class for more specific cases).
; for example you can replace "Sticky Notes"
; with "Calculator" to make calculator stay always
; on top.
Loop {
WinWait, ahk_group ontop
WinSet, AlwaysOnTop, On
SoundBeep, 1500
WinWaitClose
SoundBeep, 1000
}
(main codes are credit of mikeyww)

Newly created desktop doesn't receive keyboard events

I have created a small program which launches itself in a new desktop.
HDESK hDesktop = ::CreateDesktop(strDesktopName.c_str(),
NULL, // Reserved
NULL, // Reserved
0, // DF_ALLOWOTHERACCOUNTHOOK
GENERIC_ALL,
NULL); // lpSecurity
::SetThreadDesktop(hDesktop);
Later on, started another application on that desktop using the following lines:
PROCESS_INFORMATION pi = { 0 };
STARTUPINFO si = { 0 };
si.cb = sizeof(si);
si.lpDesktop = &strDesktop[0];
if (FALSE == ::CreateProcess(pathModuleName.file_string().c_str(), L"abc def", NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
return false;
DWORD dwWaitRes = ::WaitForSingleObject(pi.hProcess, INFINITE);
pathModuleName is a self location obtained by GetModuleFileName(NULL).
The newly created application obtains a HWND to another window and sends window messages using the following commands:
// bring window to front
::SetForegroundWindow(hwnd);
// set focus so keyboard inputs will be caught
::SetFocus(hwnd);
::keybd_event(VK_MENU, 0x45, KEYEVENTF_EXTENDEDKEY | 0, 0);
...
So basically application A on desktop DEFAULT is starting application B on desktop X, which obtains an HWND to another application C started on the same desktop X.
My problem is that keyboard events coming from application B on desktop X are not being triggered in application C. Only if I use SwitchDesktop(B), then events are triggered and code is executed properly.
What am I missing?
You are trying to simulate user input on a desktop that is not active on the physical console (screen, mouse, keyboard), which is not likely to work, and why SwitchDesktop() makes it work. According to the documentation:
SwitchDesktop function
Makes the specified desktop visible and activates it. This enables the desktop to receive input from the user.
keybd_event(), mouse_event(), SendInput(), they all simply generate and store input messages into the same input queue that the physical mouse/keyboard post their messages to. The input system does not know the difference between user input and synthesized input when dispatching the input messages to applications.
Raymond Chen touched on that in his blog:
How do I simulate input without SendInput?
SendInput operates at the bottom level of the input stack. It is just a backdoor into the same input mechanism that the keyboard and mouse drivers use to tell the window manager that the user has generated input. The SendInput function doesn't know what will happen to the input. That is handled by much higher levels of the window manager, like the components which hit-test mouse input to see which window the message should initially be delivered to.
He also posted a nice little diagram in another blog article showing where SendInput() sits in relation to the input queue:
When something gets added to a queue, it takes time for it to come out the front of the queue

Restore a minimized window of another application (C++ WinAPI)

I'm working on a C++ program that which launches a .NET Winforms application.
If the app is already running, I want to restore the window instead. I grab the .NET app's window handle and use SetForegroundWindow() to bring to to the front.
This works except when the application is minimized.
I've tried combinations of the following:
ShowWindow(windowHandle, SW_SHOW);
ShowWindow(windowHandle, SW_RESTORE);
and
SendMessage(windowHandle, WM_SYSCOMMAND, SC_RESTORE, 0);
However, when this code is executed, the event becomes stuck. In the tray I see ".NET-BroadcastEventWindow.2.0.0.0.378734a.0" come up as a second window in the tray along with my .NET app, and the app is never restored properly.
This only happens when I try to send a SW/SC_RESTORE or SHOW event. SetForegroundWindow() has no issues.
Has anyone else seen this problem before?
I've had similar problems in the past that I've solved using Get/SetWindowPlacement():
// Ensure that the given window is not minimized.
// If it is minimized, restore it to its normal state.
void EnsureNotMinimized(HWND hWnd)
{
WINDOWPLACEMENT placement;
placement.length = sizeof(placement);
if(!GetWindowPlacement(hWnd, &placement))
return;
BOOL minimized = (placement.showCmd & SW_SHOWMINIMIZED) != 0;
if(!minimized)
return;
placement.showCmd = SW_SHOWNORMAL;
SetWindowPlacement(hWnd, &placement);
}
However, I've only used this for windows that belong to my own application. I don't know if security would allow it to be used on outsiders.

C++ winAPI basics - switching through windows

Not very sure how to explain it in a clear way. Basicaly, the thing is that I'm looking for a method to change a current active window ( Self-produced def., hope it'll be understandable enough ) - window, where the text is directly typed right now. Whooh.
What have I allready discovered is msdn help and SetFocus() or SetActiveWindow(), but it doesn't solve my problem ( or, what is also possible, I'm just using it in a wrong way ).
Simply:
HWND Dest = GetFocus();
... //Some moving around on the 'alt-tab level' :-|
SetFocus(Dest);
Doesn't set the Dest window active again.
Please, excuse for the newbie questions, hope it won't take much time from you. Thanx!
SetFocus() does not bring a window to the top. It just sets the keyboard focus.
SetActiveWindow() on the other hand brings the specific window on top, but only if the application that calls it is also the application that owns it. ( according to the documentation).
When you say you did try SetActiveWindow() what do you mean? How did it fail? What results did it produce?
Another function you can try is SetForegroundWindow() in case you want to activate a window belonging to another application but this has its problems as you see from below (directly from 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.
Try using the SetForegroundWindow function.
Do however note that there are limitations on this, which are explained on the MSDN page remarks sections and I've copied here.
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.
What that means is that if you don't own the current foreground process (which is probably the case when the user tabs around) then you can't set a new foreground window.
There are several hacks around (google SetForegroundWindow and you will find them) but they are hacks and not a good idea - let the user decide what is in the foreground! (also as Raymond Chen explains on his blog here the hacks can often cause a program to hang)