用32位汇编语言写一个窗口程序

用WIN32来写窗口程序需要编写两个文件,一个是资源脚本文件:*.rc,一个是汇编源文件:*.asm。将.asm文件编译后与.rc编译后的文件链接在一起就可以得到我们的窗口程序。而资源脚本文件中包含了对菜单,加速键,图标,光标,位图等资源的定义,源文件中包含如何使用这些资源的代码。

首先我们来看资源脚本文件中菜单和加速键以及图标光标的定义。

#include					
#define ICO_MAIN			0x1000	//图标
#define IDM_MAIN			0x2000	//菜单
#define IDA_MAIN			0x2000	//加速键
#define IDM_OPEN			0x4101
#define IDM_OPTION			0x4102
#define IDM_EXIT			0x4103
#define IDM_SETFONT			0x4201
#define IDM_SETCOLOR		0x4202
#define IDM_INACT			0x4203
#define IDM_GRAY			0x4204
#define IDM_LIST			0x4207
#define IDM_DETAIL			0x4208
#define IDM_TOOLBAR			0x4209
#define IDM_TOOLBARTEXT		0x4201
#define IDM_INPUTBAR		0x4211
#define IDM_HELP			0x4301
#define IDM_STATUSBAR		0x4212
#define IDM_ABOUT			0x4302
#define	IDM_CLG				0x4303
//图标和光标
#define	ICO_BIG				0x4304
#define	ICO_SMALL			0x4305
#define	CUR_2				0x4306
//图标和光标的菜单	
#define	IDM_BIG				0x4307
#define	IDM_SMALL			0x4308
#define	IDM_CUR_1			0x4309
#define	IDM_CUR_2			0x4310
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ICO_BIG		ICON			"Big.ico"
ICO_SMALL	ICON			"Small.ico"
CUR_2		CURSOR			"2.cur"
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
IDM_MAIN			menu	discardable
BEGIN
		popup		"文件(&F)"
		BEGIN
					menuitem		"打开文件(&O)...",IDM_OPEN
					menuitem		"关闭文件(&C)...",IDM_OPTION
					menuitem		separator
					menuitem		"退出(&X)",IDM_EXIT
		END
		popup		"查看(&V)"
		BEGIN
					menuitem		"字体(&F)...\tAlt+F",IDM_SETFONT
					menuitem		"背景色(&B)...\tCtrl+Alt+B",IDM_SETCOLOR
					menuitem		separator
					menuitem		"被禁用的菜单项",IDM_INACT,INACTIVE			//表示菜单是禁用的
					menuitem		"被灰化的菜单项",IDM_GRAY,GRAYED			//表示菜单项是灰化的
					menuitem		separator
					menuitem		"列表(&L)",IDM_LIST
					menuitem		"详细资料(&D)",IDM_DETAIL,MENUBREAK			//表示从这一行起后面的菜单项都另起一行
					menuitem		separator
					popup			"工具栏(&T)"
					BEGIN
							menuitem	"标准按钮(&S)",IDM_TOOLBAR
							menuitem	"文字标签(&C)",IDM_TOOLBARTEXT
							menuitem	"命令栏(&I)",IDM_INPUTBAR
					END
					menuitem		"状态栏(&U)",IDM_STATUSBAR
		END
		popup		"图标和光标(&I)"
		BEGIN	
					menuitem		"大图标(&G)",IDM_BIG
					menuitem		"小图标(&M)",IDM_SMALL
					menuitem		separator
					menuitem		"光标A(&A)",IDM_CUR_1
					menuitem		"光标B(&B)",IDM_CUR_2
		END
		popup		"帮助(&H)",HELP
		BEGIN	
					menuitem		"帮助主题(&H)\tF1",IDM_HELP
					menuitem		separator
					menuitem		"关于本程序(&A)...",IDM_ABOUT
		END
END
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
IDA_MAIN	ACCELERATORS
BEGIN
		VK_F1,	IDM_HELP,VIRTKEY
		"B",	IDM_SETCOLOR,VIRTKEY,CONTROL,ALT
		"F",	IDM_SETFONT,VIRTKEY,ALT
END
先给出一个完整的文件。我们可以看到这是C语言的格式,因为我们的资源编译器 RC.EXE是从VS里面提取出来的,所以这个就是用C语言的格式。首先我们定义了一些菜单,图标以及加速键的命令ID,当然菜单的ID也可以使用字符串,但是这样的话在使用的时候会比较麻烦。这里有一个要注意的就是图标和光标的定义格式,图标: ID  ICON  “文件名.ico”   光标: ID  CURSOR  "文件名.cur“。这些是图标和光标的定义,上面的IDM_BIG那些是他们的菜单的命令的定义,不要搞混了。
接着往下就是菜单的定义了,定义格式是:
**菜单ID     MENU    [DISCARDABLE]
BEGIN
			 菜单项定义
END**
菜单ID就是文件中用define定义的那些东西,每一个ID都对应一个不同的菜单选项(就是那些文件啊,查看啊之类的),当然如果你想两个菜单的功能一样,可以用一样的ID。中括号中括起来的威可选部分,如果选了它就表示菜单在不再使用的时候就可以从内存中释放以节省内存。下面就是菜单项的定义,当我们点击窗口上的文件或者查看的菜单时往往会弹出许多选项,比如打开文件,关闭文件之类的,菜单项就是这些东西。菜单项的定义方法有三种。
  • MENUITEM 菜单文字 ,命令ID [,选项列表]

  • MENUITEM SEPARATOR

  • POPUP 菜单文字 [,选项列表]
    BEGIN
    菜单项目
    END

    第一种方法,就是定义普通的菜单项目,菜单文字就是选项上面显示的文字, ID就是上面定义的ID,这个主要是用来告诉我们的窗口过程我们选择了哪个菜单项目。选项列表就是菜单的各种属性,比如前面打个勾啊打个点什么的。选项列表有下面几个选项:

  • CHECKED——表示打上对勾

  • GRAYED——表示菜单是灰化的

  • INACTIVE——表示菜单项是禁用的

  • MENUBREAK或MENUBARBREAK——表示这个菜单项和以后的菜单项列到新的列中
    第二种方法,就是定义一个分隔线
    第三种方法,就是定义一个弹出式菜单,就是当我们把鼠标放在那个地方,它会显示出一个子菜单。
    定义完菜单项目后就是开始定义加速键,加速键就是快捷键,定义加速键的方法和定义菜单项目的方法类似,比菜单更简单一点,格式如下
    加速键ID ACCELERATORS
    BEGIN
    键名,命令ID [,类型] [,选项]

    END

  • 键名有三种方式可以定义:

    **1. “^字母”:表示Ctrl加上字母键

    1. “字母”:表示字母,这时后面的类型选项必须指定为VIRTKEY
    2. “数值”:表示ASCII码为该数值的字符,这时后面的类型必须指定为ASCII**
      (F1,Esc,这类的键名定义方式使用虚拟键,例如F1:VK_F1)
  • 然后是命令ID,要把加速键和对应的菜单项联系起来就要用相同的命令ID

  • 类型:就是用来表示键名字段是用虚拟键来定义还是用ASCII码来定义,比如类型是VIRTKEY,那么键名就是用虚拟键来定义

  • 选项:Alt,Control或Shift中的单个或多个的组合,如果是多个中间用逗号隔开,这样就表示菜单加速键是这些的组合键。
    完成了资源文件的定义后就开始写汇编源文件了,首先我们要了解建立一个窗口的基本步骤,首先,我们要注册一个窗口类,这里我们可以自己定义一个类名,然后我们用这个类名再去创建我们的窗口,这个操作有点像C++中的类和对象,注册窗口类的时候可以定义一些窗口风格,重要的是窗口过程的地址就是在注册窗口类的时候获得的(窗口过程后面会讲到)。建立完窗口后刷新窗口,然后就进入我们的消息循环。

  • 注册窗口类:RegisterClassEx,addr @stWndClass。这个函数就一个参数,但是这个参数指向一个结构,结构里却有12个参数要填,当然我们不会去记这些参数,应该没人会去记吧,一般都是要用的时候再去查。这个参数指向一个名为WNDCLASSEX的结构。
    WNDCLASSEX STRUCT
    cbsize DWORD ? ;结构体的字节数
    style DWORD ? ;类风格
    lpfnwndproc DWORD ? ;窗口过程地址
    cbclsextra DWORD ? ;
    cbwndextra DWORD ? ;
    hinstance DWORD ? ;所属实例句柄’
    hicon DWORD ? ;窗口图标
    hcursor DWORD ? ;窗口光标
    hbrbackground DWORD ? ;背景色
    lpszmenuname DWORD ? ;窗口菜单
    lpszclassname DWORD ? ;类名字符串的地址
    hiconsm DWORD ? ;小图标
    WNDCLASSEX ENDS

    上面就是这个结构的定义,其实有将近一半是我们用不着定义的,所以这里只介绍一部分。

  • 结构体的字节数,我们直接用 sizeof获取。

  • style 风格,比如CS_HREDRAWCS_VREDEAW表示窗口的宽度或高度改变的时候是否重画窗口,CS_DBLCLKS表示把两次快速单击的行为翻译成双击,其他还有很多参数可以在官网中查到.

  • lpfnwndproc 窗口过程地址,这个是我们写的窗口过程函数的地址

  • hinstance窗口所属实例句柄

  • lpszclassname 类名字符串的地址,就是我们自己定义的类名的字符串的地址
    当然在填结构体之前我们需要先把结构体中的字段都填0,这样以防有漏填的字段调用函数的时候出现错误。我们可以直接用一个函数来完成这个填0的操作:
    invoke RtlZeroMemory,addr @stWndClass,sizeof @stWndClass
    第一个参数是我们要填0的变量或结构体,第二个就是大小。

  • 完成注册窗口后就要开始建立窗口,建立窗口函数:CreateWindowEx的参数也有12个。

  • invoke CreateWindowEx,dwExStyle,lpClassName,lpWindowName,dwStyle,x,y,nWidth,nHeight,hWndParent,hMenu,hInstance,lpParam

    1. dwExStyle 风格
    2. lpClassName 窗口类的名称,就是注册窗口时用的那个类名
    3. lpWindowName 指向窗口名称的字符串,该名称将显示在标题栏上
    4. dwstyle风格
    5. x 窗口左上角的横坐标
    6. y 窗口左上角的纵坐标
    7. nWidth 窗口的宽
    8. nHeight窗口的高
    9. hWndParent窗口的父窗口的句柄
    10. 窗口的菜单句柄
    11. 实例句柄。窗口所属实例的句柄
    12. 一个指针,一般不会用到
      创建完成后会返回一个窗口句柄,记得保存。
      第一个参数,窗口风格。它定义了窗口的很多外形和行为,比如是否有滚动条,是否有最大化和最小化的按钮。窗口风格的预定义值如下:
      WS_EX_ACCEPTFILES:指定以该风格创建的窗口接受一个拖拽文件。
      WS_EX_APPWINDOW:当窗口可见时,将一个顶层窗口放置到任务条上。
      WS_EX_CLIENTEDGE:指定窗口有一个带阴影的边界。
      WS_EX_CONTEXTHELP:在窗口的标题条包含一个问号标志。当用户点击了问号时,鼠标光标变为一个问号的指针、如果点击了一个子窗口,则子窗口接收到WM_HELP消息。子窗口应该将这个消息传递给父窗口过程,父窗口再通过HELP_WM_HELP命令调用WinHelp函数。这个Help应用程序显示一个包含子窗口帮助信息的弹出式窗口。 WS_EX_CONTEXTHELP不能与WS_MAXIMIZEBOX和WS_MINIMIZEBOX同时使用。
      WS_EX_CONTROLPARENT:允许用户使用Tab键在窗口的子窗口间搜索。
      WS_EX_DLGMODALFRAME:创建一个带双边的窗口;该窗口可以在dwStyle中指定WS_CAPTION风格来创建一个标题栏。
      WS_EX_LEFT:窗口具有左对齐属性,这是缺省设置的。
      WS_EX_LEFTSCROLLBAR:如果外壳语言是如Hebrew,Arabic,或其他支持reading order alignment的语言,则标题条(如果存在)则在客户区的左部分。若是其他语言,在该风格被忽略并且不作为错误处理。
      WS_EX_LTRREADING:窗口文本以LEFT到RIGHT(自左向右)属性的顺序显示。这是缺省设置的。
      WS_EX_MDICHILD:创建一个MDI子窗口。
      WS_EX_NOPATARENTNOTIFY:指明以这个风格创建的窗口在被创建和销毁时不向父窗口发送WM_PARENTNOTFY消息。
      WS_EX_OVERLAPPEDWINDOW:WS_EX_CLIENTEDGE和WS_EX_WINDOWEDGE的组合。
      WS_EX_PALETTEWINDOW:WS_EX_WINDOWEDGE, WS_EX_TOOLWINDOW和WS_WX_TOPMOST风格的组合WS_EX_RIGHT:窗口具有普通的右对齐属性,这依赖于窗口类。只有在外壳语言是如Hebrew,Arabic或其他支持读顺序对齐(reading order alignment)的语言时该风格才有效,否则,忽略该标志并且不作为错误处理。
      WS_EX_RIGHTSCROLLBAR:垂直滚动条在窗口的右边界。这是缺省设置的。
      WS_EX_RTLREADING:如果外壳语言是如Hebrew,Arabic,或其他支持读顺序对齐(reading order alignment)的语言,则窗口文本是一自左向右)RIGHT到LEFT顺序的读出顺序。若是其他语言,在该风格被忽略并且不作为错误处理。
      WS_EX_STATICEDGE:为不接受用户输入的项创建一个3一维边界风格
      WS_EX_TOOLWINDOW:创建工具窗口,即窗口是一个游动的工具条。工具窗口的标题条比一般窗口的标题条短,并且窗口标题以小字体显示。工具窗口不在任务栏里显示,当用户按下alt+Tab键时工具窗口不在对话框里显示。如果工具窗口有一个系统菜单,它的图标也不会显示在标题栏里,但是,可以通过点击鼠标右键或Alt+Space来显示菜单。
      WS_EX_TOPMOST:指明以该风格创建的窗口应放置在所有非最高层窗口的上面并且停留在其L,即使窗口未被激活。使用函数SetWindowPos来设置和移去这个风格。
      WS_EX_TRANSPARENT:指定以这个风格创建的窗口在窗口下的同属窗口已重画时,该窗口才可以重画。由于其下的同属窗口已被重画,该窗口是透明的。

      窗口风格可以是这些预定义值的组合。
  • 然后就是消息循环。

