May, 2010

  • The Old New Thing

    That's a great idea, it's on the list

    • 38 Comments

    The great thing about the taskbar is that everybody and their pet dog have an idea for how it can be improved.

    The bad thing about the taskbar is that everybody and their pet dog have an idea for how it can be improved. (And everybody and their pet dog also think that their feature suggestion is the important one that should be worked on next. Especially if it's "just a tiny little feature.")

    For a few years, my office sat across the hall from the person responsible for the taskbar design and features. To help manage all the wonderful ideas that came in, my colleague maintained a spreadsheet of all suggestions that were submitted and deemed worthy of further investigation. And for each suggestion, there was a rough estimate of how much developer time it would take to implement that feature.

    The total came out to over 200 days of idealized developer time.

    In other words, under perfect conditions (for example, on the rosy assumptions that no earlier feature made a subsequent feature more complicated and that all the code will work the first time), it would take one person over 40 weeks of vacation-free full-time coding to implement all the little features people requested. Death by a thousand cuts. Or in this case, 200 cuts.

    That calculation of course ignores the time spent designing the feature, developing a test plan, running it through usability tests, writing the automated testing, debugging all the issues that arise, doing the threat modeling, fixing the compatibility issues from applications that relied on the old behavior, and all the other things that you have to do to ship a feature beyond simply writing the code for it. (Indeed, the "write the code" step is one of the smallest steps in the product development cycle!)

    Obviously, you can't implement everything on the list: You don't know what you do until you know what you don't do. (And imagine the size of the resulting taskbar configuration dialog!) But it also means that there are a lot of people with pet dogs who think you're an idiot.

    Maintaining the list was a useful exercise for another reason: Whenever anybody stopped by and said, "Hey, why doesn't the taskbar...", my colleague could say, "That's a great idea. It's on the list."

    When my colleague left the group, ownership of the list was transferred to the next person responsible for the taskbar design. For all I know, there's still a list sitting in a spreadsheet on a designer's computer somewhere.

    Part of the hard job of product design is deciding which 20 features you have the resources to pursue and which 180 to leave for next time. If you choose the right 20, people will say you're the best new UI feature in Windows, or even the best Windows 7 feature, and they won't mind too much that you didn't get to the other 180.

  • The Old New Thing

    How do I accept files to be opened via IDropTarget instead of on the command line? - bonus content

    • 17 Comments

    One of my colleagues tipped me off to some additional resources on the subject of using the DropTarget technique for accepting files for execution.

    First, it turns out that there is an SDK sample that demonstrates the DropTarget technique after all. This sample is fifteen years late according to one commenter who apparently thinks that the Platform SDK needs to provide a sample for a feature that won't be invented for another twelve years. Maybe we can use the Microsoft Research project to predict the future. No wait, we also need to get them to invent the time machine so we can take the future-predictor machine back in time 15 years.

    Second, there is a sample for the ExecuteCommand technique. The ExecuteCommand technique is preferred over the DropTarget technique because it is much easier to implement. (Translation: much less you can get wrong.) Like the DropTarget technique, the ExecuteCommand supports out-of-process activation.

    And third, as it turns out, App Path registration supports HKEY_CURRENT_USER after all, thereby addressing one of the complaints raised by a commenter (a complaint I answered incorrectly).

  • The Old New Thing

    Every window with the WS_SYSMENU style has a system menu, but it's not there until it needs to be

    • 5 Comments

    I mentioned last time that there's an optimization in the treatment of the system menu which significantly reduces the number of menus in the system.

    When a window has the WS_SYSMENU window style, it has a system menu, but until somebody calls Get­System­Menu on that window, nobody knows what its menu handle is. Until that point, the window manager doesn't actually have to commit to creating a menu for the window; it can just pretend that the window has one. (This technique goes by the fancy name lazy initialization.)

    The window manager creates a global default system menu which contains the standard system menu items. If somebody presses Alt+Space or otherwise calls up the system menu for a window that has never had Get­System­Menu called on it, the window manager just uses the global default system menu, since it knows that nobody has customized the menu. (You can't customize a menu you don't have the handle to!) Since most people never customize their system menu, this optimization avoids cluttering the desktop heap with identical copies of the same menu. This was a particularly important optimization back in the 16-bit days, when all window manager objects had to fit into a single 64KB heap (known as System Resources).

    If you are really sneaky, you can catch a glimpse of the elusive global default system menu as it whizzes by: As with any other popup menu, the handle to the menu being displayed is passed to your window's WM_INIT­MENU­POPUP, and if your program has never called Get­System­Menu, the handle that you will see is the global default system menu. Mind you, you can't do much to this menu, since the window manager blocks any attempt to modify it. (Otherwise, your program's menu modification would have an unintended effect on the menus of other programs!)

    Therefore, if your program is in the habit of modifying its system menu in its WM_INIT­MENU­POPUP handler, you should stick a dummy call to Get­System­Menu in your WM_CREATE handler to force your system menu to change from a pretend system menu to a real one.

  • The Old New Thing

    When will the window manager destroy a menu automatically, and when do I need to do it manually?

    • 17 Comments

    Our old friend Norman Diamond wonders when you are supposed to destroy a menu and when you are supposed to let Windows destroy it.

    The rules for when the window manager implicitly destroys menus are actually not that complicated.

    • If a window is destroyed, the menus attached to the window are also destroyed:
      • Attached as the menu bar (Get­Menu/Set­Menu)
      • Attached as the system menu (Get­System­Menu)
    • If a menu is destroyed, its submenus are also destroyed.
    • If you replace a MIIM_SUBMENU submenu, the old menu is destroyed.
    • If you pass bRevert = TRUE to Get­System­Menu, then the old system menu is destroyed and a clean system menu is created in its place.

    Outside of the above situations, you are on your own.

    Of course, when I write that "you are on your own" I do not mean that "every code which sees a menu is responsible for destroying it." If that were the case, you would have a disaster as the slightest passing breeze would cause people to call Destroy­Menu all over the place. Rather, I mean that in all other cases, you need to "work it out amongst yourselves" who is responsible for destroying the menu. Typically, the person who creates the menu takes responsibility for destroying it, although that responsibility can be handed off based on mutual agreement between the creator and another component.

    The original question did include a misunderstanding:

    If the old object belonged to a window class, and we destroy the old object, how do we know that other windows of the same class aren't going to get in trouble?

    The mistaken belief here is that each window of a class shares the same menu. If that were true, then if a program created two windows of the same class, modifications to one window's menu would affect the other. You can see that this is not true by inspection, or at least it was easier back in 1995. On Windows 95, open two Explorer windows, and set them into different views. The two windows now have different menus: One of them has a bullet next to the Large Icons menu item, whereas the other has a bullet next to Details.

    When you register a window class, you pass in the menu you want, but only in the form of a template:

        WNDCLASS wc;
        ...
        wc.lpszMenuName = MAKEINTRESOURCE(...);
    

    There is no menu yet, just a description of how to create a menu when the time comes. When you create a window from this class, the window manager initializes the menu by doing the equivalent of

        SetMenu(hwnd, LoadMenu(pWndClass->hInstance,
                               pWndClass->lpszMenuName));
    

    Each window gets a fresh menu from the specified menu template. Once that's done, you can change it all you want; it won't affect any the menus associated with any other windows.

    The system menu works the same way: Every window starts out with a default system menu, and when you call Get­System­Menu with bRevert = FALSE, you get a handle to that system menu, which you can modify to your heart's content without affecting any other menus. System menus have this additional wrinkle where you can pass with bRevert = TRUE to ask the window manager to destroy the current system menu and replace it with a fresh new default system menu.

    Exercise: How would you accomplish the logical equivalent of Get­System­Menu(TRUE) for the menu bar menu?

    Bonus chatter: While the system menu certainly behaves as I described it above, there's actually a little bit of optimization going on under the hood. We'll look at that next time.

  • The Old New Thing

    How do I find the bounding box for a character in a font?

    • 16 Comments

    A customer had the following question:

    I'm looking for a way to get the height of text which consists entirely of digits, for example "123", as these characters do not have any descent or internal leading. I expected functions like Get­Text­Extent to return the character's ascent minus the internal leading, but in fact it returns the ascent plus the descent plus the internal leading. I considered getting the font metrics and taking the TEXT­METRICS.tmAscent, but I'm worried that numbers in other languages might have a nonzero descent and internal leading. Is there a function I can call to return the "real" height of the text?

    Well, first of all, this question makes an assumption about digits that isn't even true in English. Fonts developed in recent years tend to keep all digits the same height (and often the same width), but fonts designed before the advent of computers (or computer fonts which were inspired by old-timey fonts) will often vary the height, and sometimes even have digits with descenders. Here's an example from the font Georgia:

    058

    Observe that the number zero is six pixels tall, whereas the number eight is nine pixels tall, and the number five has a two-pixel descender!

    Okay, so you're going to have to take the descent into account for all languages, including English. Internal leading is the space above a character to separate it from elements above it. For example, you need some space above a capital T so that the horizontal bar remains readable. Again, the assumption that English doesn't need internal leading is false.

    Okay, but what about the original question? Well, when I heard this question, my first thoughts went back to the early days of Win32 when the coolest new GDI feature was paths, and everybody was showing off the fancy text effects you could pull off with the aid of paths. My initial instinct was therefore to use the same technique as those cool demos by combining Begin­Path, Text­Out, and End­Path. Once I had a path, I could get its dimensions by using Path­To­Region and Get­Rgn­Box.

    Fortunately, it turns out that there's an easier way. The Get­Glyph­Outline function returns the glyph metrics, which describe the bounding box of the pixels of a character.

    // Create an identity matrix
    static const MAT2 c_mat2Identity = {
        { 0, 1 }, /* eM11 = 1.0 */
        { 0, 0 }, /* eM12 = 0.0 */
        { 0, 0 }, /* eM21 = 0.0 */
        { 0, 1 }, /* eM22 = 1.0 */
     };
    GetGlyphOutline(hdc, L'0', GGO_METRICS, &gm, 0, NULL, &c_mat2Identity);
    

    The dimensions of the character are returned in the GLYPH­METRICS structure, and in particular, you can derive the bounding box from the gmptGlyph­Origin, gmBlack­BoxX, and gmBlack­BoxY members.

  • The Old New Thing

    Cliff Notes: The short version of the Cliff Mass blog

    • 6 Comments

    Cliff Mass is Professor of Atmospheric Sciences at the University of Washington. To those of us in the Seattle area, he's a weather celebrity, appearing every Friday on local public radio station KUOW to provide a weekend weather forecast and discuss other weather issues. His blog covers similar material, such as his explanation of why weather radar shows lots of echoes even though there was no rain.

    His articles, while fascinating, are also quite long, so to help you get through them, there is the Cliff Mass TLDR blog. For example, the TLDR version of the mysterious echoes goes like this:

    The radar is picking up birds instead of rain
    It's been dry, but the weather radar last night showed lots of echos, suggesting it rained all night. No, it was just birds.

    Explaining the joke in the title: "Cliff Notes" is a pun on CliffsNotes, a United States producer of study guides.

  • The Old New Thing

    Why doesn't the Windows Vista copy progress dialog show the names of the files being copied?

    • 56 Comments

    When you copy multiple files in Windows Vista, the progress dialog gives you an estimate of the time remaining as well as an indication of what fraction of the operation has been completed. But one thing it doesn't tell you is the name of the file currently being copied. Why not?

    The programmer responsible for the file copy progress dialog in Windows Vista explained to me that there were a few reasons. First, there's the problem of presenting information to the user faster than the user could read it. All those filenames flashing by made users feel like they had lost control of the computer, as if it had decided to go off and do something at lightning computer speeds. Everything was happening so fast that they couldn't keep up with it much less understand what was going on and be ready to stop it if something didn't look right. Users may have been conditioned by the Hollywood Operating System, in which a rapidly changing stream of file names usually means something Really Bad is going on.

    Users also reported that the constantly-changing dialog felt unstable, like it never could figure out what it wanted to say. And of course it all happened faster than any human being could read it. There's not much value in presenting to the user information they can't read anyway.

    On the technical side, there were slight performance benefits to suppressing the display of the file names. As we saw, updating the screen can be a significant cost when you are updating continuously. Furthermore, even determining what name to display for the file is nontrivial. It's more than just extracting the string after the last backslash, because you can configure a file or directory so that the name displayed to the user differs from the physical file name. This is important when dealing with languages other than English. On Chinese systems, for example, the file whose name is Calculator.lnk is displayed to the user as 計算器.

    In Windows 7, the file copy dialog returned to showing the names of the files being copied, but only if you ask for it by clicking the More Information button. That way, the standard dialog looks normal, and only if you say, "firehose, please" do you get inundated with information faster than you can read it.

  • The Old New Thing

    Welcome to The New Old New Thing, 2010 edition

    • 48 Comments

    The blog server folks tell me that the upgrade is complete. Thanks for your patience. I'm told they will throw the switch early this afternoon.

    The new URL for this site after the switchover will be http://blogs.msdn.com/b/oldnewthing/, although there should be 301 redirects at all the old URLs so your existing links will still work. My colleague Michael Kaplan might say that the extra B is for badass. Or maybe it stands for bewildering. Or broken.

    Speaking of broken, you'll probably find a bunch of broken links, ugly layout, and other lameness (Pre-emptive snarky comment: that is, lameness beyond the normal lameness that gets posted here every weekday). I tried to fix some of the more egregious ones, but I couldn't get to them all, and I'm sure I missed a bunch too.

    Feel free to add comments to this entry pointing out any errors introduced by the migration (once they turn comments back on), and I'll see what I can do, although it may take a while since this isn't my day job.

    Known problems:

    • Starred comments are broken.
    • Links to comments are broken. Not only are they lost during the redirect, but the pagination of comments means that the comment doesn't even appear on the page you get redirected to. Update: Links to comments work as of January 1, 2011.
  • The Old New Thing

    What's the deal with What's This??

    • 17 Comments

    Via the suggestion box, Matthew Douglass-Riley wonders about the history and fate of the What's This? button. (The same question was repeated by an anonymous coward.)

    The What's This? button (more formally known as the contextual help caption button) is turned on by the WS_EX_CONTEXT­HELP extended style and takes the form of a question mark. When the user clicks the button, the cursor changes to an arrow with a question mark, and when the user clicks on a child window, a WM_HELP message is delivered to that window.

    As originally written, the intended response is for the window to call the WinHelp function with the HELP_CONTEXT­POPUP command and information describing the location of the desired help text. The WinHelp.exe program displays the help text in a pop-up window.

    As a clue to how old this technology is, you may observe that the pop-up window causes the original window to become disabled, and it comes with a rather sad looking shadow effect. This was long before the introduction of the CS_DROP­SHADOW window style or even layered windows. To get a drop shadow effect, you had to draw it yourself. And this was also the days of 4-bit color, so the drop shadow effect is actually a dither.

    Anyway, it wasn't long for What's This? to fall out of favor and become replaced with HTML Help, which in turn has taken a back seat to Windows Vista Help. It seems that help technologies change rather often, and I'm not sure why. Maybe it's because the user assistance folks are willing to experiment with a lot of different ideas, unashamed of abandoning their previous efforts when they fail to pan out. Maybe because designing a good help system is hard. Maybe because users simply don't bother checking the help, so it doesn't matter how good your help system is since nobody uses it anyway.

    I find it interesting that the Help Guidelines asks the question, "Are you using Help to fix a bad UI?" There's even an entire section titled Designing UI so that Help is unnecessary. I find the guidelines interesting because they capture lessons learned from earlier versions of Windows which violated those guidelines.

    Matthew calls out one particular dialog in Windows XP that has a non-functional contextual help caption button. Sorry about that. It's a leftover from the days when contextual help was still the recommended way of providing user assistance. The contextual help was removed, but the button wasn't cleaned up. You'll be happy to learn that the UI glitch has been fixed in Windows Vista.

  • The Old New Thing

    SHAutoComplete giveth, and SHAutoComplete taketh away

    • 4 Comments

    The SH­Auto­Complete function lets you attach autocomplete functionality to an edit control, and there are flags that describe what sources you want the autocomplete to draw from. If you call SH­Auto­Complete a second time, the second set of flags replace the original flags. The flags do not accumulate. For example, if you first call SH­Auto­Complete(SHACF_FILESYS_ONLY), and then you later call SH­Auto­Complete(SHACF_URLHISTORY), the result is that the autocompletion uses only the URL history.

    This replacement behavior (as opposed to accumulation behavior) is handy if you want to remove an autocompletion that you previously added. You just call SH­Auto­Complete a second time and leave off the flags for autocomplete sources you don't want. There's a catch, though: If you want to turn off everything, then you cannot pass zero, because that gets interpreted as SHACF_DEFAULT. You have to pass a nonzero value, and fortunately there's a handy nonzero value which means Turn off everything: SHACF_AUTOSUGGEST_FORCE_OFF.

    Let's illustrate this technique by disabling autocomplete in the common dialog, a problem which commenter Ian mistakenly solved by modifying a global setting.

    #include <windows.h>
    #include <shlwapi.h>
    #include <commctrl.h>
    #include <commdlg.h>
    #include <dlgs.h>
    
    UINT_PTR CALLBACK HookProc(HWND hdlg, UINT uMsg,
                               WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg) {
        case WM_INITDIALOG:
            PostMessage(hdlg, WM_APP, 0, 0);
            break;
        case WM_APP:
            SHAutoComplete(
              (HWND)SendDlgItemMessage(GetParent(hdlg), cmb13,
                                       CBEM_GETEDITCONTROL, 0, 0),
              SHACF_AUTOSUGGEST_FORCE_OFF);
            break;
        }
        return 0;
    }
    
    int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev,
                       LPSTR lpCmdLine, int nShowCmd)
    {
        TCHAR szFile[MAX_PATH];
        szFile[0] = TEXT('\0');
        OPENFILENAME ofn = { sizeof(ofn) };
        ofn.hInstance = hinst;
        ofn.lpstrFilter = TEXT("All files\0*.*\0");
        ofn.lpstrFile = szFile;
        ofn.nMaxFile = MAX_PATH;
        ofn.Flags = OFN_ENABLEHOOK | OFN_EXPLORER;
        ofn.lpfnHook = HookProc;
        GetOpenFileName(&ofn);
        return 0;
    }
    

    The hook procedure uses the SH­Auto­Complete function to turn off autocompletion on the file name edit control in the common dialog. There are a few annoying bits that I have to get through before I finally make that SH­Auto­Complete call: First I have to find the edit control, which means finding the combo box and then asking the combo box for the interior edit control. (Fortunately, this is already called out in the documentation for SH­Auto­Complete, so I didn't have to puzzle over it for long.) And second, I couldn't disable autocomplete directly in WM_INITDIALOG because that happens too early in the common file dialog initialization process. Instead, I post myself a message and do the "final initialization" later. (This I discovered by trial and error.)

    And there you have it, a common dialog box with no autocomplete.

    Update: Joylon Smith points out that the documentation for SHAutoComplete explicitly cautions against calling it more than once on the same window because it results in a memory leak.

    That caution was written based on information I provided back in Windows XP. The memory leak was fixed in Windows Vista, but the documentation was not updated to match. So please mentally insert "On versions of Windows prior to Windows Vista (and versions of Windows Server prior to Windows Server 2008)" at the start of that paragraph. A doc change request has also been submitted, so hopefully the revised documentation will appear soon.

Page 1 of 4 (32 items) 1234