多媒体定时器不能停止,原因?

时间:2022-07-08 23:35:18
单击按钮,启动定时器
例:
void CModelessDlg::OnTimerOn() 
{
SetMMTimer(1000, MMTimerProc);
}

bool CModelessDlg::SetMMTimer(int nTimeElapse, LPTIMECALLBACK pCallBackProc)
{
TIMECAPS TimeCaps;
if(TIMERR_NOERROR==timeGetDevCaps(&TimeCaps, sizeof(TIMECAPS)))
{
m_wAccuracy = min(max(TimeCaps.wPeriodMin, 1), TimeCaps.wPeriodMax); 
timeBeginPeriod(m_wAccuracy); //设置定时器精度
}

if(m_TimerID=timeSetEvent(nTimeElapse, 1, pCallBackProc, DWORD(this), TIME_PERIODIC/*|TIME_CALLBACK_FUNCTION*/)==0)
{
AfxMessageBox("不能计时", MB_OK | MB_ICONASTERISK);
return false;
}
m_nTimeElapse = nTimeElapse;
return true;
}


使用了用户消息函数,用来接收多媒体定时器的事件通知。
如:
LRESULT CModelessDlg::OnMMTimer(WPARAM wParam, LPARAM lParam)
{
     CString str;

str.Format("%d", counter);
if(GetDlgItem(IDC_STATIC_SHOW)->GetSafeHwnd())
{
   GetDlgItem(IDC_STATIC_SHOW)->SetWindowText(str);
}

counter++;

return true;
}

我想单击另一个按钮,停止计时
例:
void CModelessDlg::OnTimerOff() 
{
timeKillEvent(m_TimerID);
timeEndPeriod(m_wAccuracy); 

}

但是为什么消息中的函数还在响应?
如何停止

8 个解决方案

#1


如果使 timeKillEvent 后立即中止定时器,需要在 timeSetEvent 创建定时器时指定 TIME_KILL_SYNCHRONOUS 标志

timeSetEvent(nTimeElapse, 1, pCallBackProc, DWORD(this), TIME_PERIODIC)
改为
timeSetEvent(nTimeElapse, 1, pCallBackProc, DWORD(this), TIME_PERIODIC|TIME_KILL_SYNCHRONOUS)

#2


但是出现了:
ModelessDlg.cpp
E:\ModelessDlg.cpp(154) : error C2065: 'TIME_KILL_SYNCHRONOUS' : undeclared identifier
实在找不出原因,还要麻烦大侠!

#3


http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/mmfunc_5378.asp

timeSetEvent
The timeSetEvent function starts a specified timer event. The multimedia timer runs in its own thread. After the event is activated, it calls the specified callback function or sets or pulses the specified event object.

MMRESULT timeSetEvent(
  UINT           uDelay,      
  UINT           uResolution, 
  LPTIMECALLBACK lpTimeProc,  
  DWORD_PTR      dwUser,      
  UINT           fuEvent      
);
Parameters
uDelay 
Event delay, in milliseconds. If this value is not in the range of the minimum and maximum event delays supported by the timer, the function returns an error. 
uResolution 
Resolution of the timer event, in milliseconds. The resolution increases with smaller values; a resolution of 0 indicates periodic events should occur with the greatest possible accuracy. To reduce system overhead, however, you should use the maximum value appropriate for your application. 
lpTimeProc 
Pointer to a callback function that is called once upon expiration of a single event or periodically upon expiration of periodic events. If fuEvent specifies the TIME_CALLBACK_EVENT_SET or TIME_CALLBACK_EVENT_PULSE flag, then the lpTimeProc parameter is interpreted as a handle to an event object. The event will be set or pulsed upon completion of a single event or periodically upon completion of periodic events. For any other value of fuEvent, the lpTimeProc parameter is interpreted as a function pointer with the following signature: void (CALLBACK)(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2); 

