[32位汇编系列]005 - 定时器的使用(1)

有的时候, 你可能需要在程序中每隔一段时间去做某事,这就需要用到定时器,在汇编中你可以调用windows的SetTimer函数设置一个定时器来达到这个目的。今天我们就写一个简单的时钟程序, 让程序显示显示当前的时间。

先看代码:
; FileName: Clock.asm
; Author:   Thinker
; Link & Compile:
; ml /c /coff Clock.asm
; rc Clock.rc
; link /subsystem:windows Clock.obj Clock.res


    .386
    .model flat, stdcall
    option casemap : none

include        windows.inc
include        user32.inc
includelib     user32.lib
include        kernel32.inc
includelib     kernel32.lib

IDD_MAIN                EQU     101
IDC_STATIC_TIME         EQU     1000

    .data?
hInstance    dd    ?
    .const
szFormat    db    '%04d-%02d-%02d %02d:%02d:%02d', 0
    .code
_DlgProc proc hWnd, uMsg, wParam, lParam
    local    @stTime : SYSTEMTIME
    local    @szBuf[128]: byte
   
    mov    eax, uMsg
    .if    eax == WM_TIMER
        invoke    GetLocalTime, addr @stTime
        movzx    eax, @stTime.wSecond
        push     eax
        movzx    eax, @stTime.wMinute
        push     eax
        movzx    eax, @stTime.wHour
        push     eax
        movzx    eax, @stTime.wDay
        push     eax
        movzx    eax, @stTime.wMonth
        push     eax
        movzx    eax, @stTime.wYear
        push     eax
        push     offset szFormat
        lea      eax, @szBuf
        push     eax
        call     wsprintf
        add      esp, 32       
        invoke   SetDlgItemText,    hWnd, IDC_STATIC_TIME, addr @szBuf
    .elseif    eax == WM_INITDIALOG
        invoke    SetTimer, hWnd, 1, 1000, NULL
    .elseif    eax == WM_CLOSE
        invoke    KillTimer, hWnd, 1
        invoke    EndDialog, hWnd, 0
    .else
        mov    eax, FALSE
        ret
    .endif

    mov    eax, TRUE
    ret
_DlgProc endp

start:
    invoke    GetModuleHandle, NULL
    mov       hInstance, eax
    invoke    DialogBoxParam, hInstance, IDD_MAIN, NULL, offset _DlgProc, NULL
    invoke    ExitProcess, 0
    end    start

 

资源文件:

// Clock.rc
#include <resource.h>

#define IDD_MAIN                        101
#define IDC_STATIC_TIME                 1000

IDD_MAIN DIALOG DISCARDABLE  0, 0, 108, 29
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_CENTER
CAPTION "Clock"
FONT 10, "Verdana"
BEGIN
    CTEXT           "2009-07-19 16:36:30",IDC_STATIC_TIME,0,4,107,17,
                    SS_CENTERIMAGE
END

 

程序运行效果如下:
clock

程序和以前的一样, 重点在_DlgProc这个对话框过程中, 对于start标签部分,在前面的文章中已经讲过多遍,这里就不再赘述。现在来重点看看这个窗口过程:

在一开始, 定义了2个局部变量:SYSTEMTMEM结构类型的@stTime和一个byte类型的数组, 这个数组可以容纳128个字符。接着我们重点处理了3个消息:WM_TIMER, WM_INITDIALOG和WM_CLOSE

 

WM_CLOSE

这个消息的处理很简单, 用KillTimer销毁定时器, 同时用EndDialog结束对话框
KillTimer第一个参数是窗口句柄,指明要结束的是哪个对话框的定时器
第二个参数是定时器标识ID, 用于指明要结束的是这个对话框中的哪个定时器,
因为一个窗口中,可以有多个定时器, 每个定时器都要有唯一的一个整数标识

WM_INITDIALOG :

这个消息是对话框初始化的消息, 我们这里设置了一个定时器
定时器设置的函数为SetTimer, 函数原型是:
 UINT_PTR SetTimer(
  HWND hWnd,              // handle to window
  UINT_PTR nIDEvent,      // timer identifier
  UINT uElapse,           // time-out value
  TIMERPROC lpTimerFunc   // timer procedure
);

  hWnd         定时器窗口句柄
  nIDEvent     定时器ID
  uElapse      触发定时器的时间间隔
  lpTimerFunc  定时器触发时的处理函数

  该函数调用成功后, 每隔uElapse这个时间间隔(时间单位为毫秒)就会触发定时器
  如果lpTimerFunc不为空, 则用lpTimerFunc指向的函数来处理事件, 否则就会发送一个WM_TIMER消息到
  线程的消息队列,也就是说, 我们会在窗口的过程里面收到WM_TIMER消息

 

WM_TIMER :

因为我们把定时器的lpTimerFunc设置为NULL,时间设置为1000毫秒, 所以, 我们将每隔1秒钟收到一个WM_TIMER消息, 这里, 我们调用GetLocalTime获取系统当前的时间,这个时间是本地的时间,我们传进一个
SYSTEMTIME的结构指针进去, 该函数会自动用当前的时间填充这个结构的每个成员变量, 需要注意的是, 这个结构
的每个成员变量都是WORD类型的, 我们必须进行0扩展, 将16位的无符号数扩展到32位无符号数, 然后调用wsprintf这个唯一的遵循__cdecl调用约定 的windows api进行结构化的输出, 输出的字符串为szBuf,字符串的格式化形式是:
%04d-%02d-%02d %02d:%02d:%02d

%d表示是一个十进制整数, 前面加上数字表示该字符占用的位数, 如果前面再加0,表示不够的用0填充
这个格式化字符串表示, 年份占用4位, 月份、天数、时分秒都各占2位, 其它的字符原样输出, 最终的
输出结果可能是这样:
2009-07-19 21:41:30

最后用SetDlgItemText将字符串显示在一个静态文本框中。

这里有一点特别需要注意的是, 我们不能这样做:
invoke    GetLocalTime, addr @stTime
invoke    wsprintf, addr @szBuf, offset szFormat, @stTime.wYear, @stTime.wMonth, /

              @stTime.wDay, @stTime.wHour, @stTime,wMinute, @stTime.wSecond

invoke    SetDlgItemText, hWnd, IDC_STATIC_TIME, addr @szBuf

这样的代码从逻辑上看似乎没有问题, 一开始我就是这么写的, 但是我却忽视了在32位汇编中, 用invoke伪指令调用函数的时候, 默认的所有参数都是32位的, 都是DWORD类型的, 而我们这里却用@stTime.wYear这个word类型的, 可以想象最终的结果是不正确的, 事实上也证明了, 程序的运行结果是错误的

这里, 我们必须先把每个要调用的参数扩展成32位的, 然后用push指令从右往左依次压入栈中, 接着用call指令调用这个wsprintf函数, 因为这个函数是wsprintf调用约定 , 所以, 必须在后面用add esp, 32来平衡堆栈
这个32是这样来的, 因为调用wsprintf用了8个参数, 每个参数都是32位的, 即4个字节, 所以总共有4*8=32个字节


关于这个程序的汇编代码分析, 将在下一篇文章 中叙述。

<script type="text/javascript"> </script> <script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"></script>

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值