April, 2012

  • The Old New Thing

    If posting here is frequently frustrating and irritating, why do I keep doing it?

    • 50 Comments

    Appreciator wonders, if I find posting here frequently frustrating and irritating, why I keep doing it anyway?

    Imagine I announced one day, "This is too frustrating and annoying. I'm going to stop now." To the rest of the world, this would "mean something." People would discuss in hushed tones—and for the Internet, hushed tones means in a normal voice, or perhaps even louder than normal—what this "means" for blogging, for Microsoft, for whatever. People would start speculating as to what pushed me over the line, maybe muse about what this means for other bloggers, or question my actual motivations. "Is this really a cover so Raymond can quit Microsoft and work for another company?" It's easier just to avoid becoming news by not doing anything newsworthy.

    I guess I could stop if I made up some bogus but less controversial reason for stopping, say, because I wanted to "spend more time with my family."

    Generally speaking, change is news. Contrapositively, no-news requires no-change. I prefer not to be news.

    (In the same way that if I decided to change my policy and start opinionating more, people would make more of the change than if I had been opinionated from the beginning. I often envy Michael Kaplan for having established himself as an opinionated blowhard early on, which gives him the freedom to spout off on whatever he wants without creating much controversy.)

  • The Old New Thing

    What were the tests that WinG did to evaluate video cards?

    • 46 Comments

    Georg Rottensteiner was curious about the weird things that WinG performed on installation to evaluate video cards. "What did it do actually and what for?"

    I don't actually know, since I was not involved in the WinG project, but I remember chatting with one of the developers who was working on video card benchmarks.

    He says that video card benchmarks are really hard to develop, not just because video cards are complicated, but also because video drivers cheat like a Mississippi riverboat card sharp on a boat full of blind tourists.

    He discovered all sorts of crazy shenanigans. Like a video driver which compares the string you ask it to display with the text "The quick brown fox jumps over the lazy dog." If the string matches exactly, then it returns without drawing anything three quarters of the time. The reason: Benchmarks often use that sample string to evaluate text rendering performance. The driver vendors realized that the fastest code is code that doesn't run, so by ignoring three quarters of the "draw this string" requests, they could improve their text rendering performance numbers fourfold.

    That was the only one of the sneaky tricks I remember from that conversation. (I didn't realize there was going to be a quiz 17 years later or I'd have taken notes.) Another example of benchmark cheating was a driver which checked if the program name was TUNNEL.EXE and if so, enabled a collection of benchmark-specific optimizations.

    Anyway, I suspect that the weird things that the WinG installer did were specifically chosen to be things that no video card driver had figured out a way to cheat, at least at the time he wrote the test. I wouldn't be surprised if fifteen seconds after WinG was released, video driver vendors started studying it to see how they could cheat the WinG benchmark...

  • The Old New Thing

    Why doesn't the Version tab show up for very large files?

    • 40 Comments

    If you have a really large file and try to view its properties in Explorer, you may find that the Version tab doesn't appear. What's going on?

    The Version tab uses the GetFileVersionInfo family of functions to obtain version information from files. It so happens that the GetFileVersionInfo function works by calling LoadLibraryEx(LOAD_LIBRARY_AS_DATAFILE) and then using functions like FindResource to locate the version resource so it can allocate a buffer to hold the version resource plus additional data to assist in character set translation.

    If the file is larger than the available address space in the Explorer process, then the call to LoadLibraryEx will fail due to lack of address space into which to map the image. Library not loaded means no version resource to show you.

    When we explained this behavior to a customer, the customer wanted to know the exact file size at which the problem occurs.

    There is no single exact file size at which the problem occurs because the amount of available address space is determined by large numbers of factors which cannot be boiled down to a simple description. It depends on the pattern of memory allocation that took place inside Explorer, which shell extensions and DLLs got loaded at what point in time, where they got relocated to, which ones got unloaded relative to others loading, how much memory they allocated and where those allocations ended up.

    It's like asking the airline, "I know that the later I make my reservation, the less likely I'm going to get the exact seat I want. What is the cutover point before which I will get a window seat and after which I won't?" There is no specific point in time where "all reservations before this point will definitely get a window seat, and all reservations after this point will definitely not get one." It all depends on the pattern of requests before you make your reservations. Indeed, it's even possible that if you had made your reservation later, you would have gotten that window seat, because somebody who had a window seat called in a cancellation.

    One customer apparently didn't understand the unpredictability of the cutover point and asked, "Is there a way to change this limit?"

  • The Old New Thing

    Introducing the unrolled-switch anti-pattern

    • 39 Comments

    Over the years, I've seen a bunch of coding anti-patterns. I figured maybe I'll share a few.

    Today, I'll introduce what I'm calling the unrolled-switch anti-pattern, also known as "Specialization is always faster, right?"

    enum Axis
    {
        XAxis,
        YAxis,
        ZAxis,
    };
    
    // code earlier in the function ensure that
    // "axis" is always a valid axis
    int newPosition;
    switch (axis)
    {
    case XAxis:
        newPosition = m_position[XAxis] + amount;
        if (newPosition < m_minPosition[XAxis])
            newPosition = m_minPosition[XAxis];
        if (newPosition > m_maxPosition[XAxis])
            newPosition = m_maxPosition[XAxis];
        m_position[XAxis] = amount;
        break;
    case YAxis:
        newPosition = m_position[YAxis] + amount;
        if (newPosition < m_minPosition[YAxis])
            newPosition = m_minPosition[YAxis];
        if (newPosition > m_maxPosition[YAxis])
            newPosition = m_maxPosition[YAxis];
        m_position[YAxis] = amount;
        break;
    case ZAxis:
        newPosition = m_position[ZAxis] + amount;
        if (newPosition < m_minPosition[ZAxis])
            newPosition = m_minPosition[ZAxis];
        if (newPosition > m_maxPosition[XAxis])
            newPosition = m_maxPosition[XAxis];
        m_position[ZAxis] = amount;
        break;
    }
    
    As we all know, special-case code is faster than general-purpose code. Instead of writing slow general-purpose code:

        newPosition = m_position[axis] + amount;
        if (newPosition < m_minPosition[axis])
            newPosition = m_minPosition[axis];
        if (newPosition > m_maxPosition[axis])
            newPosition = m_maxPosition[axis];
        m_position[axis] = amount;
    

    we unroll it into a switch statement, thereby generating highly-optimized special-purpose code, one for each axis.

    What makes this anti-pattern particularly frustrating is that you cannot tell at a glance whether all the cases really are the same (just with different axes).

    In fact, they aren't.

    If you look closely, you'll see that we check the new Z-position against the X-axis maximum rather than the Z-axis maximum. If you're reading this code, you now start to wonder, "Is this a copy/paste bug, or is there some reason that we really do want to check the Z-position against the X-axis minimum?"

    A variation on the unrolled-switch is the unrolled-if, used if the item you want to unroll cannot be used in a switch statement:

    FruitBasket *BananaBasket;
    FruitBasket *AppleBasket;
    FruitBasket *PearBasket;
    FruitBasket *MangoBasket;
    
    if (basket == BananaBasket) {
      if (!BananaBasket->IsEmpty()) {
        fruit = BananaBasket->TakeFruit();
        if (HaveKnife()) {
          TakeKnife();
          fruit->Peel();
          fruit->Slice();
          fruit->Eat();
          ReplaceKnife();
        } else {
          BananaBasket->AddFruit(fruit);
        }
      }
    } else if (basket == AppleBasket) {
      if (!AppleBasket->IsEmpty()) {
        fruit = AppleBasket->TakeFruit();
        if (HaveKnife()) {
          TakeKnife();
          fruit->Peel();
          fruit->Slice();
          fruit->Eat();
          ReplaceKnife();
        } else {
          AppleBasket->AddFruit(fruit);
        }
      }
    } else if (basket == PearBasket) {
      if (!PearBasket->IsEmpty()) {
        fruit = PearBasket->TakeFruit();
        if (HaveKnife()) {
          TakeKnife();
          fruit->Slice();
          fruit->Eat();
          ReplaceKnife();
        } else {
          PearBasket->AddFruit(fruit);
        }
      }
    } else if (basket == MangoBasket) {
      if (!MangoBasket->IsEmpty()) {
        fruit = MangoBasket->TakeFruit();
        if (HaveKnife()) {
          TakeKnife();
          fruit->Peel();
          fruit->Slice();
          fruit->Eat();
          ReplaceKnife();
        } else {
          BananaBasket->AddFruit(fruit);
        }
      }
    }
    

    When I pointed out in an aside to the customer that this could be simplified (after fixing the copy/paste errors) to

    if (!basket->IsEmpty()) {
      fruit = basket->TakeFruit();
      if (HaveKnife()) {
        TakeKnife();
        fruit->Peel();
        fruit->Slice();
        fruit->Eat();
        ReplaceKnife();
      } else {
        basket->AddFruit(fruit);
      }
    }
    

    the response was, "Hey, that's a neat trick. I didn't realize you could do that."

    I wonder if this person also programs loops like this:

    switch (limit)
    {
    case 0:
      break;
    case 1:
      do_something(array[0]);
      break;
    case 2:
      for (int i = 0; i < 2; i++) do_something(array[i]);
      break;
    case 3:
      for (int i = 0; i < 3; i++) do_something(array[i]);
      break;
    case 4:
      for (int i = 0; i < 4; i++) do_something(array[i]);
      break;
    case 5:
      for (int i = 0; i < 5; i++) do_something(array[i]);
      break;
    case 6:
      for (int i = 0; i < 6; i++) do_something(array[i]);
      break;
    ...
    case 999:
      for (int i = 0; i < 999; i++) do_something(array[i]);
      break;
    default:
      FatalError("Need more cases to handle larger array");
      break;
    }
    
  • The Old New Thing

    I thought I was so clever, salvaging an old floppy drive from a dead computer, but I didn't think *two* steps ahead...

    • 37 Comments

    When one of the oldest computers at Microsoft still doing useful work finally died, I had the presence of mind to salvage the 5¼″ floppy drive from the machine, so that I could (someday) extract the data off all the old 5¼″ floppy discs I have packed away in boxes meaning to convert someday. (Mind you, the data capacity of a giant box of 5¼″ floppy disks is approximately equal to half of a CD.)

    Oh, and by the way, if you know what a floppy drive is, then this question on superuser.com will make you feel old.

    I thought I was so clever, salvaging an old floppy drive from a dead computer so I could use it to rescue data from obsolescence, but that was only thinking one step ahead. I failed to think two steps ahead: Nobody makes motherboards with 5¼″ floppy drive connectors!

    Bonus coincidental posting date: The Geeks Who Saved Prince of Persia's Source Code From Digital Death.

  • The Old New Thing

    Why are programs allowed to use normal characters as hotkeys?

    • 36 Comments

    alv wonders why programs are allowed to create hotkeys for normal characters. The example given is the famous AltGr modifier, equivalent to Ctrl+Alt. Programs which ignore the guidance and use Ctrl+Alt as a shortcut modifier end up stealing keys from many non-English keyboard layouts, thereby preventing users from typing things like the Euro sign € (which is an AltGr combination on most keyboards that support it), or even worse, preventing Polish users with the Polish programmer keyboard layout from typing the letter Ż.

    Given that using Ctrl+Alt as a keyboard shortcut modifier can prevent people from typing perfectly legitimate letters, why does Windows allow it in the first place?

    Because there are many cases where it is acceptable to commandeer keyboard sequences that would otherwise result in normal typing. You do it all the time and probably don't realize it.

    For example, in an edit control, the Ctrl+A shortcut is frequently overridden to mean select all instead of entering a literal U+0001 into the edit control text. You probably would go crazy if you lost the Ctrl+C, Ctrl+X and Ctrl+V hotkeys. Ctrl+Z for undo is also pretty popular. And who can forget TAB for dialog box navigation. And imagine the riots if ESC couldn't be used as a hotkey any more.

    There are some programs which go so far as to use normal typing characters as hotkeys. For example, an email program might let you compose a new message by simply pressing N. That's just the letter N, unshifted, unmodified.

    Given that there are legitimate reasons for allowing normal typing to be treated as a hotkey, implementing a global ban on that behavior would break a lot of scenarios that you personally would almost certainly want to keep working. Of course, it also means that it's the programmer's responsibility to think twice before stealing a keyboard sequence that can be used for normal typing. If the user is actually typing, you may have stolen something you should have left alone.

    You can't even have the rule "all shortcut keys which correspond to normal typing are automatically disabled when focus is on an edit control" because you would lose hotkeys like Ctrl+C and TAB, as noted above, as well as scenario-specific hotkeys such as having the IPv4 control automatically jump to the next octet when you press ..

  • The Old New Thing

    When you don't speak a language, don't sound like you speak the language

    • 35 Comments

    I appreciate the help from Christoph and Voo in refining my German. But that reminds me of a story about a friend of a friend.

    She was in Japan to visit some friends. Although she speaks English and Mandarin fluently, she doesn't know any Japanese, so her friends taught her how to say "Sorry, I don't speak Japanese." She managed to say this sentence quite well despite learning it purely phonetically.

    One day they were walking down the street as a group, and a gentleman approached and asked her for directions. She responded with the only sentence she knew: "Sorry, I don't speak Japanese."

    The gentleman was offended by this response and began scolding her for her rudeness. "Look, if you don't want to talk to me, just say so. Don't pretend like you don't speak Japanese." Since he was scolding her in Japanese, all she could do was stand there bewildered while this guy yelled at her.

    Fortunately, her friends intervened and explained to the gentleman, "No really, she doesn't speak any Japanese. We just taught her that one sentence."

    The lesson I took from this story was that when you don't speak a language, it's important to sound like you don't speak the language. In a way, it's a good thing that my German is a little bit off. That way, the person I'm talking with knows that my German is not all that great.

    Examples: During a trip to Germany, I discovered that when I asked a simple question, people would answer in rapid-fire German, overflowing my internal parsing buffer. During my trip to Sweden, I applied the lesson from this article, and found that people switched to simpler Swedish and spoke more slowly. As a result, I had little difficulty understanding what people were saying to me. (Of course, it didn't help me understand what they were saying to each other.)

  • The Old New Thing

    Shortcut properties are in the shortcut, so if they can read the shortcut, they can read the properties

    • 33 Comments

    A customer wanted to know if "there was a way to hide the properties of a shortcut."

    We asked for an explanation of the problem they were trying to solve, so we could understand what their question meant. The customer liaison explained:

    The customer is insisting on this, even though I think it's really the wrong approach. They want to put a password into the parameters of a shortcut, but they don't want their employees to see the password when they right-click the shortcut and select Properties. We're trying to convince them of better ways of doing this, but right now they want to see if they can solve it by marking the field as "hidden" somehow.

    If the password is anywhere in the shortcut file, the employees can dig it out. After all, the shell needs to dig it out, and since the shell runs with the user's privileges, in order for the shell to see it, the user must be able to see it. In other words, you can't hide anything in a shortcut because the user can just open the shortcut in Notepad and see all your "hidden" data. Or they can go to Task Manager and ask to see the command line. Or they can connect a debugger to Explorer and set a breakpoint on the Create­Process function.

    It's like saying, "I want my employees to be able to bake cakes, but I don't want them to have access to an oven. To block access to the oven, I put a combination lock on the oven controls. On the other hand, I want to write a cake recipe that lets the employees bake cakes in the oven. Therefore, the recipe says Step 5: Go to the oven and press 1234. But now the employee can just read the recipe and find out the combination to the oven! Is there a way I can write a cake recipe that lets them bake a cake without revealing the oven combination?"

    The recipe executes with the privileges of the employee. If you want the employee to be able to bake a cake by following the recipe, then they need to be able to perform the steps in the recipe, and that means being able to go to the oven and press 1234.

    The oven analogy does provide some ideas on how you can solve the problem. For example, if you simply don't want employees to be able to email the oven combination to their friends with the subject line Here's the combination to the oven!, then change the way access to the oven is managed. Instead of putting a combination lock on the oven, put an employee ID card scanner on the oven that enables the oven controls if the employee has oven privileges.

    For the original problem, this means changing your database so that instead of using a single password to control access and trusting each client to use it wisely, it uses the security identity of the client to control access. (I'm assuming that the password on the command line is a database password.)

    On the other hand, if your goal is to prevent employees from using the oven to do anything other than bake at 350°F for one hour, you can change the employee ID card scanner so that it checks the employee for cake-baking privileges, and if so, sets the oven to bake for 350°F for one hour and locks the controls (except perhaps for a cancel button). If you have multiple recipes—say, cakes and cookies—the ID card scanner checks which recipes the employees are allowed to use and lets them choose which preset they want to activate.

    For the original problem, this means changing your database so that the user identity selects which operations are permitted on the database. Some users have permission to see only records for active clients, whereas others have permission to see all records, and still others have modify permission.

    If your goal is to prevent employees from doing anything other than baking cakes according to this specific recipe, then you need to move even more actions "behind the counter", because you have no way of knowing that "that pan full of batter" was created according to your walnut cake recipe, or whether it's some unauthorized recipe with extra cinnamon. If you don't trust your employees to follow recipes, then you need to take the recipe out of their hands. The instructions are now Step 1: Order a walnut cake from the cafeteria.

    For the original problem, this means changing your database so that instead of letting the employee access records directly, the employee submits the action to the database ("change client 23 address to 123 Main Street"), and the database verifies that the employee has "change a client address" permission, and if so, performs the record update.

    Of course, if you want to ignore all the security guidance and "just hide the password in the shortcut file, why won't you answer my question?", you can just put the password somewhere other than the shortcut file. You could, say, have the shortcut run a batch file, and the batch file has the password.

  • The Old New Thing

    Why don't I get a Caps Lock warning balloon any more?

    • 32 Comments

    A customer asked for help diagnosing a problem they were experiencing on Windows XP:

    My customer reports that on their machines, they do not get the warning balloon that appears when Caps Lock is set while you are typing into a password field. I searched for relevant KB articles but couldn't find anything related to that. Can you help?

    Time for the psychic powers.

    My psychic powers tell me that the customer disabled all balloon tips.

    The customer liaison replied

    You are right. Thanks for the help.

    This is a not uncommon situation with some customers. They change a setting, and then later report that they're having some problem caused by that setting. They don't bother going to a freshly-installed machine to see whether the problem occurs there as well, in order to isolate whether the problem is related to their customizations or not. They just assume that their customizations couldn't possibly be the cause of the problem, and they are so convinced of this that they don't even mention "Oh, we customized this setting" when they ask for help.

    By the way, the setting to disable all balloon tips in the entire system is a rogue feature. At the time that balloons were originally being developed (and the rules surrounding them being refined based on research and feedback), one developer was impatient with the progress toward making the balloons less annoying, and he just went in and added a rogue feature to disable them with a sledgehammer. And now that people know about it, the rogue feature has become a support burden.

  • The Old New Thing

    How do I prevent users from opening TIF files?

    • 30 Comments

    A customer had a question about their Windows XP installations. (This question was from several years ago, so the fine details aren't really relevant any more, but I'm actually telling this story for a commentary opportunity.)

    The customer wanted to disable all file associations for TIFF files. Their first attempt was by deleting HKEY_CLASSES_ROOT\.tif and HKEY_CLASSES_ROOT\.tiff. This successfully renders TIFF files with a generic document icon, but when the user double-clicks the file, the registration is re-established and Windows Picture and Fax Viewer opens the file.

    The company had some strange company security policy that says that TIFF files should not have any file association. I don't know the rationale behind it, but they did say that they only needed to block the default file association. If the user explicitly creates a new association via the Open With dialog, then that is not covered by the policy.

    Deleting the registrations doesn't work because Windows XP has an autorepair feature for certain commonly-corrupted file associations, and TIFF is one of them. If the TIFF registration is corrupted and the user is a member of the Administrators group, then Windows XP will restore the default association. (If the user is not a member of the Administrators group, then the usual "Windows cannot open this file" dialog box appears.)

    Therefore, the solution to the customer's odd problem is not to delete the TIFF registrations (which causes them to be detected as corrupted) but rather to simply set a new default handler for TIFF files that merely displays a message to the user. If you're willing to use the Windows Script Host, then it's a one-line program:

    WScript.Echo("TIFF file associations are disabled.")
    

    If you have been reading carefully, you have already noticed a serious problem with the customer's configuration: The fact that they are seeing the TIFF autorepair code kicking in means that they are letting their employees run with Administrator privileges, which means that their so-called "security requirement" is like worrying about employees being able to sneak into the building through a ventilation grating, when you give everybody a key to the front door.

Page 1 of 3 (22 items) 123