March, 2006

  • The Old New Thing

    Before you develop a solution, make sure you really understand the problem


    A common obstacle when trying to help people solve their problems is that what people ask for and what they actually want are not always the same thing.

    For technical problems, you often get a question that makes you shake your head in disbelief, but upon closer questioning, you find that the person really doesn't want what they're asking for. What they really want is something else, but they've already "solved" half of the problem and only need your help with the other half—the half that doesn't make any sense. For example, the literal answer to "How do I write a regular expression that matches everything except XYZ" is often a horrible mess, but if you dig deeper, you'll find that they really don't need a regular expression that matches everything except XYZ; they just simplified their problem to "I know, I'll use regular expressions" and ended up creating a bigger problem. (The best solution is often a mix of regular expressions and simple program logic.)

    This problem also exists in user interface design. Rick Schaut describes one case where a user asked for a feature, when what they really wanted was an entirely different feature. Understanding the customer's problem is the first step towards solving it.

  • The Old New Thing

    Make sure to handle carcinogens safely before inhaling a different carcinogen


    One of my colleagues pointed out a sign at an automobile tire center where they use lead weights for balancing wheels. It read (paraphrased):

    Lead has been known to cause cancer. Ensure that you wash your hands before eating or smoking.
  • The Old New Thing

    Solving one problem by creating a bigger problem


    Often, people will not even realize that their solution to a problem merely replaces it with another problem. The quip attributed to Jamie Zawinski captures the sentiment:

    Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.

    For example, in response to "How do I write a batch file that..." some people will say, "First, install <perl|bash|monad|...>". This doesn't actually solve the problem; it merely replaces it with a different problem.

    In particular, if the solution begins with "First, install..." you've pretty much lost out of the gate. Solving a five-minute problem by taking a half hour to download and install a program is a net loss. In a corporate environment, adding a program to a deployment is extraordinarily expensive. You have to work with your company's legal team to make sure the licensing terms for the new program are acceptable and do not create undue risk from a legal standpoint. What is your plan of action if the new program stops working, and your company starts losing tens of thousands of dollars a day? You have to do interoperability testing to make sure the new program doesn't conflict with the other programs in the deployment. (In the non-corporate case, you still run the risk that the new program will conflict with one of your existing programs.)

    Second, many of these "solutions" require that you abandon your partial solution so far and rewrite it in the new model. If you've invested years in tweaking a batch file and you just need one more thing to get that new feature working, and somebody says, "Oh, what you need to do is throw away you batch file and start over in this new language," you're unlikely to take up that suggestion.

    So be careful when you suggest a solution that has a high activation energy. Sure, something could be taken care of by a one-line perl script, but getting perl onto the machine is hardly a one-line endeavor.

  • The Old New Thing

    On the alert for expired food-handling licenses


    Since the lines at warehouse-style stores are always ridiculously long, I like to pass the time by checking out the legally required postings of food-handling and related licenses. Usually, two or three of their licenses have expired. It could be their seafood license, or their raw meat license, or their bakery license, it's always a surprise each time I visit.

    When I point this out to someone, they usually just say, "Oh, um, you'll have to ask the main office." And when I ask the main office, they usually say, "Um, everything is just fine. The store was just slow to post the new licenses."

    In other words, nobody wants to take responsibility for breaking the law. Maybe I should just report them to the health department. Or get some tips from this book.

    Mind you, the bulletin board in our building hasn't updated its license to employ minors since it expired in 2000. I've pointed this out multiple times to our personnel department, and they assure me that they'll get right on it and post the new license, but it has yet to happen. When I sent them a reminder a few days ago, the response was disbelief, because some other "they" was supposed to have taken care of it. We'll see if anything changes.

  • The Old New Thing

    Why doesn't Windows File Protection use ACLs to protect files?


    Windows File Protection works by replacing files after they have been overwritten. Why didn't Windows just apply ACLs to deny write permission to the files?

    We tried that. It didn't work.

    Programs expect to be able to overwrite the files. A program's setup would run and it decided that it needed to "update" some system file and attempt to overwrite it. If the system tried to stop the file from being overwritten, the setup program would halt and report that it was unable to install the file. Even if the operating system detected that somebody was trying to overwrite a system file and instead gave them a handle to NUL, those programs would nevertheless notice that they had been hoodwinked because as a "verification" step, they would open the file they had just copied and compare it against the "master copy" on the installation CD.

    The solution was to let the program think it had won, and then, when it wasn't looking, put the original back.

    Now that Windows File Protection has been around for a few years, software installers have learned that it's not okay to overwrite system files (and trying to do it won't work anyway), so starting in Windows Vista, the Windows File Protection folks have started taking stronger steps to protect system files, and this includes using ACLs to make the files harder to replace. Presumably, they will have compatibility plans in place to accomodate programs whose setup really wants to overwrite a file.

  • The Old New Thing

    Basic ground rules for programming - function parameters and how they are used


    There are some basic ground rules that apply to all system programming, so obvious that most documentation does not bother explaining them because these rules should have been internalized by practitioners of the art to the point where they need not be expressed. In the same way that when plotting driving directions you wouldn't even consider taking a shortcut through somebody's backyard or going the wrong way down a one-way street, and in the same way that an experienced chess player doesn't even consider illegal moves when deciding what to do next, an experienced programmer doesn't even consider violating the following basic rules without explicit permission in the documentation to the contrary:

    • Everything not defined is undefined. This may be a tautology, but it is a useful one. Many of the rules below are just special cases of this rule.
    • All parameters must be valid. The contract for a function applies only when the caller adheres to the conditions, and one of the conditions is that the parameters are actually what they claim to be. This is a special case of the "everything not defined is undefined" rule.
      • Pointers are not NULL unless explicitly permitted otherwise.
      • Pointers actually point to what they purport to point to. If a function accepts a pointer to a CRITICAL_SECTION, then you really have to pass pointer to a valid CRITICAL_SECTION.
      • Pointers are properly aligned. Pointer alignment is a fundamental architectural requirement, yet something many people overlook having been pampered by a processor architecture that is very forgiving of alignment errors.
      • The caller has the right to use the memory being pointed to. This means no pointers to memory that has been freed or memory that the caller does not have control over.
      • All buffers are valid to the size declared or implied. If you pass a pointer to a buffer and say that it is ten bytes in length, then the buffer really needs to be ten bytes in length.
      • Handles refer to valid objects that have not been destroyed. If a function wants a window handle, then you really have to pass a valid window handle.
    • All parameters are stable.
      • You cannot change a parameter while the function call is in progress.
      • If you pass a pointer, the pointed-to memory will not be modified by another thread for the duration of the call.
      • You can't free the pointed-to memory either.
    • The correct number of parameters is passed with the correct calling convention. This is another special case of the "everything not defined is undefined" rule.
      • Thank goodness modern compilers refuse to pass the wrong number of parameters, though you'd be surprised how many people manage to sneak the wrong number of parameters past the compiler anyway, usually by devious casting.
      • When invoking a method on an object, the this parameter is the object. Again, this is something modern compilers handle automatically, though people using COM from C (and yes they exist) have to pass the this parameter manually, and occasionally they mess up.
    • Function parameter lifetime.
      • The called function can use the parameters during the execution of the function.
      • The called function cannot use the parameters once the function has returned. Of course, if the caller and the callee have agreed on a means of extending the lifetime, then those rules apply.
        • The lifetime of a parameter that is a pointer to a COM object can be extended by the use of the IUnknown::AddRef method.
        • Many functions are passed parameters with the express intent that they be used after the function returns. It is then the caller's responsibility to ensure that the lifetime of the parameter is at least as long as the function needs it. For example, if you register a callback function, then the callback function needs to be valid until you deregister the callback function.
    • Input buffers.
      • A function is permitted to read from the full extent of the buffer provided by the caller, even if not all of the buffer is required to determine the result.
    • Output buffers.
      • An output buffer cannot overlap an input buffer or another output buffer.
      • A function is permitted to write to the full extent of the buffer provided by the caller, even if not all of the buffer is required to hold the result.
      • If a function needs only part of a buffer to hold the result of a function call, the contents of the unused portion of the buffer are undefined.
      • If a function fails and the documentation does not specify the buffer contents on failure, then the contents of the output buffer are undefined. This is a special case of the "everything not defined is undefined" rule.
      • Note that COM imposes its own rules on output buffers. COM requires that all output buffers be in a marshallable state even on failure. For objects that require nontrivial marshalling (interface pointers and BSTRs being the most common examples), this means that the output pointer must be NULL on failure.

    (Remember, every statement here is a basic ground rule, not an absolute inescapable fact. Assume every sentence here is prefaced with "In the absence of indications to the contrary". If the caller and callee have agreed on an exception to the rule, then that exception applies. For example, a pointer is prototyped as volatile is explicitly marked as "This value can change from another thread," so the rule against modifying function parameters does not apply to such a pointer.)

    Coming up with this was hard, in the same way it's hard to come up with a list of illegal chess moves. The rules are so automatic that they aren't really rules so much as things that simply are and it would be crazy even to consider otherwise. As a result, I'm sure there are other "rules so obvious they need not be said" that are missing. (For example, "You cannot terminate a thread while it is inside somebody else's function.")

    One handy rule of thumb for what you can do to a function call is to ask, "How would I like it if somebody did that to me?" (This is a special case of the "Imagine if this were possible" test.)

  • The Old New Thing

    You never know until you test it with real users


    Speculate all you want about what users expect, what they want, what they care about. There's no substitute for actually running experiments to find out. Those who haven't already been following Jensen Harris really ought to be, because he talks about user interface design in a highly practical way. Consider this entry on deciding how the ribbon should behave:

    These rules came directly out of watching people use the product in the labs over the last few years. Other rules we thought would be important (such as always collapsing from right to left) turned out to not matter at all.

    Or this one on the special rule for lingering:

    What we found was that an extremely common scenario involved people using an object, clicking away from it to make the selection handles go away (to get a better look at it) and then clicking right back on the object to continue formatting it.

    Notice that prior to discovering and then implementing the "lingering" rule, users who used Contextual Tabs were frequently confused. But once the rule was created, the usability results are solid.

  • The Old New Thing

    Raymond's highly scientific predictions for the 2006 NCAA men's basketball tournament


    Methodology explained earlier.


    • Correct predictions are in green.
    • Incorrect predictions are in red.
    • (!) marks upsets correctly predicted.
    • (*) marks upsets predicted but did not take place.
    • (x) marks actual upsets not predicted.

    Dayton mini-bracket

    Monmouth (2003) Hampton
    Hampton (1978)

    Atlanta bracket

    Duke (2004) Duke George (*)
    Geo. Washington (1988) George
    UNC-Wilmington (2003)
    Syracuse (2004) Syracuse (x) Syracuse
    LSU (2005) LSU
    West Virginia (1995) West
    Southern Illinois (2001)
    Iowa (2003) Iowa (x)
    California (2003) California (x) California
    NC State (2005)
    Texas (2006) Texas

    Oakland bracket

    Memphis (2001) Memphis Arkansas Kansas Kansas
    Arkansas (1997) Arkansas (x)
    Bucknell (2004)
    Pittsburgh (1996) Pittsburgh Kansas
    Kansas (1995) Kansas (x)
    Indiana (2003) San Diego St. (*) San Diego St. Marquette
    San Diego St. (1996)
    Gonzaga (1998) Gonzaga
    Marquette (1995) Marquette (x) Marquette
    Alabama (2003)
    UCLA (1997) UCLA

    Washington, D.C. bracket

    Connecticut (1996) Connecticut Connecticut Connecticut Seton Hall
    Kentucky (2001) Kentucky
    UAB (2002)
    Washington (2004) Washington Washington (!)
    Illinois (2005) Illinois
    Michigan State (2003) George
    Mason (!)
    Mason (!)
    Seton Hall
    George Mason (1996)
    North Carolina (2000) North
    Wichita State (1999) Seton Hall (*) Seton Hall
    Seton Hall (1995)
    Tennessee (2004) Tennessee

    Minneapolis bracket

    Villanova (1988) Villanova Villanova Villanova Villanova
    Arizona (1997) Arizona
    Wisconsin (2004)
    Nevada (2005) Nevada (x) Boston
    Boston College (1996) Boston
    Oklahoma (1994) Oklahoma (x) Oklahoma Oklahoma
    UW-Milwaukee (2003)
    Florida (2003) Florida
    Georgetown (2001) Northern
    Iowa (*)
    Northern Iowa (1995)
    Ohio State (2002) Ohio State


    George Washington (1988) George
    Kansas (1995)
    Seton Hall (1995) Villanova
    Villanova (1988)

    As I noted yesterday, the final will be very close, with George Washington University edging out Villanova by two months, 1988.08.01 to 1988.10.05.

    Other people have come up with their own systems. The person a few doors down from me chose an algorithm that can be captured in three words: "Shorter name wins". But my favorite is somebody whose highly scientific method is "The team that pays its basketball coach more."

  • The Old New Thing

    Why does the version 6 animation control not use a background thread?


    Many people have noticed that the animation bar control in version 6 of the common controls no longer uses a background thread to draw the animation. Instead, it acts as if the ACS_TIMER style is always set, even if the caller didn't pass it. Why is that?

    The first reason is that the background thread didn't actually help any. In order to draw transparent animations, the painting loop needs to query the animation control's parent to obtain the background color, and that query entails sending a message to the main UI thread. Consequently, the main UI thread must remain responsive to messages in order for the animation to render properly. If the main UI thread stopped responding to messages, the WM_CTLCOLORSTATIC message sent to obtain the background color would hang, and painting would wedge.

    Therefore, the UI thread had to remain responsive to messages. And if that's the case, then there's no need for the background thread after all. The UI thread can just run a timer and draw the frames in response to the timer message.

    The second reason for getting rid of the background thread is that it actually made things worse. Another thread means more context switches and more memory pressure, since the additional thread needs a stack and other supporting data structures. Admittedly, this is a comparatively weak reason.

    The third reason is that using a background thread for painting simply encouraged bad code. Using a background thread for painting meant that the UI thread could stop responding to messages for long periods of time and usually get away with it because the query for the background color comes early in the animation cycle when the main UI thread most likely has not yet gotten into the part of the long-running procedure that stops responding to messages. As a result, the background thread encouraged programs to stop pumping messages on UI threads because "it seems to work fine". The result is programs that fail to maintain a responsive UI, resulting in periodic mysterious hangs in the window manager when another program tries to broadcast a message and gets wedged up behind the unresponsive window. This leads to increased frustration for the end user and a general feeling that "Windows sucks".

    By making the suckage obvious, programmers will be more likely to notice that they are doing something bad and do something to address it. Masking the problem with a background animation thread merely allows the problem to persist.

  • The Old New Thing

    Really, college athletics is about education (not)


    Okay, somebody handed me a NCAA Men's Basketball Bracket to fill out. I don't know squat about college sports, so I decided that I would fill in the bracket based on the following simple principle: The school whose President (or Chancellor) has served longer will win the match-up. (Not counting the first-round games of the top five seeds in each bracket, just to avoid some wild upsets.)

    At least I thought that was simple. Filling out the bracket given this rule turns out to be rather difficult, because I have yet to find a web site that has links to all the schools represented in this year's tournament. (I guess college athletics isn't about the academics, because if it were, then certainly people would be falling over themselves to extol the fantastic educational opportunities at the schools represented.)

    And it's not exactly easy to figure out the school's web site from the bracket sheet. There is for example a team labelled simply "Texas". Do I want "The University of Texas at Austin"? Do I want "The University of Texas at Dallas"? Do I want "The University of Texas at Arlington"? "Texas State University"? "Texas College"? I'm guessing it's the University of Texas at Austin, though I might be wrong.

    And then there's another box labelled "California". Which of the many California schools is that supposed to be? I don't even know what question to enter into a search engine to find out. (Turns out it's the University of California at Berkeley.)

    Even after I hunt around to figure out which of the dozen universities named "Texas" I think I really want, I then have to find out who their President is and when they assumed office. Finding the President's page (or Chancellor's page) sometimes takes work, and even if you find it, it's sometimes wrong. For example, the first thing that I noticed on the page for Syracuse University's Chancellor Nancy Cantor is that it says "Learn about Chancellor-Elect Nancy Cantor". (If you don't see that phrase on the page, turn on your screen reader or disable images, or just hover over the "Inside the Chancellor's Site" link.)

    "Well, great," I thought. "She's Chancellor-Elect, but who's the current chancellor?"

    Turns out that Nancy Cantor is the current chancellor. She was inaugurated two years ago. The "Chancellor-Elect" text is badly out of date.

    Even when I can find the site of the university's President/Chancellor it's sometimes quite a bit of work to find out when they took office. For example, go back to Nancy Cantor's page and try to figure out when she was inaugurated. You have to click on "Soul of Syracuse", then go to the News page, then scroll down to the article titled, "Amid grand community celebration, conversation and art, SU inaugurates Cantor as 11th Chancellor and President", click through to the article and read the dateline.

    November 5, 2004.

    That was a lot of work for a tiny bit of information that you think would be easily accessible on a biography page. Good luck finding the home page of the president of Utah State University; and if you manage to find the date at which he became president, then I tip my hat to you, because as far as I can tell they don't list that information on their web site.

    (By the way, according to my highly scientific method, the final game between Villanova and George Washington University will be extremely close, with GWU eking out a victory by just two months, August 1, 1988 to October 5, 1988.)

Page 2 of 5 (41 items) 12345