SetTimer的使用問題

導讀:
  定時器的使用:三種方法
  如果您需要在整個程序執行期間都使用定時器,那麼您將得從WinMain函數中或者在處理WM_CREATE消息時呼叫SetTimer,並在退出WinMain或響應WM_DESTROY消息時呼叫KillTimer。根據呼叫SetTimer時使用的參數,可以下列三種方法之一使用定時器。
  方法一
  這是最方便的一種方法,它讓Windows把WM_TIMER消息發送到應用程序的正常窗口消息處理程序中,SetTimer呼叫如下所示:
  SetTimer (hwnd, 1, uiMsecInterval, NULL) ;
  第一個參數是其窗口消息處理程序將接收WM_TIMER消息的窗口句柄。第二個參數是定時器ID,它是一個非0數值,在整個例子中假定爲1。第三個參數是一個32位無正負號整數,以毫秒爲單位指定一個時間間隔,一個60,000的值將使Windows每分鐘發送一次WM_TIMER消息。
  您可以通過呼叫
  KillTimer (hwnd, 1) ;
  方法二
  設定定時器的第一種方法是把WM_TIMER消息發送到通常的窗口消息處理程序,而第二種方法是讓Windows直接將定時器消息發送給您程序的另一個函數。
  SetTimer (hwnd, iTimerID, iMsecInterval, TimerProc) ;
  方法三
  設定定時器的第三種方法類似於第二種方法,只是傳遞給SetTimer的hwnd參數被設定爲NULL,並且第二個參數(通常爲定時器ID)被忽略了,最後,此函數傳回定時器ID:
  iTimerID = SetTimer (NULL, 0, wMsecInterval, TimerProc) ;
  如果沒有可用的定時器,那麼從SetTimer傳回的iTimerID值將爲NULL。
  KillTimer的第一個參數(通常是窗口句柄)也必須爲NULL,定時器ID必須是SetTimer的傳回值:
  KillTimer (NULL, iTimerID) ;
  --------------------
  void CALLBACK TimerProc(HWND hWnd,UINT nMsg,UINT nTimerid,DWORD dwTime);
  SDK編程筆記 — 計時器篇兩個計時器API的討論
  SetTimer函數用於創建一個計時器,KillTimer函數用於銷燬一個計時器。計時器屬於系統資源,使用完應及時銷燬。
  SetTimer的函數原型如下:
  UINT_PTR SetTimer( HWND hWnd, UINT_PTR nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc ) ;
  其中
  hWnd是和timer關聯的窗口句柄,此窗口必須爲調用SetTimer的線程所有;如果hWnd爲NULL,沒有窗口和timer相關聯並且nIDEvent參數被忽略
  nIDEvent是timer的標識,爲非零值;如果hWnd爲NULL則被忽略;如果hWnd非NULL而且與timer相關聯的窗口已經存在一個爲此標識的timer,則此次SetTimer調用將用新的timer代替原來的timer。timer標識和窗口相關,兩個不同的窗口可以擁有nIDEvent相同的tiemr
  uElapse是以毫秒指定的計時間隔值,範圍爲1毫秒到4,294,967,295毫秒(將近50天),這個值指示Windows每隔多久時間給程序發送WM_TIMER消息。
  lpTimerFunc是一個回調函數的指針,俗稱TimerFunc;如果lpTimerFunc爲NULL,系統將嚮應用程序隊列發送WM_TIMER消息;如果lpTimerFunc指定了一個值,DefWindowProc將在處理WM_TIMER消息時調用這個lpTimerFunc所指向的回調函數,因此即使使用TimerProc代替處理WM_TIMER也需要向窗口分發消息。
  關於SetTimer的返回值:如果hWnd爲NULL,返回值爲新建立的timer的ID,如果hWnd非NULL,返回一個非0整數,如果SetTimer調用失敗則返回0
  KillTimer的函數原型爲:BOOL KillTimer( HWND hWnd, UINT_PTR uIDEvent ) ;參數意義同SetTimer。
  關於KillTimer對消息隊列中剩餘未處理的WM_TIMER消息的影響,MSDN和Programming Windows上的說法完全相反。MSDN的說法很乾脆:The KillTimer function does not remove WM_TIMER messages already posted to the message queue. 而petzold則說 The KillTimer call purges the message queue of any pending WM_TIMER messages. Your program will never receive a stray WM_TIMER message following a KillTimer call. (KillTimer消除消息隊列中任何未處理的WM_TIMER消息,調用KillTimer後你的程序永遠不會收到一條“漂泊遊蕩”的WM_TIMER消息)
  關於WM_TIMER消息
  wParam爲計時器的ID;如果需要設定多個計時器,那麼對每個計時器都使用不同的計時器ID。wParam的值將隨傳遞到窗口過程中的WM_TIMER消息的不同而不同。
  lParam爲指向TimerProc的指針,如果調用SetTimer時沒有指定TimerProc(參數值爲NULL),則lParam爲0(即NULL)。
  可以通過在窗口過程中提供一個WM_TIMER case處理這個消息,或者,默認窗口過程會調用SetTimer中指定的TimerProc來處理WM_TIMER消息
  使用計時器的三種方法
  如果在程序的整個執行過程中使用計時器,一般在處理WM_CREATE消息時或WinMain中消息循環前調用SetTimer,在處理WM_DESTROY消息時或在WinMain中消息循環後return前調用KillTimer。根據SetTimer中的參數不同,有三種方法使用計時器。
  方法一:調用SetTimer時指定窗口句柄hWnd,nIDEvent中指定計時器ID,將lpTimerFunc置NULL從而不使用TimerProc;在窗口過程中處理WM_TIMER消息。調用KillTimer時,使用SetTimer中指定的hWnd和id。最好使用#define定義timer的id,例如:
  #define ID_TIMER 1
  SetTimer(hWnd,ID_TIMER,1000,NULL) ;
  KillTimer(hWnd,ID_TIMER) ;
  方法二:調用SetTimer時指定窗口句柄hWnd,nIDEvent中指定計時器ID,lpTimerFunc參數不爲NULL而指定爲TimerProc函數的指針。這種方法使用TimerProc函數(名字可自定)處理WM_TIMER消息:
  VOID CALLBACK TimerProc ( HWND hwnd, UINT message, UINT iTimerID, DWORD dwTime)
  {
  //處理WM_TIMER訊息
  }
  TimerProc的參數hwnd是在調用SetTimer時指定的窗口句柄。Windows只把WM_TIMER消息送給TimerProc,因此消息參數總是等於WM_TIMER。iTimerID值是計時器ID,dwTimer值是與從GetTickCount函數的返回值相容的值。這是自Windows啓動後所經過的毫秒數。 使用這種方法時,相關函數調用的形式爲:
  SetTimer(hWnd,ID_TIMER,1000,TimerProc) ;
  KillTimer(hWnd,ID_TIMER) ;
  方法三:調用SetTimer時不指定窗口句柄(爲NULL),iTimerID參數自然被忽略,lpTimerFunc不爲NULL而指定爲TimerProc的指針。正如上面SetTimer的討論中所說的,此時SetTimer的返回值正是新建立的計時器的ID,需將這個ID保存以供KillTimer銷燬計時器時所用。當然,KillTimer的hWnd參數也置爲NULL。這種方法同樣用TimerProc處理WM_TIMER消息。
  UINT_PTR iTimerID ;
  iTimerID = SetTimer(NULL,0,1000,TimerProc) ;
  KillTimer(NULL,iTimerID) ;
  使用這種方法的好處是不必自己指定計時器ID,這樣就不必擔心用錯ID。
  使用多個計時器
  使用多個計時器只要在建立計時器時指定不同的ID。比如用上面所述方法一時的情況:
  #define TIMER_SEC 1
  #define TIMER_MIN 2
  然後使用兩個SetTimer來設定兩個計時器:
  SetTimer (hwnd, TIMER_SEC, 1000, NULL) ;
  SetTimer (hwnd, TIMER_MIN, 60000, NULL) ;
  WM_TIMER的處理如下所示:
  case WM_TIMER:
  switch (wParam)
  {
  case TIMER_SEC:
  //每秒一次的處理
  break ;
  case TIMER_MIN:
  //每分鐘一次的處理
  break ;
  }
  return 0 ;
  改變計時器的時間間隔
  如果想將一個已經存在的計時器設定爲不同的時間間隔,可以簡單地用不同的時間值再次調用SetTimer。
  計時器精確嗎?
  計時器並不精確。有兩個原因:
  原因一:Windows計時器是硬件和ROM BIOS架構下之計時器一種相對簡單的擴充。回到Windows以前的MS-DOS程序寫作環境下,應用程式能夠通過攔截者稱爲timer tick的BIOS中斷來實現時鐘或計時器。一些爲MS-DOS編寫的程序自己攔截這個硬件中斷以實現時鐘和計時器。這些中斷每54.915毫秒產生一次,或者大約每秒18.2次。這是原始的IBM PC的微處理器頻率值4.772720 MHz被218所除而得出的結果。在Windows 98中,計時器與其下的PC計時器一樣具有55毫秒的解析度。在Microsoft Windows NT中,計時器的解析度爲10毫秒。Windows應用程式不能以高於這些解析度的頻率(在Windows 98下,每秒18.2次,在Windows NT下,每秒大約100次)接收WM_TIMER消息。在SetTimer中指定的時間間隔總是截尾後tick數的整數倍。例如,1000毫秒的間隔除以54.925毫秒,得到18.207個tick,截尾後是18個tick,它實際上是989毫秒。對每個小於55毫秒的間隔,每個tick都會產生一個WM_TIMER消息。
  可見,計時器並不能嚴格按照指定的時間間隔發送WM_TIMER消息,它總要相差那麼幾毫秒。
  即使忽略這幾個毫秒的差別,計時器仍然不精確。請看原因二:
  WM_TIMER消息放在正常的消息隊列之中,和其他消息排列在一起,因此,如果在SetTimer中指定間隔爲1000毫秒,那麼不能保證程序每1000毫秒或者989毫秒就會收到一個WM_TIMER消息。如果其他程序的執行事件超過一秒,在此期間內,您的程式將收不到任何WM_TIMER訊息。事實上, Windows對WM_TIMER消息的處理非常類似於對WM_PAINT消息的處理,這兩個消息都是低優先級的,程序只有在消息隊列中沒有其他消息時才接收它們。
  WM_TIMER還在另一方面和WM_PAINT相似:Windows不能持續向消息隊列中放入多個WM_TIMER訊息,而是將多餘的WM_TIMER消息組合成一個消息。因此,應用程序不會一次收到多個這樣的消息,儘管可能在短時間內得到兩個WM_TIMER消息。應用程序不能確定這種處理方式所導致的WM_TIMER消息「遺漏」的數目。
  可見,WM_TIMER消息並不能及時被應用程序所處理,WM_TIMER在消息隊列中的延誤可能就不能用毫秒來計算了。
  由以上兩點,你不能通過在處理WM_TIMER時一秒一秒計數的方法來計時。如果要實現一個時鐘程序,可以使用系統的時間函數如GetLocalTime ,而在時鐘程序中,計時器的作用是定時調用GetLocalTime獲得新的時間並刷新時鐘畫面,當然這個刷新的間隔要等於或小於1秒。
  SetTimer時鐘函數問題
  如下是一個定時每隔40ms顯示Hello World字符串的程序,編譯鏈接通過了,但是爲何不能顯示Hello World字符串,請高手解答:
  #include
  #include
  void CALLBACK clockFunction(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
  {
  /* process the command data from ESS, state req data from ess, state */
  /* req ack data from stage client */
  printf("Hello World!/n");
  }
  int main(void)
  {
  UINT timer_id;
  int i;
  UINT time_out = 40;
  timer_id = SetTimer(NULL, 1, time_out, clockFunction);
  printf("timer_id is %d/n", timer_id);
  while(1)
  {
  i++;
  }
  return 0;
  }
  謝謝!
  記時器應該是在 Windows 消費循環中使用的。因此,原因是用法不對。
  以你的代碼來說,只有一個主線程,而這個主線程不管做什麼,它都沒空或者說沒辦法執行回調代碼,是不是這樣?:)
  程序一直在
  while(1)
  {
  i++;
  }
  處運行。而且,還會越界。
  timer是windows message觸發的
  int main(void)
  {
  UINT timer_id;
  int i;
  UINT time_out = 40;
  timer_id = SetTimer(NULL, 1, time_out, clockFunction);
  printf("timer_id is %d/n", timer_id);
  while(1)
  {
  Sleep(1000);
  }
  return 0;
  }
  int main(void)
  {
  unsigned long time_out = 400;
  unsigned long after,before;
  while(1)
  {
  after = GetTickCount();
  if(after - before >= time_out)
  {
  printf("Hello World!/n");
  before = after;
  }
  }
  return 0;
  }
  記時器應該是在 Windows 消費循環中使用的。因此,原因是用法不對。
  以你的代碼來說,只有一個主線程,而這個主線程不管做什麼,它都沒空或者說沒辦法執行回調代碼,是不是這樣?:)
  程序一直在
  while(1)
  {
  i++;
  }
  處運行。而且,還會越界。
  timer是windows message觸發的
  int main(void)
  {
  UINT timer_id;
  int i;
  UINT time_out = 40;
  timer_id = SetTimer(NULL, 1, time_out, clockFunction);
  printf("timer_id is %d/n", timer_id);
  while(1)
  {
  Sleep(1000);
  }
  return 0;
  }
  int main(void)
  {
  unsigned long time_out = 400;
  unsigned long after,before;
  while(1)
  {
  after = GetTickCount();
  if(after - before >= time_out)
  {
  printf("Hello World!/n");
  before = after;
  }
  }
  return 0;
  }
  記時器應該是在 Windows 消費循環中使用的。因此,原因是用法不對。
  以你的代碼來說,只有一個主線程,而這個主線程不管做什麼,它都沒空或者說沒辦法執行回調代碼,是不是這樣?:)
  程序一直在
  while(1)
  {
  i++;
  }
  處運行。而且,還會越界。
  timer是windows message觸發的
  int main(void)
  {
  UINT timer_id;
  int i;
  UINT time_out = 40;
  timer_id = SetTimer(NULL, 1, time_out, clockFunction);
  printf("timer_id is %d/n", timer_id);
  while(1)
  {
  Sleep(1000);
  }
  return 0;
  }
  int main(void)
  {
  unsigned long time_out = 400;
  unsigned long after,before;
  while(1)
  {
  after = GetTickCount();
  if(after - before >= time_out)
  {
  printf("Hello World!/n");
  before = after;
  }
  }
  return 0;
  }
  SetTimer()函數的剖析(API)
  SetTimer函數
  通常有兩種方法來實現:發送WM_TIMER消息和調用應用程序定義的回調函數。
  UINT_PTR SetTimer(
  HWND hWnd, // 窗口句柄
  UINT_PTR nIDEvent, // 定時器ID,多個定時器時,可以通過該ID判斷是哪個定時器
  UINT uElapse, // 時間間隔,單位爲毫秒
  TIMERPROC lpTimerFunc // 回調函數
  ); //返回值也是定時器ID
  BOOL KillTimer(
  HWND hWnd, //窗口句柄
  UINT_PTR uIDEvent //指定銷燬的定時器ID
  );
  兩個函數配對使用。指定的窗口句柄相同,當窗口句柄爲空時,KillTimer使用返回值標識。
  在MFC程序中SetTimer被封裝在CWnd類中,調用就不用指定窗口句柄了。
  回調函數系統默認爲OnTimer(),即當第四個參數爲NULL時。

本文轉自
http://www.bokele.com/?CH=Log&ArticleID=32370
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章