窗口是屏幕上的矩形區域,消息窗口功能有限,因爲我們不能添加四個以上的按鈕以及菜單等,而且添加的按鈕必須是windows提供的按鈕,不能自定義。所以我們有必要自己創建一個多功能可自定義的窗口。
自己的窗口
創建窗口最重要的函數是CreateWindow,它可以創建重疊式窗口,彈出式窗口,子窗口等。而且可以自定義各種功能。
HWND CreateWindow( LPCTSTR lpClassName, //窗口類 LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam ); |
從函數原型可以看出創建窗口的各種屬性,一種窗口含有顯示程序名稱的標題列、菜單甚至可能還有工具列和滾動條,另一種窗口是對話框,在上面可以放置各種控件。而這些控件也是窗口,稱爲子窗口。
窗口消息處理程序
窗口處理函數是理解消息機制的關鍵,windows處理將消息循環中取得的各種消息放入這個函數中進行處理,該函數會比對各個匹配項直到找到對應的處理項,如果沒有找到就傳給一個默認的窗口函數,是程序正常結束。該函數可以在程序中也可以在dll中。使用窗口類使多個窗口能夠屬於同一個窗口類,並使用同一個窗口消息處理函數。
HELLOWIN程序
//=========================== // (c)狗尾草 2008.1.19 //=========================== #include<windows.h> LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM); int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR szCmdLine,int iCmdShow) { static TCHAR szAppName[]="HelloWin"; HWND hWnd; MSG msg; WNDCLASS wndclass; wndclass.cbClsExtra=0; wndclass.cbWndExtra=0; wndclass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.hCursor=LoadCursor(NULL,IDC_ARROW); wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION); wndclass.hInstance=hInstance; wndclass.lpfnWndProc=WndProc; wndclass.lpszClassName=szAppName; wndclass.lpszMenuName=NULL; wndclass.style=CS_HREDRAW|CS_VREDRAW; if(!RegisterClass(&wndclass)) { MessageBox(NULL,"This program requires windows nt!",szAppName,MB_ICONERROR); return 0; } hWnd=CreateWindow(szAppName, "The Hello Program!", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); ShowWindow(hWnd,iCmdShow); UpdateWindow(hWnd); while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; RECT rect; switch(message) { case WM_CREATE: return 0; case WM_PAINT: hdc=BeginPaint(hWnd,&ps); GetClientRect(hWnd,&rect); DrawText(hdc,"Hello,WindowsXP!", -1,&rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER); EndPaint(hWnd,&ps); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hWnd,message,wParam,lParam); } |
調用的函數和功能如下,絕大多數在winuser.h:
LoadIcon | 加載圖標供程序使用 |
LoadCursor | 加載鼠標光標供程序使用 |
GetStockObject | 取得一個圖形對象 |
RegisterClass | 爲程序窗口註冊窗口類別 |
MessageBox | 顯示消息框 |
CreateWindow | 根據窗口類別建立一個窗口 |
ShowWindow | 在屏幕上顯示窗口 |
UpdateWindow | 指示窗口自我更新 |
GetMessage | 從消息隊列中取得消息 |
TranslateMessage | 轉譯某些鍵盤消息 |
DispatchMessage | 將消息發送給窗口消息處理程序 |
PlaySound | 播放一個聲音文件 |
BeginPaint | 開始繪製窗口 |
GetClientRect | 取得窗口顯示區域的大小 |
DrawText | 顯示字符串 |
EndPaint | 結束繪製窗口 |
PostQuitMessage | 在消息隊列中插入一個「退出程序」消息 |
DefWindowProc | 執行內定的消息處理 |
關於一些常量定義的約定如下:
前綴 | 類別 |
CS | 窗口類別樣式 |
CW | 建立窗口 |
DT | 繪製文字 |
IDI | 圖示ID |
IDC | 遊標ID |
MB | 消息框 |
SND | 聲音 |
WM | 窗口消息 |
WS | 窗口樣式 |
常數值是沒有必要記憶的,記住常量的一些大寫標識符就可以了。
各種句柄
標識符 | 含義 |
HINSTANCE | 執行實體(程序自身)句柄 |
HWND | 窗口句柄 |
HDC | 設備內容句柄 |
句柄通常是調用函數取得的,起到一個標識身份的目的。其實實質是一個32位整數。
數據結構註解
窗口類結構
typedef struct { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; HICON hIconSm; } WNDCLASSEX, *PWNDCLASSEX; |
窗口是根據窗口類建立的,窗口類相當於一個模板。他定義了各種屬性,定義後打造出來的窗口就是他定義的那個樣子了。如果窗口類沒有定義完全,註冊的時候就會失敗,我在寫的過程中漏寫了幾個屬性導致這樣的錯誤
該消息是在註冊的時候發生的。
Msg數據結構
ttypedef struct tagMSG { HWND hwnd ; UINT message ; WPARAM wParam ; LPARAM lParam ; DWORD time ; POINT pt ; } MSG, * PMSG ; |
從消息隊列中取得的消息來填充各個屬性。
Point數據結構
typedef struct tagPOINT { LONG x ; LONG y ; } POINT, * PPOINT |
存放該消息時的鼠標座標。
建立窗口
HWND CreateWindow( LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam ); |
其中的大部分參數都可以猜出意思,最後一個參數有必要解釋一下,是用來傳遞額外的數據的,這裏並沒有用到就設爲NULL了。該函數返回一個窗口句柄,用來唯一表示一個窗口的。這樣其他函數才知道作用在哪個窗口上。
建立窗口後調用ShowWindow (hwnd, iCmdShow)來顯示窗口,第二個參數是初始的顯示方式。UpdateWindow (hwnd)是用來重畫窗口的。
消息循環
while(GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } |
消息循環功能是將事件轉換爲消息並給處理函數處理。TranslateMessage用來解釋消息,DispatchMessage用來投遞給消息處理函數,非常神奇的一個函數。
WM_PAINT消息
顯示區域無效時才產生WM_PAINT消息,遮蓋後重新要求顯示,此時爲無效。當然改變窗口大小也爲無效。
關於程序的結束
當WM_DESTROY消息收到後執行PostQuiteMessage產生WM_QUITE消息,這樣該消息GetMessage()時返回的是0,導致消息循環結束。WinMain函數繼續執行,這個例子中是返回並結束程序。從這裏我可以想象到,windows程序主要是消息循環和消息處理函數的交互。除此之外的都是一些設置和初始化等事務性的例程。
彙編版本
;========================== ; (c)狗尾草 2008.1.19 ;========================== .386 .Model Flat, StdCall Option Casemap :None Include windows.inc Include user32.inc Include kernel32.inc Include gdi32.inc includelib gdi32.lib IncludeLib user32.lib IncludeLib kernel32.lib include macro.asm
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD WndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD
.DATA szClassName db "HELLOWIN",0
.DATA? hInstance dd ?
.CODE START: invoke GetModuleHandle,NULL ;取得當前進程實例 mov hInstance,eax invoke WinMain,hInstance,NULL,NULL,SW_SHOWDEFAULT invoke ExitProcess,0 WinMain proc hInst:DWORD,hPrevInst:DWORD,CmdLine:DWORD,CmdShow:DWORD LOCAL wc :WNDCLASSEX LOCAL msg :MSG local hWnd :HWND
mov wc.cbSize,sizeof WNDCLASSEX mov wc.style,CS_HREDRAW or CS_VREDRAW or CS_BYTEALIGNWINDOW mov wc.lpfnWndProc,offset WndProc mov wc.cbClsExtra,NULL mov wc.cbWndExtra,NULL push hInst ;存儲器變量用push和pop完成賦值 pop wc.hInstance mov wc.hbrBackground,COLOR_BTNFACE+1 mov wc.lpszMenuName,NULL mov wc.lpszClassName,offset szClassName invoke LoadIcon,hInst,100 mov wc.hIcon,eax invoke LoadCursor,NULL,IDC_ARROW mov wc.hCursor,eax mov wc.hIconSm,0 invoke RegisterClassEx, ADDR wc invoke CreateWindowEx,NULL,ADDR szClassName,CTXT("henry's hellowin program"),WS_OVERLAPPEDWINDOW,200,200,400,200,NULL,NULL,hInst,NULL mov hWnd,eax invoke ShowWindow,hWnd,SW_SHOWNORMAL invoke UpdateWindow,hWnd
StartLoop: invoke GetMessage,ADDR msg,NULL,0,0 cmp eax, 0 je ExitLoop invoke TranslateMessage, ADDR msg invoke DispatchMessage, ADDR msg jmp StartLoop ExitLoop:
mov eax,msg.wParam ret WinMain endp WndProc proc hWin:DWORD,uMsg:DWORD,wParam :DWORD,lParam :DWORD LOCAL hdc:HDC LOCAL ps:PAINTSTRUCT LOCAL rect:RECT .if uMsg==WM_CREATE
.elseif uMsg == WM_DESTROY invoke PostQuitMessage,NULL .elseif uMsg == WM_PAINT invoke BeginPaint,hWin,ADDR ps mov hdc,eax invoke GetClientRect,hWin,ADDR rect invoke DrawText,hdc,CTXT("HELLO,WINDOWS XP!"),-1,ADDR rect,DT_SINGLELINE or DT_CENTER or DT_VCENTER invoke EndPaint,hWin,ADDR ps .else invoke DefWindowProc,hWin,uMsg,wParam,lParam .endif ret WndProc endp END START |
彙編版本基本結構同C版,主要更注重細節方面。程序實例句柄由一個特殊的函數取得GetModuleHandle。函數符合先聲明後使用的特點。這點與嚴格的c語言是相同的。各種高級的僞指令的使用,可以說和C語言已經是哥哥弟弟了。程序運行的結果和C版沒任何區別