毫秒级别计时器扩展----------WinForm控件开发系列
该控件是继承于 Component 基类开发的。主要是利用 winmm.dll 的 timeGetDevCaps 、timeSetEvent 、 timeKillEvent 来完成。
1 /// <summary> 2 /// 毫秒级别计时器扩展 3 /// </summary> 4 [ToolboxItem(true)] 5 [Description("毫秒级别计时器扩展")] 6 [DefaultProperty("Interval")] 7 [DefaultEvent("Tick")] 8 public partial class TimerExt : Component 9 { 10 #region 新增事件 11 12 private event EventHandler tick; 13 /// <summary> 14 /// 计时器间隔引发事件 15 /// </summary> 16 public event EventHandler Tick 17 { 18 add { this.tick += value; } 19 remove { this.tick -= value; } 20 } 21 22 #endregion 23 24 #region 新增属性 25 26 private uint interval = 10; 27 /// <summary> 28 /// 获取或设置在相对于上一次发生的Tick 事件引发的时间(以毫秒为单位)。 29 /// </summary> 30 [DefaultValue(10)] 31 [Description("获取或设置在相对于上一次发生的Tick 事件引发的时间(以毫秒为单位)。")] 32 public uint Interval 33 { 34 get 35 { 36 return this.interval; 37 } 38 set 39 { 40 if (this.interval == value || value < timecaps.wPeriodMin || value > timecaps.wPeriodMax) 41 return; 42 43 this.interval = value; 44 45 if (this.Enabled) 46 { 47 this.ReStart(); 48 } 49 } 50 } 51 52 private bool enabled = false; 53 /// <summary> 54 /// 获取或设置计时器是否正在运行。 55 /// </summary> 56 [DefaultValue(false)] 57 [Description("获取或设置计时器是否正在运行。")] 58 public bool Enabled 59 { 60 get 61 { 62 return this.enabled; 63 } 64 set 65 { 66 if (this.enabled == value) 67 return; 68 69 if (this.enabled == false) 70 { 71 this.Start(); 72 } 73 else 74 { 75 this.Stop(); 76 } 77 this.enabled = value; 78 } 79 } 80 81 /// <summary> 82 /// 计时器分辨率的信息 83 /// </summary> 84 [Description("计时器分辨率的信息")] 85 public TIMECAPS Timecaps 86 { 87 get { return TimerExt.timecaps; } 88 } 89 90 #endregion 91 92 #region 字段 93 94 /// <summary> 95 /// 计时器分辨率的信息 96 /// </summary> 97 private static TIMECAPS timecaps; 98 99 /// <summary> 100 ///作为fptc参数的函数指针 101 /// </summary> 102 private TimerExtCallback timerExtCallback; 103 104 /// <summary> 105 /// 定期是标识 106 /// </summary> 107 private uint timerID; 108 109 #endregion 110 111 #region 扩展 112 113 private delegate void TimerExtCallback(uint uTimerID, uint uMsg, uint dwUser, UIntPtr dw1, UIntPtr dw2); // timeSetEvent所对应的回调函数的签名 114 115 /// <summary> 116 /// 查询计时器设备以确定其分辨率成功 117 /// </summary> 118 private const int TIMERR_NOERROR = 0x0000; 119 120 /// <summary> 121 /// 当计时器到期时,系统将调用fptc参数指向的函数。 122 /// </summary> 123 private const int TIME_CALLBACK_FUNCTION = 0x0001; 124 125 /// <summary> 126 /// 此结构包含有关计时器分辨率的信息。单位是ms 127 /// </summary> 128 [Description("此结构包含有关计时器分辨率的信息。单位是ms")] 129 [StructLayout(LayoutKind.Sequential)] 130 public struct TIMECAPS 131 { 132 /// <summary> 133 /// 支持的最小期限。 134 /// </summary> 135 [Description("支持的最小期限")] 136 public uint wPeriodMin; 137 /// <summary> 138 /// 支持的最大期限。 139 /// </summary> 140 [Description("支持的最大期限")] 141 public uint wPeriodMax; 142 } 143 144 /// <summary> 145 /// 此函数启动指定的计时器事件。 146 /// </summary> 147 /// <param name="uDelay">事件延迟,以毫秒为单位。如果该值不在计时器支持的最小和最大事件延迟范围内,则该函数返回错误。</param> 148 /// <param name="uResolution">计时器事件的分辨率,以毫秒为单位。分辨率越高,分辨率越高;零分辨率表示周期性事件应该以最大可能的精度发生。但是,为减少系统开销,应使用适合您的应用程序的最大值。</param> 149 /// <param name="fptc">如果fuEvent指定TIME_CALLBACK_EVENT_SET或TIME_CALLBACK_EVENT_PULSE标志,则fptc参数将解释为事件对象的句柄。事件将在单个事件完成时设置或发出脉冲,或者在周期性事件完成时定期设置或触发。对于fuEvent的任何其他值,fptc参数将被解释为函数指针。</param> 150 /// <param name="dwUser">用户提供的回调数据。</param> 151 /// <param name="fuEvent">计时器事件类型。下表显示了fuEvent参数可以包含的值。</param> 152 /// <returns></returns> 153 [DllImport("winmm.dll")] 154 private static extern uint timeSetEvent(uint uDelay, uint uResolution, TimerExtCallback fptc, uint dwUser, uint fuEvent); 155 156 /// <summary> 157 /// 此功能取消指定的计时器事件。 158 /// </summary> 159 /// <param name="id">要取消的计时器事件的标识符。此标识符由timeSetEvent函数返回,该函数启动指定的计时器事件。</param> 160 /// <returns></returns> 161 [DllImport("winmm.dll")] 162 private static extern uint timeKillEvent(uint uTimerID); 163 164 /// <summary> 165 /// 此函数查询计时器设备以确定其分辨率。 166 /// </summary> 167 /// <param name="ptc">指向TIMECAPS结构的指针。该结构充满了有关计时器设备分辨率的信息。</param> 168 /// <param name="cbtc">TIMECAPS结构的大小(以字节为单位)。</param> 169 /// <returns>如果成功,则返回TIMERR_NOERROR,如果未能返回计时器设备功能,则返回TIMERR_STRUCT。</returns> 170 [DllImport("winmm.dll")] 171 private static extern uint timeGetDevCaps(ref TIMECAPS ptc, int cbtc); 172 173 #endregion 174 175 static TimerExt() 176 { 177 uint result = timeGetDevCaps(ref timecaps, Marshal.SizeOf(timecaps)); 178 if (result != TIMERR_NOERROR) 179 { 180 throw new Exception("timeGetDevCaps失败"); 181 } 182 } 183 184 public TimerExt() 185 { 186 this.timerExtCallback = new TimerExtCallback(this.TimerExtCallbackFun); 187 InitializeComponent(); 188 } 189 190 public TimerExt(IContainer container) 191 { 192 this.timerExtCallback = new TimerExtCallback(this.TimerExtCallbackFun); 193 194 container.Add(this); 195 InitializeComponent(); 196 } 197 198 #region 重写 199 200 /// <summary> 201 /// 清理所有正在使用的资源。 202 /// </summary> 203 /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param> 204 protected override void Dispose(bool disposing) 205 { 206 if (disposing && (components != null)) 207 { 208 components.Dispose(); 209 } 210 211 if (this.timerID != 0) 212 { 213 timeKillEvent(this.timerID); 214 this.timerID = 0; 215 } 216 base.Dispose(disposing); 217 } 218 219 #endregion 220 221 #region 公开方法 222 223 /// <summary> 224 /// 启动定时器 225 /// </summary> 226 public void Start() 227 { 228 if (!this.Enabled) 229 { 230 uint result = timeSetEvent(this.interval, Math.Min(1, timecaps.wPeriodMin), this.timerExtCallback, 0, TIME_CALLBACK_FUNCTION); // 间隔性地运行 231 if (result == 0) 232 { 233 throw new Exception("timeSetEvent启动计时器失败"); 234 } 235 this.enabled = true; 236 this.timerID = result; 237 } 238 } 239 240 /// <summary> 241 /// 重新开始定时器 242 /// </summary> 243 public void ReStart() 244 { 245 this.Stop(); 246 this.Start(); 247 } 248 249 /// <summary> 250 /// 暂停定时器 251 /// </summary> 252 public void Stop() 253 { 254 if (this.Enabled) 255 { 256 timeKillEvent(this.timerID); 257 this.enabled = false; 258 } 259 } 260 261 #endregion 262 263 #region 私有方法 264 265 private void TimerExtCallbackFun(uint uTimerID, uint uMsg, uint dwUser, UIntPtr dw1, UIntPtr dw2) 266 { 267 if (this.tick != null) 268 { 269 this.tick(this, null); 270 } 271 } 272 273 #endregion 274 275 }