April, 2009

  • The Old New Thing

    Windows 95 almost had floppy insertion detection but the training cost was prohibitive

    • 119 Comments

    One feature which Windows 95 almost had was floppy disk insertion detection. In other words, Windows 95 almost had the ability to detect when a floppy disk was present in the drive without spinning up the drive.

    The person responsible for Windows 95's 32-bit floppy driver studied the floppy drive hardware specification and spotted an opportunity. Working through the details of the specification revealed that, yes, if you issued just the right extremely clever sequence of commands, you could determine whether a disk was in the floppy drive without spinning up the drive. But there was a catch.

    The floppy drive hardware specification left one aspect of the drive behavior unspecified, and studying the schematics for various floppy drive units revealed that about half of the floppy drive vendors chose to implement it one way, and half the other way. Here's the matrix:

    Floppy Style Disk present Disk absent
    "A"10
    "B"01

    The results were completely reliable within each "style" of floppy drive, but the two styles produce exactly opposite results. If you knew which style of drive you had, then the results were meaningful, but the hard part was deciding which style of drive the user had.

    One idea was to have an additional "training" step built into Setup:

    • "Please insert a floppy disk into the drive and click Next."

    Once the disk was in, we could run the algorithm and see whether it returned 0 or 1; that would tell us which style of floppy drive we had.

    Unfortunately, this plan fell short for many reasons. First of all, a user who bought a computer with Windows 95 preinstalled would have bypassed the training session. You can't trust the OEM to have gone through the training, because OEMs change suppliers constantly depending on who gave them the best deal that week, and it's entirely likely that on the floor of the warehouse are a mix of both styles of floppy drive. And you certainly don't want to make the user go through this training session when they unpack their computer on Christmas morning. "Thank you for using Window 95. Before we begin, please insert a floppy disk in drive A:." You can't just try to figure out what type of drive the user has by comparing the clever technique against the boring "turn on the floppy drive light and make grinding noises" technique, at least not without displaying a warning to the user that you're about to do this—users tend to freak out when the floppy drive light turns on for no apparent reason. "Thank you for using Windows 95. Before we begin, I'm going to turn on your floppy drive light and make grinding noises. Press OK."

    Floppy disk insertion detection is not a sufficiently compelling feature that users will say, "I appreciate the benefit of going through this exercise."

    Sadly, floppy insertion detection had to be abandoned. It was one of those almost-features.

  • The Old New Thing

    The frustration of people who are not interested in why something works but only interested in the magic phrase they need to type

    • 86 Comments

    It's frustrating when people who are presumably smart (or who are at least pretending to be) have a problem and are satisfied to receive the magic phrase they need to type with no interest at all in understanding why the magic phrase works. For example, here's a question sent to an internal mailing list for users of something I'll call Program Q.

    From: X

    Why is it that after I use program Q to create a table, nothing else works? Here's a batch file I've written:

    q create table newtable
    pause
    q create table newtable2
    

    The pause command never executes; in fact, nothing in the batch file executes after the first line. How can I create multiple tables from a batch file?

    My psychic powers didn't need very much charging up to solve this one. Program Q is a popular one, and some teams like to create a wrapper around it for whatever reason.

    My psychic powers tell me that the first "q" on the path is not q.exe.

    Another person on the mailing list knew what I was getting at and picked up the ball.

    From: Y

    Try using call q create table newtable instead.

    (Maybe you too can figure out what my psychic powers told me the problem was.)

    The response:

    From: X

    Thanks, that did the trick. I put a call in front of the first line and now the entire batch file runs.

    I replied:

    Dear X. Please confirm that the first q on the path is in fact a batch file, in order to confirm that you understand the reason why you were encountering the problem, that it has nothing to do with Program Q, and that you understand why the fix works. Don't just copy the answer from your neighbor.

    I've already decided that I'm going to blog about people who accept an answer without bothering to understand why it works. Here's your chance to at least look good when that blog entry comes out.

    There was no response.

    Update: This was a mailing list for a programmer's tool. And if you're a programmer, you'd better understand what you're doing when you do it, not just cargo-cult your way through your job. Especially since this particular problem had nothing to do with Program Q in the first place. (On the other hand, I don't blame non-programmers for being satisfied with magic phrases.)

  • The Old New Thing

    On the almost-feature of floppy insertion detection in Windows 95

    • 80 Comments

    Gosh, that floppy insertion article generated a lot of comments.

    First, to clarify the table: The table is trying to say that if you had a Style A floppy drive, then issuing the magic series of commands would return 1 if a floppy was present, or 0 if the floppy was not present. On the other hand, if you had a Style B floppy drive, then issuing the magic series of commands would return 0 if a floppy was present, or 1 if the floppy was not present. That's what I was trying to say in the table. The answer was consistent within a floppy style, but you first had to know what style you had.

    The downside of waiting until the user uses a floppy for the first time is that you have the it sometimes works and sometimes doesn't problem. Dad buys a new computer and a copy of the Happy Fun Ball game for his son. Dad turns on the computer, and then follows the instructions that come with the Happy Fun Ball package: "Just insert the floppy and follow the instructions on the screen." Dad inserts the floppy and... nothing happens because this is the first time Dad used the floppy, and he was expecting autodetection to work.

    Dad says, "Stupid program doesn't work."

    Dad complains to his co-workers at work. "He loves this game Happy Fun Ball when he visits his cousin's house, so I bought a computer and a copy of Happy Fun Ball, and it doesn't work!"

    Dad tries again that evening and this time it works, because in the meantime, he inserted a floppy to do something else (say, create an emergency boot disk). Bizarre. This just reinforces Dad's impression that computers are unpredictable and he will never understand how to use them.

    One could say that a feature that mysteriously turns itself on and off is worse than a feature that simply doesn't work. At least when it doesn't work, it predictably doesn't work. Human beings value predictability.

    You can't perform the test "the first time the drive is installed" because there is no way to tell that a drive has been installed. (Classic floppy drives are not Plug-and-Play.) Even worse, you can't tell that the user has replaced the Style A floppy drive with a Style B floppy drive. The user will see that floppy insertion detection stopped working and return the drive to the store. "This drive is broken. Floppy insertion detection doesn't work."

    It is also not the case that the ambiguity in the specification indicated a flaw in the specification. The C++ language specification, for example, leaves a lot of behaviors at the discretion of the implementation. This allows implementations to choose a behavior that works best for them. The no-spin-up floppy presence detection algorithm relied on several behaviors which were covered by the specification, and one that was not. It was not part of the original charter for the floppy specification committee to support spinless presence detection; it's just something that my colleague discovered over a decade after the specification was written.

    But the main reason for not bothering is that the benefit was minuscule compared to the cost. Nobody wants floppy drives to spin up as soon as a disk is inserted. That just makes them think they've been attacked by a computer virus. It'd all just be a lot of work for a feature nobody wants. And then you'd all be posting, "I can't believe Microsoft wasted all this effort on floppy insertion detection when they should have fixed insert favorite bug here."

  • The Old New Thing

    When people ask to disable drag and drop, they often are trying to disable accidental drag and drop

    • 62 Comments

    We occasionally get customers who ask, "How do I disable drag and drop?" This is an odd request, so we ask the frequent follow-up question, "What are you really trying to do?" For many of these customers, the answer goes something like this:

    We've found that our employees often accidentally move or copy items around on the desktop and in Explorer windows because the act of pressing the mouse button causes the mouse itself to slide slightly across the table, resulting in a drag operation instead of a single click. We then have to spend a good amount of time searching for where those files ended up and trying to get their system back to its original state. We'd therefore like to disable drag and drop to avoid this problem.

    They aren't really trying to disable drag and drop. They are merely trying to disable accidental drag and drop.

    To avoid accidental drag and drop, adjust the drag sensitivity so that it takes a greater effort to trigger a drag and drop operation. By default, the mouse needs to travel four pixels with the button held down for a drag operation to be initiated. To make it harder to initiate an accidental drag operation, just crank this number higher.

    This particular customer might decide to crank the drag threshold to 50 or 100 pixels, so that the mouse has to move quite a significant distance before it is interpreted as a drag operation.

    By the way, the function for changing the drag threshold is SystemParametersInfo; check out the SPI_SETDRAGHEIGHT and SPI_SETDRAGWIDTH parameters.

    Pre-emptive correction: Do not modify the registry directly. Use the API. That's why it's there.

  • The Old New Thing

    Why is the animation common control limited to RLE8-compressed animations?

    • 48 Comments

    The animation common control is very limited in the type of animations it can play. The animation must be in AVI format, it cannot have any sound, and it must be either uncompressed or use RLE8 compression. Why so many restrictions?

    Because if it could handle other file formats, play sound, and support arbitrary codecs, it would just be a reimplementation of the multimedia animation control. (We saw this principle earlier when discussing why you can't do everything with registry values that you can do with registry keys.)

    The point of the animation common control is to play very simple animations without the overhead and accompanying complexity of the full generality of the multimedia animation control. Think of it as the 1040EZ of animation. If your animation is comparatively simple and you don't need very much control over its playback at all, then go ahead and use the animation common control, which is designed to be a simple playback control for simple animations. But if your animation is complicated, uses an advanced compression scheme, or contains audio, then you've left the world of simple animations and need to move up to the full-sized 1040 form. There's no point adding support for everything to the simplified control, because once you've added support for everything, you have the complex control that the simplified control was trying to simplify!

    Okay, fine, so the animation common control can't be a full-featured animation control or that would have defeated its purpose for existing. But why those specific limitations?

    Because those were just barely enough features to play the animations that the shell team needed to play. The animation common control was written by the Windows 95 shell team, and it supports only enough features to be able to play the animations that the Windows 95 Explorer used. After the control was written, the shell team figured, hey, we already wrote it, we may as well do other people a favor and let them call it, too. The downside of this is that even though the shell doesn't use these simple animations any more, it still has to ship the animation control because other applications rely on it. What started out as a courtesy has turned into a support burden.

  • The Old New Thing

    Taxes redux: You can't open the file until the user tells you to open it

    • 48 Comments

    One of the so-called taxes of software development on Windows is being respectful of Hierarchical Storage Management. You can't open a file until the user tells you to open it. This rule has consequences for how Explorer extracts information about a file, because what you definitely don't want is for opening a folder full of archived files in Explorer to result in all the files being recalled from tape. (Actually, file recall is just an extreme case of the cost of opening the file. You run into a similar problem if the file is on a slow medium or over a slow network connection. But just to motivate the discussion, I'll continue with the tape scenario.)

    What information does Explorer need in order to display a file in a folder? Well, it needs the file name. Fortunately, that can be determined without opening the file, since the file name is how you identify the file in the first place! When you want to open a file, you pass its name. There, you have the name. If you are showing the contents of a folder, you used a function like FindFirstFile to get a list of all the files, and the list comes in the form of names. Okay, so the name is easy. (There are still subtleties here, but they are not relevant right now.)

    Okay, what's next. The icon. Well, in order to get the icon, we need to know the file type, so let's put the icon on hold for now.

    The file creation and modification times can be obtained without opening the file; they come out as part of the FindFirstFile, or if you started with a file name, you can recover them with GetFileAttributesEx. Either way, you can get that without opening the file. Same goes for the file size.

    The other properties, like Title, Author, Summary... They all require that the file be opened, so Explorer disables the property for files that have been archived to tape. You don't want to recall a file from tape just to show its Author in Explorer.

    Okay, that leaves just the file type (and the icon, which depends on the file type). Consider the possibilities for how the file type could be determined.

    What do these problems tell us? The first problem, that reading bytes from a file forces a recall, means that file type information cannot be based on the file contents. The second problem, that reading alternate data streams also force a recall, means that you can't put it in an alternate data stream either. All that's left is storing the type in the metadata.

    But the third problem tells us that there isn't much metadata to choose from. Whatever mechanism you use needs to be able to survive being sent as an email attachment or being uploaded to an FTP site. Email attachments in particular are extremely limited. Most email programs, when asked to save an attachment, preserve only one piece of metadata: The filename. (They often don't even preserve the last modified time. And good luck getting them to preserve other ad-hoc metadata.)

    All of these problem conspire to rule out all the places you can squirrel away type information, leaving just the filename. It's a sucky choice, but it's the only choice left.

    And it means that changing a file's extension means the file type information is destroyed (which, from an end user's point of view, may as well be corruption).

  • The Old New Thing

    How to answer the telephone, according to the manual

    • 47 Comments

    I have a fancy new office telephone with a bajillion features (most of which I will never even find, much less use). The telephone comes with a helpful manual, explaining how to carry out various tasks. For example:

    Answering a Call

    Using the Handset:
    Pick up the handset.

    Cool, I was wondering about that.

  • The Old New Thing

    What kind of uncle am I?

    • 38 Comments

    Like every language, English has its own collection of words to express family relationships. There are the easy ones like mother, father, brother, and sister. Also comparatively easy are cousin, aunt, uncle, niece and nephew. But most people don't know about this "removal" part, beyond the fact that your "first cousin twice removed" is somebody you probably met once at a wedding ten years ago.

    It's really not that complicated. Some people think removal has to do with estrangement, but actually it has to do with generations. The children of your Nth cousin are your Nth cousins once removed. Their grandchildren are your Nth cousins twice removed. In general, to find your Nth cousin K times removed, go to your Nth cousin and then follow their children K times.

    You can also look at it from the point of view of the younger relative: Your Nth cousin K times removed is the Nth cousin of your Kth direct ancestor, where K = 1 is your parent, K = 2 is your grandparent, etc. For example, your father's second cousin is your second cousin once removed. There's a nice chart at genealogy.com that depicts many of these more complicated relationship terms.

    In practice, these relationship terms are considered unnecessarily formal. In casual conversation, two such relatives who are comparable in ages are called distant cousins. If one is significantly older than the other, then the older relative is called a distant aunt/uncle of the younger one.

    In many cultures, the terms for aunt and uncle can be applied more generally to any close friend of your parents. When I mentioned to my mother that the children of one of my friends call me Uncle Raymond, she asked me, "Which uncle?" This question puzzled me initially, but then I realized that in my parents' native language, as with most other Chinese dialects, there are several types of family relationships which in English all get lumped together as uncle. The term to use depends on whether the uncle is paternal or maternal, whether the uncle is older than or younger than your parent, and whether the uncle is by blood or by marriage. For example, you can use the word for husband of father's older sister or, if you wanted to be more respectful or if you need to resolve ambiguity, you can say the word that means husband of father's older sister number two. One of the reasons for so many fine distinctions for family relationships is that it is considered disrespectful to address someone older than you by name. (Compare United States culture, where it is considered disrespectful to address one's parents by name.) Consequently, resolving ambiguity cannot be accomplished as it is in Western cultures by appending the relative's name (Aunt Carol or Uncle Bob); it must be done by using a more specific title.

    But since my friend's children speak English, they can just address me as Uncle and not have to worry about which kind of uncle I am. (To my Chinese-speaking nieces, however, I am 大姑丈.)

  • The Old New Thing

    Why is there sometimes a half-second delay between the click and the action?

    • 36 Comments

    There are places in the user interface where you may click to perform an action, but the action doesn't actually take place until a half second later. Why is there a half-second delay?

    Because it's waiting to see if the user is on the way to a double-click.

    Some users simply double-click everything in sight, and depending on what the single click action is, this may or may not be a problem.

    If the click launches a modal dialog, then the second click is mostly harmless because the modal dialog appears on the same thread group as the window that received the stray second click. When that thread group next processes input (when the modal dialog is ready to accept input), the mouse click goes to the modal dialog, and usually it's in some harmless location so nothing happens. Often, the second click is not even at the location where the modal dialog appeared but rather remains over the original window, which is now disabled. Consequently, the click is ignored—no harm done.

    But if the click launches a new process or opens a new top-level unowned window, then that second click is going to cause trouble. For example, if you clicked a link that launches a control panel, the second click goes to the launcher, and the control panel itself then appears without focus. (It can't steal focus because the user denied the focus change by interacting with the launcher window.)

    A common workaround for this problem is for items that act on a single click and which fall into the second category to wait for the double-click time to see whether another click arrives. If another click arrives, then perform the operation on the second click because the user is a double-clicker. If no click arrives, then perform the operation after the double-click time elapses because the user is a single-clicker. (Filtering out accidental double-clicks is informally known as debouncing.)

    The result of this is that if you're a single-clicker, then there's a half-second wait before the operation is performed.

  • The Old New Thing

    What structure packing do the Windows SDK header files expect?

    • 32 Comments

    /Zp8

    In words, integral types and pointers up to eight bytes in size are stored at their natural alignment. Larger types are stored at eight-byte alignment.

    TypeAlignment
    BYTE, char, UCHAR 1-byte alignment
    WORD, WCHAR, short, USHORT 2-byte alignment
    DWORD, int, long, UINT, ULONG, float, 32-bit pointer 4-byte alignment
    QWORD, __int64, unsigned __int64, double, 64-bit pointer 8-byte alignment

    In other words (saying it a third time), let T be an integral or pointer type. If sizeof(T) ≤ 8, then T is aligned at a sizeof(T)-byte boundary. If sizeof(T) ≥ 8, then T is aligned at an 8-byte boundary.

Page 1 of 3 (28 items) 123