Programming Windows程式开发设计指南 第8章 计时器

8. 计时器

内容来自dedecms

 

本文来自织梦

Microsoft Windows计时器是一种输入设备,它周期性地在每经过一个指定的时间间隔後就通知应用程式一次。您的程式将时间间隔告诉Windows,例如「每10秒钟通知我一声」,然後Windows给您的程式发送周期性发生的WM_TIMER讯息以表示时间到了。

内容来自dedecms

初看之下,Windows计时器似乎不如键盘和滑鼠设备重要,而且对许多应用程式来说确实如此。但是,计时器比您可能认为的要重要得多,它不只用於计时程式,比如出现在工具列中的Windows时钟和这一章中的两个时钟程式。下面是Windows计时器的其他应用,有些可能并不那么明显: copyright dedecms

  •  多工 虽然Windows 98是一个优先权式的多工环境,但有时候如果程式尽快将控制传回给Windows效率会更高。如果一个程式必须进行大量的处理,那么它可以将作业分成小块,每接收到一个WM_TIMER讯息处理一块(我将在第二十章中对此做更多的讨论)。
     
  •  维护更新过的状态报告 程式可以利用计时器来显示持续变化资讯的「即时」更新,比如关於系统资源的变化或某个任务的进展情况。
     
  •  实作「自动储存」功能 计时器提示Windows程式在指定的时间过去後把使用者的工作储存到磁片上。
     
  •  终止程式展示版本的执行 一些程式的展示版本被设计成在其开始後,多长时间结束,比如说,30分钟。如果时间已到,那么计时器就会通知应用程式。
     
  •  步进移动 游戏中的图形物件或电脑辅助教学程式中的连续显示,需要按指定的速率来处理。利用计时器可以消除由於微处理器速度不同而造成的不一致。

    内容来自dedecms


     
  •  多媒体 播放CD声音、声音或音乐的程式通常在背景播放声音资料。一个程式可以使用计时器来周期性地检查已播放了多少声音资料,并据此协调萤幕上的视觉资讯。
     

另一项应用可以保证程式在退出视窗讯息处理程式後,能够重新得到控制。在大多数时情况下,程式不能够知道何时下一个讯息会到来。 copyright dedecms

计时器入门
 

dedecms.com

您可以通过呼叫SetTimer函式为您的Windows程式分配一个计时器。SetTimer有一个时间间隔范围为1毫秒到4,294,967,295毫秒(将近50天)的整数型态参数,这个值指示Windows每隔多久时间给您的程式发送WM_TIMER讯息。例如,如果间隔为1000毫秒,那么Windows将每秒给程式发送一个WM_TIMER讯息。

内容来自dedecms

当您的程式用完计时器时,它呼叫KillTimer函式来停止计时器讯息。在处理WM_TIMER讯息时,您可以通过呼叫KillTimer函式来编写一个「限用一次」的计时器。KillTimer呼叫清除讯息伫列中尚未被处理的WM_TIMER讯息,从而使程式在呼叫KillTimer之後就不会再接收到WM_TIMER讯息。 dedecms.com

系统和计时器
 

织梦好,好织梦

Windows计时器是PC硬体和ROM BIOS架构下之计时器一种相对简单的扩充。回到Windows以前的MS-DOS程式写作环境下,应用程式能够通过拦截者称为timer tickBIOS中断来实作时钟或计时器。一些为MS-DOS编写的程式自己拦截这个硬体中断以实作时钟和计时器。这些中断每54.915毫秒产生一次,或者大约每秒18.2次。这是原始的IBM PC的微处理器时脉值4.772720 MHz218所除而得出的结果。 本文来自织梦

Windows应用程式不拦截BIOS中断,相反地,Windows本身处理硬体中断,这样应用程式就不必进行处理。对於目前拥有计时器的每个程式,Windows储存一个每次硬体timer tick减少的计数。当这个计数减到0时,Windows在应用程式讯息伫列中放置一个WM_TIMER讯息,并将计数重置为其最初值。

dedecms.com

因为Windows应用程式从正常的讯息伫列中取得WM_TIMER讯息,所以您的程式在进行其他处理时不必担心WM_TIMER讯息会意外中断了程式。在这方面,计时器类似於键盘和滑鼠。驱动程式处理非同步硬体中断事件,Windows把这些事件翻译为规律、结构化和顺序化的讯息。 dedecms.com

Windows 98中,计时器与其下的PC计时器一样具有55毫秒的解析度。在Microsoft Windows NT中,计时器的解析度为10毫秒。

本文来自织梦

Windows应用程式不能以高於这些解析度的频率(在Windows 98下,每秒18.2次,在Windows NT下,每秒大约100次)接收WM_TIMER讯息。在SetTimer呼叫中指定的时间间隔总是截尾後tick数的整数倍。例如,1000毫秒的间隔除以54.925毫秒,得到18.207tick,截尾後是18tick,它实际上是989毫秒。对每个小於55毫秒的间隔,每个tick都会产生一个WM_TIMER讯息。 内容来自dedecms

计时器讯息不是非同步的
 

内容来自dedecms

因为计时器使用硬体计时器中断,程式写作者有时会误解,认为他们的程式会非同步地被中断来处理WM_TIMER讯息。

dedecms.com

然而,WM_TIMER讯息并不是非同步的。WM_TIMER讯息放在正常的讯息伫列之中,和其他讯息排列在一起,因此,如果在SetTimer呼叫中指定间隔为1000毫秒,那么不能保证程式每1000毫秒或者989毫秒就会收到一个WM_TIMER讯息。如果其他程式的执行事件超过一秒,在此期间内,您的程式将收不到任何WM_TIMER讯息。您可以使用本章的程式来展示这一点。事实上,WindowsWM_TIMER讯息的处理非常类似於对WM_PAINT讯息的处理,这两个讯息都是低优先顺序的,程式只有在讯息伫列中没有其他讯息时才接收它们。

dedecms.com

WM_TIMER还在另一方面和WM_PAINT相似:Windows不能持续向讯息伫列中放入多个WM_TIMER讯息,而是将多余的WM_TIMER讯息组合成一个讯息。因此,应用程式不会一次收到多个这样的讯息,尽管可能在短时间内得到两个WM_TIMER讯息。应用程式不能确定这种处理方式所导致的WM_TIMER讯息「遗漏」的数目。

copyright dedecms

这样,WM_TIMER讯息仅仅在需要更新时才提示程式,程式本身不能经由统计WM_TIMER讯息的数目来计时(在本章後面,我们将编写两个每秒更新一次的时钟程式,并可以看到如何做到这一点)。 dedecms.com

为了方便起见,下面在讨论时钟时,我将使用「每秒得到一次WM_TIMER讯息」这样的叙述,但是请记住,这些讯息并非精确的tick中断。 dedecms.com

计时器的使用:三种方法
  内容来自dedecms

如果您需要在整个程式执行期间都使用计时器,那么您将得从WinMain函式中或者在处理WM_CREATE讯息时呼叫SetTimer,并在退出WinMain或回应WM_DESTROY讯息时呼叫KillTimer。根据呼叫SetTimer时使用的参数,可以下列三种方法之一使用计时器。

dedecms.com

方法一
  织梦好,好织梦

这是最方便的一种方法,它让WindowsWM_TIMER讯息发送到应用程式的正常视窗讯息处理程式中,SetTimer呼叫如下所示: 内容来自dedecms

SetTimer (hwnd, 1, uiMsecInterval, NULL) ; 

copyright dedecms

第一个参数是其视窗讯息处理程式将接收WM_TIMER讯息的视窗代号。第二个参数是计时器ID,它是一个非0数值,在整个例子中假定为1。第三个参数是一个32位元无正负号整数,以毫秒为单位指定一个时间间隔,一个60,000的值将使Windows每分钟发送一次WM_TIMER讯息。

本文来自织梦

您可以通过呼叫

织梦好,好织梦

KillTimer (hwnd, 1) ; 

copyright dedecms

在任何时刻停止WM_TIMER讯息(即使正在处理WM_TIMER讯息)。此函式的第二个参数是SetTimer呼叫中所用的同一个计时器ID。在终止程式之前,您应该回应WM_DESTROY讯息停止任何活动的计时器。

织梦内容管理系统

当您的视窗讯息处理程式收到一个WM_TIMER讯息时,wParam参数等於计时器的ID值(上述情形为1),lParam参数为0。如果需要设定多个计时器,那么对每个计时器都使用不同的计时器IDwParam的值将随传递到视窗讯息处理程式的WM_TIMER讯息的不同而不同。为了使程式更具有可读性,您可以使用#define叙述定义不同的计时器ID

内容来自dedecms

#define TIMER_SEC 1 

copyright dedecms

#define TIMER_MIN 2 copyright dedecms 

然後您可以使用两个SetTimer呼叫来设定两个计时器:

dedecms.com

SetTimer (hwnd, TIMER_SEC, 1000, NULL) ; 
内容来自dedecms
SetTimer (hwnd, TIMER_MIN, 60000, NULL) ; copyright dedecms 

WM_TIMER的处理如下所示: 织梦内容管理系统

case    WM_TIMER: dedecms.com 
        switch (wParam) 

本文来自织梦

        { 
织梦内容管理系统
        case TIMER_SEC: dedecms.com 
               //每秒一次的处理 

copyright dedecms

               break ; 织梦好,好织梦 
        case TIMER_MIN: copyright dedecms 
               //每分钟一次的处理 

本文来自织梦

               break ; 
copyright dedecms
        } 织梦内容管理系统 
return 0 ; 织梦内容管理系统 

如果您想将一个已经存在的计时器设定为不同的时间间隔,您可以简单地用不同的时间值再次呼叫SetTimer。在时钟程式里,如果显示秒或不显示秒是可以选择的,您就可以这样做,只需简单地将时间间隔在1000毫秒和60 000毫秒间切换就可以了。

织梦内容管理系统

程式8-1显示了一个使用计时器的简单程式,名为BEEPER1,计时器的时间间隔设定为1秒。当它收到WM_TIMER讯息时,它将显示区域的颜色由蓝色变为红色或由红色变为蓝色,并通过呼叫MessageBeep函式发出响声。(虽然MessageBeep通常用於MessageBox,但它确实是一个全功能的鸣叫函式。在有音效卡的PC机上,一般可以使用不同的MB_ICON参数作为MessageBeep的一个参数以用於MessageBox,来播放使用者在「控制台」的「声音」程式中选择的不同声音)。 织梦好,好织梦

BEEPER1在视窗讯息处理程式处理WM_CREATE讯息时设定计时器。在处理WM_TIMER讯息处理期间,BEEPER1呼叫MessageBeep,翻转bFlipFlop的值并使视窗无效以产生WM_PAINT讯息。在处理WM_PAINT讯息处理期间,BEEPER1通过呼叫GetClientRect获得视窗大小的RECT结构,并通过呼叫FillRect改变视窗的颜色。

本文来自织梦

 程式8-1  BEEPER1 

本文来自织梦

BEEPER1.C 织梦内容管理系统 
/*------------------------------------------------------------------------- 
织梦内容管理系统
        BEEPER1.C  -- Timer Demo Program No. 1 

dedecms.com

                                              (c) Charles Petzold, 1998 本文来自织梦 
-------------------------------------------------------------------------*/ 

dedecms.com

  

dedecms.com

#include <windows.h> 
内容来自dedecms
  copyright dedecms 
#define ID_TIMER    1 内容来自dedecms 
  dedecms.com 
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; 
内容来自dedecms
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, 织梦内容管理系统 
                                              PSTR szCmdLine, int iCmdShow) dedecms.com 
{ 

本文来自织梦

        static TCHAR   szAppName[] = TEXT ("Beeper1") ; 本文来自织梦 
        HWND                           hwnd ; 
dedecms.com
        MSG                                   msg ; 织梦内容管理系统 
        WNDCLASS                              wndclass ; 内容来自dedecms 
      

内容来自dedecms

        wndclass.style                                = CS_HREDRAW | CS_VREDRAW ; 
织梦好,好织梦
        wndclass.lpfnWndProc                          = WndProc ; 
dedecms.com
        wndclass.cbClsExtra                           = 0 ; 内容来自dedecms 
        wndclass.cbWndExtra                           = 0 ; 
织梦好,好织梦
        wndclass.hInstance                            = hInstance ; 

织梦内容管理系统

        wndclass.hIcon                        = LoadIcon (NULL, IDI_APPLICATION) ; 

dedecms.com

        wndclass.hCursor                      = LoadCursor (NULL, IDC_ARROW) ; 织梦好,好织梦 
        wndclass.hbrBackground                = (HBRUSH) GetStockObject (WHITE_BRUSH) ; copyright dedecms 
        wndclass.lpszMenuName                 = NULL ; 
内容来自dedecms
        wndclass.lpszClassName                = szAppName ; 织梦好,好织梦 
      dedecms.com 
        if (!RegisterClass (&wndclass)) 

copyright dedecms

        { 

dedecms.com

               MessageBox (   NULL, TEXT ("Program requires Windows NT!"),  
dedecms.com
                                                     szAppName, MB_ICONERROR) ; 
本文来自织梦
               return 0 ; 本文来自织梦 
        } 织梦好,好织梦 
      

dedecms.com

        hwnd = CreateWindow (  szAppName, TEXT ("Beeper1 Timer Demo"), 本文来自织梦 
                               WS_OVERLAPPEDWINDOW, 
本文来自织梦
                       CW_USEDEFAULT, CW_USEDEFAULT, 本文来自织梦 
                       CW_USEDEFAULT, CW_USEDEFAULT, copyright dedecms 
                               NULL, NULL, hInstance, NULL) ; copyright dedecms 
           本文来自织梦 
        ShowWindow (hwnd, iCmdShow) ; 本文来自织梦 
        UpdateWindow (hwnd) ; 织梦好,好织梦 
  织梦好,好织梦 
        while (GetMessage (&msg, NULL, 0, 0)) dedecms.com 
        { 织梦好,好织梦 
                       TranslateMessage (&msg) ; 

copyright dedecms

                       DispatchMessage (&msg) ; dedecms.com 
        } 

dedecms.com

        return msg.wParam ; 

内容来自dedecms

} 织梦内容管理系统 
  

本文来自织梦

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam) dedecms.com 
{ 
织梦好,好织梦
        static BOOL    fFlipFlop = FALSE ; 织梦好,好织梦 
        HBRUSH                 hBrush ; 内容来自dedecms 
        HDC                            hdc ; 
本文来自织梦
        PAINTSTRUCT ps ; 

内容来自dedecms

        RECT        rc ; 

内容来自dedecms

      本文来自织梦 
        switch (message) dedecms.com 
     { 
织梦内容管理系统
        case    WM_CREATE: dedecms.com 
               SetTimer (hwnd, ID_TIMER, 1000, NULL) ; copyright dedecms 
                       return 0 ; 
本文来自织梦
  织梦内容管理系统 
        case    WM_TIMER : 
内容来自dedecms
                       MessageBeep (-1) ;           copyright dedecms 
                       fFlipFlop = !fFlipFlop ; 

内容来自dedecms

                       InvalidateRect (hwnd, NULL, FALSE) ; 
dedecms.com
                       return 0 ; 内容来自dedecms 
           织梦好,好织梦 
        case    WM_PAINT : 内容来自dedecms 
                       hdc = BeginPaint (hwnd, &ps) ; 

copyright dedecms

           copyright dedecms 
                       GetClientRect (hwnd, &rc) ; 
织梦内容管理系统
                       hBrush = CreateSolidBrush (fFlipFlop ? RGB(255,0,0) : RGB(0,0,255)) ; dedecms.com 
                       FillRect (hdc, &rc, hBrush) ; copyright dedecms 
  内容来自dedecms 
               EndPaint (hwnd, &ps) ; 织梦好,好织梦 
              DeleteObject (hBrush) ; 本文来自织梦 
                       return 0 ; 

织梦内容管理系统

           

dedecms.com

        case    WM_DESTROY : copyright dedecms 
                       KillTimer (hwnd, ID_TIMER) ; 内容来自dedecms 
                       PostQuitMessage (0) ; 

织梦内容管理系统

               return 0 ; 本文来自织梦 
        } 

织梦内容管理系统

        return DefWindowProc (hwnd, message, wParam, lParam) ; 

copyright dedecms

} 
本文来自织梦

因为BEEPER1每次收到WM_TIMER讯息时,都用颜色的变换显示出来,所以您可以通过呼叫BEEPER1来查看WM_TIMER讯息的性质,并完成Windows内部的一些其他操作。 织梦内容管理系统

例如,首先呼叫 控制台 的 显示器 程式,选择 效果 ,确定 拖曳时显示视窗内容 核取方块没有被选中。现在,试著移动或者缩放BEEPER1视窗,这将导致程式进入「模态讯息回圈」。Windows通过在内部讯息而非您程式的讯息回圈中拦截所有讯息,来禁止对移动或者缩放操作的任何干扰。通过此回圈到达程式视窗的大多数讯息都被丢弃,这就是BEEPER1停止蜂鸣的原因。当完成了移动与缩放之後,您将会注意到BEEPER1不能取得它所丢弃的所有WM_TIMER讯息,尽管前两个讯息的间隔可能少於1秒。

织梦好,好织梦

在「拖曳时显示视窗内容」核取方块被选中时,Windows中,的模态讯息回圈会试图给您的视窗讯息处理程式传递一些丢失的讯息。这样做有时工作得很好,有时却不行。 织梦内容管理系统

