一
引言
隨著Windows操作系統的不斷推廣
眾多軟件開發包都提供有開發基於Windows平台應用軟件的功能
雖然這些開發包不盡相同
流行的有Visual C++
Visual Basic
Delphi
C++ Builder 等多種
但由這些不同語言開發的軟件有一點卻是相同的
都是運行於Windows 操作平台
都必須接受Windows 的運行機制
作為Windows 操作系統靈魂的消息機制也就必然為眾多用不同語言開發的Windows操作系統下運行的應用程序所接受
因此
要編寫深入的Windows程序
就必須對Windows的運行機制有很好的認識和理解
本文下面將對Windows操作系統下的消息運行機制做較為深入的剖析
二
Windows事件驅動機制
我們當中不少使用VC
Delphi等作為開發語言的程序員是一步步從DOS下的Basic
C++中走過來的
而且大多在剛開始學習編程時也是先從DOS下的編程環境入手的
因此在習慣了DOS下的過程驅動形式的順序程序設計方法後
往往在向Windows下的開發環境轉型的過程中會對Windows所采取的事件驅動方式感到無法適應
因為DOS和Windows這兩種操作系統的運行機制是截然不同的
DOS下的任何程序都是使用順序的
過程驅動的程序設計方法
這種程序都有一個明顯的開始
明顯的過程以及一個明顯的結束
因此通過程序就能直接控制程序事件或過程的全部順序
即使是在處理異常時
處理過程也仍然是順序的
過程驅動的結構
而Windows的驅動方式則是事件驅動的
即程序的流程不是由事件的順序來控制
而是由事件的發生來控制
所有的事件是無序的
所為一個程序員
在編寫程序時
並不知道用戶會先按下哪個按紐
也就不知道程序先觸發哪個消息
因此我們的主要任務就是對正在開發的應用程序要發出的或要接收的消息進行排序和管理
事件驅動程序設計是密切圍繞消息的產生與處理而展開的
一條消息是關於發生的事件的消息
三
Windows的消息循環
Windows操作系統為每一個正在運行的應用程序保持有一個消息隊列
當有事件發生後
Windows並不是將這個激發事件直接送給應用程序
而是先將其翻譯成一個Windows消息
然後再把這個消息加入到這個應用程序的消息隊列中去
應用程序需要通過消息循環來接收這些消息
在MFC中使用了對WinAPI進行了很好封裝的類庫
雖然可以為編程提供一個面向對象的界面
使Windows程序員能夠以面象對象的方式進行編程
把那些進行SDK編程時最繁瑣的部分提供給程序員
使之專注於功能的實現
但是由於引入了很好的封裝特性
使我們不能直接操縱部分核心代碼
對於消息的循環和接收也只是通過類似於下面的消息映射予以很簡單的表示
BEGIN_MESSAGE_MAP(CTEMMSView
CFormView)
//{{AFX_MSG_MAP(CTEMMSView)
ON_WM_LBUTTONDOWN()
ON_COMMAND(ID_OPENDATA
OnOpenData)
ON_WM_TIMER()
ON_WM_PAINT()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
雖然上述消息映射在編程過程中處理消息非常簡練方便
但顯然是難於理解消息是如何參與循環和分發的
因此有必要通過SDK(Software Developers Kit
軟件開發工具箱)代碼深入到被MFC封裝的Windows編程的核心中來研究其具體是如何工作的
在SDK編程中
一般是在Windows應用程序的入口點WinMain函數中添加處理消息循環的代碼以檢索Windows送來的消息
然後WinMain再把這些消息分配給相應的窗口函數並處理它們
……
MSG msg; //定義消息名
while (GetMessage (&msg
NULL
))
{
TranslateMessage (&msg) ; //翻譯消息
DispatchMessage (&msg) ; //撤去消息
}
return msg
wParam ;
上述幾句雖然簡單但卻是所有Windows程序的關鍵代碼
擔負著獲取
解釋和分發消息的任務
下面就重點對其功能和作用進行分析
MSG結構在頭文件中定義如下
typedef struct tagMSG
{
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG
*PMSG;
其數據成員的具體意義如下
hwnd
消息將要發送到的那個窗口的句柄
用這個參數可以決定讓哪個窗口接收消息
message
消息號
它唯一標識了一種消息類型
每種消息類型都在Windows文件進行了預定義
wParam
一個
位的消息參數
這個值的確切意義取決於消息本身
lParam
同上
time
消息放入消息隊列中的時間
在這個域中寫入的並非當時日期
而是從Windows啟動後所測量的時間值
Windows用
這個域來使用消息保持正確的順序
pt
消息放入消息隊列時的鼠標坐標
消息循環以GetMessage調用開始
它從消息隊列中取出一個消息
該函數的四個參數可以有控制地獲取消息
第一個參數指定要接收消息的MSG結構的地址
第二個參數表示窗口句柄
一般將其設置為空
表示要獲取該應用程序創建的所有窗口的消息
第三
四參數用於指定消息范圍
後面三個參數被設置為默認值
用於接收發送到屬於這個應用程序的任何一個窗口的所有消息
在接收到除WM_QUIT之外的任何一個消息後
GetMessage()返回TRUE
如果GetMessage收到一個WM_QUIT消息
則返回FALSE以退出消息循環
終止程序運行
因此
在接收到WM_QUIT之前
帶有GetMessage()的消息循環可以一直循環下去
當除WM_QUIT的消息用GetMessage讀入後
首先要經過函數TranslateMessage()對其進行解釋
但對大多數消息來說並不起什麼作用
這裡起關鍵作用的是DispatchMessage()函數
把由GetMessage獲取的Windows消息傳送給在MSG結構中為窗口所指定的窗口過程
在消息處理函數處理完消息之後
代碼又循環到開始去接收另一個消息
這樣就完成了一個完整的消息循環
由於Windows操作系統是一種非剝奪式多任務操作系統
只有在應用程序主動交出CPU控制權後
Windows才能把控制權交給其他應用程序
在消息循環中
一定要有能交出控制的系統函數才能實現協同式多任務操作
能完成該功能的只有GetMessage
PeekMessage和WaitMessage這三個函數
如果在應用程序中長期不去調用這三個函數之一其他任務則無法執行
GetMessage函數在找不到等待應用程序處理的消息時
會自動交出控制權
由Windows把CPU的控制權交給其他等待獲取控制權的應用程序
由於任何Windows應用程序都含有一個消息循環
這種隱式交出控制權的方式可以保證合並各個應用程序共享控制權
一旦發往該應用程序的消息到達應用程序隊列
即開始執行GetMessage語句的下一條語句
使用GetMessage函數的消息循環在消息隊列中沒有消息時將等待
如果需要
可以利用這段時間進行I/O端口操作等耗時操作
不過需要在消息循環中使用PeekMessage函數來代替GetMessage
使用PeekMessage的方法同GetMessage類似
下面是一段使用PeekMessage函數的消息循環的典型例子
MSG msg;
BOOL bDone=FALSE;
do{
if(PeekMessage(&msg
NULL
PM_REMOVE)){
if(ssage==WM_QUIT)
bDone=TRUE;
else{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
//無消息處理
進行長時間操作
else{
……//長時間操作
}
}while(!bDone)
……
無論應用程序消息隊列中是否有消息
PeekMessage函數都立即返回
如果希望等待新消息入隊
可以利用無返回值的函數WaitMessage配合PeekMessage進行消息循環
四
對Windowds消息的處理
窗口過程處理消息通常以switch語句開始
對於它要處理的每一條消息ID都跟有一條case語句
這在功能上同MFC的消息映射有些類似
switch(uMsgId)
{
case WM_TIMER
//對WM_TIMER定時器消息的處理過程
return
;
case WM_LBUTTONDOWN
//對WM_ LBUTTONDOWN鼠標左鍵單擊消息的處理過程
ruturn
;
……
default:
//其他消息由這個默認處理函數來處理
return DefWindowProc(hwnd
uMsgId
wParam
lParam);
}
在處理完消息後必須返回
這很重要
否則Windows將要不停地重試下去
對於那些在程序中不准備處理的消息
窗口過程會把它們都扔給DefWindowProc進行缺省處理
而且還要返回那個函數的返回值
在消息傳遞層次中
可以認為DefWindowProc函數是最頂層的函數
該函數發出WM_SYSCOMMAND消息
由系統執行Windows環境中多數窗口所公用的各種通用操作
如更新窗口的正文標題等等
在MFC下可以用下述部分代碼實現與上述SDK代碼相同的功能
BEGIN_MESSAGE_MAP(CTEMMSView
CFormView)
//{{AFX_MSG_MAP(CTEMMSView)
ON_WM_LBUTTONDOWN()
ON_WM_TIMER()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
小結
Windows環境提供有非常豐富的系統資源
在這個基礎上可以編制出能滿足各種各樣目標功能的應用系統
要深入Windows編程就必須首先對Windows系統的運行機理有很好的認識
本文僅針對Windows的一種重要運行機制
消息機制作了較深入的剖析和闡述
對培養在Windows下的編程思想有一定的幫助
對某些相關問題的詳細論述可以參考MSDN在線幫助的
SDK Reference
部分
From:http://tw.wingwit.com/Article/os/xtgl/201311/8832.html