消息循环一般由三个函数组成,GetMessage,TranslateMessage,DispatchMessage。
在介绍这三个函数之前,先看一下Windows中窗口过程的运行过程。
windows为每一个程序的每个线程维护一个消息队列,消息队列里面存放产生的消息,比如我们鼠标的点击,或者击键,或者移动鼠标,都会产生消息。这些消息放在对应的消息队列里面。如果某个消息产生于某个应用程序的窗口内,那么这个消息也会被放到这个应用程序的消息队列里。
应用程序没有来取这个消息,这个消息就一直躺这个队列里面。当程序执行到消息循环中的GetMessage函数的时候,CPU控制权就转接到该函数所在的USER32.DLL所在的地方,其实就是转到这个函数里面取去运行,只是这个函数是在系统的DLL中。
然后该函数就从消息队列里面取出一条消息,然后把这个消息返回到应用程序里面。就是说现在指令执行又回到了应用程序中。
该消息通过TranslateMessage函数转换成ASCII码,这一步可以不要,这个不是必须的。
最后消息在应用程序中来到了
DIspatchMessage*函数手里。该函数看字面意思就知道是分派,派遣消息的。该函数得到消息后,要把消息发给窗口过程,之前有说到窗口过程,窗口过程是一个函数,但是这是一个回调函数,回调函数的意思就是该函数由系统来调用,而不是我们。我们在注册窗口的时候指定了回调函数的地址,所以系统知道到哪里调用它。
DispatchMessage并不会在程序里面直接把消息传递给回调函数,因为这个函数也是处于系统DLL内的,所以该消息严格来讲,现在是处于USER32.DLL中,而因为一个程序可能有多个窗口,该函数判断消息是哪个窗口的之后。从系统中将该消息发给窗口过程,也就是窗口回调函数。
最后窗口过程处理完消息之后,就返回到USER32.DLL的DIspatchMessge中,然后再返回到消息循环开始位置。这样,一个循环就结束了。
注册窗口和消息循环一般会写再一个函数里面,定义一般如下:

