本文總結來源出自雞啄米,感謝雞啄米。來源:http://www.jizhuomi.com/software/232.html
定時器簡介
定時器,可以幫助開發者或者用戶定時完成某項任務。在使用定時器時,我們可以給系統傳入一個時間間隔數據,然后系統就會在每個此時間間隔后觸發定時處理程序,實現周期性的自動操作。例如,我們可以在數據采集系統中,為定時器設置定時采集時間間隔為1個小時,那么每隔1個小時系統就會采集一次數據,這樣就可以在無人操作的情況下准確的進行操作。
MFC定時器
VS2010編程中,我們可以使用MFC的CWnd類提供的成員函數SetTimer實現定時器功能,也可以使用Windows API函數SetTimer來實現。兩者使用方法實際上很類似,但也有不同。
CWnd類的SetTimer成員函數只能在CWnd類或其派生類中調用,而API函數SetTimer則沒有這個限制,這是一個很重要的區別。因為本教程主要是講解MFC編程,所以這里就先重點講解MFC定時器的用法,關於API函數SetTimer的用法雞啄米會在MFC定時器講解的基礎上進行延伸。
雞啄米下面分步驟給出使用MFC定時器的方法。
1、啟動定時器。
啟動定時器就需要使用CWnd類的成員函數SetTimer。CWnd::SetTimer的原型如下:
UINT_PTR SetTimer(
UINT_PTR nIDEvent,
UINT nElapse,
void (CALLBACK* lpfnTimer
)(HWND,
UINT,
UINT_PTR,
DWORD
)
);
參數nIDEvent指定一個非零的定時器ID;參數nElapse指定間隔時間,單位為毫秒;參數lpfnTimer指定一個回調函數的地址,如果該參數為NULL,則WM_TIMER消息被發送到應用程序的消息隊列,並被CWnd對象處理。如果此函數成功則返回一個新的定時器的ID,我們可以使用此ID通過KillTimer成員函數來銷毀該定時器,如果函數失敗則返回0。
通過SetTimer成員函數我們可以看出,處理定時事件可以有兩種方式,一種是通過WM_TIMER消息的消息響應函數,一種是通過回調函數。
如果要啟動多個定時器就多次調用SetTimer成員函數。另外,在不同的CWnd中可以有ID相同的定時器,並不沖突。
2、為WM_TIMER消息添加消息處理函數,或者定義回調函數。
如果調用CWnd::SetTimer函數時最后一個參數為NULL,則通過WM_TIMER的消息處理函數來處理定時事件。添加WM_TIMER消息的處理函數的方法是,在VS2010工程的Class View類視圖中找到要添加定時器的類,點擊右鍵,選擇Properties,顯示其屬性頁,然后在屬性頁工具欄上點擊Messages按鈕,下面列表就列出了所有消息,找到WM_TIMER消息,添加消息處理函數。添加后,cpp文件中會出現類似如下內容:
- BEGIN_MESSAGE_MAP(CExample44Dlg, CDialogEx)
- ......
- ON_WM_TIMER()
- END_MESSAGE_MAP()
- void CExample44Dlg::OnTimer(UINT_PTR nIDEvent)
- {
- // TODO: Add your message handler code here and/or call default
- CDialogEx::OnTimer(nIDEvent);
- }
之后就可以在OnTimer函數中進行相應的處理了。OnTimer的參數nIDEvent為定時器ID,即在SetTimer成員函數中指定的定時器ID,如果有多個定時器,我們可以像下面這樣處理:
- void CExample44Dlg::OnTimer(UINT_PTR nIDEvent)
- {
- // TODO: Add your message handler code here and/or call default
- switch (nIDEvent)
- {
- case 1:
- // 如果收到ID為1的定時器的消息則調用func1函數
- func1();
- break;
- case 2:
- // 如果收到ID為2的定時器的消息則調用func2函數
- fun2();
- break;
- ......
- default:
- break;
- }
- CDialogEx::OnTimer(nIDEvent);
- }
如果調用CWnd::SetTimer函數時最后一個參數不為NULL,則需要定義回調函數。回調函數的形式如下:
- void CALLBACK EXPORT TimerProc(
- HWND hWnd, // handle of CWnd that called SetTimer
- UINT nMsg, // WM_TIMER
- UINT nIDEvent // timer identification
- DWORD dwTime // system time
- );
參數hWnd為調用SetTimer成員函數的CWnd對象的句柄,即擁有此定時器的窗口的句柄;參數nMsg為WM_TIMER,而且總是為WM_TIMER;參數nIDEvent為定時器ID;參數dwTime為系統啟動以來的毫秒數,即GetTickCount函數的返回值。
這樣CWnd::SetTimer函數最后一個參數就可以為TimerProc。
這里注意下,回調函數的名稱不一定為TimerProc,可以取其他名字,但返回值類型、參數的類型和個數不能改變。
雞啄米給出一個回調函數的例子:
- void CALLBACK EXPORT TimerProc(HWND hWnd,UINT nMsg,UINT nTimerid,DWORD dwTime)
- {
- switch(nTimerid)
- {
- case 1:
- // 處理ID為1的定時器的事件
- func1();
- break;
- case 2:
- // 處理ID為2的定時器的事件
- func2();
- break;
- ......
- default:
- break;
- }
- }
回調函數為全局函數,需要寫在使用它的位置的前面,或者寫在后面然后在使用之前聲明。
3、銷毀定時器。
不再使用定時器時,可以銷毀它。銷毀定時器需使用CWnd類的KillTimer成員函數,CWnd::KillTimer函數的原型如下:
- BOOL KillTimer(UINT_PTR nIDEvent);
參數nIDEvent為要銷毀的定時器的ID,是調用CWnd::SetTimer函數時設置的定時器ID。如果定時器被銷毀則返回TRUE,而如果沒有找到指定的定時器則返回FALSE。
如果要銷毀多個定時器,則多次調用KillTimer函數並分別傳入要銷毀的定時器的ID。
通過Windows API函數使用定時器
如果我們不使用MFC定時器,而通過Windows API函數使用定時器,其實是很類似的。下面雞啄米簡單說下步驟吧。
1、啟動定時器。
使用API函數SetTimer啟動定時器,SetTimer函數的原型如下:
- UINT_PTR SetTimer(
- HWND
- hWnd,
- UINT_PTR
- nIDEvent,
- UINT
- uElapse,
- TIMERPROC
- lpTimerFunc
- );
參數hWnd為與定時器關聯的窗口的句柄;參數nIDEvent為非零的定時器ID,如果hWnd等於NULL,且還不存在ID為nIDEvent的定時器,那么nIDEvent參數被忽略,然后生成一個新ID的定時器,而如果hWnd不為NULL,且hWnd指定的窗口已存在ID為nIDEvent的定時器,那么這個已存在的定時器被新定時器所取代。參數uElapse和lpTimerFunc同CWnd::SetTimer函數。
2、為WM_TIMER消息添加消息處理函數,或者定義回調函數。
如果調用SetTimer函數時最后一個參數為NULL,我們需要自己為WM_TIMER消息添加處理函數,要注意的是,WM_TIMER消息的附加數據wParam為定時器ID,lParam為回調函數的指針,如果調用SetTimer時回調函數為NULL,那么lParam也為NULL。
而如果調用SetTimer函數時最后一個參數不為NULL,我們就需要定義回調函數。回調函數的定義同MFC定時器。
3、銷毀定時器。
銷毀定時器使用KillTimer API函數,原型如下:
- BOOL KillTimer(HWND hWnd,UINT_PTR uIDEvent);
參數hWnd為與定時器關聯的窗口的句柄,與啟動定時器時SetTimer函數的hWnd參數值相同;參數uIDEvent為要銷毀的定時器的ID,如果傳遞給SetTimer的參數hWnd有效,則uIDEvent應與傳遞給SetTimer的參數nIDEvent相同,而如果SetTimer的參數hWnd為NULL,則uIDEvent應為SetTimer返回的定時器ID。該函數成功則返回TRUE,否則返回FALSE。
MFC定時器應用實例
雞啄米給大家演示一個定時器的例子,該實例功能很簡單,就是使用兩個定時器,定時更新兩個編輯框中的顯示內容,第一個編輯框每秒刷新一次,從1刷新到10,然后銷毀定時器,第二個編輯框每兩秒刷新一次,從1刷新到5,然后銷毀定時器。下面簡單說下步驟:
1、創建基於對話框的工程,名稱設為“Example44”。
2、在自動生成的對話框模板IDD_EXAMPLE44_DIALOG中,刪除“TODO: Place dialog controls here.”靜態文本控件。添加兩個靜態文本框控件,Caption分別設為“1秒鍾刷新一次”和“2秒鍾刷新一次”,再添加兩個個Edit Control控件,ID使用默認的IDC_EDIT1和IDC_EDIT2,兩者的Read Only屬性都設為True。此時的對話框模板如下圖:
3、為CExample44Dlg類添加兩個成員變量,分別為m_nData1、m_nData2,並在CExample44Dlg類的構造函數中初始化:
- CExample44Dlg::CExample44Dlg(CWnd* pParent /*=NULL*/)
- : CDialogEx(CExample44Dlg::IDD, pParent)
- {
- m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
- // 兩個數據初始化為0
- m_nData1 = 0;
- m_nData2 = 0;
- }
4、在對話框模板上雙擊OK按鈕,添加點擊消息的處理函數,並修改如下:
- void CExample44Dlg::OnBnClickedOk()
- {
- // TODO: Add your control notification handler code here
- // 啟動ID為1的定時器,定時時間為1秒
- SetTimer(1, 1000, NULL);
- // 啟動ID為2的定時器,定時時間為2秒
- SetTimer(2, 2000, NULL);
- //CDialogEx::OnOK();
- }
這樣,點擊OK按鈕時就不會退出,而是啟動兩個定時器。
5、根據上面MFC定時器講解中為WM_TIMER消息添加處理函數的方法,添加WM_TIMER的消息處理函數OnTimer,並修改其實現如下:
- void CExample44Dlg::OnTimer(UINT_PTR nIDEvent)
- {
- // TODO: Add your message handler code here and/or call default
- switch (nIDEvent)
- {
- case 1:
- // 如果m_nData1已經達到10,則銷毀ID為1的定時器
- if (10 == m_nData1)
- {
- KillTimer(1);
- break;
- }
- // 刷新編輯框IDC_EDIT1的顯示
- SetDlgItemInt(IDC_EDIT1, ++m_nData1);
- break;
- case 2:
- // 如果m_nData2已經達到5,則銷毀ID為2的定時器
- if (5 == m_nData2)
- {
- KillTimer(2);
- break;
- }
- // 刷新編輯框IDC_EDIT2的顯示
- SetDlgItemInt(IDC_EDIT2, ++m_nData2);
- default:
- break;
- }
- CDialogEx::OnTimer(nIDEvent);
- }
6、運行程序,點擊OK按鈕,查看效果。
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
1、在類中定義
afx_msg void OnTimer(UINT nIDEvent);
2、在CPP中添加
BEGIN_MESSAGE_MAP(YuJingDlg, CDialog)
ON_WM_CTLCOLOR() //顏色消息
//ON_WM_PAINT()
ON_WM_TIMER
ON_BN_CLICKED(IDCANCEL, &YuJingDlg::OnBnClickedCancel)
END_MESSAGE_MAP()
3、在初始話函數或其他按鈕響應中添加
SetTimer(1,50,NULL); //定時器1,50ms刷新一次,使用OnTimer函數
4、寫OnTimer函數
void YuJingDlg::OnTimer(UINT nIDEvent)
{
th+=0.157;
if(th>6.28) th-=6.28;
//Invalidate();
InvalidateRect(CRect(0,0,500,500)); //刷新區域
CDialog::OnTimer(nIDEvent);
}