您的位置:首页 > 其它

PostMessage, SendMessage 之区别

2006-09-26 13:40 459 查看
PostMessage马上返回而
SendMessage等待发送消息的被处理后才返回

-----------------------------------------------------------

Paul DiLascia
Download the code for this article: CQA1200.exe (57KB)

Q
I'm somewhat confused about the difference between PostMessage and SendMessage. I've seen code that uses both functions. Can you tell me when I should use PostMessage versus SendMessage?

A
There are a few subtle differences in the ways you can send messages in Windows®, but the basic difference between PostMessage and SendMessage is that SendMessage sends a message to another window immediately by calling that window's procedure and waiting for it to return, whereas PostMessage queues the message in an MSG structure and returns immediately—without waiting. MSG is short for message, not monosodium glutamate.


With SendMessage, the receiving app processes the message immediately, rather than at some later time, by fetching it from its queue. For example, suppose you write:

CString s;
pWnd->SendMessage(WM_SETTEXT, 0, "Fooblitzky");
pWnd->GetWindowText(&s);
// s is "Fooblitzky"

This example sends WM_SETTEXT to whatever window is represented by pWnd. Windows calls that window's procedure with WM_SETTEXT as the message ID, and zero and "Fooblitzky" as WPARAM and LPARAM. The message proc handles the message and returns; control returns to the next line, which calls GetWindowText. As you'd expect, s is now set to the value "Fooblitzky". SendMessage behaves like a function call, where WM_SETTEXT is the function.


What happens if you use PostMessage instead?

CString s;
pWnd->PostMessage(WM_SETTEXT, 0, "Fooblitzky");
pWnd->GetWindowText(&s);
// s is ????

