
該控件是繼承於 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 }
