Skip to content

Instantly share code, notes, and snippets.

@KOZ60
Last active June 4, 2024 10:57
Show Gist options
  • Save KOZ60/bc903f37b8938b075087ade91d324672 to your computer and use it in GitHub Desktop.
Save KOZ60/bc903f37b8938b075087ade91d324672 to your computer and use it in GitHub Desktop.
WaitableTimer
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.Win32.SafeHandles;
[DesignerCategory("code")]
public class WaitableTimer : Component
{
private static object eventElapsed = new object();
private long intervalTicks;
private bool enabled;
private readonly WaitbleTimerSlim hTimer;
private readonly EventWaitHandle cancelHandle = new ManualResetEvent(false);
private Thread thread;
public WaitableTimer() {
hTimer = new WaitbleTimerSlim();
}
public WaitableTimer(IContainer container) : this() {
if (container == null) {
throw new ArgumentNullException("container");
}
container.Add(this);
}
protected override void Dispose(bool disposing) {
base.Dispose(disposing);
hTimer.Dispose();
cancelHandle.Dispose();
}
public void Start() {
Enabled = true;
}
public void Stop() {
Enabled = false;
}
public bool Enabled {
get {
return enabled;
}
set {
if (enabled != value) {
enabled = value;
if (!DesignMode) {
if (value) {
cancelHandle.Reset();
thread = new Thread(new ParameterizedThreadStart(Loop));
thread.Start(this);
} else {
cancelHandle.Set();
thread.Join();
thread = null;
}
}
}
}
}
public long IntervalTicks {
get {
return intervalTicks;
}
set {
if (value < 0) {
throw new ArgumentException();
}
intervalTicks = value;
}
}
public decimal IntervalMillisecond {
get {
return IntervalTicks / TimeSpan.TicksPerMillisecond;
}
set {
IntervalTicks = (long)(value * TimeSpan.TicksPerMillisecond);
}
}
protected virtual void OnElapsed(EventArgs e) {
var handler = (EventHandler)Events[eventElapsed];
handler?.Invoke(this, e);
}
public event EventHandler Elapsed {
add {
Events.AddHandler(eventElapsed, value);
}
remove {
Events.RemoveHandler(eventElapsed, value);
}
}
private const int WAIT_1 = 1;
private static void Loop(object args) {
var owner = (WaitableTimer)args;
WaitHandle[] handles = new WaitHandle[] { owner.cancelHandle, owner.hTimer };
long ticks = owner.IntervalTicks;
DateTime utc = DateTime.UtcNow;
long dueTime = utc.ToFileTimeUtc() + ticks;
owner.hTimer.SetWaitableTimer(dueTime, 0);
while (WaitHandle.WaitAny(handles) == WAIT_1) {
owner.OnElapsed(EventArgs.Empty);
dueTime += ticks;
owner.hTimer.SetWaitableTimer(dueTime, 0);
}
}
}
public class WaitbleTimerSlim : WaitHandle
{
public WaitbleTimerSlim() {
var hTimer = CreateWaitableTimer(IntPtr.Zero, false, null);
if (hTimer == null || hTimer.IsInvalid) {
throw new Win32Exception();
}
SafeWaitHandle = hTimer;
}
public void SetWaitableTimer(long dueTime, int period) {
if (!SetWaitableTimer(SafeWaitHandle,
ref dueTime,
period,
IntPtr.Zero,
IntPtr.Zero, false)) {
throw new Win32Exception();
}
}
public void Cancel() {
if (!CancelWaitableTimer(SafeWaitHandle)) {
throw new Win32Exception();
}
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern SafeWaitHandle CreateWaitableTimer(
IntPtr lpTimerAttributes, bool bManualReset, string lpTimerName);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetWaitableTimer(
SafeWaitHandle hTimer, [In] ref long pDueTime, int lPeriod,
IntPtr pfnCompletionRoutine, IntPtr lpArgToCompletionRoutine, bool fResume);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CancelWaitableTimer(SafeWaitHandle hTimer);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment