ShowWindow, UpdateWindow, SendMessage, PostMessage, PeekMessage, GetMessage

ShowWindow與UpdateWindow

CWnd::UpdateWindow()

       In MSDN:Updates the client area by sending a WM_PAINT message if the update region is not empty.The UpdateWindow member function sends a WM_PAINT messagedirectly,bypassing the application queue. If the update region is empty, WM_PAINT is not sent.

       首先,ShowWindow本身是不會產生重畫消息的,它的作用僅僅是把窗口顯示出來。不過,當窗口顯示的時候,Windows會自動探測窗口的內容是否需要重畫、以及需要重畫的區域組成,比如你的窗口位置直接在屏幕外,或者你的窗口被別的窗口完全擋住,當然就不需要重畫,如果你的窗口只露出一部分,那麼就只有這一部分需要重畫。這個過程與你移動窗口、切換窗口的時候Windows所做的事情是一樣的——自動判定你的窗口有哪一部分原來不顯示而現在需要顯示,然後對這部分區域調用InvalidateRect()。這個函數的作用並不是立刻重畫這些區域,而是對這些區域做上標記。多次調用這個函數,新標記的區域會與以前標記的區域合併。

       之後,當你的消息隊列完全空了的時候,假若windows又發現你窗口所標記的重畫區域不爲空,那麼Windows就在你的消息隊列裏放一個WM_PAINT消息,讓你重畫。根據這一流程可知,假若我們的消息隊列一直很忙的話,那麼窗口是沒機會獲得WM_PAINT消息的。其次,假定消息隊列裏有若干個消息,每個都導致一部分窗口區域需要重畫,那麼最後只會重畫一次,只不過重畫的範圍是幾個區域的合併。再有,某些特殊情況下,有可能會不希望窗口被重畫、或者至少其中某一部分不要重畫,那麼你可以在消息隊列被取空之前(尚未發出 WM_PAINT),用ValidateRect把窗口的某一部分乃至全部都取消標記。如果所有以前被標記的部分全被你取消掉了,那麼等消息隊列空了以後,也不會再有WM_PAINT發出了。

       當你在處理WM_PAINT消息進行重畫的時候,BeginPaint的一個重要作用,就是在它返回的DC裏,用原來標記的區域製作一個剪裁區域(ClipRegion),從而使你的所有重畫操作都被限定在這一個區域中。這是一個很重要的特性。舉例來說,假若你的窗口用一張位圖作爲背景,處理 WM_PAINT的時候用BitBlt之類的方法往屏幕上貼圖。如果某一次有一個別的窗口僅僅蓋住了你窗口的一個小角,當它拿開的時候,如果沒有剪裁區域的話,那麼就會對整個窗口貼圖,這不僅很慢,而且會引起你窗口中的各個子窗口的閃爍。但有了剪裁區域的話,你的代碼雖然還是在對整個窗口貼圖,但實際上只有位於剪裁區域內的那部分操作有效,其它的都被Windows放棄了,所以速度會快得多。有時我們自己也需要更新窗口的顯示內容,這時候也是通過調用 InvalidateRect來做。不過,很多人在這種情況下習慣於將整個窗口統統Inalidate,這樣做倒是很方便,不過這是一個很不好的習慣。除非你需要更新的內容波及到整個窗口,否則應該僅僅把需要改變的那部分Invalidate。

      上面說的剪裁區域僅僅是BeginPaint的一個作用,BeginPaint還有其它作用,都是跟重畫這個任務緊密連接的,因此,在響應WM_PAINT消息的時候,必須使用BeginPaint所獲取的DC句柄來畫圖,絕不能用GetDC等其它方式。相對應的,這個句柄也必須使用EndPaint來釋放。如果在響應WM_PAINT的時候沒有調用BeginPaint和EndPaint(例如用 GetDC和ReleaseDC來畫圖),其中一個副作用就是:重畫區域的標記不會被取消。於是當你響應完這一個WM_PAINT之後,Windows會發現你的窗口還有區域被標記爲重畫,於是再次發出WM_PAINT,於是你就永無休止地重畫下去了。

       從上述可知,單純一個ShowWindow,照樣會正確重畫窗口內容,只不過重畫是在消息隊列取空之後。有時我們希望窗口被立即重畫,而不是去等待那個不確定的消息隊列,此時就需要用到UpdateWindow。這個函數的作用只有一個:假若當前被標記爲重畫的區域存在(不存在的話它什麼也不做),那麼立刻讓Windows使用SendMessage的方式來對你的窗口發送WM_PAINT。


SendMessage與PostMessage

       說到這裏,就要說一下SendMessage與PostMessage的區別了。PostMessage是把消息放到消息隊列尾部,然後通過程序的消息環逐個從消息隊列裏取出來進行處理。SendMessage卻不是這樣,它實際上根本不經過消息隊列。對SendMessage的處理分兩種情況:

       1、由本線程發出的SendMessage,例如在自己的消息處理過程中調用UpdateWindow,從而發出的WM_PAINT。對於這種情況,SendMessage實際上直接調用你窗口的消息處理函數。也就是說,在進行消息處理的時候對本窗口SendMessage,實際上是遞歸調用。整個過程是:消息環取出消息A(假定A是PostMessage放進消息隊列的)-> 處理消息A -> SendMessage對本線程的窗口發送消息B -> 處理消息B -> 繼續處理消息A -> 消息環再取下一個消息。

       2、由另一個線程發出的SendMessage。此時當然不能直接調用了。這種情況下,windows會把發送消息的線程掛起,然後,當接收消息的線程調用PeekMessage或者GetMessage的時候,這兩個函數會立刻調用你窗口的消息處理函數,直到處理完畢(此時發送線程的 SendMessage才返回),PeekMessage或者GetMessage纔會去檢查消息隊列,並從中取出一個返回。

       總之,不論哪種情況,SendMessage發送的消息,跟消息隊列均沒有任何關係,而且也不通過消息環執行(前者是直接調用,後者是在PeekMessage或GetMessage函數內部調用)。


PS: BeginPaint、InvalidateRect兩個函數要注意

參考文章:

http://topic.csdn.net/u/20100818/23/b30e7e3d-2969-45ec-9054-1e98cca442e5.html


PeekMessage與GetMessage
相同點:
       PeekMessage函數與GetMessage函數都用於查看應用程序消息隊列,有消息時將隊列中的消息派發出去。

不同點:
       無論應用程序消息隊列是否有消息,PeekMessage函數都立即返回,程序得以繼續執行後面的語句(無消息則執行其它指令,有消息時一般要將消息派發出去,再執行其它指令)。
       GetMessage函數只有在消息對立中有消息時返回,隊列中無消息就會一直等,直至下一個消息出現時才返回。在等的這段時間,應用程序不能執行任何指令。(從他們的不同點上來看,PeekMessage函數有點像“乞丐行乞”,有你就施捨點,沒有也不強求。GetMessage函數有點像“強盜打劫”,有你得給,沒有我就等你什麼時候有了再給,這段時間我什麼都不幹,我就等你。)

發佈了1 篇原創文章 · 獲贊 13 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章