Windows窗口程序

什麼是窗口

想要深入瞭解Windows機制就必須從我們隨處可見的Window(窗口)說起。窗口就是我們在使用圖形程序時,屏幕上顯示的矩形區域。不同的窗口可能包含一些相同組成部分,如:窗口外沿,就是我們用於拖拽窗口移動位置的外邊框。標題欄,也就是窗口程序最上方顯示標題的地方,一般用於顯示應用程序名字。一下圖片是窗口的組成部分,各個組成部分都有自己的行爲特徵。大部分窗口都和以下圖片類似,但並不是每個窗口都有標題欄,狀態欄等,舉個典型的例子,瑞星殺毒軟件的小獅子就是一個窗口。

窗口的運作方式

首先窗口程序是由事件驅動的,用戶可能隨時發出各種消息,如:操作過程中絕得窗口不夠大了,馬上拖動邊框,程序就必須馬上調整客戶區的內容以適應新窗口大小;用戶可能突然想要做其他事情,可能隨時會把窗口最小化甚至關閉,窗口程序也必須立即響應縮小和退出的消息請求。如果窗口沒有接收特定的請求,它們就會一動不動的停在桌面上什麼也不做。

它與過程驅動方式不同,順序驅動方式只是單純的按照代碼邏輯一行一行處理數據,當第一遍流程處理完畢後就直接退出程序了,無法再次重頭處理一遍,這就是事件驅動和順序驅動的區別。

說白了就是窗口程序循環接受用戶或者系統其他程序發來的消息持續處理,接收到退出事件纔會退出;而順序驅動的程序只處理一遍代碼邏輯直接退出了。

以下是實現一個窗口的完整過程:

創建一個或多個窗口回調函數,因爲一個Win32程序有可能不止一個窗口,而每個窗口必須有一個回調函數,所以一個win32程序可能有多個窗口回調。

創建一個WNDCLASSEX窗口對象填寫對應窗口的信息,並將窗口類通過RegisterClassEx()函數註冊進入Windows消息機制中。註冊後需要調用顯示創建的窗口調用ShowWindow()和UpdataWindow()函數顯示窗口在屏幕上。接下來需要調用user32.dll的GetMessage()函數,從對應程序的消息隊列中獲取對應程序的消息。然後調用user32.dll中的DispatchMessage()函數將消息分發給正確的處理該消息的窗口即可。

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; FirstWindow.asm
; 窗口程序的模板代碼
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 使用 nmake 或下列命令進行編譯和鏈接:
; ml /c /coff FirstWindow.asm
; Link /subsystem:windows FirstWindow.obj
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
		.386
		.model flat,stdcall
		option casemap:none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定義
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include		windows.inc
include		gdi32.inc
includelib	gdi32.lib
include		user32.inc
includelib	user32.lib
include		kernel32.inc
includelib	kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 數據段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
		.data?
hInstance	dd		?
hWinMain	dd		?

		.const
szClassName	db	'MyClass',0
szCaptionMain	db	'My first Window !',0
szText		db	'Win32 Assembly, Simple and powerful !',0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代碼段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
		.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 窗口過程
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_ProcWinMain	proc	uses ebx edi esi hWnd,uMsg,wParam,lParam
		local	@stPs:PAINTSTRUCT
		local	@stRect:RECT
		local	@hDc

		mov	eax,uMsg
;********************************************************************
		.if	eax ==	WM_PAINT
			invoke	BeginPaint,hWnd,addr @stPs
			mov	@hDc,eax

			invoke	GetClientRect,hWnd,addr @stRect
			invoke	DrawText,@hDc,addr szText,-1,\
				addr @stRect,\
				DT_SINGLELINE or DT_CENTER or DT_VCENTER

			invoke	EndPaint,hWnd,addr @stPs
;********************************************************************
		.elseif	eax ==	WM_CLOSE
			invoke	DestroyWindow,hWinMain
			invoke	PostQuitMessage,NULL
;********************************************************************
		.else
			invoke	DefWindowProc,hWnd,uMsg,wParam,lParam
			ret
		.endif
;********************************************************************
		xor	eax,eax
		ret

_ProcWinMain	endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
_WinMain	proc
		local	@stWndClass:WNDCLASSEX
		local	@stMsg:MSG

		invoke	GetModuleHandle,NULL
		mov	hInstance,eax
		invoke	RtlZeroMemory,addr @stWndClass,sizeof @stWndClass
;********************************************************************
; 註冊窗口類
;********************************************************************
		invoke	LoadCursor,0,IDC_ARROW
		mov	@stWndClass.hCursor,eax
		push	hInstance
		pop	@stWndClass.hInstance
		mov	@stWndClass.cbSize,sizeof WNDCLASSEX
		mov	@stWndClass.style,CS_HREDRAW or CS_VREDRAW
		mov	@stWndClass.lpfnWndProc,offset _ProcWinMain
		mov	@stWndClass.hbrBackground,COLOR_WINDOW + 1
		mov	@stWndClass.lpszClassName,offset szClassName
		invoke	RegisterClassEx,addr @stWndClass
;********************************************************************
; 建立並顯示窗口
;********************************************************************
		invoke	CreateWindowEx,WS_EX_CLIENTEDGE,offset szClassName,offset szCaptionMain,\
			WS_OVERLAPPEDWINDOW,\
			100,100,600,400,\
			NULL,NULL,hInstance,NULL
		mov	hWinMain,eax
		invoke	ShowWindow,hWinMain,SW_SHOWNORMAL
		invoke	UpdateWindow,hWinMain
;********************************************************************
; 消息循環
;********************************************************************
		.while	TRUE
			invoke	GetMessage,addr @stMsg,NULL,0,0
			.break	.if eax	== 0
			invoke	TranslateMessage,addr @stMsg
			invoke	DispatchMessage,addr @stMsg
		.endw
		ret

_WinMain	endp
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
		call	_WinMain
		invoke	ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
		end	start

程序顯示結果

Windows的消息驅動機制

在這裏詳細描述完整的Windows消息驅動機制。看下圖

Windows下的窗口程序不止1個,往往有很多程序在同時運行。當用戶點擊其中一個程序的窗口或者進行其他操作時,Windows系統對應被點擊程序就會判斷點擊的座標位置是否是當前程序的窗口座標或者窗口範圍內。如果是,則該程序就會產生一個消息結構體,記錄下消息產生的原因送入Windows系統的總消息隊列裏。然後系統再根據產生的消息分配到每個應用程序的消息隊列中。(從總的消息隊列中再次分類,將消息挨個“落戶”到每個程序對應的消息隊列中)。當應用程序調用GetMessage()函數時,系統會從調用GetMessage()函數中的應用程序對應消息隊列中取出一條消息交給應用程序,得到消息後還需要知道這條消息具體應該交給該應用程下的哪個窗口去處理,這時候就再次調用DespatchMessage()函數,將消息交給對應窗口的消息回調函數,執行該窗口的條件分支來處理這個消息。

程序之間是允許相互傳遞消息的,PostMessage()函數允許一個程序將消息發送到其他程序的消息隊列中。SendMessage()更加極端,直接將消息交給其他應用程序的具體某個窗口,讓該窗口的窗口回調消息直接處理要發送的消息。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章