_WinMain		proc
				local 	@stWndClass:WNDCLASSEX
				local 	@stMsg:MSG
				local	@stWndClassChild:WNDCLASSEX
				local	@stMsgChild:MSG
				
				invoke	GetModuleHandle,NULL						;获取模块句柄
				mov 	hInstance,eax
				invoke	RtlZeroMemory,addr @stWndClass,sizeof @stWndClass;数据结构所有字段初始化为0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;注册窗口类
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
				invoke  LoadCursor,0,IDC_HAND						;定义光标的样子
				mov		@stWndClass.hCursor,eax
				push	hInstance
				pop		@stWndClass.hInstance						;获得模块句柄
				mov		@stWndClass.cbSize,sizeof WNDCLASSEX		;结构的长度
				mov		@stWndClass.style,CS_HREDRAW or CS_VREDRAW or CS_DBLCLKS	;定义窗口风格
				mov     @stWndClass.lpfnWndProc,offset _ProcWinMain	;获取窗口过程的地址
				mov		@stWndClass.hbrBackground,COLOR_WINDOW + 1	;刷子
				mov		@stWndClass.lpszClassName,offset szClassName;类名
				invoke	RegisterClassEx,addr @stWndClass			;注册窗口
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;注册子窗口类
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
				invoke  LoadCursor,0,IDC_UPARROW						;定义光标的样子
				mov		@stWndClassChild.hCursor,eax
				push	hInstance
				pop		@stWndClassChild.hInstance						;获得模块句柄
				mov		@stWndClassChild.cbSize,sizeof WNDCLASSEX		;结构的长度
				mov		@stWndClassChild.style,CS_HREDRAW or CS_VREDRAW or CS_DBLCLKS	;定义窗口风格
				mov     @stWndClassChild.lpfnWndProc,offset _ProcWinMain;获取窗口过程的地址
				mov		@stWndClassChild.hbrBackground,COLOR_WINDOW + 3	;刷子
				mov		@stWndClassChild.lpszClassName,offset szClassNameChild;类名
				invoke	RegisterClassEx,addr @stWndClassChild			;注册窗口
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;建立并显示窗口
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
				invoke  CreateWindowEx,NULL,offset szClassName,\
						offset szCaptionMain,WS_MAXIMIZEBOX or WS_MINIMIZEBOX or WS_SYSMENU or WS_THICKFRAME or WS_VSCROLL or WS_HSCROLL,\
						100,100,600,400,\
						NULL,NULL,hInstance,NULL
						;窗口风格,窗口的类的名称,窗口的标题,窗口风格,窗口坐标,窗口大小,窗口所属父窗口,菜单,模块句柄,指针
				mov		hWinMain,eax								;获取窗口句柄
				invoke  ShowWindow,hWinMain,SW_SHOWNORMAL			;显示窗口
				invoke  UpdateWindow,hWinMain						;刷新窗口
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;建立子窗口
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
				invoke  CreateWindowEx,WS_EX_WINDOWEDGE,offset szClassNameChild,\
						offset szText,WS_CHILD,\
						50,10,100,100,\
						hWinMain,NULL,hInstance,NULL
						;窗口风格,窗口的类的名称,窗口的标题,窗口风格,窗口坐标,窗口大小,窗口所属父窗口,菜单,模块句柄,指针
				mov		hWinMainChild,eax								;获取窗口句柄
				invoke  ShowWindow,hWinMainChild,SW_SHOWNORMAL			;显示窗口
				invoke  UpdateWindow,hWinMainChild						;刷新窗口
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;按钮设置
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
				invoke	CreateWindowEx,NULL,\
						offset	szButton,offset szButtonText,\
						WS_CHILD or WS_VISIBLE,\
						10,300 ,65,22,\
						hWinMain,NULL,hInstance,NULL
				mov		hWinMainButton,eax
				;invoke	ShowWindow,hWinMainButton,SW_SHOWNORMAL
				;invoke	UpdateWindow,hWinMainButton
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;子窗口的按钮
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
				invoke CreateWindowEx,NULL,\
						offset	szButton,offset szButtonChileText,\
						WS_CHILD or WS_VISIBLE,\
						22,70,60,18,\
						hWinMainChild,NULL,hInstance,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;文本框窗口设置
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
				invoke	CreateWindowEx,NULL,\
						offset	szEdit,offset szEditText,\
						WS_CHILD or WS_VISIBLE,\
						100,300,65,22,\
						hWinMain,NULL,hInstance,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
