• The Old New Thing

    What happens if I manually post an auto-generated message into my message queue?

    • 4 Comments

    As we well know, the window manager generates various messages on-demand rather than posting them into the queue at the time the event occurs. But what happens if you manually post one of these messages, like Post­Message(hwnd, WM_PAINT, 0, 0)? Does that clear the internal flag that says "This window needs a paint message?"

    Nope.

    The window manager does not have a prank call detector. If you post a fake WM_PAINT message, then a fake WM_PAINT message shows up in the message queue. The part of the window manager which manages the "Does this window need to be repainted?" does not wiretap every telephone call to see if somebody is prank-calling a window with the WM_PAINT message, and then say, "You know what? I'm going to make that prank call a reality and make it act like a real WM_PAINT message."

    Imagine if the act of prank-calling somebody caused Prince Albert to stop by for a visit!

    In particular, the posted message goes into the posted message queue and will rise to the front of the queue as messages are retrieved. On the other hand, the auto-generated message will remain in its proto-message state until the queue is finally empty.

    It then follows as a consequence that if you post one of these auto-generated messages, like WM_TIMER message, you don't get any magical coalescing behavior because the coalescing happens as part of the auto-generation, not as part of the Post­Message. These prank-call messages also appear in the message queue in posted order rather than being generated at low priority like normal auto-generated messages.

  • The Old New Thing

    Even though mouse-move, paint, and timer messages are generated on demand, it's still possible for one to end up in your queue

    • 8 Comments

    We all know that the generated-on-demand messages like WM_MOUSE­MOVE, WM_PAINT, and WM_TIMER messages are not posted into the queue when the corresponding event occurs, but rather are generated by Get­Message or Peek­Message when they detect that they are about to conclude that there is no message to return and the generated-on-demand message can be returned. When this happens, the window manager creates the message on the fly, posts it into the queue, and hey, how about that, the Get­Message or Peek­Message function now has a message to return!

    Note that this auto-generate can happen even though the queue is not empty, because the message filters control what messages in the queue can be returned. For example, suppose the message queue contains the following messages:

    • { hwnd1, WM_CLIP­BOARD­UPDATE }
    • { hwnd2, WM_LBUTTON­DOWN }

    (Note that the above diagram is not strictly correct, because the WM_LBUTTON­DOWN message goes into the input queue, not the message queue, but the distinction is not important here.)

    Suppose you now call Get­Message(&msg, hwnd1, WM_MOUSE­FIRST, WM_MOUSE­LAST). None of the messages in the queue satisfy the message filter: The first message meets the window filter, but the message is not in range. The second message meets the message range filter, but does not meet the window filter. The Get­Message function is about to give up and say "I guess I need to wait for a message," but before it finally concedes defeat, it says, "Hang on there. I see a note that tells me that I should auto-generate a WM_MOUSE­MOVE message for window hwnd1. And that message satisfies the message filter. I'll generate it now!"

    The Get­Message function posts the { hwnd1, WM_MOUSE­MOVE } message into the queue (assigning it the current time as the timestamp), and then it says, "Hey, lookie here! A message that satisfies the filter!" It then removes the message from the queue and returns it.

    (Note that this algorithm is conceptual. It doesn't actually work this way internally. In particular, the window manager does not literally talk to itself, at least not out loud.)

    Okay, so in the Get­Message case, even if the message conceptually goes into the queue, it comes right back out immediately, so you never actually observe it there.

    Now repeat the exercise with the Peek­Message function. As before, the WM_MOUSE­MOVE message is posted into the queue with the current time as the timestamp. If the PM_REMOVE flag is passed, then the message is removed from the queue and returned, just like Get­Message. If the PM_NO­REMOVE flag is passed, then things get interesting: The message is returned but not removed from the queue.

    You now have a WM_MOUSE­MOVE message physically residing in the queue!

    This is the answer to the puzzle: If auto-generated messages are generated on demand, how is it possible for them to end up sitting in your message queue?

    I recall a bug investigation from nearly two decades ago which basically boiled down to this issue: Somebody PM_NO­REMOVE'd an auto-generated message and not only left it in the queue, but kept generating new ones without processing the old ones. Eventually, the message queue filled up.

    (Note that this is also the answer to the puzzle: If WM_MOUSE­MOVE is generated on demand, how can it be possible to retrieve a WM_MOUSE­MOVE message with a timestamp different from the current time?)

  • The Old New Thing

    How do I get a window back on the screen when it moved far, far away? Windows 7 (and 8) edition

    • 29 Comments

    Some time ago, I showed how to get a window back on the screen when it moved far, far away. That technique still works in Windows 7 and 8, but there's an easier shortcut that takes advantage of window arrangement features added in Windows 7.

    First, you switch to the application by whatever means. Then hit Win+UpArrow to maximize the window. That should put the window on-screen, albeit at the wrong size. Now you just grab the title bar of the window with the mouse and drag it off the top edge of the screen. Bingo, the window returns to its original position, and you can use the mouse to put it wherever you like.

    This trick doesn't work for windows that cannot be resized (such as Calculator), but for those windows, you can use the old version of the trick.

  • The Old New Thing

    A question about proper disposal of unwanted items with an unhelpful answer

    • 13 Comments

    On an internal mailing list about home maintenance and ownership, somebody asked:

    I have a handful of items that I need to get rid of and probably should not toss into the regular garbage. Any thoughts?

    • Older TV — 32″
    • Propane tanks (full)
    • Lighter fluid
    • Carpet glue adhesive
    • Lawn fertilizer

    The best reply was an unhelpful one.

    You've pretty much got all the components you need to build a bomb. Why dispose of them?

  • The Old New Thing

    The importance of remembering parity in a back-and-forth race on your flying bicycle

    • 2 Comments

    I dreamed that one of my friends had made the U.S. cycling team. (Perhaps because everybody else got busted for doping.) Even more implausibly, I also made the team.

    To celebrate, he challenged me to a short race. The path ran along a river, in which a medium-sized boat was setting sail. Our bicycles somehow could fly (which we considered perfectly normal) and we were flying over the boat, just about keeping pace with it.

    The boat reversed direction many times, and we reversed along with it. At one of the reversals, I thought, "I could take a shortcut if I kept going straight," but I must've lost even/odd count because I flew off the boat... heading back to the starting line.

    Bonus weirdness: For some reason, we were in Sweden, and the race commentator saw a school labeled Gymnasium and made some remark about repurposing buildings left over from the Olympics.

  • The Old New Thing

    Copying a file to the clipboard so you can paste it into Explorer or an email message or whatever

    • 8 Comments

    Today's Little Program takes a fully-qualified file name from the command line and puts that file onto the clipboard. Once there, you can paste it into an Explorer window, or into an email message, or a word processing document, or anybody else who understands shell data objects.

    #include <windows.h>
    #include <shlobj.h>
    #include <atlbase.h>
    #include <shlobj.h>
    
    class COleInitialize {
    public:
     COleInitialize() : m_hr(OleInitialize(NULL)) { }
     ~COleInitialize() { if (SUCCEEDED(m_hr)) OleUninitialize(); }
     operator HRESULT() const { return m_hr; }
     HRESULT m_hr;
    };
    
    // GetUIObjectOfFile incorporated by reference
    
    int __cdecl wmain(int argc, PWSTR argv[])
    {
     COleInitialize init;
     CComPtr<IDataObject> spdto;
    
     if (SUCCEEDED(init) &&
         argc == 2 &&
         SUCCEEDED(GetUIObjectOfFile(nullptr, argv[1], IID_PPV_ARGS(&spdto))) &&
         SUCCEEDED(OleSetClipboard(spdto)) &&
         SUCCEEDED(OleFlushClipboard())) {
      // success
     }
    
     return 0;
    }
    

    The COle­Initialize class is just the OLE counterpart to the CCo­Initialize class we saw some time ago.

    All the program does is take the file name on the command line, asks the shell for the corresponding data object, then puts that object onto the clipboard, erasing what was there before.

    Once the data is on the clipboard, our job is done so we exit.

    No, wait! If you exit while your application has data on the clipboard, that clipboard data may be lost. The documentation for Ole­Set­Clipboard notes:

    If you need to leave the data on the clipboard after your application is closed, you should call Ole­Flush­Clipboard rather than calling Ole­Set­Clipboard with a NULL parameter value.

    Therefore, we stick in a call to Ole­Flush­Clipboard before exiting. This forces any delay-rendered content to be rendered immediately, because we ain't gonna be around to delay-render it no more.

    Note that the file on the command line must be fully-qualified, because we pass it straight to Get­UI­Object­Of­File, which expects a fully-qualified path. Fixing the program to allow relative paths (and to actually print error messages and stuff) is left as an exercise, because Little Programs don't deal with annoying details like error checking and reporting.

  • The Old New Thing

    Who sends the initial WM_UPDATEUISTATE message?

    • 6 Comments

    Last time, we looked at the confusingly-named WM_UPDATE­UI­STATE and WM_CHANGE­UI­STATE messages. But how does the whole indicator thingie get off the ground?

    The default state for a window is to show all indicators. But as a special trick, the dialog manager will send a WM_UPDATE­UI­STATE message with UIS_INITIALIZE after the dialog has been initialized, which turns off the indicators if the last input event was a mouse event. This is its way of inferring whether the dialog box was triggered by a mouse or keyboard action and setting the initial indicators accordingly. (Note that if the user checked Underline keyboard shortcuts and access keys, then the dialog manager leaves the indicators enabled regardless of the last input event.)

    That special WM_UPDATE­UI­STATE message is what gives dialog boxes the extra special feature of hiding the keyboard accelerators until you use the keyboard.

    But notice that only the dialog manager does this. If you want this behavior in your own non-dialog windows, you will need to send the message yourself.

    BOOL MyWindow::OnCreate(...)
    {
     ... create and initialize any child windows ...
    
     // initialize indicators
     BOOL fAlwaysUnderline = FALSE;
     SystemParametersInfo(SPI_GETKEYBOARDCUES, 0,
                          &fAlwaysUnderline, 0);
     if (!fAlwaysUnderline) {
      SendMessage(this->m_hwnd, WM_UPDATEUISTATE,
                  MAKEWPARAM(UIS_INITIALIZE, 0), 0);
     }
    }
    

    Exercise: Why is it important to create and initialize the child windows before sending the WM_UPDATE­UI­STATE message?

    Exercise: Why can't the window manager do this automatically after WM_CREATE returns?

    Exercise: Explain the behavior this customer observes.

    We have a dialog box with three buttons. Sometimes the dialog displays underlines for the hotkeys, and sometimes it doesn't. I know about the feature which hides keyboard accelerators by default, but that doesn't explain why the setting gets ignored sometimes. The first time I show the dialog in my program, I get the underlines, but the second and subsequent times, I do not.
  • The Old New Thing

    Your electric fan is trying to kill you, and other cultural superstitions

    • 72 Comments

    In Korea, it is generally believed that leaving a fan on in an enclosed room can be fatal. Ken Jennings looks at cultural superstitions and wrote a Slate article focusing on the scourge of Korean fan death.

    My mother told me that handling cellophane tape makes you sterile. Though that may have just been her way of getting me to stop playing with cellophane tape.

    What strange cultural superstitions exist in your part of the world? (Of course, this is a bit of an unfair question, because if you genuinely believe it, then you won't recognize it as a strange cultural superstition!)

    Clarification: Please reply in the spirit of the article. Keep it fun.

  • The Old New Thing

    Untangling the confusingly-named WM_UPDATEUISTATE and WM_CHANGEUISTATE messages

    • 11 Comments

    I always get confused by the WM_UPDATE­UI­STATE and WM_CHANGE­UI­STATE messages, and I have to go figure them out each time I need to mess with them. So this time, I'm going to write it down so I don't forget. Because the act of writing it down helps me to remember.

    It's like in school, where the teacher says, "This is a closed-book, closed-notes exam, but you are allowed to bring one piece of standard 8½″×11″ paper with you, on which you can write anything you like. No funny business." You work really hard to create the ultimate sheet of paper to bring to the exam, and then it turns out that during the exam, you barely refer to it at all. Because the act of deciding what to put on the cheat sheet made you remember the material.

    Part of the problem with the messages WM_UPDATE­UI­STATE and WM_CHANGE­UI­STATE is their confusing names, because to most people update and change are basically the same concept. The difference is the direction the message travels. Before we look at that, let's look at the mysterious WPARAM.

    The WPARAM specifies what action you want to perform (initialize, set, or clear) and the target of the action (focus, accelerators, or both).

    Action Meaning
    UIS_SET Set the flag (hide the indicator).
    UIS_CLEAR Clear the flag (show the indicator).
    UIS_INITIALIZE Set or clear the flag based on whether the last input event was mouse (set) or keyboard (clear).

    Setting a flag hides the corresponding indicator. For example, if you have a UIS_SET for UISF_HIDE­FOCUS, that means that you want to hide focus indicators.

    Clearing a flag shows the corresponding indicator. For example, if you have a UIS_CLEAR for UISF_HIDE­FOCUS, that means that you want to show focus indicators.

    Yes, it's a bit of a double-negative situation.

    Each window has its own internal state that remembers which indicators have been hidden for that window. You can query this state by sending the window a WM_QUERY­UI­STATE message.

    The WM_UPDATE­UI­STATE message travels down the tree: When a window receives the WM_UPDATE­UI­STATE message, it updates its state according to the WPARAM and then forwards the message to its children. Therefore, if you want to change the state for an entire window tree, you can send the WM_UPDATE­UI­STATE message to the top-level window, and the message will be delivered to that window and all its children.

    It's called update because it says, "Okay, listen up everybody, this is what we're going to do."

    The WM_CHANGE­UI­STATE message is more like a change request. It travels up the tree: When a window receives the message, it sees if the state being requested matches the window's current state. If so, then processing stops since there is nothing to change. Otherwise, the window forwards the message to its parent. The idea here is to push the change request up the tree until it finds the top-level window.

    If a top-level window receives a WM_CHANGE­UI­STATE message for a state change that actually changes something, it turns around and sends itself a WM_UPDATE­UI­STATE message, which as we saw before, tells the entire window tree to set its indicator state to the value specified.

    Okay, let's draw a picture. Suppose we have a top-level window with two children, and suppose that everybody starts out with all indicators hidden.

               
      A
    hideFocus=1
    hideAccel=1
     
           
         
    B
    hideFocus=1
    hideAccel=1
      C
    hideFocus=1
    hideAccel=1

    Window B decides that it wants to show accelerators, say because the user tapped the Alt key. It sends itself a WM_CHANGE­UI­STATE message with a wParam of MAKEWPARAM(UIS_CLEAR, UISF_HIDE­ACCEL).

    The WM_CHANGE­UI­STATE message handler for Window B sees that the UISF_HIDE­ACCEL flag is set, so the clear action is meaningful. It forwards the request to its parent, Window A.

    The WM_CHANGE­UI­STATE message handler for Window A also sees that the UISF_HIDE­ACCEL flag is set, so the clear action is meaningful. Since it has no parent, Window A converts the WM_CHANGE­UI­STATE message to a WM_UPDATE­UI­STATE message and sends it to itself.

    The WM_UPDATE­UI­STATE message handler for Window A sees that it is being told to clear the UISF_HIDE­ACCEL flag, so it clears the flag and then forwards the mesage to both its children.

    Each of the child windows B and C receive the WM_UPDATE­UI­STATE message and see that they are also being told to clear the UISF_HIDE­ACCEL flag, so they do so. Those windows have no children of their own, so message processing stops. By this mechanism, Window B has managed to convince all the other windows in the hierarchy to clear the UISF_HIDE­ACCEL flag.

               
      A
    hideFocus=1
    hideAccel=0
     
           
         
    B
    hideFocus=1
    hideAccel=0
      C
    hideFocus=1
    hideAccel=0

    Now, suppose that Window C also decides to clear the accelerator indicator. It does the same thing as Window B and sends itself a WM_CHANGE­UI­STATE message with a wParam of MAKEWPARAM(UIS_CLEAR, UISF_HIDE­ACCEL). This time, the WM_CHANGE­UI­STATE message handler for Window C sees that the UISF_HIDE­ACCEL flag is already clear, so the clear action is redundant. Message processing stops.

    These two examples show the flow of the UI state change messages. When somebody wants to suggest a change to the UI state, they send themselves a WM_CHANGE­UI­STATE message with a description of what they want to change. The above algorithm then kicks in to decide whether the change is meaningful, and if so, it notifies all the other windows in the hierarchy about the new state.

    Next time, we'll look at how this whole indicator state thing gets off the ground.

  • The Old New Thing

    Hey look, now I'm Director of Strategic Planning, oh, and my name also changed to Oliver Lee

    • 16 Comments

    It looks like the Visio blog populated a sample organizational chart with pictures of Microsoft employees, and I am now Oliver Lee, Director of Strategic Planning.

    My secret identity has been revealed. I'm moonlighting at Contoso.

Page 1 of 388 (3,877 items) 12345»