namespace UniversalNetFrame451; using static Kernel32; using static Kernel32.DWORD; using HANDLE = IntPtr; public partial class Kernel32 { internal enum DWORD : uint { CREATE_WAITABLE_TIMER_HIGH_RESOLUTION = 0x00000002, SYNCHRONIZE = 0x00100000, TIMER_MODIFY_STATE = 0x0002, INFINITE = 0xFFFFFFFF } [DllImport("kernel32.dll", EntryPoint = "GetSystemTimeAsFileTime")] internal static extern void GetSystemTimeAsFileTime(ref ulong lpSystemTimeAsFileTime); [DllImport("kernel32.dll", EntryPoint = "CreateWaitableTimerExW")] internal static extern HANDLE CreateWaitableTimerEx(IntPtr lpTimerAttributes, [In()][MarshalAs(UnmanagedType.LPWStr)] string lpTimerName, DWORD dwFlags, DWORD dwDesiredAccess); [DllImport("kernel32.dll", EntryPoint = "SetWaitableTimer")] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool SetWaitableTimer(HANDLE hTimer, ref long lpDueTime, int lPeriod, IntPtr pfnCompletionRoutine, IntPtr lpArgToCompletionRoutine, [MarshalAs(UnmanagedType.Bool)] bool fResume); [DllImport("kernel32.dll", EntryPoint = "CancelWaitableTimer")] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool CancelWaitableTimer(HANDLE hTimer); [DllImport("kernel32.dll", EntryPoint = "WaitForSingleObject")] internal static extern uint WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); [DllImport("kernel32.dll", EntryPoint = "WaitForMultipleObjects")] internal static extern uint WaitForMultipleObjects(uint nCount, HANDLE[] lpHandles, int bWaitAll, DWORD dwMilliseconds); [DllImport("kernel32.dll", EntryPoint = "CloseHandle")] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool CloseHandle(HANDLE hObject); } public class SleepEx : IDisposable { HANDLE _hTimer = CreateWaitableTimerEx(IntPtr.Zero, null, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, SYNCHRONIZE | TIMER_MODIFY_STATE); private bool disposedValue; public bool Sleep(uint dwMilliseconds, WaitHandle breakHandle = null) { if (_hTimer == IntPtr.Zero) throw new NotSupportedException("CREATE_WAITABLE_TIMER_HIGH_RESOLUTION is supported in Windows 10, version 1803, and later"); long DueTime = -10 * 1000 * dwMilliseconds; SetWaitableTimer(_hTimer, ref DueTime, 0, IntPtr.Zero, IntPtr.Zero, false); if (breakHandle == null) return WaitForSingleObject(_hTimer, INFINITE) == 0; else return WaitForMultipleObjects(2, new nint[] { _hTimer, breakHandle.SafeWaitHandle.DangerousGetHandle() }, 0, INFINITE) == 0; } protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { // TODO: dispose managed state (managed objects) } // TODO: free unmanaged resources (unmanaged objects) and override finalizer // TODO: set large fields to null if (_hTimer != IntPtr.Zero) { CloseHandle(_hTimer); _hTimer = IntPtr.Zero; } disposedValue = true; } } // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources ~SleepEx() { // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method Dispose(disposing: false); } void IDisposable.Dispose() { // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method Dispose(disposing: true); GC.SuppressFinalize(this); } } public class TimerEx : IDisposable { HANDLE _hTimer = IntPtr.Zero; private readonly CancellationTokenSource _cancellationTokenSource = new(); private bool disposedValue; protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { // TODO: dispose managed state (managed objects) } // TODO: free unmanaged resources (unmanaged objects) and override finalizer // TODO: set large fields to null _cancellationTokenSource.Cancel(); if (_hTimer != IntPtr.Zero) { CancelWaitableTimer(_hTimer); CloseHandle(_hTimer); _hTimer = IntPtr.Zero; } disposedValue = true; } } // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources ~TimerEx() { // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method Dispose(disposing: false); } public void Dispose() { // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method Dispose(disposing: true); GC.SuppressFinalize(this); } public TimerEx(TimerCallback callback, object state, uint dueTime, uint period, uint times = uint.MaxValue) { ulong nowTime = 0; GetSystemTimeAsFileTime(ref nowTime); _hTimer = CreateWaitableTimerEx(IntPtr.Zero, null, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, SYNCHRONIZE | TIMER_MODIFY_STATE); if (_hTimer == IntPtr.Zero) throw new NotSupportedException("CREATE_WAITABLE_TIMER_HIGH_RESOLUTION is supported in Windows 10, version 1803, and later"); uint dueTimeMs = dueTime * 10000; uint periodMs = period * 10000; Task.Factory.StartNew(() => { long DueTime = (long)nowTime + dueTimeMs; SetWaitableTimer(_hTimer, ref DueTime, 0, IntPtr.Zero, IntPtr.Zero, false); WaitForSingleObject(_hTimer, INFINITE); for (uint i = 1; i <= times && !_cancellationTokenSource.IsCancellationRequested; ++i) { callback?.Invoke(state); if (disposedValue) break; GetSystemTimeAsFileTime(ref nowTime); for (; DueTime < (long)nowTime;) DueTime += periodMs; SetWaitableTimer(_hTimer, ref DueTime, 0, IntPtr.Zero, IntPtr.Zero, false); WaitForSingleObject(_hTimer, INFINITE); } }, _cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); } }