PostMessage doesn't call the window procedure. Instead, it adds an MSG structure to the message queue of the thread or process that owns pWnd, and returns. The MSG holds the message ID and parameters. The thread or application processes the message when it gets around to it. That is, when its main dispatch loop calls GetMessage and WM_SETTEXT's turn comes up. (There could be other messages already waiting before WM_SETTEXT.) In any event, PostMessage returns immediately without waiting for any of this, so when the next line (GetWindowText) executes, s won't be "Fooblitzky", unless that's what the window text happened to already be before PostMessage was called. (If pWnd belongs to another thread or process, it is of course theoretically possible that Windows could interrupt your thread exactly between PostMessage and GetWindowText, and give the other thread enough of a time slice to process WM_SETTEXT. But that's about as likely as all the molecules in your body suddenly shifting to the left one inch due to a rare confluence of random fluctuations—which is to say, quite unlikely. Well, OK, maybe not that unlikely.)


So there are two ways to deliver a message to a window: SendMessage calls the window proc directly; PostMessage uses the queue. Figure 1 illustrates the difference.



Figure 1 SendMessage versus PostMessage


With all that in mind, let's get back to the original question: when should you use SendMessage versus PostMessage? Most of the time, SendMessage does what you want. It calls a window to perform some action immediately. You want to set the text or change a bitmap or get some property or whatever, and you want to do it now because the next line of code depends on the result. MFC is full of wrapper functions that do nothing but call SendMessage. For example:

// typical MFC wrapper
inline void CEdit::Cut()
{
::SendMessage(m_hWnd, WM_CUT, 0, 0);
}

One benefit of such wrapper functions is they make SendMessage look more like the function call it is (the other benefit is that there's less to type). These wrappers—and SendMessage—are typically used by parent/child windows to communicate.


When should you use PostMessage? Typically you post a message when the action requested is not urgent and possibly lengthy, or you don't want to interrupt current processing to wait on it. These messages have the characteristic of a notification: "Hey, something just happened—in case you care." For example, when you call InvalidateRect or InvalidateRgn to inform Windows that a particular area needs painting, Windows doesn't repaint the area immediately; instead, it adds the area to a list and posts a WM_PAINT message (if one hasn't been posted already). If your app then calls InvalidateRect again, the second region is added to the list. The window doesn't get painted until later, when WM_PAINT is processed. In this way, multiple regions get updated in a single paint operation.


A similar situation arises when a worker thread notifies its UI thread that new data is waiting—by posting a message. The worker goes on working, and the UI processes the message when it gets to it. Asynchronous update is a common programming paradigm, and a clear candidate for PostMessage. In general, PostMessage is safer when you want to send a message to another process or thread, because SendMessage can block. More on this in a moment.


Another time to use PostMessage is when you want to finish processing the current message before processing the new one. For example, suppose you're handling a message and you determine it's time to quit the application (perhaps the message is an Exit command). You don't want to send WM_QUIT via SendMessage; that would call the message proc and quit immediately in the middle of whatever you were doing. Better to post WM_QUIT, so your app can process all the bye-bye messages like WM_CLOSE, WM_DESTROY, and WM_POSTNCDESTROY before quitting gracefully. This example is so commonplace there's a function called PostQuitMessage to post WM_QUIT.


PostMessage usually works better when you want to simulate a command or input event by posting WM_COMMAND or one of the keyboard or mouse messages (which is always dicey, by the way). This holds true because "real" input events usually come in sequences of related messages (such as keydown/keyup pairs) and your app may get befuddled if you try to process a new input message in the middle of one of these sequences. Post works better when simulating input.


Sometimes you have to use PostMessage to get around a quirk or bug to avoid infinite recursion. For example, suppose your WM_SETFOCUS handler (OnSetFocus) determines that the new focus window is not good for some reason and you have to change the focus to a different window. If you call SetFocus from within your OnSetFocus handler, Windows immediately sends another WM_SETFOCUS message—while you're still processing the first one! The result is infinite regress until your stack blows up. To avoid this infelicity, you can post a message to yourself—MYWM_SWITCHFOCUS—so OnSetFocus can finish before you process the message to change the focus. This is one of those examples that's easier to understand in practice than on paper. The main thing to remember is that Windows won't let you SetFocus within a WM_SETFOCUS handler.


Since SendMessage calls the window proc directly, it needs an HWND. How else can it know which message proc to call? But PostMessage adds the message to the message queue, which is associated with a thread or process, not a window. In theory, PostMessage doesn't need a window. And in fact, that's the case.

// post message to myself
PostMessage(NULL, WM_HI_THERE_HANDSOME, ...);



If the HWND is NULL, PostMessage posts the message to the current running thread's message queue. In practice, this feature isn't terribly useful since most times you want to post a message to some other thread (perhaps with PostThreadMessage); but there are always rare situations when it's convenient to post to yourself without a window. (If you think of any, please let me know.)


In case you think you're beginning to understand when to use SendMessage and PostMessage, there are three more message-sending functions you should know about: SendMessageCallback, SendNotifyMessage, and SendMessageTimeout. These functions are useful in the heady world of Win32® and multithreading. In Win32, if you call SendMessage your thread is blocked until the target thread processes the message. If the target thread is itself blocked for some other reason, SendMessage never returns. Oops.


SendNotifyMessage, SendMessageTimeout, and SendMessageCallback were invented to work around this problem. SendNotifyMessage works like SendMessage if the target window belongs to (was created by) the current thread; it works like PostMessage if the window belongs to a different thread. SendMessageTimeout is similar, but it lets you specify a maximum time to wait for the other thread to respond. The Windows function ExitWindowsEx uses SendMessageTimeout to send WM_QUERYENDSESSION to all top-level apps. That is, it tries to be nice by giving each app a chance to die gracefully, but if an app doesn't respond soon enough, ExitWindowsEx kills the app anyway. Too bad, so sad. SendMessageTimeout waits, but not forever.


As you might guess, SendMessageCallback takes a callback function. It sends the message and returns immediately; when the message has been processed, Windows calls your function. SendMessageCallback comes in handy when you'd like to use PostMessage, but you want to know when the message has been handled. Think of it as PostMessage with a return receipt.


PostMessage, SendMessageTimeout, and SendNotifyMessage are all good candidates to use if you want to broadcast a message to all top-level windows by using HWND_TOPMOST as the HWND. It's a bad idea to use HWND_TOPMOST with SendMessage because one dead process can bring your app to a halt.


Figure 2 summarizes the differences among the various message-sending functions. Whew!
http://msdn.microsoft.com/msdnmag/issues/1200/c/default.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: