Timer这玩意儿很常用,却又很烦人。烦人之处有四:

1.         如果将其设到HWND上,则

a)         必须手工维护Timer的ID,小心翼翼地保证这些ID不重复,可能有人(比如我)就不怎么喜欢手工维护硬编码的ID。

b)         必须跟一个HWND关联,在没有HWND的时候,或者HWND不方便用的时候,就麻烦了。比如前公司有个GUI系统,是个类似HWND控件和DirectUI控件混合支持的系统(看上去很强大是不?),在HWND控件上使用SetTimer很方便,直接用原生的就行了,但如果在DirectUI控件上想要搞个Timer,就傻了。

2.         如果不将其设到HWND上,则

a)         ID倒是可以让它生成,虽然我很喜欢,但不一定所有人喜欢,这与1.a两者必居其一,无法两全。

b)         回调函数又涉及成员化的问题,不然在一个对象化的系统里就很难写。

就我个人而言,我喜欢2.a特性,因此着眼于解决2.b问题。

回调函数成员化,看着好像很眼熟。不错,我们曾经在《学习下WTL的thunk》里面干过这事情。因此几个月前我就觉得对于Timer也是可以做到的,但由于各种原因没时间去弄,同时也很遗憾前公司“架构师”没有采用这种方案。

今天刚刚打眼到公司有人也做了这件事(1.a + 2.b 模式),趁目前还没去很仔细地去研究,赶紧自己先写一个差异化版本,以避免不必要的版权纠纷^_^

Thunk需要占用一个正常参数。我们观察一下Timer的回调函数格式:

VOID CALLBACK TimerProc(

_In_  HWND hWnd,

_In_  UINT uMsg,

_In_  UINT_PTR idEvent,

_In_  DWORD dwTime

);

很不错,前面三个参数几乎都是没用的(至少第一个是没用的,这就够了)。

先把原先为了WNDPROC的Thunk改得通用些,WNDPROC改成LPVOID或者模版化,所有出现“Wnd”的地方都去掉“Wnd”字样,改完后变成:

http://xllib.codeplex.com/SourceControl/latest#SourceCode/xl/Win32/GUI/xlThunk.h

然后写Timer的实现。代码比较短,我先全贴了:

typedef Function<void (DWORD dwTime)> TimerCallback;

class Timer

{

public:

Timer() : m_uTimerId(0)

{

}

~Timer()

{

Kill();

}

public:

bool Set(UINT uElapse, TimerCallback fnCallback)

{

if (m_uTimerId != 0)

{

return false;

}

m_fnCallback = fnCallback;

m_thunk.SetObject(this);

m_thunk.SetRealProc(StaticTimerProc);

m_uTimerId = SetTimer(nullptr, 0, uElapse, m_thunk.GetThunkProc());

if (m_uTimerId == 0)

{

return false;

}

return true;

}

void Kill()

{

if (m_uTimerId != 0)

{

KillTimer(nullptr, m_uTimerId);

m_uTimerId = 0;

}

}

protected:

static VOID CALLBACK StaticTimerProc(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)

{

return ((Timer *)hWnd)->m_fnCallback(dwTime);

}

protected:

UINT_PTR m_uTimerId;

Thunk<TIMERPROC> m_thunk;

TimerCallback m_fnCallback;

};

注意Timer::Set里面,设好Thunk的数据以后,直接把Timer创建在Thunk上就可以了,比起窗口那个处理干净利落多了。咦?窗口里为什么要搞个StartProc,然后再在StartProc里把回调函数设到Thunk上呢?

是这样的,注册窗口类的时候就需要一个回调函数,此时窗口未创建。CreateWindow的过程中,会调用到回调函数(WM_CREATE),如果没有特殊处理,需要调用回DefWindowProc,其第一个参数是HWND,而我们此时如果使用Thunk的话,就会篡改掉系统调用回调函数时给出的HWND,从而没法正确调用DefWindowProc。也就是说,如果第一次被调用需要使用第一个参数的,就需要像窗口的处理一样,搞个StartProc第一次用。

这里我们使用SetTimer(NULL, ...),这第一个参数任何时候都不需要使用,所以可直接将Timer创建在Thunk上。

用例:

int main()

{

xl::Timer t;

t.Set(1000, [](DWORD dwTime)

{

printf("%u\n", dwTime);

});

MSG msg = {};

while (GetMessage(&msg, nullptr, 0, 0))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

return 0;

}

运行结果:

源代码见:

http://xllib.codeplex.com/SourceControl/latest#SourceCode/xl/Win32/Timer/xlTimer.h

流浪了近一个月,我又开始上班啦!

将 Timer 对象化的更多相关文章

  1. C# - 计时器Timer

    System.Timers.Timer 服务器计时器,允许指定在应用程序中引发事件的重复时间间隔. using System.Timers: // 在应用程序中生成定期事件 public class ...

  2. winform 用户控件、 动态创建添加控件、timer控件、控件联动

    用户控件: 相当于自定义的一个panel 里面可以放各种其他控件,并可以在后台一下调用整个此自定义控件. 使用方法:在项目上右键.添加.用户控件,之后用户控件的编辑与普通容器控件类似.如果要在后台往窗 ...

  3. 【WPF】 Timer与 dispatcherTimer 在wpf中你应该用哪个?

    源:Roboby 1.timer或重复生成timer事件,dispatchertimer是集成到队列中的一个时钟.2.dispatchertimer更适合在wpf中访问UI线程上的元素 3.Dispa ...

  4. STM32F10xxx 之 System tick Timer(SYSTICK Timer)

    背景 研究STM32F10xxx定时器的时候,无意间看到了System tick Timer,于是比较深入的了解下,在此做个记录. 正文 System tick Timer是Cotex-M内核的24位 ...

  5. 本地数据Store。Cookie,Session,Cache的理解。Timer类主要用于定时性、周期性任务 的触发。刷新Store,Panel

    本地数据Store var monthStore = Ext.create('Ext.data.Store', { storeId : 'monthStore', autoLoad : false, ...

  6. WinForm用户控件、动态创建添加控件、timer控件--2016年12月12日

    好文要顶 关注我 收藏该文 徐淳 关注 - 1 粉丝 - 3       0 0     用户控件: 通过布局将多个控件整合为一个控件,根据自己的需要进行修改,可对用户控件内的所有控件及控件属性进行修 ...

  7. java Timer 定时每天凌晨1点执行任务

    import java.util.TimerTask;/** * 执行内容 * @author admin_Hzw * */public class Task extends TimerTask {  ...

  8. [C#].NET中几种Timer的使用

    这篇博客将梳理一下.NET中4个Timer类,及其用法. 1. System.Threading.Timer public Timer(TimerCallback callback, object s ...

  9. 使用系统自带的GCD的timer倒计时模板语句遇到的小坑。。

    今天折腾了下系统gcd的 但是如果不调用这句dispatch_source_cancel()那么这个timer根本不工作....解决方法如下: 实现一个倒计时用自带的gcd如此简洁.. 原因可能是如果 ...

  10. C# 定时器 Timers.Timer Forms.Timer

    1.定义在System.Windows.Forms里 Windows.Forms里面的定时器比较简单,只要把工具箱中的Timer控件拖到窗体上,然后设置一下事件和间隔时间等属性就可以了 //启动定时器 ...

随机推荐

  1. LabVantage仪器数据采集方案

    LabVantage的仪器数据采集组件为LIMS CI,是一个独立的应用程序/服务,实现仪器数据的采集(GC.LC等带有工作站的仪器). 将仪器输出数据转换为LIMS所需数据并传输,使用Talend这 ...

  2. 解决@Url.Action("Action", "Controller",new {p1=v1,p2=v2 })的传参问题

    1.首先@Url.Action("Action", "Controller",new {p1=v1,p2=v2 })后面的model参数不可以直接用变量 需要先 ...

  3. SQL中事务以及全局变量的使用

    事务的定义 简单的说,事务处理可以用来维护数据库的完整性,保证一批SQL语句要么全执行,要么全部不执行 事务的特性 原子性  一致性  持久性  隔离性        注:一元九个 事务的使用 sel ...

  4. 七、Ajax请求

    七.Ajax请求 客户端(浏览器)向服务端发起请求的形式: 地址栏:GET 超链接标签:GET form表单:GET或POST Ajax(重要):GET或POST或PUT或DELETE AJAX(As ...

  5. Java8新特性:函数式编程

    1. 概述 函数式编程学习目的: 能够看懂公司里的代码 大数据量下处理集合效率更高 代码可读性高 消灭嵌套地狱 函数式编程思想: 面向对象思想需要关注用什么对象完成什么事情.而函数式编程思想就类似于我 ...

  6. 「浙江理工大学ACM入队200题系列」问题 H: 零基础学C/C++18——三位数反转

    本题是浙江理工大学ACM入队200题第二套中的H题 我们先来看一下这题的题面. 由于是比较靠前的题目,这里插一句.各位新ACMer朋友们,请一定要养成仔细耐心看题的习惯,尤其是要利用好输入和输出样例. ...

  7. 如何通过Java代码给Word文档添加水印?

    Word中可以为文档添加的水印分为两种形式:文字水印和图片水印.水印是一种数字保护的手段,在文档上添加水印可以传达有用信息,或者在不影响正文文字显示效果的同时,为打印文档增添视觉趣味,能起到传递信息, ...

  8. phpmyadmin 数据库导出数据到excel(图文版)

    查询到想要的数据后,点击上方或下方的"导出"按钮 格式选择"CSV for MS Excel" 如果快速导出的数据乱码,可以选择"导出方式" ...

  9. 使用CRD扩展Kubernetes API

    本文是如何创建 CRD 来扩展 Kubernetes API 的教程.CRD 是用来扩展 Kubernetes 最常用的方式,在 Service Mesh 和 Operator 中也被大量使用.因此读 ...

  10. yum的$releaser与$basearch

    最近配置centos 的yum 源时,想要配置一个通配的yum源,注意到发行的网络yum源的url地址中通常有两个变量 https://vault.centos.org/$releaser/cloud ...