• The Old New Thing

    The unrecognized assistants on those do-it-yourself shows

    • 28 Comments

    Some people from my knitting group have been among those assisting in the preparation of today's episode of the knitting show Knitty Gritty on The Do It Yourself Network.

    Stop reading if you don't want the television illusion shattered, or at least cracked a bit.

    Each volunteer was assigned a section of a sweater to knit. The show's host will take those pieces and do some short demonstration with it, perhaps sewing pieces together or illustrating one of the trickier parts.

    After each volunteer made their assigned part, they also had to start working on the same sweater again, but not to finish it. They will be in the background knitting while the host does the feature demonstration.

    What's more, each volunteer has already been told what problem they will be having with the sweater, so that the host can help extricate them from whatever mess they got themselves into. Nevermind that they already made the sweater that the host was using in the demonstration just a few minutes ago. Clearly, they know what they're doing; they did all the knitting!

    If you thought the hosts of these shows actually did the cooking/sewing/knitting/whatever, well now you know better.

  • The Old New Thing

    How to host an IContextMenu, part 4 - Key context

    • 6 Comments

    Another of the bugs you may have noticed in our first attempt at displaying the context menu to the user is that the Delete command doesn't alter its behavior depending on whether you hold the shift key. Recall that holding the shift key changes the behavior of the Delete command, causing it to delete a file immediately instead of moving it to the Recycle Bin. But in our sample program, it always offers to move the file to the Recycle Bin, even if you have the shift key down.

    (You can see the difference in the wording of the dialog and in the icon. If the operation is to move the item into the Recycle Bin, you get a Recycle Bin icon and the text asks you to confirm sending the item to the Recycle Bin. If the operation will delete the item permanently, then you get an icon that shows a file and a folder fading away and the text asks you to confirm deleting the item.)

    To convey this information to the context menu, you need to pass the key states in the CMINVOKECOMMANDINFOEX structure.

              CMINVOKECOMMANDINFOEX info = { 0 };
              info.cbSize = sizeof(info);
              info.fMask = CMIC_MASK_UNICODE | CMIC_MASK_PTINVOKE;
              if (GetKeyState(VK_CONTROL) < 0) {
                info.fMask |= CMIC_MASK_CONTROL_DOWN;
              }
              if (GetKeyState(VK_SHIFT) < 0) {
                info.fMask |= CMIC_MASK_SHIFT_DOWN;
              }
    

    Make this change and observe that the dialogs you get from the Delete option now respect your shift key state.

    Warning: Before playing with this, make sure that you have enabled delete confirmation warnings or you will end up deleting your clock.avi file for real! If you want to play around with the Delete option, you may want to tweak the program so it operates on a file you don't mind losing.

    Exercise: There's another place where key context influences the context menu, namely the convention that holding the shift key while right-clicking enables "extended verbs". These are verbs that are lesser-used and therefore do not appear on the conventional context menu to avoid creating clutter. For homework, incorporate the extended verb convention into the sample program.

    [Sorry today's entries are late. Had problems connecting to the blog server.]

  • The Old New Thing

    Penguins do not fall over!

    • 20 Comments

    Everyone has read the story about penguins falling over backwards while observing low-flying aircraft. The Annals of Improbable Research reports that Dr. Richard Stone has been researching the ecological effects of helicopter overflights on King penguins and his preliminary results show that King penguins do not, in fact, fall over backwards (or forwards or even sideways) when you fly over them. On the other hand, low-flying aircraft do create distress among the penguins; Dr. Stone recommends a minimum overflight altitude of 1000 feet.

    [Typo fixed, 24 September.]

  • The Old New Thing

    How to host an IContextMenu, part 3 - Invocation location

    • 5 Comments

    One of the bugs you may have noticed in our first attempt at displaying the context menu to the user is that the Properties dialog doesn't show up at the point you clicked. The Properties dialog isn't psychic; it doesn't know where the original mouse click occurred. You have to tell it.

              CMINVOKECOMMANDINFOEX info = { 0 };
              info.cbSize = sizeof(info);
              info.fMask = CMIC_MASK_UNICODE | CMIC_MASK_PTINVOKE;
              info.hwnd = hwnd;
              info.lpVerb  = MAKEINTRESOURCEA(iCmd - SCRATCH_QCM_FIRST);
              info.lpVerbW = MAKEINTRESOURCEW(iCmd - SCRATCH_QCM_FIRST);
              info.nShow = SW_SHOWNORMAL;
              info.ptInvoke = pt;
    

    You tell it by setting the CMIC_MASK_PTINVOKE flag in the fMask and specifying the invocation point in the ptInvoke member of the CMINVOKECOMMANDINFOEX structure.

    Make this change and observe that now the Properties dialog appears at the point you clicked the mouse rather than in a random location on the screen.

    Next time, we'll clean up another minor problem with our sample program.

  • The Old New Thing

    How to contact Raymond

    If your subject matter is a suggestion for a future topic, please post it to the Suggestion Box (using the Suggestion Box link on the side of the page). If you send it to me directly, I will probably lose track of it.

    If your subject matter is personal (for example "Hi, Raymond, remember me? We went to the same high school..."), you can send email using the Contact page. (Update 25 March 2007: The contact page has been disabled because over 90% of the messages that arrive are spam.)

    I do not promise to respond to your message.

    If you are making a suggestion for a future version of a Microsoft product, submit it directly to Microsoft rather than sending it to me, because I won't know what to do with it either.

    I do not provide one-on-one technical support. If your question is of general interest, you can post it to the Suggestion Box. If you are looking for technical support, there are a variety of options available, such as newgroups, bulletin boards, forums (fora?), and Microsoft's own product support web site.

  • The Old New Thing

    How to host an IContextMenu, part 2 - Displaying the context menu

    • 6 Comments

    Instead of invoking a fixed verb, we'll ask the user to choose from the context menu and invoke the result.

    Make these changes to the OnContextMenu function:

    #define SCRATCH_QCM_FIRST 1
    #define SCRATCH_QCM_LAST  0x7FFF
    
    #undef HANDLE_WM_CONTEXTMENU
    #define HANDLE_WM_CONTEXTMENU(hwnd, wParam, lParam, fn) \
        ((fn)((hwnd), (HWND)(wParam), GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)), 0L)
    
    // WARNING! Incomplete and buggy! See discussion
    void OnContextMenu(HWND hwnd, HWND hwndContext, int xPos, int yPos)
    {
      POINT pt = { xPos, yPos };
      if (pt.x == -1 && pt.y == -1) {
        pt.x = pt.y = 0;
        ClientToScreen(hwnd, &pt);
      }
    
      IContextMenu *pcm;
      if (SUCCEEDED(GetUIObjectOfFile(hwnd, L"C:\\Windows\\clock.avi",
                       IID_IContextMenu, (void**)&pcm))) {
        HMENU hmenu = CreatePopupMenu();
        if (hmenu) {
          if (SUCCEEDED(pcm->QueryContextMenu(hmenu, 0,
                                 SCRATCH_QCM_FIRST, SCRATCH_QCM_LAST,
                                 CMF_NORMAL))) {
            int iCmd = TrackPopupMenuEx(hmenu, TPM_RETURNCMD,
                                        pt.x, pt.y, hwnd, NULL);
            if (iCmd > 0) {
              CMINVOKECOMMANDINFOEX info = { 0 };
              info.cbSize = sizeof(info);
              info.fMask = CMIC_MASK_UNICODE;
              info.hwnd = hwnd;
              info.lpVerb  = MAKEINTRESOURCEA(iCmd - SCRATCH_QCM_FIRST);
              info.lpVerbW = MAKEINTRESOURCEW(iCmd - SCRATCH_QCM_FIRST);
              info.nShow = SW_SHOWNORMAL;
              pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)&info);
            }
          }
          DestroyMenu(hmenu);
        }
        pcm->Release();
      }
    }
    

    The first change addresses the first issue brought up in the discussion of the WM_CONTEXTMENU message and fixes the HANDLE_WM_CONTEXTMENU message.

    The second change addresses the second issue, and that's the special handling of keyboard-invoked context menus. When we receive a keyboard-invoked context menu, we move it to the (0, 0) position of our client area. This keeps the context menu displayed in a vaguely sane position. (If we were a container with objects, it would have been better to display the context menu over the selected sub-object.)

    The third change actually does what we're talking about: Displaying the context menu to the user, collecting the result, and acting on it.

    You are certainly familiar with the TrackPopupMenuEx function. Here we use the TPS_RETURNCMD flag to indicate that the item the user selected should be returned by the function instead of being posted as a WM_COMMAND to our window.

    This highlights the importance of the fact that SCRATCH_QCM_FIRST is 1 and not zero. If it were zero, then we wouldn't be able to distinguish between the user selecting item zero and the user cancelling the menu.

    Once we are confident that the user has selected an item from the menu, we fill out a CMINVOKECOMMANDEX structure, specifying the user's selection in the two verb fields and indicating the invocation point via the ptInvoke member.

    Note that when you invoke a command by menu ID, you must specify the offset of the menu item relative to the starting point passed to IContextMenu::QueryContextMenu. That's why we subtracted SCRATCH_QCM_FIRST.

    When you run this program, you may notice that some things don't quite work. Most obviously, the Open With and Send To submenus don't work, but there are more subtle bugs too. We'll address them over the next few days.

  • The Old New Thing

    Swedes struggle with the meaning of sick leave

    • 7 Comments

    As part of the continuing campaign to shed their hard-working stereotype, perhaps taking a cue from their more well-adjusted Norwegian neighbors, Swedes have been taking dubious sick leave in record numbers.

    [A] study showed 40 percent believe it is enough to feel tired to stay home and draw benefits.

    A survey of 1,002 Swedes by the board also showed 65 percent believed they could go on sick leave if they felt stressed at work and 41 percent thought a conflict with their boss or workmates was a good enough reason.

    One fifth thought a strike at the child care center also made them eligible for the benefits and 71 percent said family problems entitled them always or sometimes to sick leave.

    Reuters claims Dagens Nyheter as the source, but if you punch "Anna Hedborg" into DN:s search engine, asking for articles within the last week, it finds nothing. But if you ask Google, the article is right there: Trötta sjukskriver sig (The tired call in sick). Once again, Google finds a web page that the site itself cannot.

    On a somewhat related note, a different type of sick-leave abuse has been detected.

    According to the Social Insurance Board, several people have been drawing sick-leave pay for several years, while simultaneously working another job and receiving unemployment benefits.

    Some cases are of ordinary people who defraud the Board in small amounts but over a long period of time. In the other cases, there are links to serious economic crime where, for example, money was paid out to fictitious employees at nonexistent companies.

    (Raymond's bad translation.)

    [Typo fixed 25 September.]

  • The Old New Thing

    Pitfalls in handling the WM_CONTEXTMENU message

    • 29 Comments

    Before we continue with our IContextMenu discussion, I need to take a little side trip and discuss the subtleties of the WM_CONTEXTMENU message.

    First, a correction to the existing <windowsx.h> header file:

    #undef HANDLE_WM_CONTEXTMENU
    #define HANDLE_WM_CONTEXTMENU(hwnd, wParam, lParam, fn) \
        ((fn)((hwnd), (HWND)(wParam), GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)), 0L)
    

    Apparently, HANDLE_WM_CONTEXTMENU was overlooked when the <windowsx.h> header file gained multimonitor support.

    The second subtlety of the WM_CONTEXTMENU message is the recognition that context menus can be invoked from the keyboard, not just by the mouse. If you have a 104-key keyboard, you will probably have a menu key to the right of your space bar. (Laptop owners: You're on your own. Laptop keyboards are hardly standardized.) Alternatively, you can type Shift+F10 to get the same effect.

    When the user invokes a context menu from the keyboard, the x and y coordinates are both -1. In this case, you should display the context menu for the currently-selected item (or items, if a multiple selection is active). If you miss this detail, then you will end up hit-testing against (-1, -1) and probably not find anything.

    Okay, now that these remarks on the WM_CONTEXTMENU message are out of the way, we can return to our discussion of the IContextMenu interface next time.

  • The Old New Thing

    How to host an IContextMenu, part 1 - Initial foray

    • 20 Comments

    Most documentation describes how to plug into the shell context menu structure and be a context menu provider. If you read the documentation from the other side, then you also see how to host the context menu. (This is the first of an eleven-part series with three digressions. Yes, eleven parts—sorry for all you folks who are in it just for the history articles. I'll try to toss in an occasional amusing diversion.)

    The usage pattern for an IContextMenu is as follows:

    The details of this are explained in Creating Context MenuHandlers from the point of view of the IContextMenu implementor.

    The Shell first calls IContextMenu::QueryContextMenu. It passes in an HMENU handle that the method can use to add items to the context menu. If the user selects one of the commands, IContextMenu::GetCommandString is called to retrieve the Help string that will be displayed on the Microsoft Windows Explorer status bar. If the user clicks one of the handler's items, the Shell calls IContextMenu::InvokeCommand. The handler can then execute the appropriate command.

    Read it from the other side to see what it says you need to do as the IContextMenu host:

    The IContextMenu host first calls IContextMenu::QueryContextMenu. It passes in an HMENU handle that the method can use to add items to the context menu. If the user selects one of the commands, IContextMenu::GetCommandString is called to retrieve the Help string that will be displayed on the host's status bar. If the user clicks one of the handler's items, the IContextMenu host calls IContextMenu::InvokeCommand. The handler can then execute the appropriate command.

    Exploring the consequences of this new interpretation of the context menu documentation will be our focus for the next few weeks.

    Okay, let's get started. We begin, as always, with our scratch program. I'm going to assume you're already familiar with the shell namespace and pidls so I can focus on the context menu part of the issue.

    #include <shlobj.h>
    
    HRESULT GetUIObjectOfFile(HWND hwnd, LPCWSTR pszPath, REFIID riid, void **ppv)
    {
      *ppv = NULL;
      HRESULT hr;
      LPITEMIDLIST pidl;
      SFGAOF sfgao;
      if (SUCCEEDED(hr = SHParseDisplayName(pszPath, NULL, &pidl, 0, &sfgao))) {
        IShellFolder *psf;
        LPCITEMIDLIST pidlChild;
        if (SUCCEEDED(hr = SHBindToParent(pidl, IID_IShellFolder,
                                          (void**)&psf, &pidlChild))) {
          hr = psf->GetUIObjectOf(hwnd, 1, &pidlChild, riid, NULL, ppv);
          psf->Release();
        }
        CoTaskMemFree(pidl);
      }
      return hr;
    }
    

    This simple function takes a path and gets a shell UI object from it. We convert the path to a pidl with SHParseDisplayName, then bind to the pidl's parent with SHBindToParent, then ask the parent for the UI object of the child with IShellFolder::GetUIObjectOf. I'm assuming you've had enough experience with the namespace that this is ho-hum.

    (The helper functions SHParseDisplayName and SHBindToParent don't do anything you couldn't have done yourself. They just save you some typing. Once you start using the shell namespace for any nontrivial amount of time, you build up a library of little functions like these.)

    For our first pass, all we're going to do is invoke the "Play" verb on the file when the user right-clicks. (Why right-click? Because a future version of this program will display a context menu.)

    #define SCRATCH_QCM_FIRST 1
    #define SCRATCH_QCM_LAST  0x7FFF
    
    void OnContextMenu(HWND hwnd, HWND hwndContext, UINT xPos, UINT yPos)
    {
      IContextMenu *pcm;
      if (SUCCEEDED(GetUIObjectOfFile(hwnd, L"C:\\Windows\\clock.avi",
                       IID_IContextMenu, (void**)&pcm))) {
        HMENU hmenu = CreatePopupMenu();
        if (hmenu) {
          if (SUCCEEDED(pcm->QueryContextMenu(hmenu, 0,
                                 SCRATCH_QCM_FIRST, SCRATCH_QCM_LAST,
                                 CMF_NORMAL))) {
            CMINVOKECOMMANDINFO info = { 0 };
            info.cbSize = sizeof(info);
            info.hwnd = hwnd;
            info.lpVerb = "play";
            pcm->InvokeCommand(&info);
          }
          DestroyMenu(hmenu);
        }
        pcm->Release();
      }
    }
    
        HANDLE_MSG(hwnd, WM_CONTEXTMENU, OnContextMenu);
    

    As noted in the checklist above, first we create the IContextMenu, then initialize it by calling IContextMenu::QueryContextMenu. Notice that even though we don't intend to display the menu, we still have to create a popup menu because IContextMenu::QueryContextMenu requires on. We don't actually display the resulting menu, however; instead of asking the user to pick an item from the menu, we make the choice for the user and select "Play", filling in the CMINVOKECOMMANDINFO structure and invoking it.

    But how did we know that the correct verb was "Play"? In this case, we knew because we hard-coded the file to "clock.avi" and we knew that AVI files have a "Play" verb. But of course that doesn't work in general. Before getting to invoking the default verb, let's first take the easier step of asking the user what verb to invoke. That exercise will actually distract us for a while, but we'll come back to the issue of the default verb afterwards.

    If the code above is all you really wanted (invoking a fixed verb on a file), then you didn't need to go through all the context menu stuff. The code above is equivalent to calling the ShellExecuteEx function, passing the SEE_MASK_INVOKEIDLIST flag to indicate that you want the invoke to go through the IContextMenu.

    [Typo fixed 25 September.]

  • The Old New Thing

    Why does my mouse/touchpad sometimes go berzerk?

    • 67 Comments

    Each time you move a PS/2-style mouse, the mouse send three bytes to the computer. For the sake of illustration, let's say the three bytes are x, y, and buttons.

    The operating system sees this byte stream and groups them into threes:

    x y b x y b x y b x y b

    Now suppose the cable is a bit jiggled loose and one of the "y"s gets lost. The byte stream loses an entry, but the operating system doesn't know this has happened and keeps grouping them in threes.

    x y b x b x y b x y b x

    The operating system is now out of sync with the mouse and starts misinterpreting all the data. It receives a "y b x" from the mouse and treats the y byte as the x-delta, the b byte as the y-delta, and the x byte as the button state. Result: A mouse that goes crazy.

    Oh wait, then there are mice with wheels.

    When the operating system starts up, it tries to figure out whether the mouse has a wheel and convinces it to go into wheel mode. (You can influence this negotiation from Device Manager.) If both sides agree on wheeliness, then the mouse generates four bytes for each mouse motion, which therefore must be interpreted something like this:

    x y b w x y b w x y b w x y b w

    Now things get really interesting when you introduce laptops into the mix.

    Many laptop computers have a PS/2 mouse port into which you can plug a mouse on the fly. When this happens, the built-in pointing device is turned off and the PS/2 mouse is used instead. This happens entirely within the laptop's firmware. The operating system has no idea that this switcheroo has happened.

    Suppose that when you turned on your laptop, there was a wheel mouse connected to the PS/2 port. In this case, when the operating system tries to negotiate with the mouse, it sees a wheel and puts the mouse into "wheel mode", expecting (and fortunately receiving) four-byte packets.

    Now unplug your wheel mouse so that you revert to the touchpad, and let's say your touchpad doesn't have a wheel. The touchpad therefore spits out three-byte mouse packets when you use it. Uh-oh, now things are really messed up.

    The touchpad is sending out three-byte packets, but the operating system thinks it's talking to that mouse that was plugged in originally and continues to expect four-byte packets.

    You can imagine the mass mayhem that ensues.

    Moral of the story: If you're going to hot-plug a mouse into your laptop's PS/2 port, you have a few choices.

    • Always use a nonwheel mouse, so that you can plug and unplug with impunity, since the nonwheel mouse and the touchpad both use three-byte packets.
    • If you turn on the laptop with no external mouse, then you can go ahead and plug in either a wheeled or wheel-less mouse. Plugging in a wheel-less mouse is safe because it generates three-byte packets just like the touchpad. And plugging in a wheeled mouse is safe because the wheeled mouse was not around for the initial negotiation, so it operates in compatibility mode (i.e., it pretends to be a wheel-less mouse). In this case, the mouse works, but you lose the wheel.
    • If you turn on the laptop with a wheel mouse plugged in, never unplug it because once you do, the touchpad will take over and send three-byte packets and things will go berzerk.

    Probably the easiest way out is to avoid the PS/2 mouse entirely and just use a USB mouse. This completely sidesteps the laptop's PS/2 switcheroo.

Page 373 of 425 (4,250 items) «371372373374375»