Windows應用程序的基本結構.ppt
《Windows應用程序的基本結構.ppt》由會員分享,可在線閱讀,更多相關《Windows應用程序的基本結構.ppt(135頁珍藏版)》請在裝配圖網(wǎng)上搜索。
Windows應用程序框架結構,哈爾濱工程大學,概述,理解Window編程所使用的事件驅動模型 Window編程的基本框架,Windows平臺下可視化開發(fā)工具,可視化開發(fā)系統(tǒng)集成了一系列系統(tǒng)可用資源和開發(fā)工具 1、程序調試工具包括源程序語法檢查、可執(zhí)行程序修改和運行監(jiān)視等 2、源程序編輯器和編譯器 3、資源管理器,包括圖形化窗口及組成元素的多種對象的編輯器 4、系統(tǒng)函數(shù)庫和系統(tǒng)函數(shù)開發(fā)工具 5、可選擇并構成具體語句或源程序結構的例程庫及Help,Windows程序的特點,大致說來windows編程有兩種方法: a.windows c方式(SDK),SDK編程就是直接調用windows的API進行編程; b.c++方式:即對SDK函數(shù)進行包裝,如VC的MFC,BCB的OWL等。MFC把這些API封閉起來,共有一百多個類組成. API,全稱application program interface,意思是應用程序編程接口(說起API并不僅僅指windows而言, windows支持的API叫winapi)。winapi就是應用程序和windows之間通訊的一個編程界面。windows提供了上千個API函數(shù),以方便程序員來編寫應用程序。,Windows程序的特點,WinSDK程序設計就是API方式的windows程序設計。SDK,全稱Software Developers Kit,意思是軟件開發(fā)工具箱。 MFC,全稱Microsoft Foundation Classes,偽軟把WinAPI進行封裝的類庫。它是一個類的集合,通過覆蓋WinAPI,為編程提供了一個面向對象的界面。它使windows程序員能夠利用C++面象對象的特性進行編程,類似BCB的OWL,Delphi的VCL組件。它把那些進行SDK編程時最繁瑣的部分提供給程序員,使之專注于功能的實現(xiàn)。你不妨把它想象成類似TC提供的函數(shù)庫吧。,SDK編程,利用Windows API函數(shù)編寫Windows應用程序必須首先了解以下內容: (1)窗口的概念 (2)事件驅動的概念 (3)句柄 (4)消息,Windows的事件驅動機制,Dos的過程驅動與Windows的事件驅動 在講Window消息循環(huán)之前,我想先談一下Dos與Windows驅動機制的區(qū)別: DOS程序主要使用順序的,過程驅動的程序設計方法。順序的,過程驅動的程序有一個明顯的開始,明顯的過程及一個明顯的結束,因此程序能直接控制程序事件或過程的順序。 而Windows的驅動方式是事件驅動,就是不由事件的順序來控制,而是由事件的發(fā)生來控制,所有的事件是無序的,作為一個windows程序員,在你編寫程序時,你并不知道用戶先按哪個按紐,也不知道程序先觸發(fā)哪個消息。你的任務就是對正在開發(fā)的應用程序要發(fā)出或要接收的消息進行排序和管理。,過程驅動方法和事件驅動方法,Dos編程和Windows編程不同,dos下的C編程的main()一樣,windows下的入口是WinMain()函數(shù)。 WinMain()所起的作用:初始化,展示窗口,銷毀應用程序等。 第一個參數(shù):應用程序的當前實例句柄。 第二個參數(shù):應用程序的前一個實例句柄,別管它,對于Win32位而言,它一般是NULL. 第三個參數(shù):指向任何傳給程序的命令行參數(shù)。PSTR代表“指向字符串的指針“。 第四個參數(shù):它告訴應用程序如何初始化窗口,如最大化,最小化等狀態(tài)。,WinMain函數(shù)的功能,三個基本的組成部分:函數(shù)說明、初始化和消息循環(huán),,WinMain函數(shù),,注意!Win是多任務管理的,同一應用程序的多個窗口可能會同時存,Win系統(tǒng)對每個窗口的執(zhí)行稱為一個實例,并用一個實例句柄來唯一標識,Windows常見的數(shù)據(jù)類型,在Windows.h中定義了Windows 應用程序中包含種類繁多的數(shù)據(jù)類型,重要的數(shù)據(jù)結構,兩者句柄定義的不同,句柄(handle): 在標準C庫中句柄用來對文件輸入輸出。 在Windows環(huán)境中,句柄是用來標識項目的,這些項目包括: *.模塊(module) *.任務(task) *.實例(instance) *.文件(file) *.內存塊(block of memory) *.菜單(menu) *.控制(control) *.字體(font) *.資源(resource),包括圖標(icon),光標(cursor),字符串(string) *.GDI對象(GDI object),包括位圖(bitmap),畫刷(brush),元文件(metafile),調色板(palette),畫筆(pen),區(qū)域(region),以及設備描述表(device context)。 WINDOWS程序中并不是用物理地址來標識一個內存塊,文件,任務或動態(tài)裝入模塊的,相反的,WINDOWS API給這些項目分配確定的句柄,并將句柄返回給應用程序,然后通過句柄來進行操作。,句柄是什么?,在Win32里,句柄是指向一個無值型對象(void *)的指針,是一個4字節(jié)長的數(shù)據(jù)”。句柄并不是一個真正意義上的指針。從結構上看,句柄的確是一個指針,盡管它沒有指向用于存儲某個對象的內存位置,而實際上句柄指向的是一個包含了對該對象進行的引用的位置。我們天氣熱搖扇子的時候只要抓住扇柄便可以控制整個扇子的運動了,在程序中也差不多是這個意思。通常一個句柄就可以傳遞我們所要做的事情。有經(jīng)驗的開發(fā)者肯定清楚,編寫程序總是要和各種句柄打交道的,句柄是系統(tǒng)用來標識不同對象類型的工具,如窗口、菜單等,這些東西在系統(tǒng)中被視為不同類型的對象,用不同的句柄將他們區(qū)分開來。,句柄,常用句柄類型及其說明,,應用程序通過 句柄訪問相應 的對象信息,HWND 窗口句柄 HDC 設備環(huán)境句柄 HBITMAP 位圖句柄 HCURSOR 光標句柄 HICON 圖標句柄 HFONT 字體句柄 HMENU 菜單句柄 HPEN 畫筆句柄 HFILE 文件句柄 HBRUSH 畫刷句柄 HINSTANCE 當前實例句柄,Windows程序的特點,窗口句柄: 系統(tǒng)通過窗口句柄來在整個系統(tǒng)中唯一標識一個窗口,發(fā)送一個消息時必須指定一個窗口句柄表明該消息由那個窗口接收。而每個窗口都會有自己的窗口過程,所以用戶的輸入就會被正確的處理。 所有的命名采用了匈牙利表示法。如消息的前綴使用msg.句柄使用h.函數(shù)使用fn等。 Windows程序則至少兩個主程序, 一個是WinMain(), int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance HINSTANCE hPrevInstance, // handle to previous instance LPSTR lpCmdLine, // command line int nCmdShow // show state );,Windows程序的特點,另一個是窗口過程函數(shù)WndProc,它的函數(shù)原型為: long FAR PASCAL WndProc(HWND hWnd,WORD message,WORD wParam,LONG lParam); 窗口函數(shù)與回調函數(shù): 在Windows中,應用程序通過要求Windows完成指定操作,而承擔這項通信任務的API函數(shù)就是Windows的相應窗口函數(shù)WndProc。應用程序不直接調用任何窗口函數(shù),而是等待Windows調用窗口函數(shù),請求完成任務或返回信息。為保證Windows調用這個窗口函數(shù),這個函數(shù)必須先向Windows登記,然后在Windows實施相應操作時回調,所以窗口函數(shù)又稱為回調函數(shù)。WndProc是一個主回調函數(shù),Windows至少有一個回調函數(shù)。典型的回調函數(shù)有窗口過程、對話框過程和鉤子函數(shù)。實際上,也許有不止一個的窗口過程。例如,每一個不同的窗口類都有一個與之相對應的窗口過程。 實例:在Windows中,能多次同時運行同一個應用程序,即運行多個副本,每個副本叫做一個“實例”。,Windows程序的特點,WinMain()函數(shù)的調用約定是PASCAL。 在這里PASCAL是一個調用約定,由于這種方式最早由PASCAL采用,所以這么叫。 在MSDN中的C++ Language Reference中,Calling Conventions這一章都是講調用約定的。 約定:微軟重定義了許多約定類型,為的是可以讓代碼更容易跨平臺或者跨編譯器。 其實,調用約定要解決兩個問題,都是針對堆棧操作: 1。參數(shù)傳遞的順序(本質是壓棧的順序) 2。誰負責平棧(調用者還是調用對象) 一個函數(shù)的聲明、定義和實現(xiàn)中的調用方式一般都一致。 WINAPI標識符的定義是:#define WINAPI __stdcall, __stdcall指Window調用函數(shù)的一種方式,也就是如何在堆中存取函數(shù)參數(shù)的方式。許多Windows Api函數(shù)調用聲明為__stdcall方式。,調用方式,1、_stdcall是Pascal程序的缺省調用方式,通常用于Win32 Api中,函數(shù)采用從右到左的壓棧方式,自己在退出時清空堆棧。VC將函數(shù)編譯后會在函數(shù)名前面加上下劃線前綴,在函數(shù)名后加上“@”和參數(shù)的字節(jié)數(shù)(堆棧要求分配的字節(jié)數(shù) )。 如:_TestMethod@4 2、C調用約定(即用__cdecl關鍵字說明)按從右至左的順序壓參數(shù)入棧,由調用者把參數(shù)彈出棧。另外,在函數(shù)名修飾約定方面也有所不同。 _cdecl是C和C++程序的缺省調用方式。每一個調用它的函數(shù)都包含清空堆棧的代碼,所以產(chǎn)生的可執(zhí)行文件大小會比調用_stdcall函數(shù)的大。函數(shù)采用從右到左的壓棧方式。VC將函數(shù)編譯后會在函數(shù)名前面加上下劃線前綴。是MFC缺省調用約定。如 :_TestMethod,調用方式,CALLBACK和WINAPI函數(shù)是由你設計的函數(shù),但你不能“顯式”的調用它,而是Windows 系統(tǒng)自己調用,例如窗口過程函數(shù),枚舉函數(shù)等,這是由Windows系統(tǒng)的工作機制決定的。 至于這些函數(shù)為什么要是CALLBACK,且看下面詳解: CALLBACK,WINAPI等在windef.h中被如下定義: #define CALLBACK __stdcall #define WINAPI __stdcall #define WINAPIV __cdecl #define APIENTRY WINAPI #define APIPRIVATE __stdcall #define PASCAL __stdcall,調用方式舉例,CALLBACK就是要VC編譯器在編譯時采取非默認(__cdecl)的方式(__stdcall),__stdcall和__cdecl最大的不同就是我前面說的“堆棧指針恢復的責任歸屬”----C編譯器默認(__cdecl)是主函數(shù)來恢復stack指針SP,若指定關鍵字__stdcall,則編譯器生成的碼是由被調函數(shù)自己恢復SP,看看下面的例子:假如有一個函數(shù)聲明如 int __stdcall getMax(int a ,int b); 返回兩者中較大值。在主函數(shù)中被調用,調用時VC造出的碼如下:(其它編譯器可能不同) 0040102B push 38h ;參數(shù)b 0040102D push 60h ;a 0040102F call @ILT+15(_getMax)(0x0040100f) ; call getMax() . . 為 getMax()造出的匯編語句是 00401020 push ebp 00401021 mov ebp,esp . . . .;其它指令 00401026 pop ebp 00401027 ret 8 ;在返回時修改堆棧指針,調用方式舉例,若聲明及定義時無 __stdcal,VC造出的碼如下: 0040102B push 38h 0040102D push 60h 0040102F call @ILT+15(_getMax)(0x0040100f) 00401034 add esp,8 ;esp由主函數(shù)恢復 . . 為 getMax()造出的匯編語句是 00401020 push ebp 00401021 mov ebp,esp . . . .;其它指令 00401026 pop ebp 00401027 ret ;返回 現(xiàn)在你該知CALLBACK 與不加CALLBACK 的函數(shù)在調用以及返回上有什么區(qū)別了吧。,Windows程序的特點,用位的“或”操作(操作符“|”)把若干個常數(shù)組合起來控制消息窗口顯示的按鈕和圖標等。在Windows.h中,以CS_開頭的類樣式(Class Style)標識符被定義為16位的常量,這些常量都只有某1位為1。在VC++開發(fā)環(huán)境中,可以看到CS_VREDRAW=0x0001,CS_HREDRAW=0x0002,CS_DBLCLKS =0x0008,CS_NOCLOSE=0x0200,將這些16進制數(shù)轉換為2進制數(shù),就可以發(fā)現(xiàn)它們都只有1位為1,并且為1的位各不相同。用這種方式定義的標識符稱為“位標志”,我們可以使用位運算操作符來組合使用這些樣式。例如,要讓窗口在水平和垂直尺寸發(fā)生變化時發(fā)生重繪,我們可以使用位或(|)操作符將CS_HREDRAW和CS_VREDRAW組合起來,如style=CS_HREDRAW | CS_VREDRAW。假如有一個變量具有多個樣式,而我們并不清楚該變量都有哪些樣式,現(xiàn)在我們想要去掉該變量具有的某個樣式,那么可以先對該樣式標識符進行取反(~)操作,然后再和這個變量進行與(&)操作即可實現(xiàn)。例如,要去掉先前的style變量所具有的CS_VREDRAW樣式,可以編寫代碼:style=style & ~ CS_VREDRAW。 在Windows程序中,經(jīng)常會用到這種位標志標識符,后面我們在創(chuàng)建窗口時用到的窗口樣式,也是屬于位標志標識符。,Windows程序的特點,在Windows應用程序中,每一個窗口都必須從屬于一個窗口類,窗口類定義了窗口所具有的屬性,如它的樣式、圖標、鼠標指針、菜單名稱及窗口過程名等。 窗口種類是定義窗口屬性的模板,這些屬性包括窗口式樣,鼠標形狀,菜單等等,窗口種類也指定處理該類中所有窗口消息的窗口函數(shù).只有先建立窗口種類,才能根據(jù)窗口種類來創(chuàng)建Windows應用程序的一個或多個窗口.創(chuàng)建窗口時,還可以指定窗口獨有的附加特性.窗口種類簡稱窗口類,窗口類不能重名.在建立窗口類后,必須向Windows登記.建立窗口類就是用WNDCLASS結構定義一個結構變量。,窗口的創(chuàng)建過程,窗口的創(chuàng)建過程需要四個步驟,下面列出了創(chuàng)建步驟和這個過程中涉及的類和函數(shù): 1.設計一個窗口類//很多特征(光標,圖標,背景)WNDCLASS 2.注冊窗口類//RegisterClass 3.創(chuàng)建窗口//首先定義句柄 如:HWND hwnd; CreateWindow 4.顯示及更新窗口//顯示窗口:ShowWindow,更新窗口:UpdateWindow,理解Window編程所使用的事件驅動模型,事件,消息,消息隊列和消息循環(huán) Windows應用程序是遵循事件驅動模型,通常Windows程序啟動后只是等待某些事件的發(fā)生(當然應用程序也可以進行空閑處理-當沒有事件發(fā)生時(WM_IDLE)執(zhí)行某些特定的任務)。事件有多種產(chǎn)生途徑,包括: 鍵盤中某些鍵按下,鼠標單擊,當窗口創(chuàng)建時,尺度發(fā)生變化時(Windows系統(tǒng)發(fā)給你的),發(fā)生移動,被關閉,最小化,最大化或變?yōu)榭梢姞顟B(tài)時等。 當某一個事件發(fā)生時,Windows會為該事件所針對的應用程序發(fā)送一條消息,表明該事件發(fā)生了,并在該應用程序的消息隊列中增加一條消息,該消息隊列只是保存了應用程序所接受的消息的一個優(yōu)先隊列(會根據(jù)消息的優(yōu)先級排列)。應用程序要做的事,就是在一個消息循環(huán)中不斷地檢查消息隊列,當接收到一條消息時,便將其分派給接收該消息的特定窗口的窗口過程。一個應用程序可能含有很多的窗口,窗口過程是一個與應用程序各窗口相關的特殊函數(shù)。(每一個窗口都有一個窗口過程,但多個窗口可以共用一個窗口過程),他就是我們實現(xiàn)的用于處理消息的函數(shù)。,事件驅動編程模型,事件,,Window操作系統(tǒng)檢測到 某一個事件的發(fā)生。作為 響應,Windows將給特定程 序的消息隊列發(fā)送一條消息,,,,,應用程 序A消 息隊列,應用程 序B消 息隊列,應用程 序C消 息隊列,當一條消息到來時,消息隊 列會一直將該消息保存到應用程序準備 對其進行處理為止。所以,如果應用層 序正處在忙碌狀態(tài),該消息便不會立即被 處理。此外,優(yōu)先級較高的消息會在優(yōu)先級 較低的消息之前優(yōu)先得到處理。,,,應用程序A的 消息循環(huán),應用程序B的 消息循環(huán),應用程序C的 消息循環(huán),,,窗口過程C,,窗口過程C,,窗口過程C,,應用程序的消息循環(huán)不斷地檢查消 息是否到來,當“獲取”一條 消息時,該消息便被發(fā)送給窗口過 程WndProc,該函數(shù)為消息的傳遞的目 的地。,,窗口過程是一個由應用程序開 發(fā)人員定義的函數(shù)。該函數(shù)由windows 系統(tǒng)自動調用。注意,一個單個應 用程序允許有多窗口函數(shù),,,Case WM_CREATE: Case WM_KEYDOWN: Case WM_DESTROY: 在窗口處理函數(shù)內部,編 寫消息處理操作,消息?,消息系統(tǒng)對于一個win32程序來說十分重要,它是一個程序運行的動力源泉。一個消息,是系統(tǒng)定義的一個32位的值,他唯一的定義了一個事件,向Windows發(fā)出一個通知,告訴應用程序某個事情發(fā)生了。例如,單擊鼠標、改變窗口尺寸、按下鍵盤上的一個鍵都會使Windows發(fā)送一個消息給應用程序。 消息本身是作為一個記錄傳遞給應用程序的,這個記錄中包含了消息的類型以及其他信息。例如,對于單擊鼠標所產(chǎn)生的消息來說,這個記錄中包含了單擊鼠標時的坐標。這個記錄類型叫做MSG,MSG含有來自windows應用程序消息隊列的消息信息。 消息可以由系統(tǒng)或者應用程序產(chǎn)生。系統(tǒng)在發(fā)生輸入事件時產(chǎn)生消息。舉個例子, 當用戶敲鍵, 移動鼠標或者單擊控件。系統(tǒng)也產(chǎn)生消息以響應由應用程序帶來的變化, 比如應用程序改變系統(tǒng)字體改變窗體大小。應用程序可以產(chǎn)生消息使窗體執(zhí)行任務,或者與其他應用程序中的窗口通訊。,消息中有什么?,它在Windows中聲明如下: typedef struct tagMsg { HWND hwnd; 接受該消息的窗口句柄 UINT message; 消息常量標識符,也就是我們通常所說的消息號 WPARAM wParam; 32位消息的特定附加信息,確切含義依賴于消息值 LPARAM lParam; 32位消息的特定附加信息,確切含義依賴于消息值 DWORD time; 消息創(chuàng)建時的時間 POINT pt; 消息創(chuàng)建時的鼠標/光標在屏幕坐標系中的位置 }MSG;,Msg消息結構內容,Windows系統(tǒng)的消息機制都包含2個長整型的參數(shù):WPARAM, LPARAM,可以存放指針,也就是說可以指向任何內容了。 傳遞的內容因消息各異,消息處理函數(shù)會根據(jù)消息的類型進行特別的處理,它知道傳遞的參數(shù)是什么含義。 消息在線程內傳遞時,由于在同一個地址空間中,指針的值是有效的。但是夸線程的情況就不能直接使用指針了,所以Windows系統(tǒng)提供了 WM_SETTEXT, WM_GETTEXT, WM_COPYDATA等消息,用來特殊處理,指針的內容會被放到一個臨時的內存映射文件(Memory-mapped File)里面,通過它實現(xiàn)線程間的共享數(shù)據(jù)。,消息隊列和線程的關系是什么?,消息隊列的創(chuàng)建:當一個線程第一次被創(chuàng)建時,系統(tǒng)假定線程不會用于任何與用戶相關的任務。這樣可以減少線程對系統(tǒng)資源的要求。但是,一旦該線程調用一個與圖形用戶界面有關的函數(shù) ( 如檢查它的消息隊列或建立一個窗口 ),系統(tǒng)就會為該線程分配一個消息隊列 ,以便它能夠執(zhí)行與用戶界面有關的任務。 操作系統(tǒng)可能為任何線程創(chuàng)建消息隊列,只要該線程調用了消息獲取函數(shù),甚至都不需要該線程創(chuàng)建任何窗口。 如果句柄是NULL,DispatchMessage不做任何事。因此在處理線程消息的時候不能用DispatchMessage處理,而是直接處理,線程一般沒有窗口。如下: DispatchMessage函數(shù)需要輸入一個指向MSG結構的指針(當然這個MSG結構可以是以前GetMessage或者PeekMessage獲得的)。DispatchMessage會傳送窗口句柄,消息標識符,兩個消息參數(shù)給窗口過程函數(shù),但是他不會傳送時間和鼠標位置信息。應用程序在處理消息的時候可以通過調用GetMessageTime和GetMessagePos函數(shù)獲得這兩個信息。,消息隊列的資源體,線程處理消息的例程,如果參數(shù)lpmsg指向一個WM_TIMER消息,并且WM_TIMER消息的參數(shù)IParam不為NULL,則調用IParam指向的函數(shù),而不是調用窗口程序。 見線程\multi1thread1中的 CreateMsgThread和GetMesgThread 函數(shù)中調用PeekMessage的用意。,沒有窗口如何發(fā)消息?,向一個線程傳遞消息可以使用PostThreadMessage,這個函數(shù)的接口很明確,要求傳遞一個線程Id,但是還有PostMessage和SendMessage這樣的API,要求傳遞的是窗口句柄,這可能就是我們此前會迷惑于消息隊列是跟窗口相關還是跟線程相關的原因。事實上,在調用PostMessage或SendMessage的時候,系統(tǒng)要根據(jù)傳入的窗口句柄,確認是哪一個線程創(chuàng)建了該窗口(我們也可以通過GetWindowsThreadProcessId自行獲取是哪個線程創(chuàng)建了一個窗口),然后系統(tǒng)分配一塊內存,將消息參數(shù)存儲在這塊內存中,并將這塊內存增加到相應線程的登記消息隊列中。 線程中可以有消息循環(huán),消息循環(huán)將線程中的消息取出來并且進行處理——可以自行根據(jù)消息的類型進行處理,也可以交給DispatchMessage處理,該API會回調窗口類中的窗口處理函數(shù)(依據(jù)該窗口所屬窗口類別WNDCLASS的不同分別回調不同的消息處理函數(shù))。如果線程創(chuàng)建了窗口,那么窗口的各種響應事件全部是由消息循環(huán)以及相關處理完成的,一個消息循環(huán)可以處理很多個窗口的消息。,消息分類,隊列消息和非隊列消息:從消息的發(fā)送途徑上看,消息分兩種:隊列消息和非隊列消息。 隊列消息送到系統(tǒng)消息隊列,然后到線程消息隊列;非隊列消息直接送給目的窗口過程。這里,對消息隊列闡述如下: Windows維護一個系統(tǒng)消息隊列,每個GUI線程有一個線程消息隊列(Thread message queue)。鼠標、鍵盤事件由鼠標或鍵盤驅動程序轉換成輸入消息并把消息放進系統(tǒng)消息隊列,例如WM_MOUSEMOVE、WM_LBUTTONUP、WM_KEYDOWN、WM_CHAR等等。Windows每次從系統(tǒng)消息隊列移走一個消息,確定它是送給哪個窗口的和這個窗口是由哪個線程創(chuàng)建的,然后,把它放進窗口創(chuàng)建線程的線程消息隊列。線程消息隊列接收送給該線程所創(chuàng)建窗口的消息。線程從消息隊列取出消息,通過Windows把它送給適當?shù)拇翱谶^程來處理。除了鍵盤、鼠標消息以外,隊列消息還有WM_PAINT、WM_TIMER和WM_QUIT。這些隊列消息以外的絕大多數(shù)消息是非隊列消息。,消息分類?,windows中的消息雖然很多,但是種類并不繁雜,大體上有3種:窗口消息、命令消息和控件通知消息。 窗口消息大概是系統(tǒng)中最為常見的消息,它是指由操作系統(tǒng)和控制其他窗口的窗口所使用的消息。例如CreateWindow、MoveWindow等都會激發(fā)窗口消息,還有我們在上面談到的單擊鼠標所產(chǎn)生的消息也是一種窗口消息。 命令消息,這是一種特殊的窗口消息,他用來處理從一個窗口發(fā)送到另一個窗口的用戶請求,例如按下一個按鈕,他就會向主窗口發(fā)送一個命令消息。 控件通知消息,是指這樣一種消息,一個窗口內的子控件發(fā)生了一些事情,需要通知父窗口。通知消息只適用于標準的窗口控件如按鈕、列表框、組合框、編輯框,以及Windows公共控件如樹狀視圖、列表視圖等。例如,單擊或雙擊一個控件、在控件中選擇部分文本、操作控件的滾動條都會產(chǎn)生通知消息。 她類似于命令消息,當用戶與控件窗口交互時,那么控件通知消息就會從控件窗口發(fā)送到它的主窗口。但是這種消息的存在并不是為了處理用戶命令,而是為了讓主窗口能夠改變控件,例如加載、顯示數(shù)據(jù)。,常見的window消息,VC中存在幾種系統(tǒng)定義的消息分類,不同的前綴符號經(jīng)常用于消息宏識別消息附屬的分類,系統(tǒng)定義的消息宏前綴如下:,BM 表示按鈕控制消息 CB 表示組合框控制消息 DM 表示默認下壓式按鈕控制消息 EM 表示編輯控制消息 LB 表示列表框控制消息 SBM 表示滾動條控制消息 WM 表示窗口消息,Windows應用程序常用消息,WM_LBUTTONDOWN:產(chǎn)生單擊鼠標左鍵的消息,Windows應用程序常用消息,此外,相似的消息還有: WM_LBUTTONUP:放開鼠標左鍵時產(chǎn)生; WM_RBUTTONDOWN:單擊鼠標右鍵時產(chǎn)生; WM_RBUTTONUP:放開鼠標右鍵時產(chǎn)生; WM_LBUTTONDBLCLK:雙擊鼠標左鍵時產(chǎn)生; WM_RBUTTONDBLCLK:雙擊鼠標右鍵時產(chǎn)生。,2.WM_KEYDOWN:按下一個非系統(tǒng)鍵時產(chǎn)生的消息,系統(tǒng)鍵是指實現(xiàn)系統(tǒng)操作的組合鍵,例如Alt與某個功能鍵的組合以實現(xiàn)系統(tǒng)菜單操作等,wParam:按下鍵的虛擬鍵碼,用以標識按下或釋放的鍵 1Param:記錄了按鍵的重復次數(shù)、掃描碼、轉移代碼、先前鍵的狀態(tài)等信息。,如F1的虛擬鍵碼在Windows.h文 件中定義為VK_F1,相似的消息還有WM_KEYUP, 在放開非系統(tǒng)鍵時產(chǎn)生,Windows應用程序常用消息,3.WM_ CHAR:按下一個非系統(tǒng)鍵時產(chǎn)生的消息 wParam 為按鍵的ASCII碼 1Param 與WM_KEYDOWN的相同 4. WM_CREATE:由CreateWindow函數(shù)發(fā)出的消息 wParam:未用 1Param:包含一個指向CREATESTRUCT數(shù)據(jù)結構的指針 5.WM_CLOSE:關閉窗口時產(chǎn)生的消息 wParam和1Param均未用。 6.WM_DESTROY:由DestroyWiodow函數(shù)發(fā)出的消息,Windows應用程序常用消息,7. WM_QUIT:由PostQuitMessage函數(shù)發(fā)出的消息 退出應用程序時發(fā)出的消息 wParam:含退出代碼,標識程序退出運行時的有關信息 1Param:未用 8. WM_PAINT,消息的接收,消息的接收主要有2個函數(shù):GetMessage、PeekMessage。 GetMessage原型如下:BOOL GetMessage(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax); GetMessage該函數(shù)用來獲取與hWnd參數(shù)所指定的窗口相關的且wMsgFilterMin和wMsgFilterMax參數(shù)所給出的消息值范圍內的消息。需要注意的是,如果hWnd為NULL,則GetMessage獲取屬于調用該函數(shù)應用程序的任一窗口的消息,如果wMsgFilterMin和wMsgFilterMax都是0,則GetMessage就返回所有可得到的消息。函數(shù)獲取之后將刪除消息隊列中的除WM_PAINT消息之外的其他消息,至于WM_PAINT則只有在其處理之后才被刪除。,消息的處理,接下來我們談一下消息的處理,首先我們來看一下VC中的消息泵: while(GetMessage( } }首先,GetMessage從進程的主線程的消息隊列中獲取一個消息并將它復制到MSG結構,如果隊列中沒有消息,則GetMessage函數(shù)將等待一個消息的到來以后才返回。然后TranslateAccelerator判斷該消息是不是一個按鍵消息并且是一個加速鍵消息,如果是,則該函數(shù)將把幾個按鍵消息轉換成一個加速鍵消息傳遞給窗口的回調函數(shù)。處理了加速鍵之后,函數(shù)TranslateMessage將把兩個按鍵消息WM_KEYDOWN和WM_KEYUP轉換成一個WM_CHAR,不過需要注意的是,消息WM_KEYDOWN,WM_KEYUP仍然將傳遞給窗口的回調函數(shù)。,消息的處理,處理完之后,DispatchMessage函數(shù)將把此消息發(fā)送給該消息指定的窗口中已設定的回調函數(shù)。如果消息是WM_QUIT,則GetMessage返回0,從而退出循環(huán)體。應用程序可以使用PostQuitMessage來結束自己的消息循環(huán)。通常在主窗口的WM_DESTROY消息中調用。,窗口過程,窗口過程是一個用于處理所有發(fā)送到這個窗口的消息的函數(shù)。任何一個窗口類都有一個窗口過程。同一個類的窗口使用同樣的窗口過程來響應消息。 系統(tǒng)發(fā)送消息給窗口過程將消息數(shù)據(jù)作為參數(shù)傳遞給他,消息到來之后,按照消息類型排序進行處理,其中的參數(shù)則用來區(qū)分不同的消息,窗口過程使用參數(shù)產(chǎn)生合適行為。 一個窗口過程不經(jīng)常忽略消息,如果他不處理,它會將消息傳回到執(zhí)行默認的處理。窗口過程通過調用DefWindowProc來做這個處理。窗口過程必須return一個值作為它的消息處理結果。大多數(shù)窗口只處理小部分消息和將其他的通過DefWindowProc傳遞給系統(tǒng)做默認的處理。窗口過程被所有屬于同一個類的窗口共享,能為不同的窗口處理消息。,小結,如果想在按下ESC鍵后,銷毀某一個窗口,則在窗口處理函數(shù)中,可以這樣寫: switch(uMsgId) { case WM_KEYDOWN: if (wParam==VK_ESCAPE) ::DestroyWindow(MainWndhandle); Return 0; } 該窗口沒有處理的消息交給默認的窗口過程DefWindowProc處理。 用戶或者應用程序的某些行為(調用一個API)產(chǎn)生一些事件,操作系統(tǒng)找到事件所屬的應用程序,然后向該應用程序發(fā)送一條相應的消息。然后,該消息就被加入到該應用程序的消息隊列中。之后,應用程序不斷地檢查消息隊列。每當接受到一條消息時,應用程序就會將該消息發(fā)給與該消息所屬窗口相關的窗口過程。最后,窗口過程執(zhí)行與當前消息對應的指令。,Window程序的運行機制,基本上是這樣運行的,程序從WinMain()開始,然后進入一個message loop,程序在這里等待發(fā)給它的所有消息然后一一處理,直到接收到WM_QUIT的消息的時候,message loop終止,程序結束。所以整個主程序運行的過程就是等待消息,接收消息,然后處理消息的過程。,,理解Window編程所使用的事件驅動模型,Windows程序基本框架 Windows程序具有相對固定的結構。與以前的DOS程序不同。Windows程序和操作系統(tǒng)結合更緊密??梢哉f應用程序的在運行時絕大多數(shù)時間都在等待操作系統(tǒng)的消息。我們編寫一個Windows程序其實很簡單。需要做的工作是:注冊窗口類,創(chuàng)建窗口對象,編寫窗口過程,消息循環(huán)。,,1:注冊窗口類,Windows的窗口類定義了一個窗口的風格,而類中定義的窗口過程決定了窗口的行為。每個類都有一個自己的名字,在Windows中所有的窗口都屬于一個窗口類。如果我們需要創(chuàng)建一個自己的窗口,則必須注冊一個窗口類。Win32系統(tǒng)提供了一些全局類。這些類無需注冊就可以使用。比如:Windows的一些通用控件。對話框類。等等。下面是一個注冊窗口類的代碼: WNDCLASS wc; memset( 上面的代碼 WNDCLASS 是一個結構體,我們通過設置它的成員的值來設定窗口類的風格和屬性。然后將這個結構體傳遞給系統(tǒng),申請注冊窗口類。成功返回非0值,失敗返回0.如果注冊成功。我們就可以利用該類名來創(chuàng)建窗口了。,2:注冊窗口類,創(chuàng)建窗口就比較簡單了。我們只需要調用CreateWindow函數(shù),就可以創(chuàng)建它。用戶要注意的是,在這里要了解每個參數(shù)的作用。 HWND CreatemdiWndClassWnd(void) { HWND hwnd=CreateWindow(“MyWndClass”,“MDI 多文檔”,//這兩個參數(shù),第一個為類名,就是我們前面注冊的窗口類名,第二個為窗口標題 。 WS_MINIMIZEBOX|WS_VISIBLE|WS_CLIPSIBLINGS|WS_CLIPCHILDREN|WS_MAXIMIZEBOX|WS_CAPTION|WS_BORDER|WS_SYSMENU|WS_THICKFRAME,//這里是窗口風格,我們可以在這里指定窗口是否有最大化按鈕,是否有標題欄,是否可調整邊框等等一些風格 CW_USEDEFAULT,0,CW_USEDEFAULT,0,//這里是指定窗口的坐標和尺寸 NULL, NULL, hInst,//實例句柄 NULL); ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; return hwnd; } 通過上面這個函數(shù),我們已經(jīng)創(chuàng)建了一個窗口,創(chuàng)建完畢后還不能顯示,需要調用ShowWindow()函數(shù)窗口就會正常顯示出來。 在返回前,CreateWindow給窗口過程發(fā)送一個WM_CREATE消息。對于層疊,彈出式和子窗口,CreateWindow給窗口發(fā)送WM_CREATE,WM_GETMINMAXINFO和WM_NCCREATE消息。消息WM_CREATE的IParam參數(shù)包含一個指向CREATESTRUCT結構的指針。如果指定了WS_VISIBLE風格,CreateWindow向窗口發(fā)送所有需要激活和顯示窗口的消息。,3:窗口過程,窗口過程是我們處理消息的關鍵。前面我們說過,我們的應用程序的大部分工作都由操作系統(tǒng)完成,我們只要對自己感興趣的事件處理即可。在注冊窗口類的一節(jié)我們看到過了。窗口過程是一個函數(shù),它在注冊窗口類的時候被傳遞。 LRESULT CALLBACK MainWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam) { switch (msg) { case WM_CREATE: //這里是窗口創(chuàng)建完畢的消息,每一個窗口創(chuàng)建完畢都會產(chǎn)生次事件 //do something 做我們需要做的工作。完了要break; break; case WM_CLOSE://當用戶點擊關閉按鈕的時候會觸發(fā)這個事件。 DestroyWindow(hWin); return TRUE; case WM_DESTROY: PostQuitMessage(NULL); break; default: return DefWindowProc(hWin,uMsg,wParam,lParam);//我說了。大部分工作由系統(tǒng)處理吧,不感興趣的消息。統(tǒng)統(tǒng)交給默認窗口過程。 } return 0; },窗口過程,上面的窗口過程很簡單吧。窗口過程是一個回調函數(shù),也就是說。它是由操作系統(tǒng)調用的。當我們點擊一個鼠標或者按下一個鍵后,操作系統(tǒng)會將消息發(fā)送給前臺窗口,應用程序接收到此消息后,會調用對應的窗口過程。這個時候窗口過程被執(zhí)行。 響應WM_DESTROY,調用PostQuitMessage(int)結束進程。它會投遞一個WM_QUIT消息對消息隊列中。當消息循環(huán)的GetMessage取到WM_QUIT消息,則返回0,程序結束。 所以說應用程序會自己產(chǎn)生消息。,4:消息循環(huán),消息循環(huán)顧名思義,就是循環(huán)等待消息的到來。 while (GetMessage ( 這里是一個最簡單的消息循環(huán)了。GetMessage 是從消息隊列種取得一條消息,如果消息隊列中沒有消息,這個函數(shù)就等待,直到有消息到達才會返回。當然如果收到WM_QUIT 消息,這個函數(shù)會返回假,循環(huán)跳出,這個時候就宣告應用程序關閉了。窗口過程中的 PostQuitMessage() 就是發(fā)送這個消息。通知退出消息循環(huán)。TranslateMessage () 函數(shù)的功能是對一些原始消息進行預處理,比如按鍵消息DispatchMessage ()函數(shù)則是負責分派消息,它會將消息的參數(shù)做為調用參數(shù),調用該窗口的窗口過程。,WinMain函數(shù),WinMain函數(shù)是所有windows應用程序的入口,類似于C語言中的Main函數(shù),其功能是完成一系列的定義和初始化工作,并產(chǎn)生消息循環(huán)。消息循環(huán)是整個程序運行的核心。WinMain函數(shù)實現(xiàn)以下功能。 1. 注冊窗口類,建立窗口及執(zhí)行其它必要的初始化工作; 2. 進入消息循環(huán),根據(jù)從應用程序消息隊列接受的消息,調用相應的處理過程 3. 當消息循環(huán)檢索到WM_QUIT消息時終止程序運行。 WinMain函數(shù)有三個基本的組成部份:函數(shù)說明、初始化和消息循環(huán)。 WinMain函數(shù)的說明如下: int WINAPI WinMain( //WinMain函數(shù)說明 HINSTANCE hInstance, //程序當前實例句柄 HINSTANCE hPrevInstance, //應用程序其它實例句柄 LPSTR lpCmdLine, //指向程序命令行參數(shù)的指針 int nCmdShow //應用程序開始執(zhí)行時窗口顯示方式的整數(shù)值標識 ) 由于Window操作系統(tǒng)是多任務的操作系統(tǒng),能進行多任務的管理,因此,windows應用程序可能被并行的多次執(zhí)行,因而可能出現(xiàn)同一個程序的多個窗口同時存在的情況,Windows系統(tǒng)將應用程序每一次執(zhí)行稱為該應用程序的一個實例(Instance),并用一個實例句柄唯一的標識它。 #define WINAPI _stdcall,指定函數(shù)的調用約定,即函數(shù)參數(shù)的壓棧順序,由調用者還是被調用者把參數(shù)彈出棧,以及產(chǎn)生函數(shù)修飾名的方法。,HelloWORLD示例程序的解釋,int APIENTRY WinMain(HINSTANCE hInstance, //WinMain函數(shù)說明 HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // TODO: Place code here. /* 建議采用Pascal的變量定義風格,即在程序(函數(shù))開始處定義所有變量 雖然C++的變量定義比較靈活,本程序為了使程序易于理解,未采用這種方法,便于移植。 */ char lpszClassName[]=“窗口“; //窗口類名 char lpszTitle[]=“Windows SDK編程之一 窗口示例程序“; //窗口標題名 //---------------窗口類定義------------------------------------- /* 窗口類的定義 在Windows應用程序中,窗口害定義了窗口的形式與功能。窗口類定義通過給窗口類數(shù)據(jù)結構WNDCLASS賦值完成,該數(shù)據(jù)結構中包括窗口類的各種屬性,在窗口類定義過程中常用到以下函數(shù): */,WNDCLASS wndclass; wndclass.style=0; //窗口類型為缺省類型 wndclass.lpfnWndProc=WndProc; //窗口處理函數(shù)為WndProc wndclass.cbClsExtra=0; //窗口類無擴展 wndclass.cbWndExtra=0; //窗口實例無擴展 wndclass.hInstance=hInstance; //當前實例句柄 wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION); //使用缺省圖標 /* LoadIcon():在應用程序中加載一個窗口圖標 LoadIcon()函數(shù)原型為: HICON LoadIcon( HINSTANCE hInstance,//圖標資源所在的模塊句柄,為NULL則使用系統(tǒng)預定義圖標 LPCTSTR lpIconName //圖標資源名或系統(tǒng)預定義圖標標識名 )*/,實例,實例,wndclass.hCursor=LoadCursor(NULL,IDC_ARROW); //窗口采用箭頭光標 /* LoadCursor():在應用程序中加載一個窗口光標 LoadCursor()函數(shù)原型為: HCURSOR LoadCursor( HINSTANCE hInstance,//光標資源所在的模塊句柄,為NULL則使用系統(tǒng)預定義光標 LPCTSTR lpCursorName //光標資源名或系統(tǒng)預定義光標標識名 ) */ wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); //窗口背景為白色 /* GetStockObject():獲取已經(jīng)定義的畫筆、畫刷、字體等對象的句柄 GetStockObject()函數(shù)原型為: HGDIOBJ GetStockObject(int fnObject); //fnObject為對象的標識名 */ wndclass.lpszMenuName=NULL; //窗口中無菜單 wndclass.lpszClassName=lpszClassName; //窗口類名為'窗口實例',實例,//------------------以下是進行窗口類的注冊--------------------------- /* 注冊窗口類 Windows系統(tǒng)本身提供部份預定義的窗口類,程序員也可以自定義窗口類,窗口類必須先注冊后使用。窗口類的注冊由注冊函數(shù)RegisterClass()實現(xiàn)。其形式為: RegisterClass( //指向一個傳遞給窗口的參數(shù)值的指針*/,實例,//創(chuàng)建窗口操作 HWND hwnd; //窗口結構 hwnd=CreateWindow(lpszClassName, //創(chuàng)建窗口,窗口類名 lpszTitle, //窗口實例的標題名 WS_OVERLAPPEDWINDOW, //窗口的風格 CW_USEDEFAULT,CW_USEDEFAULT, //窗口左上角坐標為缺省值 CW_USEDEFAULT,CW_USEDEFAULT, //窗口的高度和寬度為缺省值 NULL, //此窗口無父窗口 NULL, //此窗口無主菜單 hInstance, //應用程序當前句柄 NULL); //不使用該值 ShowWindow(hwnd,nCmdShow); //顯示窗口 UpdateWindow(hwnd); //繪制用戶區(qū),實例,/* 消息循環(huán) windows應用程序的運行以消息為核心。windows將產(chǎn)生的消息放入應用程序的消息隊列中而應用程序WinMain函數(shù)的消息循環(huán)提取消息隊列中的消息,并將其傳遞給窗口函數(shù)為相應處理過程處理。 MSG msg; //消息結構 while( GetMessage( * /,實例,while( GetMessage( //程序終止時,將信息返回操作系統(tǒng) },實例,//-----------------------------窗口函數(shù)--------------------------------------- /* 窗口消息處理函數(shù)定義了應用程序對接收到的不同消息的響應,它包含了應用程序對各種可用接收到的消息的處理過程,通常 ,窗口函數(shù)由一個或多個switch.case語句組成,每一條case語句 對應一種消息,當應用程序接收到一個消息時,相應的case語句被 激活并執(zhí)行相應的響應程序模塊。 窗口函數(shù)的一般形式如下: LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); Parameters hwnd :[in] Handle to the window. uMsg :[in] Specifies the message. wParam:[in] Specifies additional message information. The contents of this parameter depend on the value of the uMsg parameter. lParam:[in] Specifies additional message information. The contents of this parameter depend on the value of the uMsg parameter.,實例,Return Value The return value is the result of the message processing and depends on the message sent. LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) { switch (message) { case . break; . case WM_DESTROY: //void PostQuitMessage(int nExitCode)函數(shù)的作用是向程序發(fā)送WM_QUIT消息,nExitCode應用程序退出代碼 PostQuitMessage(0); //調用該函數(shù)發(fā)出WM_QUIT消息 default: //缺省消息處理函數(shù),以保證所的發(fā)往窗口的消息都能被處理 return DefWindowProc(hwnd,message,wParam,lParam); } return (0); */,示例,LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) { switch (message) { case WM_DESTROY: PostQuitMessage(0); //調用該函數(shù)發(fā)出WM_QUIT消息 default: //缺省消息處理函數(shù) return DefWindowProc(hwnd,message,wParam,lParam); } return (0); } /*注: 事件驅動的特點: Windows程序設計圍繞著事件或消息的產(chǎn)生驅動產(chǎn)生運行消息處理函數(shù)。Windows程序的執(zhí)行順序取決于事件發(fā)生的順序,程序的執(zhí)行是由順序產(chǎn)生的消息驅動的,程序員可以針對消息類型編寫消息處理程序以處理接收的消息,或者發(fā)出其他消息以驅動其他處理程序,但是不必預先確定消息的產(chǎn)生順序。這是面向對象編程中事件驅動的顯著特點。 事件驅動編程方法對于編寫交互程序很有用處,用這一方法編寫的程序使程序避免了死板的操作模式,從而使用戶能夠按照自己的意愿采用靈活多變的操作模式。 Windows應用程序中的消息傳遞機制: VC中存在幾種系統(tǒng)定義的消息分類,常用的消息由窗口消息、初始化消息、輸入消息、系統(tǒng)消息、剪切板消息、文當界面消息、DDE(動態(tài)數(shù)據(jù)交換)消息、應用程序自定義消息等。應用程序- 配套講稿:
如PPT文件的首頁顯示word圖標,表示該PPT已包含配套word講稿。雙擊word圖標可打開word文檔。
- 特殊限制:
部分文檔作品中含有的國旗、國徽等圖片,僅作為作品整體效果示例展示,禁止商用。設計者僅對作品中獨創(chuàng)性部分享有著作權。
- 關 鍵 詞:
- Windows 應用程序 基本 結構
裝配圖網(wǎng)所有資源均是用戶自行上傳分享,僅供網(wǎng)友學習交流,未經(jīng)上傳用戶書面授權,請勿作他用。
鏈接地址:http://m.jqnhouse.com/p-1906271.html