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. IDEA清空控制台以及Java中运行cmd命令实现清屏操作

    IDEA中清空控制台方法 在网上有看到各种的实现方法,比如: Runtime.getRuntime().exec("cls"); 或者: public static void cl ...

  2. 知识图谱顶会论文(SIGIR-2022) MorsE:归纳知识图嵌入的元知识迁移

    MorsE:归纳知识图嵌入的元知识迁移 论文题目: Meta-Knowledge Transfer for Inductive Knowledge Graph Embedding 论文地址: http ...

  3. 题解 UVA439 骑士的移动 Knight Moves

    前言 最近板子题刷多了-- 题意 一个 \(8\times 8\) 的棋盘,问马从起点到终点的最短步数为多少. \(\sf Solution\) 要求最短路径嘛,显然 bfs 更优. 读入 这个读入处 ...

  4. JMETER与BeanShell

    变量 Beanshell应用自定义变量有两种方法: #第一种方法,使用${key}格式,但是需要注意这是用应用的变量是没有定义数据类型的 #log.info(String Key)只能打印字符串,所以 ...

  5. Oracle性能优化之运行参数设置

    Oracle参数调整建议值 sessions=2150 processes=2000 open_cursors=5120 db_file_multiblock_read_count=64 log_bu ...

  6. Go语言核心36讲27

    在前面的文章中,我们一起学习了Go程序测试的基础知识和基本测试手法.这主要包括了Go程序测试的基本规则和主要流程.testing.T类型和testing.B类型的常用方法.go test命令的基本使用 ...

  7. Windows自带管理工具

    exe类notepad 记事本 control 控制面板 mstsc 远程桌面连接explorer 资源管理器 taskmgr 任务管理器resmon 资源监视器 perfmon 性能监视器reged ...

  8. vue 3.0 常用api 的简介

    vue3.0 生命周期 写法一 和vue2.x 一致 区别在于(beforeUnmount.unmount)名称不一样 写法二 在setup 中使用, 需要引用 如: import { onBefor ...

  9. bugku web2

    打开全是滑稽.... 直接查看源码(用firefox的F12来查看,其他的会注释掉)可以得到flag(抓包不知道怎么回事抓不到,不然应该也可以看到注释的flag)

  10. cv2.imread opencv读取不到图片问题

    解决办法 cv2.imread 路径中包含中文,改为英文 其他 这个问题解决了很久,都属于库的问题