September, 2003

  • The Old New Thing

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

    • 11 Comments

    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.
    FILETIME to/from SYSTEMTIME
    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 are the rules for GetWindowText so weird?

    • 8 Comments

    Joel Spolsky rightly points out that the rules for GetWindowText exhibit abstraction leakage. Why are the rules for GetWindowText so weird?

    Set the wayback machine to 1983. Your typical PC had an 8086 processor running at a whopping 4.7MHz, two 360K 5½-inch floppy drives (or if you were really loaded, one floppy drive and a 10MB hard drive), and 256KB of memory. [Original entry said 256MB - oops. Thanks to Joe Beda for pointing this out.]

    This was the world of Windows 1.0.

    Windows 1.0 was a coöperatively-multitasked system. No preëmptive multitasking here. When your program got control, it had control for as long as it wanted it. Only when you called a function like PeekMessage or GetMessage did you release control to other applications.

    This was important, because in the absence of a hardware memory manager, you really had to make sure that your memory didn't get ripped out from under you.

    One important consequence of coöperative multitasking is that if your program is running, not only do you know that no other program is running, but you also know that every window is responding to messages. Why? Because if they were hung, they wouldn't have released control to you!

    This means that it is always safe to send a message. You never had to worry about the possibility of sending a message to a hung window, since you knew that no windows were hung.

    In this simpler world, GetWindowText was a straightforward function:

    int WINAPI
    GetWindowText(HWND hwnd, LPSTR pchBuf, int cch)
    {
        // ah for the simpler days
        return SendMessage(hwnd, WM_GETTEXT, (WPARAM)cch, (LPARAM)pchBuf);
    }
    

    This worked for all windows, all the time. No special handling of windows in a different process.

    It was the transition to Win32 and preëemptive multitasking that forced the change in the rules, because for the first time, there was the possibility that (gasp) the window you were trying to communicate with was not responding to messages.

    Now you have the backwards compatibility problem. As I described in my original article, many parts of the system and many programs rely on the ability to retrieve window text without hanging. So how do you make it possible to retrieve window text without hanging, while still giving controls like the edit control the ability to do their own window text management?

    The Win32 rules on GetWindowText are the result of this attempt to reconcile conflicting goals.

    (This same story, with slight changes, also works as a discussion of why DDE works the way it does. But fewer people use DDE nowadays, so the effect is not as dramatic.)

  • The Old New Thing

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

    • 49 Comments

    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

    The World Adult Kickball Association

    "People just want to come out and have fun," Smith said. "A lot of the sports leagues out there, unless you're die-hard, unless you're so serious, people end up getting left out," he said.

    Not so with kickball. "People haven't done it since grade school, so it's not like people have been out there... playing kickball every week for the last 15 years," Smith said. [Seattle Times, 18 August 2003]

    Of course, something as goofy as this must certainly have a web site.

    My kickball story: My first day at a new school I ran the bases backwards.

  • The Old New Thing

    Determining whether your window is covered

    • 0 Comments

    The method described in the previous coding blog entry works great if you are using the window visibility state to control painting, since you're using the paint system itself to do the heavy lifting for you.

    To obtain this information outside of the paint loop, use GetDC and GetClipBox. The HDC that comes out of GetDC is clipped to the visible region, and then you can use GetClipBox to extract information out of it.

    Start with our scratch program and add these lines:

    void CALLBACK
    PollTimer(HWND hwnd, UINT uMsg, UINT_PTR idTimer, DWORD dwTime)
    {
        HDC hdc = GetDC(hwnd);
        if (hdc) {
            RECT rcClip, rcClient;
            LPCTSTR pszMsg;
            switch (GetClipBox(hdc, &rcClip)) {
            case NULLREGION:
                pszMsg = TEXT("completely covered"); break;
            case SIMPLEREGION:
                GetClientRect(hwnd, &rcClient);
                if (EqualRect(&rcClient, &rcClip)) {
                    pszMsg = TEXT("completely uncovered");
                } else {
                    pszMsg = TEXT("partially covered");
                }
                break;
            case COMPLEXREGION:
                pszMsg = TEXT("partially covered"); break;
            default:
                pszMsg = TEXT("Error"); break;
            }
            // If we wanted, we could also use RectVisible
            // or PtVisible - or go totally overboard by
            // using GetClipRgn
            ReleaseDC(hwnd, hdc);
    
            SetWindowText(hwnd, pszMsg);
        }
    }
    
    BOOL
    OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
    {
        SetTimer(hwnd, 1, 1000, PollTimer);
        return TRUE;
    }
    

    Once a second, the window title will update with the current visibility of the client rectangle.

    Polling is more expensive than letting the paint system do the work for you, so do try to use the painting method first.

    (Side note: The reason why part 9 of the scrollbar series is so slow to come out finally struck me as I tried to write it: It's too big. I've split it into parts 9 through 12, with an optional part 13; that may make the little pieces more manageable. Part 9 is written, but I want to hold off posting it until I've got at least through part 12, because something from the later parts may force me to rewrite part 9. A somewhat self-absorbed and rather boring insight into the writing process.)

  • The Old New Thing

    Even the trees are falling for the media's lies

    • 2 Comments

    The White House is doing some renovating of its own (NYT, free registration required), in order to refurbish a section of the lawn that is used by television reporters so they can stand there with a nice picture of the White House behind them.

    But that's not why I found this story interesting.

    Go about halfway down the article: "The light and heat from the television lights, sometimes 14 hours a day, were confusing the trees", causing them to sprout leaves in winter and flower out of season.

    Even the trees are falling for the media's lies.

  • The Old New Thing

    Suburbs make you fat, researchers conclude

    • 2 Comments

    Researchers have determined that living in the suburbs makes you fat. That and all the super-sized value meals.

    Speaking of super-sizing your value meal: people are tearing down their McMansions in order to build... a bigger McMansion (WSJ, paid registration required, sorry).

  • The Old New Thing

    The default answer to every dialog box is "Cancel"

    • 39 Comments

    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?

Page 4 of 4 (38 items) 1234