Windows高速定时器,多媒体定时器winmm.dll库的使用

项目里面用到的这些看起来名字高大上的定时器测试下来也是非常不准。看了源码发现也是用System.Timers.Timer或者用的是Thread休眠的方式来实现的。100毫秒就不准了。直到一番搜索,发现利用多媒体定时器winmm.dll的MillisecondTimer是可用的。原文来自博客(dehai)Timer计时不准确的问题及解决方法”。代码如下:
public sealed class MillisecondTimer : IComponent, IDisposable
{
//***************************************************** 字 段 *******************************************************************
private static TimerCaps caps;
private int interval;
private bool isRunning;
private int resolution;
private TimerCallback timerCallback;
private int timerID; //***************************************************** 属 性 *******************************************************************
/// <summary>
///
/// </summary>
public int Interval
{
get
{
return this.interval;
}
set
{
if ((value < caps.periodMin) || (value > caps.periodMax))
{
throw new Exception("超出计时范围!");
}
this.interval = value;
}
} /// <summary>
///
/// </summary>
public bool IsRunning
{
get
{
return this.isRunning;
}
} /// <summary>
///
/// </summary>
public ISite Site
{
set;
get;
} //***************************************************** 事 件 *******************************************************************
public event EventHandler Disposed; // 这个事件实现了IComponet接口
public event EventHandler Tick;
//*************************************************** 构造函数和释构函数 ****************************************************************** static MillisecondTimer()
{
timeGetDevCaps(ref caps, Marshal.SizeOf(caps));
} public MillisecondTimer()
{
this.interval = caps.periodMin; //
this.resolution = caps.periodMin; //
this.isRunning = false;
this.timerCallback = new TimerCallback(this.TimerEventCallback);
} public MillisecondTimer(IContainer container)
: this()
{
container.Add(this);
} ~MillisecondTimer()
{
timeKillEvent(this.timerID);
} //***************************************************** 方 法 *******************************************************************
/// <summary>
///
/// </summary>
public void Start()
{
if (!isRunning)
{
timerID = timeSetEvent(this.interval, this.resolution, this.timerCallback, 0, 1); // 间隔性地运行
if (timerID == 0)
{
throw new Exception("无法启动计时器");
}
isRunning = true;
}
} /// <summary>
///
/// </summary>
public void Stop()
{
if (isRunning)
{
timeKillEvent(this.timerID);
isRunning = false;
}
} /// <summary>
/// 实现IDisposable接口
/// </summary>
public void Dispose()
{
timeKillEvent(this.timerID);
GC.SuppressFinalize(this);
Disposed?.Invoke(this, EventArgs.Empty);
} //*************************************************** 内部函数 ******************************************************************
[DllImport("winmm.dll")]
private static extern int timeSetEvent(int delay, int resolution, TimerCallback callback, int user, int mode); [DllImport("winmm.dll")]
private static extern int timeKillEvent(int id); [DllImport("winmm.dll")]
private static extern int timeGetDevCaps(ref TimerCaps caps, int sizeOfTimerCaps); private void TimerEventCallback(int id, int msg, int user, int param1, int param2)
{
Tick?.Invoke(this, null); // 引发事件
}
//*************************************************** 内部类型 ****************************************************************** private delegate void TimerCallback(int id, int msg, int user, int param1, int param2); // timeSetEvent所对应的回调函数的签名 /// <summary>
/// 定时器的分辨率(resolution)。单位是ms,毫秒?
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct TimerCaps
{
public int periodMin;
public int periodMax;
}
}
}
测试代码
class Program
{
static Dictionary<string, int> dtList = new Dictionary<string, int>();
static void Main(string[] args)
{
//var timer = new DoubleThreadTimer(100, 100);
//timer.OnRunningCallback += Timer1_Tick;
//timer.Start(); var timer1 = new MillisecondTimer();
timer1.Interval = 2;
timer1.Tick += Timer1_Tick;
timer1.Start();
Console.ReadLine();
} private static void Timer1_Tick(object sender, EventArgs e)
{
var dt = DateTime.Now;
string dtStr = dt.ToString("yyyyMMdd HHmmss");
if (dtList.ContainsKey(dtStr))
{
dtList[dtStr]++;
if (dtList[dtStr] % 500 == 0)
{
Console.WriteLine(dtStr + "--->" + dtList[dtStr]);
dtList.Remove(dtStr);
}
}
else
{
if (dtList.Count > 1)
{
foreach (var item in dtList)
{
Console.WriteLine(item.Key + "--->" + item.Value);
}
dtList.Clear();
}
dtList.Add(dtStr, 1);
}
}
}

