July, 2012

  • The Old New Thing

    Why don't per-item custom icons work when I open a Zip file or some other virtual folder?

    • 19 Comments

    A customer observed that when they opened a Zip file containing an Excel spreadsheet saved as XML, the icon for the spreadsheet in the Zip folder is just a plain XML icon rather than a fancy Excel-XML icon. "Is there any way to invoke a shell icon handler on an item inside a Zip folder?"

    Even if there were a way, you wouldn't like it.

    Think about it: In order to determine whether the XML file should get a plain-XML icon or an Excel-XML icon, the Office icon handler needs to open the XML file and sniff around to see if has whatever it is that makes an XML file an Excel-XML file.

    This means that the Zip folder has to extract the file so that the icon handler can sniff it.

    This means that opening a Zip folder would result in decompressing every file in it just so that it can give the decompressed file to the icon handler so the icon handler can say what icon to show.

    You probably wouldn't like that.

    Therefore, Zip folders do not use icon handlers to obtain icons for items inside Zip files. It just uses the generic icon for the file extension.

  • The Old New Thing

    Why doesn't the Low Disk Space warning balloon show up as soon as I run low on disk space

    • 66 Comments

    A customer reported an issue with the title "The notification balloon for Low Disk Space does not appear even if the free disk is very low." They provided the following steps:

    • Install Windows 7 64-bit on a SATA drive.
    • Copy files to the system drive until disk space becomes low.
    • Observe that the notification balloon for Low Disk Space does not immediately appear.
    • The balloon appears approximately ten minutes later.

    You read through the steps nodding, "uh huh, uh huh", and then you get to the last step and you say, "Wait a second, the subject of your report was that the balloon doesn't appear at all, and now you're saying that it appears after ten minutes. So it does appear after all. What is the problem?"

    The customer explained that on earlier versions of Windows, the Low Disk Space warning balloon appeared within one minute, whereas in Windows 7 it can take up to ten minutes for the balloon to appear.

    Yup, that's right.

    In previous versions of Windows, Explorer checked for low disk space once a minute. The Windows performance folks requested that the shell reduce the frequency of checks to improve overall system performance, and the shell team agreed to reduce the frequency to once every ten minutes. (The performance team made other suggestions to reduce the impact of that code that runs every ten minutes.)

    So yes, in Windows 7, it may take up to ten minutes for Explorer to report that you are low on disk space. But Explorer never promised that those reports would be timely. Or that they would even appear in the first place. The behavior is not contractual; it's just a courtesy notification.

    Related: How full does a hard drive have to get before Explorer will start getting concerned? and How do I disable the low disk space notifications?

  • The Old New Thing

    How much gets localized in a localized version of Windows?

    • 25 Comments
    ErikF wonders, "Do the localized versions of Windows translate these weird, out of the way resources? My guess is "no", but I don't have a localized copy of Windows to look at right now."

    There are two questions here.

    The first question is "What content is in scope for localization?"

    • Everything in the resources of a DLL is in scope.
    • Registry keys are not in scope. They can't be, because different users on a machine can have different preferred languages, but there is only one registry. To work around this, the registry uses string redirection for anything that needs to be localized.
    • File names used to be in scope, because there was no facility for displaying a file to the user with a name different from its physical name. That changed in Windows 2000, and the decision to switch completely to MUI-style file names was made in Windows Vista.

    The next question is, "Of everything in scope, how much of it actually gets translated?"

    Historically, the answer has been "All of it." Not just the stuff you might expect, like strings and dialog boxes, but other stuff like animations. As you can imagine, translating every single string and dialog box (and possibly also bitmaps and animations) is an enormous undertaking.

    Windows Vista mixed this up a bit by introducing Language Interface Packs, which are "mostly-localized" versions of Windows. Each Language Interface Pack starts with one of the "fully-localized" versions, and then re-translates a subset of the resources to suit the target language. Strings left untranslated receive the translation from the base language.

    Michael Kaplan appears to have quite a supply of LIP-related puns, which he dips into whenever a new LIP is released.

  • The Old New Thing

    Why doesn't RealGetWindowClass return the real window class for my superclass?

    • 41 Comments

    A customer was reporting that the Real­Get­Window­Class function was not reporting the base window class of their superclass. (Error checking has been elided for expository purposes.)

    // Get the static window class window procedure
    WNDCLASS wc;
    GetClassInfo(NULL, TEXT("static"), &wc);
    WNDPROC StaticWndProc = wc.lpfnWndProc;
    
    // Build our derived class
    wc.lpfnWndProc = AwesomeWndProc;
    wc.hInstance = g_hinst;
    wc.lpszClassName = TEXT("AwesomeWindow");
    RegisterClass(&wc);
    
    LRESULT CALLBACK AwesomeWndProc(HWND hwnd, UINT uMsg,
                                    WPARAM wParam, LPARAM lParam)
    {
        TCHAR szClass[128];
        RealGetWindowClass(hwnd, szClass, 128);
        ASSERT(strcmp(szClass, TEXT("static")) == 0);
    
        switch (uMsg) { ... }
    
        return CallWindowProc(StaticWndProc, hwnd, uMsg, wParam, lParam);
    }
    

    The customer found that the assertion fails, returning a window class name of "AwesomeWindow" instead of "static". "I thought the point of RealGetWindowClass was to dig through the superclassing to find the base class. But it's not returning the base class."

    That's right, because you haven't told it what the base class is yet!

    "What do you mean I haven't told it? It's right there at the end of my function: CallWindowProc(StaticWndProc)."

    Yeah, but that line of code hasn't executed yet. The external behavior of your program is like this:

    WNDCLASS wc;
    wc.style = (something);
    wc.lpfnWndProc = AwesomeWndProc;
    wc.cbClsExtra = (something);
    wc.cbWndExtra = (something);
    wc.hInstance = g_hinst;
    wc.hIcon = (something);
    wc.hCursor = (something);
    wc.hbrBackground = (something);
    wc.lpszMenuName = (something);
    wc.lpszClassName = TEXT("AwesomeWindow");
    RegisterClass(&wc);
    
    
    LRESULT CALLBACK AwesomeWndProc(HWND hwnd, UINT uMsg,
                                    WPARAM wParam, LPARAM lParam)
    {
        TCHAR szClass[128];
        RealGetWindowClass(hwnd, szClass, 128);
        ASSERT(strcmp(szClass, TEXT("static")) == 0);
    
        // ... it doesn't matter what goes here
        // because it hasn't executed yet ...
    

    The window manager isn't clairvoyant. It doesn't know that AwesomeWndProc is going to do a CallWindowProc(StaticWndProc) in the future. All it knows is that somebody registered a class, and then in response to its very first message, that class asked, "Hey, you're so smart, tell me what my base class is."

    The window manager says, "Dude, you haven't shown me any base class yet. So I'm just going to say that you are your own base class."

    Since anything can go into the "... it doesn't matter what goes here ...", we can demonstrate that the window manager cannot possibly know what you're going to pass to CallWindowProc by rewriting it like this:

    // Get the static window class window procedure
    WNDCLASS wc;
    GetClassInfo(NULL, TEXT("static"), &wc);
    WNDPROC StaticWndProc = wc.lpfnWndProc;
    
    // Build our class
    wc.lpfnWndProc = AwesomeWndProc;
    wc.hInstance = g_hinst;
    wc.lpszClassName = TEXT("AwesomeWindow");
    RegisterClass(&wc);
    
    LRESULT CALLBACK AwesomeWndProc(HWND hwnd, UINT uMsg,
                                    WPARAM wParam, LPARAM lParam)
    {
        TCHAR szClass[128];
        RealGetWindowClass(hwnd, szClass, 128);
        ASSERT(strcmp(szClass, TEXT("static")) == 0);
    
        switch (uMsg) { ... }
    
        // Psych! You thought that when I asked for StaticWndProc
        // I was going to be a superclass of "static", but in fact
        // I'm just a regular boring window class.
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    

    If you felt really crazy, you could do this:

    // Get the button window class procedure
    WNDCLASS wcButton;
    GetClassInfo(NULL, TEXT("button"), &wcButton);
    WNDPROC ButtonWndProc = wcButton.lpfnWndProc;
    
    // Get the static window class window procedure
    WNDCLASS wc;
    GetClassInfo(NULL, TEXT("static"), &wc);
    WNDPROC StaticWndProc = wc.lpfnWndProc;
    
    // Build our class
    wc.lpfnWndProc = AwesomeWndProc;
    wc.hInstance = g_hinst;
    wc.lpszClassName = TEXT("AwesomeWindow");
    wc.cbWndExtra = max(wc.cbWndExtra, wcButton.cbWndExtra);
    wc.cbClsExtra = max(wc.cbClsExtra, wcButton.cbClsExtra);
    RegisterClass(&wc);
    
    LRESULT CALLBACK AwesomeWndProc(HWND hwnd, UINT uMsg,
                                    WPARAM wParam, LPARAM lParam)
    {
        TCHAR szClass[128];
        RealGetWindowClass(hwnd, szClass, 128);
        ASSERT(strcmp(szClass, TEXT("static")) == 0);
    
        switch (uMsg) { ... }
    
        // Decide at the last moment what we are.
        static WNDPROC BaseClass = nullptr;
        if (BaseClass == nullptr)
        {
            BaseClass = rand() % 2 ? StaticWndProc : ButtonWndProc;
            // Or if you are particularly perverse,
            // BaseClass = radioactive_decay_has_occurred() ?
            //                StaticWndProc : ButtonWndProc;
        }
        return CallWindowProc(BaseClass, hwnd, uMsg, wParam, lParam);
    }
    

    Since the code to decide the base class hasn't run yet, the window manager will have to use that time machine that the research division has been working on.

  • The Old New Thing

    What happens when you mark a section as DISCARDABLE?

    • 31 Comments

    In the flags you pass to the linker, you can specify that a section be made discardable. What does that mean?

    If you are a kernel-mode driver, the discardable flag means that the contents will be removed from memory after initialization is complete. This is where you put your initialization code and data.

    But if you're writing user-mode code, the discardable flag has no effect.

    Not relevant to the topic but people are going to ask anyway: The discardable flag on resources also has no effect.

    The discardable attribute for user-mode code is a left-over from 16-bit Windows, which had to simulate a hardware memory manager in software. The rule in 16-bit code was that if you marked a segment or resource as discardable, then when memory ran out, the kernel was allowed to throw the segment away, safe in the knowledge that it could get the information back by reading it from the original image.

    In 32-bit Windows, this marking of discardable versus non-discardable memory is not necessary because the memory manager (with the assistance of hardware) can manage it all transparently. For example, if you never modified a code segment, the memory manager knows that it can simply discard the memory because it can recover the data from the original image. If you allocated some zero-initialized memory and never modified it, then the memory manager can just throw the data away because it is very easy to "recover" a page full of zeroes. On the other hand, if you modified some memory, then there is nowhere the memory manager can go to recover the data, so it has to put it in the page file.

    Bonus chatter: "If discardability is meaningless in user mode, should we just delete it?"

    Well, the PE file format is used for both user-mode and kernel-mode components, so you can't delete it from one and not the other since they are the same thing.

    "I have some code that uses the pragma to make a section discardable. Should I just delete it?"

    Maybe. Or maybe that flag is being used by some other part of your application. After all, the flag exists. Maybe some other part of your program uses it as a "free flag" that it usurps for some other purpose. For example, it might be used as a signal to some post-processing tool to mean "This section is exempt from the frob catalog."

    Hopefully there's a comment that tells you why the section is being marked as discardable, and that will help you decide whether it's safe to remove the marking. Windows doesn't care, but some other part of your program might.

  • The Old New Thing

    What does the HTOBJECT hit-test code do?

    • 25 Comments

    Leo Davidson observes that a hit-test code is defined for HTOBJECT, but it is not documented, and wonders what's up.

    #define HTOBJECT            19
    

    The HTOBJECT is another one of those features that never got implemented. The code does nothing and nobody uses it. It was added back in Windows 95 for reasons lost to the mists of time, but when the reason for adding it vanished (maybe a feature got cut), it was too late to remove it from the header file because that would require renumbering HTCLOSE and HTHELP, two values which were in widespread use already. So the value just stayed in the header file, taking up space but accomplishing nothing.

  • The Old New Thing

    Microspeak: radar

    • 21 Comments

    The metaphoric use of the term radar is in general use. It refers to the set of things that some entity is paying attention to. (That entity being the owner of the radar.)

    • To fall off the radar is to stop attracting attention or garnering media coverage.
    • Something that flies below/under the radar is not being detected. This could be intentional (you may be trying to elude detection) or unintentional (you want to be noticed but can't get anybody to pay attention).
    • If you say that something is not on your radar, it means that it is not something you have been paying attention to.
    • To put on someone's radar is to draw someone's attention to something. If you put something on your own radar, it means that you are making an effort to pay attention to it.

    At Microsoft, the metaphorical radar usually belongs to a specific person or group. You might forward an email exchange to a person or group with the message "Just wanted to make sure XYZ was on your radar." In other words, "Just wanted to make sure you were aware of XYZ." There is no requirement that the recipient take action in response to the message, but you're letting them know about XYZ, just in case they weren't already aware of it.

    You can also "put something on X's radar", which means the same as in general use: To make X aware of something. Although there is no requirement that the recipient take action, the phrase is usually used when you expect that X do something. For example, "We're going to be removing support for the beta XYZ interface, and everybody needs to switch over to the RTM interface by next month. Can you put this on the radar of our partner teams?" Translation: "Can you make sure that our partner teams are aware of this? I don't want them to complain that we never warned them."

  • The Old New Thing

    Why hasn't the API hook mechanism for x64 been standardized like it was for x86?

    • 28 Comments
    Joshua posted to the Suggestion Box, "Around the time of WinXP SP2 x86, the API hook mechanism was standardized. Why wasn't the same thing done for x64?"

    Who said it was standardized for x86?

    Hooking APIs is not supported by Windows. There may be specific interfaces that expose hooks (like Co­Register­Initialize­Spy to let you monitor calls to CoInitialize and CoUninitialize, and Set­Windows­Hook­Ex to let you hook various window manager operations) but there is no supported general API hooking mechanism provided by the operating system.

    So I don't know where you got that idea from.

  • The Old New Thing

    The physics of a falling Slinky

    • 5 Comments

    Professor Ted Bunn (no relation), who still gets email about a Black Holes FAQ he wrote over fifteen years ago, recently blogged about what happens when you suspend a Slinky vertically, then let go.

    No wait, come back.

    In Part the First, he provides an alternate (but equivalent) explanation for the phenomenon.

    Allen Downey on his wonderfully-titled blog Probably Overthinking It started in with the equations, which inspired Ted to write Part the Second, which contains with lots of math, but there are also charts.

    Okay, fine, I don't blame you for leaving.

  • The Old New Thing

    What are the recommended locations for storing different types of files?

    • 19 Comments

    Some time back, I provided informal guidance regarding what types of files go into which folders. Here are the official guidelines [pdf], for those looking for something with more authority than just me blathering.

Page 2 of 3 (24 items) 123