本节学习目标:windows时间
很多人看到“定时器”就会联想到时钟,笔者也曾经也是。但是在上节课程中我们已经说过,定时器是不能用来构造定时器的,定时器用于时钟程序中只能是在定时刷新屏幕这个功能上。因为我们调用SetTimer函数实质windows内核也是截获我们时钟中断的,时钟中断每55ms发生一次,且我们设定间隔时间总是时钟中断频率的倍数。 所以它不是很准确,那么今天就让我们来学习下我们如何来获得windows的时间。
在win32编程中,常用的获取系统时间的函数有两个:
invoke GetLocalTime, lpSystemTime
invoke GetSystemTime,lpSystemTime
它们的区别是GetlocalTime返回的是当前的时间,GetSystemTime返回的是当前的格林威治标准时间,这两阁函数返回的时间数据包括 年、月、日、时、分、秒、毫秒、以及星期。由于数据较多所以无法放在eax寄存器中返回,应用程序必须预先设置一个SYSTEMTIME结构的缓冲区,并将缓冲区地址lpSystemTime当参数传递给函数,函数会把时间数据返回到这个缓冲区中。
struct SYSTEMTIME
WORD wYear;年
WORD wMonth; 月
WORD wDayOfWeek ;指定星期几,例如Sunday = 0, Monday = 1
WORD wDay;日,也就是本月的几号
WORD wHour;小时
WORD wMinute; 分
WORD wSecond; 秒
WORD wMilliseconds; 毫秒
ends
那么我们来看一段代码。这个例子是在消息框上显示当前的年月日以及星期几。
format PE GUI 4.0
include 'win32ax.inc'
;**********************数据*****************************
systime SYSTEMTIME
szBuffer db 30 dup ?
entry $
invoke GetLocalTime, systime
mov ebx, systime
movzx eax, word [ebx];年
movzx esi, word [ebx+2];月
movzx edx, word [ebx+4];星期
movzx edi, word [ebx+6];日
invoke wsprintf,szBuffer, '%d年%d月%d日 星期%d ', eax, esi, edi,edx
invoke MessageBox,NULL, szBuffer, '提示', MB_OK
invoke ExitProcess,NULL
section '.import' data import readable
library kernel32, 'kernel32.dll',/
user32, 'user32.dll'
include 'api/kernel32.inc'
include 'api/user32.inc'
那么此刻大家把这段代码编译运行,看是不是显示出当前的时间以及星期。好我们现在来解剖这段代码。
invoke GetLocalTime, systime
我们调用GetLocalTime函数,这个函数的参数就是刚刚我们声明的SYSTEMTIME结构的缓冲区地址。
然后将systime这个结构的变量的地址传送给ebx寄存器。因为等一下要用它来进行间接寻址。这些基础大家去之前黑防论坛也有相应的板块。
因为下面我们要用wsprintf函数来格式化我们的这个结构中的成员,因为此时我们获得是相应的16进制数值,我们需要转换成10进制,这样显示出来的才是我们真正的时间以及日期。我们可以调用wsprintf函数来格式化,那么我们来看下这个函数的参数。
int wsprintf(
LPTSTR lpOut, // pointer to buffer fo
LPCTSTR lpFmt, // pointer to format-control string
... // optional arguments
);
首先第一个参数是指定我们输出的缓冲区的地址 。
第二个参数是指定我们输出的格式,这个函数其实和我们的c语言sprintf很相似,没办法谁让windows是C写的呢。所以第二个参数是指向输出格式字符串的地址。
第三个参数则指定需输出的参数(也就是要格式化的参数)。
那么我们来看下由于我们是通过%d参数来格式化,%d是整数类型,占4个字节。。我们的SYSTEMTIME结构每个成员是word类型,两个字节。所以我们在进行格式化之前,我们一定要想办法把它扩展成4个字节的。这时候我们就要用到我们的mozx指令。movzx指令是扩展指令,是将源操作数扩展成4阁字节并存放到目的操作数种,其余扩展的数值位用0填充。。那么很明确,我们可以利用movzx将我们的SYSTEMTIME结构的成员一部分扩展到相应的寄存器中,然后后面用寄存器来进行格式化字符串。。
movzx eax, word [ebx];年
movzx esi, word [ebx+2];月
movzx edx, word [ebx+4];星期
movzx edi, word [ebx+6];日
首先我们取得systime结构变量的缓冲区地址,然后将这个地址赋值给我们的ebx寄存器,然后后面我们利用[ebx+常数]来进行变址寻址。
我们将格式化的后的字符串保存到之前我们声明的缓冲区中,也就是szBuffer里,这个是我们声明的50个字节的缓冲区。。
最后通过调用MessageBox函数将其显示出来。。
invoke MessageBox,NULL, szBuffer, '提示', MB_OK
很简单吧。 好的,获取本地时间,我们就讲到这里。接下来我们来学习下如何计算时间间隔。
Windows提供给我们了一个获取时间戳的函数,GetTickCount。GetTickCount函数返回的是windows本次启动以来的ms数,得到的时间数值直接在eax寄存器返回。这是一个32位的证书,可以表示的范围是1 - ffffffffh ms,所以当windows连续运行49.7天以后,计数器会清0,重新开始,虽然这个函数无法用来判断当前的具体时间,但是计算两个时间点的时间间隔是最合适不过了。。
invoke GetTickCount
mov [dwCount], eax
invoke GetTickCount
sub eax, [dwCount]
在win9x系统下,GetTickCount函数的精度为55ms,任何调用两次GetTickCount函数相减后得到的时间间隔幺么是0,要么是55ms的整数倍。在windows NT/2000/xp系统下,这个函数的精度为10ms。