Other

  • The Old New Thing

    I'll see (some of) you in Los Angeles in September

    • 49 Comments

    Jeremy Mazner has asked me to put together a 400-level session at this year's PDC. I came up with the title "Five(ish) things every Win32 developer should know (but likely doesn't)". Of course, now I have to think of five things! Here are some ideas I've been kicking around.

    • The memory scene: Physical address space != physical memory != virtual memory != virtual address space
    • Consequences of the way CPUs work: How my O(n) algorithm can run circles around your O(log n) algorithm; why much of what you learned in school simply doesn't matter
    • Parent vs. owner windows
    • Asynchronous input queues, the hazards of attaching thread input, and how it happens without your knowledge
    • Dialog units, DPI and the overloaded term "large fonts"

    Would you go to a talk that covered these topics? If not, what topics would you rather hear me talk about?

    Follow-up: The talk will be a little over an hour. (And fixed the title. Thanks, Dave.)

  • The Old New Thing

    Google is the cute two-year-old girl with curly blond hair that gets all the attention

    • 85 Comments

    Let's see, Google Maps adds the world outside the United States, Canada and the UK, and people go ga-ga. Nevermind that Google's new "maps" have nothing beyond country boundaries. "Aww, look at Google, she's so cute and adorable!"

    I'm sure the people at the existing online map services like MapQuest and MSN MapPoint are sitting there like older siblings, wondering when exactly they turned into chopped liver. MSN MapPoint has actual maps of most of Europe, and MapQuest's library of maps is larger still. (Kathmandu anyone?) Both sites provide documentation on how to link directly to them. Yet they don't get drooled over.

    Somebody at MapQuest should take out a full page ad that goes something like this:

    Dear Google Maps,

    Welcome to the rest of the world! If you ever need driving directions, don't hesitate to ask.

    Love ever,
    MapQuest

  • The Old New Thing

    Why do NTFS and Explorer disagree on filename sorting?

    • 11 Comments

    Some people have noticed that NTFS automatically sorts filenames, but does so in a manner different from Explorer. Why is that?

    For illustration purposes, I created files with the following names:

    Name Code point Description
    aU+0061Latin small letter A
    bU+0062Latin small letter B
    ×U+00D7Multiplication sign
    åU+00E5Latin small letter A with ring above
    øU+00F8Latin small letter O with stroke

    And here's the sort order for various scenarios, at least on my machine. (You'll later see why it's important whose machine you test on.)

    Plain "dir" command
    aU+0061Latin small letter A
    bU+0062Latin small letter B
    åU+00E5Latin small letter A with ring above
    ×U+00D7Multiplication sign
    øU+00F8Latin small letter O with stroke
     
    "dir /on"
    ×U+00D7Multiplication sign
    aU+0061Latin small letter A
    åU+00E5Latin small letter A with ring above
    bU+0062Latin small letter B
    øU+00F8Latin small letter O with stroke
     
    Explorer sorted by name
    ×U+00D7Multiplication sign
    aU+0061Latin small letter A
    åU+00E5Latin small letter A with ring above
    bU+0062Latin small letter B
    øU+00F8Latin small letter O with stroke

    First, notice that Explorer and "dir /on" agree on the alphabetic sort order. (Once you throw digits into the mix, things diverge.) This is not a coincidence. Both are using the default locale's word sort algorithm.

    Why does the raw NTFS sort order differ?

    Because NTFS's raw sort order has different goals.

    The "dir /on" and Explorer output are sorting the items for humans. When sorting for humans, you need to respect their locale. If my computer were in Sweden, Explorer and "dir /on" would have sorted the items in a different order:

    ×U+00D7Multiplication sign
    aU+0061Latin small letter A
    bU+0062Latin small letter B
    åU+00E5Latin small letter A with ring above
    øU+00F8Latin small letter O with stroke

    You can ask a Swede why this is the correct sort order if you're that curious. My point is that different locales have different sorting rules.

    NTFS's raw sort order, on the other hand, is not for humans. As we saw above, sorting for humans can result in different results depending on which human you ask. But there is only one order for files on the disk, and NTFS needs to apply a consistent rule so that it can find a file when asked for it later.

    In order to maintain this consistency, the NTFS raw sort order cannot be dependent upon such fickle properties as the current user's locale. It needs to lock in a sort algorithm and stick to it. As Michael Kaplan pointed out earlier, NTFS captures the case mapping table at the time the drive is formatted and continues to use that table, even if the OS's case mapping tables change subsequently. Once the string has been converted to uppercase, it then needs to be sorted. Since this is not for humans, there's no need to implement the complex rules regarding secondary and tertiary keys, the interaction between alphanumerics and punctuation, and all the other things that make sorting hard. It just compares the code points as binary values, also known as an ordinal sort.

    In summary, therefore, Explorer sorts the items so you (a human) can find them. NTFS sorts the items so it (the computer) can find them. If you're writing a program and you want the results of a directory listing to be sorted, then sort it yourself according to the criteria of your choice.

  • The Old New Thing

    Why can't the default drag/drop behavior be changed?

    • 44 Comments

    A common reaction to my explanation of whether dragging a file will result in a move or copy was that there should be a setting that lets you change the algorithm by which Explorer decides whether you want to move or copy.

    There are a few reasons why this is a bad idea.

    First, if there were such a setting, then it removes some of the predictability from the user interface. One of the benefits of a common user interface is that once you learn it, you can apply the rules generally. But if each user could customize how drag/drop works, then the knowledge you developed with drag/drop wouldn't transfer to other people's machines.

    Some people view infinite customizability as a good thing. But each added bit of customizability increases the possibility that a particular combination of settings won't get tested as heavily as perhaps it should. ("What do you mean, this doesn't work if you have the notification icons set to hide after only 5 seconds, the taskbar's auto-hide delay customized to a value larger than 5 seconds, the taskbar buttons customized to a boldface font larger than 14pt, and drag/drop operations defaulting always to move? How could you have missed that combination in your testing? Surely you should have anticipated the interaction between an auto-hide delay longer than the notification auto-hide delay combined with a nondefault drag on a network with fewer than 50 machines!")

    Infinite customizability also means that you can't just sit down in front of somebody's machine and start using it. You first have to learn how they customized their menus, button clicks, default drag effects, and keyboard macros. "Oh, on this machine, you paste by shift-right-clicking. Sorry. On my machine, I use ctrl-alt-middle-click to paste." Imagine if everybody could easily customize the order of the clutch, brake, and gas pedals in their car to suit their fancy.

    There is also the branding element. Like the Mac, Windows tries to cultivate a specific "look" that makes people say, "Hey, this computer is running Windows; I know how to use it!" My DVD player and my car both show the manufacturer's logo when they are booting up. So too does Windows.

    Even if the "change the default drag/drop behavior" option passed "settings court" and was deemed worth the additional test cost, you still have the problem that it affects only Explorer. Other programs would continue to use the old algorithm, at least until you found their settings to change how they perform default drag/drop as well, if such a setting existed at all. Imagine the confusion if Windows Explorer followed one set of rules, but Microsoft Outlook followed a different set of rules. "Oh right, this is a mail message I'm dragging; the default operation is going to be a move, not a copy."

  • The Old New Thing

    What if two programs did this?

    • 54 Comments

    The thought experiment "Imagine if this were possible" is helpful in thinking through whether Windows lets you do something or other. (A special case of this is "When people ask for security holes as features.") If the possibility leads to an obvious contradiction or the violation of generally-accepted rules of metaphysics, then you can be pretty sure that Windows doesn't support it. (Of course, the absence of such a contradiction doesn't prove that Windows does support it. But you can use it to rule out obvious bad ideas.)

    The question "What if two programs did this?" is also helpful in evaluating a feature or a design request. Combining this with "Imagine if this were possible" leads to an impressive one-two punch. Here are a few examples:

    "How do I create a window that is never covered by any other windows, not even other topmost windows?"

    Imagine if this were possible and imagine if two programs did this. Program A creates a window that is "super-topmost" and so does Program B. Now the user drags the two windows so that they overlap. What happens? You've created yourself a logical impossibility. One of those two windows must be above the other, contradicting the imaginary "super-topmost" feature.

    "How do I mark my process so that it always the first/last to receive the system shutdown notification? I want to do something before/after all other programs have shut down."

    Imagine if this were possible and imagine if two programs did this. You now have two programs both of which want to be first/last. But you can't have two first or two last things. One of them must lose. (This of course generalizes to other things people might want to be first or last.)

    "How do I make sure that my program is always the one that runs when the user double-clicks an .XYZ file?"

    Imagine if this were possible and imagine if two programs did this. Now the user double-clicks an .XYZ file. Which program runs?

    In this case, the solution is to leave the user in charge of their file associations; if they decide that they want your competitor's program to be used for .XYZ files, then that's their decision and you should respect it.

    My colleague Zeke [link fixed 11am], who is responsible, among other things, for the way file associations work in Explorer, provides a few alternatives:

    Here is the entry point in MSDN to the documentation on file associations in Explorer.

    For many of these "I want to be the X-est"-type questions, you can often come up with some sort of hack, where you run a timer that periodically checks whether you are still X, and if not, pushes you back into the X-position. And then you stop and think, "What if two programs did this?" and realize that it's a bad idea. At least I hope you do.

    Even with this explanation, some people still don't get it. I'll ask them to consider, "What if two programs did this? They'll be fighting back and forth," and the answer I get back is, "Then I can have the second program check if the first program is already running." They don't understand that they didn't write the second program.

    When two programs "duke it out" like this, you can't predict which one will win, but you can predict with 100% certainty who will lose: The user.

    I remember well when one of my colleagues called me into his office to show me two very popular commercial programs that both wanted to guarantee that they were the program that ran when the user double-clicked an .XYZ document. Since there is no such guarantee, they faked it with the timer hack.

    You installed the first program, it set itself as the .XYZ file handler, and everything seemed normal. You then installed the second program, it set itself as the new .XYZ file handler, and the first program noticed and said, "Uh-uh, I'm the program that runs .XYZ files", and changed things back. Then the second program said, "No way, I'm the program that runs .XYZ files" and set itself back.

    This childish game of "Nuh-uh/Yuh-huh!" went on while the user sat there dumbfounded and helpless, watching the icon for their .XYZ files flicker back and forth between the two programs, both of whom egotistically believed they were doing the user a "favor" by insisting on being the program that runs .XYZ files.

  • The Old New Thing

    If strncpy is so dangerous, why does Visual Studio 2005 still support it?

    • 45 Comments

    In response to the news that strncpy is so dangerous, at least one person has called for Visual Studio to revoke support for such a dangerous function, considering the continued support for the function grounds for holding the compiler manufacturer liable for any defects in programs compiled with that compiler.

    Well, for one thing, while it's true that strncpy is dangerous if used improperly, it is still a valid function, and my original discussion explained the history behind strncpy and the very specific scenario in which it is still useful. It just so happens that most people don't use the function in the manner it was intended, but instead treat it as a sort of "copy string with a character limit" function, which it isn't really.

    For another thing, just because something is dangerous doesn't mean it shouldn't be supported. Pointers and casts are dangerous, but I don't see them disappearing from C or C++ any time soon.

    Third, support for strncpy is mandated by the C standard. If you removed it, you couldn't call yourself a C compiler any more. (Not to mention breaking compatibility with existing source code that uses the strncpy function. How would you like it if you bought a so-called C compiler and found that it couldn't compile a large class of valid C programs?)

  • The Old New Thing

    Using /LARGEADDRESSAWARE on 64-bit Windows for 32-bit programs

    • 11 Comments

    Probably the biggest advantage of 64-bit Windows is not the larger registers but rather the expansive 64-bit address space. Recall that even when the /3GB switch is set, 32-bit programs receive only 2GB of address space unless they indicate their willingness to cope with addresses above 2GB by passing the /LARGEADDRESSAWARE flag.

    This flag means the same thing on 64-bit Windows. But since 64-bit Windows has a much larger address space available to it, it can afford to give the 32-bit Windows program the entire 4GB of address space to use. This is mentioned almost incidentally in Knowledge Base article Q889654 in the table "Comparison of memory and CPU limits in the 32-bit and 64-bit versions of Windows".

    In other words, certain categories of 32-bit programs (namely, those tight on address space) benefit from running on 64-bit Windows machine, even though they aren't explicitly taking advantage of any 64-bit features.

  • The Old New Thing

    Why are DLLs unloaded in the "wrong" order?

    • 15 Comments

    When a program starts or when a DLL is loaded, the loader builds a dependency tree of all the DLLs referenced by that program/DLL, that DLL's dependents, and so on. It then determines the correct order in which to initialize those DLLs so that no DLL is initialized until after all the DLLs upon which it is dependent have been initialized. (Of course, if you have a circular dependency, then this falls apart. And as you well know, calling the LoadLibrary function or the LoadLibraryEx function from inside a DLL's DLL_PROCESS_ATTACH notification also messes up these dependency computations.)

    Similarly, when you unload a DLL or when the program terminates, the de-initialization occurs so that a DLL is de-initialized after all its dependents.

    But when you load a DLL manually, crucial information is lost: Namely that the DLL that is calling LoadLibrary depends on the DLL being loaded. Consequently, if A.DLL manually loads B.DLL, then there is no guarantee that A.DLL will be unloaded before B.DLL. This means, for example, that code like the following is not reliable:

    HSOMETHING g_hSomething;
    typedef HSOMETHING (WINAPI* GETSOMETHING)(void);
    typedef void (WINAPI* FREESOMETHING)(HSOMETHING);
    
    GETSOMETHING GetSomething;
    FREESOMETHING FreeSomething;
    
    // Ignoring race conditions for expository purposes
    void LoadB()
    {
     HINSTANCE hinstB = LoadLibrary(TEXT("B.DLL"));
     if (hinstB) {
      GetSomething = (GETSOMETHING)
              GetProcAddress(hinstB, "GetSomething");
      FreeSomething = (FREESOMETHING)
              FreeProcAddress(hinstB, "FreeSomething");
     }
    }
    
    // Ignoring race conditions for expository purposes
    HSOMETHING CacheSomethingFromB()
    {
     if (!g_hSomething &&
         GetSomething && FreeSomething) {
      g_hSomething = GetSomething();
     }
     return g_hSomething;
    }
    
    BOOL CALLBACK DllMain(HINSTANCE hinst,
          DWORD dwReason, LPVOID lpReserved)
    {
     switch (dwReason) {
     ...
     case DLL_PROCESS_DETACH:
      if (g_hSomething) {
       FreeSomething(g_hSomething); // oops
      }
      break;
     }
     return TRUE;
    }

    At the line marked "oops", there is no guarantee that B.DLL is still in memory because B.DLL does not appear in the dependency list of A.DLL, even though there is a runtime-generated dependency caused by the call to LoadLibrary.

    Why can't the loader keep track of this dynamic dependency? In other words when A.DLL calls LoadLibrary(TEXT("B.DLL")), why can't the loader automatically say "Okay, now A.DLL depends on B.DLL"?

    First of all, because as I've noted before, you can't trust the return address.

    Second, even if you could trust the return address, you still can't trust the return address. Consider:

    // A.DLL - same as before except for one line
    void LoadB()
    {
     HINSTANCE hinstB = MiddleFunction(TEXT("B.DLL"));
     if (hinstB) {
      GetSomething = (GETSOMETHING)
              GetProcAddress(hinstB, "GetSomething");
      FreeSomething = (FREESOMETHING)
              FreeProcAddress(hinstB, "FreeSomething");
     }
    }
    
    // MIDDLE.DLL
    HINSTANCE MiddleFunction(LPCTSTR pszDll)
    {
     return LoadLibrary(pszDll);
    }
    

    In this scenario, the load of B.DLL happens not directly from A.DLL, but rather through an intermediary (in this case, MiddleFunction). Even if you could trust the return address, the dependency would be assigned to MIDDLE.DLL instead of A.DLL.

    "What sort of crazy person would write a function like MiddleFunction?", you ask. This sort of intermediate function is common in helper/wrapper libraries or to provide additional lifetime management functionality (although it doesn't do it any more, though it used to).

    Third, there is the case of the GetModuleHandle function.

    void UseBIfAvailable()
    {
     HINSTANCE hinstB = GetModuleHandle(TEXT("B"));
     if (hinstB) {
      DOSOMETHING DoSomething = (DOSOMETHING)
              GetProcAddress(hinstB, "DoSomething");
      if (DoSomething) {
       DoSomething();
      }
     }
    }
    

    Should this call to GetModuleHandle create a dependency?

    Note also that there are dependencies among DLLs that go beyond just LoadLibrary. For example, if you pass a callback function pointer to another DLL, you have created a reverse dependency.

    A final note is that this sort of implicit dependency, as hard as it is to see as written above, is even worse once you toss global destructors into the mix.

    class SomethingHolder
    {
    public:
     SomethingHolder() : m_hSomething(NULL);
     ~SomethingHolder()
      { if (m_hSomething) FreeSomething(m_hSomething); }
     HSOMETHING m_hSomething;
    };
    
    SomethingHolder g_SomethingHolder;
    ...
    

    The DLL dependency is now hidden inside the SomethingHolder class, and when A.DLL unloads, g_SomethingHolder's destructor will run and try to talk to B.DLL. Hilarity ensues.

  • The Old New Thing

    You can't escape those AOL CDs

    • 15 Comments

    One of my colleagues was unpacking one of those $30,000 quad-processor more-memory-than-you-know-what-to-do-with super-server computers. The kind that require their own electrical substation.

    And it came with an AOL CD.

    It's like buying a Lexus and finding a 35-cents-off coupon in the glove compartment.

    Apparently, one of the questions AOL tech support asks when people call in complaining that they can't get their AOL CD to work is, "Do you have a computer?" [Opening Panel Round, second question] because so many people who don't have computers stick the CD into their stereo or DVD player and can't get it to work.

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

  • The Old New Thing

    Another dead computer: My personal laptop

    • 33 Comments

    I'm kind of surprised at how much people reacted to my previous dead computer story. I guess there's an audience for stories about dead computers.

    Today's dead computer is my Sony Vaio PCG-Z505LE laptop, with a 600MHz processor and 192MB of RAM. Certainly a big step up from that 486/50 with 12MB of RAM.

    Laptop computers have a comparatively short lifetime. (Hardware vendors must love them.) I've learned that the best value comes from buying used laptops. You have to accept being a bit behind the curve, but the way I use my laptop, it needs to do only a small number of things:

    • surf the web,
    • read e-mail,
    • use Remote Desktop Connection to access my desktop machine,
    • download pictures from my digital camera, and
    • compile the occasional program.

    Only that last operation requires a hefty processor, and I do it so rarely that it doesn't bother me that it's kind of slow. (I just run the command line version of the compiler, so that at least takes the IDE overhead out of the picture.)

    I bought this laptop two years ago, used, and it ran just fine until a couple months ago when the internal power supply burnt out. I was ready to abandon the model line and give away the accessories I had bought, including a $200+ double-capacity battery.

    Allow me to digress on laptop batteries. Observe that batteries for old-model laptops cost almost as much as the laptops themselves. That's because the battery is the only real consumable in a laptop computer. The other components will run practically indefinitely if you don't drop them or douse them in soda, but batteries just plain wear out. That's where the money is.

    This means that many ads for used laptops will mention "needs new battery" at the end. And those are the ones I sought out. Because I have a working battery! Most prospective buyers would be turned off by a dead battery, but that didn't bother me one bit.

    The replacement laptop arrived a few days ago, and it runs great. I wiped the drive and reinstalled Windows XP from scratch. (Challenging because the laptop doesn't come with a bootable CD-ROM drive. I had to use floppies!) I may install a handful of programs but that's all. I don't like installing software on my computer. The more programs you install, the more likely there's going to be a conflict somewhere.

    The old laptop has already started being scavenged for parts. A friend of mine needed a replacement laptop hard drive, so I gave him my old one. The battery and power brick can of course be used by the new laptop. The memory from the old Vaio is no use, since the Vaio has only one memory expansion slot. The other parts of the old laptop aren't much use for anything aside from spares. Perhaps I should put the old laptop on concrete blocks on my front lawn.

    Next time (if there is a next time), the story of the dead AlphaServer.

Page 81 of 93 (922 items) «7980818283»