欢迎访问:常州市武进区嘉泽中心小学网站 !今天是:
栏目列表
您现在的位置是:首页>>教师>>计算机技术>>程序设计>>杂项>>文章内容
ATL 开发ActiveX控件 之定时器使用
发布时间:2008-11-20   点击:   来源:本站原创   录入者:佚名
 

  在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 Derived, class T, const IID* piid>
  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<Derived, T, piid>* pThis = (CTimer<Derived, T, piid>*) 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<T> m_spT;
 BOOL m_bTimerOn;
  }; 
 
  从基类中可以看到,在线程函数中定时调用子类的OnTimer接口函数。这样我们只要从CTimer 中派生,并在子类接口中定义OnTimer接口,并实现该接口函数,就可以获得定时的调用,而TimerOn则用来开始并指定定时间隔,而TimerOff则用来关闭定时器。子类的代码如下:
  ////////////////////////////////////////////////////////////////////////////
  //
  // 类: CMyControl
  // 功能: 示例 ActiveX 控件实现
  //
  class ATL_NO_VTABLE CMyControl :
  // ... 其他接口
  // 模拟定时器的支持
  public CTimer<CMyControl , IMyControl, &IID_IMyControl>
  {
   public:
     STDMETHOD(OnTimer)(void);
  };

  // 实现
  STDMETHODIMP CCFuelWatchObj::OnTimer(void)
  {
     //定时响应代码
  }

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


 


附件:
    关闭窗口
    打印文档
    账号登录
    保持登录 忘记密码?
    账号与武进教师培训平台同步