第8章 计时器

时间:2022-04-13 20:31:38
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

 第8章 计时器

①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程序】
【效果图】

第8章 计时器

/*------------------------------------------------------------
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)坐标旋轴——用来画刻度和时分针

第8章 计时器

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

【Clock程序】
效果图

第8章 计时器

/*------------------------------------------------------------
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程序】

效果图

第8章 计时器

/*------------------------------------------------------------
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);
}