July, 2008

  • The Old New Thing

    What does each country claim for its own?

    • 202 Comments

    One of the things that fascinates me is how each country's view of history is clouded by its own chauvinism. I was reminded of this when researchers were able to reconstruct the original recording from a phonautograph which predated Edison's phonograph, thereby adding another claim to the mix of who invented sound recording.

    I think the most contentious invention belongs to human flight. It seems that every country on the planet has a claim to being the pioneer in this field. I'm particularly amused that both France and Brazil claim Alberto Santos-Dumont as their own. Failure is an orphan.

    When I visited Portugal, I asked one of the professors, "What is it that students in Portugal are taught is Portugal's greatest contribution to humanity?"

    The professor had to stop and think for a while before formulating an answer.

    "Portugal has not fared very well of late economically. Our best years were long ago. I would say that our greatest contribution was our accomplishments during the Age of Discoveries."

    My question to you, dear reader, is to tell us what students in your country are taught are your country's greatest achievements, or alternatively, what students believe them to be. These beliefs need not be based in fact. I'm more interested in what it is that people want you to believe whether or not it's actually true.

    For starters, here's my list of what students are taught (or end up believing) are the great accomplishments of the United States:

    • Democracy (even though it existed for millennia prior, and some might argue whether what we have today still counts as one)
    • Powered flight (The Wright Brothers)
    • The telephone (Alexander Graham Bell)
    • The light bulb, phonograph, and motion pictures (Thomas Edison)
    • The camera (George Eastmann)
    • The elevator (Elisha Otis)
    • The automobile (Henry Ford)

    Many of these are contested, and two of them are flat-out wrong: Elisha Otis did not invent the elevator, but he made them popular in the United States thanks to safety improvements. Similarly, Henry Ford did not invent the automobile but he made them popular and affordable in the United States by using an assembly line.

  • The Old New Thing

    A modest proposal: Getting people to stop buying SUVs

    • 91 Comments

    I developed this modest proposal many years ago, but it looks like rising gasoline prices have done the job. But in case they don't, here's my plan.

    SUVs are classified as "trucks" for the purpose of C.A.F.E. regulations, and those regulations are more lenient on gasoline efficiency for trucks. As a result, the auto industry happily built SUVs safe in the knowledge that high SUV sales wouldn't lower their overall fleet mileage and therefore not risk violating the CAFE regulations.

    On the other hand, most other highway regulations do not consider SUVs to be trucks. As a result, SUVs sit in this sweet spot where they get to pretend they're a truck when it's advantageous to be a truck (avoiding CAFE regulations) and pretend that they're a car when it's advantageous to be a car (highway regulations).

    My modest proposal was to get rid of this loophole. If you're going to let SUVs be classified as trucks for the purpose of determining whether they are subject to CAFE regulations, then classify them as trucks for the purpose of highway regulations. If you're driving an SUV on a highway that has different speed limits for cars as opposed to trucks, then you have to adhere to the truck speed limit. Because you're a truck. If you're driving an SUV on a highway which bans trucks from the leftmost lane, then you are banned from the leftmost lane. Because you're a truck.

    People who are driving a truck because they actually need it for truck-like activities (hauling lumber, pulling a boat, delivering baked goods, whatever) won't be seriously inconvenienced, since they're not going to be doing any of those car-like things anyway.

    If you really want to take this principle to its conclusion, you would even have to stop at the weigh stations to ensure that you're not over weight, and to check your log book (you keep a log book, right?) to ensure that you're not violating the laws regarding the maximum permitted number of hours behind the wheel of a truck. Because you're a truck.

    Of course, the loophole could be closed the other way, too: Alter CAFE to classify SUVs as cars. Either way works for me. Just make the CAFE regulations and highway regulations agree on who is a car and who is a truck.

    As I noted, it looks like economic forces have solved the problem on their own, but in case SUVs suffer a new surge in popularity, you've got my proposal in your back pocket, ready to spring into action.

    Yes, this modest proposal is out of order, but it seemed more timely than the planned topic (solving the problem of identity theft), which I will still get to next time, if there is a next time.

  • The Old New Thing

    Why does the "Install Font" dialog look so old-school?

    • 51 Comments

    8 wonders why the "Install Font" dialog looks so old-school. (And Kevin Provance demonstrates poor reading skills by not only ignoring the paragraph that explains why the suggestion box is closed, but also asking a question that's a dup of one already in the suggestion box!)

    Because it's a really old dialog.

    That dialog has been around for probably two decades now. It works just fine, and since it's not really a high-traffic dialog, updating it takes lower priority than things that get used more often. Development and testing resources aren't infinite, after all. I'm sure that if you look harder, you can find other old dialog boxes. (My pet ugly old dialog is the Character Map program. It's hideous. and I say that even though my boss's boss wrote it.)

    Besides, people don't really add fonts that way much any more. When you install font packs, such as the Consolas Font Pack for Microsoft Visual Studio 2005, they just install the fonts as part of their setup process. It's all taken care of for you.

  • The Old New Thing

    Uninitialized floating point variables can be deadly

    • 49 Comments

    A colleague of mine related to me this story about uninitialized floating point variables. He had a function that went something like this, simplified for expository purposes. The infoType parameter specified which piece of information you're requesting, and depending on what you're asking for, one or the other of the output parameters may not contain a meaningful result.

    BOOL GetInfo(int infoType, int *intResult, double *dblResult)
    {
     int intValue;
     double dblValue;
    
     switch (infoType) {
     case NUMBER_OF_GLOBS:
      intValue = ...;
      break;
    
     case AVERAGE_GLOB_SIZE:
      dblValue = ...;
      break;
     ...
     }
     *intResult = intValue;
     *dblResult = dblValue;
     ...
    }
    

    After the product shipped, they started geting crash reports. This was in the days before Windows Error Reporting, so all they had to work from was the faulting address, which implicated the line *dblResult = dblValue.

    My colleague initially suspected that dblResult was an invalid pointer, but a search of the entire code base ruled out that possibility.

    The problem was the use of an uninitialized floating point variable. Unlike integers, not all bit patterns are valid for use as floating point values. There is a category of values known as signaling NaNs, or SNaN for short, which are special "not a number" values. If you ask the processor to, it will keep an eye out for these signaling NaNs and raise an "invalid operand" exception when one is encountered. (This, after all, is the whole reason why it's called a signaling NaN.)

    The problem was that, if you are sufficiently unlucky, the leftover values in the memory assigned to the dblValue will happen to have a bit pattern corresponding to a SNaN. And then when the processor tries to copy it to dblResult, then exception is raised.

    There's another puzzle lurking behind this one: Why wasn't this problem caught in internal testing? We'll learn about that next time.

  • The Old New Thing

    When I double-click an Excel spreadsheet, Excel opens but the document doesn't

    • 39 Comments

    Sometime last year, we got a report from a customer that whenever he double-clicks an Excel spreadsheet, Excel starts up, but the document isn't loaded. Instead, he gets an error message saying that document could not be found. He has to go to the Open dialog and open the spreadsheet manually. This report was routed to the shell team, since it appeared to be an Explorer problem.

    We studied the file type registration for Excel documents; those were fine. We suggested some breakpoints, but everything looked good there, too. Even ddespy reported that the "Open" command was being sent to Excel correctly. So far as the shell was concerned, it was sending the command that Excel registered as the "Please send me this command when you want to open a document," and yet when Explorer sent that very command, Excel responded with "Huh? What are you talking about?"

    This indicated that an investigation of Excel itself was in order, and an hour later, the problem was identified. Under Excel Options, Advanced, General, there is an option called "Ignore other applications that use Dynamic Data Exchange", and the customer had enabled this option. The consequences of enabling this option are described in Knowledge Base article Q211494: Since Excel was configured to ignore DDE requests, it ignored the DDE request that came from Explorer.

    A misconfigured copy of Excel resulted in an error message that by all indications looked like an Explorer bug. That's pretty much par for the course in Explorer-ville.

  • The Old New Thing

    Windows Vista changed the Alt+Tab order slightly

    • 38 Comments

    For decades, the Alt+Tab order was the same as the Z-order, but that changes in Windows Vista if you use the enhanced Alt+Tab feature known as Flip, which is on by default on most systems. There are three types of interactive task switching in Windows Vista:

    • Classic Alt+Tab: This is the same one that's been around since Windows 95. It shows a grid of icons.
    • Flip (new for Windows Vista): This shows a grid of thumbnails.
    • Flip3D (also new for Windows Vista): This shows a stack of windows in 3D.

    Classic Alt+Tab continues to show the icons in Z-order order, but the developer who wrote Flip told me that Flip changed it up a bit based on feedback from the design team. The first several icons are still shown in Z-order order, but if you have a lot of windows open, the rest of them are shown in alphabetical order to make it easier to pick the one you want from the list.

    I think it's a good sign that nobody seems to have noticed. A lot of user interface work tries to be invisible.

  • The Old New Thing

    Microspeak: Well, actually management-speak

    • 30 Comments

    I hate management-speak.

    Here's an example from an internal Web site.

    The purpose of this Web site is two-fold.

    1. Create a reference source (best practices) where individuals can learn how to plan/facilitate and leverage their X activities most effectively.
    2. Establish a library of X material teams can utilize.

    Wow, let's look at the first stated purpose. It goes on for so long and uses blatant management-speak such as "facilitate" and "leverage" that by the time it's over, I forget how the sentence started. Going back and reading it again, it appears that the first item is identical to the second! It's just that the first item says it in a more confusing way.

    The second item shows evidence of management wordsmithing as well. "Utilize" instead of "use". An action verb like "establish" rather than a state verb like "to be". And those changes actually render the sentence incorrect. The purpose of the site isn't to "establish" a library; the purpose of the site is to be that library. Establishing the library is what you did when you created the site in the first place! That purpose has already been completed.

    I think the people who built this Web site just copied their annual review goals into the Web site text, forgetting that the review goal describes what you are supposed to do, not what the thing you created is supposed to do.

    Here's how I would have written it:

    This Web site is a library of X materials.
  • The Old New Thing

    Theorize if you want, but if the problem is right there in front of you, why not go for the facts?

    • 29 Comments

    On an internal discussion list, somebody asked a question similar to this:

    My program launches a helper program. This helper program does different things based on the command line parameters, but the way I'm using it, it just prints its results to the console and exits. When I launch this program and wait for it to exit, my wait never completes. Why isn't the helper program exiting? Here's the code that I'm using to launch the helper process...

    It wasn't long before people chimed in with their suggestions.

    Have your main program call exit() at the end.
    If you're redirecting stdout, you may be forgetting to drain the pipe. Otherwise, if the program generates too much output, the pipe fills and the helper program blocks writing to it.
    Alas, that didn't help. Whether I redirect the output or not, the helper process still hangs.

    Eventually I had to step in.

    I can't believe I'm reading this discussion. It's like watching a room full of child psychologists arguing over why Billy is crying. They're all expounding on their pet theories, and none of them bothers to ask Billy, "Why are you crying?"

    Connect a debugger to the helper process. See why it's crying.

    You can sit around developing theories all you want, but since the problem is right there in front of you, the quickest way to figure out why the helper process isn't exiting is to connect a debugger and look at it to see why it's not exiting.

    This is like the software version of the black crayons story.

  • The Old New Thing

    Don't be helpless: You can put things together, it doesn't have to be a single command

    • 29 Comments

    Humans are distinguished among all animal species by their advanced development of and heavy reliance on tools. Don't betray your ancestors. Use those tools you have.

    For example, during the debugging of a thread pool problem, it looked like somebody did a PostThreadMessage to a thread pool thread and left the message unprocessed after the thread pool function returned. Who could it have been? Well, one idea was to see if there were any DLLs in the system which called both QueueUserWorkItem and PostThreadMessage.

    I did a little legwork and contributed the following analysis to the mail thread:

    Of all the DLLs loaded into the process, the following call PostThreadMessage:

    SHLWAPI.dll 77D72436 221 PostThreadMessageA
    SHELL32.dll 77D78596 222 PostThreadMessageW
    ole32.dll 77D78596 222 PostThreadMessageW
    ... (list trimmed; you get the idea) ...

    Of those DLLs, these also call QueueUserWorkItem:

    shlwapi.dll
    shell32.dll
    ... (list trimmed; you get the idea) ...

    Astounded, somebody wanted to know how I came up with that list.

    Nothing magic. You have the tools, you have a brain, so connect the dots.

    The lm debugger command lists all the DLLs loaded into the process. Copy the output from the debugger window and paste it into a text file. Now write a little script that takes each line of the text file and does a link /dump /imports on the corresponding DLL. I happen to prefer perl for this sort of thing, but you can use a boring batch file if you like.

    for /f %i in (dlls.txt) do ^
    @echo %i & link /dump /imports %i | findstr PostThreadMessage
    

    Scrape the results off the screen, prune out the misses, and there you have it.

    "I tried that, but the result wasn't in the same format as what you posted."

    Well, yeah. There's no law that says that I can't manually reformat the data before presenting it in an email message. Since there were only a dozen hits, it's not worth writing a script to do that type of data munging. Typing "backspace, home, up-arrow" twelve times is a lot faster than writing a script to take the output of the above batch file and turn it into the output I used in the email message.

    Another boring batch file filters the list to those DLLs that also call QueueUserWorkItem. Writing it (or a script in your favorite language) is left as an exercise.

    No rocket science here. Just taking a bunch of tools and putting them together to solve a problem. That's what your brain is for, after all.

  • The Old New Thing

    Why is the LOADPARMS32 structure so messed up?

    • 26 Comments

    If you look at the LOADPARMS32 structure, you'll find a horrific mishmash. Double-null-terminated strings, a null-terminated string, some WORDs, and even a Pascal-style string. What's going on here?

    Each of those members comes from a different era in time. The oldest member is the Pascal-style command line, which dates back to CP/M. On CP/M, command lines were stored at a fixed location, namely 0080h through 00FFh, in the form of a Pascal string. The byte at 0080h specified the length of the command line, and the bytes at 0081h through 00FFh contained the command line itself.

    MS-DOS based much of its initial interface on CP/M in order to make porting to the new operating system easier, and part of what got carried over was the way command lines were passed from one program to another. The MS-DOS function to load a program took two parameters, a pointer to a null-terminated string (specifying the module to load) and a pointer to a parameter block which took the following form:

    LOADPARMS       struc
    loadp_environ   dw      ?       ; environment of new process
    loadp_cmdline   dd      ?       ; command line of new process
    loadp_fcb1      dd      ?       ; first FCB
    loadp_fcb2      dd      ?       ; second FCB
    LOADPARMS       ends
    

    To ease the transition, Windows 1.0 used the same MS-DOS interface for launching programs: You loaded up the registers and issued an int 21h instruction. All the parameters had the same meaning. Generally speaking, 16-bit Windows used the old MS-DOS interface for a lot of functionality, especially disk access. Want to write to a file? Put the file handle in the BX register, the number of bytes in the CX register, a pointer to the buffer in the DS:DX registers, function code 40h in the AH register, and issue an int 21h, just like in MS-DOS.

    Why do this? Well, it saved the Windows team from having to invent a whole boatload of functions that duplicated what MS-DOS already did, and it meant that existing MS-DOS programs didn't need to change a thing in their file I/O code. If they used a runtime library designed for MS-DOS (C or otherwise), that library would still write to files by setting registers and issuing an int 21h. If you want people to switch to your new platform, you need to make it easy, and "you don't have to change anything; it all just works" is pretty easy. (One minor change was that the first FCB was repurposed to contain the nCmdShow; the magic value of "2" in the first word of the FCB signals that it's not really an FCB.)

    As a minor convenience, the LoadModule function provided a C-callable version of the low-level int 21h, but you still had to provide the parameters in the form of the MS-DOS exec structure. It wasn't until later versions of Windows that the WinExec function was added, thereby providing a much more convenient interface to starting a new program. No longer did you have to mess with the crazy MS-DOS exec structure and its strange way of passing the command line and nCmdShow.

    The people who were designing Win32 created their own function CreateProcess to launch a new process, but for backward compatiblity, they retained the old WinExec and even older LoadModule mechanisms. The pointers in the crazy 16-bit exec block got converted to 32-bit, but the craziness of what they pointed to was retained to make porting old code easier. The int 21h interface no longer exists, of course. The craziness is just a leftover from the old MS-DOS days. The WinExec and LoadModule functions are now just stub functions that convert their parameters and call the CreateProcess function to do the real work.

Page 1 of 4 (40 items) 1234