August, 2008

  • The Old New Thing

    Why was the RAM drive removed from Windows 95?


    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

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


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





    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.)


    ¹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?


    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


    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

    The unwritten rule of riding a Seattle Metro bus


    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

    What's with this MSH_MOUSEWHEEL message?


    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.

    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

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


    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");

    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

    Wedding + two-year-old flower girl = wildcard


    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

    Icons and cursors know where they came from


    If you create an icon by calling LoadIcon, the window manager loads the specified icon from the module you specified, but it also remembers where the icon came from. (This discussion also applies, mutatis mutandis to cursors, but I will just talk about icons to avoid awkardness.) When you pass the LR_COPYFROMRESOURCE flag to the CopyImage function, the window manager goes back to the original icon source to create the copy you requested rather than blindly stretching the pixels of the icon you passed in.

    Remember that an ICO file represents not just one icon but rather a collection of icons (known as an "icon group"), each at a different size or color depth. The intent is that each icon in the icon group depicts the same underlying image, just tuned for particular settings. (Now, mind you, there is no enforcement of this intent. If you make your 16×16 image a smiling-face and your 32×32 image a barking dog, well, then that's your problem.) For example, a single ICO file might contain a 16×16 image, a 32×32 image, and a 48×48 image. If somebody asks for the icon at one of those sizes, then the corresponding image is used. On the other hand, if somebody asks for, say, the 24×24 image, the window manager will take the 32×32 image and stretch it to the necessary size.

    You can recover this "hidden source information" with the GetIconInfoEx function (new for Windows Vista). If the icon was loaded by ordinal, then the szResName is an empty string and the ordinal is placed in the wResID member. If the icon was loaded by name, then wResID is zero and szResName contains the resource name.

    This is just some background information about icons (and cursors). Next time, we'll put this information to use to solve a problem.

    [Raymond is currently away; this message was pre-recorded.]

  • The Old New Thing

    As a courtesy to performers and other audience members, please turn off blinking head-mounted devices


    When a theater or concert performance begins, there is often an announcement asking audience members to silence their cell phones and similar devices. Some time ago, a light blinked near the ear of a gentleman a few rows in front of me throughout the entire performance. I couldn't tell what it was for sure, but it appeared to be the indicator light of a Bluetooth headset.


    Five seconds later:


    Five seconds later:


    Repeat for two hours.

    What I want to know is, was this guy expecting a call during the show? If his phone rang, would he answer it from his seat?

    (Added: This was before Bluetooth headsets were in common use. It wasn't until after the show that I was able to guess what it was.)

Page 2 of 4 (37 items) 1234