Finding and Fixing a Five Second Stall

witness_2_windowskeys
The great thing about programming on Windows is that it is the only commercially viable platform where you can ship software to users without getting approval from a giant bureaucracy (well, perhaps I should say it used to be). The not-so-great thing about programming on Windows is that, well, the Windows API is a horrific nightmare.

Granted, it is not uniformly horrific. It ranges from only slightly scarey (xinput, comdlg32) to full psycho (DirectShow, Event Tracing, TabletPC, etc.). But no matter which part of the spectrum you encounter, programming on Windows tends to be about doing a lot of unnecessary research, experimentation, and debugging due to a confluence of poorly designed APIs.

Since time spent debugging Windows issues isn’t often chronicled, I thought I’d go ahead and describe my experience this Saturday of tracking down a Windows bug in The Witness.

The Symptom

Apparently, not everyone on The Witness team was seeing the symptom. But trust me, it was there.

On both Jon and my machines, there was a period during the initial loading of the game where you couldn’t do things like ALT-TAB back to the debugger. If you set a breakpoint then ran the game, when the breakpoint eventually hit and the debugger came to the front, it would wait about five seconds before it allowed you to actually interact with it. Sometimes, in rare cases, it would go into some sort of psycho mode whereby mouse movements stuttered, and the computer would literally beep at you as the mouse moved, for several seconds. Then everything would return to normal.

While not technically a showstopper, this is the kind of bug that’s worth fixing if you can, because it costs you real development time every day. Five seconds wasted every breakpoint is bad news, and people often underestimate the effects of frustration that can build up over time due to flaky and unpleasant development environments. So I decided to try to track it down.

Because Windows is a closed-source platform, there’s no great way to track down problems of this nature. However, having programmed on Windows for over twenty years now, I have a nose for it, so I felt like I knew the best place to start: since there was definitely a UI blackout somewhere during startup, there had to be something occurring early in the execution of the program that was causing at least that particular symptom. So before doing anything else, I started stepping through the entire startup sequence of the program to see if anything out of the ordinary struck me.

I very quickly came upon this code:

// @@ The hook appears to install properly, but is never invoked :(
keyboard_hook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc,
                                 GetModuleHandle(NULL), 0);

Now, I don’t want to make it sound like the “find the source of the problem” phase of Windows debugging is straightforward. It usually takes a very long time, sometimes multiple days. But as luck would have it, this was literally the first piece of code I suspected, and it turned out to be the culprit.

I commented out the SetWindowsHookEx, ran the program, and poof! All the symptoms were gone.

The Problem

From experience, I’ve learned that it’s always best to fully understand a problem before you fix it. If you just patch over its symptoms but never figure out what the problem really was, it will often come back to haunt you.

In this context, that meant two things: first, I should figure out specifically why the keyboard hook was causing the UI stalls; second, I should determine why the code had been trying to call SetWindowsHookEx in the first place, even though apparently it never quite worked (according to the “is never invoked” comment).

The first part turns out to be obvious in hindsight, but it took me a little while to put all the pieces together and be sure I had a solid explanation for what we were seeing. As with any puzzle, looking at the finished picture is easy, but figuring out what that picture might be when all you have are little pieces coming in one at a time is much more difficult. Here’s the best picture I could come up with, but of course there’s no way to completely verify it external to Microsoft:

Normal Windows hooks are pieces of code which execute in the address space of the eventual message recipient, not the installer of the hook. It is for this reason that, normally, if you want to install a global hook, you must put the hook code in a DLL, because that DLL must be mapped into the address space of every running executable on the machine that can receive Windows messages.

But low-level hooks are completely different. A low-level hook like WH_KEYBOARD_LL is a piece of code that resides in only the process that registers the hook. Windows remembers which thread registered the hook, and when any other executable is about to have a hooked message posted to its message queue, Windows actually waits until it can switch contexts back to the hook thread, run the hook in that context, and then finally deliver the message.

This leads directly to the symptom that we were experiencing. When the game is loading, its main thread isn’t yielding to Windows frequently like it is during normal play. Since the main thread is the one that registered the hook, Windows must wait for that thread to finish all the load-time work it was doing and call a function which yields to Windows in order for Windows to actually call the hook procedure. Since no keyboard processing can occur until the hook procedure has run, the keyboard becomes unresponsive as messages back up waiting to be processed through the hook. This completely explains the UI problems during startup, and fits with the oddity that the mouse never seemed to be affected, just the keyboard.

