October, 2012

  • The Old New Thing

    Irony patrol: Recycling bins

    • 40 Comments

    Microsoft has a large corporate recycling effort. Every office, every mail room, every kitchenette, every conference room has a recycling bin. The dining facilities earned Green Restaurant Certification, and there is a goal of making the cafeterias a zero-landfill facility by 2012. (Hey, that's this year!)

    A few years ago, I found one room in my building that didn't have a recycling bin, and you'd think it'd be one of the rooms near the top of the list for needing one.

    The room without a recycling bin was the copy machine room.

    As a result, people were throwing their unwanted cover sheets and other paper waste into the regular garbage.

    I decided to be somebody. I took a recycling bin from an unused office and moved it into the copy room.

    Bonus recycling bin irony: For many years, each office had three recycling bins, each labeled for its intended contents: white paper, mixed paper, and aluminum cans. Improvements in automated sorting technology removed the need to separate these recyclables manually, and in 2008, all three recycling bins were replaced with a single recycle bin, which was labeled with the simple three-arrow recycling logo.

    The irony is that Microsoft was going to toss all the old recycling bins into a landfill because they couldn't find anybody who wanted them.

    Alert Microsoft employee Tom Roth found the right people in Building Facilities and got them to stop the trucks as they were about 100 feet from dumping 40,000 perfectly good plastic bins into a landfill. Tom's son Justin works in the recycling industry, and he used his contacts to get the word out, and soon requests for recycling bins were coming in from all over the state of Washington. It took three months, but they eventually found homes for all of the recycle bins.

    Ironic disaster averted.

  • The Old New Thing

    The cries of "Oh no!" emerge from each office as the realization slowly dawns

    • 35 Comments

    Today is the (approximate) 15th anniversary of the Bedlam Incident. To commemorate that event, here's a story of another email incident gone horribly awry.

    Some time ago, an email message was sent to a large mailing list. It came from somebody in the IT department and said roughly, "This is a mail sent on behalf of Person X to check if your XYZ server has migrated to the new datacenter. Please visit http://blahblah and confirm that your server name is of the form XXYYNNZZ. If not, please Reply to All."

    Uh-oh.

    The seasoned Microsoft employees (and the new employees who paid attention during new employee orientation) recognized the monster that was about to be unleashed, and the cries of "Oh no!" could be heard emerging from each office as the realization dawned.

    And then it started. All the replies from people saying "I'm still on the old datacenter." And then the replies from people saying, "Stop replying!"

    What's frustrating is that you can't do anything about the catastrophe that is unfolding. Any attempt to reply to the message telling people to stop replying only makes the problem worse. All you can do is stand back and wait for the fire to burn itself out.

    Ten minutes later, Person X sent a message to the mailing list. "Please DO NOT Reply all to this email thread. I am working with the IT department to see if there is another way to get this information."

    It took another ten minutes for the messages to finally stop, but that seems to have shut things down. Now it's time for blame and speculation!

    We were never told whose brilliant idea it was to try to gather information by sending mail to 7000 people telling them to reply all. One theory was that Person X went to the IT department saying "Hi, I'd like to collect information XYZ from this large list of people. Can you help?" And some new hire in the IT department said, "Sure, I can get that information for you. I'll just send everybody some email!"

    After the dust settled, somebody made a tally of the damage.

    Number of people on the mailing list: around 7000.
    Number of replies: 70.
    Of those, number of replies saying "stop replying": 17.

    To commemorate this event, a colleague of mine who maintains a popular internal tool pushed out an upgraded version. The new version had a checkbox on the main page:

    I have not been migrated to the new datacenter.

    Bonus chatter: It so happens that the message was sent at the beginning of the summer, and most of the "I'm still on the old datacenter" replies came from summer interns. Maybe it was a test.

  • The Old New Thing

    What's the difference between F5 and F8 at the boot screen?

    • 34 Comments

    Ian B wondered what the difference is between pressing F5 and F8 while Windows is booting.

    I have no idea either. My strategy was to just mash on the function keys, space bar, DEL key, anything else I can think of. Keep pressing them all through the boot process, and maybe a boot menu will show up.

    The F5 hotkey was introduced in Windows 95, where the boot sequence hotkeys were as follows:

    • ESC - Boot in text mode.
    • F5 - Boot in Safe Mode.
    • Shift+F5 - Boot to Safe Mode MS-DOS.
    • Ctrl+F5 - Boot to Safe Mode MS-DOS with drive compression disabled.
    • Alt+F5 - Boot with LOADTOP=0 for Japanese systems.
    • F6 - Boot in Safe Mode with networking.
    • F4 - Boot to previous version of MS-DOS.
    • Ctrl+F4 - Boot to previous version of MS-DOS with drive compression disabled.
    • F8 - Boot to menu.
    • Shift+F8 - Boot with step-by-step confirmation.
    • Ctrl+F8 - Boot with step-by-step confirmation with drive compression disabled.

    Man, that's an insane number of boot options all buried behind obscure function keys. Boy am I glad we got rid of them. This frees up room in my brain for things like Beanie Baby trivia.

    Bonus chatter: The next generation of computers boots so fast that there's no time to hit any of these hotkeys!

  • The Old New Thing

    Usage guidance for a popcorn machine in the kitchenette

    • 34 Comments

    My colleague KC Lemson tipped me off to a sign hanging next to a popcorn machine in one of the kitchens:

     

    Please do not leave
    popcorn kettle on

    while unattended.
    The fire truck will
    come
    , and they don't
    want any popcorn.

    🚒

    A friend of mine happened to have a chat with a fire fighter who used to be assigned to the fire station nearest to Microsoft main campus. According to him, the top three reasons for being called to a Microsoft building are (in no particular order)

    • Burnt popcorn.
    • Somebody has a panic attack and mistakes it for a heart attack.
    • Somebody pulls the fire alarm because they are under a lot of stress because they missed a deadline, and they think pulling the fire alarm will buy them some time. (Where do they think they are, college?)

    This week is Fire Prevention Week.

  • The Old New Thing

    Diversion: Generating a random color from JavaScript

    • 32 Comments

    A colleague posed a little puzzle for a fun little app he was writing in HTML: He wanted to generate a random color.

    If you search around the intertubes, you can find several possible attempts at a solution, like this collection, and an interesting example that has trouble with the pigeonhole principle.

    The original function to generate a random color went like this:

    // Pad a string of up to two characters with a leading zero
    // so the result is always exactly two characters long.
    function padZero(v) {
     return (v.length == 1) ? '0' + v : v;
    }
    
    function randomColor() {
     return "#" + padZero(Math.floor(Math.random() * 256)).toString(16) +
                  padZero(Math.floor(Math.random() * 256)).toString(16) +
                  padZero(Math.floor(Math.random() * 256)).toString(16);
    }
    

    Can you do better? (My solution after the jump.)


    That was a short jump.

    My first simplification was recognizing that three random 8-bit values is the same as one random 24-bit value.

    function padZeros6(v) {
     while (v.length < 6) v = "0" + v;
     return v;
    }
    
    function randomColor() {
     return "#" + 
        padZeros6(Math.floor(Math.random() * 16777216).toString(16));
    }
    

    Next, I got rid of the padZeros6 function by simply setting bit 25 to force a 7-digit result, then removing the leading 1.

    function randomColor() {
     return "#" + 
        (Math.floor(Math.random() * 16777216) +
                                    16777216).toString(16).substr(1);
    }
    

    Finally, I did some factoring.

    function randomColor() {
     return "#" + 
        Math.floor((1 + Math.random()) * 16777216).toString(16).substr(1);
    }
    

    That last bit was a bit dodgy due to the wonders of floating point arithmetic, but hey, it's a puzzle now.

    Finally, I realized that CSS supports #rgb as shorthand for #rrggbb, so if you don't mind that your color palette is reduced to 4096 colors (and in the case of my colleague's little app, that was not an issue), you can shorten it a bit more:

    function randomColor() {
     return "#" + 
        Math.floor((1 + Math.random()) * 4096).toString(16).substr(1);
    }
    
  • The Old New Thing

    In the conversion to 64-bit Windows, why were some parameters not upgraded to SIZE_T?

    • 29 Comments

    James wonders why many functions kept DWORD for parameter lengths instead of upgrading to SIZE_T or DWORD_PTR.

    When updating the interfaces for 64-bit Windows, there were a few guiding principles. Here are two of them.

    • Don't change an interface unless you really need to.
    • Do you really need to?

    Changing an interface causes all sorts of problems when porting. For example, if you change the parameters to a COM interface, then you introduce a breaking change in everybody who implements it. Consider this hypothetical interface:

    // namedobject.idl
    interface INamedObject : IUnknown
    {
        HRESULT GetName([out, string, sizeof(cchBuf)] LPWSTR pszBuf,
                        [in] DWORD cchBuf);
    };
    

    And here's a hypothetical implementation:

    // contoso.cpp
    class CContosoBasicNamedObject : public INamedObject
    {
        ...
        HRESULT GetName(LPWSTR pszBuf, DWORD cchBuf)
        {
            return StringCchPrintfW(pszBuf, cchBuf, L"Contoso");
        }
        ...
    };
    

    Okay, now it's time to 64-bit-ize this puppy. So you do the natural thing: Grow the DWORD parameter to DWORD_PTR. Since DWORD_PTR maps to DWORD on 32-bit systems, this is a backward-compatible change.

    // namedobject.idl
    interface INamedObject : IUnknown
    {
        HRESULT GetName([out, string, sizeof(cchBuf)] LPWSTR pszBuf,
                        [in] DWORD_PTR cchBuf);
    };
    

    Then you recompile the entire operating system and find that the compiler complains, "Cannot instantiate abstract class: CContosoBasicNamedObject." Oh, right, that's because the INamed­Object::Get­Name method in the implementation no longer matches the method in the base class, so the method in the base class is not overridden. Fortunately, you have access to the source code for contoso.cpp, and you can apply the appropriate fix:

    // contoso.cpp
    class CBasicNamedObject : public INamedObject
    {
        ...
        HRESULT GetName(LPWSTR pszBuf, DWORD_PTR cchBuf)
        {
            return StringCchPrintfW(pszBuf, cchBuf, L"Basic");
        }
        ...
    };
    

    Yay, everything works again. A breaking change led to a compiler error, which led you to the fix. The only consequence (so far) is that the number of "things in code being ported from 32-bit Windows to 64-bit Windows needs to watch out for" has been incremented by one. Of course, too much of this incrementing, and the list of things becomes so long that developers are going to throw up their hands and say "Porting is too much work, screw it." Don't forget, the number of breaking API changes in the conversion from 16-bit to 32-bit Windows was only 117.

    You think you fixed the problem, but you didn't. Because there's another class elsewhere in the Contoso project.

    class CSecureNamedObject : public CBasicNamedObject
    {
        ...
        HRESULT GetName(LPWSTR pszBuf, DWORD cchBuf)
        {
            if (IsAccessAllowed())
            {
                return CBasicNamedObject::GetName(pszBuf, cchBuf);
            }
            else
            {
                return E_ACCESSDENIED:
            }
        }
    }
    

    The compiler did not raise an error on CSecure­Named­Object because that class is not abstract. The INamed­Object::Get­Name method from the INamed­Object interface is implemented by CBasic­Named­Object. All abstract methods have been implemented, so no "instantiating abstract class" error.

    On the other hand, the CSecure­Named­Object method wanted to override the base method, but since its parameter list didn't match, it ended up creating a separate method rather than an override. (The override pseudo-keyword not yet having been standardized.) As a result, when somebody calls the INamed­Object::Get­Name method on your CSecure­Named­Object, they don't get the one with the security check, but rather the one from CBasic­Named­Object. Result: Security check bypassed.

    These are the worst types of breaking changes: The ones where the compiler doesn't tell you that something is wrong. Your code compiles, it even basically runs, but it doesn't run correctly. Now, sure, the example I gave would have been uncovered in security testing, but I chose that just for drama. Go ahead and substitute something much more subtle. Like say, invalidating the entire desktop when you pass NULL to Invalidate­Rect.

    Okay, so let's look back at those principles. Do we really need to change this interface? The only case where expanding to SIZE_T would make a difference is if an object had a name longer than 2 billion characters. Is that a realistic end-user scenario? Not really. Therefore, don't change it.

    Remember, you want to make it easier for people to port their program to 64-bit Windows, not harder. The goal is make customers happy, not create the world's most architecturally pure operating system. And customers aren't happy when the operating system can't run their programs (because every time the vendor try to port it, they keep stumbling over random subtle behavior changes that break their program).

  • The Old New Thing

    The TEMP directory is like a public hot tub whose water hasn't been changed in over a year

    • 28 Comments

    A customer reported that they couldn't install product X. When they ran the installer, the got the error message

    setup.exe - Application Error
    The application was unable to start correctly (0xc00000ba). Click OK to close the application.

    The product X setup team weren't sure what to make of this, and they asked if anybody had any ideas.

    The error code 0xc00000ba is STATUS_FILE_IS_A_DIRECTORY, which means that something was supposed to be a file, but instead it was a directory. The path-searching algorithm is not a backtracking algorithm, so once it finds something wrong, it just stops rather than backing up and trying the next directory.

    This was enough of a clue to direct further investigation, which revealed that the customer had a directory named C:\Users\Bob\AppData\Local\Temp\version.dll\. The customer responded, "There are plenty of directories with names of DLLs in my TEMP directory, but getting rid of this one fixes the issue. Thanks!"

    (Puzzle: Why are there so many directories with the names of DLLs? Psychic answer.)

    I slipped something past you a little while back. Did you notice?

    Okay, I gave it away in the subject line. The setup program is running from the TEMP directory. That should already set off alarm bells.

    The TEMP directory is a dumping ground of random junk. A downloader may have put a DLL there and forgotten to delete it. (Or worse, expected it to stay there forever.) And that DLL might be from an incompatible version of some DLL your setup program uses. (I have seen applications ship their own custom versions of system DLLs! Yeah, because the x86 version of shlwapi.dll from Windows 2000 is drop-in compatible with the version of shlwapi.dll that comes with Windows 7.) Who knows what other yucky things have been lying around in that directory. Since the application directory is the first directory searched when the system looks for a DLL, a rogue DLL in the TEMP directory is a trap waiting to be sprung. (A similar issue applies to a shared Downloads directory.)

    It's like the horror movie trope where the frightened pretty girl runs into a room, slams the door shut, then breathes a sigh of relief, believing herself to be safe. But she didn't check that the room was empty! (In other words, she created her airtight hatchway around an insecure room.)

    The Program X setup team decided to change their installer so that it created a subdirectory of TEMP and extracted the main setup program there. That way, it got a fresh hot tub with clean water.

    Remember, the directory is the application bundle. If you drop your application into a random directory, you've just added everything in that directory to your bundle. And if you don't secure your application directory, you're allowing anybody to add components to your bundle. That's one of the reasons why the Logo program encourages (requires?) applications to install into the Program Files directory: The ACLs on the Program Files directory allow write access only to administrators and installers. This makes the application bundle secure by default. If you want to make your application bundle insecure, you have to go out of your way.

  • The Old New Thing

    How does Explorer deal with recent files that were renamed?

    • 25 Comments

    Roni wonders how Explorer manages to keep track of files that were moved or renamed. Specifically, "opening a shortcut to a renamed file actually updates the shortcut's destination and opens the renamed file. How is this done? Is it an NTFS feature?"

    This feature has been around since Windows 95. If the target of a shortcut no longer exists, the shell tries to resolve the shortcut; i.e., find the object, wherever it ended up moving to. As I explained several months before the question was posted, the algorithm used by the shell varies depending on the operating system and the file system and your domain policies. Possibly also the phase of the moon, one can never be sure.

    It's not that Explorer actually keeps track of the files as they move around, just in case you had a shortcut to them. Rather, the shortcut remembers enough information about the file so that if the file moves, Explorer can try looking for it.

    The fact that shortcuts can resolve targets means that shortcuts are a handy tool for keeping track of files that might move around. If you want to keep track of a file, you can just create a shortcut to it (you don't even need to save it in a file), and when it comes time to find the file, you just resolve the shortcut.

  • The Old New Thing

    If there's a problem with a wiki, then you can fix it; that's why it's a wiki

    • 24 Comments

    On an internal mailing list, somebody asked a question about how to do X with Y, and I replied with a link to an internal wiki that described how to do X with Y (answer: use the Z tool).

    Somebody else replied, "Time to update that article because the link to the Z tool is broken."

    Apparently, this person forgot that one of the defining characteristics of a wiki is that it's easy to edit. (Another characteristic is that it is collaboratively-edited; there is no central authority.)

    In other words, if you see something wrong, fix it yourself. Don't just stand around saying somebody should do something. Be someone. Because on a wiki, there is no default value for somebody.

  • The Old New Thing

    Combo boxes have supported incremental searching for quite some time now

    • 21 Comments
    Back in August 2007, I promised to post a program the following day but it appears that I never did. Oops. I discovered this as I went through my "things to blog about" pile and figured better late than never. Though five years late is pretty bad.

    Here's a program which fills a combo box with some strings.

    #include <windows.h>
    #include <windowsx.h>
    
    static LPCTSTR rgpszList[] = {
        TEXT("Austria"),
        TEXT("Belgium"),
        TEXT("Bulgaria"),
        TEXT("Cyprus"),
        TEXT("Czech Republic"),
        TEXT("Denmark"),
        TEXT("Estonia"),
        TEXT("Finland"),
        TEXT("France"),
        TEXT("Germany"),
        TEXT("Greece"),
        TEXT("Hungary"),
        TEXT("Ireland"),
        TEXT("Italy"),
        TEXT("Latvia"),
        TEXT("Lithuania"),
        TEXT("Luxembourg"),
        TEXT("Malta"),
        TEXT("Netherlands"),
        TEXT("Poland"),
        TEXT("Portugal"),
        TEXT("Romania"),
        TEXT("Slovakia"),
        TEXT("Slovenia"),
        TEXT("Spain"),
        TEXT("Sweden"),
        TEXT("United Kingdom"),
    };
    
    INT_PTR CALLBACK DlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg) {
        case WM_INITDIALOG:
            for (int i = 0; i < ARRAYSIZE(rgpszList); i++) {
                SendDlgItemMessage(hdlg, 100, CB_ADDSTRING, 0,
                                   (LPARAM)rgpszList[i]);
            }
            return TRUE;
        case WM_COMMAND:
            if (GET_WM_COMMAND_ID(wParam, lParam) == IDCANCEL) {
                EndDialog(hdlg, 0);
            }
            break;
        }
        return FALSE;
    }
    
    int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,
                       LPSTR lpCmdLine, int nShowCmd)
    {
        DialogBox(hinst, MAKEINTRESOURCE(1), NULL, DlgProc);
        return 0;
    }
    
    // scratch.rc
    #include <windows.h>
    
    1 DIALOG 50, 50, 185, 98
    STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_DLGFRAME | WS_SYSMENU
    BEGIN
        COMBOBOX 100,7,40,150,300,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL
    END
    

    Run this program and start typing: "S"-"L"-"O"-"V"-"E"... Hey, look, the combo box is performing incremental search and once you hit the "E", it selected Slovenia, the first item in the list which begins with the letters S-L-O-V-E.

    Wait a few seconds, and try it again. This time, type "S"-"P", and hey look, it selected Spain. You didn't have to go through all those other "S" countries to get to it.

    If you hit F4 to open the combo box and then type "S"-"L"-"O"-"V"-"E", observe that there is a tiny vertical line that tells you where you are in the incremental search string.

    As I noted some time ago, the incremental search resets after four times the double-click time, or two seconds by default.

    Note: The "things to blog about" pile has over 2000 items in it, so there really isn't much need for a Suggestion Box, but I open it up once every few years just for show.

Page 1 of 3 (25 items) 123