« Intraprocess Message Queue | Main | iPhone TableView control »
Monday
Dec072009

Using the multimedia timer from c#

In my previous project I developed software components used in large scale real time simulations.  In many of the components I wrote, I needed to know a certain amount of time had passed before doing something.  I needed timing information that was more accurate, a timer that was more consistent during certain loads and had greater resolution. 

The timer options available in .Net do not meet these requirements.  The System.Windows.Forms.Timer can only be used for window forms based applications and does not meet the requirements stated above.  The System.Timers.Timer and System.Threading.Timer timers can be used without the need of a windows message pump and have better accuracy but are not consistent specially under heavy cpu loads.

The option of using the multimedia timer has greater resolution and accuracy the the timers provided by the standard .Net classes.  The MultimediaTimer class provides the same interface methods as the System.Timers.Timer class.

 

 

   1:  using System;
   2:  using System.Runtime.InteropServices;
   3:  using System.Diagnostics;
   4:   
   5:  namespace System.Timers
   6:  {
   7:      // Summary:
   8:      //     Represents the method that will handle the
   9:      //     System.Timers.MultimediaTimer.Elapsed event
  10:      //     of a System.Timers.MultimediaTimer.
  11:      //
  12:      // Parameters:
  13:      //   sender:
  14:      //     The source of the event.
  15:      //
  16:      //   e:
  17:      //     An System.Timers.MultimediaElapsedEventHandler object that contains
  18:      //     the event data.
  19:      public delegate void MultimediaElapsedEventHandler(object sender, 
  20:                                                  MultimediaElapsedEventArgs e);
  21:   
  22:      // Summary:
  23:      //     Provides data for the System.Timers.Timer.Elapsed event.
  24:      public class MultimediaElapsedEventArgs : EventArgs
  25:      {
  26:          // Summary:
  27:          //     Gets the time the System.Timers.Multimedia.Elapsed event was 
  28:          //     raised.
  29:          //
  30:          // Returns:
  31:          //     The time the System.Timers.Multimedia.Elapsed event was raised.
  32:          public DateTime SignalTime { get; internal set;}
  33:   
  34:          internal MultimediaElapsedEventArgs()
  35:          {
  36:              SignalTime = DateTime.Now;
  37:          }
  38:      }
  39:      
  40:      // Summary:
  41:      //     Generates recurring events in an application.
  42:      public class MultimediaTimer : IDisposable
  43:      {
  44:          //Lib API declarations
  45:          /// <summary>
  46:          /// Times the set event.
  47:          /// </summary>
  48:          /// <param name="uDelay">The u delay.</param>
  49:          /// <param name="uResolution">The u resolution.</param>
  50:          /// <param name="lpTimeProc">The lp time proc.</param>
  51:          /// <param name="dwUser">The dw user.</param>
  52:          /// <param name="fuEvent">The fu event.</param>
  53:          /// <returns></returns>
  54:          [DllImport("Winmm.dll", CharSet = CharSet.Auto)]
  55:          private static extern uint timeSetEvent(uint uDelay, uint uResolution, 
  56:                        TimerCallback lpTimeProc, UIntPtr dwUser, uint fuEvent);
  57:   
  58:          /// <summary>
  59:          /// Times the kill event.
  60:          /// </summary>
  61:          /// <param name="uTimerID">The u timer ID.</param>
  62:          /// <returns></returns>
  63:          [DllImport("Winmm.dll", CharSet = CharSet.Auto)]
  64:          private static extern uint timeKillEvent(uint uTimerID);
  65:   
  66:          /// <summary>
  67:          /// Times the get time.
  68:          /// </summary>
  69:          /// <returns></returns>
  70:          [DllImport("Winmm.dll", CharSet = CharSet.Auto)]
  71:          private static extern uint timeGetTime();
  72:   
  73:          /// <summary>
  74:          /// Times the begin period.
  75:          /// </summary>
  76:          /// <param name="uPeriod">The u period.</param>
  77:          /// <returns></returns>
  78:          [DllImport("Winmm.dll", CharSet = CharSet.Auto)]
  79:          private static extern uint timeBeginPeriod(uint uPeriod);
  80:   
  81:          /// <summary>
  82:          /// Times the end period.
  83:          /// </summary>
  84:          /// <param name="uPeriod">The u period.</param>
  85:          /// <returns></returns>
  86:          [DllImport("Winmm.dll", CharSet = CharSet.Auto)]
  87:          private static extern uint timeEndPeriod(uint uPeriod);
  88:   
  89:          
  90:          /// <summary>
  91:          ///Timer type definitions
  92:          /// </summary>
  93:          [Flags]
  94:          public enum fuEvent : uint
  95:          {
  96:              /// <summary>
  97:              /// OneHzSignalEvent occurs once, after uDelay milliseconds. 
  98:              /// </summary>
  99:              TIME_ONESHOT = 0,     
 100:              /// <summary>
 101:              /// 
 102:              /// </summary>
 103:              TIME_PERIODIC = 1,
 104:              /// <summary>
 105:              ///  callback is function
 106:              /// </summary>
 107:              TIME_CALLBACK_FUNCTION = 0x0000,  
 108:   
 109:          }
 110:   
 111:          /// <summary>
 112:          /// Delegate definition for the API callback
 113:          /// </summary>
 114:          private delegate void TimerCallback(uint uTimerID, uint uMsg,
 115:                                      UIntPtr dwUser, UIntPtr dw1, UIntPtr dw2);
 116:   
 117:          /// <summary>
 118:          /// The current timer instance ID
 119:          /// </summary>
 120:          private uint id = 0;
 121:   
 122:          /// <summary>
 123:          /// The callback used by the the API
 124:          /// </summary>
 125:          private TimerCallback timerCallback;
 126:   
 127:    
 128:          /// <summary>
 129:          /// Initializes a new instance of the System.Timers.MultimediaTimer 
 130:          //  class, and sets all the properties to their initial values.
 131:          /// </summary>
 132:          public MultimediaTimer()
 133:          {
 134:              Interval = 100;
 135:              AutoReset = true;
 136:              Enabled = false;
 137:              //Initialize the API callback
 138:              timerCallback = CallbackFunction;
 139:          }
 140:          
 141:          /// <summary>
 142:          ///    Initializes a new instance of the System.Timers.MultimediaTimer 
 143:          ///    class, and sets the
 144:          ///    System.Timers.MultimediaTimer.Interval property to the specified 
 145:          ///    time period.
 146:          ///
 147:          /// Parameters:
 148:          ///   interval:
 149:          ///     The time, in milliseconds, between events.
 150:          ///
 151:          /// Exceptions:
 152:          ///   System.ArgumentException:
 153:          ///     The value of the interval parameter is less than or equal to 
 154:          ///     zero, or greater than System.Int32.MaxValue.
 155:          /// </summary>
 156:          /// <param name="interval">The interval.</param>
 157:          public MultimediaTimer(uint interval)
 158:          {
 159:              Interval = interval;
 160:              AutoReset = true;
 161:              Enabled = false;
 162:              //Initialize the API callback
 163:              timerCallback = CallbackFunction;
 164:          }
 165:   
 166:          
 167:          /// <summary>
 168:          /// Gets or sets a value indicating whether the 
 169:          /// System.Timers.MultimediaTimer should raise
 170:          /// the System.Timers.MultimediaTimer.Elapsed event each time the 
 171:          /// specified interval elapses
 172:          /// or only after the first time it elapses.
 173:          ///
 174:          /// Returns:
 175:          ///     true if the System.Timers.MultimediaTimer should raise the 
 176:          ///     System.Timers.MultimediaTimer.Elapsed
 177:          ///     event each time the interval elapses; false if it should raise 
 178:          ///     the System.Timers.MultimediaTimer.Elapsed
 179:          ///     event only once, after the first time the interval elapses. The 
 180:          ///     default is true.
 181:          /// </summary>
 182:          /// <value><c>true</c> if [auto reset]; otherwise, <c>false</c>.</value>
 183:          public bool AutoReset { get; set; }
 184:          
 185:          /// <summary>
 186:          /// Gets or sets a value indicating whether the 
 187:          /// System.Timers.MultimediaTimer should raise
 188:          /// the System.Timers.MultimediaTimer.Elapsed event.
 189:          ///
 190:          /// Returns:
 191:          ///     true if the System.Timers.MultimediaTimer should raise the 
 192:          ///     System.Timers.MultimediaTimer.Elapsed
 193:          ///     event; otherwise, false. The default is false.
 194:          ///        
 195:          /// </summary>
 196:          /// <value><c>true</c> if enabled; otherwise, <c>false</c>.</value>
 197:          public bool Enabled { get; private set; }
 198:   
 199:          private object syncLock = new object();
 200:   
 201:          /// <summary>
 202:          /// Gets or sets the interval at which to raise the    
 203:          /// System.Timers.MultimediaTimer.Elapsed event.
 204:          ///
 205:          /// Returns:
 206:          ///     The time, in milliseconds, between raisings of the 
 207:          ///     System.Timers.MultimediaTimer.Elapsed
 208:          ///     event. The default is 100 milliseconds.
 209:          ///
 210:          /// Exceptions:
 211:          ///   System.ArgumentException:
 212:          ///     The interval is less than or equal to zero.
 213:          /// </summary>
 214:          /// <value>The interval.</value>
 215:          public uint Interval { get; set; }         
 216:         
 217:              
 218:          /// <summary>
 219:          /// Occurs when the interval elapses.  
 220:          /// </summary>
 221:          public event MultimediaElapsedEventHandler Elapsed;
 222:   
 223:         
 224:          /// <summary>
 225:          /// Releases the resources used by the System.Timers.MultimediaTimer.
 226:          /// </summary>
 227:          public void Close()
 228:          {
 229:              Dispose();
 230:          }
 231:                         
 232:            
 233:          /// <summary>
 234:          /// Starts raising the System.Timers.MultimediaTimer.Elapsed event by 
 235:          /// setting System.Timers.MultimediaTimer.Enabled
 236:          /// to true.
 237:          ///
 238:          /// Exceptions:
 239:          ///   System.ArgumentOutOfRangeException:
 240:          ///     The System.Timers.MultimediaTimer is created with an interval 
 241:          ///     equal to or greater than
 242:          ///     System.Int32.MaxValue + 1, or set to an interval less than zero.
 243:          /// </summary>
 244:          public void Start()
 245:          {
 246:              lock (syncLock)
 247:              {
 248:                  //Kill any existing timer
 249:                  Stop();
 250:                  Enabled = false;
 251:   
 252:                  //Set the timer type flags
 253:                  fuEvent f = fuEvent.TIME_CALLBACK_FUNCTION | (AutoReset ? 
 254:                                   fuEvent.TIME_PERIODIC : fuEvent.TIME_ONESHOT);
 255:                              
 256:                  id = timeSetEvent(Interval, 0, timerCallback, UIntPtr.Zero, 
 257:                                                                        (uint)f);
 258:                  if (id == 0)
 259:                  {
 260:                      throw new Exception("timeSetEvent error");
 261:                  }
 262:                  Debug.WriteLine("MultimediaTimer " + id.ToString() + " 
 263:                                                                       started");
 264:                  Enabled = true;
 265:              }
 266:          }
 267:   
 268:            
 269:          /// <summary>
 270:          /// Stops raising the System.Timers.MultimediaTimer.Elapsed event by 
 271:          /// setting System.Timers.MultimediaTimer.Enabled
 272:          ///  to false.
 273:          /// </summary>
 274:          public void Stop()
 275:          {
 276:              lock (syncLock)
 277:              {
 278:                  if (id != 0)
 279:                  {
 280:                      timeKillEvent(id);
 281:                      Debug.WriteLine("MultimediaTimer " + id.ToString() + " 
 282:                                                                        stopped");
 283:                      id = 0;
 284:                      Enabled = false;
 285:                  }
 286:              }
 287:          }
 288:   
 289:          /// <summary>
 290:          /// Called when [timer].
 291:          /// </summary>
 292:          protected virtual void OnTimer()
 293:          {
 294:              if (Elapsed != null)
 295:              {
 296:                  Elapsed(this, new MultimediaElapsedEventArgs());
 297:              }
 298:          }
 299:   
 300:          /// <summary>
 301:          /// CBs the func.
 302:          /// </summary>
 303:          /// <param name="uTimerID">The u timer ID.</param>
 304:          /// <param name="uMsg">The u MSG.</param>
 305:          /// <param name="dwUser">The dw user.</param>
 306:          /// <param name="dw1">The DW1.</param>
 307:          /// <param name="dw2">The DW2.</param>
 308:          private void CallbackFunction(uint uTimerID, uint uMsg, UIntPtr dwUser, 
 309:                                                        UIntPtr dw1, UIntPtr dw2)
 310:          {
 311:              //Callback from the MultimediaTimer API that fires the Timer event. 
 312:              // Note we are in a different thread here
 313:              OnTimer();
 314:          }
 315:   
 316:          #region IDisposable Members
 317:   
 318:          private bool _disposed = false;     
 319:          
 320:   
 321:          /// <summary>
 322:          /// Performs application-defined tasks associated with freeing, 
 323:          ///  releasing, or resetting unmanaged resources.
 324:          /// Releases all resources used by the current 
 325:          /// System.Timers.MultimediaTimer.
 326:          ///
 327:          /// Parameters:
 328:          ///   disposing:
 329:          ///     true to release both managed and unmanaged resources; false to 
 330:          ///     release only
 331:          ///     unmanaged resources.
 332:          /// </summary>
 333:          public void Dispose()
 334:          {
 335:              Dispose(true);
 336:              GC.SuppressFinalize(this);
 337:          }
 338:   
 339:          /// <summary>
 340:          /// Releases unmanaged and - optionally - managed resources
 341:          /// </summary>
 342:          /// <param name="disposing"><c>true</c> to release both managed and 
 343:          ///    unmanaged resources; <c>false</c> to release only unmanaged 
 344:          ///  resources.</param>
 345:          private void Dispose(bool disposing)
 346:          {
 347:              if (!_disposed)
 348:              {
 349:                  if (disposing)
 350:                  {
 351:                      Stop();
 352:                  }
 353:              }
 354:              _disposed = true;
 355:          }
 356:   
 357:          /// <summary>
 358:          /// Releases unmanaged resources and performs other cleanup operations 
 359:          /// before the
 360:          /// <see cref="MMTimer"/> is reclaimed by garbage collection.
 361:          /// </summary>
 362:          ~MultimediaTimer()
 363:          {
 364:              Dispose(false);
 365:          }
 366:   
 367:          #endregion
 368:      }
 369:  }
<\article>

PrintView Printer Friendly Version

EmailEmail Article to Friend

Reader Comments (1)

Hey, that was interesting,
Its fantastic i can now use the multimedia timer from c#
Anyway, thanks for the post

January 21, 2010 | Unregistered Commentersoftware development

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>