• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dapplo / Dapplo.Windows / 24262799127

10 Apr 2026 08:30PM UTC coverage: 32.208% (+0.2%) from 31.968%
24262799127

push

github

web-flow
Merge pull request #55 from dapplo/copilot/add-more-apis-for-system-state

Add Dapplo.Windows.SystemState package for system power state management

607 of 2018 branches covered (30.08%)

Branch coverage included in aggregate %.

41 of 74 new or added lines in 4 files covered. (55.41%)

1 existing line in 1 file now uncovered.

1702 of 5151 relevant lines covered (33.04%)

28.79 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.43
/src/Dapplo.Windows.SystemState/WaitableTimer.cs
1
// Copyright (c) Dapplo and contributors. All rights reserved.
2
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3

4
using System;
5
using System.Threading;
6

7
namespace Dapplo.Windows.SystemState;
8

9
/// <summary>
10
/// A managed wrapper around a Windows waitable timer that can optionally wake the system from sleep or hibernation.
11
/// See <a href="https://learn.microsoft.com/en-us/windows/win32/sync/waitable-timer-objects">Waitable Timer Objects</a>
12
/// </summary>
13
public sealed class WaitableTimer : IDisposable
14
{
15
    private IntPtr _handle;
16
    private bool _disposed;
17

18
    /// <summary>
19
    /// Gets a value indicating whether the timer has been created successfully.
20
    /// </summary>
21
    public bool IsValid => _handle != IntPtr.Zero;
2✔
22

23
    /// <summary>
24
    /// Creates a new unnamed waitable timer.
25
    /// </summary>
26
    /// <param name="manualReset">
27
    /// If <c>true</c>, creates a manual-reset notification timer.
28
    /// If <c>false</c>, creates a synchronization timer.
29
    /// </param>
30
    public WaitableTimer(bool manualReset = false)
5✔
31
    {
32
        _handle = SystemStateApi.CreateWaitableTimer(IntPtr.Zero, manualReset, null);
5✔
33
        if (_handle == IntPtr.Zero)
5!
34
        {
NEW
35
            throw new InvalidOperationException($"Failed to create waitable timer. Error: {System.Runtime.InteropServices.Marshal.GetLastWin32Error()}");
×
36
        }
37
    }
5✔
38

39
    /// <summary>
40
    /// Creates or opens a named waitable timer.
41
    /// </summary>
42
    /// <param name="name">The name of the timer.</param>
43
    /// <param name="manualReset">
44
    /// If <c>true</c>, creates a manual-reset notification timer.
45
    /// If <c>false</c>, creates a synchronization timer.
46
    /// </param>
47
    public WaitableTimer(string name, bool manualReset = false)
1✔
48
    {
49
        _handle = SystemStateApi.CreateWaitableTimer(IntPtr.Zero, manualReset, name);
1✔
50
        if (_handle == IntPtr.Zero)
1!
51
        {
NEW
52
            throw new InvalidOperationException($"Failed to create waitable timer '{name}'. Error: {System.Runtime.InteropServices.Marshal.GetLastWin32Error()}");
×
53
        }
54
    }
1✔
55

56
    /// <summary>
57
    /// Sets the timer to fire once after the specified delay.
58
    /// </summary>
59
    /// <param name="delay">The delay before the timer fires.</param>
60
    /// <param name="wakeSystem">
61
    /// If <c>true</c>, the system will be woken from sleep or hibernation when the timer fires.
62
    /// Requires the SE_SYSTEMTIME_NAME privilege.
63
    /// </param>
64
    /// <returns><c>true</c> if the timer was set successfully.</returns>
65
    public bool SetOnce(TimeSpan delay, bool wakeSystem = false)
66
    {
67
        ThrowIfDisposed();
3✔
68
        // Convert TimeSpan to 100-nanosecond intervals (negative = relative time)
69
        long dueTime = -delay.Ticks;
2✔
70
        return SystemStateApi.SetWaitableTimer(_handle, ref dueTime, 0, IntPtr.Zero, IntPtr.Zero, wakeSystem);
2✔
71
    }
72

73
    /// <summary>
74
    /// Sets the timer to fire at the specified absolute UTC time.
75
    /// </summary>
76
    /// <param name="dueTime">The UTC time at which the timer should fire.</param>
77
    /// <param name="wakeSystem">
78
    /// If <c>true</c>, the system will be woken from sleep or hibernation when the timer fires.
79
    /// Requires the SE_SYSTEMTIME_NAME privilege.
80
    /// </param>
81
    /// <returns><c>true</c> if the timer was set successfully.</returns>
82
    public bool SetAt(DateTimeOffset dueTime, bool wakeSystem = false)
83
    {
84
        ThrowIfDisposed();
1✔
85
        long fileTime = dueTime.ToFileTime();
1✔
86
        return SystemStateApi.SetWaitableTimer(_handle, ref fileTime, 0, IntPtr.Zero, IntPtr.Zero, wakeSystem);
1✔
87
    }
88

89
    /// <summary>
90
    /// Sets the timer to fire periodically.
91
    /// </summary>
92
    /// <param name="initialDelay">The delay before the first firing.</param>
93
    /// <param name="period">The period between subsequent firings, in milliseconds.</param>
94
    /// <param name="wakeSystem">
95
    /// If <c>true</c>, the system will be woken from sleep or hibernation on the first firing.
96
    /// Requires the SE_SYSTEMTIME_NAME privilege.
97
    /// </param>
98
    /// <returns><c>true</c> if the timer was set successfully.</returns>
99
    public bool SetPeriodic(TimeSpan initialDelay, int period, bool wakeSystem = false)
100
    {
NEW
101
        ThrowIfDisposed();
×
NEW
102
        long dueTime = -initialDelay.Ticks;
×
NEW
103
        return SystemStateApi.SetWaitableTimer(_handle, ref dueTime, period, IntPtr.Zero, IntPtr.Zero, wakeSystem);
×
104
    }
105

106
    /// <summary>
107
    /// Cancels the timer so it no longer fires.
108
    /// </summary>
109
    /// <returns><c>true</c> if the cancel was successful.</returns>
110
    public bool Cancel()
111
    {
112
        ThrowIfDisposed();
1✔
113
        return SystemStateApi.CancelWaitableTimer(_handle);
1✔
114
    }
115

116
    /// <summary>
117
    /// Waits for the timer to be signaled.
118
    /// </summary>
119
    /// <param name="timeout">Maximum time to wait. Use <see cref="Timeout.InfiniteTimeSpan"/> to wait indefinitely.</param>
120
    /// <returns><c>true</c> if the timer was signaled; <c>false</c> if the wait timed out.</returns>
121
    public bool Wait(TimeSpan timeout)
122
    {
123
        ThrowIfDisposed();
2✔
124
        using var waitHandle = new WaitableTimerWaitHandle(_handle);
2✔
125
        return waitHandle.WaitOne(timeout);
2✔
126
    }
2✔
127

128
    /// <summary>
129
    /// Waits indefinitely for the timer to be signaled.
130
    /// </summary>
131
    public void Wait()
132
    {
NEW
133
        Wait(Timeout.InfiniteTimeSpan);
×
NEW
134
    }
×
135

136
    private void ThrowIfDisposed()
137
    {
138
        if (_disposed)
7✔
139
        {
140
            throw new ObjectDisposedException(nameof(WaitableTimer));
1✔
141
        }
142
    }
6✔
143

144
    /// <inheritdoc/>
145
    public void Dispose()
146
    {
147
        if (_disposed)
6!
148
        {
NEW
149
            return;
×
150
        }
151

152
        _disposed = true;
6✔
153
        if (_handle != IntPtr.Zero)
6✔
154
        {
155
            SystemStateApi.CloseHandle(_handle);
6✔
156
            _handle = IntPtr.Zero;
6✔
157
        }
158
    }
6✔
159

160
    /// <summary>
161
    /// A WaitHandle wrapper for the waitable timer that does not own the handle (no close on finalize).
162
    /// </summary>
163
    private sealed class WaitableTimerWaitHandle : WaitHandle
164
    {
165
        public WaitableTimerWaitHandle(IntPtr handle)
2✔
166
        {
167
            SafeWaitHandle = new Microsoft.Win32.SafeHandles.SafeWaitHandle(handle, ownsHandle: false);
2✔
168
        }
2✔
169
    }
170
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc