When a COM object is instantiated on an STA thread, the thread usually has to implement a message pump in order to marshal calls to and fro other threads (see here).
How the pumping works is actually disclosed. There are internal calls to the .NET runtime which in turn use CoWaitForMultipleHandles to perform the wait on STA threads. The documentation for that API is pretty lacking, but reading some COM books and the Wine source code could give you some rough ideas.
Internally it calls MsgWaitForMultipleObjectsEx with the QS_SENDMESSAGE | QS_ALLPOSTMESSAGE | QS_PAINT flags. Let's dissect what each one is used for.
QS_PAINT is the most obvious, WM_PAINT messages are processed in the message pump. Thus it is really bad idea to do any locking in paint handlers because it will likely get into re-entrant loop and cause a stack overflow.
QS_SENDMESSAGE is for messages sent from other threads and applications. This is actually one way of how the interprocess communication works. The ugly part is that it is also used for UI messages from Explorer and Task Manager, so it pumps WM_CLOSE message (right-click on a non-responsive application in the taskbar and select Close), tray icon messages and possibly something else (WM_ENDSESSION).
QS_ALLPOSTMESSAGE is for the rest. The messages are actually filtered, so only messages for the hidden apartment window and DDE messages (WM_DDE_FIRST - WM_DDE_LAST) are processed.