1、第 6 章 Windows 编程6.1 简答题(1 )什么是应用程序接口(API )?API 是一些类型、常量和函数的集合,提供了编程中使用的库函数的途径。(2 )什么是静态连接?连接程序从库文件中抽取需要的子程序插入到最终的可执行代码中,叫做静态连接。(3 )运行 Windows 应用程序,有时为什么会提示某个 DLL 文件不存在?Windows 程序在运行时需要加载其配套的动态连接库 DLL 文件,当其没有被搜索到时就会提示不存在。(4 ) ADDR 与 OFFSET 有何不同?都是地址操作符,后接标号或变量名表示它们的地址。但是 addr 只用在 invoke 语句中,获取局部变量的地址
2、。 Offset 只能获取全局变量的偏移地址。(5 ) ExitProcess 函数可以按汇编语言习惯全部使用小写字母表示吗?不能,因为 Windows 的 API 函数按照 C 语言习惯区别大小写字母,是不同的(6 ) Win32 API 中可以使用哪两种字符集?8 位的 ASCII 字符集和 16 位的 Unicode 字符集(7 )为什么调用 API 函数之后, ECX 等寄存器改变了?因为 API 函数并不是按照汇编语言的规则编写的,它的规则是不保护它们(8 )条件控制“.IF”伪指令的条件是在汇编阶段进行判断吗?不是。条件控制伪指令在会变阶段要转换为一组功能相当等比较、测试喝转移指令
3、,是在执行阶段进行判断的。(9 )为什么 32 位 api 函数的地址指针也可以转换为汇编语言的双字类型?32 位 api 函数的地址指针与汇编语言的双字类型 DWORD 相对应(10 )在 masm32 软件包支持下的汇编语言程序中为什么没有看到对 windows 常量、函数等的定义和声明?对 windows 常量、函数等的定义和声明已经包含在 windows.inc、kerne;32.inc 及 user32.inc等文件中。6.2 判断题(1 ) Windows 可执行文件中包含动态连接库中的代码。错,不含,运行时才加载(2 )导入库文件和静态子程序库文件的扩展名都是.lib,所以两者性
4、质相同。错,导入库中记录的是动态连接库中函数等的名称及存储位置等信息,不含执行代码。(3 ) INVOKE 语句只能传递主存操作数,不能传递寄存器值。错,可以使用寄存器参数(4 ) Windows 控制台是命令行窗口,也就是 MS-DOS 窗口。错,Windows 控制台与 DOS 窗口本质不同(5 )与高级语言类似,汇编语言中使用结构变量也需要先说明结构类型对(6 ) proc 伪指令可以使用 uses 操作符,但是 proto 伪指令不可以使用。对(7 )在宏定义中,local 伪指令声明标识符;而在过程定义中,local 伪指令用于分配局部变量。对(8 )条件汇编 IF 和条件控制 .I
5、F 伪指令都包括条件表达式,它们的表达形式一样。对(9 )条件控制.IF 伪指令和循环控制伪指令.WHILE 中的条件表达式具有相同的表达形式。对(10 )masm32 软件包只支持 32 位图形界面应用程序的开发,不支持控制台应用程序的开发。错。6.3 填空题(1 ) Windows 系统有 3 个最重要的系统动态连接库文件,它们是_、_和_。KERNEL32.DLL,USER32.DLL,GDI32.DLL(2 )进行 windows 应用程序开发时,需要( )库文件;执行该应用程序时,则需要对应的( )库文件。导入库,动态链接库(3 )获得句柄函数 GetStdHandle 执行结束,使
6、用_ 提供返回结果。EAX(4 )函数 GetStdHandle 需要一个参数,对标准输入设备应该填入( )数值,对标准输出设备应该填入 ( )数值,对边准错误输出设备应该填入( )数值-10,-11,-12(5 )调用 ReadConsole 函数时,用户在键盘上按下数字 8,然后回车,则键盘缓冲区的内容一次是( ) 。38h,0dh,0ah(6 ) WriteConsole 和 ReadConsole 函数的参数类似,都有 5 个,第 1 个参数是_,第2 个参数是输出或输入缓冲区的_,第 3 个参数是输出或输入的字符_ ,第 4 个参数指向实际输出或输入字符个数的变量,最后 1 个参数一
7、般要求代入 _。句柄实例,地址,个数,0(7 )消息窗口函数 MessageBox 有 4 个参数,第 1 个是 0,第 2 个是要显示字符串的() ,第 3 个是()的地址指针,第 4 个参数指明窗口形式。注意字符串要使用()作为结尾标志。地址指针(即首地址) ,窗口标题,0(8 )要使用获取系统日期时间函数 GetLocalTime,需要定义一个()结构变量,其中返回系统时间数值,这些数值采用 2 进制编码,例如,日期返回的编码是 0019h,它表示日期是() 。SYSTEMTILE,25(9 )使用扩展的 proc 伪指令编写子程序比较方便,例如,子程序中需要保护和恢复 esi和 edi
8、 寄存器,就只需要使用()既可以。Uses esi edi(10 )masm 进行汇编时生成最大化源代码列表,其中语句前使用字母()表示是通过包含文件插入的语句,使用“*”符号的语句是()的代码,而语句前的数字则说明是()语句。C,汇编程序生成,宏调用习题 6.4执行 CPUID 指令,直接使用控制台输出函数将处理器识别字符串显示出来。.686.model flat,stdcalloption casemap:noneincludelib binkernel32.libExitProcess proto,:dwordGetStdHandle proto,:dword WriteConsoleA
9、proto,:dword,:dword,:dword,:dword,:dword WriteConsole equ STD_OUTPUT_HANDLE = -11.dataouthandle dword ?outbufferbyte The processor , 12 dup(0)outbufsize = sizeof outbuffer outsize dword ?.codemov eax,0cpuid ;执行处理器识别指令mov dword ptr outbuffer+outbufsize-12,ebxmov dword ptr outbuffer+outbufsize-8,edxmo
10、v dword ptr outbuffer+outbufsize-4,ecxinvoke GetStdHandle,STD_OUTPUT_HANDLEmov outhandle,eaxinvoke WriteConsole,outhandle,addr outbuffer,outbufsize,addr outsize,0invoke ExitProcess,0习题 6.5直接使用控制台输入和输出函数实现例 6-2 的功能(不使用 readmsg,dispmsg).注意,输入和输出句柄只要各获取一个既可。.686.model flat,stdcalloption casemap:noneinc
11、ludelib binkernel32.libExitProcess proto,:dwordexit macro dwexitcodeinvoke ExitProcess,dwexitcodeendmGetStdHandle proto,:dwordWriteConsoleA proto,:dword,:dword,:dword,:dword,:dwordWriteConsole equ ReadConsoleA proto,:dword,:dword,:dword,:dword,:dwordReadConsole equ STD_INPUT_HANDLE = -10STD_OUTPUT_H
12、ANDLE = -11.datamsg1 byte Please enter your name: ,0msg2 byte Welcome ,0nbuf byte 80 dup(0)msg3 byte to Win32 Console!,0_outhandle dword ?_inhandle dword ?_insize dword ?_outsize dword ?.codestart:invoke GetStdHandle,STD_OUTPUT_HANDLEmov _outhandle,eaxinvoke GetStdHandle,STD_INPUT_HANDLEmov _inhandl
13、e,eaxinvoke WriteConsole,_outhandle,addr msg1,sizeof msg1,addr _outsize,0invoke ReadConsole,_inhandle,addr nbuf,80,addr _insize, 0invoke WriteConsole,_outhandle,addr msg2,sizeof msg2,addr _outsize,0sub _insize,2invoke WriteConsole,_outhandle,addr nbuf,_insize,addr _outsize,0invoke WriteConsole,_outh
14、andle,addr msg3,sizeof msg3,addr _outsize,0exit 0end start习题 6.6直接使用控制台输出函数实现某个主存区域内容的显示。要求改进显示形式,例如,每行显示 16 个字节(128 位) ,每行开始先显示首个主存单元的偏移地址,然后用冒号分隔主存内容。.datavar byte This is a test!,ABCDEFG,0123456789_outsize dword ?_outhandle dword ?_membuffer byte 57 dup(20h),13,10.codestart: mov eax,offset varmov
15、 ecx,sizeof varcall dispmeminvoke ExitProcess,0dispmem proctest ecx,ecx ;个数为 0,不显示jz dispm11 ;退出pushadmov esi,ecx ;ESI=要显示内容的字节数mov edi,eax ;EDI=要显示内容的地址invoke GetStdHandle,STD_OUTPUT_HANDLEmov _outhandle,eax ;获得输出句柄;1.显示缓冲区全部填充为空格dispm1: xor ebx,ebx ;指示显示缓冲区dispm2: mov _membufferebx, inc ebxcmp ebx
16、,(sizeof _membuffer)-2jb dispm2;2.显示内容所在的存储器地址xor ebx,ebxmov ecx,8 ;地址是十六进制 8 位mov eax,edidispm3: rol eax,4mov dl,aland dl,0fhor dl,30hcmp dl,39hjbe dispm4add dl,7dispm4: mov _membufferebx,dlinc ebxloop dispm3mov _membufferebx,: ;显示冒号add ebx,2mov ecx,16 ;一行最多显示 16 个字节;3.显示一个字节内容dispm5: mov al,edimov
17、 dl,alshr dl,4or dl,30hcmp dl,39hjbe dispm6add dl,7dispm6: mov _membufferebx,dlinc ebxand al,0fhor al,30hcmp al,39hjbe dispm7add al,7dispm7: mov _membufferebx,aladd ebx,2inc edi ;指向下一个要显示的字节dec esijz dispm10 ;没有要显示的内容,退出loop dispm5invoke WriteConsole,_outhandle,addr _membuffer,sizeof _membuffer,addr
18、 _outsize,0jmp dispm1dispm10: invoke WriteConsole,_outhandle,addr _membuffer,sizeof _membuffer,addr _outsize,0popad dispm11: retdispmem endp习题 6.7执行 CPUID 指令,在消息窗口显示处理器识别字符串,要求该消息窗有 OK 和 Cancel 两个按钮。MessageBoxA proto :dword,:dword,:dword,:dwordMessageBox equ NULL equ 0MB_OK equ 1.dataszCaptionbyte 消
19、息窗口,0outbufferbyte 本机的处理器是, 12 dup(0),0outbufsize = sizeof outbuffer-1.codestart: mov eax,0cpuid ;执行处理器识别指令mov dword ptr outbuffer+outbufsize-12,ebxmov dword ptr outbuffer+outbufsize-8,edxmov dword ptr outbuffer+outbufsize-4,ecxinvoke MessageBox,NULL,addr outbuffer,addr szCaption,MB_OKinvoke ExitPro
20、cess,NULLend start习题 6.8参考 5-10,利用 MessageBox 函数创建的消息窗口显示 32 位通用寄存器内容。.686.model flat,stdcalloption casemap:noneincludelib libkernel32.libincludelib libuser32.libExitProcess proto,:dwordexit macro dwexitcodeinvoke ExitProcess,dwexitcodeendmGetStdHandle proto,:dwordWriteConsoleA proto,:dword,:dword,:
21、dword,:dword,:dwordWriteConsole equ MessageBoxA proto,:dword,:dword,:dword,:dwordMessageBox equ STD_INPUT_HANDLE = -10STD_OUTPUT_HANDLE = -11;宏定义dreg32 macro reg32local dreg1,dreg2mov eax,reg32;显示 reg32 寄存器mov ecx,8 xor ebx,ebxdreg1: rol eax,4mov edx,eaxand dl,0fhadd dl,30h ;转化为相应的 ASCII 码值cmp dl,39
22、h ;区别 0 9 和 AF 数码jbe dreg2add dl,7dreg2: mov rd假设一些数据mov ebx,0abcdef00hmov ecx,eaxmov edx,ebxmov esi,11111111hmov edi,22222222hmov ebp,espcall dprdexit 0dprdprocpushadpush edxpush ecxpush ebxdreg32 eax ;显示 EAXpop ebxdreg32 ebx ;显示 EBXpop ecxdreg32 ecx ;显示 ECXpop edxdreg32 edx ;显示 EDXdreg32 esi ;显示 E
23、SIdreg32 edi ;显示 EDIdreg32 ebp ;显示 EBPadd esp,36 ;获得进入该子程序前的 ESPdreg32 esp ;显示 ESPsub esp,36 ;恢复 ESPmov eax,offset rdeaxinvoke MessageBox ,0 ,eax , addr strCaption,0popadret dprdendpend start习题 6.9利用获得系统时间函数,将年月日时分秒星期等时间完整地显示出来。可以创建一个控制台程序,也可以创建一个消息窗口程序。习题 6.10结构数据类型如何说明,结构变量如何定义,结构字段如何引用略习题 6.11条件控
24、制伪指令的条件表达式中,逻辑与“.endif.endifcall dispc习题 6.14使用条件控制.if 和循环控制 .while 伪指令编写习题 4.21 程序,并生成完整的列表文件xor eax,eaxxor ebx,ebxmov ecx,lengthof string.while (ecx !=0).if (stringebx=20h)inc eax.endifinc ebxdec ecx.endw习题 6.15调用 GetCommandLine 函数,可以从 eax 返回指向命令行输入字符串(包含路径、文件名和参数) 。要求编程利用 MessageBox 函数输出这个字符串。.68
25、6.model flat,stdcalloption casemap:noneincludelib libkernel32.libincludelib libuser32.libExitProcess proto,:dwordMessageBoxA proto :dword,:dword,:dword,:dwordMessageBox equ NULL equ 0MB_OK equ 0GetCommandLineAproto GetCommandLine equ .data; 数据定义szCaptionbyte 命令行内容.codestart:; 主程序invoke GetCommandLineinvoke MessageBox,0,eax,addr szCaption,MB_OKinvoke ExitProcess,0; 子程序end start习题 6.16在 windows 窗口应用程序中例 6-11 的基础上,增加单击鼠标右键弹出另一个消息窗口的功能,在 masm32 开发环境生成可执行文件。