dwUser 
User-supplied callback data. 
fuEvent 
Timer event type. This parameter may include one of the following values. Value Meaning 
TIME_ONESHOT Event occurs once, after uDelay milliseconds. 
TIME_PERIODIC Event occurs every uDelay milliseconds. 


The fuEvent parameter may also include one of the following values. Value Meaning 
TIME_CALLBACK_FUNCTION When the timer expires, Windows calls the function pointed to by the lpTimeProc parameter. This is the default.  
TIME_CALLBACK_EVENT_SET When the timer expires, Windows calls the SetEvent function to set the event pointed to by the lpTimeProc parameter. The dwUser parameter is ignored.  
TIME_CALLBACK_EVENT_PULSE When the timer expires, Windows calls the PulseEvent function to pulse the event pointed to by the lpTimeProc parameter. The dwUser parameter is ignored.  
TIME_KILL_SYNCHRONOUS Passing this flag prevents an event from occurring after the timeKillEvent() function is called. 



Return Values
Returns an identifier for the timer event if successful or an error otherwise. This function returns NULL if it fails and the timer event was not created. (This identifier is also passed to the callback function.)

Remarks
Each call to timeSetEvent for periodic timer events requires a corresponding call to the timeKillEvent function.

Creating an event with the TIME_KILL_SYNCHRONOUS and the TIME_CALLBACK_FUNCTION flag prevents the event from occurring after the timeKillEvent function is called. 

Requirements 
  Windows NT/2000/XP: Included in Windows NT 3.1 and later.
  Windows 95/98/Me: Included in Windows 95 and later.
  Header: Declared in Mmsystem.h; include Windows.h.
  Library: Use Winmm.lib.

#4


— 多 媒 体 定 时 器。 它 主 要 通 过 以 下 函 数 来 实 现:  
 
---- timeBeginPeriod — — 建 立 应 用 程 序 使 用 的 定 时 器 分 辨 率;  
 
---- timeEndPeriod — — 清 除 前 面 用timeBeginPeriod 函 数 建 立 的 最 小 定 
 时 器 分 辨 率;  
 
---- timeSetEvent — — 产 生 一 个 在 指 定 的 时 间 或 时 间 周 期 间 隔 内  
执 行 的 定 时 器 事 件;  
 
---- timeKillEvent — — 删 除 前 面 用timeSetEvent 产 生 的 定 时 器 事 件;  
 
 
---- timeGetDevCaps — — 返 回 关 于 定 时 器 服 务 能 力 的 信 息。  
 
---- 下 面 就 结 合 一 段 程 序 来 具 体 说 明 它 的 用 法。 这 段 程 序 的 主 
 要 功 能 是: 设 置 两 个 时 钟 定 时 器, 一 个 间 隔 是1 毫 秒, 一 个 间  
隔 是2 秒, 每 执 行 一 次, 输 出 当 前 系 统 时 钟 值 到 文 件“cure.out”, 
 以 比 较 此 定 时 器 的 精 确 度。( 此 程 序 在 中 文windows95 及Microsoft  
VC5.0 编 译 通 过。 只 节 取 与 定 时 器 有 关 的 部 分 程 序。)  
 
#include < stdio.h > 
//包含所用系统函数的头文件,  
如果编译有问题,可调整此语句的位置 
#include < mmsystem.h > 
 
//定义1毫秒和2秒时钟间隔,以毫秒为单位 
#define ONE_MILLI_SECOND1 
#define TWO_SECOND 2000 
//定义时钟分辨率,以毫秒为单位 
#define TIMER_ACCURACY1 
 
UINT wTimerRes_1ms,wTimerRes_2s;  //定义时间间隔 
UINT wAccuracy;//定义分辨率 
UINT TimerID_1ms,TimerID_2s;//定义定时器句柄 
 
CCureApp::CCureApp() 
∶  fout("cure.out", ios::out)//打开输出文件“cure.out” 

// TODO: add construction code here, 
// Place all significant  
       initialization in InitInstance 
