本節學習目標: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。