方法二
 

本文来自织梦

设定计时器的第一种方法是把WM_TIMER讯息发送到通常的视窗讯息处理程式,而第二种方法是让Windows直接将计时器讯息发送给您程式的另一个函式。

织梦好,好织梦

接收这些计时器讯息的函式被称为「callback」函式,这是一个在您的程式之中但是由Windows呼叫的函式。您先告诉Windows此函式的位址,然後Windows呼叫此函式。这看起来也很熟悉,因为程式的视窗讯息处理程式实际上也是一种callback函式。当注册视窗类别时,要将函式的位址告诉Windows,当发送讯息给程式时,Windows会呼叫此函式。

织梦好,好织梦

SetTimer并非是唯一使用callback函式的Windows函式。CreateDialogDialogBox函式(将在第十一章中介绍)使用callback函式处理对话方块中的讯息;有几个Windows函式(EnumChildWindowEnumFontsEnumObjectsEnumPropsEnumWindow)把列举资讯传递给callback函式;还有几个不那么常用的函式(GrayStringLineDDASetWindowHookEx)也要求callback函式。 本文来自织梦

像视窗讯息处理程式一样,callback函式也必须定义为CALLBACK,因为它是由Windows从程式的程式码段呼叫的。callback函式的参数和callback函式的传回值取决於callback函式的目的。跟计时器有关的callback函式中,输入参数与视窗讯息处理程式的输入参数一样。计时器callback函式不向Windows传回值。

dedecms.com

我们把以下的callback函式称为TimerProc(您能够选择与其他一些用语不会发生冲突的任何名称),它只处理WM_TIMER讯息:

织梦好,好织梦

VOID CALLBACK TimerProc (      HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime) 

copyright dedecms

{ 内容来自dedecms 
        处理WM_TIMER讯息 
本文来自织梦
} 

内容来自dedecms

TimerProc的参数hwnd是在呼叫SetTimer时指定的视窗代号。Windows只把WM_TIMER讯息送给TimerProc,因此讯息参数总是等於WM_TIMERiTimerID值是计时器IDdwTimer值是与从GetTickCount函式的传回值相容的值。这是自Windows启动後所经过的毫秒数。

内容来自dedecms

BEEPER1中已经看到过,用第一种方法设定计时器时要求下面格式的SetTimer呼叫:

织梦好,好织梦

SetTimer (hwnd, iTimerID, iMsecInterval, NULL) ; 

织梦好,好织梦

您使用callback函式处理WM_TIMER讯息时,SetTimer的第四个参数由callback函式的位址取代,如下所示: 织梦内容管理系统

SetTimer (hwnd, iTimerID, iMsecInterval, TimerProc) ; dedecms.com 

我们来看看一些范例程式码,这样您就会了解这些东西是如何组合在一起的。在功能上,除了Windows发送一个计时器讯息给TimerProc而非WndProc之外,程式8-2所示的BEEPER2程式与BEEPER1是相同的。注意,TimerProcWndProc一起被宣告在程式的开始处。 copyright dedecms

 程式8-2  BEEPER2 内容来自dedecms 
BEEPER2.C 
织梦内容管理系统
/*--------------------------------------------------------------------------- 织梦好,好织梦 
        BEEPER2.C --   Timer Demo Program No. 2 copyright dedecms 
                                      (c) Charles Petzold, 1998 

织梦内容管理系统

---------------------------------------------------------------------------*/ 
dedecms.com
  本文来自织梦 
#include <windows.h> 

本文来自织梦

#define ID_TIMER    1 

织梦内容管理系统

  
织梦内容管理系统
LRESULTCALLBACK               WndProc   (HWND, UINT, WPARAM, LPARAM) ; 
织梦好,好织梦
VOID    CALLBACK       TimerProc (HWND, UINT, UINT,   DWORD ) ; copyright dedecms 
  内容来自dedecms 
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, 

本文来自织梦

                                                             PSTR szCmdLine, int iCmdShow) 织梦内容管理系统 
{ 

织梦好,好织梦

        static char    szAppName[]            = "Beeper2" ; 

织梦内容管理系统

        HWND                                  hwnd ; 

本文来自织梦

        MSG                                   msg ; 
内容来自dedecms
        WNDCLASS                       wndclass ; 
织梦内容管理系统
      
内容来自dedecms
        wndclass.style                                        = CS_HREDRAW | CS_VREDRAW ; 

内容来自dedecms

        wndclass.lpfnWndProc                                  = WndProc ; dedecms.com 
        wndclass.cbClsExtra                                   = 0 ; 内容来自dedecms 
        wndclass.cbWndExtra                                   = 0 ; 
内容来自dedecms
        wndclass.hInstance                                    = hInstance ; dedecms.com 
        wndclass.hIcon                                        = LoadIcon (NULL, IDI_APPLICATION) ; 
dedecms.com
        wndclass.hCursor                                      = LoadCursor (NULL, IDC_ARROW) ; 

织梦好,好织梦

        wndclass.hbrBackground                        = (HBRUSH) GetStockObject (WHITE_BRUSH) ; copyright dedecms 
        wndclass.lpszMenuName                         = NULL ; dedecms.com 
        wndclass.lpszClassName                        = szAppName ; copyright dedecms 
      

织梦好,好织梦

        if (!RegisterClass (&wndclass)) 织梦内容管理系统 
        { 
本文来自织梦
                       MessageBox (   NULL, TEXT ("Program requires Windows NT!"),  
织梦内容管理系统
                                                                     szAppName, MB_ICONERROR) ; 

本文来自织梦

                       return 0 ; 内容来自dedecms 
        } 

内容来自dedecms

      

dedecms.com

        hwnd = CreateWindow (  szAppName, "Beeper2 Timer Demo", 
织梦内容管理系统
                                                             WS_OVERLAPPEDWINDOW, 
dedecms.com
                                      CW_USEDEFAULT, CW_USEDEFAULT, dedecms.com 
                                  CW_USEDEFAULT, CW_USEDEFAULT, 
织梦好,好织梦
                                 NULL, NULL, hInstance, NULL) ; dedecms.com 
      copyright dedecms 
        ShowWindow (hwnd, iCmdShow) ; copyright dedecms 
        UpdateWindow (hwnd) ; 织梦好,好织梦 
           copyright dedecms 
        while (GetMessage (&msg, NULL, 0, 0)) 
织梦内容管理系统
        { 
copyright dedecms
                       TranslateMessage (&msg) ; 

织梦好,好织梦

               DispatchMessage (&msg) ; 织梦好,好织梦 
        } 

dedecms.com

        return msg.wParam ; 本文来自织梦 
} 
内容来自dedecms
  dedecms.com 
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 
dedecms.com
{ 
dedecms.com
       switch (message) 织梦好,好织梦 
     { dedecms.com 
        case    WM_CREATE: 内容来自dedecms 
                       SetTimer (hwnd, ID_TIMER, 1000, TimerProc) ; 
织梦好,好织梦
                       return 0 ; copyright dedecms 
           织梦好,好织梦 
        case    WM_DESTROY: 

内容来自dedecms

                       KillTimer (hwnd, ID_TIMER) ; 

内容来自dedecms

                       PostQuitMessage (0) ; 本文来自织梦 
                       return 0 ; 
织梦好,好织梦
        } 
织梦内容管理系统
        return DefWindowProc (hwnd, message, wParam, lParam) ; 

织梦好,好织梦

} 本文来自织梦 
  

内容来自dedecms

VOID CALLBACK TimerProc (HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime) 
本文来自织梦
{ 织梦内容管理系统 
        static BOOL fFlipFlop = FALSE ; 织梦好,好织梦 
        HBRUSH                         hBrush ; dedecms.com 
        HDC                                   hdc ; dedecms.com 
        RECT                                  rc ; 

本文来自织梦

         dedecms.com 
        MessageBeep (-1) ; 内容来自dedecms 
        fFlipFlop = !fFlipFlop ; 

织梦内容管理系统

      
织梦内容管理系统
        GetClientRect (hwnd, &rc) ; 

copyright dedecms

        hdc = GetDC (hwnd) ; 
内容来自dedecms
        hBrush = CreateSolidBrush (fFlipFlop ? RGB(255,0,0) : RGB(0,0,255)) ; copyright dedecms 
      copyright dedecms 
        FillRect (hdc, &rc, hBrush) ; 本文来自织梦 
       ReleaseDC (hwnd, hdc) ; 
dedecms.com
       DeleteObject (hBrush) ; copyright dedecms 
} 
织梦内容管理系统

方法三
 

织梦内容管理系统

设定计时器的第三种方法类似於第二种方法,只是传递给SetTimerhwnd参数被设定为NULL,并且第二个参数(通常为计时器ID)被忽略了,最後,此函式传回计时器ID

织梦内容管理系统

iTimerID = SetTimer (NULL, 0, wMsecInterval, TimerProc) ; 织梦好,好织梦 

如果没有可用的计时器,那么从SetTimer传回的iTimerID值将为NULL

内容来自dedecms

KillTimer的第一个参数(通常是视窗代号)也必须为NULL,计时器ID必须是SetTimer的传回值:

织梦好,好织梦

KillTimer (NULL, iTimerID) ; 内容来自dedecms 

传递给TimerProc计时器函式的hwnd参数也必须是NULL。这种设定计时器的方法很少被使用。如果在您的程式在不同时刻有一系列的SetTimer呼叫,而又不希望追踪您已经用过了那些计时器ID,那么使用此方法是很方便的。

本文来自织梦

既然您已经知道了如何使用Windows计时器,就可以开始讨论一些有用的计时器程式了。 copyright dedecms

计时器用於时钟
 

dedecms.com

时钟是计时器最明显的应用,因此让我们来看看两个时钟,一个数位时钟,一个类比时钟。

内容来自dedecms

建立数位时钟
  织梦内容管理系统

程式8-3所示的DIGCLOCK程式,使用类似LED7个显示方块显示了目前的时间。 copyright dedecms

 程式8-3  DIGCLOCK 内容来自dedecms 
DIGCLOCK.C copyright dedecms 
/*---------------------------------------------------------------------------- 
织梦内容管理系统
        DIGCLOCK.C --          Digital Clock 

copyright dedecms

                                              (c) Charles Petzold, 1998 本文来自织梦 
----------------------------------------------------------------------------*/ 织梦好,好织梦 
  
本文来自织梦
#include <windows.h> copyright dedecms 
#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") ; dedecms.com 
       HWND                                  hwnd ; 内容来自dedecms 
        MSG                                   msg ; 内容来自dedecms 
        WNDCLASS                       wndclass ; 
织梦好,好织梦
  

dedecms.com

        wndclass.style                                        = CS_HREDRAW | CS_VREDRAW ; 

内容来自dedecms

        wndclass.lpfnWndProc                                  = WndProc ; 织梦好,好织梦 
        wndclass.cbClsExtra                                   = 0 ; 织梦内容管理系统 
        wndclass.cbWndExtra                                   = 0 ; 
内容来自dedecms
        wndclass.hInstance                                    = hInstance ; 

copyright dedecms

        wndclass.hIcon                                        = LoadIcon (NULL, IDI_APPLICATION) ; 

copyright dedecms

        wndclass.hCursor                                      = LoadCursor (NULL, IDC_ARROW) ; 

织梦好,好织梦

        wndclass.hbrBackground                        = (HBRUSH) GetStockObject (WHITE_BRUSH) ; 内容来自dedecms 
        wndclass.lpszMenuName                         = NULL ; 本文来自织梦 
        wndclass.lpszClassName                        = szAppName ; copyright dedecms 
  dedecms.com 
        if (!RegisterClass (&wndclass)) 
本文来自织梦
        { copyright dedecms 
                       MessageBox (   NULL, TEXT ("Program requires Windows NT!"),  本文来自织梦 
                                                                     szAppName, MB_ICONERROR) ; 织梦内容管理系统 
                       return 0 ; 织梦内容管理系统 
        } 
dedecms.com
  内容来自dedecms 
        hwnd = CreateWindow (  szAppName, TEXT ("Digital Clock"), 
dedecms.com
                        WS_OVERLAPPEDWINDOW, copyright dedecms 
                        CW_USEDEFAULT, CW_USEDEFAULT, 
织梦好,好织梦
                        CW_USEDEFAULT, CW_USEDEFAULT, copyright dedecms 
                        NULL, NULL, hInstance, NULL) ; copyright dedecms 
  copyright dedecms 
        ShowWindow (hwnd, iCmdShow) ; 

copyright dedecms

        UpdateWindow (hwnd) ; 织梦好,好织梦 
  

copyright dedecms

        while (GetMessage (&msg, NULL, 0, 0)) 

织梦好,好织梦

                       { copyright dedecms 
                               TranslateMessage (&msg) ; 内容来自dedecms 
                               DispatchMessage (&msg) ; 

本文来自织梦

                       } 
织梦内容管理系统
        return msg.wParam ; 织梦好,好织梦 
        } 
本文来自织梦
  

dedecms.com

void DisplayDigit (HDC hdc, int iNumber) copyright dedecms 
{ 本文来自织梦 
        static BOOL  fSevenSegment [10][7] = { 织梦内容管理系统 
                         1, 1,        1,      0,      1,      1,      1,             // 0 本文来自织梦 
                         0, 0,        1,      0,      0,      1,      0,             // 1 内容来自dedecms 
                         1, 0,        1,      1,      1,      0,      1,             // 2 本文来自织梦 
                         1, 0,        1,      1,      0,      1,      1,             // 3 
copyright dedecms
                         0, 1,        1,      1,      0,      1,      0,             // 4 织梦内容管理系统 
                         1, 1,        0,      1,      0,      1,      1,             // 5 
dedecms.com
                         1, 1,        0,      1,      1,      1,      1,             // 6 copyright dedecms 
                         1, 0,        1,      0,      0,      1,      0,             // 7 

本文来自织梦

                         1, 1,        1,      1,      1,      1,      1,             // 8 
织梦好,好织梦
                         1, 1,        1,      1,      0,      1,      1 } ;          // 9 

dedecms.com

        static POINT ptSegment [7][6] = { dedecms.com 
                                      7,  6,  11,    2,  31,  2, 35,  6,  31, 10, 11, 10, 内容来自dedecms 
                    6,  7,  10, 11, 10, 31, 6,   35, 2,  31, 2,  11, copyright dedecms 
                    36, 7,  40, 11, 40, 31, 36,  35, 32, 31, 32, 11, 织梦好,好织梦 
                    7 , 36, 11, 32, 31, 32, 35,  36, 31, 40, 11, 40, copyright dedecms 
                    6 , 37, 10, 41, 10, 61, 6,   65, 2,  61, 2,  41, 本文来自织梦 
                    36, 37, 40, 41, 40, 61, 36,  65, 32, 61, 32, 41, 
本文来自织梦
                    7 , 66, 11, 62, 31, 62, 35,  66, 31, 70, 11, 70 } ; 
dedecms.com
        int            iSeg ; 内容来自dedecms 
        for (iSeg = 0 ; iSeg < 7 ; iSeg++) 

dedecms.com

                       if (fSevenSegment [iNumber][iSeg]) 
dedecms.com
                                      Polygon (hdc, ptSegment [iSeg], 6) ; 织梦好,好织梦 
} 织梦内容管理系统 
  dedecms.com 
void DisplayTwoDigits (HDC hdc, int iNumber, BOOL fSuppress) 本文来自织梦 
{ 

copyright dedecms

        if (!fSuppress || (iNumber / 10 != 0)) 

dedecms.com

                       DisplayDigit (hdc, iNumber / 10) ; 
织梦好,好织梦
        OffsetWindowOrgEx (hdc, -42, 0, NULL) ; 

内容来自dedecms

        DisplayDigit (hdc, iNumber % 10) ; dedecms.com 
        OffsetWindowOrgEx (hdc, -42, 0, NULL) ; 织梦好,好织梦 
} 织梦内容管理系统 
  

织梦好,好织梦

void DisplayColon (HDC hdc) 内容来自dedecms 
{ 

内容来自dedecms

        POINT ptColon [2][4] = {       2,      21,     6,      17,     10,     21,     6,        25, 

本文来自织梦

                                2,    51,     6,      47,     10,     51,     6,      55 } ; dedecms.com 
  内容来自dedecms 
        Polygon (hdc, ptColon [0], 4) ; 

copyright dedecms

        Polygon (hdc, ptColon [1], 4) ; copyright dedecms 
  copyright dedecms 
        OffsetWindowOrgEx (hdc, -12, 0, NULL) ; 

copyright dedecms

} copyright dedecms 
  dedecms.com 
