Skip to content

Instantly share code, notes, and snippets.

@mjs3339
Last active September 1, 2022 07:14
Show Gist options
  • Save mjs3339/b98bbf4075be0176ac521c9875652dfe to your computer and use it in GitHub Desktop.
Save mjs3339/b98bbf4075be0176ac521c9875652dfe to your computer and use it in GitHub Desktop.
C# Multimedia Timer TimeSetEvent TimeKillEvent
public class MMTimer : IDisposable
{
private const int EventTypePeriodic = 1;
private const uint STATUS_SUCCESS = 0;
private const uint STATUS_TIMER_RESOLUTION_NOT_SET = 0xC0000245;
private readonly MultimediaTimerCallback Callback;
private bool disposed;
private bool enabled;
private bool highestPossibleResolution;
private int interval, resolution;
private volatile uint timerId;
public MMTimer()
{
Callback = TimerCallbackMethod;
Interval = 10;
}
[Description("Enables or Disables this timer event.")]
public bool Enabled
{
get => enabled;
set
{
if (value == enabled)
return;
enabled = value;
if (enabled)
Start();
else
Stop();
}
}
[Description("Data associated with this object.")]
public object Tag { get; set; }
public int Interval
{
get => interval;
set
{
if (disposed)
throw new ObjectDisposedException("MMTimer");
if (value < 0)
throw new ArgumentOutOfRangeException("value");
interval = value;
var tc = GetTimerCaps();
if (value < tc.periodMin || value > tc.periodMax)
throw new Exception($"Value{value} is out of range.\nMust be greater than or equal to{tc.periodMin} or \n" +
$"Less than or equal to{tc.periodMax} (Milliseconds).");
}
}
[Description("If set to true the effect is system wide.")]
public bool HighestPossibleResolution
{
get => highestPossibleResolution;
set
{
if (IsRunning)
Stop();
highestPossibleResolution = value;
var DesiredResolutionIn100nsUnits = 5000u;
var CurrentResolution = 0u;
lock (this)
{
switch (NtSetTimerResolution(DesiredResolutionIn100nsUnits, highestPossibleResolution, ref CurrentResolution))
{
case STATUS_SUCCESS:
Tag = $"The current resolution is {CurrentResolution / 10000D} Ms.";
break;
case STATUS_TIMER_RESOLUTION_NOT_SET:
Tag = "Error setting resolution.";
break;
default:
throw new Exception("NtSetTimerResolution call failed.");
}
}
}
}
public bool IsRunning => timerId != 0;
public void Dispose()
{
Dispose(true);
}
private void Dispose(bool disposing)
{
if (disposed)
return;
disposed = true;
if (IsRunning)
{
TimeKillEvent(timerId);
timerId = 0;
}
if (disposing)
{
Elapsed = null;
GC.SuppressFinalize(this);
}
}
public event EventHandler Elapsed;
~MMTimer()
{
Dispose(false);
}
private static TimerCaps GetTimerCaps()
{
var caps = new TimerCaps();
timeGetDevCaps(ref caps, Marshal.SizeOf(caps));
return caps;
}
[DllImport("ntdll.dll", EntryPoint = "NtSetTimerResolution")]
private static extern uint NtSetTimerResolution(uint DesiredResolution, bool SetResolution, ref uint CurrentResolution);
public void Start()
{
if (disposed)
throw new ObjectDisposedException("MMTimer");
if (IsRunning)
return;
uint userCtx = 0;
timerId = TimeSetEvent((uint) Interval, 1, Callback, ref userCtx, EventTypePeriodic);
if (timerId == 0)
{
var error = Marshal.GetLastWin32Error();
throw new Win32Exception(error);
}
}
public void Stop()
{
if (disposed)
throw new ObjectDisposedException("MMTimer");
if (!IsRunning)
return;
TimeKillEvent(timerId);
timerId = 0;
}
[DllImport("winmm.dll")]
private static extern int timeGetDevCaps(ref TimerCaps caps, int sizeOfTimerCaps);
[DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeKillEvent")]
private static extern void TimeKillEvent(uint uTimerId);
private void TimerCallbackMethod(uint id, uint msg, ref uint userCtx, uint rsv1, uint rsv2)
{
var handler = Elapsed;
if (handler != null)
handler(this, EventArgs.Empty);
}
[DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeSetEvent")]
private static extern uint TimeSetEvent(uint msDelay, uint msResolution, MultimediaTimerCallback callback, ref uint userCtx, uint eventType);
[StructLayout(LayoutKind.Sequential)]
private struct TimerCaps
{
public readonly int periodMin;
public readonly int periodMax;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment