消息处理(二):投递与发送
消息的投递(Post)过程类似于普通信件的投递过程。普通信件通过邮递员到达收信方的信箱里,收信方取得该信,阅读信件内容,并可能回信。在这个过程中,邮递员的责任只是将信件送到收信者的信箱,一切任务就完成了。
消息的发送(Send)过程理解为打电话交流的过程。甲找到乙的电话号码,然后开始拨号。如果运气好,拨通了乙的电话,那么两人就联系上了。假设甲方从不主动挂断电话,那么只有乙方先挂断电话,两人的一次联系才宣告结束。
PostMessage函数
BOOL PostMessage( HWND hWnd, //目标窗口句柄 UINT Msg, //消息 WPARAM wParam, //第一个消息参数 LPARAM lParam //第二个消息参数 );
CWnd::PostMessage提供了对PostMessage的简单包装,他首先断言窗口句柄表示一个窗口,然后以窗口句柄为第一个参数调用全局的PostMessage:
_AFXWIN_INLINE BOOL CWnd::PostMessage(UINT message, WPARAM wParam, LPARAM lParam) { ASSERT(::IsWindow(m_hWnd)); return ::PostMessage(m_hWnd, message, wParam, lParam); }
hWnd有两个特殊的值:HWND_BROADCAST和NULL。前者表示要将该消息投递到系统当前所有的非子窗口。后者表示由线程而不是某个窗口的窗口过程来处理该消息。
Msg表示投递的消息标志,最后两个参数则表示特定于该消息的参数。千万要注意,因为投递消息类似于异步调用,所以在消息参数中传递指针是非常危险。
SendMessage函数
LRESULT SendMessage( HWND hWnd, //接收消息的目标窗口 UINT Msg, //消息标志 WPARAM wParam, //第一个消息参数 LPARAM lParam //第二个消息参数 );
可以将hWnd的参数设置为HWND_BROADCAST,表示系统所有的非子窗体广播此消息。
为了避免调用线程陷入永久的等待状态,可以用SendMessageTimeout代替SendMessage:
LRESULT SendMessageTimeout( HWND hWnd, //窗口句柄 UINT Msg, //消息标记 WPARAM wParam, //第一个消息参数 LPARAM lParam, //第二个消息参数 UINT fuFlags, //消息发送选项 UINT uTimeout, //超时,以毫秒为单位 PDWORD_PTR lpdwResult, //返回值,依赖于特定的消息 );
关键是fuFlag参数,它决定了该如何发送该消息,fuFlag参数的值及含义如下表:
值 | 说 明 |
SMTO_ABORTIFHUNG | 如果目标线程处于挂起状态,调用线程将不等待超时而立即返回 |
SMTO_BLOCK | 阻止调用线程在目标线程处理完消息之前处理其他请求 |
SMTO_NORMAL | 在等待目标线程处理消息时,允许调用线程处理其他请求 |
SMTO_NOTIMEOUTIFNOTHUNG | 对于XP/WIN2000有意义:如果目标线程未挂起,调用线程即使发生超时也会等待目标线程处理完该消息 |
如果调用成功,SendMessageTimeout会返回非0值;如果调用失败或发生超时,则返回0。为了防止调用线程被无限等待,可以将fuFlag参数设为SMTO_BLOCK,并设置一定的超时时间。
说明:如果是广播消息,假定有两个目标窗口将处理该消息,那么目标线程将总共有2*uTimeout毫秒的等待期。
CWnd::SendMessage对SendMessage进行了简单的包装,它验证窗口句柄的有效性,然后调用SendMessage API函数:
_AFXWIN_INLINE LRESULT CWnd::SendMessage(UINT message, WPARAM wParam, LPARAM lParam) { ASSERT(::IsWindow(m_hWnd)); return ::SendMessage(m_hWnd, message, wParam, lParam); }
提示:CWnd没有提供对SendMessageTimeout的包装。
同属于SendMessage系列的API还有SendNotifyMessage和SendMessageCallback,其行为特性介于PostMessage和SendMessage之间。
SendNotifyMessage函数:
BOOL SendNotifyMessage( HWND hWnd; //窗口句柄 UINT Msg; //消息标志 WPARAM wParam, //第一个消息参数 LPARAM lParam //第二个消息参数 );
如果hWnd窗口由调用线程创建,那么其行为特性跟SendMessage相同,这时将等待消息处理完毕后才返回。如果hWnd由另一线程创建,其行为特性则与PostMessage相同,它不必等待目标线程处理完该消息而直接返回。
SendMessageCallback函数:
SendMessageCallback的行为特性类似于PostMessage,也是在发出消息后,不等待目标窗口处理该消息就立刻返回,但它允许消息处理完毕后,让系统执行指定的函数。
BOOL SendMessageCallback( HWND hWnd, //窗口句柄 UINT Msg, //消息标志 WPARAM wParam, //第一个消息参数 LPARAM lParam, //第二个消息参数 SENDASYNCPROC lpCallBack, //回调函数 ULONG_PTR dwData //应用范围内的数据 );
SENDASYNCPROC是回调函数的类型,它具有如下形式的签名:
VOID CALLBACK SendAsyncProc( HWND hwnd, //目标窗口的句柄 UINT uMsg, //消息标记 ULONG_PTR dwData, //应用范围的数据 LRESULT lResult //消息处理结束,依赖于特定的消息 );
传入回调函数的dwData正是在SendMessageCallback中的dwData参数指定的。