When the game hit a breakpoint, the keyboard hook couldn’t execute at all because the debugger had halted the thread required to execute it. This would have meant that keyboard input would be completely halted, and the debugger would never be able to receive keystrokes, if it weren’t for the fact that Windows actually uses a timeout on hook calls. If the timeout value is exceeded waiting for the hook thread to become available, the hooking executable is assumed to have crashed, and it will silently remove that hook and continue with normal operation.

That timeout value? Five seconds — right in line with our observed pause (technically, it’s whatever is set in the registry under HKEY_LOCAL_MACHINE\Control Panel\Desktop\LowLevelHooksTimeout, but the default is five seconds).

So, no mystery as to the pathology. But what about the second part? What was the code actually trying to do?

The Windows Logo Keys

Although SetWindowsHookEx might seem like an odd call to make in a game executable, it’s actually quite common. It’s there to prevent the Windows logo keys on modern keyboards from ruining full-screen games. Because the keys are placed in a location that’s easy to hit by mistake, many gamers would accidentally hit them, causing their games to be instantly deactivated by Windows in favor of bringing up the start menu. So widely acknowledged was this problem that, when these keys were first introduced, Microsoft itself published a recommended work-around. From Disabling Shortcut Keys in Games:

This article describes how to temporarily disable keyboard shortcuts in Microsoft Windows to prevent disruption of game play for full screen games… Use a low-level keyboard hook to filter out the Windows key from being processed.

The article goes on to show sample code that is basically the same code that was being used in The Witness. It’s a simple SetWindowsHookEx call with WH_KEYBOARD_LL whose hook routine does nothing but block VK_LWIN and VK_RWIN keys from being processed. The only difference between the code in The Witness and the code in the sample is that The Witness would conscientiously remove the hook when it no longer had focus, and then reinstall the hook when focus was regained. The sample code, by contrast, just set a global variable.

So if this was what the code was doing, why the frowny face? If you look back at the comment, it states very clearly that the hook was never being invoked, even though no error message came back from Windows. And, although I normally remap my Windows keys via the registry, I went ahead and mapped them back to see if The Witness was properly blocking their effects.

Much as the frowny face foretold, it wasn’t.

Obeying the Static Discipline

I don’t know anything about digital circuit design, but I once watched a lecture by Gill Pratt where he explained something called “the static discipline”. In essence, it is the requirement that any component in a circuit must accept voltages with a certain range for “1” and “0”, and then must output its own results in a certain range. These ranges are defined so as to require that components always produce equal or better conditioned voltages than their inputs, thus ensuring that the digital signals being propagated through the circuit don’t degrade into noise due to lots of tiny losses.

For some reason, I took this concept to heart in a programming sense and have found it is a good rule to code by. My version of the static discipline, adapted for software, is that whenever you are making a modification to a piece of code, you should always leave it in a state of stability equal to or better than how you found it. And preferably the latter.

All too often, people go in to fix a problem or add a feature to a piece of code, and they just hammer on it until it does that one new thing. The resulting code is then usually more fragile, less well designed, more unnecessarily complex, etc. To try to prevent myself from having this effect, I try to observe the static discipline.

And so it was with this Windows key situation. I could see what the code was supposed to be doing, and although I could reasonably fix my bug by just commenting out the hook (because it didn’t work), I felt it was the more disciplined thing to do to figure out why the hook wasn’t working, and to implement something that did what it was trying to do. Since there was no obvious bug, I had to start experimenting.

After adding some debug outputs and running Spy to keep an eye on the window messages, the first thing I tried was removing the part of the code that unregistered the hook when his window lost focus. Since I didn’t know exactly how his windowing code worked at all levels, I figured it wasn’t entirely out of the question that it could be getting disabled prematurely.

Although that turned out not to be the case, this experiment did pay off unexpectedly (as many do): although the keyboard hook still never got called when The Witness window had focus, the hook did start getting called when it lost focus. Yes, oddly enough, the hook was working just fine for everyone else’s window, but on the one window where it actually needed to work, it wasn’t working.

This lead to the obvious question, what was The Witness doing differently?

Raw Input

This was a case where having lots of Windows programming experience probably saved a lot of time. Instead of having to experiment blindly, I knew right off the bat that Windows Raw Input probably had something to do with the situation. I’ve debugged lots of Windows front end code in the past, and any time Raw Input, DirectInput, XInput, or anything else with the word “input” in it is involved, you know that things are going to be going a little haywire.

I commented out the device registration call for Raw Input, and lo and behold, the hook started working for The Witness window, no problem. So, although I was completely unable to find any documentation on MSDN that discussed what was going on under the hood, it must be the case that Raw Input processing happens before low-level keyboard hook processing in such as way as to prevent the keyboard hook processing from ever seeing keyboard messages on a Raw Input-registered window. Normally, this wouldn’t be a problem, but in our case, the Microsoft-recommended fix for the Windows logo key problem relies on keyboard hooks. The Witness needs Raw Input to get good low-level relative mouse movements, but it also needs to disable the Windows logo keys; sadly, this is the kind of situation that is all too routine for anyone who programs Windows professionally.

I couldn’t think of any plausible ways that I could convince Raw Input to start using the Windows keyboard hook, since I don’t have access to any of the Windows source code and I have no idea how it works internally. So I focused my efforts on Raw Input.

I experimented with just about everything I could think of. I tried not calling DefWindowProc on WM_INPUT calls, to see if it was the default window processing that was using the Windows logo key events to switch to the start menu. No such luck. I tried using RIDEV_NOLEGACY during Raw Input initialization to see if preventing keyboard messages from being generated would solve the problem. It didn’t. After many failed attempts, and finding no relevant information on the web, I decided to call it a night and sleep on it.

When I woke the next morning, for some reason I remembered the most important thing anyone programming Windows must remember: never, ever trust the documentation. It’s always either inaccurate or incomplete. So when I sat down at my keyboard, the first thing I did was try using the RIDEV_NOHOTKEYS flag when initializing Raw Input.

You see, the previous day I hadn’t bothered to try this flag, because it explicitly says in the documentation that it does not block system-level hotkeys, only application-level hotkeys (like the kind you create yourself with RegisterHotKey). But why was I believing that? That was just something somebody wrote down when they needed to create a documentation page. They probably never even saw the actual code for Raw Input in the first place.

Surprise surprise, RIDEV_NOHOTKEYS worked. After all the fussing, all that had to be done to make The Witness impervious to Windows Logo keys was one little flag during Raw Input initialization. No keyboard hook necessary.

Zero-Sum Game

It’s worth taking a step back for a moment and asking the question, why did all of this happen? Why did I have to spend several hours working on code that has nothing to do with my project, is not valuable in any intrinsic way, and whose specifics are relevant only in a fleeting temporal sense (there will be no need to know anything about RIDEV_NOHOTKEYS when the next user input model for Windows is invariably introduced)?

The answer is the same on Windows as it is on most every other platform that exists today: the platform providers just don’t spend real resources designing and improving their APIs. If there had simply been an API call, WindowIgnoresWindowsLogoKeys(HWND), then everyone would always just call that, and the platform developers and Microsoft could field it and do the right thing, no matter how many times they revised their platform and its infrastructure.

Better yet, way back when they first started catering to games with Windows 95, they could have introduce a call like BeginFullScreenGame(), and that could have done literally everything for you: initialize the window, set up the input stream, prepare the sound device, etc. Then they wouldn’t have had to ever worry about the Windows logo keys at all, because they would have been automatically disabled in any application that initialized itself with that call.

But instead of any of these things, Microsoft chose to ship a new feature (the Windows logo hotkey) without ever thinking about the consequences. Then they patched over its problems by telling developers to install a keyboard hook (SetWindowsHookEx), something that has nothing to do with the actual problem but which was the most expedient way for them to get the problem solved without doing any real work on their platform. Then they broke that method, which they’d previously advocated, when they introduce Raw Input, so (presumably) they introduced a new way to do the same thing (RIDEV_NOHOTKEYS), although they failed to document it. Then they didn’t even bother to publish an obvious tech article explaining this change, or even revise their old documents saying that SetWindowsHookEx was the preferred solution.

As broken as all that sounds, it is absolutely par for the course. On every project, in every discipline, developers just like me spend hundreds of hours doing nothing but diagnosing and repairing problems with the platforms on which we ship our software. It may sound like it’s just “part of the job”, but the pernicious thing about it is that development resources are always finite. External to the platform, development is always a zero-sum game. The hours I spent tracking down this bug were hours that won’t get spent making parts of The Witness better, and there’s nothing me or the team can do about that. If we want to ship a polished project, it’s a sacrifice we have to make.

And it’s a sacrifice that’s not just made by us, but by thousands of teams on thousands of projects every year. The cost of obtuse, easy to misuse APIs is multiplied across all these projects, turning the hundreds of hours we will spend on Witness platform work (across multiple platforms) into hundreds of thousands of hours when you consider that this same work has to be done by every other developer, too. It’s really thousands of zero-sum games being played, all across the industry, and the end result is universally worse software because the underlying platforms take up too much effort.

But there is a way for this not to be a zero-sum game, and that’s if there’s a shift in thinking internal to the platform providers. If Microsoft, for example, started realizing that software on Windows gets better if their APIs are clear, concise, and difficult to misuse, they might start changing their development processes to make API quality a priority. They might try to hire and cultivate good API designers, and have real, talented developers who do nothing but work on the APIs to the internal systems.

If that happened, the relatively small amount of hours spent on this endeavor internal to Microsoft (or Sony, or Apple, or Nintendo, or Google) would have a massively leveraged effect. If nobody has to think about things like the Windows keys again, that’s four hours right there that every software project gets back. That is literally thousands of hours, just that. Don’t need to worry about task switching or window minimization? That’s a day, at least — which makes it thousands of days saved. Straightforward user input? Easy access to camera inputs? Each little thing adds up until every developer is getting weeks of their schedule back to devote to working on their actual software, not to mention all the bugs that wouldn’t slip through the cracks because the developers may not have been experienced enough with the particular platform to know to look for them.

So how do we get there? Honestly, I have no idea. In the meantime, I guess we just keep writing articles like this one that detail the right solutions as we find them, and hope that saves everyone some of the vast amount of time that we all lose to platform-related problems each year. Beyond that, it’s really up to the platform providers to start taking these issues more seriously.

This entry was posted in Development. Bookmark the permalink. Trackbacks are closed, but you can post a comment.

45 Comments

  1. Devin Raposo
    Posted December 18, 2012 at 3:12 pm | Permalink

    Excellent article! Could you explain what you mean by a “keyboard hook”? It would help my understanding of some things you mentioned more clear. I am kind of new to *real* software development. Thanks.

    • Posted December 18, 2012 at 3:21 pm | Permalink

      Windows operates partially on a message architecture, where threads in the system have queues of messages that can come from other threads and so on. These messages are things like WM_MOUSEMOVE, or WM_SIZE, which are messages that tell the thread that one of the windows it has created is having the mouse moved over it, or being resized, respectively.

      Some of these messages, like WM_KEYDOWN, are keyboard events. Normally they begin life somewhere in a device driver that has actually communicated with a keyboard, and Windows will see to it that the message gets moved from that origination point through to the thread that owns the window that currently has the keyboard focus. This is the way that input works on Windows, in a nutshell, although the actual chain of command is much more complicated (as you might imagine).

      One of the things Windows lets applications do is to “hook” into this chain of events in between where the messages originate and where they eventually end up. A “keyboard hook” is a piece of code that you write that you ask Windows to put _in between_ the originator of a message and its recipient. So when the user presses a key on the keyboard, and the device driver sees that, and tells Windows that it’s time to send a keyboard event, instead of Windows putting a WM_KEYDOWN event directly into the queue of some application, it first calls the hook code. The hook code can look at the event and decide what to do with it. One of the things it can do, as was the case in the article, is tell windows _not_ to dispatch the event at all.

      In this way, keyboard hooks can effectively suppress keypresses entirely, so that the application with the keyboard focus never actually sees them.

      Does that make sense?

      – Casey

  2. Q
    Posted December 18, 2012 at 3:18 pm | Permalink

    I have never in my 10 years of PC gaming hit the Windows key by mistake or heard of anybody doing it – and even so, it wouldn’t amount to something repeatable often.

    Seems to me a good waste of time. Low level keyboard hooks in games – now I’ve seen everything!:)

  3. Artfunkel
    Posted December 18, 2012 at 3:24 pm | Permalink

    I have a pet theory: at least half of the reason .Net exists is the opportunity it provided to Microsoft to create a new, sane Windows API without admitting to the awfulness of the C one.

    • Posted December 18, 2012 at 5:11 pm | Permalink

      That could certainly be the reason, but unfortunately the .NET one is even worse, because it’s a poorly designed API _and_ it’s slower.

      – Casey

      • Posted December 20, 2012 at 8:24 pm | Permalink

        Totally disagree. C# and the .NET framework is the best development environment for a huge range of common software that exists today. We use it for all our internal tools – including lots of realtime apps doing heavy-duty stuff. I think C# is one of the best-designed languages in the world, and the .NET framework is one of the most expressive and clear standard libraries around. Performance is only really a problem in situations of serious computation – you wouldn’t use C# for super heavy-duty number-crunching.

        Some object to both based on “bloatware” bugbears but those are irrelevant to anyone who’s not OCD. Hard drives are massive now and RAM is cheap, and the tradeoff for the development time it buys you back (to your point about that!) is absolutely worth it. Our expert C# devs who have decades of C and C++ experience say that their default is everything not on an embedded platform should be C# unless there’s a particularly good exceptional reason to use C/C++, and usually there isn’t.

        • Posted December 22, 2012 at 10:18 am | Permalink

          Alright, hit me. Post the code sample for C# that disables the Windows keys. Is it one line like it should be?

          – Casey

  4. Devin Raposo
    Posted December 18, 2012 at 3:32 pm | Permalink

    Yes, I think I understand. Thanks for the quick reply!

  5. Posted December 18, 2012 at 3:40 pm | Permalink

    At work we came across what I think is the same problem and found that disabling the Visual Studio debug hosting process fixed the problem. I can’t say I understand why (I wasn’t on the project), but those guys haven’t had a problem since.

  6. Michael Bayne
    Posted December 18, 2012 at 3:41 pm | Permalink

    Having just shipped a game on both iOS and Android, I have a long list of API/platform annoyances that I had to deal with on Android, which I did not have to deal with on iOS. Whether you like Apple’s walled garden or not, the APIs on iOS are, by and large, well thought out, do the right thing by default, and usually make it difficult to do the wrong thing. By contrast, many of the APIs on Android are vastly more complex to use, end up doing something annoying for the end user if you don’t take the time to understand them clearly and research how to make them behave sensibly, and exhibit a great deal more “accumulated cruft” as they have evolved over the various Android API revisions. Add to that the annoyance that the two thousand plus devices that are “supported” by my game run hundreds of different versions of Android, each of which has its own uniquely buggy implementation of the Android APIs it purports to support, and you have a situation that’s almost as bad as developing for Windows. :)

  7. Seph
    Posted December 18, 2012 at 3:47 pm | Permalink

    Seeing how much the closed source nature of Windows seems to bother you, how is it on the Linux side of things?

    • Posted December 18, 2012 at 5:13 pm | Permalink

      I can’t really speak to the Linux side of things yet, because I’ve only just begun to work on that platform. Talk to me in a few years when I have more Linux experience and I’ll have a worthwhile perspective on that, hopefully!

      – Casey

      • d
        Posted December 18, 2012 at 9:01 pm | Permalink

        More than anything else I think what’s crucial to great API design is choosing the perfect amount of abstraction. When you stop and really think about them (or even better, try to design your own in an environment without them) you begin to see that a lot of standard library functions are nothing short of sublime. *nixes have kept a lot of that design philosophy to this day, whereas Windows eschewed it from the start. The more cross-platform development you do, the more this shows.

        • d
          Posted December 18, 2012 at 9:06 pm | Permalink

          Eschewed it from the start, that is, apart from implementing the standard library. They didn’t have a lot of choice there (if they did, I’m sure we’d have a read() that took a variable-length context structure with members filled with pointers to an array of fixed-size buffers, or some such nonsense).

  8. Simon
    Posted December 18, 2012 at 4:31 pm | Permalink

    So how do we get there? Honestly, I have no idea.
    I do: Metro apps. (or “Modern apps” or “Windows Store apps”). They used Win8 to redesign the API (WinRT). And I like the new design way better. Mostly well done API design. Especially that they only support async IO calls. Now if just everybody upgraded to Win8…

    • Posted December 18, 2012 at 5:14 pm | Permalink

      I suspect that you an I have very different opinions of what makes a good API if you thnk Windows Run Time (Metro) is a good platform.

      – Casey

      • Alexey
        Posted December 18, 2012 at 6:25 pm | Permalink

        Casey, could you point to an API that you do consider well-designed?

        • Posted December 18, 2012 at 7:13 pm | Permalink

          On the Microsoft side, the one that comes to mind is XInput. It’s pretty reasonable. There’s probably a few things I’d do differently, but it’s not the kind of thing I’d get in a fuss about.

          The only reason it’s “slightly scarey” (as I said in the article) is not because of the API design, but because it has (had? they may have fixed some in more recent versions) a bad bug in it where if you poll a gamepad that isn’t connected, it takes a big CPU hit (ie., see my post about it here: http://mollyrocket.com/forums/viewtopic.php?t=487).

          – Casey

  9. MondSemmel
    Posted December 18, 2012 at 4:34 pm | Permalink

    That comment explains quite a lot of your rants against Windows (and I guess any other OS) on the Jeff & Casey show…

    That situation is a real annoyance, though it’s easy to generalize from programming or MS: No matter what environment you work or live in, you are bound by its flaws, problems, and limits – say politics in general, or tax policy, or (institutionalized or just cultural) racial/gender discrimination, and so on. Or even pesky natural laws like gravity…
    But I guess as long as it’s human-made, we should (and could, in theory if not in practice) try to fix these issues as far as possible.

    But on that point, is there actually any OS that’s genuinely great and hassle-free to program for? I guess if the answer to that one was universally “yes”, then everybody would use it…

  10. Aditya
    Posted December 18, 2012 at 6:29 pm | Permalink

    Understood a lot about windows hook problem.. thanks for this amazing article.. :)

  11. d
    Posted December 18, 2012 at 8:41 pm | Permalink

    Pretty much every back-assward thing you find in Windows can be traced back to a pathological need at
    Microsoft to be different from Unix. Why use forward slashes in paths when you can use backslashes? The time wasted on extra keystrokes escaping backslashes in paths could uncharitably be measured in lifetimes.

    • Alexey
      Posted December 18, 2012 at 9:17 pm | Permalink

      In case of path separators, it was actually Unix that went against the flow. The other OSs of the time used forward slashes for passing arguments to programs, and DOS followed in their footsteps. Windows simply had to be compatible with DOS.

      Really, most backwards things you find in Windows are due to their compatibility efforts.

    • Mike Nelson
      Posted March 18, 2013 at 9:33 am | Permalink

      So true, d.

      # this needs to be escaped 4 times unline the usual 5 times…
      my_dev_env=c:\\\\\\\\dev\\\\\\\\project

  12. incendiary
    Posted December 18, 2012 at 10:05 pm | Permalink

    Are you sure you actually want to disable the Windows key? I use it all the time when playing slow-paced full-screen games to get me back to the desktop while keeping the game running in the background. I can check email or whatever and then get right back to my game. Often it’s the only thing that works to do so (Alt-Tab sometimes doesn’t work and it’s a bit messy when it does).

    I’m just saying, if the game is slow-paced, and the main controls aren’t near the Windows key, some people may actually *want* a functioning Windows key (I’ll accept that I’m probably in the minority here).

    • Posted December 19, 2012 at 9:43 am | Permalink

      While I appreciate the fact that Windows hotkeys tend to be a little obtuse, there are actually already hotkeys that do the same thing as a single press of the Windows key which are not dangerous as far as accidental keystrokes. CTRL-ESC, for example, brings up the Start menu, just as a single press of the Windows key will, and that key combination will still work in Witness. But you are unlikely to ever hit it by accident.

      – Casey

  13. Ed
    Posted December 18, 2012 at 11:31 pm | Permalink

    Great article! I noted a poster that said he never had this problem!???? That’s crazy, I remember hitting the Windows key in a raid and wiped! I literally removed that key from the keyboard! You hit the core of the problem, I can relate to so many issues similar to this in Java, thanks for the article.

  14. Aravind
    Posted December 19, 2012 at 5:47 am | Permalink

    “… whenever you are making a modification to a piece of code, you should always leave it in a state of stability equal to or better than how you found it. And preferably the latter……”

    Love it!

  15. Vance Renadi
    Posted December 19, 2012 at 6:36 am | Permalink

    I agree with you, the only games where I would see removal of the windows key as a feature rather than a flaw is in highly competitive multiplayer games, and I now realize about half the games I ever played weren’t ‘bugged’ but likely removed the capability to do something I see no reason for them to have done.

  16. Degg35
    Posted December 19, 2012 at 3:12 pm | Permalink

    I’m not sure I fully understand how this works, but won’t setting RIDEV_NOHOTKEYS disable *all* of the hotkeys? I.e. will I be stuck in the game unless I exit via the game’s menu? (I hate Quake 3 for doing that to me.)
    If the game had a bug which prevented me from normally quitting the game (e.g. some sort of freeze without Windows realizing and offering me to close it), will I be stuck and have to use the reset button?

    • Posted December 19, 2012 at 11:47 pm | Permalink

      No, RIDEV_NOHOTKEYS does not block CTRL-ALT-DELETE and ALT-TAB, etc., so you still have plenty of ways to get out of the application if for some reason it becomes unresponsive. It’s only the Windows logo keys that are being suppressed here.

      – Casey

  17. Vincent Povirk
    Posted December 19, 2012 at 5:19 pm | Permalink

    I was told recently that explorer uses RegisterHotKey to create those windows key shortcuts. I’m guessing that “system level hotkeys” means things like Ctrl+Alt+Delete (though I am not aware of a single other example).

    If my interpretation is correct, this is a case of the MSDN authors including information that would be obvious to most people, while explaining it so badly that it’s worse than leaving it out. My memories of “Old New Thing” suggest they have a tendency to repeat technical explanations that they don’t fully understand and that weren’t meant to be documentation.

  18. Posted December 21, 2012 at 5:49 am | Permalink

    How about documenting this behaviour on the MSDN page then? There is a community additions section and there are usually loads of information on undocumented/misdocumented features. And yes, sometimes the MSDN pages get fixed because of these comments. So that even if you despise the Windows API, you contribute to its progress instead of ranting about it all :)

    • Posted December 22, 2012 at 10:04 am | Permalink

      Because I don’t get paid to make Microsoft’s products better? They are a $231 billion company. They could hire fifty people to do nothing but search the web for posts like this, check the source code, and update their MSDN pages to have correct information, and it wouldn’t even make a line item in their quaterly report.

      I, on the other hand, am an independent developer on a independent project that squeaks by on the savings of a principal or two. If I’m going to spend my time doing something, it’s going to be improving the community for The Witness, not improving the community of Microsoft, the company that constantly costs _us_ time and resources because they couldn’t be bothered to do their job.

      And that’s why I spend what precious little free time I have writing posts for this blog and answering the comments on it instead of spending my time fixing Microsoft’s erroneous documentation.

      – Casey

      • Chris Subagio
        Posted December 23, 2012 at 2:31 pm | Permalink

        It’s also worth pointing out that this is a contribution anyway: often a webpage like this comes up when you search for an MS API related problem, and judging by the last decade of MSDN, it’s also more likely to stick around and be visible.

        I gave up searching MSDN directly years ago; by default I now google search my problems and if a page ranks higher than the MSDN doc page, 90% of the time it’s a page like this that actually addresses the problem more usefully.

        Thanks for the breakdown, btw. I’ve run into the problem before and had to go the disable the hook route because I didn’t have time to dissect it. If I ever see it again, I’ll know where to come back for reference.

  19. Egon Elbre
    Posted December 23, 2012 at 6:14 am | Permalink

    A tip that a fellow developer gave me for debugging Windows is to use ReactOS/WINE source. The source should have similar behavior and it occasionally has nice comments about weird behaviors.

    • Posted December 23, 2012 at 4:26 pm | Permalink

      Yeah, that seems like a good idea. If a WINE dev has already gone through this process trying to properly emulate Windows, then you can piggyback on that and save yourself a lot of time I bet… I should install the WINE source here on my laptop, it’s a Linux Mint machine.

      – Casey

  20. Jake McKenzie
    Posted December 23, 2012 at 6:45 am | Permalink

    I’m taking my first class in C in the spring at Uni. I found this article to be a really good resource Casey. I lament the future hours I will probably spend going over the same problems you bring up here. I think about all these new things that games have brought that I love in PC gaming like the full screen window, dual monitor support, controller support at the start of the application and it all makes my head spin thinking about how to properly implement it the right way.

  21. Tricia
    Posted December 31, 2012 at 10:52 pm | Permalink

    I wanted to put in my two cents on the Windows hotkey. I actually prefer it not to be deactivated. I frequently want to minimize a full screen game that has no built in minimize feature (Guild Wars 2 is an example of a game that has this feature nicely implemented in the pause screen), and I usually use the Windows hotkey for that. I don’t think I’ve ever had issues with accidental button presses on that key. With it disabled, I’m far more likely to have to shut down the entire game just to Google the solution to some in-game problem I’m facing.

    I realize that accidental presses are an issue for some gamers, but I wanted to suggest that you add a minimize button for convenience’s sake.

    I also wanted to mention that I find these programming articles to be quite interesting, and that I really appreciated your article about Window’s monopoly on releases on Windows 8. I was entirely unaware of the new requirements.

    • Posted January 7, 2013 at 5:07 pm | Permalink

      Tricia: You can always use CTRL-ESC, which was the previous way to do this before the Windows key was added to keyboards. It is a much more sane way for games to allow this behavior, because nobody can really hit CTRL-ESC by accident. It should work in all games, too, as I don’t think there are ways to disable it, although admittedly I have never tried. I, like you, appreciate the need to have a way to switch from a game to the Start menu, and CTRL-ESC seems like a good way to do that, so I would never go out of my way to try to disable it, nor recommend that others do so.

      – Casey

  22. Michael Yacavone
    Posted January 4, 2013 at 8:19 am | Permalink

    I don’t have experience programming on Windows, but apropos of the comment from “d” above, while learning iOS programming in 2012 my brain REALLY liked the level of abstraction in Cocoa. Seems like those NextStep guys took the time to think it through. OTOH, I understand there are plenty of Cocoa APIs that also have teh suck, recently Twitter-elaborated by Wil Shipley: http://twitter.com/wilshipley/status/277920619893510144

  23. Posted February 21, 2013 at 9:28 am | Permalink

    Another workaround issues with accidentally hitting the windows key is implementing your full screen program in such a way so that accidentally alt-tabbing or windows keying out of the game is a very cheap action. IE, it happens instantly. This is commonly achieved using border-less fullscreen. More games should do it.

  24. Carson Kanaan
    Posted March 17, 2013 at 4:10 am | Permalink

    I enjoyed reading this piece, Casey, though I came late to it.

    Your description of how problems germinate in platform vendor’s product, and then bloom in the work of every developer who works above that level squarely captures the scale of the impact:

    it’s a sacrifice that’s not just made by us, but by thousands of teams on thousands of projects every year [...] when you consider that this same work has to be done by every other developer, too. It’s really thousands of zero-sum games being played, all across the industry, and the end result is universally worse software because the underlying platforms take up too much effort.

    In a somewhat-related technical context, I was speaking to a sysadmin and former colleague of mine who worked for a large company, where he was part of a team that managed that company’s 220,000-desktop user base.

    One of their internally-developed frameworks was used by a number of their critical operations management applications, and it had an occasional processing stall that would unreliably surface to impact several thousand users across their global network every couple of weeks.

    People would complain, but because it rarely impacted more than a few persons at a time in any particular local office, no one really had a sense of the scale of the problem across 340 offices on six continents.

    For a long time, it was nearly impossible to focus developer resources on corralling and taming this bug, since the framework was technically nobody’s responsibility anymore.

    A newer developer with little to do his first few weeks became enamored by the bug and set out to calculate the person-hour impact to the company from these cumulative stalls. After adding the needed instrumentation wrapper to a key library, he found that in a given month, there were nearly 44 person-days of wasted time caused by this stall–a staggering amount, and a staggering cost to the company given that some of the people whose work this stall affected were very costly employees.

    After realizing that this was a “hundred-thousand dollar bug”, it was relatively easy to get the people who could fix it to fix it. But the practical fact remains that stuck in the low-level framework, out of sight and out of mind of most application developers, it was impactful in non-obvious ways.

    Thanks again for your writeup.

    If you ever find that the talk by Gill Pratt on “the static discipline” is online anywhere, I’d welcome a link.

    (There was a similar concept espoused by early engineers in the Internet community: “be liberal in what you accept, conservative in what you send out”, which assured that a broad range of user-level applications on the Internet would just ‘work’ despite poorly-formatted messages and protocol violations because even if somebody sent you garbage, it was your responsibility to clean it up before you sent it along to the next recipient.)

Post a Comment

Your email is never published nor shared.

You may use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>