February, 2005

  • The Old New Thing

    The history of the Windows PowerToys

    • 81 Comments

    During the development of Windows 95, as with the development of any project, the people working on the project write side programs to test the features they are adding or to prototype a feature. After Windows 95 shipped, some of those programs were collected into the first edition of the Windows 95 Power Toys.

    As I recall, the first edition contained the following toys:

    CabView
    This was a handy internal tool which also served as a test of the shell folder design.

    CDAutoPlay, DeskMenu, FlexiCD, QuickRes
    These were side toys originally written by shell developers for their own personal use.

    Command Prompt Here, Explore From Here
    These were proof-of-concept toys which tested the shell command extension design.

    Round Clock
    This was a program to test regional windows.

    Shortcut Target Menu
    This was a feature idea that didn't quite make it.

    I wasn't around when the decision was made to package these toys up and ship them, so I don't know what the rule was for deciding what was PowerToy-worthy and what wasn't. Nor do I know where the name PowerToy came from. (Probably somebody just made it up because it sounded neat.)

    Upon the enormous success of the PowerToys, a second edition was developed. This time, people knew that they were writing a PowerToy, as opposed to the first edition of the PowerToys which was merely cobbled together from stuff lying around. The second edition of the Windows 95 PowerToys added FindX, Send To X, the Telephony Locator Selector, XMouse, and Tweak UI.

    Later, the kernel team released their own set of toys, known as the Windows 95 Kernel Toys. Alas, the original blurb text is not on the Microsoft downloads site, but here's an archived copy. (In reality, it was I who wrote all of the Kernel Toys, except for the Time Zone Editor, which came from the Windows NT Resource Kit. I also wrote the somewhat whimsical original blurb.)

    This was all back in the day when it was easy to put up something for download. No digital signatures, no virus checking, no paperwork. Just throw it up there and watch what happens. Today, things are very different. Putting something up for download is a complicated process with forms to fill out in triplicate and dark rooms with card readers. I wouldn't be surprised if an abandoned salt mine in Montana were somehow involved.

    Nowadays, every team at Microsoft seems to have their own PowerToys, trading on the good name of the Windows shell team who invented the whole PowerToys idea. (As far as I can tell, we don't get any royalties from other divisions calling their toys "PowerToys".) A quick check reveals the following PowerToys available for download from Microsoft; I may have missed some.

    (Plus, of course, the Windows XP PowerToys, which does come from the shell team. The Internet Explorer team originally called their stuff PowerToys, but they later changed the name to Web Accessories, perhaps to avoid the very confusion I'm discussing here.)

    What's frustrating is that since they are all called "PowerToys", questions about them tend to go to the shell team, since we are the ones who invented PowerToys. We frequently have to reply, "Oh, no, you're having a problem with the XYZ PowerToys, not the classic Windows PowerToys. We're the folks who do the classic Windows PowerToys."

    Even the blog name "PowerToys" has been co-opted by the Visual Studio team to promote their Powertoys for Visual Studio 2003.

    Some people claim that Tweak UI was written because Microsoft got tired of responding to customer complaints. I don't know where they got that from. Tweak UI was written because I felt like writing it.

    That page also says that sometimes PowerToys vanish without warning. That's true. A few years ago, all the Windows XP PowerToys were taken down so they could be given a security review. Some of them didn't survive and didn't come back. Other times, a PowerToy will be pulled because a serious bug was found. Since PowerToys are spare-time projects, it can take a very long time for a bug to get fixed, tested, and re-published. For example, the HTML Slide Show Wizard was pulled after a (somewhat obscure) data-loss bug was found. Fixing the bug itself took just a few days, but testing and filling out all the associated paperwork took six months.

    There's no moral to this story. Just a quick history lesson.

  • The Old New Thing

    Windowless controls are not magic

    • 61 Comments

    It seems that when people notice that the Internet Explorer rendering engine doesn't use HWNDs for screen elements, they think that Internet Explorer is somehow "cheating" and doing something "undocumented" and has an "unfair advantage".

    Nevermind that windowless controls have been around since 1996. They aren't magic. Mind you, they're a lot of work, but they aren't magic.

    Clearly Internet Explorer cannot create a real HWND for every element in an HTML page. There is a limit of 10,000 USER handles per process, and you are likely to run out of desktop heap long before then.

    The Internet Explorer team went and reimplemented all of the controls that a web page would need. They have their own windowless checkbox control, a windowless listbox control, a windowless edit box, and so on. In addition to reproducing all the functionality of the windowed controls, the Internet Explorer folks also had to reproduce the "look" of the windowed controls, down to the last pixel. (Functions like DrawThemeBackground and DrawFrameControl prove extremely helpful here.)

    If I recall correctly, the only element that is still windowed is the <SELECT> element.

    If you squint, you can see some places where they didn't quite nail it. For example, if you right-click in a text box, options like "Right to left reading order" and "Insert Unicode control character" are missing. As another example, notice that IE's scroll bars do not light up when you hover over them.

    Remember, I never worked on Internet Explorer; all I know is what I learn from people from that team. (jeffdav for example, joins the shell team for lunch nearly every day.) If you have questions about Internet Explorer, you would likely have much better success asking the Internet Explorer team yourself via their team blog.

  • The Old New Thing

    How to detect programmatically whether you are running on 64-bit Windows

    • 46 Comments

    To detect programmatically whether your 32-bit program is running on 64-bit Windows, you can use the IsWow64Process function.

    Do not do as some people do and hard-code the list of 64-bit processors. You'd think that after the hard-coded list of 64-bit processors changed the first time (when x64 was added to ia64), people would have learned their lesson.

    But how do you detect programmatically from your 64-bit process whether you are running on 64-bit Windows? Easy.

    BOOL Is64BitProcessRunningOn64BitWindows()
    {
     return TRUE;
    }
    

    The fact that your 64-bit program is running at all means that you are running on 64-bit Windows! If it were a 32-bit machine, your program wouldn't be able to run.

    It's like asking the question, "Is the power on?" If there were no power, your program wouldn't be able to ask the question.

    Of course, if you want a single source code base that can be compiled both as a 32-bit program and as a 64-bit program, you have a tiny amount of work to do.

    BOOL Is64BitWindows()
    {
    #if defined(_WIN64)
     return TRUE;  // 64-bit programs run only on Win64
    #elif defined(_WIN32)
     // 32-bit programs run on both 32-bit and 64-bit Windows
     // so must sniff
     BOOL f64 = FALSE;
     return IsWow64Process(GetCurrentProcess(), &f64) && f64;
    #else
     return FALSE; // Win64 does not support Win16
    #endif
    }
    

    I threw in a branch for 16-bit programs if you're crazy enough to be still writing 16-bit Windows programs.

  • The Old New Thing

    "Support our troops" wristbands provide only 1/3 support

    • 43 Comments

    Over at Slate, Timothy Noah notes how much money from so-called "awareness bracelets" actually go to the causes they purport to champion.

    In summary,

    Bracelet Vendor Cause Portion that
    benefits cause
    Yellow "Livestrong" Lance Armstrong Foundation supporting cancer survivors all
    Cinnamon Tsunami Relief Red Cross, UNICEF, CARE 1/2
    Camo-green 7-11 USO 1/3
    Light blue Awareness Depot Tsunami disaster relief
    (unspecified charity)
    1/10
    Yellow Awareness Depot Support our troops 0

    I find it ironic that the "Support our troops" bracelet doesn't actually support our troops. And is yellow really the right color to use to demonstrate support for the military?

  • The Old New Thing

    You cannot globally reserve user-mode address space

    • 41 Comments

    Occasionally, somebody asks for a way to reserve user-mode address space globally. In other words, they want to allocate address space in all processes in the system (current and future). Typically this is because they want to map some memory into each process and don't want to go through the trouble of designing the shared memory blocks so that they can be relocated.

    This is obviously not possible.

    Why obviously? Well, imagine if this were possible.

    "Imagine if this were possible" is one of the logic tests you can apply to a theory to see if it can possibly be true. Here, we're using it to determine whether a proposed behavior is possible. [Typo fixed 10am.] (There is a corresponding thought experiment, "Imagine if things actually worked that way.")

    What are the consequences of global address space allocation?

    Well, first of all, there's no guarantee that by the time you request your global address space, there will be any available addresses at all. Consider a program that uses every last scrape of user-mode address space. (It can do this by just calling VirtualAlloc(MEM_RESERVE) in a loop; since no memory is being committed, this doesn't actually require 2GB of RAM.) Run such a program and no global address space allocations are possible until that program exits.

    So even if it were possible, it wouldn't be reliable. Your program would have to be prepared for the situation where no global address space was available. Since you're going to have to write fallback code anyway, you didn't save yourself any work.

    Next, suppose it were possible, that there were some imaginary GlobalVirtualAlloc function. Well, then I can write a program that calls this imaginary function in a loop, sucking up all available global virtual address space, and run it as a non-administrator.

    I just violated security. (The general principle here is that a non-administrator should not be allowed to affect other users. We've already seen one scenario where a non-administrator can crash a program running as administrator due to insecure use of shared memory.)

    My imaginary program sucked up all global virtual address space, reducing the address space available to programs running as administrator. If there aren't many programs running on the system, my imaginary program will probably be able to suck up quite a lot of address space this way, which in turn will cause a corresponding reduction in address space to those administrative programs. I can therefore cause those programs to run out of address space sooner, resulting in premature failure (denial of service).

    Yes, you could decide that "global" address space reservation is available only to administrators, but that wouldn't help a lazy programmer, since the program would not work when run as a non-administrator - you have to write the fallback code anyway. Or you could decide that "global" address space reservation applies only to users within the same session with the same security token, but again, you have to write the fallback code anyway if the global reservation fails; you didn't save yourself any work. When the time comes, you can use VirtualAlloc and pass a preferred address to try to get the memory at that address; if it fails, then use the fallback code that you already wrote.

    The moral of the story is that each process gets its own address space and each process manages its address space independently of other processes. (Of course, a process can grant a user PROCESS_VM_OPERATION permission, which gives that user permission to mess with the address space of that process. But changes to that process's address space does not affect the address space of other processes.)

  • The Old New Thing

    MsgWaitForMultipleObjects and the queue state

    • 37 Comments

    One danger of the MsgWaitForMultipleObjects function is calling it when there are already messages waiting to be processed, because MsgWaitForMultipleObjects returns only when there is a new event in the queue.

    In other words, consider the following scenario:

    • PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) returns TRUE indicating that there is a message.
    • Instead of processing the message, you ignore it and call MsgWaitForMultipleObjects.

    This wait will not return immediately, even though there is a message in the queue. That's because the call to PeekMessage told you that a message was ready, and you willfully ignored it. The MsgWaitForMultipleObjects message tells you only when there are new messages; any message that you already knew about doesn't count.

    A common variation on this is the following:

    • MsgWaitForMultipleObjects returns that there is a message.
    • You call PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) and process that message.
    • You call MsgWaitForMultipleObjects to wait for more messages.

    If it so happens that there were two messages in your queue, the MsgWaitForMultipleObjects does not return immediately, because there are no new messages; there is an old message you willfully ignored, however.

    When MsgWaitForMultipleObjects tells you that there is a message in your message queue, you have to process all of the messages until PeekMessage returns FALSE, indicating that there are no more messages.

    Note, however, that this sequence is not a problem:

    • PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) returns FALSE indicating that there is no message.
    • A message is posted into your queue.
    • You call MsgWaitForMultipleObjects and include the QS_ALLPOSTMESSAGE flag.

    This wait does return immediately, because the incoming posted message sets the "There is a new message in the queue that nobody knows about" flag, which QS_ALLPOSTMESSAGE matches and therefore causes MsgWaitForMultipleObjects to return immediately.

    The MsgWaitForMultipleObjectsEx function lets you pass the MWMO_INPUTAVAILABLE flag to indicate that it should check for previously-ignored input.

    Armed with this knowledge, explain why the observed behavior with the following code is "Sometimes my program gets stuck and reports one fewer record than it should. I have to jiggle the mouse to get the value to update. After a while longer, it falls two behind, then three..."

    // Assume that there is a worker thread that processes records and
    // posts a WM_NEWRECORD message for each new record.
    
    BOOL WaitForNRecords(HANDLE h, UINT cRecordsExpected)
    {
      MSG msg;
      UINT cRecords = 0;
      while (true) {
        switch (MsgWaitForMultipleObjects(1, &h,
                             FALSE, INFINITE, QS_ALLINPUT)) {
        case WAIT_OBJECT_0:
          DoSomethingWith(h); // event has been signalled
          break;
        case WAIT_OBJECT_1:
          // we have a message - peek and dispatch it
          if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
          }
          if (SendMessage(hwndNotify, WM_GETRECORDCOUNT,
                          0, 0) >= cRecordsExpected) {
            return TRUE; // we got enough records
          }
          break;
        default:
          return FALSE; // unexpected failure
        }
      }
    }
    
  • The Old New Thing

    Two brief reminiscences on the Windows XP "Comments?" button

    • 35 Comments

    In beta versions of Windows XP, there was special code in the window manager to give every window a link in the upper right corner called "Comments?" which if clicked on displayed a dialog that allowed you to submit feedback to Microsoft about that window.

    Since this was a beta release, there was no anonymity when you submitted feedback. (You signed away your anonymity when you agreed to the special beta license agreement and typed in your beta ID number.) Yet we got more than one feedback submission that begin, "Hi, I pirated this copy of Windows XP, and here's some feedback."

    In its initial incarnation, the word in the title bar was "Lame", but people with a weaker sense of humor changed it to the less confrontational "Comments?". The name "Lame" came from a recurring sketch on local comedy show Almost Live! called "The Lame List, or What's Weak This Week (brought to you with the help of Seattle's Heavy Metal community)".

  • The Old New Thing

    How to act like you know Chinese even though you don't

    • 34 Comments

    I was riding in a car with a friend when a Chinese pop album spun up on the CD. I made a remark about the song, and my friend, knowing that I was studying Mandarin, asked me, "Do you know what she's singing about?"

    I immediately replied, "She's singing about love."

    "Wow," my friend responded. "Your Chinese is quite good."

    "Nope," I said. "But all pop songs are about love."

    If somebody asks you what a pop song is about, you can answer "love", and you've got a very high chance of being correct.

  • The Old New Thing

    What's the deal with the DS_SHELLFONT flag?

    • 34 Comments

    It indicates that you want the Windows 2000 default shell font. But that doesn't mean that you're going to get it.

    In order to indicate that you would like the "Windows 2000" look for your dialog, you have to do three things and hope for a fourth:

    1. Use a DIALOGEX template instead of a DIALOG template.
    2. Set the DS_SHELLFONT flag in your dialog styles.
    3. Set the dialog font to "MS Shell Dlg".
    4. Hope that you're running on Windows 2000 or later on a system that has the new Windows 2000 dialog font enabled.

    If all four conditions are satisfied, then your dialog gets the "Windows 2000" look.

    If any condition fails, then you get the "classic" dialog font. Note that the fourth condition is not within your program's control. Consequently, you have to make sure your dialog looks good in both the "classic" dialog font and the new one.

    For property sheet pages, things are more complicated.

    It would be visually jarring for there to be a mix of fonts on a property sheet. You wouldn't want the "Advanced" button to be in MS Sans Serif but the "Apply" button in Tahoma. To avoid this problem, the property sheet manager looks at all the pages in the property sheet. If they are all using the "Windows 2000" look, then the property sheet uses the "Windows 2000" look also. But if there is even a single page that does not use the "Windows 2000" look, then the property sheet reverts to the "classic" look and also converts all the "Windows 2000"-look pages to "classic" look.

    This way, all the pages in the property sheet have the "classic" look instead of having a mishmash of some pages with the classic look and some with the Windows 2000 look.

    That's why you will occasionally find that a shell property sheet has reverted to the "classic" look. Some shell extension infected the property sheet with a page that does not have the "Windows 2000" look, and for the sake of visual consistency, the property sheet manager set all the pages on the property sheet to "classic" look.

    This is another reason it is important that you test your property sheet pages both with the "Windows 2000" look and the "classic" look. If your property sheet page ends up sharing a property sheet with a non-"Windows 2000"-look page, your page is going to be reverted to "classic" look.

  • The Old New Thing

    Why do minimized windows have an apparent size of 160x31?

    • 31 Comments

    We discussed a few months ago the issue of where windows minimized to before the taskbar was invented. In the modern taskbar world, why do minimized windows have an apparent size of 160x31?

    The size isn't just apparent. That's really their size. You can see them, for example, if you fire up a program that uses the Multiple Document Interface.

    Observe the appearance of the window "Book1". This is a minimized window (though minimized to its MDI host rather than to the desktop). With the introduction of Windows Explorer, which put files on the desktop in the form of icons, it became necessary to change the appearance of minimized windows in order to avoid confusing a minimized program icon from a desktop icon. A minimized program, therefore, took the form of a miniature title bar.

    The programming interface to minimized windows remained the same, for compatibility reasons. (And please let's just agree to disagree on whether backwards compatibility is a good thing or not.) That's why the function to tell whether a window is minimized continues to be called IsIconic, the message you receive when someone tries to restore a minimized program is still called WM_QUERYOPEN, and the OpenIcon function can still be used to "open" a minimized "icon". All even though minimized windows haven't looked like icons for nearly ten years.

    The OpenIcon function is just an old-fashioned way of saying ShowWindow(hwnd, SW_NORMAL), in the same way that the CloseWindow function (dating back to Windows 1.0) is an extremely old-fashioned way of saying ShowWindow(hwnd, SW_MINIMIZE).

Page 1 of 3 (22 items) 123