ATL 开发ActiveX控件之定时器使用(改进,含源码)

原定时器设计:http://blog.csdn.net/strmagic/archive/2007/10/23/1840365.aspx (如不能访问请看本文最后的原文附录)

原定时器缺点1:使用继承方式无法实现一个类具有多个定时器

原因:采用继承方式

改进思路:将继承方式给成聚合,将定时器作为类的成员变量,这样便可以定义多个定时器

具体方案:1.增加public的成员变量 T* m_cls和 UINT m_nTimerID;     

2.将TimerOn函数中的第1条语句修改为Derived* pDerived = ((Derived*)m_cls);(m_cls指需要使用定时器的类的对象.原设计方案采用继承方式,因此使用this,而改进方案作为m_cls的成员变量,因此不能在使用this)

3.修改OnTimer函数,增加一个参数UINT m_nTimerID.(m_nTimerID是定时器ID,用于识别多个定时器)     

4.在需要使用定时器的类(本例为自定义的类Catl)中定义两个定时器(可以定义任意个,这里以两个为例).         

CTimer m_timerForRFID;//读取标签的定时器         

CTimer m_timerForCountTime;//计算剩余时间的定时器

5.定义为定时器定义两个标识符,作为定时器ID         

#define TIMERID_RFID 100         

#define TIMERID_COUNTTIME 200

6.在Catl的构造函数中增加:         

m_timerForRFID.m_cls=this;        

m_timerForRFID.m_nTimerID=TIMERID_RFID;        

m_timerForCountTime.m_cls=this;        

m_timerForCountTime.m_nTimerID=TIMERID_COUNTTIME;

7.使用 打开定时器:m_timerForCountTime.TimerOn(m_dwTimerInterval);m_timerForRFID.TimerOn(m_dwTimerInterval); 关闭定时器:m_timerForRFID.TimerOff();m_timerForCountTime.TimerOff();

处理函数:使用switch case 根据不同的定时器ID进行处理.

 

原定时器缺点2:不能在需要使用定时器的OnTimer函数中关闭定时器

原因:原定时器使用线程模拟定时器的关键代码是Apartment函数中的while循环,在循环中增加sleep达到定时功能.其结束的条件是m_bTimerOn=FALSE.由于m_bTimerOn是关键成员变量,不易对外公开,因此在OnTimer函数中不能访问m_bTimerOn来结束,而TimerOff必须由需要使用定时器的线程来调用才能结束定时器线程,而不能定时器线程自己调用TimerOff来结束自身(线程结束自身的最佳办法是线程函数退出)

改进思路:OnTimer增加BOOL* b_isOver参数,其中b_isOver和m_bTimerOn共同作为Apartment函数中的while循环结束条件

具体方案:

1.修改Apartment函数的while循环如下:     

while(b_isOver==false&&m_bTimerOn)      {          Sleep(m_dwTimerInterval);          if (!m_bTimerOn)          break;          if(m_spT)          {              m_spT->OnTimer(m_nTimerID,&b_isOver);          }      }       

 

2.在OnTimer中设置b_isOver,如果需要结束,可在OnTimer中将b_isOver=TRUE.

 

附改进后源代码:

头文件:

// Timer.h: interface for the CTimer class. // ////////////////////////////////////////////////////////////////////// #if !defined(AFX_TIMER_H__AC105C31_E7EA_4F81_B13A_47BD9A52E6BB__INCLUDED_) #define AFX_TIMER_H__AC105C31_E7EA_4F81_B13A_47BD9A52E6BB__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 // 多线程模拟定时器 template class CTimer { public: CTimer() { m_bTimerOn = FALSE; } HANDLE TimerOn(DWORD dwTimerInterval) { Derived* pDerived = ((Derived*)m_cls); m_dwTimerInterval = dwTimerInterval; m_bTimerOn = TRUE; m_pStream = NULL; HRESULT hRes; // 接口列集 hRes = CoMarshalInterThreadInterfaceInStream(*piid, (T*)pDerived, &m_pStream); m_hThread= CreateThread(NULL, 0, &_Apartment, (void*)this, 0, &m_dwThreadID); return m_hThread; } void TimerOff() { if (m_bTimerOn) { m_bTimerOn = FALSE; AtlWaitWithMessageLoop(m_hThread); } } // 实现函数 private: static DWORD WINAPI _Apartment(void* pv) { CTimer* pThis = (CTimer*) pv; pThis->Apartment(); return 0; } DWORD Apartment() { CoInitialize(NULL); HRESULT hRes; m_spT.Release(); //接口散集 if (m_pStream) hRes = CoGetInterfaceAndReleaseStream(m_pStream, *piid,(void**)&m_spT); BOOL b_isOver=false; while(b_isOver==false&&m_bTimerOn) { Sleep(m_dwTimerInterval); if (!m_bTimerOn) break; if(m_spT) { m_spT->OnTimer(m_nTimerID,&b_isOver); } } m_spT.Release(); CoUninitialize(); return 0; } // 属性 public: DWORD m_dwTimerInterval; T* m_cls; HANDLE m_hThread; UINT m_nTimerID; // 实现属性 private: DWORD m_dwThreadID; BOOL m_bTimerOn; LPSTREAM m_pStream; CComPtr m_spT; }; #endif // !defined(AFX_TIMER_H__AC105C31_E7EA_4F81_B13A_47BD9A52E6BB__INCLUDED_) 源文件: // Timer.cpp: implementation of the CTimer class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "Timer.h" ////////////////////////////////////////////////////////////////////// // Construction/Destruction //////////////////////////////////////////////////////////////////////

 

附原文:

