10

Raymond has blogged about how programs can get/steal the "foreground love" by using RegisterHotkey, which, when invoked, will transfer the foreground-ness to your application.

Attempts to do this manually fail miserably (e.g. using SetForegroundWindow, SwitchToWindow, etc.), because applications must not be able to steal the focus from a user (so that keypresses don't go to the wrong place).

The trouble is, today I noticed something weird:

  1. I try to Safely Remove an external drive.

  2. There is a ~7-second pause.

  3. During the pause, I am vigorously typing inside a window.

  4. Suddenly, a message box steals the foreground-ness from my app, and my typing goes into the message box instead.

Clearly, this isn't using a hotkey mechanism -- and yet, Windows was able to steal the focus from my app.

I really doubt that there is anything like a "backdoor" being used just for this particular purpose (though please correct me if I'm wrong), so, assuming that isn't the case, there must be a way to do this correctly, without using a hotkey mechanism.

So the question is, how is this accomplished?

Note:

Hans noted that the "backdoor" is AttachInputThread, but I'm not really convinced that's what's happening here -- especially since Raymond says that method can cause hangs. Ideas?

Community
  • 1
  • 1
user541686
  • 205,094
  • 128
  • 528
  • 886
  • Btw, if someone can confirm this actually happens (Windows 7), that'd be great, since that would tell me I'm not hallucinating... (though I did try to repeat it, and it seemed to work...) – user541686 May 23 '12 at 07:54
  • What message box are you talking about? I can't repro. – user703016 May 23 '12 at 08:29
  • I expect it's in the grace period after interacting with the "Safely remove the drive" applet. – Deanna May 23 '12 at 10:18
  • 1
    The back door is AttachThreadInput(). – Hans Passant May 23 '12 at 10:53
  • @HansPassant: Whoa that's surprising... I didn't expect the OS to do something like this. Interesting though, thanks for the info! – user541686 May 23 '12 at 14:43
  • @Cicada: It's the message box that tells you the drive is still in use. – user541686 May 23 '12 at 14:43
  • @HansPassant: Wait, I just came across [this post](http://blogs.msdn.com/b/oldnewthing/archive/2008/08/01/8795860.aspx). Doesn't that post say this methods is invalid? – user541686 May 30 '12 at 14:36
  • A message box can use MB_SYSTEMMODAL flag to steal the focus and bring the window to top. This is normally used to show critical messages that need immediate attention. – Vishal Jul 09 '12 at 06:42
  • I don't think there's anything special about the behaviour of the Safely Remove Hardware dialog in this case. I've often noticed that an application that you've launched can take the focus once it has finished initialization, even if that took a relatively long time. – Harry Johnston Jul 13 '12 at 03:41
  • @HarryJohnston: Regarding new processes, MSDN says, *A process can set the foreground window only if one of the following conditions is true: The process was started by the foreground process.* That's very interesting... are you saying that's the cause? – user541686 Jul 13 '12 at 03:43
  • It's just a guess. It depends on the interpretation; does it mean the process has to have been started by the process that is currently the foreground process, or that the process has to have been started by the process that was the foreground process at the time? Empirically, it would appear to be the latter, but unless someone from MS speaks up we're just speculating. This is one of those cases where the only really useful documentation is the source code. :-) – Harry Johnston Jul 13 '12 at 04:00
  • @HarryJohnston: Haha very true. I just did a test, and it seems like that's not the case -- the mere fact of being started by the process which was at the foreground at some point isn't enough. So I think it's the currently-foreground process... so it probably doesn't explain what's happening. – user541686 Jul 13 '12 at 04:16

3 Answers3

1

I've done some experimenting, and from what I can see this happens if and only if the new window belongs to Windows Explorer. Certain control panels are implemented within Explorer or as Explorer plugins, for example. I could most easily reproduce it by opening Action Center from the Start Menu (with the Start Menu configured to show Control Panel items in a menu).

I suspect, then, that this behaviour is a consequence of the fact that Windows Explorer owns the desktop window, which the GUI treats as a special case.

The only slightly odd thing is that I couldn't reproduce this behaviour with the USB dialog you're talking about, which (when I tried it) was generated by a separate process (an instance of rundll32.exe). That might depend on other factors, though.

Harry Johnston
  • 35,639
  • 6
  • 68
  • 158
  • I don't think the GUI subsystem treats Explorer as a special case, but it's not really out of the question that something along those lines is happening either... – user541686 Jul 13 '12 at 05:44
1

I can't think of a way to test this that isn't more complicated than I have time for right now, but looking closely at the SetForegroundWindow docs, http://msdn.microsoft.com/en-us/library/ms633539(VS.85).aspx , one of the conditions listed under the remarks concerning processes that can set the foreground is:

  • The process received the last input event.

Unless I'm mistaken, Windows Explorer receives all input events in order to check for Hotkeys, other such focus-stealing keypresses, and mouse clicks outside the bounds of the current window, etc.

Because of its permanent "received the last input event" status, Explorer qualifies as something that can set the foreground and can thus cause a MessageBox it causes to be shown to become the foreground without any special functionality or undocumented behaviors.

MikeBMcL
  • 414
  • 3
  • 6
0

As already mentioned, input of windows of different threads is processed independently. AttachThreadInput API allows to share thread states, in particular:

By using the AttachThreadInput function, a thread can attach its input processing mechanism to another thread. [...] This also allows threads to share their input states, so they can call the SetFocus function to set the keyboard focus to a window of a different thread.

Now when you see what window is currently in foreground, if you share your thread state with that of the foreground window thread, your SetFocus will steal focus from there.

CWindow Window = GetForegroundWindow();
if(Window)
{
  const DWORD nWindowThreadIdentifier = Window.GetWindowThreadID();
  const DWORD nThreadIdentifier = GetCurrentThreadId();
  AttachThreadInput(nThreadIdentifier, nWindowThreadIdentifier, TRUE);
  GetDlgItem(IDC_EDIT).SetFocus(); // This succeeds now as we are sharing thread state with foreground window
  AttachThreadInput(nThreadIdentifier, nWindowThreadIdentifier, FALSE);
  m_sAction = _T("Done");
} else
  m_sAction = _T("Nothing to do");

See also: source code snippet, binary

Roman R.
  • 68,205
  • 6
  • 94
  • 158
  • And that method is valid? http://stackoverflow.com/questions/10715550/how-does-windowss-safely-remove-hardware-dialog-get-the-foreground-love/11382608#comment14080942_10715550 – user541686 Jul 08 '12 at 17:39
  • 1
    Stealing focus is not valid in first place. Windows have incrementally made it more difficult for software leaving user to choose which application is to be foreground. I would rather choose a different method to notify user on something, without focus tricks. – Roman R. Jul 08 '12 at 20:20
  • So you're saying Windows is engaging in invalid behavior? – user541686 Jul 13 '12 at 04:17