-
-
[study-note]Windows环境下32位汇编语言程序设计--第4章 第一个窗口程序4.1 4.2 分析窗口程序(1)
-
发表于: 2008-4-17 00:43 3968
-
第4章 第一个窗口程序
4.2 分析窗口程序(1)
了解了消息驱动体系的工作流程以后,让我们来分析如何用Win32汇编实现这一切,本节和下一节将详细分析FirstWindow源程序。
4.2.1 模块和句柄
1. 模块的概念
一个模块代表的是一个运行中的exe文件或dll文件,用来代表这个文件中所有的代码和资源,磁盘上的文件不是模块,装入内存后运行时就叫做模块。一个应用程序调用其他DLL中的API时,这些DLL文件被装入内存,就产生了不同的模块,为了区分地址空间中的不同模块,每个模块都有一个惟一的模块句柄来标识。(作者这里对模块的定义下得比较好,只是我有个疑问,就是应用程序调用其它的DLL中的API时,这些DLL就会装入内存,就产生了不同的模块,是不是就是说调用其它文件的DLL就会产生不同的模块,那相同的模块是什么?指的是没有调用其它DLL中的API的应用程序吗?)
很多API函数中都要用到程序的模块句柄,以便利用程序中的各种资源,所以在程序的一开始就先取得模块句柄并存放到一个全局变量中可以省去很多的麻烦,在Win32中,模块句柄在数值上等于程序在内存中装入的起始地址。(因为要利用程序中的各种资源,所以要获取模块的句柄,且模块句柄就是程序在内存中的偏移地址,疑问就是资源有很多,如位图等,程序是如何通过句柄来获取它的资源的呢?不会就是简单的调用API来获取这些资源吧?)
取模块句柄使用的API函数是GetModuleHandle,它的使用方法是:
invoke GetModuleHandle,lpModuleName (获取模块句柄的API参数只有一个,但其功能强大之极不必多说.不知道除了这个API,还可以有其它的方法可以获取模块的句柄吗?)
lpModuleName参数是一个指向含有模块名称字符串的指针,可以用这个函数取得程序地址空间中各个模块的句柄,例如,如果想得到User32.dll的句柄以便使用其中包含的图标资源,那么可以如下使用:(实在没有想到,一个DLL里面还会包含位图资源,原以为DLL里面仅仅包含一些代码,没有想到还包含着位图资源,又重新认识了一次DLL.模块名称指的是应用程序的名字或DLL的名字吗?根据所给的模块名称,GetModuleHandle就可以自动在内存中搜索并返回相应的模块句柄了吗?很想知道这个API是怎么实现这个功能的,我想这个应该涉及操作系统的知识了吧.即操作系统管理着内存中所有的模块及其名称.)
szUserDll db 'User32.dll',0
…
invoke GetModuleHandle,addr szUserDll
.if eax
mov hUserDllHandle,eax
.endif
…(模块名称说白了就是一个字符串,或是应用程序的名称,或是DLL的名称,它们都是以0结尾的。)
如果使用参数NULL调用GetModuleHandle,那么得到的是调用者本模块的句柄,我们的源程序中就是这样使用的:
invoke GetModuleHandle,NULL
mov hInstance,eax
可以注意到,把返回的句柄放到了hInstance变量里而并不是放在hModule中,为什么是 hInstance呢? Instance是“实例”,它的概念来自于Win16,Win16中不同运行程序的地址空间并非是完全隔离的,一个可执行文件运行后形成“模块”,多次加载同一个可执行文件时,这个“模块”是公用的,为了区分多次加载的“拷贝”,就把每个“拷贝”叫做实例,每个实例均用不同的“实例句柄”(hInstance)值来标识它们。(不同的程序的地址空间并非完全隔离指的是程序的某些东西是为多个程序共用的,如资源和代码。只是有可能一整个模块都公用的吗?有可能,如user32.dll就有可能被所有的程序共用.)
但在Win32中,程序运行时是隔离的,每个实例都使用自己私有的4 GB空间,都认为自己是惟一的,不存在一个模块的多个实例的问题,实际上在Win32中,实例句柄就是模块句柄,但很多API原型中用到模块句柄的时候使用的名称还是沿用hInstance,所以我们还是把变量名称取为hInstance。(在这里知道了两个非常重要的信息:一是程序多次载入内存后,只有一个模块,即只有一个实例,即实例句柄就是模块句柄。但这和这段话前面第一句话,每个实例都使用自己私有的4GB空间是什么意思?难道内存中的一个模块存在多个实例吗?这里涉及到了CPU的工作模式了。我对这句话,理解不是很清晰,还请不小心看到这里的文字的高人指点迷津。)
在C语言的编程中,hInstance通过WinMain由系统传入,WinMain的原型是:
WinMain(hInstance,hPrevInstance,lpzCmdParam,nCmdShow),程序不用自己去获得hInstance,但在Win32汇编中必须自己获取,如果不了解hModule就是hInstance的话,就无法得知如何得到hInstance,因为并没有一个类似于GetInstanceHandle之类的API函数。(汗,这里说的C语言编程居然是在windows平台下进行的。感觉程序有点莫名其妙,在我的脑海里c语言写的就是在DOS平台下运行的程序。用C写的程序可以直接读写显存,在WINDOWS下编程可以直接读写显存吗?如果只可以,如何实现.还请高人指点。)
2. 句柄是什么
随着分析的深入,句柄(handle)一词也出现得频繁了起来,“句柄”是什么呢?句柄只是一个数值而已,它的值对程序来说是没有意义的,它只是Windows用来表示各种资源的编号而已,所以只有Windows才知道怎么使用它来引用各种资源。(句柄是一个对程序来说没有意义的数值,因为只有windows才知道怎么使用它来引用各种资源,至于windows是如何通过句柄来引用模块中的各种资源的呢?这对我来说一个是件非常非常神秘的事。)
举例说明,屏幕上已经有10个窗口,Windows把它们从1到10编号,应用程序又建立了一个窗口,现在Windows把它编号为11,然后把11当做窗口句柄返回给应用程序,应用程序并不知道11代表的是什么,但在操作窗口的时候,把11当做句柄传给Windows,Windows自然可以根据这个数值查出是哪个窗口。当该窗口关闭的时候,11这个编号作废。第二次运行的时候,如果屏幕上现有5个窗口,那么现在句柄可能就是6了,所以,应用程序并不用关心句柄的具体数值是多少。打个比方,可以把句柄当做是商场中寄放书包时营业员给的纸条,纸条上的标记用户并不知道是什么意思,但把它交还给营业员的时候,她自然会找到正确的书包。(句柄是一个编号,虽然用它描述句柄有点土,但似乎没有什么比用它来理解句柄更好的了。这里举的三个例子,非常形象生动的说明了什么是编号以及编号的作用是什么。)
Windows中几乎所有的东西都是用句柄来标识的,文件句柄、窗口句柄、线程句柄和模块句柄等,同样道理,不必关心它们的值究竟是多少,拿来用就是了!(看到这句话,觉得句柄仅仅是一个编号吗?我问过一些编程达人,他们告诉我句柄有的是编号,有的是指针,到底这种说法有没有问题?还是指针也是一个编号,只是它是由偏移地址决定的呢?)
4.2.2 创建窗口
在创建窗口之前,先要谈到“类”。“类”的概念读者都不陌生,主要是为了把一组物体的相同属性归纳整理起来封装在一起,以便重复使用,在“类”已定义的属性基础上加上其他个性化的属性,就形成了各式各样的个体。(这里涉及了三个很重要的概念:类、属性和个体。只要将类里的属性赋予一些值,就可以得到一个个体了。一个类可以生成多个个体,而一个个体能由多个类生成吗?类指的是结构体类型吧,如果是这样,那嵌套定义结构体类型,不就可以实现由多个类生成一个个体了吗?)
Windows中创建窗口同样使用这样的层次结构。首先定义一个窗口类,然后在窗口类的基础上添加其他的属性建立窗口。不用一步到位的办法是因为很多窗口的基本属性和行为都是一样的,如按钮、文本输入框和选择框等,对这些东西Windows都预定义了对应的类,使用时直接使用对应的类名建立窗口就可以了。只有用户自定义的窗口才需要先定义自己的类,再建立窗口。这样可以节省资源。(使用一步到位的办法来创建窗口就是直接使用WINDOWS预定义的类来创建窗口,这样做的原因是因为很多窗口的基本属性和行为都是一样的,MS为了减轻程序员的负担以及提高开发软件的效率,于是就帮用户定义了很多窗口的类,我们可以直接使用这些类来创建窗口。而不使用一步到位的办法来创建窗口就是使用自己定义的窗口类来创建窗口。因为有了很多预定义的类,所以我们只在需要的时候才定义我们的窗口类,因为使用了预定义的类,所以我们不需要在程序中再给这些类的属性来一个个的赋值,这样做的结果是节省了系统资源,节省了内存和CPU,这样说对吗?对于我这样的初学者来说,要理解这句话,可真的不容易啊。)
1. 注册窗口类
建立窗口类的方法是在系统中注册,注册窗口类的API函数是RegisterClassEx,最后的“Ex”是扩展的意思,因为它是Win16中RegisterClass的扩展。一个窗口类定义了窗口的一些主要属性,如:图标、光标、背景色、菜单和负责处理该窗口所属消息的函数。这些属性并不是分成多个参数传递过去的,而是定义在一个WNDCLASSEX结构中,再把结构的地址当参数一次性传递给RegisterClassEx,WNDCLASSEX是WNDCLASS结构的扩展。(这里最容易理解的就是扩展了,可以是结构体类型的扩展,也可以是函数功能的扩展。以下为windows.h中扩展窗口类结构体类型的定义,及一个经典的给窗口类变量赋值的例子。)
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
在FirstWindow程序中,注册窗口类的代码是:
local @stWndClass:WNDCLASSEX ;定义一个WNDCLASSEX结构
…
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
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
本资料来自罗云彬的WIN32汇编教程,本人只是添加一点点注解而已(见括号内容),而这仅是个人见解,如有雷同,纯属巧合。如有不同见解,也可发表出来,大家一起讨论,本文章最终解释权归罗去彬所有。谢谢。
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
4.2 分析窗口程序(1)
了解了消息驱动体系的工作流程以后,让我们来分析如何用Win32汇编实现这一切,本节和下一节将详细分析FirstWindow源程序。
4.2.1 模块和句柄
1. 模块的概念
一个模块代表的是一个运行中的exe文件或dll文件,用来代表这个文件中所有的代码和资源,磁盘上的文件不是模块,装入内存后运行时就叫做模块。一个应用程序调用其他DLL中的API时,这些DLL文件被装入内存,就产生了不同的模块,为了区分地址空间中的不同模块,每个模块都有一个惟一的模块句柄来标识。(作者这里对模块的定义下得比较好,只是我有个疑问,就是应用程序调用其它的DLL中的API时,这些DLL就会装入内存,就产生了不同的模块,是不是就是说调用其它文件的DLL就会产生不同的模块,那相同的模块是什么?指的是没有调用其它DLL中的API的应用程序吗?)
很多API函数中都要用到程序的模块句柄,以便利用程序中的各种资源,所以在程序的一开始就先取得模块句柄并存放到一个全局变量中可以省去很多的麻烦,在Win32中,模块句柄在数值上等于程序在内存中装入的起始地址。(因为要利用程序中的各种资源,所以要获取模块的句柄,且模块句柄就是程序在内存中的偏移地址,疑问就是资源有很多,如位图等,程序是如何通过句柄来获取它的资源的呢?不会就是简单的调用API来获取这些资源吧?)
取模块句柄使用的API函数是GetModuleHandle,它的使用方法是:
invoke GetModuleHandle,lpModuleName (获取模块句柄的API参数只有一个,但其功能强大之极不必多说.不知道除了这个API,还可以有其它的方法可以获取模块的句柄吗?)
lpModuleName参数是一个指向含有模块名称字符串的指针,可以用这个函数取得程序地址空间中各个模块的句柄,例如,如果想得到User32.dll的句柄以便使用其中包含的图标资源,那么可以如下使用:(实在没有想到,一个DLL里面还会包含位图资源,原以为DLL里面仅仅包含一些代码,没有想到还包含着位图资源,又重新认识了一次DLL.模块名称指的是应用程序的名字或DLL的名字吗?根据所给的模块名称,GetModuleHandle就可以自动在内存中搜索并返回相应的模块句柄了吗?很想知道这个API是怎么实现这个功能的,我想这个应该涉及操作系统的知识了吧.即操作系统管理着内存中所有的模块及其名称.)
szUserDll db 'User32.dll',0
…
invoke GetModuleHandle,addr szUserDll
.if eax
mov hUserDllHandle,eax
.endif
…(模块名称说白了就是一个字符串,或是应用程序的名称,或是DLL的名称,它们都是以0结尾的。)
如果使用参数NULL调用GetModuleHandle,那么得到的是调用者本模块的句柄,我们的源程序中就是这样使用的:
invoke GetModuleHandle,NULL
mov hInstance,eax
可以注意到,把返回的句柄放到了hInstance变量里而并不是放在hModule中,为什么是 hInstance呢? Instance是“实例”,它的概念来自于Win16,Win16中不同运行程序的地址空间并非是完全隔离的,一个可执行文件运行后形成“模块”,多次加载同一个可执行文件时,这个“模块”是公用的,为了区分多次加载的“拷贝”,就把每个“拷贝”叫做实例,每个实例均用不同的“实例句柄”(hInstance)值来标识它们。(不同的程序的地址空间并非完全隔离指的是程序的某些东西是为多个程序共用的,如资源和代码。只是有可能一整个模块都公用的吗?有可能,如user32.dll就有可能被所有的程序共用.)
但在Win32中,程序运行时是隔离的,每个实例都使用自己私有的4 GB空间,都认为自己是惟一的,不存在一个模块的多个实例的问题,实际上在Win32中,实例句柄就是模块句柄,但很多API原型中用到模块句柄的时候使用的名称还是沿用hInstance,所以我们还是把变量名称取为hInstance。(在这里知道了两个非常重要的信息:一是程序多次载入内存后,只有一个模块,即只有一个实例,即实例句柄就是模块句柄。但这和这段话前面第一句话,每个实例都使用自己私有的4GB空间是什么意思?难道内存中的一个模块存在多个实例吗?这里涉及到了CPU的工作模式了。我对这句话,理解不是很清晰,还请不小心看到这里的文字的高人指点迷津。)
在C语言的编程中,hInstance通过WinMain由系统传入,WinMain的原型是:
WinMain(hInstance,hPrevInstance,lpzCmdParam,nCmdShow),程序不用自己去获得hInstance,但在Win32汇编中必须自己获取,如果不了解hModule就是hInstance的话,就无法得知如何得到hInstance,因为并没有一个类似于GetInstanceHandle之类的API函数。(汗,这里说的C语言编程居然是在windows平台下进行的。感觉程序有点莫名其妙,在我的脑海里c语言写的就是在DOS平台下运行的程序。用C写的程序可以直接读写显存,在WINDOWS下编程可以直接读写显存吗?如果只可以,如何实现.还请高人指点。)
2. 句柄是什么
随着分析的深入,句柄(handle)一词也出现得频繁了起来,“句柄”是什么呢?句柄只是一个数值而已,它的值对程序来说是没有意义的,它只是Windows用来表示各种资源的编号而已,所以只有Windows才知道怎么使用它来引用各种资源。(句柄是一个对程序来说没有意义的数值,因为只有windows才知道怎么使用它来引用各种资源,至于windows是如何通过句柄来引用模块中的各种资源的呢?这对我来说一个是件非常非常神秘的事。)
举例说明,屏幕上已经有10个窗口,Windows把它们从1到10编号,应用程序又建立了一个窗口,现在Windows把它编号为11,然后把11当做窗口句柄返回给应用程序,应用程序并不知道11代表的是什么,但在操作窗口的时候,把11当做句柄传给Windows,Windows自然可以根据这个数值查出是哪个窗口。当该窗口关闭的时候,11这个编号作废。第二次运行的时候,如果屏幕上现有5个窗口,那么现在句柄可能就是6了,所以,应用程序并不用关心句柄的具体数值是多少。打个比方,可以把句柄当做是商场中寄放书包时营业员给的纸条,纸条上的标记用户并不知道是什么意思,但把它交还给营业员的时候,她自然会找到正确的书包。(句柄是一个编号,虽然用它描述句柄有点土,但似乎没有什么比用它来理解句柄更好的了。这里举的三个例子,非常形象生动的说明了什么是编号以及编号的作用是什么。)
Windows中几乎所有的东西都是用句柄来标识的,文件句柄、窗口句柄、线程句柄和模块句柄等,同样道理,不必关心它们的值究竟是多少,拿来用就是了!(看到这句话,觉得句柄仅仅是一个编号吗?我问过一些编程达人,他们告诉我句柄有的是编号,有的是指针,到底这种说法有没有问题?还是指针也是一个编号,只是它是由偏移地址决定的呢?)
4.2.2 创建窗口
在创建窗口之前,先要谈到“类”。“类”的概念读者都不陌生,主要是为了把一组物体的相同属性归纳整理起来封装在一起,以便重复使用,在“类”已定义的属性基础上加上其他个性化的属性,就形成了各式各样的个体。(这里涉及了三个很重要的概念:类、属性和个体。只要将类里的属性赋予一些值,就可以得到一个个体了。一个类可以生成多个个体,而一个个体能由多个类生成吗?类指的是结构体类型吧,如果是这样,那嵌套定义结构体类型,不就可以实现由多个类生成一个个体了吗?)
Windows中创建窗口同样使用这样的层次结构。首先定义一个窗口类,然后在窗口类的基础上添加其他的属性建立窗口。不用一步到位的办法是因为很多窗口的基本属性和行为都是一样的,如按钮、文本输入框和选择框等,对这些东西Windows都预定义了对应的类,使用时直接使用对应的类名建立窗口就可以了。只有用户自定义的窗口才需要先定义自己的类,再建立窗口。这样可以节省资源。(使用一步到位的办法来创建窗口就是直接使用WINDOWS预定义的类来创建窗口,这样做的原因是因为很多窗口的基本属性和行为都是一样的,MS为了减轻程序员的负担以及提高开发软件的效率,于是就帮用户定义了很多窗口的类,我们可以直接使用这些类来创建窗口。而不使用一步到位的办法来创建窗口就是使用自己定义的窗口类来创建窗口。因为有了很多预定义的类,所以我们只在需要的时候才定义我们的窗口类,因为使用了预定义的类,所以我们不需要在程序中再给这些类的属性来一个个的赋值,这样做的结果是节省了系统资源,节省了内存和CPU,这样说对吗?对于我这样的初学者来说,要理解这句话,可真的不容易啊。)
1. 注册窗口类
建立窗口类的方法是在系统中注册,注册窗口类的API函数是RegisterClassEx,最后的“Ex”是扩展的意思,因为它是Win16中RegisterClass的扩展。一个窗口类定义了窗口的一些主要属性,如:图标、光标、背景色、菜单和负责处理该窗口所属消息的函数。这些属性并不是分成多个参数传递过去的,而是定义在一个WNDCLASSEX结构中,再把结构的地址当参数一次性传递给RegisterClassEx,WNDCLASSEX是WNDCLASS结构的扩展。(这里最容易理解的就是扩展了,可以是结构体类型的扩展,也可以是函数功能的扩展。以下为windows.h中扩展窗口类结构体类型的定义,及一个经典的给窗口类变量赋值的例子。)
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
在FirstWindow程序中,注册窗口类的代码是:
local @stWndClass:WNDCLASSEX ;定义一个WNDCLASSEX结构
…
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
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
本资料来自罗云彬的WIN32汇编教程,本人只是添加一点点注解而已(见括号内容),而这仅是个人见解,如有雷同,纯属巧合。如有不同见解,也可发表出来,大家一起讨论,本文章最终解释权归罗去彬所有。谢谢。
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
他的文章
看原图
赞赏
雪币:
留言: