第8章 计时器

8.1 计时器的基本知识

(1)SetTimer时间参数:1毫秒到4294 967 295毫秒(近50天)

(2)Windows本身处理BIOS中断,应用程序不需介入,Windows会每个计时器保持一个计数值,硬件时钟滴答一次,这个值减1.当计数为0时,发送WM_TIMER到消息队列,同时计数值重新恢复到原始值。

(3)WIN98的计时器周期约为55毫秒,Windows NT大约为10毫秒。即SetTimer参数小于这个数值,则根据操作系统取其中的一个值。SetTimer会把指定的时间间隔舍入到时钟滴答的整数倍,如1000ms/54.925=18.2个时钟滴答,舍入后为18个时钟滴答,即实际的间隔为989ms。

(4)计时器消息不是异步的。在指定的时间内可能因程序忙而收到不到WM_TIMER消息。WM_TIMER消息与WM_PAINT消息都是低优先级的,只有当队列没其他消息时,程序才会接收它们。

8.2 使用计时器的三种方法

(1)处理WM_TIMER消息

①SetTimer(hwnd,ID_TIMER,uiMsecInterval,NULL);//ID_TIMER>0,不能为0.

②KillTimer(hwnd,ID_TIMER);

③消息参数:wParam=ID_TIMER; lParam =0;


例: 定义2个计时器

#define TIMER_SEC 1

#define TIMER_MIN 2

//设置定时器

SetTimer(hwnd,TIMER_SEC,1000,NULL);  //1秒

SetTimer(hwnd,TIMER_MIN,60000,NULL); //1分钟

//WM_TIMER处理逻辑

case WM_TIMER:

switch(wParam)

{

case TIMER_SEC:

[每秒钟一次的处理]

Break;

case TIMER_MIN:

[每分钟一次的处理]

Break;

}

return 0;

【Beeper1程序】

/*------------------------------------------------------------
BEEPER1.C -- Timer Demo Program No.1
(c) Charles Petzold, 1998
------------------------------------------------------------*/
#include <windows.h>
#define ID_TIMER 1
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("Beeper1");
    HWND         hwnd;
    MSG          msg;
    WNDCLASS     wndclass;
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = szAppName;
    if (!RegisterClass(&wndclass))
    {
        MessageBox(NULL, TEXT("This program requires Windows NT!"),
            szAppName, MB_ICONERROR);
        return 0;
    }

    hwnd = CreateWindow(szAppName,                  // window class name
        TEXT("Beeper1 Timer Demo"), // window caption
        WS_OVERLAPPEDWINDOW,        // window style
        CW_USEDEFAULT,              // initial x position
        CW_USEDEFAULT,              // initial y position
        CW_USEDEFAULT,              // initial x size
        CW_USEDEFAULT,              // initial y size
        NULL,                       // parent window handle
        NULL,                       // window menu handle
        hInstance,                  // program instance handle
        NULL);                     // creation parameters

    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static BOOL fFlipFlop = FALSE;
    HBRUSH hBrush;
    HDC         hdc;
    PAINTSTRUCT ps;
    RECT        rect;

    switch (message)
    {
    case WM_CREATE:
        SetTimer(hwnd, ID_TIMER, 1000, NULL);
        return 0;
    case WM_TIMER:
        MessageBeep(-1);
        fFlipFlop = !fFlipFlop;
        InvalidateRect(hwnd, NULL, FALSE);
        return 0;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);

        GetClientRect(hwnd, &rect);
        hBrush = CreateSolidBrush(fFlipFlop ? RGB(255, 0, 0) : RGB(0, 0, 255));
        FillRect(hdc, &rect, hBrush);
        DeleteObject(hBrush);
        EndPaint(hwnd, &ps);
        return 0;

    case WM_DESTROY:
        KillTimer(hwnd, ID_TIMER);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

(2)利用回调函数处理——将忽略窗口过程中的WM_TIMER消息处理代码

①VOID CALLBACK MyTimerProc(HWNDhwnd,UINT message,UINT iTimerID,DWORD dwTime)

②SetTimer(hwnd,iTimerID,iMsecInterval,MyTimerProc);

★注意:从回调函数中得到的dwTime等于GetTickCount返回的值,指的是自Windows启动到现在的毫秒数。

【Beeper2程序】

/*------------------------------------------------------------
BEEPER2.C -- Timer Demo Program No.2
(c) Charles Petzold, 1998
------------------------------------------------------------*/
#include <windows.h>
#define ID_TIMER 1
VOID CALLBACK TimerProc(HWND, UINT, UINT, DWORD);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("Beeper2");
    HWND         hwnd;
    MSG          msg;
    WNDCLASS     wndclass;
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = szAppName;
    if (!RegisterClass(&wndclass))
    {
        MessageBox(NULL, TEXT("This program requires Windows NT!"),
            szAppName, MB_ICONERROR);
        return 0;
    }

    hwnd = CreateWindow(szAppName,                  // window class name
        TEXT("Beeper2 Timer Demo"), // window caption
        WS_OVERLAPPEDWINDOW,        // window style
        CW_USEDEFAULT,              // initial x position
        CW_USEDEFAULT,              // initial y position
        CW_USEDEFAULT,              // initial x size
        CW_USEDEFAULT,              // initial y size
        NULL,                       // parent window handle
        NULL,                       // window menu handle
        hInstance,                  // program instance handle
        NULL);                     // creation parameters

    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
