久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Windows消息機(jī)制(Windows Messaging)

 fisher60 2012-12-11

Windows消息機(jī)制(Windows Messaging)

Windows的應(yīng)用程序一般包含窗口(Window),,它主要為用戶提供一種可視化的交互方式,窗口是由線程(Thread)創(chuàng)建的,。Windows系統(tǒng)通過消息機(jī)制來管理交互,,消息(Message)被發(fā)送,保存,,處理,一個線程會維護(hù)自己的一套消息隊(duì)列(Message Queue),,以保持線程間的獨(dú)占性,。隊(duì)列的特點(diǎn)無非是先進(jìn)先出,這種機(jī)制可以實(shí)現(xiàn)一種異步的需求響應(yīng)過程,。


消息的是什么樣子的,?

消息由一個叫MSG的結(jié)構(gòu)體定義,包括窗口句柄(HWND),,消息ID(UINT),,參數(shù)(WPARAM, LPARAM)等等:
struct MSG
{
    HWND hwnd;
    UINT message;
    WPARAM wParam;
    LPARAM lParam;
    DWORD time;
    POINT pt;
};


消息ID是消息的類型標(biāo)識符,由系統(tǒng)或應(yīng)用程序定義,,消息ID為消息劃分了類型,。同時,也可以看出消息是對應(yīng)于特定的窗口(窗口句柄)的,。

消息是如何分類的,?其前綴都代表什么含義?

消息ID只是一個整數(shù),,Windows系統(tǒng)預(yù)定義了很多消息ID,,以不同的前綴來劃分,比如WM_*,,CB_*等等,。
具體見下表:

Prefix Message category
ABM Application desktop toolbar
BM Button control
CB Combo box control
CBEM Extended combo box control
CDM Common dialog box
DBT Device
DL Drag list box
DM Default push button control
DTM Date and time picker control
EM Edit control
HDM Header control
HKM Hot key control
IPM IP address control
LB List box control
LVM List view control
MCM Month calendar control
PBM Progress bar
PGM Pager control
PSM Property sheet
RB Rebar control
SB Status bar window
SBM Scroll bar control
STM Static control
TB Toolbar
TBM Trackbar
TCM Tab control
TTM Tooltip control
TVM Tree-view control
UDM Up-down control
WM General window


應(yīng)用程序可以定義自己的消息,,其取值范圍必須大于WM_USER。

如何通過消息傳遞任何參數(shù),?

Windows系統(tǒng)的消息機(jī)制都包含2個長整型的參數(shù):WPARAM, LPARAM,,可以存放指針,也就是說可以指向任何內(nèi)容了,。
傳遞的內(nèi)容因消息各異,,消息處理函數(shù)會根據(jù)消息的類型進(jìn)行特別的處理,它知道傳遞的參數(shù)是什么含義,。

消息在線程內(nèi)傳遞時,,由于在同一個地址空間中,指針的值是有效的,。但是夸線程的情況就不能直接使用指針了,,所以Windows系統(tǒng)提供了 WM_SETTEXT, WM_GETTEXT, WM_COPYDATA等消息,用來特殊處理,,指針的內(nèi)容會被放到一個臨時的內(nèi)存映射文件(Memory-mapped File)里面,,通過它實(shí)現(xiàn)線程間的共享數(shù)據(jù)。


消息隊(duì)列和線程的關(guān)系是什么,?消息隊(duì)列的結(jié)構(gòu)是什么樣子的,?

Windows系統(tǒng)本身會維護(hù)一個唯一的消息隊(duì)列,以便于發(fā)送給各個線程,,這是系統(tǒng)內(nèi)部的實(shí)現(xiàn)方式,。
而對于線程來說,每個線程可以擁有自己的消息隊(duì)列,,它和線程一一對應(yīng),。在線程剛創(chuàng)建時,消息隊(duì)列并不會被創(chuàng)建,,而是當(dāng)GDI的函數(shù)調(diào)用發(fā)生時,,Windows系統(tǒng)才認(rèn)為有必要為線程創(chuàng)建消息隊(duì)列。
消息隊(duì)列包含在一個叫THREADINFO的結(jié)構(gòu)中,,有四個隊(duì)列:

Sent Message Queue
Posted Message Queue
Visualized Input Queue
Reply Message Queue

之所以維護(hù)多個隊(duì)列,,是因?yàn)椴煌⒌奶幚矸绞胶吞幚眄樞蚴遣煌摹?/P>

線程和窗口是一一對應(yīng)的嗎?如果想要有兩個不同的窗口對消息作出不同反應(yīng),,但是他們屬于同一個線程,,可能嗎?

窗口由線程創(chuàng)建,,一個線程可以創(chuàng)建多個窗口,。窗口可由CreateWindow()函數(shù)創(chuàng)建,但前提是需要提供一個已注冊的窗口類(Window Class),,每一個窗口類在注冊時需要指定一個窗口處理函數(shù)(Window Procedure),,這個函數(shù)是一個回調(diào)函數(shù),,就是用來處理消息的。而由一個線程來創(chuàng)建對應(yīng)于不同的窗口類的窗口是可以的,。
由此可見,,只要注冊多個窗口類,每個窗口都可以擁有自己的消息處理函數(shù),,而同時,,他們屬于同一個線程。


如何發(fā)送消息,?

消息的發(fā)送終歸通過函數(shù)調(diào)用,,比較常用的有PostMessage(),SendMessage(),,另外還有一些Post*或Send*的函數(shù),。函數(shù)的調(diào)用者即發(fā)送消息的人。
這二者有什么不同呢,?SendMessage()要求接收者立即處理消息,,等處理完畢后才返回。而PostMessage()將消息發(fā)送到接收者隊(duì)列中以后,,立即返回,,調(diào)用者不知道消息的處理情況。

他們的的原型如下:

LRESULT SendMessage(
    HWND hwnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam);
LRESULT PostMessage(
    HWND hwnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam);
SendMessage()要求立即處理,,所以它會直接調(diào)用窗口的消息處理函數(shù)(Window Procedure),,完成后返回處理結(jié)果。
但這僅限于線程內(nèi)的情況,,夸線程時它調(diào)不到處理函數(shù),只能把消息發(fā)送到接收線程的隊(duì)列Sent Message Queue里,。如果接收線程正在處理別的消息,,那么它不會被打斷,直到它主動去獲取隊(duì)列里的下一條消息時,,它會拿到這一條消息,,并開始處理,完成后他會通知發(fā)送線程結(jié)果(猜測是通過ReplyMessage()函數(shù)),。
在接收線程處理的過程中,,發(fā)送線程會掛起等待SendMessage()返回。但是如果這時有其他線程發(fā)消息給這個發(fā)送線程,,它可以響應(yīng),,但僅限于非隊(duì)列型(Non-queued)消息。

這種機(jī)制可能引起死鎖,,所以有其他函數(shù)比如SendMessageTimeout(),, SendMessageCallback()等函數(shù)來避免這種情況,。

PostMessage()并不需要同步,所以比較簡單,,它只是負(fù)責(zé)把消息發(fā)送到隊(duì)列里面,,然后馬上返回發(fā)送者,之后消息的處理則再受控制,。

消息可以不進(jìn)隊(duì)列嗎,?什么消息不進(jìn)隊(duì)列?

可以,。實(shí)際上MSDN把消息分為隊(duì)列型(Queued Message)和非隊(duì)列型(Non-queued Message),,這只是不同的路由方式,但最終都會由消息處理函數(shù)來處理,。
隊(duì)列型消息包括硬件的輸入(WM_KEY*等),、WM_TIMER消息、WM_PAINT消息等,;非隊(duì)列型的一些例子有WM_SETFOCUS, WM_ACTIVE, WM_SETCURSOR等,,它們被直接發(fā)送給處理函數(shù)。

其實(shí),,按照MSDN的說法和消息的路由過程可以理解為,,Posted Message Queue里的消息是真正的隊(duì)列型消息,而通過SendMessage()發(fā)送到消息,,即使它進(jìn)入了Sent Message Queue,,由于SendMessage要求的同步處理,這些消息也應(yīng)該算非隊(duì)列型消息,。也許,,Windows系統(tǒng)會特殊處理,使消息強(qiáng)行繞過隊(duì)列,。


誰來發(fā)送消息,?硬件輸入是如何被響應(yīng)的?

消息可以由Windows系統(tǒng)發(fā)送,,也可以由應(yīng)用程序本身,;可以向線程內(nèi)發(fā)送,也可以夸線程,。主要是看發(fā)送函數(shù)的調(diào)用者,。

對于硬件消息,Windows系統(tǒng)啟動時會運(yùn)行一個叫Raw Input Thread的線程,,簡稱RIT,。這個線程負(fù)責(zé)處理System Hardware Input Queue(SHIQ)里面的消息,這些消息由硬件驅(qū)動發(fā)送。RIT負(fù)責(zé)把SHIQ里的消息分發(fā)到線程的消息隊(duì)列里面,,那RIT是如何知道該發(fā)給誰呢,?如果是鼠標(biāo)事件,那就看鼠標(biāo)指針?biāo)傅拇翱趯儆谀膫€線程,,如果是鍵盤那就看哪個窗口當(dāng)前是激活的,。一些特殊的按鍵會有所不同,比如 Alt+Tab,,Ctrl+Alt+Del等,,RIT能保證他們不受當(dāng)前線程的影響而死鎖。RIT只能同時和一個線程關(guān)聯(lián)起來,。
有可能,,Windows系統(tǒng)還維護(hù)了除SHIQ外地其他隊(duì)列,分發(fā)給線程的隊(duì)列,,或者直接發(fā)給窗口的處理函數(shù),。


消息循環(huán)是什么樣子?線程何時掛起,?何時醒來,?

想象一個通常的Windows應(yīng)用程序啟動后,會顯示一個窗口,,它在等待用戶的操作,,并作出反應(yīng)。
它其實(shí)是在一個不斷等待消息的循環(huán)中,,這個循環(huán)會不斷去獲取消息并作出處理,,當(dāng)沒有消息的時候線程會掛起進(jìn)入等待狀態(tài)。這就是通常所說的消息循環(huán),。

一個典型的消息循環(huán)如下所示(注意這里沒有處理GetMessage出錯的情況):

while(GetMessage(&msg, NULL, 0, 0 ) != FALSE)
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);  
}
這里GetMessage()從隊(duì)列里取出一條消息,,經(jīng)過TranslateMessage(),主要是將虛擬按鍵消息(WM_KEYDOWN等)翻譯成字符消息(WM_CHAR等),。
DispatchMessage()將調(diào)用消息處理函數(shù),。這里有一個靈活性,消息從隊(duì)列拿出之后,,也可以不分發(fā),進(jìn)行一些別的特殊操作,。

下面在看看GetMessage()的細(xì)節(jié):

BOOL GetMessage(     
    LPMSG lpMsg,
    HWND hWnd,
    UINT wMsgFilterMin,
    UINT wMsgFilterMax
);
GetMessage()會從隊(duì)列中取出消息,,填到MSG結(jié)構(gòu)中通過參數(shù)返回。如果此時的消息是WM_QUIT,,也就標(biāo)識線程需要結(jié)束,,則 GetMessage()返回FALSE,那么while循環(huán)會終止。返回TRUE表示取到其他消息,,可以繼續(xù)循環(huán)并運(yùn)行里面的內(nèi)容,。如果返回-1表示 GetMessage()出錯。

其他幾個參數(shù)是用來過濾消息的,,可以指定接收消息的窗口,,以及確定消息的類型范圍。

這里還需要提到一個概念是線程的Wake Flag,,這是一個整型值,,保存在THREADINFO里面和4個消息隊(duì)列平級的位置。它的每一位(bit)代表一個開關(guān),,比如QS_QUIT, QS_SENDMESSAGE等等,,這些開關(guān)根據(jù)不同的情況會被打開或關(guān)閉。GetMessage()在處理的時候會依賴這些開關(guān),。

GetMessage()的處理流程如下:

1. 處理Sent Message Queue里的消息,,這些消息主要是由其他線程的SendMessage()發(fā)送,因?yàn)樗麄儾荒苤苯诱{(diào)用本線程的處理函數(shù),,而本線程調(diào)用 SendMessage()時會直接調(diào)用處理函數(shù),。一旦調(diào)用GetMessage(),所有的Sent Message都會被處理掉,,并且GetMessage()不會返回,;

2. 處理Posted Message Queue里的消息,這里拿到一個消息后,,GetMessage()將它拷貝到MSG結(jié)構(gòu)中并返回TRUE,。注意有三個消息WM_QUIT, WM_PAINT, WM_TIMER會被特殊處理,他們總是放在隊(duì)列的最后面,,直到?jīng)]有其他消息的時候才被處理,,連續(xù)的WM_PAINT消息甚至?xí)缓喜⒊梢粋€以提高效率。從后面討論的這三個消息的發(fā)送方式可以看出,,使用Send或Post消息到隊(duì)列里情況不多,。

3. 處理QS_QUIT開關(guān),這個開關(guān)由PostQuitMessage()函數(shù)設(shè)置,,表示線程需要結(jié)束,。這里為什么不用Send或Post一個 WM_QUIT消息呢?據(jù)稱:一個原因是處理內(nèi)存緊缺的特殊情況,,在這種情況下Send和Post很可能失?。黄浯问强梢员WC線程結(jié)束之前,,所有Sent 和Posted消息都得到了處理,,這是因?yàn)橐WC程序運(yùn)行的正確性,,或者數(shù)據(jù)丟失?不得而知,。
如果QS_QUIT打開,,GetMessage()會填充一個WM_QUIT消息并返回FALSE。

4. 處理Virtualized Input Queue里的消息,,主要包括硬件輸入和系統(tǒng)內(nèi)部消息,,并返回TRUE;

5. 再次處理Sent Message Queue,,來自MSDN卻沒有解釋,。難道在檢查2、3,、4步驟的時候可能出現(xiàn)新的Sent Message,?或者是要保證推后處理后面兩個消息;

6. 處理QS_PAINT開關(guān),,這個開關(guān)只和線程擁有的窗口的有效性(Validated)有關(guān),,不受WM_PAINT的影響,當(dāng)窗口無效需要重畫的時候這個開關(guān)就會打開,。當(dāng)QS_PAINT打開的時候,,GetMessage()會返回一個WM_PAINT消息。處理QS_PAINT放在后面,,因?yàn)橹乩L一般比較慢,,這樣有助于提高效率;

7. 處理QS_TIMER開關(guān),,和QS_PAINT類似,,返回WM_TIMER消息,之所以它放在QS_PAINT之后是因?yàn)槠鋬?yōu)先級更低,,如果Timer消息要求重繪但優(yōu)先級又比Paint高,,那么Paint就沒有機(jī)會運(yùn)行了。

如果GetMessage()中任何消息可處理,,GetMessage()不會返回,,而是將線程掛起,也就不會占用CPU時間了,。
類似的WaitMessage()函數(shù)也是這個作用,。

還有一個PeekMessage(),其原型為:

BOOL PeekMessage(     
    LPMSG lpMsg,
    HWND hWnd,
    UINT wMsgFilterMin,
    UINT wMsgFilterMax,
    UINT wRemoveMsg
);
它的處理方式和GetMessage()一樣,,只是多了一個參數(shù)wRemoveMsg,,可以指定是否移除隊(duì)列里的消息。最大的不同應(yīng)該是,,當(dāng)沒有消息可處理時,PeekMessage()不是掛起等待消息的到來,而是立即返回FALSE,。

WM_DESTROY, WM_QUIT, WM_CLOSE消息有什么不同,?

而其他兩個消息是關(guān)于窗口的,WM_CLOSE會首先發(fā)送,,一般情況程序接到該消息后可以有機(jī)會詢問用戶是否確認(rèn)關(guān)閉窗口,,如果用戶確認(rèn)后才調(diào)用 DestroyWindow()銷毀窗口,此時會發(fā)送WM_DESTROY消息,,這時窗口已經(jīng)不顯示了,,在處理WM_DESTROY消息是可以發(fā)送 PostQuitMessage()來設(shè)置QS_QUIT開關(guān),WM_QUIT消息會由GetMessage()函數(shù)返回,,不過此時線程的消息循環(huán)可能也即將結(jié)束,。

窗口內(nèi)的消息的路由是怎樣的?窗口和其控件的關(guān)系是什么,?

一個窗口(Window)可以有一個Parent屬性,,對一個Parent窗口來說,屬于它的窗口被稱為子窗口(Child Window),??丶–ontrol)或?qū)υ捒颍―ialog)也是窗口,他們一般屬于某個父窗口,。
所有的窗口都有自己的句柄(HWND),,消息被發(fā)送時,這個句柄就已經(jīng)被指定了,。所以當(dāng)子窗口收到一個消息時,,其父窗口不會也收到這個消息,除非子窗口手動的轉(zhuǎn)發(fā),。
關(guān)于更詳細(xì)的窗口和控件,,會在另一篇中討論。


誰來處理消息,?消息處理函數(shù)能發(fā)送消息么,?

由消息處理函數(shù)(Window Procedure)來處理。消息處理函數(shù)是一個回調(diào)函數(shù),,其地址在注冊窗口類的時候注冊,,只有在線程內(nèi)才能調(diào)用。

其原型為:

typedef LRESULT (CALLBACK* WNDPROC)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

處理函數(shù)內(nèi)部一般是一個switch-case結(jié)構(gòu),,來針對不同的消息類型進(jìn)行處理,。Windows系統(tǒng)還為所有窗口預(yù)定義了一個默認(rèn)的處理函數(shù) DefWindowProc(),它提供了最基本的消息處理,,一般在不需要特殊處理的時候(即在switch的default分支)會調(diào)用這個函數(shù),。
由同一個窗口類創(chuàng)建的一組窗口共享一個消息處理函數(shù),,所以在編寫處理函數(shù)的時候要小心處理窗口實(shí)例的局部變量。

處理函數(shù)里可以發(fā)送消息,,但是可以想象有可能出現(xiàn)循環(huán),。另外處理函數(shù)還常常被遞歸調(diào)用,所以要減少局部變量的使用,,以避免遞歸過深是棧溢出,。

最后關(guān)于處理函數(shù)特化的問題將在另外的文章討論。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多