Sleep, Hibernation, Modern Standby, and Long-Running Apps — Designing Around 'It Stopped Overnight'
· Go Komura · Sleep, Modern Standby, Power Management, SetThreadExecutionState, Long-Running, Timer, Tray-Resident App, C#, .NET, Bug Investigation, Windows Development, Technical Consulting
“I was sure the monitoring app had been running all night, but when I checked in the morning the graph just stopped dead at 1 AM.” “The collection tool had been rock-solid for months on a desktop PC, but the moment we switched to a laptop, gaps started showing up in the records.” — Of all the symptoms that come up in consultations about long-running business apps, this is the classic of classics. Check the logs, and there’s no exception, no crash — just several hours of records missing entirely. Far more often than not, the culprit isn’t a bug in the app at all — it’s Windows’s power management.
What makes this tricky is that the word “sleep” isn’t actually one single thing. Traditional S3 sleep, hibernation (S4), and Modern Standby (S0 low power idle) — now the norm on newer laptops — each look different from an app’s perspective, both in how they stop it and in which countermeasures work. Modern Standby in particular gets misunderstood because of its billing as “the system keeps running even during sleep,” but in reality, desktop apps get actively stopped even more aggressively than under the older models.
This article covers just enough about the types of sleep and how an app behaves during sleep, then works through how to choose among three designs — preventing sleep, designing around sleep, and waking the machine at a set time — complete with implementation examples and a decision table.
1. The Bottom Line First
- Windows has three sleep models — traditional S3 sleep, hibernation (S4), and Modern Standby (S0 low power idle) — and a Modern Standby-capable machine doesn’t support S1 through S3. You can check which one your PC uses with
powercfg /a.123 - Desktop apps don’t keep running during Modern Standby either. The Desktop Activity Moderator (DAM) suspends the threads of desktop processes (services in Session 0 get throttled instead). Don’t design on the assumption that “it’s S0, so it must keep running.”4
- No threads run during sleep, and how a timer’s deadline gets counted also differs by API generation. Since Windows 8, relative timers and waits (
SetWaitableTimer’s relative form,SleepEx, and the like) don’t count time spent asleep, and carry the remaining duration over to after resume.56 .NET’s timers, through .NET 10, are implemented to count sleep time (so if the deadline passed while asleep, they fire right after resume), and starting with .NET 11 they switch to not counting it.6 Elapsed-time APIs are likewise a mix of ones that do count sleep time (GetTickCount, QueryPerformanceCounter — the basis for Stopwatch) and ones that don’t (QueryUnbiasedInterruptTime).78 - A TCP connection that goes idle during sleep can end up silently dropped by an intermediate device’s idle timeout — a NAT, a firewall, a load balancer (for example, Azure Load Balancer’s default is to drop it after 4 minutes with no notification). Design on the assumption that you’ll need to reconnect after resume.94
- The proper way to suppress sleep is
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED). It only affects automatic sleep triggered by an idle timeout, though — it cannot stop sleep the user explicitly triggers with the power button or by closing the lid. Set it only for the section you actually need, and always clear it afterward.1011 - You can confirm whether suppression is taking effect with
powercfg /requests. ThePowerCreateRequestfamily of APIs lets you attach a reason string to the request, which shows up in this listing — making it much easier to investigate in the field.121314 - If you’re designing around sleep instead, detect it in .NET with
SystemEvents.PowerModeChanged, or in Win32 withWM_POWERBROADCAST(PBT_APMSUSPEND / PBT_APMRESUMEAUTOMATIC). The grace period for the suspend notification is only about 2 seconds per app, and under critical battery conditions the system can go to sleep with no notification at all.15161718 - To run something reliably at a fixed time, Task Scheduler’s “Wake the computer to run this task” (WakeToRun) is the first choice. It keeps the system awake until the task finishes. It does depend on the wake-timer permission in Power Options, though, so verifying that the machine actually wakes up in the field is essential.1920
2. Windows Sleep Isn’t One Single Thing
Let’s start with just the three states you need as groundwork before thinking about countermeasures.
| State | Common name | What it is | From an app’s perspective |
|---|---|---|---|
| S3 | Traditional sleep | CPU halted, only RAM stays powered to hold state | No computation runs at all1 |
| S4 | Hibernation | Memory contents written to a hibernation file, then power off | Same as above; resume restores from that file1 |
| S0 low power idle | Modern Standby | System keeps partially running at low power and resumes instantly | Desktop apps get stopped by DAM24 |
S3 and S4 are both “a state where the system runs no computation at all and looks, to all appearances, off.”1 Modern Standby, by contrast, is a model closer to a smartphone’s power model: it keeps waiting at low power with the network connection maintained even with the screen off, and resumes in under a second from the power button. Machines that support Modern Standby don’t use S1 through S3.221
What matters here is the Desktop Activity Moderator (DAM) that ships on Modern Standby-capable machines. DAM is the mechanism that clamps down desktop app execution to be equivalent to S3 sleep when the system enters standby: processes in an interactive session have every one of their threads suspended, while services in Session 0 get throttled (suspended most of the time, running only intermittently).4 In other words, the expectation that “apps keep running during sleep because it’s Modern Standby” is exactly backwards — from the app’s perspective it stops just like under S3 — and, unlike S3, because “the system itself is running while only the app is stopped,” it’s safest to understand this as something that shows up as clock drift and inconsistent timer behavior.4
You can check which model your PC (or a customer’s on-site PC) uses, without needing administrator rights, with the following command.3
> powercfg /a
The following sleep states are available on this system:
Standby (S0 Low Power Idle) Network Connected
Hibernate
...
If it shows Standby (S3), the machine is an S3 machine; if it shows S0 Low Power Idle, it’s a Modern Standby machine. Whenever the complaint is “records started dropping out after we switched to a laptop,” this is the first thing to check.
3. What Happens to an App During Sleep and After Resume
3.1 Threads and Timers
No threads execute during sleep (S3/S4, and during a DAM suspend).14 What’s easy to overlook is how a timer’s or a wait’s “deadline” counts sleep time, and this differs depending on which layer of the API you’re using.
- A Win32 relative timer (the relative form of
SetWaitableTimer/SetWaitableTimerEx) counted time spent in low-power states on Windows 7 and earlier (the countdown kept advancing even during sleep), but doesn’t on Windows 8 and later. A relative timer that spans a sleep fires only after waiting out its remaining time following resume.5 - Likewise, wait APIs that take a timeout (
SleepEx,WaitForMultipleObjectsEx, and the like) stop counting non-running time such as sleep, starting with Windows 8. The remaining time carries over across the sleep.6 - When a managed .NET timer (
System.Threading.Timerand the like) fires depends on the runtime version. That’s becauseEnvironment.TickCount64includes sleep time through .NET 10 (it’s based on GetTickCount64) and stops including it starting with .NET 11 (switching to a QueryUnbiasedInterruptTime basis); the change documentation itself warns that “there may be code that no longer fires the timer immediately after resume” as a result.6
In other words, if “an app measuring with a 10-second System.Threading.Timer sleeps for 8 hours,” it will never fire 8 hours’ worth of ticks all at once regardless — but whether it fires once immediately after resume, or waits out its remaining time first, depends on the API layer and the runtime version. Either way, samples during sleep are lost. Rather than build your recovery logic around “it fires right after resume,” it’s safer to explicitly reschedule using the resume event covered in Chapter 5.
3.2 Elapsed-Time Measurement Drifts
Elapsed-time APIs are a mix of ones that include sleep time and ones that don’t.
| API | Sleep time |
|---|---|
| GetTickCount / GetTickCount64 | Included7 |
| QueryPerformanceCounter (the basis for .NET’s Stopwatch) | Included (standby, hibernate, connected standby)8 |
| QueryUnbiasedInterruptTime | Not included (working-state time only)227 |
| Environment.TickCount / TickCount64 | Included through .NET 10 (GetTickCount64-based); changed to not included starting with .NET 11 (QueryUnbiasedInterruptTime-based)6 |
Code that says “take the next sample once Stopwatch says 10 seconds have passed” ends up meaning “8 hours and 10 seconds have passed” once it spans a sleep, and conversely, TickCount-based elapsed-time checks change behavior once you move to .NET 11. Keeping a clear division of labor — use Stopwatch for “how long an operation took,” and DateTime/DateTimeOffset for “the wall-clock time something should next run,” re-anchoring that baseline on the resume event (Chapter 5) — keeps you from getting tripped up by either sleep or a runtime upgrade. The design of short-period timers itself is covered in Why You Should Prefer Event Waits Over Sleep(1) on Windows.
3.3 A TCP Connection Dies “Silently”
During sleep, since the app can’t communicate, the connection goes idle. The problem lies with devices along the path. Intermediate devices like NAT, firewalls, and load balancers drop an idle flow on a timeout, and in many configurations, they drop it silently, notifying neither side. Azure Load Balancer’s default behavior, for instance, is to “silently drop a flow once it reaches the idle timeout (4 minutes by default).”9 Internal office routers and the embedded TCP stacks on devices in the field carry similar timeouts.
As a result, the app’s socket looks perfectly fine right after resume, and then either the next send errors out, or it hangs waiting for a response until it times out. DAM’s documentation itself explicitly notes that you need to account for how process suspension affects a connection’s lifetime and any in-progress handshake.4 On top of that, on a Modern Standby machine running on battery, network activity during sleep is, by default, quiesced entirely (Adaptive Connected Standby).23 The standard practice is to treat the connection as suspect and discard/reconnect as soon as you get a resume event. For diagnosing a connection that “looks alive but is actually dead,” see Diagnosing Why TCP Retransmission Stalls Industrial Camera Communication as well.
4. Making Sure It Doesn’t Sleep — SetThreadExecutionState and Power Requests
When you can’t afford sleep for just the few hours a measurement is running, the proper tool is SetThreadExecutionState. Call it from C# via P/Invoke.
using System.Runtime.InteropServices;
internal static class PowerGuard
{
[Flags]
private enum EXECUTION_STATE : uint
{
ES_CONTINUOUS = 0x80000000,
ES_SYSTEM_REQUIRED = 0x00000001,
ES_DISPLAY_REQUIRED = 0x00000002,
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags);
/// <summary>Call when a measurement starts: suppresses automatic sleep (call from the same thread as End)</summary>
public static void Begin() =>
SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS |
EXECUTION_STATE.ES_SYSTEM_REQUIRED);
/// <summary>Always call when a measurement ends: clears the suppression (call from the same thread as Begin)</summary>
public static void End() =>
SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS);
}
Here are the specifics you need to keep in mind.
- Add
ES_CONTINUOUSand the effect persists until you call it again withES_CONTINUOUSset. Without it, the call only resets the idle timer once, so if you want it to persist you need to keep calling it periodically.10 ES_SYSTEM_REQUIREDsuppresses system sleep, andES_DISPLAY_REQUIREDsuppresses the display turning off. For background measurement, the former is enough on its own. SettingES_DISPLAY_REQUIREDtoo, when it’s fine for the screen to go dark, is just wasted power.10- It cannot prevent sleep caused by the user pressing the power button or closing the lid. This function only affects automatic sleep triggered by an idle timeout. The official documentation itself states plainly that a user’s explicit action should be respected.10
- The system keeps a count of threads that have called
SetThreadExecutionState, and it goes to sleep once that count hits zero and there’s no user input.11 If the process dies abnormally, the suppression disappears with it, so you don’t need to worry about ending up with “a PC that never sleeps because someone forgot to clear the suppression” — worst case, a reboot resolves it — but by the same token, this offers no crash resilience of its own: if the process crashes, the sleep suppression it was providing simply vanishes right along with it, with nothing left protecting a still-running measurement. - As its name suggests, what this function sets is the execution state of the calling thread.10 Set and clear it from the same thread. Because an
async/awaitcontinuation can run on a different thread-pool thread, an implementation that callsBegin()andEnd()across anawaitcan end up with the clear call landing on a different thread than the one that set the suppression, missing it entirely, and leaving the suppression in place for as long as the original thread stays alive. It’s safest to pin these calls to a thread whose identity is guaranteed, such as the UI thread; if your design needs to cross threads, use the handle-based power-request API covered next instead.
Since Windows 7, there’s also a newer API for the same purpose: power requests (PowerCreateRequest / PowerSetRequest / PowerClearRequest). Its practical advantage is that you can pass a reason string via REASON_CONTEXT when you create the request; besides PowerRequestSystemRequired, the request types include PowerRequestExecutionRequired, which suppresses process suspension on a Modern Standby machine.1413 The official best practice is “Set right before the scenario begins, Clear as soon as it ends, and clean up the handle before the process exits.”13
Modern Standby machines do have an important restriction, though: on a battery-powered Modern Standby system, SystemRequired/ExecutionRequired requests get cut off 5 minutes after the sleep timeout would otherwise have been reached. And regardless of power source, a request ends the moment sleep is triggered by user action — the power button, closing the lid, or Start menu Sleep.13 In other words, “keep running on a laptop even with the lid closed, even on battery” is simply not something an app can achieve on its own effort. That kind of requirement has to be guaranteed through power settings and operational policy instead — settings that prevent sleep on lid close, or staying on AC power.
You can confirm whether suppression is actually taking effect from an elevated command prompt.
> powercfg /requests
SYSTEM:
[PROCESS] \Device\HarddiskVolume3\Apps\SensorLogger.exe
powercfg /requests is a command that lists the power requests currently blocking sleep or display-off, and it’s useful both for investigating “a PC that mysteriously never sleeps” and for confirming your own app’s suppression.12 Be aware, conversely, that an administrator can use powercfg /requestsoverride to configure a specific process’s requests to be ignored.12 The underlying design assumption is that the suppression API is a “request,” not an absolute guarantee.
One last word about good behavior. An implementation that leaves ES_CONTINUOUS | ES_SYSTEM_REQUIRED set for the entire time the app is running means a resident app permanently overrides the power plan the user set up. On a laptop that drains the battery, and on a shared PC it affects other uses of the machine too. The principle is to scope suppression to only “the window where the operation that can’t tolerate sleep is actually running” (the official documentation’s own example is “set it when recording starts, clear it when recording finishes”10). Incidentally, there’s also PowerToys Awake as a temporary workaround a user can apply themselves, and it too runs internally on the same mechanism — a thread requesting an execution state.24 It’s also a useful reference point for deciding where to draw the line between implementing suppression in the app versus leaving it to an operational tool.
5. Designing Around Sleep — Detection, Reconnection, and Recording Gaps
For most resident monitoring or collection apps, coexisting with sleep turns out to be the sounder design, rather than forbidding it outright. What you need is three things: knowing before it happens, knowing when it resumes, and rebuilding state after resume.
In .NET, Microsoft.Win32.SystemEvents.PowerModeChanged is the entry point.15
using Microsoft.Win32;
SystemEvents.PowerModeChanged += OnPowerModeChanged;
private void OnPowerModeChanged(object sender, PowerModeChangedEventArgs e)
{
switch (e.Mode)
{
case PowerModes.Suspend:
// The grace period is short: limit this to flushing buffers and recording the time measurement stopped
_logger.Info("suspend at {0:O}", DateTimeOffset.Now);
_collector.Pause();
break;
case PowerModes.Resume:
// 1) Record the gap itself as data
_logger.Info("resume at {0:O}", DateTimeOffset.Now);
// 2) Assume the connection is dead, discard it, and reconnect
_connection.Reset();
// 3) Recompute the schedule against the wall clock and re-arm timers
_scheduler.Rebase(DateTimeOffset.Now);
// 4) Explicitly resume collection only once rebuilding is done (don't leave it paused)
_collector.Resume();
break;
}
}
The official documentation flags two caveats about this event: it doesn’t fire unless a message pump is running (a Windows service needs something like a hidden form for this), and because it’s a static event, failing to unsubscribe leaks it.15 A GUI app can just use it directly, but for a console or service-style collection app, you either have to set up your own message window to receive Win32’s WM_POWERBROADCAST, or use PowerRegisterSuspendResumeNotification, which can receive a callback with no HWND at all (this is also the path notifications take in a DAM environment).416
Let’s also cover what the Win32-level events actually mean.
- PBT_APMSUSPEND: notification right before sleep. There’s only about 2 seconds of grace per app, and exceeding it can get you cut off by the system. Limit what you do here to flushing and recording the time — never write anything time-consuming, like cleanup over the network.1718
- PBT_APMRESUMEAUTOMATIC: a notification guaranteed to arrive on every single resume. Put your reconnection and rescheduling logic here.25
- PBT_APMRESUMESUSPEND: arrives after PBT_APMRESUMEAUTOMATIC when resume happens through user action (or once the user returns). It doesn’t arrive for an automatic resume such as a remote wake, so if you put your “resume handling” here and only here, an unattended resume never triggers the rebuild.26
- On top of that, for a critical sleep — say, an imminent battery cutoff — there’s no advance notification at all.18 Write your resume-side handling to be idempotent, so it still works correctly even in a case where you never received a suspend event in the first place.
There’s one more thing just as important as handling resume itself: recording a gap explicitly as a gap. Collection data that spans a sleep isn’t “a missing value” — it’s “not measured, because the system was stopped” — and if you keep that interval, along with the suspend/resume timestamps, in both the log and the data itself, whoever looks at the blank spot in the graph later won’t mistake it for an actual failure. What a long-running app’s logs should capture is also covered in Investigating an Industrial Camera Long-Running Crash — the Handle Leak Case.
Incidentally, there’s another “you didn’t notice it, but it got slow” problem in the same family as sleep: throttling from Windows 11’s Efficiency Mode (EcoQoS). See What Is Windows Efficiency Mode — the Green Leaf Icon and How to Turn It Off for that one.
6. Making Sure It Runs at a Fixed Time — Task Scheduler’s Wake-and-Run
Implementing a time-driven job — something like “aggregate and transfer at 2 AM” — with a resident app’s timer plus sleep suppression is a poor approach, because it means killing sleep for the whole night. For this use case, Task Scheduler’s “Wake the computer to run this task” (WakeToRun) is the real answer.
A task with WakeToRun enabled wakes the computer from sleep or hibernation at its scheduled time, and keeps the system awake until the task finishes (if it was already awake, it likewise requests that the system stay awake until completion). The screen may stay off when it wakes — that’s normal.19
There are conditions for this wake-up to actually happen, though. As official troubleshooting guidance points out, this depends on “Allow wake timers” being enabled in Power Options and the wake setting on the BIOS side also being enabled, and it’s not at all unusual for newer laptops, by power-saving design, to be configured not to allow a wake at all.20 powercfg /waketimers can list the wake timers currently active,12 so always verify on the actual deployed hardware that it genuinely wakes up. If you want to wake the machine from your own app, there’s also the option of a wakeable timer — passing TRUE for fResume on SetWaitableTimer. In that case, after an automatic wake, the system stays awake only for the duration of the unattended idle timer (a minimum of 2 minutes), and goes back to sleep promptly unless the app declares itself “in use” via SetThreadExecutionState. If the work after waking takes a while, the correct combination is to pair it with the suppression from Chapter 4.27
Operational design questions like the account a task runs under, the “ends with 0x1” problem, and preventing multiple simultaneous runs are covered in Task Scheduler Tasks That Don’t Run, or End With 0x1 — Diagnosis and a Safe Operational Design.
7. A Decision Table — Suppress, Design Around Resume, or Wake
The three designs aren’t mutually exclusive — you pick a primary and supporting approach for each type of app and combine them.
| App type | First choice | Combination / notes |
|---|---|---|
| Measurement / data collection (continuous measurement over hours to days) | Suppress with ES_SYSTEM_REQUIRED, scoped to the measurement window |
Always also implement resume handling (user-triggered sleep can’t be prevented10). On battery-powered Modern Standby machines, the 5-minute cutoff applies, so make AC power a requirement13 |
| Nightly batch job / scheduled transfer | Task Scheduler + WakeToRun19 | More power-efficient and reliable than a resident process plus suppression. Requires confirming wake-timer permission20 |
| Resident monitoring / notification agent | Design around sleep (detect via PowerModeChanged → reconnect, reschedule, record gaps)15 | For a dedicated machine whose sole purpose is monitoring, disable sleep at the power-plan level rather than in the app |
| Desktop display tool (presentations, dashboard display, etc.) | Use ES_DISPLAY_REQUIRED together with ES_SYSTEM_REQUIRED, scoped to while it’s displaying10 |
Always clear it when display ends. For an always-on display device, handle it via power settings instead |
| 24/7 equipment control / line PC | Disable sleep entirely via power settings (guaranteed operationally) | Don’t rely on the app’s suppression API as a safety net. Use powercfg /requests for periodic checks12 |
There are two axes to the decision: “is there a window of time it’s fine for it to be stopped?” (if yes, Task Scheduler; if no, power settings), and “whose PC does it run on?” (the more an app runs on a user’s personal or shared laptop, the more you should lean toward designing around resume rather than suppression).
8. Summary
- Sleep comes in S3, hibernation (S4), and Modern Standby (S0 low power idle), and you can tell them apart with
powercfg /a. Even on a Modern Standby machine, desktop apps get suspended by DAM, so the assumption that “it keeps running during sleep” doesn’t hold. - Neither threads nor timers advance during sleep. Since Windows 8, relative timers and waits don’t count sleep time and carry the remainder over, and .NET’s timers behave the same way starting with .NET 11 (through .NET 10 they can fire immediately upon resume). Elapsed-time APIs are a mix of ones that do and don’t count sleep time. Track elapsed time with Stopwatch and wall-clock time with DateTime, and re-anchor your baseline on resume.
- A TCP connection dies silently at an intermediate device’s idle timeout. The standard practice is to discard and reconnect on the resume event.
- Suppress with
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED), scoped to only the window you need. It can’t prevent user-triggered sleep, and on a battery-powered Modern Standby machine it gets cut off after 5 minutes. Confirm it withpowercfg /requests. - Handle resume with
SystemEvents.PowerModeChanged/WM_POWERBROADCAST. Since the suspend notification’s grace period is only about 2 seconds, and sleep can happen with no notification at all, write the resume side idempotently and record the gap interval. - For running at a fixed time, Task Scheduler’s WakeToRun is the real answer. Design it to include both the wake-timer power setting and verifying the wake-up on the actual hardware.
Related Articles
- Task Scheduler Tasks That Don’t Run, or End With 0x1 — Diagnosis and a Safe Operational Design
- Why You Should Prefer Event Waits Over Sleep(1) on Windows
- Diagnosing Why TCP Retransmission Stalls Industrial Camera Communication
- Investigating an Industrial Camera Long-Running Crash — the Handle Leak Case
- What Is Windows Efficiency Mode — the Green Leaf Icon and How to Turn It Off
Related Consulting Areas
Komura Software LLC handles the design and implementation of long-running Windows apps for measurement, monitoring, and data collection, along with investigating long-running-specific failures like “it stopped overnight” or “the records have gaps.” Feel free to consult us on diagnosing hard-to-reproduce symptoms tied to sleep and power management as well.
- Windows App Development
- Bug Investigation & Root Cause Analysis
- Technical Consulting & Design Review
- Contact
References
-
Microsoft Learn, System Sleeping States. On S1 through S4 being sleep states that run no computation, on S3 retaining only memory while S4 saves to a hibernation file, and on powercfg /a being able to list the sleep states available on a system. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, System power states. On S0 low power idle (Modern Standby) keeping the system partially running at low power, on Modern Standby-capable SoC systems not using S1 through S3, and on no notification being given for a critical transition. ↩ ↩2 ↩3
-
Microsoft Learn, Overview of Modern Standby Testing and Diagnostics. On powercfg /a being able to identify Modern Standby support (via a Standby (S0 Low Power Idle) entry in its output). ↩ ↩2
-
Microsoft Learn, Desktop Activity Moderator. On DAM clamping down desktop app execution to be equivalent to S3, on processes in an interactive session having every thread suspended while Session 0 is throttled, on the WM_POWERBROADCAST notification before suspend, on the inconsistency between timer behavior/running time and the wall clock, and on the need to account for connection lifetimes. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8
-
Microsoft Learn, SetWaitableTimerEx function. On a relative-time timer including time spent in a low-power state on Windows 7 and earlier (the countdown kept advancing even during sleep), versus not including it starting with Windows 8 (the countdown doesn’t advance during sleep). ↩ ↩2
-
Microsoft Learn, Environment.TickCount made consistent with Windows timeout behavior. On the breaking change where Environment.TickCount/TickCount64 is GetTickCount64-based (including sleep time) through .NET 10 and switches to QueryUnbiasedInterruptTime-based (not including it) starting with .NET 11, on wait APIs that take a timeout (SleepEx/WaitForMultipleObjectsEx) having already stopped counting non-running time since Windows 8, and on this change potentially causing code that no longer fires a timer immediately after resume. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Windows Time. On the elapsed time from GetTickCount/GetTickCount64 including time spent in sleep and hibernation, and on QueryUnbiasedInterruptTime including only working-state time. ↩ ↩2 ↩3
-
Microsoft Learn, Acquiring high-resolution time stamps. On managed code’s System.Diagnostics.Stopwatch using QPC as its time base, and on QueryPerformanceCounter returning a tick count that includes time spent in standby, hibernate, and connected standby. ↩ ↩2
-
Microsoft Learn, Load Balancer TCP Reset and Idle Timeout. On a load balancer’s default behavior being to silently drop a flow once it reaches the idle timeout (4 minutes by default), on sending a TCP reset being a feature that must be explicitly enabled, and on TCP keep-alive being used as a countermeasure. ↩ ↩2
-
Microsoft Learn, SetThreadExecutionState function. On the meaning of ES_CONTINUOUS/ES_SYSTEM_REQUIRED/ES_DISPLAY_REQUIRED, on a call without ES_CONTINUOUS only resetting the idle timer once, on user-triggered sleep being unpreventable, and on the usage example of setting it only for the duration of the required operation and clearing it afterward. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8
-
Microsoft Learn, System Sleep Criteria. On the system counting apps/threads that have called SetThreadExecutionState, and entering sleep once that count is zero and there is no user input. ↩ ↩2
-
Microsoft Learn, Powercfg command-line options. On /requests listing the power requests blocking sleep or display-off, on /requestsoverride being able to make a specific process’s requests ignored, and on /waketimers being able to list the currently active wake timers. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, PowerSetRequest function. On request types such as PowerRequestSystemRequired/PowerRequestExecutionRequired, on a request being cut off 5 minutes after exceeding the sleep timeout when a Modern Standby system is on DC power, on a request ending when sleep is triggered by user action, and on the best practice of attaching a reason string and calling Set right before and Clear right after. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, PowerCreateRequest function. On creating a power-request object with a specified REASON_CONTEXT, and releasing it with CloseHandle once it’s no longer needed. ↩ ↩2
-
Microsoft Learn, SystemEvents.PowerModeChanged Event. On this being an event that fires on suspend/resume, on it not firing unless a message pump is running (a service needs something like a hidden form), and on it leaking if not unsubscribed, since it’s a static event. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, WM_POWERBROADCAST message. On power-management events being delivered to a window as a WM_POWERBROADCAST message (PBT_APMSUSPEND / PBT_APMRESUMEAUTOMATIC / PBT_APMRESUMESUSPEND, and so on). ↩ ↩2
-
Microsoft Learn, PBT_APMSUSPEND event. On this being sent right before suspend, and on the grace period for handling it being about 2 seconds, with the system able to interrupt handling that exceeds it. ↩ ↩2
-
Microsoft Learn, System Power Management Events. On no advance notification being given for an emergency suspend such as one triggered by critical battery, and on handling of the suspend notification timing out at a maximum of 2 seconds per app. ↩ ↩2 ↩3
-
Microsoft Learn, ITaskSettings::get_WakeToRun method. On WakeToRun waking the computer to run a task and keeping it awake until the task completes, and on the screen sometimes staying off when it wakes. ↩ ↩2 ↩3
-
Microsoft Learn, Automatic maintenance. On the checklist for a scheduled wake not working listing the BIOS wake setting, the “Allow Wake Timer” power option, and a task’s WakeToRun setting, and on it being common for modern laptops to be configured not to allow an S3 wake. ↩ ↩2 ↩3
-
Microsoft Learn, What is Modern Standby. On Modern Standby achieving instant on/off while maintaining the network connection under the S0 low power idle model, and on resume (from the power button to the screen lighting up) taking under one second. ↩
-
Microsoft Learn, QueryUnbiasedInterruptTime function. On unbiased interrupt time counting only working-state time and not including time spent in sleep or hibernation. ↩
-
Microsoft Learn, Modern standby network connectivity. On Adaptive Connected Standby quiescing network activity during sleep while running on battery, unless there is a scenario that requires it. ↩
-
Microsoft Learn, PowerToys Awake utility. On this being a utility that keeps the PC awake without changing the power plan, working by spinning up a background thread that requests the machine state, and reverting to normal power-plan behavior once it exits. ↩
-
Microsoft Learn, PBT_APMRESUMEAUTOMATIC event. On this being an event delivered on every single resume, not itself indicating the user’s presence, and on PBT_APMRESUMESUSPEND being delivered afterward once user activity is detected. ↩
-
Microsoft Learn, PBT_APMRESUMESUSPEND event. On this being delivered after PBT_APMRESUMEAUTOMATIC on a resume that is user-triggered or where the user is detected, and on only PBT_APMRESUMEAUTOMATIC being delivered for a remote wake. ↩
-
Microsoft Learn, System Wake-up Events. On setting fResume to TRUE on SetWaitableTimer allowing the timer to wake the system, and on an automatic wake setting a minimum 2-minute unattended idle timer, after which the system goes back to sleep unless SetThreadExecutionState indicates it is in use. ↩
Related Articles
Recent articles sharing the same tags. Deepen your understanding with closely related topics.
MAX_PATH and Windows Path/Filename Pitfalls — the 260-Character Limit, Reserved Names, Trailing Dots, and Case Sensitivity
A rundown of the path and filename limits behind the classic 'file not found' bug. Covers the breakdown of MAX_PATH=260, enabling long pa...
Pitfalls of Network Drives and UNC Paths — Working With File Servers (Shared Folders) From a Business Application
This article organizes the classic problems that show up when a business application writes to or watches a shared folder: why a drive le...
System Tray Icons and Toast Notifications in Windows Apps — NotifyIcon Pitfalls and Choosing the Right AppNotification API
A practical guide to keeping a business Windows app resident in the system tray and notifying users with toast notifications. Covers the ...
When Your In-House Windows App Gets Flagged as a Virus — Handling Microsoft Defender False Positives and Living With the Performance Impact
We lay out the proper way to respond when Microsoft Defender flags your in-house Windows app as malware: how modern antivirus detection a...
Do Business Apps Run on Windows on Arm? — The Reality of x64 Emulation (Prism) and Native DLLs/COM
An answer, aimed at developers and IT staff, to 'will our business app run on Windows on Arm?' Covers how x64 emulation (Prism) works, th...
Related Topics
These topic pages place the article in a broader service and decision context.
Windows Technical Topics
Topic hub for KomuraSoft LLC's Windows development, investigation, and legacy-asset articles.
UI Threading & Timers
Topic page for WPF / WinForms UI threading, async flow, Dispatcher usage, and timer decisions.
Bug Investigation & Long-Run Failures
Topic page for intermittent failures, communication diagnosis, long-run crashes, and failure-path test foundations.
Where This Topic Connects
This article connects naturally to the following service pages.
Windows App Development
We support Windows desktop applications that involve resident processing, device integration, operational logging, and maintainable structure.
Bug Investigation & Root Cause Analysis
We investigate difficult production issues such as intermittent failures, long-run crashes, leaks, and communication stoppages.
Frequently Asked Questions
Common questions about the topic of this article.
- How do you keep the PC from sleeping only while your app is running?
- The basic approach is to call SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED) when the operation starts, and clear it with SetThreadExecutionState(ES_CONTINUOUS) when it ends. Add ES_DISPLAY_REQUIRED only if you also need to keep the screen on. This only affects automatic sleep caused by an idle timeout — it cannot prevent sleep that the user explicitly triggers, such as pressing the power button or closing the lid. You can confirm whether the suppression is actually taking effect with powercfg /requests.
- What is Modern Standby, and how is it different from traditional sleep?
- It's a newer sleep model, also called S0 low power idle, where the system stays partially active at low power and can resume instantly. A Modern Standby-capable machine doesn't support the traditional S3 sleep state. However, only activity the OS has explicitly permitted keeps 'running' — desktop apps have every one of their threads suspended by the Desktop Activity Moderator (DAM), so from the app's point of view it stops just as it would under S3. You can check which mode your PC uses with powercfg /a.
- Why does a TCP connection stop working after resuming from sleep?
- Because the app can't communicate while asleep, the connection goes idle, and intermediate devices along the path — NAT, firewalls, load balancers — drop the flow on an idle timeout. Many devices drop it silently, without notifying either side, so the app's socket looks perfectly fine right up until the first send or receive after resume, which either errors out immediately or hangs until it times out waiting for a response. The standard practice is to treat the connection as suspect and rebuild it as soon as you receive a resume event.
- For running a reliable nightly batch job, is sleep suppression or Task Scheduler the better choice?
- Task Scheduler's 'Wake the computer to run this task' (WakeToRun) is the first choice. It wakes the PC at the scheduled time and keeps it awake until the task finishes, so you never have to kill sleep for the whole night. That said, it won't wake if wake timers are disabled in Power Options, so you must confirm 'Allow wake timers' is enabled and verify on the actual hardware that it really wakes up. Suppressing sleep continuously wastes power the whole time and amounts to a poorly-behaved design that overrides the user's own power settings.
Author Profile
Profile page for the article author.
Go Komura
Representative of KomuraSoft LLC
Focused on Windows software development, technical consulting, and investigations into failures that are difficult to reproduce.
Public links