May, 2008

  • The Old New Thing

    Apparently I've been promoted by mistake all these years


    A few years ago, this posting on the secret to getting promoted caught my eye.

    After several years at Microsoft, I had an epiphany today. I came to the realization that the best way, rather the only way -- to get promoted is to demonstrate the ability to hire and retain top talent. Other factors do go a long way but without the ability to hire, you are going to reach your glass ceiling sooner than you think.

    I have done absolutely nothing to demonstrate this ability, and if I managed to hire or retain anybody, it was purely by accident and for that I apologize.

    As far as I'm aware, I've never done anything listed in the article as "the only way" to get promoted. I must have been promoted by mistake. (And I suspect Mini-Microsoft might have a thing or two to say to the claim that the only way to get a promotion at Microsoft is to hire, hire, hire.)

    Yes, I know that the phrase "the only way" was written for rhetorical effect and is not to be taken literally. I'm making a joke, people!

    And as it so happens, I also took the text out of context. The target audience for the remarks was senior management, not dorky programmer types like me. So, strike two.

    Aftermath: After reading this entry in my "upcoming entries" blog queue accessible only to Microsoft employees, a fellow employee emailed me to say that this Web site was a factor in deciding to come to Microsoft, so my claim that I have done absolutely nothing is incorrect. I wrote back, "Well, maybe so, but it's also the case that I haven't been promoted since you were hired."

    The response: "I guess I must not be top talent."

  • The Old New Thing

    Email tip: If they didn't get your message, then they won't know that they didn't get it


    I illustrate this point with a conversation that actually happened:

    Person 1: "Did you get my email?"

    Person 2: "Which one?"

    Person 1: "The last one."

  • The Old New Thing

    Psychic debugging: Why does ExitProcess(1) produce an exit code of zero?


    Here's a question that came from a customer. By now, you should already have the necessary psychic powers to answer it.

    Our program calls ExitProcess(1) to indicate that it exited unsuccessfully. The process that launched our program waits for the program to exit and then calls GetExitCodeProcess to retrieve the exit code. The function succeeds, but the exit code is zero! How can this be?

    Hint: Read about how processes terminate on Windows XP.

  • The Old New Thing

    News flash: It's dangerous to drive a car if you are blind


    The BBC reminds us that if you have no eyes, it's dangerous for you to drive a car.

    Follow-up: The man was given a suspended jail sentence and—I am not making this up—has been banned from driving for three years.

    Three years?

  • The Old New Thing

    If you pass invalid parameters, then all bets are off


    Alun Williams pointed out that if you pass invalid parameters to DeferWindowPos, it does not destroy the HDWP. Well, yeah, because if you pass invalid parameters, then all bets are off.

    Different functions perform different degrees of parameter validation; the degree to which this is done is typically guided by security concerns. Information that crosses security boundaries must be fully-validated, whereas a call to an in-process function has very little in the way of security obligations with respect to invalid parameters, since a bad caller could just mess with the in-process function directly; no need to try to "trick" it with invalid parameters.

    In practice, most functions that perform parameter validation go something like this:

     if (any parameter is invalid) {
       signal invalid parameter error in an appropriate manner
     } else {
       actually do something

    (In some cases, the validation code is not even written by a human being. Instead, there's a script that parses the header files and autogenerates the validation code.)

    If there is an invalid parameter, the entire operation is typically abandoned. Because, after all, how can you expect a function even to get off the ground when it doesn't have all its parameters? I mean, how can the DeferWindowPos destroy the HDWP when it fails to validate its parameters, if the invalid parameter might be the HDWP?

    Regardless of the degree to which parameter validation occurs, if you pass invalid parameters, then (generally speaking) there are no guarantees. Passing valid parameters is part of the basic ground rules for programming. If you break your end of the deal, then the function is under no obligation to hold up its end.

  • The Old New Thing

    Canoeing around the Washington Park Arboretum


    This weekend is Opening Day of the Seattle boating season. Even if you don't own a boat, you can enjoy the warmer weather and rent a canoe and paddle around the Arboretum. (Don't do it on Opening Day, though.)

    Here's a nice map courtesy of the Northwest Outdoor Center. Pay heed to avoid the green section marked Caution. I made a wrong turn and ended up in the Caution area, and I found myself slogging through very shallow water.

    Assuming you avoid the shallows, it's a very pleasant experience. Commune with the ducks and other birds. Pull over at one of the beaches and take a lunch break (assuming you remembered to pack lunch). Wave at the cars stuck on the 520 bridge.

    Bonus reading material: The Miracle of Foster Island.

  • The Old New Thing

    You didn't know you could add properties by atom, and it's a good thing you didn't know


    As I noted a few days ago, there is weirdness associated with properties added by atom. This weirdness stems from the fact that adding properties by atom is really a hole in the original implementation rather than something designed on purpose.

    The original 16-bit code for adding and removing properties went roughly like this:

    BOOL SetProp(HWND hwnd, LPSTR pszName, HANDLE hValue)
        ... let's look only at the part that adds a new property ...
        ATOM atm = HIWORD(pszName) ? GlobalAddAtom(pszName) : LOWORD(pszName);
        if (atm == 0) return FALSE;
        ... add the atom "atm" to the property list ...
    HANDLE RemoveProp(HWND hwnd, LPSTR pszName)
        ATOM atm = HIWORD(pszName) ? GlobalFindAtom(pszName) : LOWORD(pszName);
        if (atm == 0) return NULL;
        ... look for the atom "atm" in the property list and remove it ...
        if (!found) return NULL;
        // clean up the atom
        if (HIWORD(pszName)) GlobalDeleteAtom(atm);
    void CleanPropertiesWhenWindowIsDestroyed(HWND hwnd)
        for (each property on the window) {
            if (atm >= MAXINTATOM) GlobalDeleteAtom(atm);
        .. delete memory used for recording properties ...

    First, let's look at properties set and removed via integer atoms. These are simple: When setting the property, we just add it to the property list, and when removing the property, we remove it. Nothing fancy going on here.

    Similarly, there's nothing particularly exciting going on if a property is set and removed by name. When setting the property, we use GlobalAddAtom to convert the string to an atom (incrementing the reference count), and when removing it, we use GlobalDeleteAtom to clean it up (decrementing the reference count and removing the atom if the reference count goes to zero).

    Finally, when a window is destroyed with outstanding properties, we clean them up by calling GlobalDeleteAtom on all the string atoms, counteracting the GlobalAddAtom we performed when we added the property.

    So what's the big deal? Looks great, right?

    See if you can find the hole in this implementation.

    Hint 1: There are actually three ways of adding and removing properties from a window, not the two I led you to believe.

    Hint 2: What happens if you mix and match these three methods?

    Hint 3: What happens to each of the three types of properties when the window manager is forced to clean them up?

    These problems with properties were fixed a long time ago, but old-timers remain wary of adding named properties by string atom. It's one of those superstitions.

  • The Old New Thing

    Why every advertising agency needs to have a review panel of twelve-year-old boys


    The Office of Government and Commerce needed a new logo, and they hired a design firm to develop one for £14,000. The conversations between the design company and their client may have gone something like this:

    Designer: Okay, so here are some ideas we came up with.

    Client: I don't like any of them. The lines are too thick. I want something lighter, more friendly and less bureaucratic.

    Second meeting.

    Designer: Here are some variations that use thinner lines.

    Client: Nope, these are all still ugly. Give me something with more circles. Less angular.

    Third meeting.

    Designer: We came up with some variations on your circle idea.

    Client: No, we can't use these. They have colour in them. A coloured logo would make our letterhead much more expensive.

    And so on. Finally, the design team comes up with something the client approves of. Now it's time to order the mousepads and unveil the logo.


    A friend of mine remarked, "This is why every advertising agency needs to have a review panel of twelve-year-old boys."

  • The Old New Thing

    Strange uses for window class atoms


    When you register a window class (with the RegisterClass or RegisterClassEx function), you get an ATOM back. What use is this atom?

    Not much.

    You can use this atom in many places where a window class name can be used; just convert it to a string with the MAKEINTATOM macro. Let's change our scratch program to illustrate:

    ATOM g_atmClass;
        g_atmClass = RegisterClass(&wc);
        if (!g_atmClass) return FALSE;
    int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,
                       LPSTR lpCmdLine, int nShowCmd)
            hwnd = CreateWindow(
                MAKEINTATOM(g_atmClass),        /* Class Name */
                "Scratch",                      /* Title */
                WS_OVERLAPPEDWINDOW,            /* Style */
                CW_USEDEFAULT, CW_USEDEFAULT,   /* Position */
                CW_USEDEFAULT, CW_USEDEFAULT,   /* Size */
                NULL,                           /* Parent */
                NULL,                           /* No menu */
                hinst,                          /* Instance */
                0);                             /* No special parameters */

    We save the atom returned by the RegisterClass function and use it (in the form of a MAKEINTATOM) in place of the class name. if you run this program, you'll see that it works exactly the same as the old version that used the class name. The class atom is valid as long as the class remains registered.

    Functions that accept a MAKEINTATOM as the class name include CreateWindow, FindWindow, GetClassInfo, and UnregisterClass (and the Ex versions of them).

    Why would you do this?

    Well, there really isn't much reason. The string name works just as well as the atom, so the atom is just one more thing to keep track of. However, even though you don't use it, you have to be aware that other people might. For example, the lpszClass member of the CREATESTRUCT structure is usually a pointer to a string, but it could be a MAKEINTATOM if somebody decided to pass an atom instead of a string to CreateWindow. Those of you who've read the first Bonus Chapter of my book are already familiar with the program that crashed when somebody created a window via an atom.

    There is one interesting thing you can do with the atom: If you have a valid class atom, you can quickly tell whether a window belongs to that class by checking the window word for the atom:

    if (GetWindowWord(hwnd, GWW_ATOM) == atom) ...

    This technique saves you the trouble of calling GetClassName and then doing a string comparison, reducing it instead to an integer comparison. This technique makes it very simple to write a TestIfWndIsDialog function:

    BOOL TestIfWndIsDialog(HWND hwnd)
      return GetWindowWord(hwnd, GWW_ATOM) == (ULONG_PTR)WC_DIALOG;

    Exercise: Discuss the limitations of the above TestIfWndIsDialog function.

Page 4 of 4 (39 items) 1234