2017年4月4日 星期二

Windows消息傳遞

http://blog.xuite.net/grimmslaw/78/53174061-SendMessage()+%E8%88%87+PostMessage()

在C++就是使用PostMessage和SendMessage
而在C#其實有另一種委派(delegate),這方面我就不熟了

所以我這邊只講PostMessage和SendMessage

基本上現代的程式都是多執行緒程式,一般在做處理時,要將運算比較多的部分移到背景執行緒去做,最後再通知主執行緒處理畫面UI的動作,這時候就會用到PostMessage和SendMessage,

當然您也可以用Timer監控一些flag做一些UI更新,但是您想想,假設我30ms觸發Timer檢查一次UI要不要更新,那1秒鐘要檢查30次,要是您要檢查很多總類的UI更新,那不就要判斷很多flag例如下面這樣

void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
If(flag1)
{
// 更新某部分UI
}
If(flag2)
{
// 更新某部分UI
}
If(flag3)
{
// 更新某部分UI
}
}


這樣有壞處,就是您Timer要一直跑,再來就是您處理這麼多flag更新畫面不累嗎
因此後來就有所謂的PostMessage和SendMessage,通知主Form的WndProc處理畫面

如連結講得的
PostMessage通知完主Form處理會馬上return,當主畫面有空時會去處理接收到的消息(PostMessage一次,會處理一次)
SendMessage則是會Block住,等到送出去的消息處理完,才會執行下一行


基本上意思就是thread做工作,要處理UI時再通知主Form處理,只需要一次的動作,也比較直觀,並且不需要寫一大堆flag做同步動作

當然了thread的處理會有同步的問題要解決,基本上要先知道Critical Section或mutex來處理同步問題,礙於篇幅,這裡先不講,往後會再補充詳細

總之意思大概就是假設您將影像存在一個陣列裡,要是這時您通知主Form處理將影像陣列畫到畫面上,結果您的執行緒在while loop裡又重新將影像陣列重新new,這時您可能剛好再畫影像,結果影像陣列剛好重新new時位置換地方了,導致出錯

這種細節很討厭的,要做很多同步處理,只要沒處理好就會當機,程式根本就不能用
好啦,總之會有同步問題往後有機會會講清楚
這裡您可以參考我的範例裡面註解的地方做建置,您大概就知道用法了,

總之我會有幾個Button,您點點看,照我說的地方下中斷點,追Code看看,您就知道了
這裡就不打那麼多了,請看我的範例就好

總之Wndproc是Windows程式用來接收訊息的地方,每個元件都有一個Wndproc,但是基於方便,我們統一送給主Form的Wndproc,而不是到處送給底下的vcl元件,避免太亂

用法舉PostMessage的例子(SendMessage一樣,只是效果不同)
PostMessage(Handle, WM_EVENT, event, (DWORD)data);
其中Handle是您要處理的主Form
WM_EVENT是自訂義的東西,用來識別您要接收的事件

#define WM_EVENT WM_USER + 10000

Event就是您的事件放在lparam,data,放您要送的變數或指標放在rparam,用以傳遞訊息
總之送過去,您只要照我這樣寫,對應的處理函式會被觸發,您就可以在那邊處理UI的東西了

我這只提供MESSAGE_MAP法,其實您可以virtual繼承Wndproc來觸發您要執行的事件,意思是相同的

這裡要提到的是,照理講,我裡面Button7在執行緒執行UI操作,應該會當掉的,不知道為什麼在XE10沒當…

其實在執行緒執行UI不是每次都會當的,他是有機會當掉,例如兩個執行緒同時存取相同VCL元件,

我範例裡面有6個按鈕5~10,您如果隨便點不點7,10,影像顯示正常,

要是您隨便點的話,例如7,9快速點(先把我連續post_event註解打開,讓他比較容易打架,不然您改成兩個執行緒,用while loop一直畫),過一陣子TImage顯示會異常,

也就是兩個執行緒不能同步更新TImage的,還是乖乖通知主Form更新吧

範例連結
https://drive.google.com/open?id=0B6u5oifJv3EKV184Smh3SFdYelU






沒有留言:

張貼留言