?? 見招拆招《windows程序設計》(二) .txt
字號:
最后,一個老經驗是:在一些Windows范例程序中,您可能在WinMain中看到以下程序代碼:
if (!hPrevInstance)
{
wndclass.cbStyle = CS_HREDRAW | CS_VREDRAW ;
初始化其它 wndclass
RegisterClass (&wndclass) ;
}
這是出于「舊習難改」的原因。在16位的Windows中,如果您啟動正在執行的程序的一個新執行實體,WinMain的hPrevInstance參數將是前一個執行實體的執行實體句柄。為節省內存,兩個或多個執行實體就可能會共享相同的窗口類別。這樣,窗口類別就只在hPrevInstance是NULL的時候才注冊,這表明程序沒有其它執行實體。
在32位的Windows中,hPrevInstance總是NULL。此程序代碼會正常執行,而實際上也沒必要檢查hPrevInstance。
建立窗口
窗口類別定義了窗口的一般特征,因此可以使用同一窗口類別建立許多不同的窗口。實際呼叫CreateWindowEx建立窗口時,可能指定有關窗口的更詳細的信息。
Windows程序設計新手有時會混淆窗口類別和窗口之間的區別,以及為什么一個窗口的所有特征不能被一次設定好。實際上,以這種方式分開這些樣式信息是非常方便的。例如,所有的按鈕窗口都可以依據同樣的窗口類別來建立,與這個窗口類別相關的窗口消息處理程序位于Windows內部。由窗口類別來負責處理按鈕的鍵盤和鼠標輸入,并定義按鈕在屏幕上的外觀形象。從這一點看來,所有的按鈕都是以同樣的方式工作的。但是并非所有的按鈕都是一樣的。它們可以有不同的大小,不同的屏幕位置,以及不同的字符串。后面的這樣一些特征是窗口定義的一部分,而不是窗口類別定義的。
[細心的讀者比對原文時會發現CreateWindows API并不存在,這是因為C語言會調用不同的API來"解釋"這個宏]
傳遞給RegisterClass函數的信息會在一個數據結構中設定好,而傳遞給CreateWindowEx函數的信息會在函數單獨的參數中設定好。下面是HELLOWIN.ASM中的CreateWindowEx呼叫,每一個字段都做了完整的說明:
invoke CreateWindowEx,
NULL,
ADDR szAppName, ;window class name
CTXT("http://www.aogosoft.com"), ;window caption
WS_OVERLAPPEDWINDOW, ;window style
200, ;initial x position
200, ;initial y position
400, ;initial x size
200, ;initial y size
NULL, ;parent window handle
NULL, ;window menu handle
hInst, ;program instance handle
NULL ;creation parameters
mov hWnd,eax
標記為「window class name」的參數是szAppName,它含有字符串「HelloWin」-這是程序注冊的窗口類別名稱。這就是我們建立的窗口聯結窗口類別的方式。
此程序建立的窗口是一個普通的重迭式窗口。它含有一個標題列,標題列左邊有一個系統菜單按鈕,標題列右邊有縮小、放大和關閉圖示,四周還有一個表示窗口大小的邊框。這是標準樣式的窗口,名為WS_OVERLAPPEDWINDOW,出現在CreateWindow的「窗口樣式」參數中。如果看一下Windows.inc,您將會發現此樣式是幾種位旗標的組合:
WS_OVERLAPPEDWINDOW equ
WS_OVERLAPPED OR
WS_CAPTION OR
WS_SYSMENU OR
WS_THICKFRAME OR
WS_MINIMIZEBOX OR
WS_MAXIMIZEBOX
「窗口標題」是顯示在標題列中的文字。
注釋著「initial x position」和「initial y position」的參數指定了窗口左上角相對于屏幕左上角的初始位置。由于這些參數使用CW_USEDEFAULT標識符,指示Windows使用重迭窗口的內定位置。(CW_USEDEFAULT定義為0x80000000。)內定情況下,Windows依次對新建立的窗口定位,使各窗口左上角的垂直和水平距離在屏幕上按一定的大小遞增。與此類似,注釋著「initial x size」和「initial y size」的參數分別指定窗口的寬度和高度。同樣使用了CW_USEDEFAULT標識符,表明希望Windows使用內定尺寸。
在建立一個「最上層」窗口,如應用程序窗口時,注釋為「父窗口句柄」的參數設定為NULL。通常,如果窗口之間存在有父子關系,則子窗口總是出現在父窗口的上面。應用程序窗口出現在桌面窗口的上面,但不必為呼叫CreateWindow而找出桌面窗口的句柄。
因為窗口沒有菜單,所以「窗口菜單句柄」也設定為NULL。「程序執行實體句柄」設定為執行實體句柄,它是作為WinMain的參數傳遞給這個程序的。最后,「建立參數」指標設定為NULL,可以用這個參數存取稍后程序中可能引用到的數據。
CreateWindow傳回被建立的窗口的句柄,該句柄存放在變量hwnd中,后者被定義為HWND型態(「窗口句柄型態」)。Windows中的每個窗口都有一個句柄,程序用句柄來使用窗口。許多Windows函數需要使用hwnd作為參數,這樣,Windows才能知道函數是針對哪個窗口的。如果一個程序建立了許多窗口,則每個窗口均有一個句柄。窗口句柄是Windows程序所處理最重要的句柄之一。
顯示窗口
在CreateWindow呼叫傳回之后,Windows內部已經建立了這個窗口。這就是說,Windows已經配置了一塊內存,用來保存在CreateWindow呼叫中指定窗口的全部信息跟一些其它信息,而Windows稍后就是依據窗口句柄找到這些信息的。
然而,光是這樣子,窗口并不會出現在視訊顯示器上。您還需要兩個函數呼叫,一個是:
invoke ShowWindow,hWnd,SW_SHOWNORMAL
第一個參數是剛剛用CreateWindow建立的窗口句柄。第二個參數是作為參數傳給WinMain的iCmdShow。它確定最初如何在屏幕上顯示窗口,是一般大小、最小化還是最大化。在開始菜單中安裝程序時,使用者可能做出最佳選擇。如果窗口按一般大小顯示,那么WinMain接收到后傳遞給ShowWindow的就是SW_SHOWNORMAL﹔如果窗口是最大化顯示的,則為SW_SHOWMAXIMIZED。而如果窗口只顯示在工作列上,則是SW_SHOWMINNOACTIVE。
ShowWindow函數在顯示器上顯示窗口。如果ShowWindow的第二個參數是SW_SHOWNORMAL,則窗口的顯示區域就會被窗口類別中定義的背景畫刷所覆蓋。函數呼叫
invoke UpdateWindow,hWnd
會重畫顯示區域。它經由發送給窗口消息處理程序(即HELLOWIN.ASM中的WndProc函數)一個WM_PAINT消息做到這一點。后面,我們將說明WndProc如何處理這個消息。
消息循環
呼叫UpdateWindow之后,窗口就出現在顯示器上。程序現在必須準備讀入使用者用鍵盤和鼠標輸入的數據。Windows為當前執行的每個Windows程序維護一個「消息隊列」。在發生輸入事件之后,Windows將事件轉換為一個「消息」并將消息放入程序的消息隊列中。
程序通過執行一塊稱之為「消息循環」的程序代碼從消息隊列中取出消息:
StartLoop:
invoke GetMessage,ADDR msg,NULL,0,0
cmp eax, 0
je ExitLoop
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
jmp StartLoop
ExitLoop:
msg變量是型態為MSG的結構,型態MSG在Windows.inc中定義如下:
MSG STRUCT
hwnd DWORD ?
message DWORD ?
wParam DWORD ?
lParam DWORD ?
time DWORD ?
pt POINT <>
MSG ENDS
POINT數據型態也是一個結構,它在Windows.inc中定義如下:
POINT STRUCT
x DWORD ?
y DWORD ?
POINT ENDS
消息循環以GetMessage呼叫開始,它從消息隊列中取出一個消息:
invoke GetMessage,ADDR msg,NULL,0,0
這一呼叫傳給Windows一個指標,指向名為msg的MSG結構。第二、第三和第四個參數設定為NULL或者0,表示程序接收它自己建立的所有窗口的所有消息。Windows用從消息隊列中取出的下一個消息來填充消息結構的各個字段,結構的各個字段包括:
hwnd 接收消息的窗口句柄。在HELLOWIN程序中,這一參數與CreateWindowEx傳回的hwnd值相同,因為這是該程序擁有的唯一窗口。
message 消息標識符。這是一個數值,用以標識消息。對于每個消息,均有一個對應的標識符,這些標識符定義于Windows表頭文件(其中大多數在Windows.inc中),以前綴WM(「window message」,窗口消息)開頭。例如,使用者將鼠標光標放在HELLOWIN顯示區域之內,并按下鼠標左按鈕,Windows就在消息隊列中放入一個消息,該消息的message字段等于WM_LBUTTONDOWN。這是一個常數,其值為0x0201。
wParam 一個32位的「message parameter(消息參數)」,其含義和數值根據消息的不同而不同。
lParam 一個32位的消息參數,其值與消息有關。
time 消息放入消息隊列中的時間。
pt 消息放入消息隊列時的鼠標坐標。
只要從消息隊列中取出消息的message字段不為WM_QUIT(其值為0x0012),GetMessage就傳回一個非零值。WM_QUIT消息將導致GetMessage傳回0。
敘述
invoke TranslateMessage, ADDR msg
將msg結構傳給Windows,進行一些鍵盤轉換。(關于這一點,我們將在后面深入討論。)
敘述
invoke DispatchMessage, ADDR msg
又將msg結構回傳給Windows。然后,Windows將該消息發送給適當的窗口消息處理程序,讓它進行處理。這也就是說,Windows將呼叫窗口消息處理程序。在HELLOWIN中,這個窗口消息處理程序就是WndProe函數。處理完消息之后,WndProc傳回到Windows。此時,Windows還停留在DispatchMessage呼叫中。在結束DispatchMessage呼叫的處理之后,Windows回到HELLOWIN,并且接著從下一個GetMessage呼叫開始消息循環。
窗口消息處理程序
以上我們所討論的都是必要的負擔:注冊窗口類別,建立窗口,然后在屏幕上顯示窗口,程序進入消息循環,然后不斷從消息隊列中取出消息來處理。
實際的動作發生在窗口消息處理程序中。窗口消息處理程序確定了在窗口的顯示區域中顯示些什么以及窗口怎樣響應使用者輸入。
在HELLOWIN中,窗口消息處理程序是命名為WndProc的函數。窗口消息處理程序可任意命名(只要求不和其它名字發生沖突)。一個Windows程序可以包含多個窗口消息處理程序。一個窗口消息處理程序總是與呼叫RegisterClass注冊的特定窗口類別相關聯。CreateWindowEx函數根據特定窗口類別建立一個窗口。但依據一個窗口類別,可以建立多個窗口。
窗口消息處理程序總是定義為如下形式:
WndProc proc hWin:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -