September, 2003

  • The Old New Thing

    Why do some people call the taskbar the "tray"?


    Short answer: Because they're wrong.

    Long answer:

    The official name for the thingie at the bottom of the screen is the "taskbar". The taskbar contains a variety of elements, such as the "Start Button", a collection of "taskbar buttons", the clock, and the "Taskbar Notification Area".

    One of the most common errors is to refer to the Taskbar Notification Area as the "tray" or the "system tray". This has never been correct. If you find any documentation that refers to it as the "tray" then you found a bug.

    In early builds of Windows 95, the taskbar originally wasn't a taskbar; it was a folder window docked at the bottom of the screen that you could drag/drop things into/out of, sort of like the organizer tray in the top drawer of you desk. That's where the name "tray" came from. (Some might argue that this was taking the desktop metaphor a bit too far.)

    Artist's conception (i.e., Raymond sat down with Paint and tried to reconstruct it from memory) of what the tray looked like at this time:

    The tray could be docked to any edge of the screen or it could be undocked and treated like any other window.

    Then we ditched the tray and replaced it with the taskbar. We went through a doc scrub to change all occurrences of "tray" to "taskbar". If you go through the shell documentation, you should not find the word "tray" anywhere.

    A little while later, we added notification icons to the taskbar.

    I think the reason people started calling it the "system tray" is that on Win95 there was a program called "systray.exe" that displayed some icons in the notification area: volume control, PCMCIA (as it was then called) status, battery meter. If you killed systray.exe, you lost those notification icons. So people thought, "Ah, systray must be the component that manages those icons, and I bet its name is 'system tray'." Thus began the misconception that we have been trying to eradicate for over eight years...

    Even worse, other groups (not the shell) picked up on this misnomer and started referring it to the tray in their own documentation and samples, some of which even erroneously claim that "system tray" is the official name of the notification area.

    "But why do you care? That's what everybody calls it now, may as well go with the flow."

    How would you like it if everybody started calling you by the wrong name?

    Summary: It is never correct to refer to the notification area as the tray. It has always been called the "notification area".

  • The Old New Thing

    Why is there no programmatic access to the Start menu pin list?


    We learned our lesson the hard way.

    In Windows 95, we gave programmatic access to the Start menu "Fast items" list - the items that appear at the top of the Start menu above the Programs list. This area was meant for the user to customize with their favorite links, but programs quickly saw the opportunity and spammed themselves into it every chance they got.

    In IE, we gave programmatic access to the Favorites menu, and once again, programs spammed themselves into it.

    In Windows XP we intentionally did not give programmatic access to the bold list of items at the top of the Start menu (the "pin list"). The pin list is for users to put their favorite icons. It is not the place for a program to decide unilaterally, "I am so cool. I am your favorite icon. I just know it. So I'll put myself there because, well, I'm so cool."

    Because we knew that the moment we let people mess with the pin list, everybody would install themselves into it and it would become meaningless (and annoying).

    What's particularly galling are the programs that, as part of their install, decide that they are so cool they want to be everywhere to make sure you don't miss out on the coolest most amazing program ever written in the history of mankind, so they go into the Start menu, into the Fast items, onto the desktop, into the Quick Launch, onto your Favorites, take over as your default autoplay handler, and even hang out as an icon next to the clock on the taskbar just in case you somehow missed all those other places - and each time you run them, they go and recreate those icons and settings in case you "accidentally lost them".

    I hate those programs.

  • The Old New Thing

    An insight into the Windows 95 startup sound


    Doo, dudududingggggg.... ding.... ding... ding...

    In an interview with Joel Selvin at the San Francisco Chronicle, Brian Eno explains.

    Q: How did you come to compose "The Microsoft Sound"?

    A: The idea came up at the time when I was completely bereft of ideas. I'd been working on my own music for a while and was quite lost, actually. And I really appreciated someone coming along and saying, "Here's a specific problem -- solve it."

    The thing from the agency said, "We want a piece of music that is inspiring, universal, blah-blah, da-da-da, optimistic, futuristic, sentimental, emotional," this whole list of adjectives, and then at the bottom it said "and it must be 3 1/4 seconds long."

    I thought this was so funny and an amazing thought to actually try to make a little piece of music. It's like making a tiny little jewel.

    In fact, I made 84 pieces. I got completely into this world of tiny, tiny little pieces of music. I was so sensitive to microseconds at the end of this that it really broke a logjam in my own work. Then when I'd finished that and I went back to working with pieces that were like three minutes long, it seemed like oceans of time.

    The Windows 95 CD contained extra multimedia toss-ins. The ones I remember are a cartoon or two by Bill Plympton, a Weezer music video, and music video of Edie Brickell singing Good Times.

    For some reason, everybody wanted to know the artist from the Good Times video. Nobody was interested in the artists who did any of the other stuff. (Okay, probably nobody asked about Weezer because, well, that's the group right there in the filename.)

    Hint: Right-click and select Properties. That will tell you the artist.

    Oh, and the question nobody asked but I'm going to answer it anyway: The composer of the Windows 95 Easter Egg theme is Brian Orr. Here's his story of how it came to be.

  • The Old New Thing

    The default answer to every dialog box is "Cancel"


    The problem with displaying UI is that people will take every opportunity to ignore it. This story of how people deal with virus warning dialogs (via Don Browning) is a template for how users treat any unexpected dialog: They try to get rid of it.

    We see this time and time again. If you are trying to accomplish task A, and in the process of doing it, an unexpected dialog box B appears, you aren't going to stop and read and consider B carefully. You're going to try to find the quickest path to getting rid of dialog B. For most people, this means minimizing it or clicking "Cancel" or just plain ignoring it.

    This manifests itself in many ways, but the basic idea is, "That dialog box is scary. I'm afraid to answer the question because I might answer it incorrectly and lose all my data. So I'll try to find a way to get rid of it as quickly as possible."

    Here are some specific examples, taken from real customers:

    • "How do I make this error message go away? It appears every time I start the computer."

      "What does this error message say?"

      "It says, 'Updates are ready to install.' I've just been clicking the X to make it go away, but it's really annoying."

    • "Every time I start my computer, I get this message that says that updates are ready to install. What does it mean?"

      "It means that Microsoft has found a problem that may allow a computer virus to get into your machine, and it's asking for your permission to fix the problem. You should click on it so the problem can be fixed."

      "Oh, that's what it is? I thought it was a virus, so I just kept clicking No."

    • "When I start the computer I get this big dialog that talks about Automatic Updates. I've just been hitting Cancel. How do I make it stop popping up?"

      "Did you read what the dialog said?"

      "No. I just want it to go away."

    • "Sometimes I get the message saying that my program has crashed and would I like to send an error report to Microsoft. Should I do it?"

      "Yes, we study these error reports so we can see how we can fix the problem that caused the crash."

      "Oh, I've just been hitting Cancel because that's what I always do when I see an error message."

      "Did you read the error message?

      "Why should I? It's just an error message. All it's going to say is 'Operation could not be performed because blah blah blah blah blah.'"

    When most people buy a car, they don't expect to have to learn how an engine works and how to change spark plugs. They buy a car so they can drive it to get from point A to point B. If the car makes a funny noise, they will ignore it as long as possible. Eventually, it may bother them to the point of taking it to a mechanic who will ask incredulously, "How long has it been doing this?" And the answer will be something like, "Oh, about a year."

    The same goes for computers. People don't want to learn about gigabytes and baud and security zones. They just want to send email to their friends and surf the web.

    I myself have thrown out a recall notice because I thought it was junk mail. And computers are so filled with pop-up messages that any new pop-up message is treated as just another piece of junk mail to be thrown away.

    Automobile manufacturers have learned to consolidate all their error messages into one message called "Check engine". People are conditioned to take the car in to a mechanic when the "Check engine" light goes on, and let the mechanic figure out what is wrong. Can we have a "Check engine" light for computers? Would it be feasible?

  • The Old New Thing

    How to recognize different types of timestamps from quite a long way away


    The great thing about timestamps is that there are so many to choose from. Sometimes, while debugging (or reading incomplete documentation) you'll find a timestamp and wonder how to convert it into something readable. Here are some tips.

    We will use November 26, 2002 at 7:25p PST as our sample time.

    UNIX timestamps are in seconds since January 1, 1970 UTC. It is a 32-bit number, the only 32-bit number in common use as a timestamp.

    November 26, 2002 at 7:25p PST = 0x3DE43B0C.

    If it's a 32-bit value starting with "3", it's probably a UNIX time. (The "3" era began in 1995 and ends in 2004.)

    To convert these values to something readable, you have several choices.

    The C runtime time_t value is the same as a UNIX timestamp, so you can use the ctime() function, for example.

    This is the time format used by the C runtime and by the Windows NT event log.

    Number two: The Win32 FILETIME

    Win32 FILETIME values count 100-nanosecond intervals since January 1, 1600 UTC. It is a 64-bit number.

    November 26, 2002 at 7:25p PST = 0x01C295C4:91150E00.

    If it's a 64-bit value starting with "01" and a letter, it's probably a Win32 FILETIME. The "01A" era began in 1972 and the "01F" era ends in 2057.

    To convert these values to something readable, you can use the FileTimeToSystemTime() function followed by GetDateFormat() and GetTimeFormat().

    Number three: The CLR System.DateTime

    Warning: Actual .NET content (I'm sorry). CLR System.DateTime values count 100-nanosecond intervals since January 1, 1 UTC. It is a 64-bit number. These aren't used much yet.

    November 26, 2002 at 7:25p PST = 0x08C462CB:FCED3800. (? somebody check my math)

    If it's a 64-bit value starting with "08" and a letter, it's probably a CLR System.DateTime. The "08A" began in 1970 and the "08F" era ends in 2056.

    To convert these values to something readable, construct a System.DateTime object passing the 64-bit time value as the constructor parameter.

    Number four: The DOS date/time format

    The DOS date/time format is a bitmask:

                   24                16                 8                 0
    +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
    |Y|Y|Y|Y|Y|Y|Y|M| |M|M|M|D|D|D|D|D| |h|h|h|h|h|m|m|m| |m|m|m|s|s|s|s|s|
    +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
     \___________/\________/\_________/ \________/\____________/\_________/
         year        month       day      hour       minute        second
    The year is stored as an offset from 1980. Seconds are stored in two-second increments. (So if the "second" value is 15, it actually represents 30 seconds.)

    These values are recorded in local time.

    November 26, 2002 at 7:25p PST = 0x2D7A9B20.

    To convert these values to something readable, convert it to a FILETIME via DosDateTimeToFileTime, then convert the FILETIME to something readable.

    Number five: OLE Automation date format

    The OLE automation date format is a floating point value, counting days since midnight 30 December 1899. Hours and minutes are represented as fractional days.

    Converting among these formats

    Often there is no direct conversion between two formats; you will have to go through some intermediary formats.

    UNIX timestamp to/from Win32 FILETIME
    Converting a UNIX timestamp to a WIn32 FILETIME is described in KB article Q167297 and a scaled-down version of the article is also available in the Platform SDK. Some high school algebra will get you the reverse conversion.
    Use FileTimeToSystemTime() and SystemTimeToFileTime().
    FILETIME to/from System.DateTime
    Use System.DateTime.FromFileTime() and System.DateTime.ToFileTime().
    OLE date to/from System.DateTime
    Use System.DateTime.FromOADate() and System.DateTime.ToOADate().
    DOS date/time to/from FILETIME
    Use DosDateTimeToFileTime() and FileTimeToDosDateTime().
    DOS date/time to/from SYSTEMTIME
    Parse it yourself.
    SYSTEMTIME to/from OLE date.
    Use SystemTimeToVariantTime() and VariantTimeToSystemTime(), or use VarDateFromUdate() and VarUdateFromDate().
    DOS date/time to/from OLE date.
    Use DosDateTimeToVariantTime() and VariantTimeToDosDateTime().

    Let's see if I can draw a little chart.

    UNIX FILETIME System.DateTime SYSTEMTIME OLE date DOS date

    I'm not sure that chart actually cleared up anything.

    If you allow yourself to use MFC, then there are some more conversions available.

    UNIX time, FILETIME, SYSTEMTIME, or DOS date/time to OLE date format.
    Use the MFC COleDateTime helper object.

    I won't bother trying to add these (unidirectional) arrows to the chart above.

    Brad Abrams' blog followed some of these arrows and produced a cute little formula to convert UNIX time_t directly to System.DateTime.

    Other time formats

    JScript's Date object constructor can construct from an integer representing milliseconds since 1970. This is the same as UNIX time, just multiplied by 1000.

  • The Old New Thing

    Why you can't rotate text

    In a previous entry, I said that the word "Start" disappears because the alternative is worse.

    Somebody in a comment asked, "Why not draw the text vertically?"

    Ah, now you get to learn about the exciting world of vertical text.

    We originally intended to run text vertically in the new XP Start menu. In original designs for the menu, your name ran vertically up the left side of the menu instead of running across the top.

    Rotating text is problematic in languages that traditionally run vertically, such as Chinese. Since you probably don't have Chinese fonts installed, pretend that %, &, and ' are the Chinese characters for your name. In traditional vertical text, it would be written as shown in Example 1 below. Notice that the English text is rotated clockwise. This preserves the top-to-bottom reading order.

    (Amy Smith)
    Amy Smith
    %&' (Amy Smith)
    Example 1 Example 2 Example 3

    As a concession to Western influences, it is permissible to render Chinese characters left-to-right, in which case your name would be written as "%&' (Amy Smith)".

    Compare this to the traditional Western way of rotating text. Text which would normally be rendered as "Amy Smith" is rotated counter-clockwise and rendered as shown in Example 2.

    Now consider what happens if you take a Chinese name rendered the Western way, "%&' (Amy Smith)", then rotate the Western way, resulting in Example 3. Notice that from a Chinese point of view, everything is upside-down! The character that is supposed to be at the top (%) is now at the bottom.

    Windows for many years now has been multilingual. This means that the same underlying code runs regardless of language. Changing a language merely changes the strings being displayed. This means that there can be no language-specific UI. In this case, it means that we can't have separate rotation rules for Chinese as opposed to English or German.

    (And even if we were allowed to have separate rotation rules, we would have to be able to tell whether the name was in the form above or was in the form "Amy Smith (%&')". In this form, we should rotate it as in example 2, since this is an English string with Chinese characters embedded; as opposed to our example above where we had a Chinese string with English characters embedded. Those of you who have seen Arabic and English mixed together get to see punctuation marks bandied about with similar degrees of confusion.)

    Multilingual support also explains why you see things like "1 folder(s)" instead of "1 folder" and "2 folders". Why not have two format strings, one for when the number of items is exactly one, and one for when the number of items is two or more?

    Well, for one, that would significantly increase the number of strings we would have to carry around. (If you say "just add s to make the plural" then you really need to get out more!)

    For two, some languages (such as Slovene) have a "dual" number in addition to singular and plural. The Lahir language has singular (one), dual (two), trial (three), paucal (a few), and plural (many). So now you have to have perhaps five versions of every string that contains a replacable number.

    This also explains why you see a lot of strings of the form "Property: Value" (for example, "Last modified: Monday, September 29, 2003") instead of a phrase ("Last modified on Monday, September 29, 2003"). This is necessary to avoid problems caused by grammar. If you attempt to compose a phrase, you have to worry about subject/verb agreement, gender and number agreement, declensions, all sorts of things that computers aren't good at. The only safe solution is to avoid it entirely and use the "Property: Value" notation instead.

    We did get one special exception to the "grammar independence" rule: Personalized folders. When you view somebody else's "My Documents" folder, it says "Chris's Documents". We made this request to the translators and they worked really hard to make sure that the templates for possessive forms were accurate in all the languages we support. (Fortunately, we didn't have to deal with languages where the form of the template depended on us knowing whether Chris is a man or a woman.)

  • The Old New Thing

    Why does the taskbar default to the bottom of the screen?


    It didn't always.

    The original taskbar didn't look at all like what you see today. It defaulted to the top of the screen and looked something like this.

    This is definitely not what it actually looked like. It has been so long I forgot precisely what it looked like (I didn't realize there was going to be a quiz ten years later), but this captures the basic flavor, at least for the purpose of this discussion.

    The point is that the bar took the form, not of buttons, but of tabs. Each tab corresponded to a running window, which melded into the tab. You switched window by clicking the corresponding tab.

    You can see vestiges of this style in the TCS_BUTTONS style in the tab control. When we switched to the button-look for the taskbar, we still had a lot of switching code based on the tabs metaphor, and it was less work to add a button-look to the tab control than it was to rewrite all the switching code.

    The tabbed look was abandoned for various reasons, one of which was what everybody else has already noticed: If you put the taskbar at the top of the screen, lots of windows end up sliding under it, because they assumed that the usable area of the screen began at (0,0). Other windows would "creep" up the screen because they used GetWindowPlacement to save their window position (which returns workspace coordinates, where (0,0) is the first usable pixel) but use SetWindowPos to restore it (which uses screen coordinates, where (0,0) is the upper left pixel of the primary monitor).

    There were too many apps that kept sliding under the top-docked taskbar so we had to abandon that idea and move it to the bottom.

    It's somewhat disheartening to observe that now, eight years later, apps still mess up their coordinate systems and keep sliding under a top-docked or left-docked taskbar.

  • The Old New Thing

    Scrollbars part 11: Towards an even deeper understanding of the WM_NCCALCSIZE message


    The other form of the WM_NCCALCSIZE message is the complicated one, when the WPARAM is TRUE. In this case, the LPARAM is a pointer to a NCCALCSIZE_PARAMS structure. When Windows sends the WM_NCCALCSIZE message, the NCCALCSIZE_PARAMS structure is filled out like this:

    • rgrc[0] = new window rectangle (in parent coordinates)
    • rgrc[1] = old window rectangle (in parent coordinates)
    • rgrc[2] = old client rectangle (in parent coordinates)

    Notice that the client rectangle is given in parent coordinates, not in client coordinates.

    When your window procedure returns, Windows expects the NCCALCSIZE_PARAMS structure to be filled out like this:

    • rgrc[0] = new client rectangle (in parent coordinates)

    The new client rectangle specifies where the client area of the window should be located, given the new window rectangle.

    Furthermore, if you return anything other than 0, Windows expects the remaining two rectangles to be filled out like this:

    • rgrc[1] = destination rectangle (in parent coordinates)
    • rgrc[2] = source rectangle (in parent coordinates)

    (If you return 0, then Windows assumes that the destination rectangle equals the new client rectangle and the source rectangle equals the old client rectangle.)

    The source and destination rectangles specify which part of the old window corresponds to which part of the new window. Windows will copy the pixels from the source rectangle to the destination rectangle and preserve their validity. The return value of the WM_NCCALCSIZE message specifies how the bits should be matched up if the two rectangles are not the same size. The default behavior is to align them at the top and left edges.

    Let's demonstrate custom valid rectangles with a fresh scratch program. (We'll come back to the scrollbar program.) First, a helper function that computers the "center" of a rectangle.

    void GetRectCenter(LPCRECT prc, POINT *ppt)
        ppt->x = prc->left + (prc->right - prc->left)/2;
        ppt->y = prc->top + (prc->bottom - prc->top)/2;

    Exercise: Why do we use the formula c = a + (b-a)/2 instead of the simpler c = (a+b)/2?

    Here's our new PaintContent function:

    PaintContent(HWND hwnd, PAINTSTRUCT *pps)
        //  For debugging flicker - fill with an annoying color for 1 second
        DWORD dwLimit = GdiSetBatchLimit(1);
        FillRect(pps->hdc, &pps->rcPaint,
        FillRect(pps->hdc, &pps->rcPaint,
        //  Draw "concentric" rectangles
        RECT rc;
        GetClientRect(hwnd, &rc);
        POINT ptCenter;
        GetRectCenter(&rc, &ptCenter);
        int limit = max(rc.bottom, rc.right) / 2;
        rc.left = rc.right = ptCenter.x; = rc.bottom = ptCenter.y;
        for (int i = 0; i < limit; i += 10) {
            InflateRect(&rc, 10, 10);
            FrameRect(pps->hdc, &rc, GetSysColorBrush(COLOR_WINDOWTEXT));

    When debugging flicker problems, it helps to insert intentionally ugly background painting and annoying pauses so you can see what you are painting. Note, though, that when you do this, you also need to call GdiSetBatchLimit to disable batching. Otherwise, GDI will optimize out the redundant fill and you won't see anything special.

    The real work happens inside our WM_NCCALCSIZE handler.

    UINT OnNcCalcSize(HWND hwnd, BOOL fCalcValidRects,
                               NCCALCSIZE_PARAMS *pcsp)
        UINT uRc = (UINT)FORWARD_WM_NCCALCSIZE(hwnd, fCalcValidRects,
                                               pcsp, DefWindowProc);
        if (fCalcValidRects) {
            //  Give names to these things
            RECT *prcClientNew = &pcsp->rgrc[0];
            RECT *prcValidDst  = &pcsp->rgrc[1];
            RECT *prcValidSrc  = &pcsp->rgrc[2];
            // Compute the old and new center points
            POINT ptOldCenter, ptNewCenter;
            GetRectCenter(prcValidSrc, &ptOldCenter);
            GetRectCenter(prcClientNew, &ptNewCenter);
            //  Tell USER how the old and new client rectangles match up
            *prcValidDst = *prcClientNew; // use entire client area
            prcValidDst->left += ptNewCenter.x - ptOldCenter.x;
            prcValidDst->top += ptNewCenter.y - ptOldCenter.y;
            uRc = WVR_VALIDRECTS;
        return uRc;
        /* Add to WndProc */
        HANDLE_MSG(hwnd, WM_NCCALCSIZE, OnNcCalcSize);

    How does this work?

    If fCalcValidRects, then we do extra work to compute our valid rectangles by seeing how much the window content needs to be shifted and shifting the valid destination rectangle by the same amount. USER copies the upper left corner of the valid source rectangle to the upper left corner of the destination rectangle, so shfiting the upper left corner of the destination rectangle lets us adjust where USER will copy the pixels.

    Play with this program: Grab the window and resize it. Observe that the central portion of the window client area is copied from the original window and is not redrawn. This has two benefits: First, there is no flicker. Second, this improves redraw performance since you aren't drawing pixels unnecessarily. This second remark is particularly important when using the Remote Desktop feature, since Remote Desktop has to transfer all drawn pixels over the network to the client. The fewer pixels you have to transfer, the more responsive your program will be.

    Now that we have a better understanding of the WM_NCCALCSIZE message, we can use this knowledge to improve our scrollbars.

  • The Old New Thing

    Why is the readonly property for folders so strange?


    It's actually a signal to Explorer to look harder. It doesn't mean that the directory is read-only.

    If a folder has the Readonly or System flag set, then Explorer will look for a desktop.ini file which describes the folder customizations. For performance reasons, Explorer does this only if the directory has the +R or +S flag. (This is enormously important on slow networks.)

    There are two KB articles on this subject, and I will defer to them for much of the discussion. This is the version that applies to Windows XP and Windows Server 2003. There is also a version that applies to older versions of Windows, although the UseSystemForSystemFolders policy still applies.

    Coders shold use the function PathMakeSystemFolder to mark a folder as requiring special attention from Explorer.

  • The Old New Thing

    You too can dress like Raymond


    Yes, I'm the Raymond that Kraig Brockschmidt is writing about when he discusses "Dress Like Raymond Day".

Page 1 of 4 (38 items) 1234