Work's heading up right now, so this one will be briefer than expected.
Windows has its own idea of threading models. In general, each window on the system is owned by a thread, and that thread is responsible for ensuring that the thread is constantly pumping messages for that window.
This last can cause some real difficulties. As an example, just recently someone posted on an internal mailing list that they were having problems with their application hanging.
They'd started a really long process on their windows application's main thread, and they wanted to provide feedback to the user. So they created a new thread, and had the thread put up a progress dialog while the main thread was busy. They were upset because their dialog box didn't make progress, instead it seemed to get frozen just after coming up.
Raymond immediately came to the rescue with the right answer: The new window was trying to send a WM_SETFOCUS to the old window to tell the old window that it was losing focus, and because the old window's thread wasn't pumping messages, the new window hung.
This is why windows design books keep talking about having a "UI thread" and a "work thread" when designing your applications - it's absolutely critical to keep windows messages pumping on your main thread.
So if your thread ever creates a window, you must pump messages for that window regularly. The other critical thing to remember is that there are certain functions that require interaction with the windowproc for the window (by calling SendMessage), this is especially true for windows controls (which are typically built from one or more windows). And there are other functions (like DestroyWindow) that MUST be called from the thread that owns the window.
In general, you can't ever call SendMessage send a message to a window that's owned by another thread (you might get away with it but your chances of deadlocking are pretty high). There are a couple of ways around this. First, you could use the GetWindowThreadProcessId API to see if your current thread is the thread that owns the window (this is what the .Net framework "Control.InvokeRequired" method does). If your thread owns the window, you can simply call SendMessage, if it doesn't, you can post a custom message to the window and do your work on that thread (more-or-less, that's what "Control.Invoke" does under the covers).
Another method is to use the SendNotifyMessage API. Of course, if you use SendNotifyMessage to send a message to another thread, then then you can't use this API to retrieve information from the destination window (like issuing a WM_GETITEM call).
While writing this article, I found an old but still relevant article on MSDN here that describes all of this fairly well.
Edit1: Fixed typo (thanks Ryan).
Edit2: Added comment about DestroyWindow