If an operation results in messages being sent, then naturally the target window must be processing messages for the operation to complete


If an operation includes as part of its processing sending messages, then naturally the target window for those messages must be processing messages (or more precisely, the thread which owns the target window must be processing messages) in order for the operation to complete. Why? Because processing messages is the only way a window can receive messages!

It's sort of tautological yet not obvious to everyone.

Generally you run into this problem when you try to manipulate a window from a thread different from the one which created the window. Since windows have thread affinity, operations from off-thread typically need to get moved onto the thread which owns the window because that's where the window really "lives".

The window manager will often try to see how much it can do without marshalling to the thread which owns the window, but when message traffic is involved, you are pretty much stuck. Messages are delivered to a window on the thread to which the window belongs, and there's no way around that.

There are subtle ways in which a function called off-thread can result in message traffic. Generally speaking, you should just assume that any operation on a window may generate messages: Even if they don't do so today, they may do so in the future. For example, changing a window's style did not generate message traffic in early versions of Windows, but in Windows 95, it began generating WM_STYLECHANGING and WM_STYLECHANGED messages. This isn't called out explicitly in the documentation for SetWindowLong but it's implied by the documentation for WM_STYLECHANGING and WM_STYLECHANGED.

Why isn't there an explicit callout in the documentation for SetWindowLong? At the time the SetWindowLong documentation was originally written, the WM_STYLECHANGING and WM_STYLECHANGED messages did not exist. Therefore the documentation was complete at the time of writing. Circumstances changed elsewhere in the system that had secondary effects on SetWindowLong, but nobody bothered to update the documentation, probably because it didn't even occur to anybody that these effects existed. And then these secondary effects lead to tertiary effects: SetScrollInfo may change the window style to add or remove the WS_HSCROLL or WS_VSCROLL style, which in turn results in a call to SetWindowLong which in turn results in sending the WM_STYLECHANGING and WM_STYLECHANGED messages. Next come quaternary effects on functions like FlatSB_SetScrollInfo, since they call SetScrollInfo as part of their functioning. And so on, and so on. Just tracking down the full ripple effect of those two new messages is probably impossible.

But the root cause of all these ripple effects is operating on a window (particularly modifying a window) from a thread different from the thread that owns the window. Avoid that, and you'll avoid the whole issue of which operations generate messages and which manage to sneak by without needing to send any messages (at least not yet).

Comments (16)
  1. Joshua says:

    In other words, why documentation is always incomplete.

  2. Ben Voigt [Visual C++ MVP] says:

    The glaring exception being calling SendMessage from the thread that owns the window, or is that not considered sending a message?

    (Yes, I know that much of the post makes it clear we're talking about cross-thread messages, but the highly quotable first sentence omits that constraint.)

    [That's already covered by the rule "a thread can receive incoming sent messages if it is waiting for an outbound sent message to complete." If a thread is sending a message to itself, then the conditions are met. (The incoming and outbound messages happen to be the same message.) -Raymond]
  3. rs says:

    Isn't there a problem even if you send messages from the thread that owns the window? What if you decide to call SetScrollInfo in your WM_STYLECHANGING handler?

    [See Ben's comment. A thread can respond to messages that it sends to itself since it meets the criteria for receiving them. So much for trying to cover advanced topics in the blog… -Raymond]
  4. rs says:

    I suppose my comment was a bit off-topic. I meant to say that the fact functions can send undocumented messages can cause trouble even in the same thread. Though in many situations the window manager probably works around recursive calls etc.

    "So much for trying to cover advanced topics in the blog…"

    Those messaging and user interface topics are the ones I enjoy most, so keep on sharing them with us!

  5. Clipboarder Gadget says:

    @rs

    Before SetWindowLong wasn't sending WM_STYLECHANGING messages you couldn't call SetWindowLong from WM_STYLECHANGING because WM_STYLECHANGING wasn't invented yet… If I understand you right…

    But you could have called SetWindowLong for every incoming message on your own window. That's why I'm quite impressed that that message made it into Windows 95.

  6. Miral says:

    Presumably, with access to the source, it wouldn't have been hard to comprehensively identify the docs that would need to be updated, if not necessarily so to actually do the update.  After all, WM_STYLECHANGING could only be generated in response to some SetWindowLong(GWL_STYLE, …), so any function that calls that would need its help updated accordingly (and then cascade).

    The trick lies in recognising that a doc change is needed at the time the new feature is introduced — which sounds like it should be easy, but often isn't, especially when the doc team and the development team are separate.  (Now where's that time machine?)

    (And do please keep talking about this sort of stuff.  It's fascinating.)

    [Okay, let's try it. Let's find all the functions that call SetWindowLong(GWL_STYLE, …). Oh, there are 10,000 of them. Most of them are not public APIs, but that doesn't preclude them from being called indirectly from a public API. And it's not just the transitive call closure; you have to do data tracing, too. Say, a function that calls SetWindowLong(GWL_STYLE) is called only conditionally from function X, which is called from function Y only when a certain flag is set. That flag is set by function Z, which is called from a public API. Aha, so the public API needs to be documented as "This may result in WM_STYLECHANGING being sent to the window." Trick question! The way that the public API calls function Z is in such a way that the code path that sets the flag is never hit. Oh, did I mention that your 10,000 hits didn't catch all the possible callers? (It didn't find people who do "int which = GWL_STYLE; SetWindowLong(which, …).") -Raymond]
  7. Drak says:

    I agree with Raymond here. Even in our (smaller than Windows :) products, trying to figure out where a certain function is called and from what locations is extremely difficult. And somehow you always manage to miss some places until after it goes to 'testing'.

  8. Chris says:

    I find the assertion in the title of the post surprising. Are there not fire-and-forget cases where you don't require that the message is actually received, just that it is sent and if it doesn't get through – because the recipient isn't processing messages – that's the recipient's "too bad"?

  9. Damien says:

    @Chris – perhaps the reader is expected to gather that this is specifically about Sending messages, as opposed to Posting messages. SendMessage doesn't return until the target window procedure has finished processing the message.

  10. Alex Grigoriev says:

    Another can of worms those hidden SendMessage calls open is the fact that those calls also dispatch any queued sent messages from another thread (including messages sent by hidden SendMessage calls). This may cause undesired and undocumented reentrancy.

    Unless SendMessageTimeout(,,,SMTO_BLOCK) is used in every case.

  11. Gabe says:

    Of course, prior to Win32, it didn't really matter if some operation resulted in messages being sent — the mere fact that your code is executing means that every other window in the system is processing messages.

  12. Ben Voigt [Visual C++ MVP] says:

    @Raymond: Does the behavior Alex describes (sent messages from other threads get processed) occur when a thread sends its own window a message?  I'm pretty sure it does not, contrary to the rule you've said applies.  (NB: The SendMessage page on MSDN says "If the specified window was created by the calling thread, the window procedure is called immediately as a subroutine.")

    It's not that you can't cover advanced topics on your blog, it's that a few of your readers are familiar enough with the topic to know when there's even more complexity (or inconsistency) than your explanation accounts for.

    [I'm assuming that people who understand the complexity can fill in the details themselves. Remember the Nitpicker's Corner? (And the inbound sends are processed while waiting, and if you send to yourself there is no wait. But you knew that, so why bring it up?) -Raymond]
  13. Alex Grigoriev says:

    Remember kids. Don't mess with other threads' windows. And everything will be OK. Seriously. "Can of worms" applies completely to cross-thread windowing calls.

  14. Ben Voigt [Visual C++ MVP] says:

    @Alex: Unless you're trying to use message passing to simplify parallel programming.  Which is a VERY good idea.  But then you should use PostMessage, not SendMessage.

  15. Miral says:

    [Oh, did I mention that your 10,000 hits didn't catch all the possible callers? (It didn't find people who do "int which = GWL_STYLE; SetWindowLong(which, …).") -Raymond]

    You could find all functions that call SetWindowLong, and then filter that list by those that also refer to GWL_STYLE.  Should catch most of them.  (Granted, there are still potential pathological cases, but they're unlikely to occur outside of contrived examples — at least in this case.)

    But your response basically agrees with what I was trying (perhaps not very well) to say: identifying the docs that *might* need to be updated would be relatively easy — figuring out which of those actually need to be updated and in which way is the tricky bit.

    It's much easier (from an application perspective) to follow Alex & Ben's advice and avoid using SendMessage from any thread other than the one that owns the window.  Makes life much simpler. :)

    [You're assuming that the SetWindowLong and GWL_STYLE are in the same function.

    void UpdateBits(int nIndex, LONG lMask, LONG lValue)
    {
     LONG lCurrent = GetWindowLong(m_hwnd, nIndex);
     LONG lNew = (lValue & lMask) | (lCurrent & ~lMask);
     SetWindowLong(m_hwnd, nIndex, lNew);
    }
    UpdateBits(GWL_STYLE, WS_CAPTION, 0);
    

    This is hardly a contrived example. UpdateBits is actually a quite handy function. But we're still in basic agreement: Identifying all the functions that are affected is not practical in a large code base. -Raymond]

  16. Marc K says:

    It's fine to argue that keeping documentation up to date is a tough task, but it is now known by Microsoft employees that calling SetWindowLong will generate WM_STYLECHANGING and WM_STYLECHANGED messages and the official documentation for SetWindowLong still doesn't mention this.  (Though, it is mentioned in the Community Content section.)

Comments are closed.

Skip to main content