October, 2010

  • The Old New Thing

    Why does TaskDialog return immediately without showing a dialog?

    • 9 Comments

    A customer reported a problem with the Task­Dialog function.

    We've encountered a strange behavior in the Task­Dialog function. A user reported that when exiting our application, our program played an error beep that didn't appear to be associated with an error. After investigating, we found that the sound is coming from our application trying to display an error dialog before closing by calling Task­Dialog. The error beep is played but no dialog appears.

    Some background on the error condition that we're trying to report: We're calling Create­Window­Ex, and the window procedure creates another window in its WM_CREATE handler. It looks like the original window is destroyed before WM_CREATE is finished, so Create­Window­Ex returns ERROR_INVALID_WINDOW_HANDLE. It's not clear why this is causing Task­Dialog to fail, but this is the only scenario where we see this behavior. All other calls to Task­Dialog work as expected. We know how to fix the original problem with Create­Window­Ex, but we would like to understand what's going on with Task­Dialog in case there's another bug here.

    With a little bit of psychic powers, you can solve this customer's problem too.

    (If you have a good memory, you may have noticed that it's a variation on a previous puzzle. But I get to keep recycling it because every year, a new batch of developers stumbles across the problem.)

  • The Old New Thing

    Wildly popular computer game? The Windows product team has you covered

    • 30 Comments

    In Windows 95, the most heavily-tested computer game was DOOM. Not because the testers spent a lot of time developing test plans and test harnesses and automated run-throughs. Nope, it was because it was by far the most popular game the Windows 95 team members played to unwind.

    It was a huge breakthrough when DOOM finally ran inside a MS-DOS box and didn't require booting into MS-DOS mode any more. Now you could fire up DOOM without having to exit all your other programs first.

    I've learned that in Windows Vista, the most heavily tested game was World of Warcraft. Most members of the DirectX application compatibility team are WoW players, in addition to a large number in the Windows division overall.

    So if you have a wildly popular computer game for the PC, you can be pretty sure that the Windows team will be all over it. "For quality control purposes, I assure you."

    Related story: How to make sure your network card works with Windows.

  • The Old New Thing

    Secret passages on Microsoft main campus

    • 33 Comments

    They aren't really "secret passages" but they are definitely underutilized, and sometimes they provide a useful shortcut.

    At the northwest corner of Building 50, there are two doors. One leads to a stairwell that takes you to the second floor. That's the one everybody uses. The other door is a service entrance that takes you to the cafeteria. If your office is on the second or third floor in the northwest corner, it's faster to use the service hallway to get to the cafeteria than it is to walk to the core of the building and take the main stairs.

    There is a service tunnel that runs from the first floor of Building 86 (entrance next to the first floor cafeteria elevator) through the loading dock to the Central Garage, where you can continue to Building 85 or 84. This is not really any faster than the regular route, but it does have the advantage of being underground and mostly indoors, which is a major benefit when it is cold or raining.

    What is your favorite secret passage at your workplace?

  • The Old New Thing

    On understanding that getting married comes with changes in lifestyle

    • 13 Comments

    A friend of mine who had been married less than a year received a phone call from Adam, one of his still-single friends:

    "Hey, Joe! Irving and I are going into town, hit some bars, hang out at some clubs, wanna come along?"

    My friend replied, "Hang on a second."

    A beat.

    "Nope. Still married!"

    As far as I know, Adam and Irving are still single.

  • The Old New Thing

    Why is the origin at the upper left corner?

    • 32 Comments

    Via the Suggestion Box, Dirk Declercq asks why the default client-area coordinate origin is at the upper left corner instead of the lower left corner. (I assume he also intends for the proposed client-area coordinate system to have the y-coordinate increasing as you move towards the top of the screen.)

    Well, putting the client area origin at the lower left would have resulted in the client coordinate space not being a simple translation of the screen coordinate space. After all, the screen origin is at the upper left, too. Windows was originally developed on left-to-right systems, where the relationship between client coordinates and screen coordinates was a simple translation. Having the y-coordinate increase as you move down the screen but move up the client would have just been one of those things you did to be annoying.

    Okay, so why not place the screen origin at the lower left, too?

    Actually, OS/2 does this, and DIBs do it as well. And then everybody wonders why their images are upside-down.

    Turns out that the people who designed early personal computers didn't care much for mathematical theory. The raster gun of a television set starts at the upper left corner, continues to the right, and when it reaches the right-hand edge of the screen, it jumps back to the left edge of the screen to render the second scan line. Why did television sets scan from the top down instead of from the bottom up? Beats me. You'll have to ask the person who invented the television (who, depending on whom you ask, is Russian or American or German or Scottish or some other nationality entirely), or more specifically, whoever invented the scanning model of image rendering, why they started from the top rather than from the bottom.

    Anyway, given that the video hardware worked from top to bottom, it was only natural that the memory for the video hardware work the same way. (The Apple II famously uses a peculiar memory layout in order to save a chip.)

    Who knows, maybe if the design of early computers had been Chinese, we would be wondering why the origin was in the upper right corner with the pixels in column-major order.

    Bonus chatter: Even mathematicians can't get their story straight. Matrices are typically written with the origin element at the upper left. Which reminds me of a story from the old Windows 95 days. The GDI folks received a defect report from the user interface team, who backed up their report with a complicated mathematical explanation. The GDI team accepted the change request with the remark, "We ain't much fer book lernin."

  • The Old New Thing

    Which ferry should we take from Germany back to Denmark? Oh, it's this one, except for that one word I don't understand

    • 26 Comments

    Writing that entry last Friday reminded me that I never did share my stories of that emergency vacation, save for a cryptic list of learnings. Here's a little story about the ferry crossing.

    Part of the trip involved driving from Copenhagen (København) to Munich (München), which means taking a ferry. (It's faster than driving down the peninsula.) Realizing that we would also have to take the ferry on the return trip on Saturday, we grabbed a ferry schedule during a rest stop in Denmark.

    What we didn't realize until it was time for the return trip was that the schedule was (naturally) in Danish, a language none of us could read or speak. As the closest thing in the car to an expert in northern European languages (it wasn't a close vote), I was called upon to do my best to puzzle out the timetable. The choice was between the Puttgarten–Rødby ferry and the Rostock–Gedser ferry. If we could make it, the Rostock–Gedser ferry would have been a better choice since it is a more direct route.

    I was able to decode that the Puttgarten–Rødby ferry ran every half hour around the clock, whereas the Rostock–Gedser ferry stopped running at 11:30pm. (This sounds like a no-brainer, except that the Puttgarten–Rødby timetable didn't give the actual run times; it just said afgang hver halve time, sejltid 0:45.) We consulted the map, estimated our driving time, and calculated that if we stayed focused and didn't dawdle at the rest breaks, we could make the 11:30pm Rostock–Gedser ferry.

    In the fine print of the schedule I was able to translate that the late-night Rostock–Gedser ferry run does not operate on ... um ... lørdag.

    What's lørdag?

    We decided to play it safe and go to Puttgarten.

    It was a good call, because lørdag is Danish (and as I later learned, Swedish) for Saturday.

    Bonus ferry story: After paying the fare on the Danish side for the morning crossing into Germany, the toll booth agent quickly mumbled, "Syv, sieben, seven" as we pulled away. He said it so quickly and unexpectedly, the English-speaking people in the car didn't hear the "seven" since they tuned out what the guy said once they determined that it wasn't English. I was paying more attention, figuring there was an off-chance the agent would speak German to us, and I picked out the sieben, and then upon mentally rewinding and replaying what he said, I also recognized the "seven."

    But why did he keep saying "seven" in various languages? As we reached the waiting area, we figured it out: Because he was telling us to line up in row number seven.

    Bonus ferry storylet: As we waited for the ferry, I noticed that the car behind us had a sticker saying that it was purchased from... a town just a few miles from where I grew up! How the heck did a car from a small town in New Jersey end up in Denmark? We asked the people standing next to the car—it was a nice day, so people stood outside—and they explained that they bought it from somebody who moved to Denmark from the States, and he brought his car with him. Strange coincidences abound.

    Of course, an even stranger coincidence would have been if I happened to have known the original owner. (Not the case here.)

  • The Old New Thing

    Non-psychic debugging: Why you're leaking timers

    • 22 Comments

    I was not involved in this debugging puzzle, but I was informed of its conclusions, and I think it illustrates both the process of debugging as well as uncovering a common type of defect. I've written it up in the style of a post-mortem.

    A user reported that if they press and hold the F2 key for about a minute, our program eventually stops working. According to Task Manager, our User object count has reached the 10,000 object limit, and closer inspection revealed that we had created over 9000 timer objects.

    We ran the debugger and set breakpoints on SetTimer and KillTimer to print to the debugger each timer ID as it was created and destroyed. Visual inspection of the output revealed that all but one of the IDs being created was matched with an appropriate destruction. We re-ran the scenario with a conditional breakpoint on SetTimer set to fire when that bad ID was set. It didn't take long for that breakpoint to fire, and we discovered that we were setting the timer against a NULL window handle.

    A different developer on the team arrived at the same conclusion by a different route. Instead of watching timers being created and destroyed, the developer dumped each timer message before it was dispatched and observed that most of the entries were associated with NULL window handles.

    Two independent analyses came to the same conclusion: We were creating a bunch of thread timers and not destroying them.

    A closer inspection of the code revealed that thread timers were not intended in the first place. Each time the user presses F2, the code calls SetTimer and passes a window handle it believes to be non-NULL. The timer is destroyed in the window procedure's WM_TIMER handler, but since the timer was registered against the wrong window handle, the WM_TIMER is never received by the intended target's window procedure, and the timer is never destroyed.

    The window handle is NULL due to a defect in the code which handles the F2 keypress: The handle that the code wanted to use for the timer had not yet been set. (It was set by a later step of F2 processing.) The timer was being set by a helper function which is called both before and after the code that sets the handle, but it obviously was written on the assumption that it would only be called after.

    To reduce the likelihood of this type of defect being introduced in the future, we're going to introduce a wrapper function around SetTimer which asserts that the window handle is non-NULL before calling SetTimer. (In the rare case that we actually want a thread timer, we'll have a second wrapper function called SetThreadTimer.)

    I haven't seen the wrapper function, but I suspect it goes something like this:

    inline UINT_PTR SetWindowTimer(
        __in HWND hWnd, // NB - not optional
        __in UINT_PTR nIDEvent,
        __in UINT uElapse,
        __in_opt TIMERPROC lpTimerFunc)
    {
        assert(hWnd != NULL);
        return SetTimer(hWnd, nIDEvent, uElapse, lpTimerFunc);
    }
    
    inline UINT_PTR SetThreadTimer(
        __in UINT uElapse,
        __in_opt TIMERPROC lpTimerFunc)
    {
        return SetTimer(NULL, 0, uElapse, lpTimerFunc);
    }
    
    __declspec(deprecated)
    WINUSERAPI
    UINT_PTR
    WINAPI
    SetTimer(
        __in_opt HWND hWnd,
        __in UINT_PTR nIDEvent,
        __in UINT uElapse,
        __in_opt TIMERPROC lpTimerFunc);
    

    There are few interesting things here.

    First, observe that the annotation for the first parameter to SetWindowTimer is __in rather than __in_opt. This indicates that the parameter cannot be NULL. Code analysis tools can use this information to attempt to identify potential defects.

    Second, observe that the SetThreadTimer wrapper function omits the first two parameters. For thread timers, the hWnd passed to SetTimer is always NULL and the nIDEvent is ignored.

    Third, after the two wrapper functions, we redeclare the SetTimer, but mark it as deprecated so the compiler will complain if somebody tries to call the original function instead of one of the two wrappers. (The __declspec(deprecated) extended attribute is a nonstandard Microsoft extension.)

    Exercise: Why did I use __declspec(deprecated) instead of #pragma deprecated(SetTimer)?

Page 3 of 3 (27 items) 123