//给时间间隔变量赋值 
wTimerRes_1ms = ONE_MILLI_SECOND ; 
wTimerRes_2s = TWO_SECOND; 
 
TIMECAPS  tc; 
//通过函数timeGetDevCaps取出系统 
的分辨率取值范围(对intel系统, 
1~16毫秒),//如果无错则继续 
if(timeGetDevCaps(&tc, sizeof(TIMECAPS))  
== TIMERR_NOERROR) 

//分辨率的值不能超出系统的取值范围 
wAccuracy = min(max(tc.wPeriodMin,  
TIMER_ACCURACY),tc.wPeriodMax); 
//调用timeBeginPeriod函数设置定时器 
的分辨率,类似于for循环的步长 
timeBeginPeriod(wAccuracy); 
//设置定时器 
InitializeTimer(); 


 
CCureApp::~CCureApp() 

//结束时钟 
fout < <  "结束时钟" < <  endl; 
//删除两个定时器 
timeKillEvent(TimerID_1ms); 
timeKillEvent(TimerID_2s); 
//删除设置的分辨率 
timeEndPeriod(wAccuracy); 

注:使用完的定时器及分辨率一定 
要删除,否则系统会越来越慢。 
 
void CCureApp::InitializeTimer() 

StartOneMilliSecondTimer(); 
StartTwoSecondTimer(); 

 
//一毫秒定时器的回调函数, 
类似于中断处理程序 
voidPASCAL//一定要声明为全局PASCAL函数, 
否则编译会有问题 
OneMilliSecondProc(UINT wTimerID, UINT msg, 
DWORD dwUser, DWORD dw1, DWORD dw2) 

static int ms = 0;//定义计数器 
CCureApp *app = (CCureApp *)dwUser; 
//取得系统时间以毫秒为单位 
DWORD osBinaryTime = GetTickCount(); 
//输出计数器值和当前系统时间到文件  
app- >fout < <  ++ms < <  ": 1ms : " 
 < <  osBinaryTime < <  endl; 

 
//加装1毫秒定时器 
void  CCureApp::StartOneMilliSecondTimer() 

if((TimerID_1ms = timeSetEvent(wTimerRes_1ms, wAccuracy, 
 (LPTIMECALLBACK) OneMilliSecondProc,//回调函数 
 (DWORD)this,//用户自传送到回调函数的数据 
/*周期调用,只使用一次用TIME_ONESHOT*/ 
TIME_PERIODIC)) == 0) 

AfxMessageBox("不能计时", MB_OK | MB_ICONASTERISK); 

else//不等于0表明加装成功,  
返回此定时器的句柄 
fout < <  "16ms计时:" < <  endl; 

 
以下为2秒定时器的回调函 
数和加装函数,与1毫秒的类似; 
void  PASCAL 
TwoSecondProc(UINT wTimerID, UINT msg, 
  DWORD dwUser, DWORD dw1, DWORD dw2) 

static int s = 0; 
CCureApp *app = (CCureApp *)dwUser; 
DWORD osBinaryTime = GetTickCount();  
app- >fout < <  "*********************** 
***********************" < <  endl; 
app- >fout < <  ++s < <  ": 2s : " 
 < <  osBinaryTime < <  endl; 

 
void CCureApp::StartTwoSecondTimer() 

if((TimerID_2s = timeSetEvent(wTimerRes_2s, wAccuracy, 
 (LPTIMECALLBACK) TwoSecondProc, 
 (DWORD)this, 
  TIME_PERIODIC)) == 0) 

AfxMessageBox("不能计时", MB_OK | MB_ICONASTERISK); 

else 
fout < <  "2s计时:" < <  endl; 

#5


感谢各位的回复!
在MSDN中虽有:TIME_KILL_SYNCHRONOUS
但是我查了文件mmsystem.h,该文件中只有:
#define TIME_ONESHOT 0
#define TIME_PERIODIC 1
#define TIME_CALLBACK_FUNCTION 0
#define TIME_CALLBACK_EVENT_SET 16
#define TIME_CALLBACK_EVENT_PULSE 32
这是怎么回事!
我能自己加上:
#define TIME_KILL_SYNCHRONOUS 64(瞎猜的!)
吗?
另外,我怀疑我的定时器不能关闭,是否也和消息机制有关,烦劳各位再给会诊一下!

