June, 2006

  • The Old New Thing

    Fumbling around in the dark and stumbling across the wrong solution

    • 68 Comments

    I don't mean to pick on this series of entries, but it illustrates an interesting pattern of stumbling across the wrong "solution".

    The series begins by attempting to trigger the system's monitor blank timeout by posting a message to the desktop window. As we saw earlier, the desktop window is a very special window and as a rule should be avoided, since it won't behave like windows created by applications. In particular, the author tried to post a message to the desktop window. This used to work in the historically open world of the window manager, but security and robustness concerns have come to take priority over compatibility. In Windows XP SP2, the desktop window resists being disabled because programs were doing it inadvertently, and it appears that the desktop also resists having messages posted to it. My guess is that this was done as a way to strengthen protection against shatter attacks. This did improve robustness and stability, but it also broke the article's dubious PostMessage hack.

    Enter round three, wherein the author fumbled around for other windows the monitor blank timeout message could be posted to, and eventually the author found that posting the message to the mysterious window HWND_TOPMOST = -1 seemed to do the trick.

    I knew in the back of my mind that people developed software this way, but the hopeful part of my brain continued to wish that it was merely taking place in a fantasy world. Making up intentionally invalid parameters and seeing what happens falls into the category of malicious goofing around, not in the realm of software engineering and design. Even if you find something that seems to work, you certainly wouldn't design a product around it!

    (Similarly, I've seen people ask questions like "What does message 49251 mean?" This is the reverse case: Seeing a made-up number and attempting to assign meaning to it. Message numbers starting at 0xC000 (decimal 49152) are messages registered via RegisterWindowMessage. The numerical value of the message associated with a registered window message is unpredictable and varies from desktop to desktop. The only guarantee is that it will remain consistent within a single desktop.)

    If you look more carefully at what the author stumbled across, you'll see that the "solution" is actually another bug. It so happens that the numerical value -1 for a window handle is suspiciously close to the value of HWND_BROADCAST:

    #define HWND_BROADCAST  ((HWND)0xffff)
    

    It so happens that internally, the window manager supports (HWND)-1 as an alternative value for HWND_BROADCAST. (I leave you to speculate why.) As a result, what the author actually is doing is broadcasting the monitor power-off message to all top-level windows! As we saw before, broadcasting messages is a very dangerous business, and in this case, the author is just lucky that all the windows on the desktop interpret the message the same way, that it is safe to process the message multiple times, and none of the windows perform any special filtering for that message. (Another author stumbled across the same incorrect "solution" but didn't provide any insight into the process by which the result was arrived at. Yet another author sort of found some issues but didn't quite put them all together.)

    For example, a presentation program might want to suppress monitor power-off when it is the foreground window by trapping the message and turning the monitor back on. If such a program happens to be running, broadcasting the power-off message to all top-level windows would turn off the monitor for all the windows that deferred to system default behavior, but when that presentation program received the message, it would turn the monitor back on. Now you're at the mercy of the order in which the windows process that broadcast message. When the presentation program processes the message, the monitor will turn back on, and if that program happens to be the last one to process the message (say, it got paged out and was slow to page back in), then the monitor will merely blink off and back on.

    The correct solution is not to post messages to random windows. If you want the message to go through window message default processing, create a window and process it yourself. Don't try to trick some other window (or in this case, hundreds of other windows simultaneously) into doing it for you.

  • The Old New Thing

    The continuing phenomenon of size inflation in fast food

    • 67 Comments

    Wendy's is getting rid of "Biggie" and "Great Biggie" size drinks and fries from their menu. Oh, they're still offering them, just with a different name. What used to be "Biggie" is now "medium" and what used to be "Great Biggie" is now "large". Even the "small" drink is a massive 20 ounces, or two and a half FDA servings.

  • The Old New Thing

    Why can't you say </script> in a script block?

    • 61 Comments

    Because it ends the script block, of course. Duh, what's so hard about that?

    Because if you have script that generates script, you'll find yourself caught out if you're not careful. For example, you can't say

    document.write("<SCRIPT>blahblah</SCRIPT>");
    

    in a script block because the HTML parser will see the </SCRIPT> and conclude that your script block is over. In other words, the script block extends as far as the highlighted section below:

    <SCRIPT>
    document.write("<SCRIPT>blahblah</SCRIPT>");
    </SCRIPT><!-- mismatched tag -->
    

    The parser doesn't understand "quoted strings" or "comments" or anything like that. It just looks for the nine characters "<", "/", "S", "C", "R", "I", "P", "T", and ">". When it sees them, it decides that the script block is over and returns to HTML parsing.

    Why doesn't the parser understand quoted string?

    Well, in order to parse quoted strings, you have to be able to parse comments:

    <SCRIPT>
    /* unmatched quotation mark " ignored since it's in a comment */
    </SCRIPT><!-- you might expect this to end the script block -->
    

    But every language has a different comment syntax. JScript uses /* ... */ and //, Visual Basic uses ', perl uses #, and so on. And even if you got comments figured out, you also would need to know how to parse quoted strings. Perl, for example, has a very large vocabulary for expressing quoted strings, from the simple "..." and '...' to the idiosyncratic qq:...:. And I lied about the JScript comment and quotation syntax; it's actually more complicated than I suggested:

    /"//"</SCRIPT>is this inside or outside quotes?
    

    That first quotation mark is itself quoted and does not count as a "beginning of quoted string" marker. And the // sequence is not a comment marker. The first slash in the // sequence ends the regular expression, and the second is a division operator.

    It would be unreasonable to expect the HTML parser to be able to understand every language both present and future. (At least not until clairvoyance has been perfected.)

    <SCRIPT>
    'is this a quoted string?'</SCRIPT>
    Is this inside or outside the script block?
    '<SCRIPT>' is this a new script block
    or the continuation of the previous one?
    </SCRIPT>
    

    One "solution" would be to require all languages to conform to one of a fixed number of quotation and comment syntaxes. Nevermind that not even JScript conforms to the basic syntax, as we saw above, thanks to the complicated quotation rules implied by regular expression shorthand. And do you really want all HTML parsers to understand perl?

    Another "solution" would be to have the language processor do the parsing and tell the HTML parser where the </SCRIPT> tag is. This has its own problems, however. First, it means that the HTML parser would still have to load the language parser even for DEFER script blocks, which sort of defeats one of the purposes of DEFER. Even worse, it means that a web page that used a language that the system didn't support would become unparseable:

    <SCRIPT LANG="unknown-language">
    Lorem ipsum dolor sit amet,
    ...
    

    If a language parser were required to locate the end of the script block, it would be impossible to parse past this point.

    So how do you work around this aspect of HTML parsing? You have to find an alternate way of expressing the string you want. Typically, this is done by breaking in up into two strings that you then reassemble:

    document.write("<SCRIPT>blahblah</SCRI"+"PT>");
    
  • The Old New Thing

    What happened to the traffic circle at the corner of 156th Ave NE and NE 56th Way?

    • 57 Comments

    Windows Live Local and Google Maps both show a traffic circle at the corner of 156th Ave NE and NE 56th Way, but if you pay the intersection a visit in person, you won't find one. It was replaced with a speed bump in 2005. Why?

    I stumbled across the explanation completely by happenstance. There was a small article in the local newspaper that described an accident that occurred elsewhere in Redmond at a traffic circle. A car was driving down the street in excess of the speed limit and failed to negotiate the circle, resulting in the car going off the road. In the flurry of legal action that ensued, somehow the City of Redmond ended up being held responsible for creating "dangerous driving conditions" or something like that. As a result, the City of Redmond went around removing all the city's traffic circles and replacing them with speed bumps.

    Apparently, in this country, a city can be held responsible for conditions that are dangerous to people who are willfully violating the law by exceeding the posted maximum safe speed.

    Minor league baseball team the Altoona Curve announced a Salute to Frivolous Lawsuit Night promotion for their game on July 2, though it may be that their lawyers subsequently advised against it, since it doesn't appear on their official list of promotions... Perhaps they feared a frivolous lawsuit.

  • The Old New Thing

    Why can't you programmatically reorder the items on the Start menu?

    • 47 Comments

    The classic Start menu and the "All Programs" portion of the Windows XP Start menu permit you to customize the order of the shortcuts that appear there. You can use drag/drop to rearrange them, or force them to be sorted by name. But why is there no programmatic interface to these actions?

    Because the power would be used for evil far more than it would be used for good.

    As noted before, the Start menu is highly contentious screen real estate. If a programmatic interface were given to the order of the items on the Start menu, programs would just use it to force their shortcut to the top (and possibly demote their competition). Once you accept that programs will be doing this to the Start menu, the ability for the user to reorder the items would be lost since programs would just override the user's preferences with their own.

  • The Old New Thing

    The subtle usability considerations of conference nametags

    • 44 Comments

    When you go to a conference or some other event where everybody wears a nametag, pay closer attention to the nametag design. There are many subtle usability mistakes that I see far too often.

    First of all, is your name easy to read? It's called a nametag, after all; the name and affiliation of the wearer should be the most prominent thing on the tag. I've been to events where the most prominent thing on the nametag was the name of the conference itself. Hey, everybody presumably already knows what conference they're attending. It's printed on the agenda sheet, it's printed on the tote bag, it's printed on every sign at the venue, it's even printed on the pens you gave out, for goodness' sake. Tell them something they don't know: Who they are talking to. (Corollary: Don't put the name at the bottom of the tag.)

    Okay, now that you've got the name on the nametag in a position and style where it actually serves its purpose, you have to make sure the tag is visible when worn. Most computer events use a lanyard-style nametag. If the lanyard length is not adjustable, then you have a new problem: You have to make the cord long enough to go around the wearer's head. But once you do that, the cord is now so long that the nametag itself hangs around the wearer's belly-button. This is already awkward enough, but if the conference entails sit-down meetings, the nametag will end up into the wearer's lap. And if you have the meetings at tables, the nametag will disappear beneath the surface of table. A nametag that you can't see isn't doing its job.

    (Diagrams require a VML-enabled browser.)

    Can flip

    Stable

    Great, you have a name on the nametag that people can see, you are keeping the tag visible, you think you're home free. But wait, how is your nametag mounted to the lanyard? Nearly all lanyard nametags I've seen are mounted from a single clip or hole at the top center. With this design, the nametag can easily flip around, pushing the person's name into their chest and showing the nametag's backside to the rest of the world. One solution to this problem is to make the nametag reversible, so that even if it flips, the name is still visible. Another solution is to mount the nametag from two holes, one in each top corner. In this manner, the nametag becomes flip-resistant.


    Just a few little details in nametag design. But you'd be surprised how many people miss them. (The PDC nametags are the only one in recent memory that addressed all three problems.)

  • The Old New Thing

    Lies and statistics: 600,000 Chinese engineers

    • 43 Comments

    Everybody "knows" that China produced 600,000 engineers in 2004 (as compared to 70,000 in the United States), but Carl Bialik at the Wall Street Journal [corrected 9:30am] smelled something funny, so he chased the source of the numbers to see whether this "fact" was indeed true. It wasn't. NPR interviewed a Duke professor whose class undertook their own investigation of these bogus numbers. The US number is really closer to 140,000. The count of Indian engineers? Nobody really knows. Deans of universities don't even know how many colleges belong to their university! China was even stranger.

    The Chinese central government in Beijing had simply decided that 600,000 is the number of engineers they want China to graduate each year. "The government has told the provinces that they have to graduate more engineers, so the provinces tell the government what they want to hear."

    If the central government decrees that the country will produce 600,000 engineers, then by golly, that's what the official statistics will say, what a surprise.

  • The Old New Thing

    Pitfalls of transparent rendering of anti-aliased fonts

    • 41 Comments

    Windows provides a variety of technologies for rendering monochrome text on color displays, taking advantage of display characteristics to provide smoother results. These include grayscale anti-aliasing as well as the more advanced ClearType technique. Both of these methods read from the background pixels to decide what pixels to draw in the foreground. This means that rendering text requires extra attention.

    If you draw text with an opaque background, there is no problem because you are explicitly drawing the background pixels as part of the text-drawing call, so the results are consistent regardless of what the previous background pixels were. But if you draw text with a transparent background, then you must make sure the background pixels that you draw against are the ones you really want.

    The most common way people mess this up is by drawing text multiple times. I've seen programs which draw text darker and darker the longer you use it. We'll see here how this can happen and what you need to do to avoid it. Start with the scratch program and make these changes:

    HFONT g_hfAntialias;
    HFONT g_hfClearType;
    
    BOOL
    OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
    {
     g_hfAntialias = CreateFont(-20, 0, 0, 0, FW_NORMAL, 0, 0, 0,
        DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
        ANTIALIASED_QUALITY, DEFAULT_PITCH, TEXT("Tahoma"));
     g_hfClearType = CreateFont(-20, 0, 0, 0, FW_NORMAL, 0, 0, 0,
        DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
        CLEARTYPE_QUALITY, DEFAULT_PITCH, TEXT("Tahoma"));
     return g_hfAntialias && g_hfClearType;
    }
    
    void
    OnDestroy(HWND hwnd)
    {
     if (g_hfAntialias) DeleteObject(g_hfAntialias);
     if (g_hfClearType) DeleteObject(g_hfClearType);
     PostQuitMessage(0);
    }
    
    
    void MultiPaint(HDC hdc, int x, int y, int n)
    {
     LPCTSTR psz = TEXT("The quick brown fox jumps over the lazy dog.");
     int cch = lstrlen(psz);
     for (int i = 0; i < n; i++) {
       TextOut(hdc, x, y, psz, cch);
     }
    }
    
    void
    PaintContent(HWND hwnd, PAINTSTRUCT *pps)
    {
     int iModePrev = SetBkMode(pps->hdc, TRANSPARENT);
     HFONT hfPrev = SelectFont(pps->hdc, g_hfAntialias);
     MultiPaint(pps->hdc, 10,  0, 1);
     MultiPaint(pps->hdc, 10, 20, 2);
     MultiPaint(pps->hdc, 10, 40, 3);
     SelectFont(pps->hdc, g_hfClearType);
     MultiPaint(pps->hdc, 10, 80, 1);
     MultiPaint(pps->hdc, 10,100, 2);
     MultiPaint(pps->hdc, 10,120, 3);
     SelectFont(pps->hdc, hfPrev);
     SetBkMode(pps->hdc, iModePrev);
    }
    

    This program creates two fonts, one with anti-aliased (grayscale) quality and another with ClearType quality. (I have no idea why people claim that there is no thread-safe way to enable ClearType on an individual basis. We're doing it just fine here.)

    Run this program and take a close look at the results. Observe that in each set of three rows of text, the more times we overprint, the darker the text. In particular, notice that overprinting the anti-aliased font makes the result significantly uglier and uglier!

    What went wrong?

    The first time we drew the text, the background was a solid fill of the window background color. But when the text is drawn over itself, the background it sees is the previous text output. When the algorithm decides that "This pixel should be drawn by making the existing pixel 50% darker," it actually comes out 75% darker since the pixel is darkened twice. And if you draw it three times, the pixel comes out 88% darker.

    When you draw text, draw it exactly once, and draw it over the background you ultimately want. This allows the anti-aliasing and ClearType engines to perform their work with accurate information.

    The programs that darken the text are falling afoul of the overprinting problem. When the programs decide that some screen content needs to be redrawn (for example, if the focus rectangle needs to be added or removed), they "save time" by refraining from erasing the background and merely drawing the text again (but with/without the focus rectangle). Unfortunately, if you don't erase the background, then the text ends up drawn over a previous copy of itself, resulting in darkening.

    The solution is to draw text over the correct background. If you don't know what background is on the screen right now, then you need to erase it in order to set it to a known state. Otherwise, you will be blending text against an unknown quantity, which leads to inconsistent (and ugly) results.

    If you keep your eagle eyes open, you can often spot another case where people make the overprinting mistake: When text in a control (say, a check box) becomes darker and darker the more times you tab through it. This happens when programs don't pay close attention to the flags passed in the DRAWITEMSTRUCT that is passed to the WM_DRAWITEM message. For example, some people simply draw the entire item in response to the WM_DRAWITEM message, even though the window manager passed the ODA_FOCUS flag, indicating that you should only draw or erase the focus rectangle. This is not a problem if drawing the entire item includes erasing the background, but if you assume that the WM_ERASEBKGND message had erased the background, then you will end up overprinting your text in the case where you were asked only to draw the focus rectangle. In that case, the control is not erased; all you have to do is draw the focus rectangle. If you also draw the text, you are doing what the MultiPaint function did: Drawing text over text, and the result is text that gets darker each time it repaints.

  • The Old New Thing

    Understanding what significant digits really mean

    • 40 Comments

    A double-precision floating point number carries fifteen significant digits. What does this really mean?

    I multiplied 0.619207 by 10,000,000 and got 6192069.999999991 instead of 6192070. That's only six significant digits; where's my fifteen?

    Talking about significant digits is really just a shorthand for talking about relative precision. "Fifteen significant digits" means that the representational resolution is one part in 1015. It doesn't mean that the first fifteen digits are correct. (If you spend more time with numerical analysis, you can even see people talking about things like "five and a half significant digits". If the meaning of "significant digits" were literal, how could you have half a digit?)

    The relative error in the above computation is 9 / 6192070000000000 = 1.5 × 10-15, which is consistent with about fifteen significant digits. (And that's assuming the decimal representations are exact, which they aren't.)

    Even if you take a literalist interpretation of significant digits, the values are still good to fifteen digits. Remember that 0.99999... = 1, and therefore the values

    6192069.999999991
    6192069.999999999...

    agree to fifteen significant digits, as promised.

    Now, if you're a hyperliteralist and refuse to accept that 0.99999... = 1, then you are forced to accept that the only possible numbers of significant digits are zero and infinity. Consider a computation whose result is the value 1 exactly, and that the computation is performed to N significant digits (with N > 0). Since you do not accept that 0.9 agrees with 1.0 to even one significant digit, the only values that agree with 1.0 to at least one significant digit must be of the form "one point something". Let's call the result 1 + ε with 0 ≤ ε < 1. Now subtract this result from 2.0, yielding 1 − ε. Again, since you do not accept that 0.9 agrees with 1.0 to even one significant digit, in order for this result to be good to N significant digits (N > 0), the result must be of the form "one point something". Let's call that result 1 + δ with 0 ≤ δ < 1.

    Therefore, we have 1 − ε = 1 + δ and therefore, ε = −δ. Since both δ and ε are greater than or equal to zero, the only way for this equation to be satisfied is to have ε = δ = 0. Consequently, the only number which is equal to 1 to any nonzero number of significant digits (if you subscribe to the hyperliteral definition of significant digits) would be 1 itself. In other words, the only positive number of significant digits is infinity. And I think we all agree that if the only valid numbers of significant digits are zero and infinity, then the whole concept of significant digits would become kind of silly.

    That's why significant digits don't use the hyperliteralist definition.

  • The Old New Thing

    Why did the Add or Remove Programs control panel try to guess all that information?

    • 38 Comments

    As we saw earlier, the "Add or Remove Programs" control panel used several heuristics to attempt to determine things like program size and frequency of user. Why did it bother doing this at all?

    At the time the feature was added, disk space was not cheap like it is today. One of the problems users were having was running out of disk space and not knowing what they could safely delete. Thus was born the Disk Cleanup utility, which attempted to guide the user through various things that could be deleted in order to make disk space available.

    In addition to cleaning up temporary files, you could also remove programs that weren't being used. But how do you know which programs you weren't using? (Maybe you were using a program without realizing it because it ran automatically.) And how do you know how much disk space would be recovered if you removed a program? That's where the program size and frequency of use heuristics came in. By providing this information (or at least trying to), the "Add or Remove Programs" control panel could help users decide which programs to remove.

    Of course, nowadays, with hard drives in the hundreds-of-gigabytes range, disk space has become so cheap as to be nearly free. The need to remove programs to make more disk space available is largely gone, but the feature remains as a vestigial organ.

Page 1 of 4 (34 items) 1234