void DisplayTime (HDC hdc, BOOL f24Hour, BOOL fSuppress) 内容来自dedecms 
{ 本文来自织梦 
        SYSTEMTIME st ; 
织梦好,好织梦
        GetLocalTime (&st) ; 织梦内容管理系统 
        if (f24Hour) 
本文来自织梦
               DisplayTwoDigits (hdc, st.wHour, fSuppress) ; 织梦好,好织梦 
        else 

内容来自dedecms

        DisplayTwoDigits (hdc, (st.wHour %= 12) ? st.wHour : 12, fSuppress) ; 织梦内容管理系统 
        DisplayColon (hdc) ; 
dedecms.com
        DisplayTwoDigits (hdc, st.wMinute, FALSE) ; copyright dedecms 
        DisplayColon (hdc) ; 

织梦好,好织梦

        DisplayTwoDigits (hdc, st.wSecond, FALSE) ; 
copyright dedecms
} 织梦内容管理系统 
LRESULT CALLBACK WndProc (     HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam) copyright dedecms 
{ 织梦好,好织梦 
        static BOOL            f24Hour, fSuppress ; dedecms.com 
        static HBRUSH          hBrushRed ; 
织梦内容管理系统
        static int                     cxClient, cyClient ; 

内容来自dedecms

        HDC                                           hdc ; 

内容来自dedecms

        PAINTSTRUCT   ps ; 织梦内容管理系统 
        TCHAR                                 szBuffer [2] ; 

本文来自织梦

  
copyright dedecms
        switch (message) 
本文来自织梦
        { 本文来自织梦 
        case    WM_CREATE: 

copyright dedecms

                       hBrushRed = CreateSolidBrush (RGB (255, 0, 0)) ; dedecms.com 
                       SetTimer (hwnd, ID_TIMER, 1000, NULL) ;// fall through copyright dedecms 
  

织梦好,好织梦

        case    WM_SETTINGCHANGE: 

本文来自织梦

               GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_ITIME, szBuffer, 2) ; 织梦好,好织梦 
               f24Hour = (szBuffer[0] == '1') ; 
内容来自dedecms
  织梦好,好织梦 
               GetLocaleInfo (LOCALE_USER_DEFAULT, LOCALE_ITLZERO, szBuffer, 2) ; copyright dedecms 
               fSuppress = (szBuffer[0] == '0') ; 内容来自dedecms 
  dedecms.com 
                InvalidateRect (hwnd, NULL, TRUE) ; 本文来自织梦 
               return 0 ; 

织梦内容管理系统

  织梦好,好织梦 
        case    WM_SIZE: 

dedecms.com

               cxClient = LOWORD (lParam) ; copyright dedecms 
                       cyClient = HIWORD (lParam) ; 本文来自织梦 
                       return 0 ; 
织梦内容管理系统
  

copyright dedecms

        case    WM_TIMER: 

织梦内容管理系统

                       InvalidateRect (hwnd, NULL, TRUE) ; 
dedecms.com
                       return 0 ; copyright dedecms 
  dedecms.com 
        case    WM_PAINT: 
内容来自dedecms
                       hdc = BeginPaint (hwnd, &ps) ; 
copyright dedecms
  dedecms.com 
                       SetMapMode (hdc, MM_ISOTROPIC) ; 内容来自dedecms 
                       SetWindowExtEx (hdc, 276, 72, NULL) ; dedecms.com 
                       SetViewportExtEx (hdc, cxClient, cyClient, NULL) ; 织梦内容管理系统 
  

内容来自dedecms

                       SetWindowOrgEx (hdc, 138, 36, NULL) ; 内容来自dedecms 
                       SetViewportOrgEx (hdc, cxClient / 2, cyClient / 2, NULL) ; 

dedecms.com

                       SelectObject (hdc, GetStockObject (NULL_PEN)) ; 
内容来自dedecms
                       SelectObject (hdc, hBrushRed) ; 
织梦内容管理系统
  织梦内容管理系统 
                       DisplayTime (hdc, f24Hour, fSuppress) ; copyright dedecms 
  内容来自dedecms 
                       EndPaint (hwnd, &ps) ; 内容来自dedecms 
                       return 0 ; 
内容来自dedecms
  

织梦内容管理系统

        case    WM_DESTROY: 

内容来自dedecms

                       KillTimer (hwnd, ID_TIMER) ; dedecms.com 
                       DeleteObject (hBrushRed) ; 内容来自dedecms 
                       PostQuitMessage (0) ; 

本文来自织梦

                       return 0 ; 
内容来自dedecms
        } 
内容来自dedecms
        return DefWindowProc (hwnd, message, wParam, lParam) ; 织梦好,好织梦 
} 本文来自织梦 

DIGCLOCK视窗如图8-1所示。 织梦好,好织梦


  织梦内容管理系统

本文来自织梦

 

织梦好,好织梦

8-1 DIGCLOCK的萤幕显示 织梦好,好织梦

 

虽然,在图8-1中您看不到时钟的数字是红色的。DIGCLOCK的视窗讯息处理程式在处理WM_CREATE讯息处理期间建立了一个红色的画刷并在处理WM_DESTROY讯息处理期间清除它。WM_CREATE讯息也为DIGCLOCK设定了一个一秒的计时器,该计时器在处理WM_DESTROY讯息处理期间被终止(待会将讨论对GetLocaleInfo的呼叫)。

dedecms.com

在收到WM_TIMER讯息後,DIGCLOCK的视窗程序呼叫InvalidateRect简单地使整个视窗无效。这不是最佳方法,因为每秒整个视窗都要被擦除和重画,有时会引起显示器的闪烁。依据目前的时间使视窗需要更新的部分无效是最好的解决方法。然而,在逻辑上这样做的确很复杂。 dedecms.com

在处理WM_TIMER讯息处理期间使视窗无效会迫使所有程式的真正活动转入WM_PAINTDIGCLOCKWM_PAINT讯息一开始将映射方式设定为MM_ISOTROPIC。这样,DIGCLOCK将使用水平方向和垂直方向相等的轴。这些轴(由SetWindowExtEx呼叫设定)是水平276个单位,垂直72个单位。当然,这些轴定得有点太随意了,但它们是按照时钟数位元的大小和间距安排的。

内容来自dedecms

DIGCLOCK将视窗原点设定为(138,36),这是视窗范围的中心;将视埠原点设定为(cxClient / 2,cyClient / 2)。这意味著时钟的显示位於DIGCLOCK显示区域的中心,但是该DIGCLOCK也可以使用在显示幕左上角的原点(0, 0)的轴。 dedecms.com

然後WM_PAINT将目前画刷设定为之前建立的红画刷,将目前画笔设定为NULL_PEN,并呼叫DIGCLOCK中的函式DisplayTime织梦内容管理系统

取得目前时间
  dedecms.com

DisplayTime函式开始呼叫Windows函式GetLocalTime,它带有一个的SYSTEMTIME结构的参数,在WINBASE.H中定义为:

内容来自dedecms

typedef struct _SYSTEMTIME  
织梦内容管理系统
{ 
织梦好,好织梦
        WORD    wYear ; 
dedecms.com
        WORD    wMonth ; 织梦内容管理系统 
        WORD    wDayOfWeek ; 
本文来自织梦
        WORD    wDay ; 

织梦好,好织梦

        WORD    wHour ; 

织梦好,好织梦

        WORD    wMinute ; 织梦内容管理系统 
        WORD    wSecond ; copyright dedecms 
        WORD    wMilliseconds ; 
dedecms.com
}  织梦内容管理系统 
SYSTEMTIME, * PSYSTEMTIME ; 内容来自dedecms 

很明显,SYSTEMTIME结构包含日期和时间。月份由1开始递增(也就是说,一月是1),星期由0开始递增(星期天是0)。wDay成员是本月目前的日子,也是由1开始递增的。

内容来自dedecms

SYSTEMTIME主要用於GetLocalTimeGetSystemTime函式。GetSystemTime函式传回目前的世界时间(Coordinated Universal TimeUTC),大概与英国格林威治时间相同。GetLocalTime函式传回当地时间,依据电脑所在的时区。这些值的精确度完全决定於使用者所调整的时间精确度以及是否指定了正确的时区。可以双击工作列的时间显示来检查电脑上的时区设定。第二十三章会有一个程式,能够通过Internet精确地设定时间。 copyright dedecms

Windows还有SetLocalTimeSetSystemTime函式,以及在/Platform SDK/Windows Base Services/General Library/Time中说明的其他与时间有关的函式。 dedecms.com

显示数字和冒号
 

织梦内容管理系统

如果DIGCLOCK使用一种模拟7段显示的字体将会简单一些。否则,它就得使用Polygon函式做所有的工作。

copyright dedecms

DIGCLOCK中的DisplayDigit函式定义了两个阵列。fSevenSegment阵列有7BOOL值,用於从09的每个十进位数字。这些值指出了哪一段需要显示(为1),哪一段不需要显示(为0)。在这个阵列中,7段由上到下、由左到右排序。7段中的每个段都是一个6边的多边形。ptSegment阵列是一个POINT结构的阵列,指出了7个段中每个点的图形座标。每个数字由下列程式码画出:

copyright dedecms

for     (iSeg = 0 ; iSeg < 7 ; iSeg++) 织梦好,好织梦 
        if (    fSevenSegment [iNumber][iSeg]) 织梦好,好织梦 
                       Polygon (hdc, ptSegment [iSeg], 6) ; 织梦内容管理系统 