;消息循环
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
				.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

这里增加了一个子窗口的定义,子窗口的建立方法和窗口的一样,只是创建窗口的时候参数里要标明父窗口句柄。
然后窗口过程,也就是回调函数的定义一般如下:

_ProcWinMain	proc	uses ebx edi esi,hWnd,uMsg,wParam,lParam	;服务窗口的句柄,消息标识,参数,参数
				local   @stPs:PAINTSTRUCT
				local	@stRect:RECT
				local	@hDc
	
				mov	eax,uMsg
				.if eax == WM_CLOSE
					invoke DestroyWindow,hWinMain					;销毁窗口
					invoke PostQuitMessage,NULL						;退出程序
				.elseif 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
				.else
					invoke DefWindowProc,hWnd,uMsg,wParam,lParam
					ret
				.endif
				xor ax,ax
				ret
_ProcWinMain	endp

回调函数有着固定的格式,规定为四个参数。
第一个参数hWnd是产生该消息的窗口的句柄。第二个参数uMsg是消息标识,表示该消息的类型。后面两个参数会因为消息的类型不同而有点不同。
要注意窗口过程中有一个函数:DefWindowProc。该函数就是帮我们处理我们不想处理的消息,因为在窗口中,就算是鼠标移动一下或者随便点击一下都会产生消息。如果所有的消息都我们自己来处理的话,那就不是上面这些代码能解决的了。该函数会以默认的方式处理我们不处理的消息。
如果我们要关闭窗口,可以使用
DestroyWindow函数,该函数只有一个参数,就是要销毁的窗口的句柄。
DestroyWindow函数只是销毁窗口,如果我们没有调用
PostQuitMessage* 函数来退出消息循环的话,就算窗口销毁了,消息循环还在继续,所以一定要记得使用这个函数。
最后,只要在程序开始的地方调用消息循环所在的函数就可以了。
将上面的代码编译链接后运行一下:
用32位汇编语言写一个窗口程序_第1张图片大功告成!

你可能感兴趣的:(WIN32)