在ATL无窗口ACTIVEX控件中响应timer消息

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

  最近,在尝试着写一个能够显示静态和动态图片的ACTIVEX控件,就像QQ的一样,能插入到RICHEDIT控件中。由于只是在尝试,很多功能都没有实现,只实现显示。如果单单显示静态图片,是很简单的。但要是显示动态GIF图片,就有一定的难度,首先要分析图片的帧数,还要控件图片按顺序显示。如果写成控件,还要保证插入到RICHEDIT控件中还能动起来,就更有难度了,因为ATL在响应TIMER事件的时候有很多困难要克服。

  对于图片的显示,由于只是在尝试,我只提供一个接口函数Play(),图片是写死在控件里的。让控件显示图片只要调用该函数即可。基于原理是,在Play()中,载入图片,判断图片是不是动态的GIF图片,不是,则直接显示,是动态的GIF,则创建一个TIMER对象,将TIMER的时间间隔设置为图片的第一帧的应该显示的时间长度,在OnTimer()中,则将图片应该显示的帧切换到下一帧,并修改TIMER的时间间隔为该帧应该显示的时间长度。至于如何显示动态GIF图片,可以看我写的另一篇文章。

  网上有文章说,要让ATL控件响应TIMER消息,只要将控件设为有窗口,然后调用SetTimer()来设置TIMER,然后在OnTimer()中响应timer消息即可。我照着做了,当要生成的控件显示动态图片时,在测试容器中测试的时候是会动的,但当把控件插入到richedit控件中时,图片就不会动了。据检查,原因应该是窗口句柄为NULL。(明明设为有窗口控件了,m_hWnd仍然为NULL,真的不知道为什么。如果有人知道的话,希望你能告诉我。因为我也是刚刚接触COM组件的开发)。

  不得已,只好改成无窗口的控件。但无窗口怎么响应timer消息呢,没有窗口句柄啊。确实如此,没有句柄是无法接收TIMER消息的。有一种方法是可以用TimeProc来响应,就是说,在SetTimer()的时候,窗口句柄可以为空,但我们给这个函数的最后一个参数传一个回调函数。然后,在我们的回调函数中实现换帧。但这种方法有一个不好之处是你要在回调函数中访问控件对象的成员变量,还要管理每个控件的timer对象,必须将每一个控件和它的timer映射起来进行集中管理,因为TimeProc回调函数是由系统调用的,不管哪个控件的timer间隔到了,它都被调用,就是说TimeProc是被所以的控件实例共享的,你要在TimeProc中判断现在是哪个控件的时候来了。此外,刚才说了回调函数中访问控件对象的成员变量,像必须传一个对象指针给TimeProc,而TimeProc的格式是固定的,你只能通过全局函数来实现。总之,管理起来非常复杂。

  既然没有句柄,我们就给它一个。怎么给?有一个比较笨但看起来还不错的方法,就是引入一个辅助对象,该对像是个窗口对象,当然,它就有句柄了。有句柄,我们就可以给它SetTimer,并能通过OnTimer()响应来响应timer消息,我们只要在该OnTimer中,调用控件的实现换帧的函数即可。下面是代码的实现:

class CSImgCtl;//我们的控件类

class CWinHidden :
public CWindowImpl<CWinHidden, CWindow, CNullTraits>
{
 BEGIN_MSG_MAP(CWinHidden)
        MESSAGE_HANDLER(WM_TIMER, OnTimer)
  END_MSG_MAP()
public:
 CWinHidden():m_nTimer(0), m_pFullCtrl(NULL){};
 ~CWinHidden()
 {
  if (m_nTimer != 0)
  {
   KillTimer(m_nTimer);
   m_nTimer = 0;
  }
 }
 void AttachCtl(CSImgCtl* pFullCtrl)
 {
  m_pFullCtrl = pFullCtrl;
 }
 BOOL SetThisTimer(UINT nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc )
 {
  if (m_hWnd == NULL)
  {
   RECT rt = {0, 0, 0, 0};
   Create(NULL, rt);
  }
  m_nTimer = ::SetTimer(m_hWnd, nIDEvent, uElapse, lpTimerFunc);
  return m_nTimer == 0;
 }
 void KillThisTimer()
 {
  if (m_nTimer != 0)
  {
   KillTimer(m_nTimer);
   m_nTimer = 0;
  }
 }
public:
 LRESULT OnTimer(UINT, WPARAM, LPARAM, BOOL&);
private:
 CSImgCtl*   m_pFullCtrl;
 UINT        m_nTimer;
};

LRESULT CWinHidden::OnTimer(UINT uMsg, WPARAM wParam,
       LPARAM lParam, BOOL& bHandled)
{
 if ((UINT)wParam != m_nTimer)
  return -1;

 if (m_pFullCtrl != NULL)
  m_pFullCtrl->OnTimer(uMsg, wParam, lParam, bHandled);//调用控件对象的函数
 return 0;
}

首先,在我们的控件类中,添加一个辅助类的成员

CWinHidden m_HideWnd;

然后,当我们要显示图片时,我们在Play()函数中添加如下代码

if (m_pImg == NULL)
 {
  m_pImg = new ImageEx(L"wk.gif");
  if (m_pImg == NULL)
   return S_OK;

 if (m_pImg->IsAnimatedGif())
  {  
   long lFrameTime = m_pImg->GetFrameTime();
   m_HideWnd.AttachCtl(this);                //将辅助类对象与本对象绑定
   m_HideWnd.SetThisTimer(1, lFrameTime, NULL);//为辅助类对象设置时钟
  }

  FireViewChange();

}

最后,在我们控件的OnTimer()中,实现换帧即可。

  从代码上看来,我们只不过是间接实现了OnTimer而已,确实如此,这也是不得已的办法,原因上面说了。但是,我们可以看到,如果我们要写一个无窗口控件,而又要通过消息来完成一些功能时,我们也可以通过这个函数来实现想要的功能,只要在辅助类的OnTimer()中调用控件实现你要的功能的函数,然后立即关闭时钟。要调用这些功能时,只要在辅助类对象上调用SetTimer()就行。比如:

m_HideWnd.AttachCtl(this);                //将辅助类对象与本对象绑定
m_HideWnd.SetThisTimer(1, 50, NULL);//为辅助类对象设置时钟

执行上面的代码50MS后,OnTimer()将被调用,我们在我们的OnTimer()中这样实现即可

LRESULT CWinHidden::OnTimer(UINT uMsg, WPARAM wParam,
       LPARAM lParam, BOOL& bHandled)
{
 if ((UINT)wParam != m_nTimer)
  return -1;

 if (m_pFullCtrl != NULL)
  m_pFullCtrl->SomeFunc();//调用控件对象的函数,在这个函数中实现我们要的功能
 return 0;
}

补充:我以前写的时候,没有注意到会存在BUG,如果按我上面写的话,在删除控件的时候会出现严重的BUG并导致程序崩溃。出错的原因在于CWinHidden析构的时候没有将窗口删除,所以,我们要在把它的析构函数修改为:

~CWinHidden()
 {
  if (m_nTimer != 0)
  {
   KillTimer(m_nTimer);
   m_nTimer = 0;
  }
  if (m_hWnd != NULL)
  {
   ::DestroyWindow(m_hWnd);
   m_hWnd = NULL;
  }
 }

加粗的几行是补充上去的。

ATL 开发ActiveX控件之定时器使用 转载自:http://www.evget.com/articles/evget_780.html在Window编程中,我们经常要依靠定时器来定时触发某些代码的执行。但在ATLActiveX 编程中,定时器的使用受到了一定限制,下面,我就根据开发经验谈谈如何在ATLActiveX编程中使用定时器这一资源。首先,为了优化的性能,当前ActiveX 控件 分两种有窗口和无窗口控件。在有窗口控件 阅读详情

