Eventually, nothing is special any more


Commenter ulric suggested that two functions for obtaining the "current" window should exist, one for normal everyday use and one for "special use" when you want to interact with windows outside your process.

I'd be more at ease however if the default behaviour of the API was to return HWND for the current process only, and the apps that really need HWND from other potentially other processes would have to be forced to use another API that is specifically just for that.

This is an excellent example of suggesting something that Windows already does. The special function has become so non-special, you don't even realize any more that it's special.

Originally, in 16-bit Windows, the function for getting the "current" window was GetActiveWindow. This obtained the active window across the entire system. One of the major changes in Win32 is the asynchronous input model, wherein windows from different input queues receive separate input. That way, one program that has stopped responding to input doesn't clog up input for other unrelated windows. Win32 changed the meaning of GetActiveWindow to mean the active window from the current input queue.

In 16-bit Windows, there was only one input queue, the global one. In 32-bit Windows, each thread (or group of input-attached threads) gets its own input queue.

As a result of this finer granularity, when a program was ported from 16-bit Windows to 32-bit Windows, it didn't "see" windows from other programs when it called functions like GetFocus or GetActiveWindow. As every Win32 programmer should know, these states are local to your input queue.

Okay, let's look at what we've got now. GetFocus and GetActiveWindow give you the status of your input queue. In other words, in a single-threaded program (which, if you're coming from 16-bit Windows, is the only type of program there is), calling GetActiveWindow gives you the active window from your program. It doesn't return the active window from another program.¹ Things are exactly as ulric suggested!

Now let's look at the second half of the suggestion. If a program really needs to get a window from potentially other processes, it would have to use some other function that is specifically just for that. And indeed, that's why the GetForegroundWindow function was added. The GetForegroundWindow function is the special function specifically designed for obtaining windows from other processes.

Therefore, we did exactly what ulric recommended, and it still turned into a mess. Why?

Because once you create something special, it doesn't remain special for long.

It may take a while, but eventually people find that the regular function "doesn't work" (for various definitions of "work"), and they ask around for help. "When I call GetActiveWindow, I'm not getting the global active window; I'm just getting the local one. How do I get the global one?" Actually, they probably don't even formulate the question that clearly. It's probably more like "I want to get the active window, but GetActiveWindow doesn't work."

And then somebody responds with "Yeah, GetActiveWindow doesn't work. I've found that GetForegroundWindow works a lot better."

The response is then "Wow, that works great! Thanks!"

Eventually, the word on the street is "GetActiveWindow doesn't work. Use GetForegroundWindow instead." Soon, people are using it for everything, waxing their car, calming a colicky baby, or improving their sexual attractiveness.

What used to be a function to be used "only in those rare occasions when you really need it" has become "the go-to function that gets the job done."

In fact, the unfashionableness of the active window has reached the point that people have given up on calling it the active window at all! Instead, they call it the foreground window from the current process. It's like calling a land line a "wired cell phone".

Requiring a new flag to get the special behavior doesn't change things at all. It's the same story, just with different names for the characters. "GetFocalWindow² doesn't work unless you pass the GFW_CROSSPROCESS flag." Soon, everybody will be passing the GFW_CROSSPROCESS not because they understand what it does but just because "That's what I was told to do" and "It doesn't work if I don't pass it."

Footnotes

¹Assuming you haven't run around attaching your thread to some other program's input queue. This is a pretty safe assumption since the AttachThreadInput function didn't exist in 16-bit Windows either.

²GetFocalWindow is an imaginary function created for the purpose of the example.

[Raymond is currently away; this message was pre-recorded.]

Comments (25)
  1. John says:

    HWND GetActiveForegroundWindowEx2(BOOL bSpecial)

    {

       if(bSpecial)

       {

           return GetForegroundWindow();

       }

       else

       {

           return GetActiveWindow();

       }

    }

  2. Josh says:

    @John: That was a joke, right?

  3. DWalker says:

    You’re right, of course, about the "passing a special flag" solution not being effective in real life.  

    In a perfect world, a flag called "DontUseThisUnlessYouReallyNeedToAndYouKnowWhatYoureDoingAndYouHaveReadTheDocFirst" would actually work and not be abused.

  4. Ben says:

    Who said programs have only a single thread and a single input queue?  In normal circumstances, GetActiveWindow() only returns the current thread’s active window. If you use mutliple threads, you are SOL.

  5. Ulric says:

    <chills> I’m really going to have to start watching what I say :P

  6. I got all excited there for a sec.  GetFocalWindow??  I was like, "wow, what’s this new thing I’ve never heard about??"  And then I saw the footnote.  Day ruined.  Thanks Raymond.  <g>

  7. Alexandre Grigoriev says:

    In the end, it all boils down to a cargo cult programming. Get this example from the web or from MSJ, fudge it till it does what you want, voila. No need to read the docs and learn something.

  8. John says:

    @Josh:

    Obviously :)

    @Ben:

    True, but I don’t think there is much of a use case for this scenario.

  9. Dean Harding says:

    Methinks Ben needs to read the WHOLE post…

  10. Duke of New York says:

    ‘It’s like calling a land line a "wired cell phone"’

    You might be surprised at how many software developers say "forward backslash." There are at least two, enough to surprise the heck out of me.

  11. hexatron says:

    Of course, all this is made thoroughly opaque by the documentation of these functions.

    GetForegroundWindow:

    The GetForegroundWindow function returns a handle to the foreground window (the window with which the user is currently working). The system assigns a slightly higher priority to the thread that creates the foreground window than it does to other threads.

    GetActiveWindow:

    The GetActiveWindow function retrieves the window handle to the active window attached to the calling thread’s message queue.

    Perhaps not the clearest possible distinction…

  12. Chris says:

    This is why getting full, unambiguous documentation for functions out there in good time is so important.

  13. denis bider says:

    This is an example of poor naming. Functionality tends not to be abused as much if it is named appropriately.

    Instead of silently modifying the behavior of GetActiveWindow(), it should have been deprecated and replaced with something like GetOwnActiveWindow() and GetGlobalActiveWindow().

    This would have made things much clearer to users, and would embed a pointer that would help people understand <i>why</i> one function "works better" in some circumstances than the other.

  14. David Levine says:

    Here’s the documentation for GetActiveWindow()…

    "Return Value

    The return value is the handle to the active window attached to the calling thread’s message queue. Otherwise, the return value is NULL. "

    There’s a lot of interpretation and knowledge required to go from that to the understanding that Raymond has of its behavior; several additional lines of explanation would go a long way. The world outside Redmond did not write Windows and we do not have access to the source code; we should not be required to be insiders to write correct code to the Windows API.

  15. kL says:

    In Cocoa Apple solved this problem by making it so incredibly awkward to get system-wide frontmost window (you need old carbon calls or… AppleScript!), that people give up and just use API for getting current process’ frontmost/key window :)

  16. Joe Butler says:

    The problem seems to be a case of people picking the first function that ‘sounds’ like it might do what they are looking for – without first reviewing the total set of window-related functions.  If they discover a function called GetForgroundWindow() and imagine, or more likely, hope this is what they wanted without exploring further then they are simply poor developers.  Whatever Microsoft choose to do, these people are going to screw something up at some point and we’ll have a handful of people with hindsight saying, well of course Microsoft shouldn’t have called it CreateWindow(), they should have called it AttemptToCreateWindowButSomethingMightHappenToPreventSuccess_Remarks_SeeAlso_AttemptToCreateWindowButSomethingMightHappenToPreventSuccessEx()

    There are a few commentators here that seem to think it’s the documentation’s fault that people get it wrong, but I think if someone looks at a function called GetActiveWindow() and GetForegroundWindow() and then sees the documentation has different descriptions, and the remarks section of one refers to a case where you would want to use the other, but can’t be bothered to understand the distinction, then it is down to the programmer who decided to use the first function they find with an sort of right sounding name.

  17. MadQ says:

    @Duke of New York: That’s even worse than "Backward Backslash", which would at least be correct.

  18. Ben says:

    Dean: I’m missing something then.  Each thread gets its own input queue (you can attach them, but that isn’t always acceptable).  Per Raymond, GetActiveWindow() returns the "active" window for the current input queue.  

    The implication is that if the program has multiple input queues, it won’t return the "topmost window in my process".  Rather, it returns the topmost window on the current thread.

    Did I miss something?

  19. Bob says:

    Hooray for cargo cult programming!

    Forget wasting time understanding what you’re doing! Just try random stuff until it "seems" to work, or ask other kids on the net what they’ve found "seems" to work!

    Fortunately, ${NEW_TECHNOLOGY} (be it Java or Python or C# or whatever) is so amazing and easy to use that anyone can write perfect code, and thus will be immune to all the mistakes that were the fault of ${OLD_TECHNOLOGY}.

  20. SuperKoko says:

    Most bad programming practices are language-independent.

    In the end, bad programmers always win!

    Yipee!

  21. Ulric says:

    It’s not really relevant that GetActiveWindow only returns it for the current thread.

    The vast majority of application only have one thread.  Generally the multithreaded app do all UI in a single UI thread.  Furthermore, generally anyone that wants to know the active window would be doing it from the same thread, anyway.  Making an app that uses multiple UI threads is *very* complicated and very much an edge case – and I’ve worked on huge video editing and 3D CAD applications.

    I do agree with some comments above that GetForegroundWindow could have had a better name that implies when you see it that it returns windows that may not belong to you.  It could have used another name or returned the process handle to imply it, etc. It’s a reasonable suggestion — though of course it’s too late now :)

  22. Gabe says:

    Ulric, generally apps that do UI in multiple threads are like Explorer or IE in that they show separate, unrelated windows with each thread. Indeed, I’ve written one myself. In these cases, each thread would only want its own active window.

    It’s hard to imagine a scenario where a process has multiple UI threads and needs one to find another’s active window.

  23. Ken Hagan says:

    MS could change the behaviour of GetForegroundWindow() so that it returned NULL unless the application was on an administrator approved list of "apps allowed to wrest control of the UI away from the end-user".

    But then someone would post some code to the web that added your app to this list at install time. Still, at least a vigilant end-user could remove it afterwards.

    But then, your app would crash and your product support team would tell the end-users "What do you expect? You broke our installation!".

    Gosh that’s a depressing train of thought. Is that the sort of stuff the Windows team have to think about every day? How do they stay sane? Why don’t they all just end up as axe-wielding homocidal maniacs bent on the grizzly extermination of all "application programmers"?

  24. Jim Howard says:

    These api’s usually come up when management tells the windows developer that our application must pop up on top and seize control of the mouse and keyboard from all other processes, because we’re so special.

  25. Required caveat: I am not MS-bashing here; I am well aware of how hard a task is at hand, and *nobody* does it right.  MS has kept a remarkably consistent API over the years.  That is to say – "all software sucks, all hardware sucks"…

    @Ken Hagan:

    "Gosh that’s a depressing train of thought. Is that the sort of stuff the Windows team have to think about every day? How do they stay sane? Why don’t they all just end up as axe-wielding homocidal maniacs bent on the grizzly extermination of all "application programmers"?"

    Probably because they realize that there’s an equal proportion of potential axe-wielding homicidal maniacs bent on the grisly extermination of "those guys who keep changing the rules for Win32 programming"… and that there are more of them than there are Microsoft employees…

Comments are closed.