ScpControl/ScpTimer.cs (102 lines of code) (raw):
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace ScpControl
{
/// <summary>
/// A high precision timer.
/// </summary>
public partial class ScpTimer : Component
{
#region Private fields
private readonly WaitOrTimerDelegate _callback;
private IntPtr _timerHandle = IntPtr.Zero;
#endregion
#region Ctors
public ScpTimer()
{
InitializeComponent();
Interval = 100;
_callback = OnTick;
}
public ScpTimer(IContainer container) : this()
{
container.Add(this);
}
#endregion
#region Public properties
// TODO: what's this used for?!
public object Tag { get; set; }
public bool Enabled
{
get { return (_timerHandle != IntPtr.Zero); }
set
{
lock (this)
{
if (Enabled == value) return;
if (value)
{
Start();
}
else
{
Stop();
}
}
}
}
public uint Interval { get; set; }
#endregion
#region Events
private void OnTick(IntPtr lpParameter, bool timerOrWaitFired)
{
if (Tick != null)
{
Tick(this, EventArgs.Empty);
}
}
public event EventHandler Tick;
#endregion
#region Public methods
public void Start()
{
lock (this)
{
if (!Enabled)
{
CreateTimerQueueTimer(out _timerHandle, IntPtr.Zero, _callback, IntPtr.Zero, Interval, Interval,
ExecuteFlags.WT_EXECUTEDEFAULT);
}
}
}
public void Stop()
{
lock (this)
{
if (Enabled)
{
DeleteTimerQueueTimer(IntPtr.Zero, _timerHandle, IntPtr.Zero);
_timerHandle = IntPtr.Zero;
}
}
}
#endregion
#region P/Invoke
private delegate void WaitOrTimerDelegate(IntPtr lpParameter, bool timerOrWaitFired);
private enum ExecuteFlags : uint
{
/// <summary>
/// By default, the callback function is queued to a non-I/O worker thread.
/// </summary>
WT_EXECUTEDEFAULT = 0x00000000,
/// <summary>
/// The callback function is invoked by the timer thread itself. This flag should be used only for short tasks or it
/// could affect other timer operations.
/// The callback function is queued as an APC. It should not perform alertable wait operations.
/// </summary>
WT_EXECUTEINTIMERTHREAD = 0x00000020,
/// <summary>
/// The callback function is queued to an I/O worker thread. This flag should be used if the function should be
/// executed in a thread that waits in an alertable state.
/// The callback function is queued as an APC. Be sure to address reentrancy issues if the function performs an
/// alertable wait operation.
/// </summary>
WT_EXECUTEINIOTHREAD = 0x00000001,
/// <summary>
/// The callback function is queued to a thread that never terminates. It does not guarantee that the same thread is
/// used each time. This flag should be used only for short tasks or it could affect other timer operations.
/// Note that currently no worker thread is truly persistent, although no worker thread will terminate if there are any
/// pending I/O requests.
/// </summary>
WT_EXECUTEINPERSISTENTTHREAD = 0x00000080,
/// <summary>
/// The callback function can perform a long wait. This flag helps the system to decide if it should create a new
/// thread.
/// </summary>
WT_EXECUTELONGFUNCTION = 0x00000010,
/// <summary>
/// The timer will be set to the signaled state only once. If this flag is set, the Period parameter must be zero.
/// </summary>
WT_EXECUTEONLYONCE = 0x00000008,
/// <summary>
/// Callback functions will use the current access token, whether it is a process or impersonation token. If this flag
/// is not specified, callback functions execute only with the process token.
/// Windows XP/2000: This flag is not supported until Windows XP with SP2 and Windows Server 2003.
/// </summary>
WT_TRANSFER_IMPERSONATION = 0x00000100
};
[DllImport("kernel32.dll")]
private static extern bool CreateTimerQueueTimer(out IntPtr phNewTimer,
IntPtr timerQueue, WaitOrTimerDelegate callback, IntPtr parameter,
uint dueTime, uint period, ExecuteFlags flags);
[DllImport("kernel32.dll")]
private static extern bool DeleteTimerQueueTimer(IntPtr timerQueue, IntPtr timer,
IntPtr completionEvent);
#endregion
}
}