#6


我的回调函数是这样写的,我怀疑定时器不能关闭和这个程序有关,但不知问题在哪里?

void CALLBACK CModelessDlg::MMTimerProc(UINT wTimerID, UINT msg, DWORD  dwUser, DWORD dw1, DWORD dw2)
{
CModelessDlg* pThis = reinterpret_cast<CModelessDlg*>( dwUser );
ASSERT( pThis != NULL );

if(pThis->m_hWnd != NULL)
pThis->OnMMTimer(0, 0);
}

#7


装一个最新的SDK试试

#8


高手请给看一下呀!

#1


如果使 timeKillEvent 后立即中止定时器,需要在 timeSetEvent 创建定时器时指定 TIME_KILL_SYNCHRONOUS 标志

timeSetEvent(nTimeElapse, 1, pCallBackProc, DWORD(this), TIME_PERIODIC)
改为
timeSetEvent(nTimeElapse, 1, pCallBackProc, DWORD(this), TIME_PERIODIC|TIME_KILL_SYNCHRONOUS)

#2


但是出现了:
ModelessDlg.cpp
E:\ModelessDlg.cpp(154) : error C2065: 'TIME_KILL_SYNCHRONOUS' : undeclared identifier
实在找不出原因,还要麻烦大侠!

#3


http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/mmfunc_5378.asp

timeSetEvent
The timeSetEvent function starts a specified timer event. The multimedia timer runs in its own thread. After the event is activated, it calls the specified callback function or sets or pulses the specified event object.

MMRESULT timeSetEvent(
  UINT           uDelay,      
  UINT           uResolution, 
  LPTIMECALLBACK lpTimeProc,  
  DWORD_PTR      dwUser,      
  UINT           fuEvent      
);
Parameters
uDelay 
Event delay, in milliseconds. If this value is not in the range of the minimum and maximum event delays supported by the timer, the function returns an error. 
uResolution 
Resolution of the timer event, in milliseconds. The resolution increases with smaller values; a resolution of 0 indicates periodic events should occur with the greatest possible accuracy. To reduce system overhead, however, you should use the maximum value appropriate for your application. 
lpTimeProc 
Pointer to a callback function that is called once upon expiration of a single event or periodically upon expiration of periodic events. If fuEvent specifies the TIME_CALLBACK_EVENT_SET or TIME_CALLBACK_EVENT_PULSE flag, then the lpTimeProc parameter is interpreted as a handle to an event object. The event will be set or pulsed upon completion of a single event or periodically upon completion of periodic events. For any other value of fuEvent, the lpTimeProc parameter is interpreted as a function pointer with the following signature: void (CALLBACK)(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2); 

dwUser 
User-supplied callback data. 
fuEvent 
Timer event type. This parameter may include one of the following values. Value Meaning 
TIME_ONESHOT Event occurs once, after uDelay milliseconds. 
TIME_PERIODIC Event occurs every uDelay milliseconds. 


The fuEvent parameter may also include one of the following values. Value Meaning 
TIME_CALLBACK_FUNCTION When the timer expires, Windows calls the function pointed to by the lpTimeProc parameter. This is the default.  
TIME_CALLBACK_EVENT_SET When the timer expires, Windows calls the SetEvent function to set the event pointed to by the lpTimeProc parameter. The dwUser parameter is ignored.  
TIME_CALLBACK_EVENT_PULSE When the timer expires, Windows calls the PulseEvent function to pulse the event pointed to by the lpTimeProc parameter. The dwUser parameter is ignored.  
TIME_KILL_SYNCHRONOUS Passing this flag prevents an event from occurring after the timeKillEvent() function is called. 



