跟我一起学编程系列课程: 第一篇汇编语言 32位汇编
第十二章MS-Windows程序设计 第一节 ●Win32控制台编程 ●编写Windows图形界面应用程序 ●动态内存分配 ●IA-32内存管理 ●本章小结
九、控制台窗口的操作 通过Win32 API可以对控制台窗口及其缓冲区进行非常多的控制操作,图11.1表明屏幕缓冲区有可能大于控制台窗口当前显示的行数。控制台窗口就像一个视图,仅仅显示部分缓冲区的内容。
九、控制台窗口的操作 有几个函数可以影响控制台窗口以及它在与之关联的屏幕缓冲区中的位置: •SetConsoleWindowInfo函数设置控制台窗口在与之相关的屏幕缓冲区中的大小和位置。 •GetConsoleScreenBufferInfo函数返回控制台窗口矩形在与之相关屏幕缓冲区中的坐标,此外这个函数还返回其他一些数据。 •SetConsoleCursorPosition函数把光标定位到屏幕缓冲区的指定位置,如果这个位置不在可视区域内,控制台窗口会滚动直到光标可见为止。 •ScrollConsoleScreenBuffer函数将屏幕缓冲区中的部分或者全部文件移动出去,这个函数可能会影响到控制台窗口中的显示文本。 SetConsoleTitle函数 SetConsole函数允许改变控制台窗口的标题栏文字,例如: .data titleStr BYTE "Console title",0 .code INVOKE SetConsoleTitle, ADDR titleStr GetConsoleScreenBufferInfo函数 GetConsoleScreenBufferInfo函数返回控制台窗口当前状态的相关信息,这个函数有两个参数:控制台屏幕的句柄以及指向一个结构的指针,这个结构将由下列函数填写: GetConsoleScreenBufferInfo PROTO, hConsoleOutput:HANDLE, lpConsoleScreenBufferInfo:PTR CONSOLE_SCREEN_BUFFER_INFO
九、控制台窗口的操作 下面是CONSOLE_SCREEN_BUFFER_INFO结构的定义: CONSOLE_SCREEN_BUFFER_INFO STRUCT dwSize COORD <> dwCursorPosition COORD <> wAttributes WORD ? srWindow SMALL_RECT <> dwMaximumWindowSize COORD <> CONSOLE_SCREEN_BUFFER_INFO ENDS dwsize字段中返回屏幕缓冲区的大小,它们是以字符数位单位进行度量的行数和列数;dwCursorPosition字段返回光标的位置。这两个字段都是用COORD结构定义的。wAttributes返回WriteConsole和WriteFile等函数将字符写入控制台窗口时使用的前景和背景色;srWindow字段返回控制台窗口在屏幕缓冲区中的坐标;dwMaximumWindowSize字段返回控制台窗口的最大可能尺寸,这个数据是根据当前屏幕缓冲区的大小,字体和视频显示的尺寸综合得出的。下面是使用该函数的简单例子: .data consoleInfo CONSOLE_SCREEN_BUFFER_INFO <> outHandle HANDLE ? .code INVOKE GetConsoleScreenBufferInfo, outHandle, ADDR consoleInfo
九、控制台窗口的操作 图11.2是Microsoft Visual Studio的调试器里显示的该数据结构的图例:
九、控制台窗口的操作 SetConsoleWindowInfo函数 函数原型如下所示: SetConsoleWindowInfo PROTO, hConsoleOutput:HANDLE, ; screen buffer handle bAbsolute:DWORD, ; coordinate type lpConsoleWindow:PTR SMALL_RECT ; ptr to window rectangl bAbsolute参数指定了lpConsoleWindow参数指定的坐标如何解释,如果bAbsolute的值是TRUE,那么坐标代表控制台窗口新的左上角和右下角的位置;如果bAbsolute的值是FALSE,那么新的坐标将被加到当前控制台坐标上面使用。 下面的Scrool.asm程序在屏幕缓冲区上显示了50行文本,然后改变控制台窗口的位置和大小,这达到了有效的回滚文字的效果。程序用到了SetConsoleWindowInfo函数: TITLE Scrolling the Console Window (Scroll.asm) INCLUDE Irvine32.inc .data 略
九、控制台窗口的操作 由于集成开发编辑环境可能会影响控制台窗口的外观和动作,因此最好是在MS-Windows的资源管理器中或命令行上直接允许这个程序,而不是通过IDE运行。另外,在程序运行后读者必须按下两次键盘:第一次是为了清除屏幕缓冲区,另一次是将退出(这个功能是为了测试加上的)。 SetConsoleScreenBufferSize函数 SetConsoleScreenBufferSize函数允许将屏幕缓冲区的大小设置为X列Y行。函数原型如下所示: SetConsoleScreenBufferSize PROTO, hConsoleOutput:HANDLE, ; handle to screen buffer dwSize:COORD ; new screen buffer size
十、光标的控制 Win32 API提供了设置光标的大小,可见性和屏幕位置的函数,与这些函数相关的一个重要的数据结构称为CONSOLE_CURSOR_INFO,其中包含了光标大小和可见性等相关信息: CONSOLE_CURSOR_INFO STRUCT dwSize DWORD ? bVisible DWORD ? CONSOLE_CURSOR_INFO ENDS dwSize字段是光标占字符方格大小的百分比(1~100);bVisible等于TURE(1) 的时候,光标是可见的。 GetConsoleCursorInfo函数 GetConsoleCursorInfo函数返回控制台光标的大小和可见性等信息,调用的时候需要传递给函数一个指向CONSOLE_CURSOR_INFO结构的指针: GetConsoleCursorInfo PROTO, hConsoleOutput:HANDLE, lpConsoleCursorInfo:PTR CONSOLE_CURSOR_INFO 在默认状态下,光标尺寸等于25,也就是说光标占一个字符放个的25%大小。
十、光标的控制 SetConsoleCursorInfo函数 SetConsoleCursorInfo函数设置控制台光标的大小和可见性。同样,调用的时候需要传递给函数一个CONSOLE_CURSOR_INFO结构的指针: SetConsoleCursorInfo PROTO, hConsoleOutput:HANDLE, lpConsoleCursorInfo:PTR CONSOLE_CURSOR_INFO SetConsoleCursorPosition函数 SetConsoleCursorPosition函数设置光标的X和Y坐标位置,调用的时候需要传递给函数一个COORD结构和控制台输出句柄: SetConsoleCursorPosition PROTO, hConsoleOutput:HANDLE, ; input mode handle dwCursorPosition:COORD ; screen X,Y coordinates
十一、文本颜色的控制 有两种方式用来设置控制台窗口中的文本颜色,可以调用SetConsoleTextAttribute函数来设置当前的文本颜色,这样以后输出的所有文本的颜色都会改变。或者,可以使用WriteConsoleOutputAttribute函数来设置指定位置字符位置的颜色属性。GetConsoleScreenBufferInfo函数返回当前屏幕的颜色以及其他的控制台信息。 SetConsoleTextAttribute函数 SetConsoleTextAttribute函数用来设置以后输出到控制台窗口的字符的前景和背景色。函数原型: SetConsoleTextAttribute PROTO, hConsoleOutput:HANDLE, ; console output handle wAttributes:WORD ; color attribute WriteConsoleOutputAttribute函数 WriteConsoleOutputAttribute函数复制一个颜色属性数值的值到控制台屏幕缓冲区中连续位置的字符格中,字符格的位置可以指定的。函数原型如下所示: WriteConsoleOutputAttribute PROTO, hConsoleOutput:DWORD, ; output handle lpAttribute:PTR WORD, ; write attributes nLength:DWORD, ; number of cells dwWriteCoord:COORD, ; first cell coordinates lpNumberOfAttrsWritten:PTR DWORD ; output count
十一、文本颜色的控制 lpAttribute指向一个颜色属性数组,每个数组元素的低字节中存放颜色属性值;nLength是数组的长度;dwWriteCoord是屏幕中要接收这些属性的字符格的起始坐标;lpNumberofAttrsWritten指向一个变量,函数返回的时候将在此填写实际被设置了颜色属性的字符格的数量。 WriteColor 例子程序 为了演示如何使用颜色属性,例子程序WriteColors.asm创建了一个字符数组和一个属性数组,属性数组里面的每个属性对应字符数组里面的每个字符。程序调用WriteConsoleOutputAttribute函数把颜色属性复制到屏幕缓冲区中,然后调用WriteConsoleOutputCharacter函数把字符复制到屏幕缓冲区的同样位置: TITLE Writing Text Colors (WriteColors.asm) INCLUDE Irvine32.inc .data outHandle HANDLE ? 略
十二、时间和日期函数 Win32 API提供了大量的时间和日期函数。对于初学者而言,可以用它们来获取或者设置当前日期和时间。本节的内容仅仅示范了一小部分的时间和日期函数,读者可以查阅Platform SDK文档,进一步了解表11.9中列出的这些函数。
十二、时间和日期函数 SYSTEMTIME结构:与日期和时间相关的Win32 API函数常常会用到SYSTEMTIME结构: SYSTEMTIME STRUCT wYear WORD ? ; year (4 digits) wMonth WORD ? ; month (1-12) wDayOfWeek WORD ? ; day of week (0-6) wDay WORD ? ; day (1-31) wHour WORD ? ; hours (0-23) wMinute WORD ? ; minutes (0-59) wSecond WORD ? ; seconds (0-59) wMilliseconds WORD ? ; milliseconds (0-999) SYSTEMTIME ENDS wDayOfWeek字段的值含义为:0=星期日,1=星期一,依次类推。由于计算机的内部时钟每经过一个时间间隔定期与外部时钟源同步更新时钟的,所以wMillisseconds字段的值并不是完全精确的。
十二、时间和日期函数 GetLocalTime和SetLocalTime函数 GetLocalTime函数获取当前的日期和时间,该时间已经按照系统日期和时间转换成了本地时区的时间。当调用这个函数的时候,需要传递给函数一个指向SYSTEMTIME结构的指针: GetLocalTime PROTO, lpSystemTime:PTR SYSTEMTIME 下面是GetlocalTime函数的调用示例: .data sysTime SYSTEMTIME <> .code INVOKE GetLocalTime, ADDR sysTime SetLocalTime函数设置系统的当前时间和日期,调用的时候也需要传递一个指向SYSTEMTIME结构的指针,其中包含了要设置的时间值: SetLocalTime PROTO, 如果函数执行成功,返回值是非0值,如果执行失败,返回值为0.
十二、时间和日期函数 GetTickCount函数 GetTickCount函数返回系统启动以来所经过的毫秒数: GetTickCount PROTO ;返回值在EAX中 由于计数值是一个双字,所以当系统连续运行49.7天后,计数值将归0.可以在一个循环中使用这个函数来监经过的时间,并在某个时间到达的时候退出循环。 下面的例子程序Timer.asm测量两次调用GetTickCount之间经过的时间,并检查时间计数器值是否发生了回滚(超过49.7天).类似的代码可以用在很多不同的程序中: TITLE Calculate Elapsed Time (Timer.asm) ; Demonstrate a simple stopwatch timer, using ; the Win32 GetTickCount function. 略 Sleep函数 程序有时需要暂停或延迟一小段时间。可以编写一个循环达到延时的目的,但循环的执行时间在不同的处理器上是不同的。除此之外,循环计算还会大量消耗CPU资源,同时降低其他程序的执行速度。Sleep函数挂起当前的线程指定的毫秒数: Sleep PROTO dwMilliseconds:DWORD 由于大多数汇编语言程序通常是单线程的,因此这里假设一个线程就代表一个程序。线程在睡眠的时候不会占用处理器的时间。
十二、时间和日期函数 GetDateTime子程序 Irvine32库中的GetDateTime过程返回了一个64位的整数,这个数值是自1601年1月1日开始的以100ns为单位的计数值。这看起来很奇怪,但是微软却使用这个数值来跟踪文件的日期和时间。Win32 SDK文档中建议读者使用下面的步骤来准备系统日期和时间,以便进行其他的日期和时间计算: 1.调用一个函数(例如GetDateTime)来填写SYSTEMTIME结构 2.用SyetemTimeToFileTime函数把SYSTEMTIME结构转换到FILETIME结构。 3.把FILETIME结构中的结果复制到一个64位的QWORD中。 FILETIME结构把一个64位的QWORD值划分为两个DWORD值: FILETIME STRUCT loDateTime DWORD ? hiDateTime DWORD ? FILETIME ENDS 下面的GetDateTime接收一个指向64位QWORD变量的参数,并把当前的日期和时间存储在这个变量中,时间格式使用FILETIME格式: ;--------------------------------------------------
十二、时间和日期函数 GetDateTime PROC, pStartTime:PTR QWORD LOCAL sysTime:SYSTEMTIME, flTime:FILETIME ;; Gets and saves the current local date/time as a ; 64-bit integer (in the Win32 FILETIME format). ;-------------------------------------------------- ; Get the system local time INVOKE GetLocalTime, ADDR sysTime ; Convert the SYSTEMTIME to FILETIME INVOKE SystemTimeToFileTime, ADDR sysTime, ADDR flTime ; Copy the FILETIME to a 64-bit integer mov esi,pStartTime mov eax,flTime.loDateTime mov DWORD PTR [esi],eax mov eax,flTime.hiDateTime mov DWORD PTR [esi+4],eax ret GetDateTime ENDP 由于返回的时间是一个64位的整数,因此可以使用7.5节讲述的扩展精度算术运算技术进行日期的算术运算。
本节视频的课后练习 1、哪条LINK命令可以把程序的目标文件类型指定为Win32控制台程序? 2、(对/错)以W字母结尾的函数(如WriteConsoleW)是设计用来处理宽字符集(Unicode等16位字符)的。 3、(对/错)Unicode字符集是Windows98操作系统内置的字符集。 4、(对/错)ReadConsole函数从输入缓冲区读取鼠标的信息。 5、(对/错)Win32控制台输入函数可以检测到用户合适改变了控制台窗口的大小。 6、说出下列标准的Windows类型分别对应于哪些MASM数据类型? BOOL COLORREF HANDLE LPSTR WPARAM 7、哪个Win32函数返回标准输入的句柄? 8、哪个高级Win32函数从键盘读入一个字符串并把字符串放到一个缓冲区中? 9、试举一个使用ReadConsole函数的例子。 10、描述一下COORD结构。 11、试举一个使用WriteConsole函数的例子。 12、试举一个使用CreateFile函数来打开一个现存文件以便进行读数据的例子。 13、试举一个例子,使用CreateFile函数建立一个普通属性的新文件,如果文件存在,则将其覆盖。
本节视频的课后练习 14、试举一个使用ReadFile函数的例子 15、试举一个使用WriteFile函数的例子。 16、哪个Win32函数用来移动文件读写指针? 17、哪个Win32函数用来改变控制台窗口标题栏上面的文字? 18、哪个Win32函数用来改变屏幕缓存区的尺寸? 19、哪个Win32函数用来改变光标的大小? 20、哪个Win32函数用来设置要输出的文本的颜色? 21、哪个Win32函数用来把一个颜色属性数组复制到控制台屏幕缓冲区中连续的字符格上? 22、哪个Win32函数用来让程序暂停指定毫秒数的时间? 课后练习请务必完成后,再继续学习后面的课程。官网视频播放页面有参考答案。
0512-57882866 www.bcdaren.com 昆山爱达人 1250121864 昆山爱达人信息技术有限公司 视频录制:编程达人 视频提供 视频录制:编程达人 联系电话: 0512-57882866 官网地址: www.bcdaren.com 联系公众号: 昆山爱达人 联系QQ: 1250121864 编程达人APP: