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消息就可以了。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值