类似地(但更简单),DisplayColon函式在小时与分钟、分钟与秒之间画一个冒号。数字是42个单位宽,冒号是12个单位宽,因此6个数字与2个冒号,总宽度是276个单位,SetWindowExtEx呼叫中使用了这个大小。

copyright dedecms

回到DisplayTime函式,原点位於最左数字位置的左上角。DisplayTime呼叫DisplayTwoDigitsDisplayTwoDigits呼叫DisplayDigit两次,并且在每次呼叫OffsetWindowOrgEx後,将视窗原点向右移动42个单位。类似地,DisplayColon函式在画完冒号後,将视窗原点向右移动12个单位。用这种方法,不管物件出现在视窗内的哪个地方,函式对数字和冒号都使用同样的座标。 copyright dedecms

这个程式的其他技巧是以12小时或24小时的格式显示时间以及当最左边的小时数字为0时不显示它。

织梦好,好织梦

国际化
  dedecms.com

尽管像DIGCLOCK这样显示时间是非常简单的,但是要显示复杂的日期和时间还是要依赖Windows的国际化支援。格式化日期和时间的最简单的方法是呼叫GetDateFormatGetTimeFormat函式。这些函式在/Platform SDK/Windows Base Services/General Library/String Manipulation/String Manipulation Reference/String Manipulation Functions中有记载,但是它们在/Platform SDK/Windows Base Services/International Features/National Language Support中进行了说明。这些函式接受SYSTEMTIME结构并且依据使用者在「控制台」的「区域设定」程式中所做的选择而将日期和时间格式化。 copyright dedecms

DIGCLOCK不能使用GetDateFormat函式,因为它只知道显示数字和冒号,然而,DIGCLOCK应该能够根据使用者的参数选择来显示12小时或24小时的格式,并禁止(或不禁止)开头的小时数字。您可以从GetLocaleInfo函式中取得这种资讯。虽然GetLocaleInfo/Platform SDK/Windows Base Services/General Library/String Manipulation/String Manipulation Reference/String Manipulation Functions中有记载,但是这个函式使用的识别字在/Platform SDK/Windows Base Services/International Features/National Language Support/National Language Support Constants中有说明。

dedecms.com

DIGCLOCK在处理WM_CREATE讯息时,最初呼叫GetLocaleInfo两次,第一次使用LOCALE_ITIME识别字(确定使用的是12小时还是24小时格式),然後使用LOCALE_ITLZERO识别字(在小时显示中禁止前面显示0)。GetLocaleInfo函式在字串中传回所有的资讯,但是在大多数情况下把字串转变为整数并不是非常容易。DIGCLOCK把字串储存在两个静态变数中并把它们传递给DisplayTime函式。 copyright dedecms

如果使用者更改了任何系统设定,则会将WM_SETTINGCHANGE讯息传送给所有的应用程式。DIGCLOCK通过再次呼叫GetLocaleInfo处理这个讯息。以这种方式,您可以在「控制台」的「区域设定」程式中进行不同的设定来实验一下。

织梦内容管理系统

在理论上,DIGCLOCK也应该使用LOCALE_STIME识别字呼叫GetLocaleInfo。这会传回使用者为时间的小时、分钟和秒等单个部分选择的字元。因为DIGCLOCK被设定为仅显示冒号,所以不管选择了什么,都会得到冒号。要指出时间是A.M.P.M.,应用程式可以使用带有LOCALE_S1159LOCALE_S2359识别字的GetLocaleInfo函式。这些识别字使程式获得适合於使用者国家/地区和语言的字串。

dedecms.com

我们也可以让DIGCLOCK处理WM_TIMECHANGE讯息,这样它将系统时间与日期发生变化的讯息通知应用程式。DIGCLOCKWM_TIMER讯息而每秒更新一次,实际上没有必要这样作,对WM_TIMECHANGE讯息的处理使得每分钟更新一次的时钟变得更为合理。 织梦好,好织梦

建立类比时钟
 

内容来自dedecms

类比时钟不必关心国际化问题,但是由於图形所引起的复杂性却抵消了这种简化。为了正确地产生时钟,您需要知道一些三角函数。CLOCK如程式8-4所示。

织梦好,好织梦

 程式8-4  CLOCK 织梦内容管理系统 
CLOCK.C dedecms.com 
/*--------------------------------------------------------------------------- 

织梦内容管理系统

   CLOCK.C -- Analog Clock Program 本文来自织梦 
                                      (c) Charles Petzold, 1998 

织梦内容管理系统

---------------------------------------------------------------------------*/ dedecms.com 
  dedecms.com 
#include <windows.h> 

本文来自织梦

#include <math.h> 
内容来自dedecms
  copyright dedecms 
#define ID_TIMER                      1 织梦内容管理系统 
#define TWOPI                                        (2 * 3.14159) 

本文来自织梦

  织梦内容管理系统 
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; 
dedecms.com
        MSG                                   msg; 
本文来自织梦
        WNDCLASS                       wndclass ; dedecms.com 
      织梦内容管理系统 
        wndclass.style                                        = CS_HREDRAW | CS_VREDRAW ; copyright dedecms 
        wndclass.lpfnWndProc                          = WndProc ; dedecms.com 
        wndclass.cbClsExtra                           = 0 ; 内容来自dedecms 
        wndclass.cbWndExtra                           = 0 ; copyright dedecms 
        wndclass.hInstance                            = hInstance ; 

dedecms.com

        wndclass.hIcon                                = NULL ; dedecms.com 
        wndclass.hCursor                              = LoadCursor (NULL, IDC_ARROW) ; 

内容来自dedecms

        wndclass.hbrBackground                        = (HBRUSH) GetStockObject (WHITE_BRUSH) ; 织梦内容管理系统 
        wndclass.lpszMenuName                         = NULL ; copyright dedecms 
        wndclass.lpszClassName                        = szAppName ; 

copyright dedecms

      

内容来自dedecms

        if (!RegisterClass (&wndclass)) 本文来自织梦 
        { 织梦好,好织梦 
                       MessageBox (   NULL, TEXT ("Program requires Windows NT!"),  织梦好,好织梦 
                                                                     szAppName, MB_ICONERROR) ; dedecms.com 
                       return 0 ; 
织梦好,好织梦
        } copyright dedecms 
        hwnd = CreateWindow (  szAppName, TEXT ("Analog Clock"), 
本文来自织梦
                         WS_OVERLAPPEDWINDOW, 

copyright dedecms

                         CW_USEDEFAULT, CW_USEDEFAULT, 内容来自dedecms 
                         CW_USEDEFAULT, CW_USEDEFAULT, 内容来自dedecms 
                         NULL, NULL, hInstance, NULL) ; copyright dedecms 
      
内容来自dedecms
        ShowWindow (hwnd, iCmdShow) ; 

本文来自织梦

        UpdateWindow (hwnd) ; 
dedecms.com
      
copyright dedecms
        while (GetMessage (&msg, NULL, 0, 0)) 
copyright dedecms
        { 
本文来自织梦
                               TranslateMessage (&msg) ; copyright dedecms 
                               DispatchMessage (&msg) ; 
copyright dedecms
     } 

内容来自dedecms

        return msg.wParam ; 
dedecms.com
} copyright dedecms 
  织梦内容管理系统 
void SetIsotropic (HDC hdc, int cxClient, int cyClient) 

织梦好,好织梦

{ dedecms.com 
        SetMapMode (hdc, MM_ISOTROPIC) ; 织梦好,好织梦 
        SetWindowExtEx (hdc, 1000, 1000, NULL) ; 

copyright dedecms

        SetViewportExtEx (hdc, cxClient / 2, -cyClient / 2, NULL) ; copyright dedecms 
        SetViewportOrgEx (hdc, cxClient / 2,  cyClient / 2, NULL) ; 
织梦内容管理系统
} 

copyright dedecms

  内容来自dedecms 
void RotatePoint (POINT pt[], int iNum, int iAngle) 
织梦好,好织梦
{ 内容来自dedecms 
        int   i ; 

本文来自织梦

        POINT ptTemp ; 织梦内容管理系统 
      

织梦内容管理系统

        for (i = 0 ; i < iNum ; i++) 

织梦内容管理系统

        { 

内容来自dedecms

                       ptTemp.x = (int) (pt[i].x * cos (TWOPI * iAngle / 360) + 
copyright dedecms
                                      pt[i].y * sin (TWOPI * iAngle / 360)) ; 
dedecms.com
           copyright dedecms 
                       ptTemp.y = (int) (pt[i].y * cos (TWOPI * iAngle / 360) - 

织梦好,好织梦

                                      pt[i].x * sin (TWOPI * iAngle / 360)) ; 

dedecms.com

           
本文来自织梦
                       pt[i] = ptTemp ; copyright dedecms 
     } 内容来自dedecms 
} 

织梦好,好织梦

  织梦内容管理系统 
