Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>There's no managed API that I'm aware of, but that said, it's one of the not-so-hairy P/Invokes to do - here's a quick-and-dirty class I just threw together and simple usage:</p> <p>(I should note I've only <em>very</em> basically tested this...probably needs some tweaking) [EDIT: ok, got a chance to tune it a bit over lunch, this should <em>work</em>, more or less]</p> <pre><code>void Main() { var waitFor = 6000; var tickAt = 2000; var tickEvery = 1000; var sw = Stopwatch.StartNew(); var running = true; var apcTask = Task.Factory.StartNew(() =&gt; { try { Console.WriteLine("APC:Creating timer..."); ApcTimer timer = new ApcTimer(@"Global\WillThisWork", tickAt, tickEvery, true); timer.Tick += (o,e) =&gt; { Console.WriteLine("APC:Hey, it worked! - delta:{0}", sw.Elapsed); }; Console.WriteLine("APC:Starting timer..."); timer.Start(); while(running); Console.WriteLine("APC:Stopping timer..."); timer.Dispose(); Console.WriteLine("APC:Finishing - delta:{0}", sw.Elapsed); } catch(Exception ex) { Console.WriteLine(ex); } }); Thread.Sleep(waitFor); running = false; Task.WaitAll(apcTask); } public class ApcTimer : IDisposable { public delegate void TimerApcCallback(object sender, EventArgs args); public event TimerApcCallback Tick; private const long _MILLISECOND = 10000; private const long _SECOND = 10000000; private IntPtr _hTimer = IntPtr.Zero; private long _delayInMs; private int _period; private bool _resumeFromSleep; private Task _alerter; private CancellationTokenSource _cancelSource; private bool _timerRunning; public ApcTimer( string timerName, long delayInMs, int period, bool resumeFromSleep) { _hTimer = CreateWaitableTimer(IntPtr.Zero, false,timerName); if(_hTimer == IntPtr.Zero) { // This'll grab the last win32 error nicely throw new System.ComponentModel.Win32Exception(); } _delayInMs = delayInMs; _period = period; _resumeFromSleep = resumeFromSleep; } public void Start() { var sw = Stopwatch.StartNew(); Debug.WriteLine("ApcTimer::Starting timer..."); StopAlerter(); SetTimer(_delayInMs); _cancelSource = new CancellationTokenSource(); _alerter = Task.Factory.StartNew( ()=&gt; { _timerRunning = true; while(_timerRunning) { var res = WaitForSingleObject(_hTimer, -1); if(res == WaitForResult.WAIT_OBJECT_0) { if(Tick != null) { Tick.Invoke(this, new EventArgs()); } SetTimer(_period); } } }, _cancelSource.Token); Debug.WriteLine("ApcTimer::Started!"); } public void Dispose() { Debug.WriteLine("ApcTimer::Stopping timer..."); StopAlerter(); CancelPendingTimer(); if(_hTimer != IntPtr.Zero) { var closeSucc = CloseHandle(_hTimer); if(!closeSucc) { throw new System.ComponentModel.Win32Exception(); } _hTimer = IntPtr.Zero; } Debug.WriteLine("ApcTimer::Stopped!"); } private void SetTimer(long waitMs) { // timer delay is normally in 100 ns increments var delayInBlocks = new LARGE_INTEGER() { QuadPart = (waitMs * _MILLISECOND * -1)}; bool setSucc = false; setSucc = SetWaitableTimer(_hTimer, ref delayInBlocks, 0, IntPtr.Zero, IntPtr.Zero, _resumeFromSleep); if(!setSucc) { // This'll grab the last win32 error nicely throw new System.ComponentModel.Win32Exception(); } } private void CancelPendingTimer() { if(_hTimer != IntPtr.Zero) { Debug.WriteLine("ApcTimer::Cancelling pending timer..."); CancelWaitableTimer(_hTimer); } } private void StopAlerter() { _timerRunning = false; if(_alerter != null) { Debug.WriteLine("ApcTimer::Shutting down alerter..."); _cancelSource.Cancel(); Task.WaitAll(_alerter); } } #region secret pinvoke goodness [DllImport("Kernel32.dll", SetLastError=true)] static extern WaitForResult WaitForSingleObject([In] IntPtr hHandle, int dwMilliseconds); [DllImport("Kernel32.dll", SetLastError=true)] [return:MarshalAs(UnmanagedType.Bool)] static extern bool CancelWaitableTimer([In] IntPtr hTimer); [DllImport("Kernel32.dll", SetLastError=true)] [return:MarshalAs(UnmanagedType.Bool)] static extern bool SetWaitableTimer( [In] IntPtr hTimer, [In] ref LARGE_INTEGER dueTime, [In] int period, [In] IntPtr completionRoutine, [In] IntPtr argToCallback, [In] bool resume); [DllImport("Kernel32.dll", SetLastError=true)] static extern IntPtr CreateWaitableTimer( IntPtr securityAttributes, bool manualReset, string timerName); [DllImport("Kernel32.dll", SetLastError=true)] static extern IntPtr CreateWaitableTimerEx( IntPtr securityAttributes, string timerName, TimerCreateFlags flags, TimerAccessFlags desiredAccess); [DllImport("Kernel32.dll", SetLastError=true)] [return:MarshalAs(UnmanagedType.Bool)] static extern bool CloseHandle(IntPtr handle); private const int INFINITE_TIMEOUT = 1; [Flags] private enum WaitForResult : int { WAIT_ABANDONED = 0x00000080, WAIT_OBJECT_0 = 0, WAIT_TIMEOUT = 0x00000102, WAIT_FAILED = -1 } [Flags] private enum TimerAccessFlags : int { TIMER_ALL_ACCESS = 0x1F0003, TIMER_MODIFY_STATE = 0x0002, TIMER_QUERY_STATE = 0x0001 } [Flags] private enum TimerCreateFlags : int { CREATE_WAITABLE_TIMER_MANUAL_RESET = 0x00000001 } [StructLayout(LayoutKind.Sequential)] public struct LargeIntegerSplitPart { public uint LowPart; public int HighPart; } [StructLayout(LayoutKind.Explicit)] public struct LARGE_INTEGER { [FieldOffset(0)] public LargeIntegerSplitPart u; [FieldOffset(0)] public long QuadPart; } #endregion } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload