August, 2008

  • The Old New Thing

    Why was the RAM drive removed from Windows 95?

    • 59 Comments

    Commenter Travis Owens asks, "Why was the RAM drive removed from Windows 95?"

    As with many of these types of accusatory questions, this comes with a false hidden assumption, in this case, that the RAM drive was in Windows 95 to begin with.

    Remember that Windows 95 introduced a new driver model, so any RAM drive would have had to be written from scratch. (Well, okay, you could use the old 16-bit RAM drive, but then you'd be going through the real-mode mapper, at which point your performance would go down the tubes.) As you admitted, it was a very niche feature that usually hurts but can sometimes help in very specific circumstances. Which Windows 95 file system feature would you have cut so that the geeks could have their RAM drive? Should we even be showing RAM drives to typical users, who would just turn it on without understanding its consequences, winding up with a computer that ran worse than if it had never been there?

    If you want a RAM drive, you can sit down with the DDK and write one. But the Windows 95 folks decided that adding support for SCSI hard drives would be a more effective use of the time and skills of the Windows 95 storage device driver development team—which, as I recall, consisted of three people.

    Mind you, if you really want your precious RAM drive, I've heard rumors that it's tucked away on your Windows Server 2003 CD under the names RAMDISK.IN_ and RAMDISK.SY_, and that there is a way to add a RAM drive to your Windows PE disk. (And no, I haven't heard any rumors about Windows Server 2008.)

  • The Old New Thing

    The unwritten rule of riding a Seattle Metro bus

    • 62 Comments

    The Metro King County transit site has all the facts about how to ride the bus, but there's another rule that is applied by convention rather than by any formal codification:

    For some reason, and I see this only in Seattle, it is customary to say Thank you to the bus driver as you get off the bus.

    Tip for new riders: If you aren't familiar with the area, you can ask the bus driver to announce the stop you intend to get off at.

    Bonus tip for bicyclists: There is an experimental program running this summer to allow bicyclists to ride out-of-service buses across the 520 bridge for free. You have to get on at Evergreen Point and get off at Montlake (or vice versa). This is in addition to the existing policy of allow bicyclists to ride out-of-service buses across the bridge (paying the normal fare), eastbound as far as 51st St, assuming the bus is heading that way. (This bonus tip is not that helpful to Microsoft employees, who already get free bus passes and a special bike shuttle, but I figured I'd toss it out there.)

  • The Old New Thing

    If you return FALSE from DLL_PROCESS_ATTACH, will you get a DLL_PROCESS_DETACH?

    • 28 Comments

    If you return FALSE from DLL_PROCESS_ATTACH, will you get a DLL_PROCESS_DETACH?

    Yes.

    No.

    ...

    Yes.

    All three answers are correct, for different formulations of the question.

    From the kernel's point of view, the answer is a simple Yes. If a DLL's entry point returns FALSE to the DLL_PROCESS_ATTACH notification, it will receive a DLL_PROCESS_DETACH notification.

    However, most C and C++ programs do not use the raw DLL entry point. Instead, they use the C runtime entry point, which will have a name something like DllMainCRTStartup. That entry point function does work to manage the C runtime library and calls your entry point (which you've probably called DllMain) to see what you think.

    If you compiled your program prior to around 2002 and your DllMain function returns FALSE in response to the DLL_PROCESS_ATTACH notification, then the C runtime code says, "Oh, well, I guess I'm not running after all" and shuts itself down. When the kernel calls the C runtime entry point with the DLL_PROCESS_DETACH notification, the C runtime says, "Oh, I'm already shut down, thanks for asking" and returns immediately, which means that your entry point is not called with the DLL_PROCESS_DETACH notification. In other words, if you wrote your program prior to around 2002, the answer is No.

    Sometime in 2002 or maybe 2003, the C runtime folks changed the behavior. If your DllMain function returns FALSE in response to the DLL_PROCESS_ATTACH notification, you will nevertheless get the DLL_PROCESS_DETACH notification. In other words, if you wrote your program after around 2002 or maybe 2003, then the answer is Yes. Why change? Maybe they wanted to match the kernel behavior more closely, maybe they considered their previous behavior a bug. You'll have to ask them.

    What does this mean for you, the programmer? Some people may look at this and conclude, "Well, now that I know how each of the specific scenarios works, I can rely on knowing the behavior that results from the scenario I'm in. For example, since I'm using Visual Studio 2008, the answer is Yes." But I think that's the wrong conclusion, because you usually do not have total control over how your program is compiled and linked. You may share your code with another project, and that other project may not know that you are relying on the behavior of a specific version of Visual Studio 2008; they will compile your program with Borland C++ version 5.5,¹ and now your program is subtly broken. My recommendation is to write your DllMain function so that it works correctly regardless of which scenario it ends up used in. (And since you shouldn't be doing much in your DllMain function anyway, this shouldn't be too much of a burden.)

    Footnote

    ¹I do not know what the behavior of Borland C++ version 5.5 is with respect to returning FALSE from DllMain. I didn't feel like doing the research to find a compiler whose behavior is different from Visual Studio 2008, so I just picked one at random. I have a 50/50 chance of being right.

  • The Old New Thing

    Why, when you sort by name, doesn't it actually sort by name?

    • 25 Comments

    When you right-click on the desktop or in an Explorer window and select "Sort by... Name", why doesn't it actually sort by name?

    Because "Sort according to the default sort order for the column whose title is... Name" is too long.

    The default sort order for the first column (whose name is "Name") is to sort by name, mostly. The actual sort order for the desktop and file folders (note: implementation detail, subject to change at any time) is really a two-tiered sort. First, the items are sorted into buckets; the first bucket contains the virtual folders, the second bucket contains file folders (directories), and the third bucket contains regular files. Sorting within the first bucket is done by the "item order"; sorting within the second and third buckets is by name. (And it's not by ASCII code any more. There's also special treatment for numbers and periods.)

    The "item order" (a term I just made up) for My Computer, My Documents, and Recycle Bin were specifically chosen in Windows 95 so that the icons on the desktop appeared in that order. I believe it was in Windows 2000 that the order was changed to My Documents, My Computer, Recycle Bin in order to put the focus on document management.

    (And those who have read Appendix A of my book will know what sort of application compatibility problems arose from that simple change.)

  • The Old New Thing

    The gradual erosion of the car trip experience

    • 38 Comments

    How will kids today learn to get along with their siblings? I just learned that another of the basic childhood conflict resolution scenarios has disappeared, thanks to the dual-screen DVD player and entertainment system for your car, so each kid can remain content without the burden of having to interact with their annoying brother or sister. The traditional car ride games will slowly fade away, replaced with questions like, "Grandma, where's the Nintendo?"

    Why stop there? Why not just equip the car with tranquilizing gas in the back seat? The kids go in, you knock them unconscious, and you wake them up when you arrive at the destination.

    One of my friends told me that as a child, she objected that her brother was looking out her window, a degree of territoriality I was previously not aware of. Her parents naturally ridiculed her for making such a complaint, and I think she turned out okay.

  • The Old New Thing

    AOL is moving closer to jettisoning dial-up, except for one person

    • 10 Comments

    When I read this story that AOL is ready to spin off their dial-up service, I wondered if they will remember to give the new dial-up number to the winner of that contest.

  • The Old New Thing

    Psychic debugging: Why can't StreamReader read apostrophes from a text file?

    • 24 Comments

    As is customary, the first day of CLR Week is a warm-up. Actually, today's question is a BCL question, not a CLR question, but only the nitpickers will bother to notice.

    Can somebody explain why StreamReader can’t read apostrophes? I have a text file, and I read from it the way you would expect:

    StreamReader sr = new StreamReader("myfile.txt");
    Console.WriteLine(sr.ReadToEnd());
    sr.Close();
    

    I expect this to print the contents of the file to the console, and it does—almost. Everything looks great except that all the apostrophes are gone!

    You don't have to have very strong psychic powers to figure this one out.

    Here's a hint: In some versions of this question, the problem is with accented letters.

    Your first psychic conclusion is that the text file is probably an ANSI text file. But StreamReader defaults to UTF-8, not ANSI. One version of this question actually came right out and asked, "Why can't StreamReader read apostrophes from my ANSI text file?" The alternate version of the question already contains a false hidden assumption: StreamReader can't read apostrophes from an ANSI text file because StreamReader (by default) doesn't read ANSI text files at all!

    But that shouldn't be a factor, since the apostrophe is encoded the same in ANSI and UTF-8, right?

    That's your second clue. Only the apostrophe is affected. What's so special about the apostrophe? (The bonus hint should tip you off: What's so special about accented letters? What property do they share with the apostrophe?)

    There are apostrophes and there are apostrophes, and it's those "weird" apostrophes that are the issue here. Code points U+2018 (‘) and U+2019 (’) occupy positions 0x91 and 0x92, respectively, in code page 1252, and these "weird" apostrophes are all illegal lead bytes in UTF-8 encoding. And the default behavior for the Encoding.UTF8Encoding encoding is to ignore invalid byte sequences. Note that StreamReader does not raise an exception when incorrectly-encoded text is encountered. It just ignores the bad byte and continues as best it can, following Burak's advice.

    Result: StreamReader appears to ignore apostrophes and accented letters.

    There are therefore multiple issues here. First, you may want to look at why your ANSI text file is using those weird apostrophes. Maybe it's intentional, but I suspect it isn't. Second, if you're going to be reading ANSI text, you can't use a default StreamReader, since a default StreamReader doesn't read ANSI text. You need to set the encoding to System.Text.Encoding.Default if you want to read ANSI text. And third, why are you using ANSI text in the first place? ANSI text files are not universally transportable, since the ANSI code page changes from system to system. Shouldn't you be using UTF-8 text files in the first place?

    At any rate, the solution is to decide on an encoding and to specify that encoding when creating the StreamReader.

    This exercise is just another variation on Keep your eye on the code page.

  • The Old New Thing

    What's with this MSH_MOUSEWHEEL message?

    • 34 Comments

    The hardware folks had this mouse wheel thing they were making, and they needed a way to get applications to support the mouse. Now, one way of doing this was to say, "Well, we'll start selling this wheel mouse, but no applications can use it until the next version of Windows is released, one that supports the wheel." Of course, that would have meant waiting until Windows NT 4 came out, and who know when that would be. Plus it meant that people would have to upgrade Windows in order to take advantage of their fancy new mouse. As you can imagine, they weren't too pleased with the "wait a few years" plan.

    In the interim, they proposed a stopgap mechanism for applications to respond to the mouse wheel. Enter the zmouse.h header file and its MSH_MOUSEWHEEL registered message. When you installed the wheel mouse driver, it listened for wheel events from the hardware and posted this new message when the mouse wheel turned, and applications could just respond to either the WM_MOUSEWHEEL message (if running on a version of Windows that supported the message) or the MSH_MOUSEWHEEL message (if running on an older version of Windows that didn't). Unfortunately, the two messages behave differently, so it's not a simple matter of writing

    if (uMsg == WM_MOUSEWHEEL || uMsg == g_msgWheel) {
     ... do wheel stuff ...
    }
    

    (These next few paragraphs summarize what is already spelled out in MSDN; you can skip them if you already know how the messages work.)

    First, let's look at WM_MOUSEWHEEL. This message is delivered to the window that has focus (in the SetFocus sense). If the window procedure doesn't handle the message and just passes it through to the DefWindowProc function, then the DefWindowProc function forward the message to the window's parent. In this way, the WM_MOUSEWHEEL message automatically "bubbles outward" from the focus window up the parent chain until somebody finally handles the message (or it goes all the way to the top without being handled at all).

    On the other hand, the MSH_MOUSEWHEEL message works from the outside in. It is delivered to the foreground window (in the SetForegroundWindow sense). If the window procedure doesn't want to handle the message, it can forward the message to child windows of its choice, until one of them returns TRUE to indicate that the message was handled, or until no further candidates exist.

    I'll summarize these differences in a table, since people seem to like tables so much.

    WM_MOUSEWHEEL MSH_MOUSEWHEEL
    Propagation direction Inside-out Outside-in
    Propagation mechanism DefWindowProc SendMessage
    Handling Automatic Manual: Application checks return value
    from child to determine what to do next
    Return value if processed Zero TRUE
    Return value if not processed DefWindowProc FALSE

    Notice that WM_MOUSEWHEEL is much simpler, and the inside-out propagation mechanism retains the spirit of other messages such as WM_CONTEXTMENU and WM_SETCURSOR. Why can't MSH_MOUSEWHEEL do it the same way?

    Well, first of all, MSH_MOUSEWHEEL doesn't have the luxury of being able to modify the DefWindowProc function. After all, that's the whole point of introducing the message in the first place, because we're trying to add wheel support to an older operating system that predated mouse wheels. Put in other words, if we could modify DefWindowProc to handle the MSH_MOUSEWHEEL message, then we wouldn't have needed the MSH_MOUSEWHEEL message to begin with; we would've just modified DefWindowProc to handle the WM_MOUSEWHEEL message.

    The argument in the previous paragraph is a frustratingly common one. Given a problem X and a workaround Y, somebody will ask, "Why didn't you use method Z?" If you look at method Z, though, you'll see that it suffers from the exact same problem X.

    Here's a real-world example of the "confused workaround":

    "Since the I-90 bridge is closed, I can't take the 550 bus to get from Bellevue to Safeco Field. Instead, I'm going to take the 230 to Redmond, and then change to the 545."

    — Well, that's silly. Why not take the 245 to Eastgate, and then change to the 554? It's a lot faster.

    "Um, the 554 uses the I-90 bridge, too."

    Okay, so you can't change DefWindowProc, but why not at least propagate the MSH_MOUSEWHEEL from the inside out instead of from the outside in?

    Starting with the focus window assumes you can even find out what the focus window is, but if you had paid attention to the Five Things Every Win32 Programmer Should Know, you would have known that each thread has its own focus window. (Not nitpickily true, but true enough.) Consequently, when the helper program that injects MSH_MOUSEWHEEL messages calls GetFocus, it just gets its own focus window, not the focus window of the thread that controls the foreground window. (Remember, we're talking 1996, long before the GetGUIThreadInfo function was invented. History buffs can find out more from Meet The Inventor of the Mouse Wheel.) Since inside-out was off the table, that pretty much forced outside-in.

    Now that you know how mouse wheel messages work, you can explain the behavior this customer is seeing:

    I'm seeing the WM_MOUSEWHEEL message being delivered to the wrong child window. I have a parent window with two children. Even though I move the mouse pointer over child 1, the WM_MOUSEWHEEL goes to child 2.
  • The Old New Thing

    Wedding + two-year-old flower girl = wildcard

    • 22 Comments

    I was in San Francisco this weekend for a wedding. The flower girl was the bride's two-year-old niece, and when you add a two-year-old to the wedding party, you never know what's going to happen, because two-year-olds don't understand the world the same way adults do.

    During the unity candle ceremony, the two-year-old pushed her way to the table, joining the bride and groom, in order to get a front-row seat for the ritual. And then when the unity candle was lit, she tried to blow it out.

    Because when you're two years old, the only time you see people make a big deal about lighting a candle is when it's atop a birthday cake, and you know what to do with birthday candles!

  • The Old New Thing

    The stroke-count-based sort isn't random, although it looks that way if you only see it in translation

    • 13 Comments

    During the NBC coverage of the opening ceremony of the Beijing Olympics, the announcers more than once said that the teams will not be entering in the normal order, but rather in a random order based on the number of strokes in the team's name as translated into Chinese.

    This is an odd use of the word random. You might say that at the Athens Olympics, the teams did not enter in the normal order, but rather in a random order based on the collation of the characters in the team's name as translated into Greek.

    Teams enter the stadium in the collation order customary for the host nation, with the exception that Greece always goes first and the host nation always goes last. (Which created an interesting puzzle for the Athens Olympics: How do you go both first and last?) For Chinese, there are a variety of collations, which fall into two broad categories: pronunciation-based and stroke-count-based, and the organizers chose one of the stroke-count-based sorts. Fair enough.

    Of course, it seems random if you don't get to see the original Chinese names. And for some reason, NBC didn't show the Chinese names for the countries, so the opportunity to see the sort order in action was lost on the viewing audience. Viewers in the United States who saw the Ghanaian, Canadian, and Gabonese teams enter one after the other would just say "Yeah, that's the stupid random order again," because they didn't see that it was , 拿大, and .

    (Conspiracy theorists would say that NBC didn't show the Chinese names to allow them to manipulate the order of the teams and place the United States closer to the end of the list than it normally would appear.)

    Freaky Raymond-trivia that will probably show up on Wikipedia within 30 minutes: The first character of my given Chinese name is 瑞, which happens also to be the first character for the Chinese names of the countries 瑞士 (Switzerland) and 瑞典 (Sweden), so it's only fitting that the first two languages I learned in a classroom setting were Swiss German and Swedish. Alas, life is not as poetic as it should be, for the second character of my given Chinese name sorts after , so I would actually have marched between Sweden and Nauru (瑙魯). Of course, another impediment to my marching between Switzerland and Sweden is the more significant fact that I am not personally an Olympic team.

    Bonus griping about language reporting: The official Chinese-language cheer has been reported by the English-language press as China, add oil! While literally true, it's also a misleading translation. The phrase 加油 does mean, if you take it apart word by word, to add oil, but as a phrase, it means to refuel or, when used metaphorically, to cheer on. Chinese speakers don't think about oil when they say 加油 any more than English-speakers think of objects hurtling through the air when they say throw a party. In both cases, you only think about the phrase literally when you're making a pun or are otherwise playing around with the language.

Page 2 of 4 (37 items) 1234