相关推荐

从零上手Duilib:Windows C++轻量级UI库入门与实践指南

在Windows桌面应用开发领域,C++开发者常面临界面库的选择难题。MFC略显陈旧,Qt虽然功能强大但体积庞大。此时,轻量级、高性能的纯C++本地界面库成为理想的替代方案。其核心原理在于将UI布局与业务逻辑分离,通过XML描述界面结构,C++处理交互逻辑,从而显著提升开发效率和可维护性。这种架构尤其适合对执行体体积和运行时性能有严格要求的客户端应用。Duilib正是这一技术的典型代表,它允许开发者像构建网页一样,用XML快速定义窗口、按钮、列表等控件,并通过高效的渲染引擎实现流畅的界面体验。本文将围绕**

cuanji3287的博客 419

duilib CListUI滚动条自动滚到底部

duilib CListUI滚动条自动滚到底部 flyfish 在EndDown()失效时使用 原理:定时器处理滚动条,时间间隔50S,处理完关闭定时器 当界面更新50S之后,开始将滚动条滚动到底部 假设有一个窗口CMainWnd 头文件 virtual LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL&...

二分掌柜的 2263

duilib 界面库 实现timer定时器

看了大神介绍的duilib感觉已被同龄人狠狠地甩在背后。所以痛下决心,之后要多花时间写代码。 大神教程传送门: http://www.cnblogs.com/Alberl/p/3341956.html 现在的问题是想基于duilib实现一个timer定时器。工程基础大概是在 http://www.cnblogs.com/Alberl/p/3343763.html 因为自己的东西是基于大神...

weixin_30252709的博客 512

c++中 SetTimer的应用

<br />#pragma once<br />#include "VideoSourceServer/IVideoSourceServer.h"<br />#include "ConformRtmp/IRtmpConform.h"<br />#include "Buffer.h"<br />#include "TVCritSec.h"<br />#include "DrawVideoGDI.h"<br />#include "BitmapInfoHelpFunc.h"<br />#include "Wav

binglingyuyang的专栏 2138