VOID CALLBACK TimerProc(HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime)
{
    HBRUSH hBrush;
    HDC         hdc;
    RECT        rect;
    static BOOL fFlipFlop = FALSE;
    MessageBeep(-1);
    fFlipFlop = !fFlipFlop;
    hdc = GetDC(hwnd);
    GetClientRect(hwnd, &rect);
    hBrush = CreateSolidBrush(fFlipFlop ? RGB(255, 0, 0) : RGB(0, 0, 255));
    FillRect(hdc, &rect, hBrush);
    DeleteObject(hBrush);
    ReleaseDC(hwnd, hdc);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{

    switch (message)
    {
    case WM_CREATE:
        SetTimer(hwnd, ID_TIMER, 1000, TimerProc);
        return 0;
        // case WM_TIMER:
        //MessageBeep(-1);
        //fFlipFlop = !fFlipFlop;
        //InvalidateRect(hwnd, NULL, FALSE);
        //return 0;

    case WM_DESTROY:
        KillTimer(hwnd, ID_TIMER);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

(3)方法三——与第2种相似,但SetTimer的设置如下。(一般很少这样用)


参数


设置


备注


hwnd


NULL


SetTimer、KillTimer、回调函数的hwnd都要设为NULL;


第2个参数


0


被忽略,填0


第4个参数


TimerProc


回调函数


返回值


计时器的ID


如返回0,说明没有可用的计时器

8.3 使用计时器作为时钟

8.3.1 数字时钟

(1)获取系统时间:GetLocalTime(当地时间)和GetSystemTime(协调世界时UTC)

(2)显示数字和冒号——自定义函数DisplayDigit和DisplayColon

①BOOL fSevenSegment[10][7]——用来控制0-9这10个数字的LED相应段开与关

②static POINT ptSegment[7][6]——表示LED中7个六边形的顶点坐标。

③数字宽度为42个单位,高度为72个单位,冒号是12个单位宽。6个数字加冒号共276个单位宽度。

(3)国际化——控制面板【区域和语言选项】中设置格式化日期和时间

①显示12或24小时格式。

//获得是否是24小时格式

GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_ITIME, szBuffer, 2);

f24Hour = (szBuffer[0] ==‘1‘);

②小时数字是0不显示的处理,见DisplayTwoDigits函数。

//获取是否显示小时的第1个0.

GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_ITLZERO, szBuffer, 2);

fSuppress= (szBuffer[0] == ‘0‘);

【DigClock程序】
【效果图】

/*------------------------------------------------------------
DIGCLOCK.C -- Digital Clock
(c) Charles Petzold, 1998
------------------------------------------------------------*/
#include <windows.h>
#define ID_TIMER 1
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("DigClock");
    HWND         hwnd;
    MSG          msg;
    WNDCLASS     wndclass;
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = szAppName;
    if (!RegisterClass(&wndclass))
    {
        MessageBox(NULL, TEXT("This program requires Windows NT!"),
            szAppName, MB_ICONERROR);
        return 0;
    }

    hwnd = CreateWindow(szAppName,                  // window class name
        TEXT("Digital Clock"), // window caption
        WS_OVERLAPPEDWINDOW,        // window style
        CW_USEDEFAULT,              // initial x position
        CW_USEDEFAULT,              // initial y position
        CW_USEDEFAULT,              // initial x size
        CW_USEDEFAULT,              // initial y size
        NULL,                       // parent window handle
        NULL,                       // window menu handle
        hInstance,                  // program instance handle
        NULL);                     // creation parameters

    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
void DisplayDigit(HDC hdc, int iNumber)
{
    static BOOL fSevenSegment[10][7] = {
        1, 1, 1, 0, 1, 1, 1, //0
        0, 0, 1, 0, 0, 1, 0, //1
        1, 0, 1, 1, 1, 0, 1, //2
        1, 0, 1, 1, 0, 1, 1, //3
        0, 1, 1, 1, 0, 1, 0, //4
        1, 1, 0, 1, 0, 1, 1, //5
        1, 1, 0, 1, 1, 1, 1, //6
        1, 0, 1, 0, 0, 1, 0, //7
        1, 1, 1, 1, 1, 1, 1, //8
        1, 1, 1, 1, 0, 1, 1 };//9
    //定义数字8的每个笔划(共7笔,每笔形状为6边形)
    static POINT ptSegment[7][6] = {
        7, 6, 11, 2, 31, 2, 35, 6, 31, 10, 11, 10,       //第0笔
        6, 7, 10, 11, 10, 31, 6, 35, 2, 31, 2, 11,      //第1笔
        36, 7, 40, 11, 40, 31, 36, 35, 32, 31, 32, 11,  //第2笔
        7, 36, 11, 32, 31, 32, 35, 36, 31, 40, 11, 40,  //第3笔
        6, 37, 10, 41, 10, 61, 6, 65, 2, 61, 2, 41,     //第4笔
        36, 37, 40, 41, 40, 61, 36, 65, 32, 61, 32, 41, //第5笔
        7, 66, 11, 62, 31, 62, 35, 66, 31, 70, 11, 70 }; //第6笔

    for (int iSeg = 0; iSeg < 7; iSeg++)
    {
        if (fSevenSegment[iNumber][iSeg])
        {
            Polygon(hdc, ptSegment[iSeg], 6);
        }
    }
}
void DisplayTwoDigits(HDC hdc, int iNumber, BOOL fSuppress)
{
    //将0显示为“ 0”或“00”
    if (!fSuppress || (iNumber / 10 != 0))
        DisplayDigit(hdc, iNumber / 10);  //执行到这里,0显示为“00”
    OffsetWindowOrgEx(hdc, -42, 0, NULL);
    DisplayDigit(hdc, iNumber % 10);
    OffsetWindowOrgEx(hdc, -42, 0, NULL);//每个数字的宽度为42个像素
}
void DisplayColon(HDC hdc)
{
    POINT ptColon[2][4] = {
        2, 21, 6, 17, 10, 21, 6, 25,
        2, 51, 6, 47, 10, 51, 6, 55 };
    Polygon(hdc, ptColon[0], 4);
    Polygon(hdc, ptColon[1], 4);
    OffsetWindowOrgEx(hdc, -12, 0, NULL);  //冒号的宽度为12像素
}
void DisplayTime(HDC hdc, BOOL f24Hour, BOOL fSuppress)
{
    SYSTEMTIME st;
    GetLocalTime(&st);
    //显示小时,24小时格式变或12小时格式
    if (f24Hour)
    {
        DisplayTwoDigits(hdc, st.wHour, fSuppress);
    }
    else
    {
        //12小时格式,将0\12\24,统一显示为12
        DisplayTwoDigits(hdc, (st.wHour %= 12) ? st.wHour : 12, fSuppress);
    }

    DisplayColon(hdc);
    //显示分钟
    DisplayTwoDigits(hdc, st.wMinute, FALSE);
    DisplayColon(hdc);
    //显示秒数
    DisplayTwoDigits(hdc, st.wSecond, FALSE);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static BOOL f24Hour, fSuppress;
    static HBRUSH hBrushRed;
    static int cxClient, cyClient;
    TCHAR  szBuffer[2];
    HDC         hdc;
    PAINTSTRUCT ps;

    switch (message)
    {
    case WM_CREATE:
        SetTimer(hwnd, ID_TIMER, 1000, NULL);
        hBrushRed = CreateSolidBrush(RGB(255, 0, 0));
        //继续执行
    case WM_SETTINGCHANGE:
        //获得是否是24小时格式
        GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, szBuffer, 2);
        f24Hour = (szBuffer[0] == ‘1‘);
        //获取是否显示小时的第1个0.
        GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITLZERO, szBuffer, 2);
        fSuppress = (szBuffer[0] == ‘0‘);
        InvalidateRect(hwnd, NULL, TRUE);
        return 0;
    case WM_SIZE:
        cxClient = LOWORD(lParam);
        cyClient = HIWORD(lParam);
        return 0;
    case WM_TIMER:
        InvalidateRect(hwnd, NULL, TRUE);
        return 0;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        SetMapMode(hdc, MM_ISOTROPIC); //各向同性
        SetWindowExtEx(hdc, 276, 72, NULL);//共6个数字,每个数字宽42,2对显号,每对宽12
        SetViewportExtEx(hdc, cxClient, cyClient, NULL);
        //输出中心移动到客户区中央
        SetWindowOrgEx(hdc, 138, 36, NULL);
        SetViewportOrgEx(hdc, cxClient / 2, cyClient / 2, NULL);
        SelectObject(hdc, GetStockObject(NULL_PEN));
        SelectObject(hdc, (HGDIOBJ)hBrushRed);
        DisplayTime(hdc, f24Hour, fSuppress);
        EndPaint(hwnd, &ps);
        return 0;

    case WM_DESTROY:
        KillTimer(hwnd, ID_TIMER);
        DeleteObject(hBrushRed);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

8.3.2 模拟时钟

(1)坐标系统的设置——各向同性、y轴向上为正,坐标轴原点在客户区中央。

①逻辑窗口范围(1000,1000)

②视口范围(cxClient/2,cyClient/2);

(2)坐标旋轴——用来画刻度和时分针

(3)stPrevious用来判断小时和分钟是否要更新。如果要,用白色画笔在之前的时间位置重绘一遍,相于当抹掉。如果不需要,只有秒针被抹掉。然后,用黑笔画所有指针一次。

【Clock程序】
效果图

/*------------------------------------------------------------
CLOCK.C -- Analog Clock Program
(c) Charles Petzold, 1998
------------------------------------------------------------*/
#include <windows.h>
#include <math.h>
#define  TWOPI (2*3.14159)
#define  ID_TIMER 1
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("Clock");
    HWND         hwnd;
    MSG          msg;
    WNDCLASS     wndclass;
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = szAppName;
    if (!RegisterClass(&wndclass))
    {
        MessageBox(NULL, TEXT("This program requires Windows NT!"),
            szAppName, MB_ICONERROR);
        return 0;
    }

    hwnd = CreateWindow(szAppName,                  // window class name
        TEXT("Analog Clock"),      // window caption
        WS_OVERLAPPEDWINDOW,        // window style
        CW_USEDEFAULT,              // initial x position
        CW_USEDEFAULT,              // initial y position
        CW_USEDEFAULT,              // initial x size
        CW_USEDEFAULT,              // initial y size
        NULL,                       // parent window handle
        NULL,                       // window menu handle
        hInstance,                  // program instance handle
        NULL);                     // creation parameters

    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
void SetIsotropic(HDC hdc, int cxClient, int cyClient)
{
    SetMapMode(hdc, MM_ISOTROPIC);
    SetWindowExtEx(hdc, 1000, 1000, NULL); //逻辑高度和逻辑宽度
    SetViewportExtEx(hdc, cxClient / 2, -cyClient / 2, NULL); //y轴向上为正
    SetViewportOrgEx(hdc, cxClient / 2, cyClient / 2, NULL); //坐标原点位于客户区中央
}
void RotatePoint(POINT pt[], int iNum, int iAngle)
{
    POINT ptTemp;
    for (int i = 0; i < iNum; i++)
    {
        ptTemp.x = (int)(pt[i].x*cos(iAngle*TWOPI / 360) + pt[i].y*sin(iAngle*TWOPI / 360));
        ptTemp.y = (int)(pt[i].y*cos(iAngle*TWOPI / 360) - pt[i].x*sin(iAngle*TWOPI / 360));
        pt[i] = ptTemp;
    }
}
//画针面的刻度
void DrawClock(HDC hdc)
{
    POINT pt[3];
    for (int iAngle = 0; iAngle < 360; iAngle += 6)
    {
        pt[0].x = 0;
        pt[0].y = 900;
        RotatePoint(pt, 1, iAngle);

        pt[2].x = pt[2].y = (iAngle % 5) ? 33 : 100; //圆的大小(直径)

        //Ellipse左上角的点
        pt[0].x -= pt[2].x / 2;
        pt[0].y -= pt[2].y / 2;
        //Ellipse右下角的点
        pt[1].x = pt[0].x + pt[2].x;
        pt[1].y = pt[0].y + pt[2].y;

        SelectObject(hdc, GetStockObject(BLACK_BRUSH));
        Ellipse(hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y);
    }
}
void DrawHands(HDC hdc, SYSTEMTIME* pst, BOOL fChange)
{
    static POINT pt[3][5] = {
        0, -150, 100, 0, 0, 600, -100, 0, 0, -150, //时针
        0, -200, 50, 0, 0, 800, -50, 0, 0, -200,   //分针
        0, 0, 0, 0, 0, 0, 0, 0, 0, 800 };           //秒针
    int iAngle[3];
    POINT ptTemp[3][5];
    iAngle[0] = (pst->wHour * 30) % 360 + pst->wMinute / 2; //时针每小时30度。
    iAngle[1] = pst->wMinute * 6; //每分钟,分针走6度。
    iAngle[2] = pst->wSecond * 6; //每秒,秒针走6度。

    memcpy(ptTemp, pt, sizeof(pt));
    for (int i = fChange ? 0 : 2; i < 3; i++) //判断是否只画秒针
    {
        RotatePoint(ptTemp[i], 5, iAngle[i]);
        Polyline(hdc, ptTemp[i], 5);
    }
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC         hdc;
    PAINTSTRUCT ps;
    static SYSTEMTIME stPrevious;
    BOOL fChange;
    SYSTEMTIME st;
    static int cxClient, cyClient;

    switch (message)
    {
    case WM_CREATE:
        SetTimer(hwnd, ID_TIMER, 1000, NULL);
        GetLocalTime(&st);
        stPrevious = st;
        return 0;
    case WM_SIZE:
        cxClient = LOWORD(lParam);
        cyClient = HIWORD(lParam);
        return 0;
    case WM_TIMER:
        GetLocalTime(&st);

        //小时和分钟是否改变
        fChange = st.wHour != stPrevious.wHour ||
            st.wMinute != stPrevious.wMinute;

        hdc = GetDC(hwnd);
        SetIsotropic(hdc, cxClient, cyClient);
        //将旧的时分秒针用白色擦掉
        SelectObject(hdc, GetStockObject(WHITE_PEN));
        DrawHands(hdc, &stPrevious, fChange);
        //画新的时分秒针
        SelectObject(hdc, GetStockObject(BLACK_PEN));
        DrawHands(hdc, &st, TRUE);
        ReleaseDC(hwnd, hdc);
        stPrevious = st;
        return 0;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        SetIsotropic(hdc, cxClient, cyClient);
        DrawClock(hdc);
        DrawHands(hdc, &stPrevious, TRUE);

        EndPaint(hwnd, &ps);
        return 0;

    case WM_DESTROY:
        KillTimer(hwnd, ID_TIMER);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

8.4 在状态报告上使用计时器

(1)窗口固定大小:其大小从自定义的FindWindowSize函数求得。窗口风格WS_BORDER。

(2)创建屏幕DC,程序可以从屏幕任何地方获得当前鼠标指针位置的像素颜色。

【WhatClr程序】

效果图

/*------------------------------------------------------------
WHATCLR.C -- Displays Color Under Cursor
(c) Charles Petzold, 1998
------------------------------------------------------------*/
#include <windows.h>
#define ID_TIMER 1
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void FindWindowSize(int*, int*);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("WhatClr");
    HWND         hwnd;
    MSG          msg;
    WNDCLASS     wndclass;
    int cxWindow, cyWindow;
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = szAppName;
    if (!RegisterClass(&wndclass))
    {
        MessageBox(NULL, TEXT("This program requires Windows NT!"),
            szAppName, MB_ICONERROR);
        return 0;
    }

    FindWindowSize(&cxWindow, &cyWindow);
    hwnd = CreateWindow(szAppName,                  // window class name
        TEXT("What Color"), // window caption
        WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_BORDER,        // window style
        CW_USEDEFAULT,              // initial x position
        CW_USEDEFAULT,              // initial y position
        cxWindow,                   // initial x size
        cyWindow,              // initial y size
        NULL,                       // parent window handle
        NULL,                       // window menu handle
        hInstance,                  // program instance handle
        NULL);                     // creation parameters

    ShowWindow(hwnd, iCmdShow);
    UpdateWindow(hwnd);

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}
void FindWindowSize(int* pcxWindow, int* pcyWindow)
{
    HDC hdcScreen;
    TEXTMETRIC tm;
    hdcScreen = CreateIC(TEXT("DISPLAY"), NULL, NULL, NULL); //屏幕hdc
    GetTextMetrics(hdcScreen, &tm);
    DeleteDC(hdcScreen);
    *pcxWindow = 2 * GetSystemMetrics(SM_CXBORDER) + 12 * tm.tmAveCharWidth; //宽度=2个边框加12个字符的宽度
    *pcyWindow = 2 * GetSystemMetrics(SM_CYBORDER) + GetSystemMetrics(SM_CYCAPTION) + 2 * tm.tmHeight;;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static HDC hdcScreen;
    static COLORREF cr, crLast;
    POINT pt;
    TCHAR szBuffer[16];
    HDC         hdc;
    PAINTSTRUCT ps;
    RECT        rect;

    switch (message)
    {
    case WM_CREATE:
        hdcScreen = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
        SetTimer(hwnd, ID_TIMER, 100, NULL);
        return 0;
    case WM_TIMER:
        GetCursorPos(&pt);
        cr = GetPixel(hdcScreen, pt.x, pt.y);
        if (cr != crLast)
        {
            crLast = cr;
            InvalidateRect(hwnd, NULL, TRUE);
        }
        return 0;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);

        GetClientRect(hwnd, &rect);
        wsprintf(szBuffer, TEXT(" %02X %02X %02X "), GetRValue(cr), GetGValue(cr), GetBValue(cr));
        DrawText(hdc, szBuffer, -1, &rect,
            DT_SINGLELINE | DT_CENTER | DT_VCENTER);

        EndPaint(hwnd, &ps);
        return 0;

    case WM_DESTROY:
        DeleteDC(hdcScreen);
        KillTimer(hwnd, ID_TIMER);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}
时间: 2024-10-06 05:00:45

第8章 计时器的相关文章

第9章 用内核对象进行线程同步(2)_可等待计时器(WaitableTimer)

9.4 可等待的计时器内核对象——某个指定的时间或每隔一段时间触发一次 (1)创建可等待计时器:CreateWaitableTimer(使用时应把常量_WIN32_WINNT定义为0x0400) 参数 描述 psa 安全属性(如使用计数.句柄继承等) bManualReset 手动重置计时器还是自动重置计时器. ①当手动计时器被触发,所有正在等待计时器的线程都变可为可调度. ②当自动计时器被触发时,只有一个正在等待计数器的线程变为可调度 pszName 对象的名字 (2)也可以打开一个己经存在的

(转)C#精确时间计时器

原文地址:http://blog.sina.com.cn/s/blog_699d3f1b01012vgb.html 1 调用WIN API中的GetTickCount [DllImport("kernel32")]static extern uint GetTickCount(); 从操作系统启动到现在所经过的毫秒数,精度为1毫秒,经简单测试发现其实误差在大约在15ms左右 缺点:返回值是uint,最大值是2的32次方,因此如果服务器连续开机大约49天以后,该方法取得的返回值会归零 用

第十一章 Shell常用命令与工具(二)

本章涉及命令如下: 11.31 wget 功能:非交互式网络下载,类似于HTTP客户端 常用选项: -b,  --background         后台运行 日志记录和输入文件: -o,  --output-file=FILE      日志写到文件 -a, --append-output=FILE     日志追加到文件 -d,  --debug              打印debug信息,会包含头信息 -q,  --quiet              退出,不输出 -i,  --in

JavaScript进阶 - 第8章 浏览器对象

第8章 浏览器对象 8-1 window对象 window对象是BOM的核心,window对象指当前的浏览器窗口. window对象方法: 注意:在JavaScript基础篇中,已讲解了部分属性,window对象重点讲解计时器. 任务 在右边编辑器script标签内补充代码,弹出对话框"欢迎来到慕课网". 定义一个函数,实现打开一个网页,宽为600,高为400. 当点击"点击我,打开新窗口"按钮时,在打开网页. 如果忘记了,可以查看JavaScript基础篇. 代码

《Java并发变成实践》读书笔记---第二章 线程安全性

什么是线程安全性 要编写线程安全的代码,其核心在于要对状态访问操作进行管理,特别是对共享的(Shared)和可变的(Mutable)状态的访问.从非正式的意义上来说,对象的状态是指存储在状态变量(例如实例或静态域)中的数据."共享"意味着变量可以由多个线程同时访问,而"可变"则意味着变量的值在其生命周期内可以发生变化.所以编写线程安全的代码更侧重于如何防止在数据上发生不受控的并发访问. 如果当多个线程访问同一个可变的状态变量时没有使用合适的同步,那么程序就会出现错误

《Programming WPF》翻译 第8章 2.Timeline

原文:<Programming WPF>翻译 第8章 2.Timeline Timeline代表了时间的延伸.它通常还描述了一个或多个在这段时间所发生的事情.例如,在前面章节描述的动画类型,都是Timeline.可哦率这样的DoubleAnimation: <DoubleAnimation From=”10” To=”300” Duration=”0:0:5” /> 正如Duration属性指出的,这代表了一个5秒的时间长度.所有类型的Timeline总是有一个开始时间和一个持续时

第十一章:WEB浏览器中的javascript

客户端javascript涵盖在本系列的第二部分第10章,主要讲解javascript是如何在web浏览器中实现的,这些章节介绍了大量的脚本宿主对象,这些对象可以表示浏览器窗口.文档树的内容.这些章节同样涵盖重要的web应用所需要的网络编程API.本地存储和检索数据.画图等.主要包含内容有以下章节: web浏览器中的javascript / window对象 /  脚本化文档 /  脚本化css / 事件处理 / 校本化http / jQuery类库 / 客户端存储  /  多媒体和图形编程 /

第12章-Swing编程 --- 使用JProgressBar、ProgressMonitor和BoundedRangeModel创建进度条

第12章-Swing编程 --- 使用JProgressBar.ProgressMonitor和BoundedRangeModel创建进度条 (一)创建进度条 使用JProgressBar,可以很方便的创建进度条,其步骤如下: (1)创建一个JProgressBar对象,创建该对象时也可以指定3个参数,用于设置进度条的排列方向(竖直和水平).进度条的最大值和最小值.也可以在创建该对象时不传入任何参数,而是在后面程序中修改这3个属性. 下面代码创建了JProgressBar对象 //创建一条垂直进

Java 线程第三版 第一章Thread导论、 第二章Thread的创建与管理读书笔记

第一章 Thread导论 为何要用Thread ? 非阻塞I/O I/O多路技术 轮询(polling) 信号 警告(Alarm)和定时器(Timer) 独立的任务(Task) 并行算法 第二章 Thread的创建与管理 一.什么是Thread ? Thread是所在主机执行的应用程序任务(task). 只有一个线程的例子: public class Factorial { public static void main(String[] args) { int n = 5; System.ou