SDK 編寫 Windows 程序需完成 3 個框架模塊:
<1> 填空賦值 WNDCLASS 定義窗口類并注冊;
<2> 創建窗口,顯示、刷新窗口;
<3> 消息循環,編寫消息處理函數。
熟悉框架后,我們的重點工作放在感興趣的消息處理上。
有以上分析可分模塊完成注冊窗口類和創建窗口工作,即分別設計功能函數 InitWindowsClass 和 InitWindows ,改造后的完整程序如下,從以下程序可以更清楚的認識 windows 程序框架。
Windows 窗口應用程序與 DOS 控制臺應用程序是不同的,編寫一個基于 Windows API 的典型的應用程序需要編寫處理以下四個任務:
l 初始化
l 實例化
l 啟動消息循環
l 響應消息
前面三個任務總是發生在 WinMain 函數中, WinMain 是每一個 Windows 程序的入口點,相當 于 C/C++ 中的 main 。第四個任務是傳統上稱為 WndProc 的函數來承擔的。
1 .初始化(注冊窗口類,創建窗口)
要定義一個窗口類 struct WNDCLASS WndClass ; 該數據結構實際存儲的是一個窗口的屬性集。 調用 ATOM RegisterClass ( CONST WNDCLASSW * lpWndClass ); 函數注冊 lpWndClass 指向的窗口類模板。窗口類是一個內核對象,不同的窗口類以名稱( WNDCLASS 的 lpszClassName 字段)區分。
例如后面 MFC 中的 AfxRegisterClass 函數中,注冊窗口類前先調用 GetClassInfo 函數檢測預注冊的窗口類是否已注冊。 GetClassInfo 函數用來獲取指定名稱的窗口類信息,其原型如下:
BOOL GetClassInfo ( HINSTANCE hInstance , LPCTSTR lpClassName , LPWNDCLASSW lpWndClass );
以下為 AfxRegisterClass 函數代碼片段:
// WINCORE.CPP
BOOL AFXAPI AfxRegisterClass ( WNDCLASS * lpWndClass )
{
WNDCLASS wndcls ;
if ( GetClassInfo ( lpWndClass -> hInstance , lpWndClass -> lpszClassName , & wndcls ))
{
// class already registered
return TRUE ;
}
if (!:: RegisterClass ( lpWndClass ))
{
TRACE1 ("Can't register window class named %s/n",
lpWndClass -> lpszClassName );
return FALSE ;
}
//……
}
對于一個已注冊的窗口類,可調用 BOOL UnregisterClass ( LPCTSTR lpClassName , HINSTANCE hInstance ); 函數進行注銷。
以下為 Win32SDK 示例程序中的注冊窗口類、創建窗口的代碼片段:
lpszProviderClass = __TEXT( " MyWndClass ") ;
WndClass . lpszClassName = lpszProviderClass ;
然后給定義窗口風格以及用于該窗口類的消息處理函數。然后 CreateWindow ( lpszProviderClass ,……)將使用名稱為 lpszProviderClass 的窗口類 WndClass 作為模版創建類的實例。
在調用 CreateWindow(Ex) 時,第一個參數也可以直接使用系統預定義( Predefined )的子窗口控件類,例如 "BUTTON" , "EDIT" , "COMBOBOX" 等這些是系統內部已注冊的 WNDCLASS ,免去調用 RegisterClass 。
創建窗口后,調用 GetClassName 函數獲取指定窗口 hWnd 所使用的窗口類名稱。其函數原型如下:
int GetClassName ( HWND hWnd , LPTSTR lpClassName , int nMaxCount );
2 .實例化(顯示窗口,刷新窗口)
一旦窗口被創建,還需要完成若干步才能運行你的程序。首先,當一個窗口被創建時,它通常是不可見的,一般要調用 BOOL ShowWindow ( HWND hWnd , int nCmdShow ); 函數 來顯示窗口,調用 BOOL UpdateWindow ( HWND hWnd ); 函數來刷新窗口。
函數 UpdateWindow 向窗口 hWnd 發送第一個 WM_PAINT 消息以更新它的客戶區。當 ShowWindow 使窗口顯示在屏幕上時,窗口的客戶區會被 WndClass . hbrBackground 擦除,調用 UpdateWindow 函數將促使客戶區重繪,以顯示其內容。
3 .消息循環
( 1 )消息的產生
無論用戶移動鼠標或按下某個鍵, Windows 系統將創建一個消息(對應數據結構為 MSG )并將之投放到對應窗口 (MSG.hwnd 指標 ) 所在線程的消息隊列中。
( 2 )獲取消息
應用程序調用 GetMessage 函數從調用線程消息隊列中取出 (pop) 消息。
GetMessage 函數原型如下:
BOOL GetMessage ( LPMSG lpMsg, HWND hWnd , UINT wMsgFilterMin, UINT wMsgFilterMax);
取出的消息存放至參數一 lpMsg 所指內存(本地聲明的 MSG 結構體變量)。
參數二 hWnd 標識要檢查消息的窗口,如果為 NULL ,則獲取屬于調用線程消息隊列中任一(窗口)消息;如果不為 NULL ,則只獲取指定窗口的消息。
wMsgFilterMin 和 wMsgFilterMax 指定獲取消息碼的范圍。一般將兩參數都置零,表示無消息 ID 范圍限制。
注意該函數是阻塞的,直到有一個( hWnd 的)消息到來,它才返回。如果消息為 WM_QUIT ,則該函數返回 FALSE ,結束消息循環;否則返回 TRUE ,繼續下一輪消息循環。
<1> 等待消息
函數 BOOL WaitMessage ( VOID ); 使調用線程掛起,直到一個新的消息放到調用線程消息隊列中才返回。
<2> 取出消息
函數 PeekMessage 查看調用線程消息隊列中(指定窗口 hWnd )是否有消息。如果有消息則取出放入 lpMsg 所指 MSG 結構體中,返回 TRUE ;如果無(指定窗口 hWnd 的)消息則立即退出(即非阻塞),返回 FALSE 。其函數原型如下:
BOOL PeekMessage ( LPMSG lpMsg , HWND hWnd , UINT wMsgFilterMin , UINT wMsgFilterMax , UINT wRemoveMsg );
前四個參數同 GetMessage , wRemoveMsg 參數指定當有消息讀取消息后,是否從消息隊列中移除該消息。其為下列值之一:
PM_NOREMOVE: 讀取后消息依然留在隊列中;
PM_REMOVE: 讀取后消息從隊列中移除;
<3> GetMessage = WaitMessage + PeekMessage ( PM_REMOVE ) 。
( 3 )翻譯消息
如果輸入列表中有一個屬于你的應用程序的消息,并且是按鍵消息時, TranslateMessage 將虛擬鍵消息轉換為字符消息( WM_KEYDOWN + WM_KEYUP = WM_CHAR 或 WM_SYSKEYDOWN + WM_SYSKEYUP = WM_SYSCHAR ),再將字符消息( WM_CHAR 或 WM_SYSCHAR )投遞到調用線程的消息隊列中。
TranslateMessage 函數內部機制大致如下:
BOOL TranslateMessage ( CONST MSG * lpMsg )
{
if ( lpMsg -> message == WM_KEYDOWN || lpMsg -> message == WM_SYSKEYDOWN )
{
UINT message ;
if ( lpMsg -> message == WM_KEYDOWN )
{
message = WM_CHAR ;
}
else if ( lpMsg -> message == WM_SYSKEYDOWN )
{
message = WM_SYSCHAR ;
}
PostMessage ( lpMsg -> hwnd , message , lpMsg -> wParam , lpMsg -> lParam );
return TRUE ; // 消息被轉換
}
return FALSE ;
}
( 4 )路由消息
取出消息后,需要進行處理。應用程序調用 DispatchMessage 函數 將取得的消息發送到窗口消息處理函數 WndProc 。 WndProc 采用 switch-case 分支結構對不同的消息不同的響應處理。
WndProc 為 WNDPROC 函數指針,其類型如下:
typedef LRESULT ( CALLBACK * WNDPROC )( HWND , UINT , WPARAM , LPARAM );
DispatchMessage 函數內部機制大致如下:
LRESULT DispatchMessage ( CONST MSG * lpMsg )
{
// 根據 lpMsg->hwnd 查找用于創建該窗口的 WNDCLASS ( CreateWindow 的 lpClassName 指定)的 lpfnWndProc;
CallWindowProc ( lpfnWndProc , lpMsg -> hwnd , lpMsg -> message , lpMsg -> wParam , lpMsg -> lParam );
}
4 .消息處理
Windows 編程中常見的消息有:窗口創建消息 WM_CREATE, 窗口繪制消息 WM_PAINT ,按鍵消息 WM_KEYDOWN, WM_CHAR, 窗口關閉消息 WM_CLOSE ,窗口銷毀消息 WM_DESTROY ,退出應用程序消息 WM_QUIT 等。
默認情況下,關閉窗口的處理流程如下:
點擊關閉按鈕 à 系統向指定窗口發送 WM_CLOSE 消息 à WM_CLOSE 消息響應中調用 DestroyWindow 向窗口發送 WM_DESTROY 消息 à WM_DESTROY 消息響應中調用 PostQuitMessage 向窗口發送 WM_QUIT 消息結束窗口所屬線程的消息循環( GetMessage 函數返回), 終止窗口所屬線程。
實際應用軟件在響應關閉消息時通常將窗口最小化(到托盤),并不真正關閉。應用程序將在沒有窗口的條件下繼續運行。
參考 : 窗口破 壞 過程與Windows 消息循環 、 WM_CLOSE 、WM_DESTROY 和WM_QUIT 三者的區別 。
每個 Windows 程序(進程)都至少包含一個窗口和一個應用程序的實例,在程序中表現為窗口句柄 hWnd 和實例句柄 hInstance. 我們可以透過以下幾條程序語句一窺 Windows 程序運行機制:
WndClass.hInstance= hInstance ; // 這一賦值表明要注冊的窗口類屬于當前實例
ShowWindow( hWnd ,nCmdShow); UpdateWindow( hWnd ); // 顯示刷新該程序窗口
GetMessage // 提取消息隊列中的消息
TranslateMessage(& Msg ); // 翻譯消息,該函數負責將消息的虛擬鍵轉換成字符消息
DispatchMessage(& Msg ); // 將參數 lpMSG 標識的消息發送給窗口函數 WndProc
switch( nMessage ) case: // 按接受到的消息值 nMessage 判斷是哪一種消息并處理
以下為 Windows 應用程序執行流程圖:
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
