Fasm---Win32彙編學習5

          Fasm---Win32彙編學習5

Win32彙編學習5----定時器

                 定時器顧名思義,就像我們現實中的鬧鐘一樣,我們設定一個時間段,然後設置在這個時間段發生什麼事情。 就如我們的鬧鐘,我們設定一個時間段,那麼到了這個時間段的話,則鬧鐘就會發出“嘟嘟嘟”的聲音。 但是鬧鐘這個定時器,他不用我們設置相應的過程,就是到了時間段去發生什麼事情。。    但是我們windows中定時器,需要我們自己去設置到了這個時間段去發生什麼事情。。           

                  在Dos操作系統中我們要用到定時功能一般由兩種方法,一種是用一個空循環來延時;二是截獲時鐘中斷,計算機的硬件時鐘會以每55ms 一次的頻率出發8號中斷,而在默認的int 08h 中斷處理程序中由依據調用int 1ch的代碼,所以截獲int 08h 或int 1ch都可以達到定時的要求。第一種方法的定時效果隨計算機主頻的不同可能會大不相同,相比之下,第二種方法常用。            

      

                在保護模式下,我們用戶程序不可能去截獲時鐘中斷,所以操作系統用提供定時器的方法來滿足用戶的類似需求。

 

  定時器使用:

               在應用程序需要使用定時器時,可以用SetTimer函數向Windows申請一個定時器,要求系統在指定的時間後,“通知”應用程序,如果申請成功的話,系統會以指定的時間週期調用SetTimer函數指定的回調函數,或者向指定的窗口過程發送WM_TIMER消息。與Dos操作系統固定以55ms(毫秒)間隔觸發中斷服務程序相比,SetTimer函數可以指定的消息間隔更加靈活,------以ms爲單位,可以指定的時間週期爲一個32位的整數。也就是說1-4294967295ms,這可以將近50天的範圍。

 

             但是具體的使用不要被這個函數的參數所迷惑,由於windows的定時器同樣是基於時鐘中斷的,所以雖然單位是 ms,但精度還是55ms,如果指定一個小於55ms的週期不管是1ms 還是 54ms ,Windows最快的也只能是在每個時鐘中斷的時候觸發這個定時器,也就是說實際上這個定時器以55ms爲觸發週期的,另外當指定一個時間間隔的時候,windows以與這個間隔最接近的55ms的整數倍時間來觸發定時器,假定建立一個週期位1000ms的定時器,定時器的觸發週期實際上不是1s而是989ms(55ms*18)。

            使用定時器還有一個要點是定時器消息是一個低級別的消息,這表現兩個方面:首先,Windows只有在消息隊列沒有其他消息的時候纔會發送WM_TIMER消息。如果某個窗口過程忙於處理某個消息沒有返回,使消息隊列中由消息積累起來,那麼WM_TIMER消息就會被丟棄,在消息隊列再度空閒的時候,被丟棄的WM_TIMER消息不會在被補發,(用一句經典的話是“過去的就讓它過去吧”);其次消息隊列中不會有多條WM_TIMER消息,如果消息隊列中已經有一條WM_TIMER消息,還沒來得及處理,又到了定時的時刻,那麼兩條WM_TIMER消息就會被合併成一條。

          由此可見應用程序不能靠定時器來保證某件事情必須在規定的時刻被處理,另外也不能對定時器消息計數來計算已經過去了多少時間。

         那麼我們來寫個程序來給大家演示一下。

     我們還用昨天的代碼加以修改。

    format PE GUI 4.0
    include 'win32ax.inc'
   

  macro memmov [dst, src]
    {
    common
    push [src]
    pop [dst]
    }

  ID_TIMER1 equ 100
    ;************************數據********************************
    szClassName db 'first Windows',0
    szWndName    db '我的第一個程序',0
    szCommand dd ?
    hIcon    rd 1
    hInstanse rd 1
    hCursor    rd 1
    hWnd    rd 1


    entry $
        invoke GetModuleHandle,NULL
        mov    [hInstanse], eax
        invoke GetCommandLine,NULL
        mov [szCommand], eax
        stdcall _WinMain,hInstanse, NULL, [szCommand], SW_SHOWDEFAULT
        invoke ExitProcess,NULL
       
    proc _WinMain    hInstance:DWORD, hPrevInstance:DWORD, lpCmdLine:DWORD, nCmdShow:DWORD
        local @wc : WNDCLASSEX
        local @msg : MSG
       
       
        invoke RtlZeroMemory,addr @wc,sizeof.WNDCLASSEX
        invoke LoadIcon,NULL, IDI_WINLOGO
        mov [hIcon], eax
        invoke LoadCursor,NULL, IDC_ARROW
        mov [hCursor], eax
        mov [@wc.cbSize], sizeof.WNDCLASSEX
        mov [@wc.style], CS_HREDRAW or CS_VREDRAW
        mov [@wc.lpfnWndProc], _WndProc
        mov [@wc.cbClsExtra], NULL
        mov [@wc.cbWndExtra], NULL
     memmov @wc.hInstance, hInstance
    memmov @wc.hIcon, hIcon
    memmov @wc.hCursor, hCursor


        mov [@wc.hbrBackground], COLOR_WINDOW
        mov [@wc.lpszMenuName], NULL
        mov [@wc.lpszClassName], szClassName
        ;註冊窗口類
        invoke RegisterClassEx,addr @wc
        ;建立窗口
   
        invoke CreateWindowEx, NULL, szClassName, szWndName,/
WS_OVERLAPPEDWINDOW,/
    100, 100, 600, 400,/
    NULL, NULL, [hInstanse], NULL
        mov [hWnd], eax
        invoke ShowWindow,[hWnd],SW_SHOWNORMAL
        invoke UpdateWindow,[hWnd]
   
GetMsg:
        invoke GetMessage,addr @msg, NULL, 0, 0
        or eax, eax
        jz EndMsg
        invoke TranslateMessage,addr @msg
        invoke DispatchMessage,addr @msg
        jmp GetMsg
       
       
EndMsg:
        mov eax, [@msg.wParam]
        ret
    endp
   
   
    proc TimerProc hWnd:DWORD, wMsg:DWORD, idEvent:DWORD, dwTime:DWORD
       
        pushad
        invoke MessageBeep,-1
        popad
        ret
   
    endp

    proc _WndProc uses ebx esi edi,hWnd:DWORD, wMsg:DWORD, wParam:DWORD, lParam:DWORD
        local @hdc:DWORD
        local @ps : PAINTSTRUCT
        local @rect: RECT
       
        cmp [wMsg], WM_DESTROY
        jz Quit
        cmp [wMsg], WM_PAINT
        jz PAIT
        cmp [wMsg], WM_CREATE
        jz SETTIMER
        invoke DefWindowProc,[hWnd],[wMsg],[wParam],[lParam]
        ret
   
    SETTIMER:
        invoke SetTimer,[hWnd], ID_TIMER1, 1000, TimerProc
        jmp endWnd
   
    PAIT:
        invoke BeginPaint,[hWnd], addr @ps
        mov [@hdc], eax ;保存設備環境的句柄
        invoke GetClientRect,[hWnd], addr @rect
        invoke DrawText,[@hdc], szWndName, -1, addr @rect,/
        DT_SINGLELINE or DT_CENTER or DT_VCENTER
        invoke EndPaint,[hWnd], addr @ps
        jmp endWnd
   
    Quit:
        invoke PostQuitMessage,NULL
        invoke KillTimer,[hWnd], ID_TIMER1
        jmp endWnd
       
   
    endWnd:
        xor eax,eax
        ret
    endp

   
;///////////////////////////輸入表////////////////////////////////////////////////

   
section '.import' data import readable writeable
       
   
    library kernel32, 'kernel32.dll',/
            user32, 'user32.dll',/
            gdi32, 'gdi32.dll'     
    include 'api/kernel32.inc'
    include 'api/user32.inc'
    include 'api/gdi32.inc'

 

 

我們可以看到上面我們的代碼只有幾句和之前的不同。

cmp [wMsg], WM_CREATE
        jz SETTIMER

    SETTIMER:
        invoke SetTimer,[hWnd], ID_TIMER1, 1000, TimerProc
        jmp endWnd
   

  我們處理了WM_CREATE消息。那麼WM_CREATE消息是在我們調用CreateWindowEx函數後觸發。 我們這裏通過在調用CreateWindowEx後,

來創建一個定時器。那麼創建定時器的函數是SetTimer函數。

UINT SetTimer(

HWND hWnd,    // handle of window for timer messages
UINT nIDEvent,    // timer identifier
UINT uElapse,    // time-out value
TIMERPROC lpTimerFunc     // address of timer procedure
);   

hWnd是定時器消息的窗口句柄。 nIDEvent是定時器的標誌(因爲我們靠它來分別多個定時器),uElapse參數是定時器的時間週期,以ms位單位,這個是必須只得另的。    lpTimerFunc是定時器過程。    如果定時器建立成功返回的是定時器的標示符。

撤銷定時器的方法是通過KillTimer函數。

KillTimer(

HWND hWnd,    // handle of window that installed timer
UINT uIDEvent     // timer identifier
);

hWnd是我們創建定時器時候的參數, uIDEvent是定時器的標示符。

invoke SetTimer,[hWnd], ID_TIMER1, 1000, TimerProc

這個語句定了一個標示爲ID_TIMER1、消息發往TimerProc子程序的定時器,時間週期爲1秒。那麼在我們創建定時器成功的話,那麼每一秒,Windows就會調用我們這裏指定的TimerProc函數。

proc TimerProc hWnd:DWORD, wMsg:DWORD, idEvent:DWORD, dwTime:DWORD
       
        pushad
        invoke MessageBeep,-1
        popad
        ret
   
    endp
我們可以看到,TimerProc函數我們這裏要遵循相應的Windows規定。它的實際參數我們要遵循。

我們可以看到這個過程,我們只是簡單讓它調用MessageBep函數。從這裏我們就可以想到,在我們定時器創建成功的話,那麼每秒會發生“嘟嘟”的聲音,因爲這裏我們的定時器相應過程,我們只是讓它簡單的調用MessageBep函數來發出響聲。那麼此時大家可以將這段代碼放到FASM編譯器裏看看是不是此時每秒會發生嘟嘟的聲音。。

 

那麼我們使用定時器還有一種方法就是通過相應WM_TIMER消息。那麼此時就要將SetTimer最後一個參數設置爲NULL。

看代碼

 

    format PE GUI 4.0
    include 'win32ax.inc'
   
    ID_TIMER1 equ 100
    ;************************數據********************************
    szClassName db 'first Windows',0
    szWndName    db '我的第一個程序',0
    szCommand dd ?
    hIcon    rd 1
    hInstanse rd 1
    hCursor    rd 1
    hWnd    rd 1
 

  macro memmov [dst, src]
    {
    common
    push [src]
    pop [dst]
    }


    entry $
        invoke GetModuleHandle,NULL
        mov    [hInstanse], eax
        invoke GetCommandLine,NULL
        mov [szCommand], eax
        stdcall _WinMain,hInstanse, NULL, [szCommand], SW_SHOWDEFAULT
        invoke ExitProcess,NULL
       
    proc _WinMain    hInstance:DWORD, hPrevInstance:DWORD, lpCmdLine:DWORD, nCmdShow:DWORD
        local @wc : WNDCLASSEX
        local @msg : MSG
       
       
        invoke RtlZeroMemory,addr @wc,sizeof.WNDCLASSEX
        invoke LoadIcon,NULL, IDI_WINLOGO
        mov [hIcon], eax
        invoke LoadCursor,NULL, IDC_ARROW
        mov [hCursor], eax
        mov [@wc.cbSize], sizeof.WNDCLASSEX
        mov [@wc.style], CS_HREDRAW or CS_VREDRAW
        mov [@wc.lpfnWndProc], _WndProc
        mov [@wc.cbClsExtra], NULL
        mov [@wc.cbWndExtra], NULL
         memmov @wc.hInstance, hInstance
    memmov @wc.hIcon, hIcon
    memmov @wc.hCursor, hCursor

        mov [@wc.hbrBackground], COLOR_WINDOW
        mov [@wc.lpszMenuName], NULL
        mov [@wc.lpszClassName], szClassName
        ;註冊窗口類
        invoke RegisterClassEx,addr @wc
        ;建立窗口
   
        invoke CreateWindowEx, NULL, szClassName, szWndName,/
WS_OVERLAPPEDWINDOW,/
    100, 100, 600, 400,/
    NULL, NULL, [hInstanse], NULL
        mov [hWnd], eax
        invoke ShowWindow,[hWnd],SW_SHOWNORMAL
        invoke UpdateWindow,[hWnd]
   
GetMsg:
        invoke GetMessage,addr @msg, NULL, 0, 0
        or eax, eax
        jz EndMsg
        invoke TranslateMessage,addr @msg
        invoke DispatchMessage,addr @msg
        jmp GetMsg
       
       
EndMsg:
        mov eax, [@msg.wParam]
        ret
    endp
   
   


    proc _WndProc uses ebx esi edi,hWnd:DWORD, wMsg:DWORD, wParam:DWORD, lParam:DWORD
        local @hdc:DWORD
        local @ps : PAINTSTRUCT
        local @rect: RECT
       
        cmp [wMsg], WM_DESTROY
        jz Quit
        cmp [wMsg], WM_PAINT
        jz PAIT
        cmp [wMsg], WM_CREATE
        jz SETTIMER
        cmp [wMsg], WM_TIMER
        jz TIMER
        invoke DefWindowProc,[hWnd],[wMsg],[wParam],[lParam]
        ret
    TIMER:
        invoke MessageBeep,-1
        jmp endWnd   
   
    SETTIMER:
        invoke SetTimer,[hWnd], ID_TIMER1, 1000, NULL
        jmp endWnd
   
    PAIT:
        invoke BeginPaint,[hWnd], addr @ps
        mov [@hdc], eax ;保存設備環境的句柄
        invoke GetClientRect,[hWnd], addr @rect
        invoke DrawText,[@hdc], szWndName, -1, addr @rect,/
        DT_SINGLELINE or DT_CENTER or DT_VCENTER
        invoke EndPaint,[hWnd], addr @ps
        jmp endWnd
   
    Quit:
        invoke PostQuitMessage,NULL
        invoke KillTimer,[hWnd], ID_TIMER1
        jmp endWnd
       
   
    endWnd:
        xor eax,eax
        ret
    endp

   
;///////////////////////////輸入表////////////////////////////////////////////////

   
section '.import' data import readable writeable
       
   
    library kernel32, 'kernel32.dll',/
            user32, 'user32.dll',/
            gdi32, 'gdi32.dll'     
    include 'api/kernel32.inc'
    include 'api/user32.inc'
include 'api/gdi32.inc'

 

其實也很簡單,就是響應WM_TIMER消息,因爲我們如果不設定定時器相應過程,那麼windows會在每次定時器週期到的話,會發送WM_TIMER給相應的窗口。 此時我們只要相應WM_TIMER消息就可以了。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章