Return Values
Returns an identifier for the timer event if successful or an error otherwise. This function returns NULL if it fails and the timer event was not created. (This identifier is also passed to the callback function.)

Remarks
Each call to timeSetEvent for periodic timer events requires a corresponding call to the timeKillEvent function.

Creating an event with the TIME_KILL_SYNCHRONOUS and the TIME_CALLBACK_FUNCTION flag prevents the event from occurring after the timeKillEvent function is called. 

Requirements 
  Windows NT/2000/XP: Included in Windows NT 3.1 and later.
  Windows 95/98/Me: Included in Windows 95 and later.
  Header: Declared in Mmsystem.h; include Windows.h.
  Library: Use Winmm.lib.

#4


— 多 媒 体 定 时 器。 它 主 要 通 过 以 下 函 数 来 实 现:  
 
---- timeBeginPeriod — — 建 立 应 用 程 序 使 用 的 定 时 器 分 辨 率;  
 
---- timeEndPeriod — — 清 除 前 面 用timeBeginPeriod 函 数 建 立 的 最 小 定 
 时 器 分 辨 率;  
 
---- timeSetEvent — — 产 生 一 个 在 指 定 的 时 间 或 时 间 周 期 间 隔 内  
执 行 的 定 时 器 事 件;  
 
---- timeKillEvent — — 删 除 前 面 用timeSetEvent 产 生 的 定 时 器 事 件;  
 
 
---- timeGetDevCaps — — 返 回 关 于 定 时 器 服 务 能 力 的 信 息。  
 
---- 下 面 就 结 合 一 段 程 序 来 具 体 说 明 它 的 用 法。 这 段 程 序 的 主 
 要 功 能 是: 设 置 两 个 时 钟 定 时 器, 一 个 间 隔 是1 毫 秒, 一 个 间  
隔 是2 秒, 每 执 行 一 次, 输 出 当 前 系 统 时 钟 值 到 文 件“cure.out”, 
 以 比 较 此 定 时 器 的 精 确 度。( 此 程 序 在 中 文windows95 及Microsoft  
VC5.0 编 译 通 过。 只 节 取 与 定 时 器 有 关 的 部 分 程 序。)  
 
#include < stdio.h > 
//包含所用系统函数的头文件,  
如果编译有问题,可调整此语句的位置 
#include < mmsystem.h > 
 
//定义1毫秒和2秒时钟间隔,以毫秒为单位 
#define ONE_MILLI_SECOND1 
#define TWO_SECOND 2000 
//定义时钟分辨率,以毫秒为单位 
#define TIMER_ACCURACY1 
 
UINT wTimerRes_1ms,wTimerRes_2s;  //定义时间间隔 
UINT wAccuracy;//定义分辨率 
UINT TimerID_1ms,TimerID_2s;//定义定时器句柄 
 
CCureApp::CCureApp() 
∶  fout("cure.out", ios::out)//打开输出文件“cure.out” 

// TODO: add construction code here, 
// Place all significant  
       initialization in InitInstance 
//给时间间隔变量赋值 
wTimerRes_1ms = ONE_MILLI_SECOND ; 
wTimerRes_2s = TWO_SECOND; 
 
TIMECAPS  tc; 
//通过函数timeGetDevCaps取出系统 
的分辨率取值范围(对intel系统, 
1~16毫秒),//如果无错则继续 
if(timeGetDevCaps(&tc, sizeof(TIMECAPS))  
== TIMERR_NOERROR) 

//分辨率的值不能超出系统的取值范围 
wAccuracy = min(max(tc.wPeriodMin,  
TIMER_ACCURACY),tc.wPeriodMax); 
//调用timeBeginPeriod函数设置定时器 
的分辨率,类似于for循环的步长 
timeBeginPeriod(wAccuracy); 
//设置定时器 
InitializeTimer(); 


 
CCureApp::~CCureApp() 