void DrawClock (HDC hdc) 织梦内容管理系统 
{ copyright dedecms 
        int   iAngle ; 

copyright dedecms

        POINT pt[3] ; dedecms.com 
        for (iAngle = 0 ; iAngle < 360 ; iAngle += 6) 内容来自dedecms 
     { 本文来自织梦 
                       pt[0].x        =   0 ; 
织梦内容管理系统
                       pt[0].y        = 900 ; copyright dedecms 
           

织梦好,好织梦

                       RotatePoint (pt, 1, iAngle) ; 
copyright dedecms
           
内容来自dedecms
                       pt[2].x        = pt[2].y = iAngle % 5 ? 33 : 100 ; copyright dedecms 
           本文来自织梦 
                       pt[0].x -      = pt[2].x / 2 ; 本文来自织梦 
                       pt[0].y -      = pt[2].y / 2 ; 

dedecms.com

           copyright dedecms 
                       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) ; 
本文来自织梦
       } dedecms.com 
} 织梦好,好织梦 
  dedecms.com 
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                            i, iAngle[3] ; 
织梦内容管理系统
        POINT                          ptTemp[3][5] ; 织梦好,好织梦 
      织梦好,好织梦 
        iAngle[0]      = (pst->wHour * 30) % 360 + pst->wMinute / 2 ; 织梦好,好织梦 
        iAngle[1]      =  pst->wMinute  *  6 ; 织梦好,好织梦 
        iAngle[2]      =  pst->wSecond  *  6 ; copyright dedecms 
      
dedecms.com
        memcpy (ptTemp, pt, sizeof (pt)) ; 
本文来自织梦
        for (i = fChange ? 0 : 2 ; i < 3 ; i++) 
dedecms.com
        { copyright dedecms 
                       RotatePoint (ptTemp[i], 5, iAngle[i]) ; 内容来自dedecms 
                       Polyline (hdc, ptTemp[i], 5) ; 织梦好,好织梦 
        } 内容来自dedecms 
} 本文来自织梦 
  

织梦好,好织梦

LRESULT CALLBACK WndProc (     HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam) 
copyright dedecms
{ 

内容来自dedecms

        static int        cxClient, cyClient ; 织梦内容管理系统 
        static SYSTEMTIME stPrevious ; 
dedecms.com
        BOOL                                          fChange ; copyright dedecms 
        HDC                                           hdc ; 

织梦内容管理系统

        PAINTSTRUCT                                   ps ; 

织梦内容管理系统

        SYSTEMTIME                                    st ; copyright dedecms 
           内容来自dedecms 
        switch (message) 本文来自织梦 
        { 

内容来自dedecms

        case    WM_CREATE : 
dedecms.com
                       SetTimer (hwnd, ID_TIMER, 1000, NULL) ; dedecms.com 
                       GetLocalTime (&st) ; 

本文来自织梦

                       stPrevious = st ; 

织梦好,好织梦

                       return 0 ; 
织梦内容管理系统
           织梦好,好织梦 
        case    WM_SIZE : 
织梦好,好织梦
                       cxClient = LOWORD (lParam) ; dedecms.com 
                       cyClient = HIWORD (lParam) ; 本文来自织梦 
                       return 0 ; 

本文来自织梦

           

copyright dedecms

        case    WM_TIMER : 
织梦好,好织梦
                       GetLocalTime (&st) ; 
copyright dedecms
                     
本文来自织梦
                       fChange =      st.wHour          !    = stPrevious.wHour || 

织梦内容管理系统

                                                             st.wMinute !           = stPrevious.wMinute ; 本文来自织梦 
           copyright dedecms 
                       hdc = GetDC (hwnd) ; 织梦好,好织梦 
           

copyright dedecms

                       SetIsotropic (hdc, cxClient, cyClient) ; 
内容来自dedecms
           copyright dedecms 
                       SelectObject (hdc, GetStockObject (WHITE_PEN)) ; dedecms.com 
                       DrawHands (hdc, &stPrevious, fChange) ; 内容来自dedecms 
           

copyright dedecms

                       SelectObject (hdc, GetStockObject (BLACK_PEN)) ; 

织梦内容管理系统

                       DrawHands (hdc, &st, TRUE) ; dedecms.com 
           dedecms.com 
                       ReleaseDC (hwnd, hdc) ; 
dedecms.com
           织梦好,好织梦 
                       stPrevious = st ; 

内容来自dedecms

                       return 0 ; 

copyright dedecms

           
内容来自dedecms
        case    WM_PAINT : 本文来自织梦 
                       hdc = BeginPaint (hwnd, &ps) ; 内容来自dedecms 
           本文来自织梦 
                       SetIsotropic (hdc, cxClient, cyClient) ; 本文来自织梦 
                       DrawClock    (hdc) ; 
本文来自织梦
                       DrawHands    (hdc, &stPrevious, TRUE) ; 

织梦内容管理系统

           
dedecms.com
                       EndPaint (hwnd, &ps) ; 本文来自织梦 
                       return 0 ; 

织梦内容管理系统

           

copyright dedecms

        case    WM_DESTROY : 

织梦好,好织梦

                       KillTimer (hwnd, ID_TIMER) ; 织梦好,好织梦 
                       PostQuitMessage (0) ; 织梦内容管理系统 
                       return 0 ; 
织梦好,好织梦
     } copyright dedecms 
        return DefWindowProc (hwnd, message, wParam, lParam) ; 本文来自织梦 
} dedecms.com 

CLOCK萤幕显示如图8-2

内容来自dedecms


  copyright dedecms

织梦好,好织梦

 

copyright dedecms

8-2 CLOCK的萤幕显示 dedecms.com

 

等方向性(isotropic)映射对於这样的应用来说是理想的,CLOCK.C中的SetIsotropic函式负责设定此模式。在呼叫SetMapMode之後,SetIsotropic将视窗范围设定为1000,并将视埠范围设定为显示区域的一半宽度和显示区域的负的一半高度。视埠原点被设定为显示区域的中心。我在第五章中讨论过,这将建立一个笛卡儿座标系,其点(0,0)位於显示区域的中心,在所有方向上的范围都是1000copyright dedecms

RotatePoint函式是用到三角函数的地方,此函式的三个参数分别是一个或者多个点的阵列、阵列中点的个数以及以度为单位的旋转角度。函式以原点为中心按顺时针方向(这对一个时钟正合适)旋转这些点。例如,如果传给函式的点是(0,100)-即12:00的位置-而角度为90度,那么该点将被变换为(100,0)-即3:00。它使用下列公式来做到这一点:

织梦好,好织梦

x' = x * cos (a) + y * sin (a) 

copyright dedecms

y' = y * cos (a) - x * sin (a) 织梦内容管理系统 

RotatePoint函式在绘制时钟表面的点和表针时都是有用的,我们将马上看到这一点。 本文来自织梦

DrawClock函式绘制60个时钟表面的点,从顶部(12:00)开始,其中每个点离原点900单位,因此第一个点位於(0,900),此後的每个点按顺时针依次增加6度。这些点中的l2个直径为100个单位;其余的为33个单位。使用Ellipse函式来画点。 本文来自织梦

DrawHands函式绘制时钟的时针、分针和秒针。定义表针轮廓(当它们垂直向上时的形状)的座标存放在一个POINT结构的阵列中。根据时间,这些座标使用RotatePoint函式进行旋转,并用WindowsPolyline函式进行显示。注意时针和分针只有当传递给DrawHandsbChange参数为TRUE时才被显示。当程式更新时钟的表针时,大多数情况下时针和分针不需要重画。 内容来自dedecms

现在让我们将注意力转到视窗讯息处理程式。在WM_CREATE讯息处理期间,视窗讯息处理程式取得目前时间并将它存放在名为dtPrevious的变数中,这个变数将在以後被用於确定时针或者分针从上次更新以来是否改变过。 dedecms.com

第一次绘制时钟是在第一个WM_PAINT讯息处理期间,这只不过是依次呼叫SetIsotropicDrawClockDrawHands,後者的bChange参数被设定为TRUE

本文来自织梦

WM_TIMER讯息处理期间,WndProc首先取得新的时间并确定是否需要重新绘制时针和分针。如果需要,则使用一个白色画笔和上一次时间绘制所有的表针,从而有效地擦除它们。否则,只对秒针使用白色画笔进行擦除,然後,再使用一个黑色画笔绘制所有的表针。

内容来自dedecms

以计时器进行状态报告
  织梦好,好织梦

本章的最後一个程式是我在第五章提到过的。它是一个使用GetPixel函式的好例子。 本文来自织梦

WHATCLR (见程式8-5)显示了滑鼠游标下目前图素的RGB颜色。 织梦内容管理系统

 程式8-5  WHATCLR 
copyright dedecms
WHATCLR.C 

织梦好,好织梦

