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()更加极端,直接将消息交给其他应用程序的具体某个窗口,让该窗口的窗口回调消息直接处理要发送的消息。

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