HighPercisionTimer.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. namespace UniversalNetFrame451;
  2. using static Kernel32;
  3. using static Kernel32.DWORD;
  4. using HANDLE = IntPtr;
  5. public partial class Kernel32
  6. {
  7. internal enum DWORD : uint
  8. {
  9. CREATE_WAITABLE_TIMER_HIGH_RESOLUTION = 0x00000002,
  10. SYNCHRONIZE = 0x00100000,
  11. TIMER_MODIFY_STATE = 0x0002,
  12. INFINITE = 0xFFFFFFFF
  13. }
  14. [DllImport("kernel32.dll", EntryPoint = "GetSystemTimeAsFileTime")]
  15. internal static extern void GetSystemTimeAsFileTime(ref ulong lpSystemTimeAsFileTime);
  16. [DllImport("kernel32.dll", EntryPoint = "CreateWaitableTimerExW")]
  17. internal static extern HANDLE CreateWaitableTimerEx(IntPtr lpTimerAttributes, [In()][MarshalAs(UnmanagedType.LPWStr)] string lpTimerName, DWORD dwFlags, DWORD dwDesiredAccess);
  18. [DllImport("kernel32.dll", EntryPoint = "SetWaitableTimer")]
  19. [return: MarshalAs(UnmanagedType.Bool)]
  20. internal static extern bool SetWaitableTimer(HANDLE hTimer, ref long lpDueTime, int lPeriod, IntPtr pfnCompletionRoutine, IntPtr lpArgToCompletionRoutine, [MarshalAs(UnmanagedType.Bool)] bool fResume);
  21. [DllImport("kernel32.dll", EntryPoint = "CancelWaitableTimer")]
  22. [return: MarshalAs(UnmanagedType.Bool)]
  23. internal static extern bool CancelWaitableTimer(HANDLE hTimer);
  24. [DllImport("kernel32.dll", EntryPoint = "WaitForSingleObject")]
  25. internal static extern uint WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
  26. [DllImport("kernel32.dll", EntryPoint = "WaitForMultipleObjects")]
  27. internal static extern uint WaitForMultipleObjects(uint nCount, HANDLE[] lpHandles, int bWaitAll, DWORD dwMilliseconds);
  28. [DllImport("kernel32.dll", EntryPoint = "CloseHandle")]
  29. [return: MarshalAs(UnmanagedType.Bool)]
  30. internal static extern bool CloseHandle(HANDLE hObject);
  31. }
  32. public class SleepEx : IDisposable
  33. {
  34. HANDLE _hTimer = CreateWaitableTimerEx(IntPtr.Zero, null, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, SYNCHRONIZE | TIMER_MODIFY_STATE);
  35. private bool disposedValue;
  36. public bool Sleep(uint dwMilliseconds, WaitHandle breakHandle = null)
  37. {
  38. if (_hTimer == IntPtr.Zero)
  39. throw new NotSupportedException("CREATE_WAITABLE_TIMER_HIGH_RESOLUTION is supported in Windows 10, version 1803, and later");
  40. long DueTime = -10 * 1000 * dwMilliseconds;
  41. SetWaitableTimer(_hTimer, ref DueTime, 0, IntPtr.Zero, IntPtr.Zero, false);
  42. if (breakHandle == null)
  43. return WaitForSingleObject(_hTimer, INFINITE) == 0;
  44. else
  45. return WaitForMultipleObjects(2, new nint[] { _hTimer, breakHandle.SafeWaitHandle.DangerousGetHandle() }, 0, INFINITE) == 0;
  46. }
  47. protected virtual void Dispose(bool disposing)
  48. {
  49. if (!disposedValue)
  50. {
  51. if (disposing)
  52. {
  53. // TODO: dispose managed state (managed objects)
  54. }
  55. // TODO: free unmanaged resources (unmanaged objects) and override finalizer
  56. // TODO: set large fields to null
  57. if (_hTimer != IntPtr.Zero)
  58. {
  59. CloseHandle(_hTimer);
  60. _hTimer = IntPtr.Zero;
  61. }
  62. disposedValue = true;
  63. }
  64. }
  65. // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
  66. ~SleepEx()
  67. {
  68. // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
  69. Dispose(disposing: false);
  70. }
  71. void IDisposable.Dispose()
  72. {
  73. // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
  74. Dispose(disposing: true);
  75. GC.SuppressFinalize(this);
  76. }
  77. }
  78. public class TimerEx : IDisposable
  79. {
  80. HANDLE _hTimer = IntPtr.Zero;
  81. private readonly CancellationTokenSource _cancellationTokenSource = new();
  82. private bool disposedValue;
  83. protected virtual void Dispose(bool disposing)
  84. {
  85. if (!disposedValue)
  86. {
  87. if (disposing)
  88. {
  89. // TODO: dispose managed state (managed objects)
  90. }
  91. // TODO: free unmanaged resources (unmanaged objects) and override finalizer
  92. // TODO: set large fields to null
  93. _cancellationTokenSource.Cancel();
  94. if (_hTimer != IntPtr.Zero)
  95. {
  96. CancelWaitableTimer(_hTimer);
  97. CloseHandle(_hTimer);
  98. _hTimer = IntPtr.Zero;
  99. }
  100. disposedValue = true;
  101. }
  102. }
  103. // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
  104. ~TimerEx()
  105. {
  106. // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
  107. Dispose(disposing: false);
  108. }
  109. public void Dispose()
  110. {
  111. // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
  112. Dispose(disposing: true);
  113. GC.SuppressFinalize(this);
  114. }
  115. public TimerEx(TimerCallback callback, object state, uint dueTime, uint period, uint times = uint.MaxValue)
  116. {
  117. ulong nowTime = 0;
  118. GetSystemTimeAsFileTime(ref nowTime);
  119. _hTimer = CreateWaitableTimerEx(IntPtr.Zero, null, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, SYNCHRONIZE | TIMER_MODIFY_STATE);
  120. if (_hTimer == IntPtr.Zero)
  121. throw new NotSupportedException("CREATE_WAITABLE_TIMER_HIGH_RESOLUTION is supported in Windows 10, version 1803, and later");
  122. uint dueTimeMs = dueTime * 10000;
  123. uint periodMs = period * 10000;
  124. Task.Factory.StartNew(() =>
  125. {
  126. long DueTime = (long)nowTime + dueTimeMs;
  127. SetWaitableTimer(_hTimer, ref DueTime, 0, IntPtr.Zero, IntPtr.Zero, false);
  128. WaitForSingleObject(_hTimer, INFINITE);
  129. for (uint i = 1; i <= times && !_cancellationTokenSource.IsCancellationRequested; ++i)
  130. {
  131. callback?.Invoke(state);
  132. if (disposedValue)
  133. break;
  134. GetSystemTimeAsFileTime(ref nowTime);
  135. for (; DueTime < (long)nowTime;)
  136. DueTime += periodMs;
  137. SetWaitableTimer(_hTimer, ref DueTime, 0, IntPtr.Zero, IntPtr.Zero, false);
  138. WaitForSingleObject(_hTimer, INFINITE);
  139. }
  140. }, _cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
  141. }
  142. }