目前,Windows软件一般使用Timer定时器进行定时。Timer定时器是由应用程序响应定时消息WM_TIMER实现定时。Timer定时器是IBM PC硬件和ROM BIOS构造的定时器的简单扩充。PC的ROM初始化8253定时器来产生硬件中断08H,而08H中断的频率为18.2Hz,即至少每隔54.925 ms中断一次。此外,这个定时消息的优先权太低,只有在除WM_PAINT外的所有消息被处理完后,才能得到处理。多媒体定时器也是利用系统定时器工作的,但它的工作机理和普通定时器有所不同。首先,多媒体定时器可以按精度要求设置8253的T/C0通道的计数初值,使定时器不存在54.945ms的限制;其次,多媒体定时器不依赖于消息机制,而是用函数产生一个独立的线程,在一定的中断次数到达后,直接调用预先设置好的回调函数进行处理,不必等到应用程序的消息队列为空,从而切实保障了定时中断得到实时响应,使其定时精度可达1ms。
调用timeGetDevCaps可以获得定时服务参数。
timeGetDevCaps函数的定义如下:
MMRESULT timeGetDevCaps(LPTIMECAPS ptc, UINT cbtc);
调用timeBeginPeriod函数可以设置定时的最小分辨率。
timeBeginPeriod函数的定义如下:
MMRESULT timeBeginPeriod(UINT uPeriod);
调用timeSetEvent设置并启动定时器。
timeSetEvent函数的定义如下:
MMRESULT timeSetEvent(UINT uDelay, UINT uResolution, LPTIMECALLBACK lpTimeProc, DWORD dwUser, UINT fuEvent);
调用timeKillEvent函数可以取消定时器。
timeKillEvent函数的定义如下:
MMRESULT timeKillEvent(UINT uTimerID);
(1)创建一个基于对话框的应用程序,名称为Demo。
(2)在IDD_DEMO_DIALOG对话框资源中添加控件,如表所示。
类型 | ID | 标题 |
Edit | IDC_TIME |
|
Button | IDC_TEST1 | 设置定时器 |
Button | IDC_TEST2 | 取消定时器 |
(3)创建1个基类为CWinThread的类CTimerThread。
(4)在TimerThread.h文件中定义宏,代码如下:
#define WM_THREADMSG WM_USER+1
(5)在TimerThread.h文件中定义数据结构,代码如下:
typedef struct THREAD_PARAM
{
HWND hWnd;
int nTime;
HANDLE hTimerEvent;
HANDLE hExitEvent;
}_THREAD_PARAM;
(6)在CWinThread类中添加成员变量,代码如下:
public:
THREAD_PARAM* m_pThreadParam;
(7)在CTimerThread类中重载CWinThread::Run函数,代码如下:
// TimerThread.cpp
int CTimerThread::Run()
{
while (TRUE)
{
//等待定时事件
while (WAIT_TIMEOUT ==
WaitForSingleObject(m_pThreadParam->hTimerEvent, 200))
{
//等待退出事件
if (WaitForSingleObject(m_pThreadParam->hExitEvent, 0) == WAIT_OBJECT_0)
{
::AfxEndThread(0);
}
}
m_pThreadParam->nTime += 100;
//向主线程窗口发送消息
::PostMessage(m_pThreadParam->hWnd, WM_THREADMSG, 0, 0);
//复位定时事件
ResetEvent(m_pThreadParam->hTimerEvent);
}
}
(8)在CDemoDlg类中添加成员变量,代码如下:
// DemoDlg.cpp
private:
UINT m_nTimerID;
CTimerThread* m_pTimerThread;
THREAD_PARAM m_ThreadParam;
(9)在CDemoDlg类的构造函数和析构函数中添加如下代码:
// DemoDlg.cpp
CDemoDlg::CDemoDlg(CWnd* pParent /*=NULL*/)
: CDialog(CDemoDlg::IDD, pParent)
{
// …
m_nTimerID = 0;
m_pTimerThread = NULL;
m_ThreadParam.nTime = 0;
m_ThreadParam.hTimerEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
m_ThreadParam.hExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
}
CDemoDlg::~CDemoDlg()
{
//取消定时器
if (m_nTimerID != 0)
{
timeKillEvent(m_nTimerID);
}
//结束定时线程
if (m_pTimerThread != NULL)
{
//设置退出事件
SetEvent(m_ThreadParam.hExitEvent);
//等待定时线程结束
::WaitForSingleObject(m_pTimerThread->m_hThread, INFINITE);
delete m_pTimerThread;
m_pTimerThread = NULL;
}
CloseHandle(m_ThreadParam.hTimerEvent);
CloseHandle(m_ThreadParam.hExitEvent);
}
(10)在CDemoDlg类中为重载CDialog::OnInitDialog函数,代码如下:
// DemoDlg.cpp
BOOL CDemoDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// …
SetDlgItemInt(IDC_TIME, 0);
return TRUE;
}
(11)在CDemoDlg类中为WM_THREADMSG添加消息处理函数,代码如下:
// DemoDlg.cpp
BEGIN_MESSAGE_MAP(CDemoDlg, CDialog)
//{{AFX_MSG_MAP(CDemoDlg)
// …
ON_MESSAGE(WM_THREADMSG, OnMsgFunc)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
LRESULT CDemoDlg::OnMsgFunc()
{
SetDlgItemInt(IDC_TIME, m_ThreadParam.nTime);
return 1;
}
(12)在CDemoDlg类中为Button控件添加BN_CLICKED消息处理函数,代码如下:
// DemoDlg.cpp
void CDemoDlg::OnTest1()
{
//取消定时器
if (m_nTimerID != 0)
{
timeKillEvent(m_nTimerID);
}
//结束定时线程
if (m_pTimerThread != NULL)
{
//设置退出事件
SetEvent(m_ThreadParam.hExitEvent);
//等待定时线程结束
::WaitForSingleObject(m_pTimerThread->m_hThread, INFINITE);
delete m_pTimerThread;
m_pTimerThread = NULL;
}
m_ThreadParam.hWnd = GetSafeHwnd();
m_ThreadParam.nTime = 0;
SetDlgItemInt(IDC_TIME, 0);
TIMECAPS tc;
//获得定时器分辨率
if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR)
{
return;
}
UINT nResolution = min(max(tc.wPeriodMin, 1), tc.wPeriodMax);
UINT nInterval = 100;
if (nInterval < nResolution)
{
nInterval = nResolution;
}
//设置定时最小分辨率
timeBeginPeriod(nResolution);
//设置定时器
m_nTimerID = timeSetEvent(nInterval, nResolution,
(LPTIMECALLBACK)m_ThreadParam.hTimerEvent,
(DWORD)this, TIME_PERIODIC | TIME_CALLBACK_EVENT_SET);
//创建定时线程
m_pTimerThread = (CTimerThread*)AfxBeginThread(RUNTIME_CLASS(CTimerThread),
THREAD_PRIORITY_ABOVE_NORMAL, 0, CREATE_SUSPENDED);
m_pTimerThread->m_bAutoDelete = FALSE;
m_pTimerThread->m_pThreadParam = &m_ThreadParam;
m_pTimerThread->ResumeThread();
}
void CDemoDlg::OnTest2()
{
//取消定时器
if (m_nTimerID != 0)
{
timeKillEvent(m_nTimerID);
}
//结束定时线程
if (m_pTimerThread != NULL)
{
//设置退出事件
SetEvent(m_ThreadParam.hExitEvent);
//等待定时线程结束
::WaitForSingleObject(m_pTimerThread->m_hThread, INFINITE);
delete m_pTimerThread;
m_pTimerThread = NULL;
}
}