7

OK, I get it: focus stealing is evil. Or at least it is 99.9% of the time. But I really need to steal the focus reliably on Windows 8, and so far I'm thwarted by the hordes of people insisting focus stealing is always evil.

Scenario: we run a custom application on an ordinary PC running Windows 8.1 (soon to be Windows 10). The screen, keyboard and mouse sit roughly 5m off the ground up some stairs that the forklift operator really shouldn't climb. The one input device they have is a numeric keypad on an extender cable down at their level. Everything they need to do they can do from that keypad... so long as some evil program hasn't stolen our application's focus, or some remote user hasn't logged out and left another application with focus.

The application is essentially a maximised desktop application - it fills the screen (but is not strictly a "full screen" or "topmost" application), and therefore allows other applications to appear in front of it when required. But when the mouse goes idle, we want this application to resume its "normal" position in front of all other applications so that it gets focus and the numeric keypad input will work reliably.

On Windows 7, using SetForegroundWindow() (enabled by AllowSetForegroundWindow() works fine - the application can be brought back to the front and resume focus. On Windows 8, SetForegroundWindow() only results in the taskbar icon flashing, but the application does not regain focus, forcing our user to climb the stairs... where the full keyboard and mouse is too tempting for them not to press buttons they shouldn't, and chaos typically ensues.

So please sir: can our (MFC, desktop) application steal back the focus once the mouse has gone idle for 1 minute, because it is more or less the only application that should normally be running anyway. If that is permitted, how do we steal it reliably?

underscore_d
  • 6,309
  • 3
  • 38
  • 64
omatai
  • 3,448
  • 5
  • 47
  • 74
  • As a thought, launch a topmost window that is unrelated that relays the keyboard commands to the window which you think should get them; have it disappear on mouse movement, and respawn after 1 minute of mouse idle? I have no idea if that would work; the last time I did that kind of hack was windows XP in order to cheat at ProgressQuest (I believe we where responsible for the spawning of the Hall of Infamy) – Yakk - Adam Nevraumont Sep 11 '18 at 23:45
  • The question has to be asked -- how many product cycles until *your* application is the evil program that steals the focus from a cybermagic neurosurgery forklift scalpel operator? – Kerrek SB Sep 11 '18 at 23:45
  • 2
    @omatai You could try setting `HKEY_CURRENT_USER\Control Panel\Desktop\ForegroundLockTimeout` to `0` on said Windows 8.1 machine. – Swordfish Sep 11 '18 at 23:52
  • You might try asking here: https://social.technet.microsoft.com/forums/en-us/home or on superuser. But a different question: that is, "How can I make a Windows 8.1/10 install behave like Windows XP with regard to focus stealing" The question is appropriate here, I just think you might have better luck asking how to fix the problem *as a user* and then working backward from there. – zzxyz Sep 11 '18 at 23:53
  • 2
    I bet this is an X/Y problem: Likely, the forklift operator is using a barcode scanner, that is providing input via the keyboard? That is almost always bad design choice for *this very reason*. Does the scanner have another interface, like serial? It really is easier, more maintainable, more compatible with other software (even though your application is used 99% of the time, there's still that 1%). I have to agree with what you're trying to avoid - don't do this. – Steve Sep 12 '18 at 00:53
  • 2
    @zzxyz How do I programatically pass focus to an application of my choice is a programming question. It could also be a su question. He has an API that used to work, and doesn't work anymore. – Yakk - Adam Nevraumont Sep 12 '18 at 01:02
  • 1
    @Yakk-AdamNevraumont - I totally agree with what you're saying, and wasn't trying to suggest the question was wrong here. I was trying to help broaden the pool of potential answers, because I have a hunch--could be wrong--that more people know how to solve this problem administratively than programatically (old apps that need to continue to behave as before, etc) – zzxyz Sep 12 '18 at 01:11
  • 1
    @Steve - this is not an X/Y problem. There is no barcode scanner. The forklift operator couldn't find barcodes on the stinky crap he is moving if he tried. He really just wants a simple switch to swap from filling one bin to another, but we gave him a numeric keypad as we could foresee limiited additional functions being required in future. That solution worked fine on Win XP and on Win 7; it no longer works on Win 8. Can we make it work? That's the question. – omatai Sep 12 '18 at 01:34
  • You know, there are consumer grade (and possibly industrial grade) USB devices with a single button that you can make do anything you want, including send some kind of signal to your program... – Steve Sep 12 '18 at 01:37
  • 2
    Great: if I can do anything I want, I'd like to steal the focus :-) How do I do *that*? – omatai Sep 12 '18 at 01:39
  • 1
    The cleanest option is to not rely on input focus altogether. Presumably, you know the hardware device your application should respond to. You can set up [Raw Input](https://learn.microsoft.com/en-us/windows/desktop/inputdev/raw-input), filtered to the particular hardware device, and have the message handler invoke the desired application operation. Using the `RIDEV_INPUTSINK` flag allows the caller to receive input, even when the caller is not in the foreground. – IInspectable Sep 12 '18 at 09:30
  • @yak: You cannot reliably relay input in a way you suggested: [Replaying input is not the same as reprocessing it](https://blogs.msdn.microsoft.com/oldnewthing/20121206-00/?p=5903). – IInspectable Sep 12 '18 at 09:33
  • @IInspectable That may be true, but your link talks about replaying not relaying. – Yakk - Adam Nevraumont Sep 12 '18 at 10:18
  • @yak: How do you propose to *relay* input, without *replaying* it? Once your topmost window has handled the input, you don't get a second chance to handle it. – IInspectable Sep 12 '18 at 10:19

3 Answers3

3

Configure hotkeys on numeric keypad (RegisterHotKey).

Pressing a registered hotkey gives you the foreground activation love by Raymond Chen

After you call the RegisterHotKey function to register a hotkey, the window manager will send you a WM_HOTKEY message when the user presses that hotkey, and along with it, you will get the foreground love. If you call SetForegroundWindow from inside your hotkey handler, the foreground window will change according to your instructions.

mklement0
  • 382,024
  • 64
  • 607
  • 775
Daniel Sęk
  • 2,504
  • 1
  • 8
  • 17
  • This looks encouraging, particularly if one can synthesise a hot key and send it... which feels like a legitimate form of cheating. Too busy to try it right now, and also may have an acceptable solution by doing nothing at all... – omatai Sep 17 '18 at 22:08
  • 1
    It seems that the chromium project is using this technique, too: https://cs.chromium.org/chromium/src/ui/base/win/foreground_helper.cc?sq=package:chromium&dr=C&g=0&l=37 – phimuemue Jul 11 '19 at 13:00
0

I would try it in this way:

  1. Setup a timer in you application. That checks GetForegroundWindow on a regular basis.
  2. If GetForgroundWindow does not belong to your process (GetWindowThreadProcessId)
  3. If a different process onws the foreground window use AttachThreadInput and attach your input queue to the input queue of the other process.
  4. Now use SetForegoundWindow and detach the thread input again.
  5. Now you can use SetFocus as needed to control the input focus of your program.
phimuemue
  • 34,669
  • 9
  • 84
  • 115
xMRi
  • 14,982
  • 3
  • 26
  • 59
  • IMHO yes. Depends on this "User Account Control: Only elevate UIAccess applications that are installed in secure locations" security policy setting. More Info here : https://learn.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/user-account-control-only-elevate-uiaccess-applications-that-are-installed-in-secure-locations – xMRi Sep 12 '18 at 10:19
0

Possible solution (with major limitations): do nothing extra; wait.

One of our service technicians observed that on the third or fourth attempt to regain focus using AllowSetForegroundWindow() and SetForegroundWindow() as had been working on Windows 7, Windows 8 finally allowed our application to regain focus. It is not clear what the conditions are that make this work, or if it works reliably, but we have now observed our application regaining focus from beneath Chrome, from beneath another (self-developed) MFC application, and from beneath a third party application - all desktop applications. Approximately 3-4 minutes needed to elapse in each case before focus was surrendered back to our (desktop) application.

However, we have not witnessed it regain focus from beneath metro applications, and nor do we expect it (e.g. hit the Windows key and leave the system lingering on the Start screen).

In our (restricted) situation, we are willing to take the gamble that our users will not launch a metro application that obscures our desktop application, at least not without restoring our application, since their business relies on it. Our main concern is that one of our busy service technicians will log in remotely, get distracted, and carelessly leave one of our desktop utilities with the focus. Waiting 3-4 minutes appears to be a solution to this specific scenario.

omatai
  • 3,448
  • 5
  • 47
  • 74