/*-------------------------------------------------------------------------- 内容来自dedecms 
        WHATCLR.C -- Displays Color Under Cursor 

织梦内容管理系统

                                      (c) Charles Petzold, 1998 
织梦好,好织梦
---------------------------------------------------------------------------*/ copyright dedecms 
  织梦好,好织梦 
#include <windows.h> 
织梦好,好织梦
#define ID_TIMER    1 

内容来自dedecms

void FindWindowSize (int *, int *) ; 
内容来自dedecms
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; 

织梦内容管理系统

  dedecms.com 
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, 内容来自dedecms 
                                                             PSTR szCmdLine, int iCmdShow) 织梦好,好织梦 
{ 

织梦内容管理系统

        static TCHAR   szAppName[] = TEXT ("WhatClr") ; 织梦内容管理系统 
        HWND                                  hwnd ; 本文来自织梦 
        int                                   cxWindow, cyWindow ; 
织梦好,好织梦
        MSG                                   msg ; 织梦内容管理系统 
        WNDCLASS                       wndclass ; 

织梦好,好织梦

      

织梦内容管理系统

        wndclass.style                                = CS_HREDRAW | CS_VREDRAW ; 

织梦好,好织梦

        wndclass.lpfnWndProc                          = WndProc ; 

内容来自dedecms

        wndclass.cbClsExtra                           = 0 ; 内容来自dedecms 
        wndclass.cbWndExtra                           = 0 ; 
织梦好,好织梦
        wndclass.hInstance                            = hInstance ; 
内容来自dedecms
        wndclass.hIcon                                = LoadIcon (NULL, IDI_APPLICATION) ; 织梦好,好织梦 
        wndclass.hCursor                              = LoadCursor (NULL, IDC_ARROW) ; 本文来自织梦 
        wndclass.hbrBackground                    = (HBRUSH) GetStockObject (WHITE_BRUSH) ; 本文来自织梦 
       wndclass.lpszMenuName  = NULL ; 
dedecms.com
        wndclass.lpszClassName                    = szAppName ; 织梦内容管理系统 
      织梦好,好织梦 
        if (!RegisterClass (&wndclass)) 织梦内容管理系统 
        { 

织梦好,好织梦

                       MessageBox (   NULL, TEXT ("This program requires Windows NT!"), 
dedecms.com
                                                                     szAppName, MB_ICONERROR) ; 内容来自dedecms 
                       return 0 ; 内容来自dedecms 
        } 内容来自dedecms 
      本文来自织梦 
        FindWindowSize (&cxWindow, &cyWindow) ; 
内容来自dedecms
        hwnd = CreateWindow (szAppName, TEXT ("What Color"), 织梦好,好织梦 
               WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_BORDER, 本文来自织梦 
               CW_USEDEFAULT, CW_USEDEFAULT, 本文来自织梦 
               cxWindow, cyWindow, dedecms.com 
               NULL, NULL, hInstance, NULL) ; 
copyright dedecms
      内容来自dedecms 
        ShowWindow (hwnd, iCmdShow) ; 织梦好,好织梦 
        UpdateWindow (hwnd) ; 

织梦内容管理系统

      

织梦好,好织梦

        while (GetMessage (&msg, NULL, 0, 0)) 

内容来自dedecms

        { 内容来自dedecms 
                               TranslateMessage (&msg) ; 
内容来自dedecms
                               DispatchMessage (&msg) ; 
织梦内容管理系统
        } 织梦内容管理系统 
        return msg.wParam ; 
dedecms.com
} 
本文来自织梦
  

copyright dedecms

void FindWindowSize (int * pcxWindow, int * pcyWindow) 
dedecms.com
{ 本文来自织梦 
        HDC                   hdcScreen ; 
dedecms.com
        TEXTMETRIC     tm ; 
内容来自dedecms
  
copyright dedecms
        hdcScreen = CreateIC (TEXT ("DISPLAY"), NULL, NULL, NULL) ; 织梦内容管理系统 
        GetTextMetrics (hdcScreen, &tm) ; 

织梦好,好织梦

        DeleteDC (hdcScreen) ; copyright dedecms 
      内容来自dedecms 
        * pcxWindow = 2 *      GetSystemMetrics (SM_CXBORDER)  +  

dedecms.com

                                      12 * tm.tmAveCharWidth ; dedecms.com 
        * pcyWindow = 2 *      GetSystemMetrics (SM_CYBORDER)  + 

织梦好,好织梦

                            GetSystemMetrics (SM_CYCAPTION) +  copyright dedecms 
                                   2 * tm.tmHeight ; 
copyright dedecms
} 织梦内容管理系统 
  内容来自dedecms 
LRESULT CALLBACK WndProc (     HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam) 织梦好,好织梦 
{ 织梦内容管理系统 
        static COLORREF        cr, crLast ; 内容来自dedecms 
        static HDC                     hdcScreen ; 织梦好,好织梦 
        HDC                            hdc ; copyright dedecms 
        PAINTSTRUCT            ps ; 本文来自织梦 
        POINT                          pt ; 织梦内容管理系统 
        RECT                   rc ; 
内容来自dedecms
        TCHAR                          szBuffer [16] ; 

织梦内容管理系统

      

dedecms.com

        switch (message) 

dedecms.com

        { copyright dedecms 
        case    WM_CREATE: 内容来自dedecms 
                       hdcScreen = CreateDC (TEXT ("DISPLAY"), NULL, NULL, NULL) ; 内容来自dedecms 
                       SetTimer (hwnd, ID_TIMER, 100, NULL) ; 内容来自dedecms 
                       return 0 ; 本文来自织梦 
           本文来自织梦 
        case    WM_TIMER: 

织梦好,好织梦

                       GetCursorPos (&pt) ; 
织梦内容管理系统
                       cr = GetPixel (hdcScreen, pt.x, pt.y) ; 织梦内容管理系统 
  本文来自织梦 
                       SetPixel (hdcScreen, pt.x, pt.y, 0) ; copyright dedecms 
           

织梦内容管理系统

                       if (cr != crLast) 织梦内容管理系统 
                       { 
织梦内容管理系统
                               crLast = cr ; 

copyright dedecms

                               InvalidateRect (hwnd, NULL, FALSE) ; 

织梦好,好织梦

                       } 
本文来自织梦
                       return 0 ; 内容来自dedecms 
           

dedecms.com

        case    WM_PAINT: copyright dedecms 
                       hdc = BeginPaint (hwnd, &ps) ; 织梦内容管理系统 
           

dedecms.com

                       GetClientRect (hwnd, &rc) ; 
dedecms.com
           copyright dedecms 
                       wsprintf (szBuffer, TEXT ("  %02X %02X %02X  "), 织梦内容管理系统 
                               GetRValue (cr), GetGValue (cr), GetBValue (cr)) ; dedecms.com 
           织梦好,好织梦 
                       DrawText (hdc, szBuffer, -1, &rc, 织梦好,好织梦 
                           DT_SINGLELINE | DT_CENTER | DT_VCENTER) ; 
内容来自dedecms
           织梦内容管理系统 
                       EndPaint (hwnd, &ps) ; 织梦内容管理系统 
                       return 0 ; dedecms.com 
           
本文来自织梦
        case    WM_DESTROY: dedecms.com 
                      DeleteDC (hdcScreen) ; 
织梦好,好织梦
                       KillTimer (hwnd, ID_TIMER) ; 
织梦内容管理系统
                       PostQuitMessage (0) ; 

织梦好,好织梦

                       return 0 ; 

织梦好,好织梦

        } 

内容来自dedecms

        return DefWindowProc (hwnd, message, wParam, lParam) ; 织梦好,好织梦 
} 

内容来自dedecms

WHATCLRWinMain中做了一点与以往不同的事。因为WHATCLR的视窗只需要显示十六进位RGB值那么大,所以它在CreateWindow函式中使用WS_BORDER视窗样式建立了一个不能改变大小的视窗。要计算视窗的大小,WHATCLR通过先呼叫CreateIC再呼叫GetSystemMetrics以取得用於视讯显示的装置内容资讯。计算好的视窗宽度和高度值被传递给CreateWindow织梦好,好织梦

WHATCLR的视窗讯息处理程式在处理WM_CREATE讯息处理期间,呼叫CreateDC建立了用於整个视讯显示的装置内容。这个装置内容在程式的生命周期内都有效。在处理WM_TIMER讯息处理期间,程式取得目前滑鼠游标位置的图素。在处理WM_PAINT讯息处理期间显示RGB颜色。 织梦内容管理系统

您可能想知道,从CreateDC函式中取得的装置内容代号是否能让您在萤幕的任意位置显示一些东西,而不光只是取得图素颜色。答案是可以的,一般而言,让一个应用程式在另一个程式控制的画面区域上画图是不好的,但在某些特殊情况下,这可能会非常有用。 本文来自织梦