//结束时钟 
fout < <  "结束时钟" < <  endl; 
//删除两个定时器 
timeKillEvent(TimerID_1ms); 
timeKillEvent(TimerID_2s); 
//删除设置的分辨率 
timeEndPeriod(wAccuracy); 

注:使用完的定时器及分辨率一定 
要删除,否则系统会越来越慢。 
 
void CCureApp::InitializeTimer() 

StartOneMilliSecondTimer(); 
StartTwoSecondTimer(); 

 
//一毫秒定时器的回调函数, 
类似于中断处理程序 
voidPASCAL//一定要声明为全局PASCAL函数, 
否则编译会有问题 
OneMilliSecondProc(UINT wTimerID, UINT msg, 
DWORD dwUser, DWORD dw1, DWORD dw2) 

static int ms = 0;//定义计数器 
CCureApp *app = (CCureApp *)dwUser; 
//取得系统时间以毫秒为单位 
DWORD osBinaryTime = GetTickCount(); 
//输出计数器值和当前系统时间到文件  
app- >fout < <  ++ms < <  ": 1ms : " 
 < <  osBinaryTime < <  endl; 

 
//加装1毫秒定时器 
void  CCureApp::StartOneMilliSecondTimer() 

if((TimerID_1ms = timeSetEvent(wTimerRes_1ms, wAccuracy, 
 (LPTIMECALLBACK) OneMilliSecondProc,//回调函数 
 (DWORD)this,//用户自传送到回调函数的数据 
/*周期调用,只使用一次用TIME_ONESHOT*/ 
TIME_PERIODIC)) == 0) 

AfxMessageBox("不能计时", MB_OK | MB_ICONASTERISK); 

else//不等于0表明加装成功,  
返回此定时器的句柄 
fout < <  "16ms计时:" < <  endl; 

 
以下为2秒定时器的回调函 
数和加装函数,与1毫秒的类似; 
void  PASCAL 
TwoSecondProc(UINT wTimerID, UINT msg, 
  DWORD dwUser, DWORD dw1, DWORD dw2) 

static int s = 0; 
CCureApp *app = (CCureApp *)dwUser; 
DWORD osBinaryTime = GetTickCount();  
app- >fout < <  "*********************** 
***********************" < <  endl; 
app- >fout < <  ++s < <  ": 2s : " 
 < <  osBinaryTime < <  endl; 

 
void CCureApp::StartTwoSecondTimer() 

if((TimerID_2s = timeSetEvent(wTimerRes_2s, wAccuracy, 
 (LPTIMECALLBACK) TwoSecondProc, 
 (DWORD)this, 
  TIME_PERIODIC)) == 0) 

AfxMessageBox("不能计时", MB_OK | MB_ICONASTERISK); 

else 
fout < <  "2s计时:" < <  endl; 

#5


感谢各位的回复!
在MSDN中虽有:TIME_KILL_SYNCHRONOUS
但是我查了文件mmsystem.h,该文件中只有:
#define TIME_ONESHOT 0
#define TIME_PERIODIC 1
#define TIME_CALLBACK_FUNCTION 0
#define TIME_CALLBACK_EVENT_SET 16
#define TIME_CALLBACK_EVENT_PULSE 32
这是怎么回事!
我能自己加上:
#define TIME_KILL_SYNCHRONOUS 64(瞎猜的!)
吗?
另外,我怀疑我的定时器不能关闭,是否也和消息机制有关,烦劳各位再给会诊一下!

#6


我的回调函数是这样写的,我怀疑定时器不能关闭和这个程序有关,但不知问题在哪里?

void CALLBACK CModelessDlg::MMTimerProc(UINT wTimerID, UINT msg, DWORD  dwUser, DWORD dw1, DWORD dw2)
{
CModelessDlg* pThis = reinterpret_cast<CModelessDlg*>( dwUser );
ASSERT( pThis != NULL );

if(pThis->m_hWnd != NULL)
pThis->OnMMTimer(0, 0);
}

#7


装一个最新的SDK试试

#8


高手请给看一下呀!