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. 魔改editormd组件,优化ToC渲染效果

    前言 我的StarBlog博客目前使用 editor.md 组件在前端渲染markdown文章,但这个组件自动生成的ToC(内容目录)不是很美观,我之前魔改过一个树形组件 BootStrap-Tree ...

  2. 教你如何解决T+0的问题

    摘要:T+0查询是指实时数据查询,数据查询统计时将涉及到最新产生的数据. 本文分享自华为云社区<大数据解决方案:解决T+0问题>,作者: 小虚竹 . T+0问题 T+0查询是指实时数据查询 ...

  3. 学习ASP.NET Core Blazor编程系列九——服务器端校验

    学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...

  4. Archlinux + Dwm 配置流程

    本着学习C的态度来了解dwm,本身作为一个i3wm的追崇者,与dwm会擦出怎么样的火花呢? 下载安装dwm archlinuxcn源配置 编辑/etc/pacman.conf文件,添加bfsu的arc ...

  5. 修改linux系统时间

    在Linux系统中,可以用date命令来显示或设定系统的日期与时间 1. 查看系统时间 [root@iZ2ze0gm3scdypc0i15r8yZ ~]# date Tue Aug 16 00:10: ...

  6. selenium被某些网页检测不允许正常访问、登录等,解决办法

    网站通过什么方式检测 function b() { return "$cdc_asdjflasutopfhvcZLmcfl_"in u || d.webdriver } 通过上方的 ...

  7. 【DL论文精读笔记】AlexNet

    1.1引言 1.2数据集 就是ImageNet,当时计算机视觉最大的数据集 1.3结构 采用双GPU结构实现,并行处理图像,2-3,5-全连接部分中间还将特征图共享 最后全连接层输出的4096的语义信 ...

  8. hashlib/subprocess/logging模块

    内容概要 hashlib加密模块 subprocess模块 logging日志模块 软件开发主要流程 加密详情 1.加密的意义: 加密是指将明文数据转化程密文数据>>>>为了保 ...

  9. 【Devexpress】gridcontorl设置某个特定单元格不可编辑

    在gridcontorl中一般情况下我们使用的都是设置一列不可编辑 那么如何设置一个单元格不可编辑呢 在gridView1_ShowingEditor事件中可以实现.这个事件的意思是允许取消激活编辑器 ...

  10. Fastjsonfan反序列化(一)

    前置知识 Fastjson 是一个 Java 库,可以将 Java 对象转换为 JSON 格式,当然它也可以将 JSON 字符串转换为 Java 对象. Fastjson 可以操作任何 Java 对象 ...