测试结论: 时间间隔设置为5毫秒,表现很稳定。2毫秒有时准。可靠性比.net的timer准好多倍啊
加上了些注释,并针对项目进行了封装:
/// <summary>
/// 用Windows多媒体定时器winmm.dll库封装的定时器
/// </summary>
public class MillisecondTimer : ITimer, IDisposable
{
#region 定时器事件类型
const int TIME_ONESHOT = 0;
const int TIME_PERIODIC = 1;
#endregion private bool isRunning = false;
/// <summary>
/// 定时器的分辨率(resolution)。单位是毫秒
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct TimerCaps
{
public int periodMin;
public int periodMax;
}
#region winmm.dll API声明
/// <summary>
/// 设置一个媒体定时器(定时器事件类型常用的有2种,0:TIME_ONESHOT,1:TIME_PERIODIC)
/// </summary>
/// <param name="delay">以毫秒指定事件的周期。</param>
/// <param name="resolution">以毫秒指定延时的精度,数值越小定时器事件分辨率越高。缺省值为1ms。</param>
/// <param name="callback">指向一个回调函数。(委托实例)</param>
/// <param name="user">存放用户提供的回调数据。</param>
/// <param name="mode">指定定时器事件类型:0->TIME_ONESHOT:uDelay毫秒后只产生一次事件;1->TIME_PERIODIC :每隔delay毫秒周期性地产生事件。</param>
/// <returns>定时器的ID,释放资源的时候需要</returns>
[DllImport("winmm.dll")]
private static extern int timeSetEvent(int delay, int resolution, TimerCallback callback, int user, int mode);
/// <summary>
/// 结束定时器,释放资源
/// </summary>
/// <param name="id">设置定时器返回的ID</param>
/// <returns></returns>
[DllImport("winmm.dll")]
private static extern int timeKillEvent(int id);
/// <summary>
/// 初始化结构体,TimerCaps{periodMin,periodMax}
/// </summary>
/// <param name="caps">TimerCaps</param>
/// <param name="sizeOfTimerCaps">TimerCaps的长度</param>
/// <returns></returns>
[DllImport("winmm.dll")]
private static extern int timeGetDevCaps(ref TimerCaps caps, int sizeOfTimerCaps);
#endregion
private delegate void TimerCallback(int id, int msg, int user, int param1, int param2); // timeSetEvent所对应的回调函数的签名
public event EES.Common.ManualTimer.TimerCallback OnRunningCallback;
public event EES.Common.ManualTimer.TimerCallback OnStartedCallback;
public event EES.Common.ManualTimer.TimerCallback OnStopedCallback; private TimerCallback m_TimerCallback;
private int timerID;
private TimerCaps caps = new TimerCaps(); public MillisecondTimer(int dueTime, int period)
{
timeGetDevCaps(ref caps, Marshal.SizeOf(caps));
caps.periodMin = period;
caps.periodMax = dueTime;
isRunning = false;
m_TimerCallback = new TimerCallback(TimerEventCallback);
} /// <summary>
/// 触发事件
/// </summary>
/// <param name="id"></param>
/// <param name="msg"></param>
/// <param name="user"></param>
/// <param name="param1"></param>
/// <param name="param2"></param>
private void TimerEventCallback(int id, int msg, int user, int param1, int param2)
{
OnRunningCallback?.Invoke(this, new EventArgs());
} /// <summary>
/// 启动定时器,回调OnStartedCallback
/// </summary>
public void Start()
{
if (!isRunning)
{
timerID = timeSetEvent(caps.periodMin, caps.periodMin, m_TimerCallback, 0, TIME_PERIODIC); // 间隔性地运行
GC.KeepAlive(m_TimerCallback);
if (timerID == 0)
{
throw new EESException("无法启动计时器");
}
isRunning = true;
if (isRunning)
OnStartedCallback?.Invoke(this, EventArgs.Empty);
}
}
/// <summary>
///停止定时器
/// </summary>
public void Stop()
{
if (isRunning)
{
timeKillEvent(timerID);
isRunning = false;
OnStopedCallback?.Invoke(this, EventArgs.Empty);
Dispose();
}
}
/// <summary>
/// 获取定时器允许状态
/// </summary>
/// <returns></returns>
public bool IsRunning()
{
return isRunning;
}
/// <summary>
/// Dispose
/// </summary>
public void Dispose()
{
if (!timerID.Equals(0))
timeKillEvent(timerID);
GC.SuppressFinalize(this);
}
}
Windows高速定时器,多媒体定时器winmm.dll库的使用的更多相关文章
- CVE-2012-0003 Microsoft Windows Media Player ‘winmm.dll’ MIDI文件解析远程代码执行漏洞 分析
[CNNVD]Microsoft Windows Media Player ‘winmm.dll’ MIDI文件解析远程代码执行漏洞(CNNVD-201201-110) Microsoft Wi ...
- winmm.dll包含函数
DLL 文件: winmm 或者 winmm.dll DLL 名称: Windows Multimedia API 描述: winmm.dll是Windows多媒体相关应用程序接口,用于低档的音频和游 ...
- VC++或QT下 高精度 多媒体定时器
在VC编程中,用SetTimer可以定义一个定时器,到时间了,就响应OnTimer消息,但这种定时器精度太低了.如果需要精度更高一些的定时器(精 确到1ms),可以使用下面的高精度多媒体定时器进行代码 ...
- CVE-2012-0003:Microsoft Windows Media Player winmm.dll MIDI 文件堆溢出漏洞调试分析
0x01 蜘蛛漏洞攻击包 前言:2012 年 2月,地下黑产中流行着一款国产名为蜘蛛漏洞的攻击包 -- "Zhi-Zhu Exploit Pack",该工具包含 5 个漏洞,都是在 ...
- 【STM32H7教程】第32章 STM32H7的TIM定时器基础知识和HAL库API
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第32章 STM32H7的TIM定时器基础知识和H ...
- windows 自带winmm.dll播放音频问题
同事用的一个录音小程序在他机器上可以用,换了两个电脑不能用,获取音频长度时总是0,检查代码也没有发现具体问题.最后发现是电脑声卡驱动的问题.更新声卡驱动好了. 附上播放音频的代码: 首先,导入dll文 ...
- STM32 HAL库学习系列第4篇 定时器TIM----- 开始定时器与PWM输出配置
基本流程: 1.配置定时器 2.开启定时器 3.动态改变pwm输出,改变值 HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1); 函数总结: __HAL_TIM ...
- golang调用c++的dll库文件
最近使用golang调用c++的dll库文件,简单了解了一下,特作此笔记:一.DLL 的编制与具体的编程语言及编译器无关 dll分com的dll和动态dll,Com组件dll:不管是何种语言写的都可以 ...
- JNA 如何 加载多个 存在依赖的 DLL 库
JNA 的出现,极大的简化了原有的 JNI 技术.下面是JNA github地址:https://github.com/java-native-access/jna 1. 简单的一个例子: /** S ...
随机推荐
- Android开发之漫漫长途 Ⅷ——Android Binder(也许是最容易理解的)
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...
- cocos2dx-3.0(14)------SpriteBatchNode与SpriteFrameCache加快渲染
----我的生活,我的点点滴滴! ! 大家都知道一个游戏里面会有大量的图片.每一个图片渲染是须要时间的,以下分析两个类来加快渲染速度.加快游戏执行速度 一.SpriteBatchNode 1.先说下渲 ...
- POJ 2127 最长公共上升子序列
动态规划法: #include <iostream> #include <cstdio> #include <fstream> #include <algor ...
- hdu 4939
题意: 长度为n个单位的map,每一个单位须要时间t来走完. 每一个单位能够放置一个塔.一共同拥有三种塔,每种塔的作用不同: 1.仅仅能攻击当前单位.每秒x点伤害(红塔) 2.攻击当前单位之后的全部单 ...
- 开发wordpress主题
查看我的wordpress站点 第一步:下载wordpress安装包 官网下载==> 第二步:本地安装开发环境 官网给出的环境要求: 可以选择安装wamp/xampp集成PHP开发环境,安装很简 ...
- Html转JSP样式变型问题解决
一.问题描述 在我没将写好的 html 页面转变为 java web 中的 JSP 页面时.有时会发现,我们将 css .js 都引入到了页面中.当样式和我们想象的不一样,那么我们就要去解决这个问题. ...
- 《ActiveMQ in Action》【PDF】下载
内容介绍TheApache ActiveMQ message broker is an open source implementation ofthe Java Message Service sp ...
- jmeter监控服务资源
转:http://www.cnblogs.com/chengtch/p/6079262.html 1.下载需要的jmeter插件 如图上面两个是jmeter插件,可以再下面的链接中下载: https ...
- 【Jenkins】Jenkins权威指南英文版:jenkins-the-definitive-guide.pdf
原文链接:http://www.bogotobogo.com/DevOps/Jenkins/images/Intro_install/jenkins-the-definitive-guide.pdf
- VR\AR 使用 SceneKit
VR\AR 使用 SceneKit http://www.jianshu.com/c/70d63e3941fd