LRESULT CMyPlayer::OnGetMinMaxInfo(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { MONIT...

LRESULT CMyPlayer::OnGetMinMaxInfo(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 是一个 Windows 窗口消息处理函数,它处理 WM_GETMINMAXINFO 消息。 WM_GETMINMAXINFO 消息是当窗口创建或改变大小时发送给窗口消息。它用来告诉窗口最小和最大尺寸。收到此...

weixin_42577735的博客 181

Visual C++经典实例源码:从环境配置到现代化改造的实战指南

C++作为系统级编程的核心语言,其开发环境配置与项目迁移是开发者常遇的工程实践难题。理解编译原理、链接机制和运行时库依赖,是解决跨版本兼容性问题的关键。在Windows平台开发中,Visual Studio IDE与MSVC编译器构成了主流技术生态,掌握其项目属性配置、平台工具集和字符集设置,能有效提升开发效率与代码可移植性。这些技术价值在维护遗留系统、学习经典设计模式时尤为突出,例如在桌面应用开发中处理MFC消息映射、数据库访问等场景。本文聚焦于《Visual C++开发实例大全》源码的**环境搭建**与

weixin_30448603的博客 352

基于Duilib库创建定时器机制

对话框demo框架 在基于Duilib库开发对话框时,我们一般建议基于WinImplBase类作为该对话框的基类,因为在WinImplBase类中封装了需用许多操作,便于对话框的创建;下面我们就基于WinImplBase类实现定时器功能。 /*某个对话框的基类demo,这里只截取了部分函数声明*/ class CXXXDialog: public WindowImplBase { public: ...

Keep Moving~ 1230

Duilib 实现右下角弹出像QQ新闻窗口,3秒后自动关闭(一)

转载:https://blog.twofei.com/667/ 自绘或子类化控件时,有时需要处理鼠标进入(MouseEnter)/鼠标离开(MouseLeave)/鼠标停留(MouseHover)消息,虽然有定义这两个消息常量(WM_MOUSEHOVER 与 WM_MOUSELEAVE),但默认情况下 Windows 的窗口消息是不会产生这三个消息的。但提供了一个 API 函数_TrackMou...

weixin_34406086的博客 432

Duilib 实现右下角弹出像QQ新闻窗口,3秒后窗口透明度渐变最后关闭,若在渐变过程中鼠标放到窗口上,窗口恢复最初状态(二)...

效果:   1.定义两个个定时器ID   #define ID_TIMER_DISPLAY_DELAY 30 #define ID_TIMER_DISPLAY_CLOSE 40   2.添加一个成员函数和成员变量 void SetAlpha(HWND hWnd,int Alpha); int m_nFlag ; void CPopUpWnd::SetAlpha(H...

weixin_34126215的博客 442

duilib库分析: 消息流程分析

转 看下CWindowWnd类与CPaintManagerUI类是咋进行消息分发的吧. 1. 先看下CPaintManagerUI类的MessageLoop函数: [cpp]view plaincopyprint? voidCPaintManagerUI::MessageLoop() { MSGmsg={0}; ...

weixin_34043301的博客 356

使用定时器显示GIF动画的ATL控件实现

最近在做一个IM软件,IM软件非常重要的功能就有动态表情的插入和显示。实现GIF图片显示的方法主要有两种,一种是通过HTML控件,一种是通过RichEdit控件。HTML控件显示GIF动画的效率是很高的,但是可操控性不好(比如不允许表情修改尺寸,插入后自动滚动到聊天记录窗口的底部等);RichEdit的可操控性就好得多了,但是插入和显示GIF动画就比较麻烦。 网上流传的向CRichEditCtr

haoekin的专栏 4113

WIN32学习——定时器消息

1 定时器消息     可以在程序中设置定时器,当到达时间间隔时,定时器会向窗口发送一个WM_TIMER消息,定时器的精度为毫秒,但是精准度很低。   2 消息的参数     WPARAM - 定时器ID     LPARAM - 定时器处理函数的指针   3 定时器的使用步骤     a、创建定时器       UINT SetTimer( HWND hWnd, //定时器窗

L_brend361 876

复合控件制作示例

平时在编程时,常想自己编一些适用的控件以利于程序开发,最近编了一个定时关机的控件。现把它提供出来,为和我类似的朋友提供小小的帮助。由于我也是初学乍练,有不当的地方请各位高手给予指出。 1、利用ATL COM Wizard新建一个ATL DLL工程,工程名定为TrueShutDown,其余所有设置保持为默认。 2、利用插入菜单中的"ATL Object Wizard"插入对象,选择复合控件(Comp

seu_xuxueqi的专栏 1136

duilib开发(九):定时器和超链接

一、定时器和超链接

yp18792574062的博客 1366

A Quick MFC and WTL Comparison

A Quick MFC and WTL Comparison By: Kenn Scribner for Visual C++ DeveloperDownload the codeBackground: I really like working with ATL, but it would have been nice

PowerDSP的专栏 1540

ATL和MFC来创建ActiveX控件(二)

ATL和MFC来创建ActiveX控件(二)2010-02-01 15:08  控件翻译  MFC和ATL在翻译处理上是相似的。在每一个框架里,实现控件的类具有一个名为OnDraw的虚函数。你只需将你的翻译代码添加到OnDraw函数里。然而,在各框架里,OnDraw函数得工作有所不同。  MFC的OnDraw在两种上下文下调用。第一个上下文发生在控件响应

HikerLive的专栏 977
上一篇: SIP:A Simple Session Establishment Example
下一篇: UDP穿越NAT原理(转载)
gxulg
博客等级 码龄23年 9粉丝 29原创
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值