在Window编程中,我们经常要依靠定时器来定时触发某些代码的执行。但在ATL 的ActiveX 编程中,定时器的使用受到了一定限制,下面,我就根据开发经验谈谈如何在ATL 的 ActiveX编程中使用定时器这一资源。
  首先,为了优化的性能,当前ActiveX  控件 分两种有窗口和无窗口控件。
在有窗口控件中你所获得的编程资源几乎和普通的Windows编程没有两样,而定时器的使用也一样。在ATL中你只要在控件的构造函数中指定控件基类CComControlBase 的成员m_bWindowOnly 为 true 就可以创建一个有窗口的ActiveX控件。示例如下:
   myControl : myControl(){
   m_bWindowOnly = true;
   }
  这样就可以在程序中用 SetTimer/KillTimer 和 响应 WM_TIMER消息来设置并使用定时器。
  当作为一个ATL程序员,我们当然不会放弃每一个优化程序性能的机会,下面我就来介绍一下在ATL中的无窗口ActiveX控件中使用定时器的几种方法:
  第一种:使用回调函数。在VC中,由于MFC和ATL了包装,我们可以忽略窗口句柄参数,直接创建一个和窗口关联的定时器。当无窗口控件由于没有窗口句柄,直接调用CWindow的SetTimer函数会给你抛出一个ASSERT对话框来。这时候我们只好回到API来寻求帮助, 由于 API SetTimer在创建一个定时器时除了窗口句柄,还为我们提供了通过回调函数来响应定时事件,这样,我们就可以绕过无窗口控件的约束使用定时器了。
  首先,定义一个定时器的回调函数:
  VOID CALLBACK TimerProc(
    HWND hwnd,        // 定时器消息的窗口句柄
    UINT message,     // WM_TIMER 消息
    UINT idTimer,     // 定时器标志
    DWORD dwTime)     // 当前系统启动计时
  {
     //这里添加你的定时器处理代码
  }
  接下来,就可以使用SetTimer来使用定时器了
  uResult = SetTimer(NULL,      // handle to main window
             ID_TIMER,          // 定时器标识
             1000,              // 1 秒间隔
             (TIMERPROC)TimerProc); // 回调函数
  
  第二种:通常我们在使用定时器时,往往希望在定时事件出发时可以通知自己的ActiveX 控件,虽然上面方法可以,但在参数的传递上有些不便,这里我使用多线程来模拟实习之,作为ATL中的编程风格,在向实现类添加某些功能,通常使用模板基类来扩充,故首先定义如下基类: 
  ////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////// // // 多线程模拟定时器 // 因 无窗口ATICVX 控件不能直接使用Timer, 而且为了保证定时的准确和精度,所以使用之 // 无窗口还可以通过 回调过程方式使用 Timer 但精度不能保证,所以此处排出 // template class CTimer { public: CTimer() { m_bTimerOn = FALSE; } HRESULT TimerOn(DWORD dwTimerInterval) { Derived* pDerived = ((Derived*)this); m_dwTimerInterval = dwTimerInterval; if (m_bTimerOn) // 已经启动,仅仅调整定时间隔 return S_OK; m_bTimerOn = TRUE; m_dwTimerInterval = dwTimerInterval; m_pStream = NULL; HRESULT hRes; // 接口列集 hRes = CoMarshalInterThreadInterfaceInStream(*piid, (T*)pDerived, &m_pStream); // 创建线程并传递 this 指针 m_hThread = CreateThread(NULL, 0, &_Apartment, (void*)this, 0, &m_dwThreadID); return S_OK; } void TimerOff() { if (m_bTimerOn) { m_bTimerOn = FALSE; AtlWaitWithMessageLoop(m_hThread); } } // 实现函数 private: static DWORD WINAPI _Apartment(void* pv) { CTimer* pThis = (CTimer*) pv; pThis->Apartment(); return 0; } DWORD Apartment() { CoInitialize(NULL); HRESULT hRes; m_spT.Release(); //接口散集 if (m_pStream) hRes = CoGetInterfaceAndReleaseStream(m_pStream, *piid, void**)&m_spT); while(m_bTimerOn) { Sleep(m_dwTimerInterval); if (!m_bTimerOn) break; m_spT->OnTimer(); } m_spT.Release(); CoUninitialize(); return 0; } // 属性 public: DWORD m_dwTimerInterval; // 实现属性 private: HANDLE m_hThread; DWORD m_dwThreadID; LPSTREAM m_pStream; CComPtr m_spT; BOOL m_bTimerOn; };  
  从基类中可以看到,在线程函数中定时调用子类的OnTimer接口函数。这样我们只要从CTimer 中派生,并在子类接口中定义OnTimer接口,并实现该接口函数,就可以获得定时的调用,而TimerOn则用来开始并指定定时间隔,而TimerOff则用来关闭定时器。子类的代码如下:
  //////////////////////////////////////////////////////////////////////////// // // 类: CMyControl // 功能: 示例 ActiveX 控件实现 // class ATL_NO_VTABLE CMyControl : // ... 其他接口 // 模拟定时器的支持 public CTimer { public: STDMETHOD(OnTimer)(void); }; // 实现 STDMETHODIMP CCFuelWatchObj::OnTimer(void) { //定时响应代码 }

声明:方法二参考了MSDN中相关资料。
  
  后记:实际上,在无窗口ActiveX控件中使用定时器的方法是很多,在这里只概述了两种。原本想就ATL中使用定时器这一功能进行比较详细描述,但由于中间工作原因间断,现在思源枯竭,只能写这些勒,如果这篇文章对于初次接触 ATL ActiveX 控件编程朋友有所参考,那在下就很感激了。

你可能感兴趣的:(C++,ASP.NET)