Windows Power Requests: Keep a Real Work Scenario Awake, Then Release It
Use Windows power requests with explicit reason strings and balanced clear operations, while respecting Modern Standby, user sleep, battery, and shutdown policy.
An application can ask Windows to keep the system or display awake while a real operation is in progress. That request is an exception to the user’s idle power policy, not a general promise that the application may run forever. A correct implementation creates a request for a named scenario, activates it immediately before the work, clears it as soon as the work ends, and releases its handles on every exit path. Forgotten requests waste energy, increase heat, and can prevent a laptop from entering low-power idle.
Choose the narrowest request type
PowerCreateRequest creates a request object with a REASON_CONTEXT describing why the application needs power. PowerSetRequest increments the count for a request type, while PowerClearRequest decrements it. Display-required keeps the display on; system-required asks the system to remain awake instead of sleeping after inactivity. To keep both the display and system awake, Microsoft documents that both request types are needed. PowerRequestExecutionRequired is application-only and has OS-version constraints; services cannot use that type.
Create the object when the component is initialized if useful, but do not activate a request merely because the application is open. Set it just before a bounded operation such as an active presentation, user-requested media playback, or a file transfer that must finish while the device is awake. Clear each type once the specific scenario finishes. Because calls increment and decrement counts, an unbalanced pair can leave the aggregate request active longer than intended.
HANDLE request = PowerCreateRequest(&reason);
if (request == INVALID_HANDLE_VALUE) {
const DWORD error = GetLastError();
// Fail the keep-awake feature visibly; do not assume it succeeded.
}
bool active = false;
if (PowerSetRequest(request, PowerRequestSystemRequired)) {
active = true;
}
// Run only the operation whose lifecycle owns this request.
if (active) {
PowerClearRequest(request, PowerRequestSystemRequired);
}
CloseHandle(request);
This is a control-flow sketch, not an RAII implementation. Real code should use a scope guard or an equivalent cleanup owner, check both set and clear results, and preserve the first relevant error. If setting the request fails, the operation can often continue with an explicit warning, but it must not claim that the system was guaranteed to remain awake.
User intent and modern sleep models still win
Power requests do not override an explicit user choice to sleep in every situation. Except for Away Mode on traditional S3 systems, requests are terminated when the user initiates system sleep using the power button, lid close, or Start menu. Modern Standby (S0 low-power idle) also has different behavior from legacy S3; on DC power, Windows terminates system and execution requests five minutes after the configured sleep timeout has expired. Design the work to resume safely after wake or to save progress before the user closes the lid.
Do not use a request as a way to keep a service or background agent perpetually awake. On Modern Standby devices, continuous ES_SYSTEM_REQUIRED or repeated short timers can drain the battery while the user believes the device is asleep. Prefer event-driven work, Task Scheduler triggers, push notifications, or a resumable job model for background activity. A request should match a visible or operationally necessary period that an administrator or user can understand.
For display scenarios, distinguish “screen must remain visible” from “system must remain processing.” A slideshow or video call may need the display; a transfer may need the system but not the display. Avoid requesting both automatically. The reason string should be localized and specific enough to appear meaningfully in diagnostics, rather than saying only “application active.”
Balance counts across all control paths
Power request types have reference counts. If two independent components use the same request object, one component clearing a type should not clear another component’s still-active request. Keep ownership per scenario or centralize it behind a reference-counted service that knows which work item activated which type. Make cancellation, errors, timeouts, user pause, and application shutdown converge on one release path.
The request handle itself also needs a defined lifetime. Close it only after all scenarios using it have cleared their active counts. If a process crashes, Windows removes the process’s requests, but relying on crash cleanup is not an acceptable normal release strategy. For a service that is stopping, clear its requests before the service exits so the user does not need a reboot or diagnostic tool to understand why sleep was blocked.
Avoid calling PowerSetRequest repeatedly in a polling loop. Each successful call increments a count; it is not an idempotent “ensure awake” setter. If a retry is needed after an error, know whether the prior call succeeded. Track per-type activation state explicitly and clear exactly the successful activations.
An application with several features should not let each feature independently hold an opaque process-wide request forever. Put the request behind a small manager that accepts a scenario token, reason, and requested type, and returns a disposable lease. The lease clears the same type exactly once when the scenario ends. Record the owner feature and a deadline for diagnostics, and make cancellation dispose the lease even if the underlying operation exits through an exception or early return. This pattern makes “why is the laptop awake?” answerable without guessing which code path forgot to clear a count.
Do not collapse all request types into one boolean. A video call may need both display and system activity; a headless file transfer may need only the system; a time-sensitive foreground task may use an execution request where supported. An unrelated component clearing a display request must not accidentally cancel another component’s system request. Keep per-type counts and owners, and ensure one failed clear is reported for remediation rather than discarded in a destructor with no telemetry.
Power requests also do not promise network availability, prevent a user from closing a laptop, or bypass application lifecycle management. They express a power requirement to the operating system under documented conditions. The actual result depends on platform state, policy, and user action. If an operation cannot safely be interrupted, make its data durable incrementally and design resume/retry semantics; a wake request alone is not a data-integrity strategy.
Diagnose with power policy evidence
Use powercfg /requests to inspect active system, display, away-mode, and execution requests. Compare the application’s reason text with its internal scenario state. If a laptop refuses to sleep, do not immediately change the power plan or disable a service: first determine whether the application has an active request, whether that request is legitimate, and whether its owner reached the clear path. If no request appears, inspect other blockers such as device or driver activity separately.
Capture request activation and clear events in application diagnostics with a scenario ID, request type, start time, end time, and clear result. Set a watchdog for unusually long operations, but do not silently clear a request if the underlying work still requires awake time; surface cancellation or completion policy to the operator. If the feature becomes stuck, provide a safe way to cancel it and release the request without losing the recovery state.
When a request appears under powercfg /requests, correlate the reason text to the exact component and active scenario. If it is not present, check whether the API call failed, was cleared, or was terminated by explicit sleep; then investigate separate drivers, devices, or scheduled wake sources. A power request and a wake timer answer different questions. Do not erase all system requests with a blanket override as a first-line fix, because that can hide a legitimate active recording, presentation, or transfer and create a separate failure.
For Modern Standby battery testing, use a repeatable idle interval, record AC/DC state, and compare expected work duration with the platform’s power policy. A request that remains active on AC may be subject to different behavior on battery. Test across the hardware models the product supports; the sleep architecture is a platform property, not a feature an application can infer from a single API return. Make telemetry identify the OS build and power mode so support can distinguish application bugs from changed platform policy.
Test the matrix that changes behavior
Test on AC and battery, with traditional sleep and Modern Standby where the target hardware supports them, and with the screen timeout shorter than the operation. Verify system-required and display-required independently, then test explicit user-initiated sleep, lid close, cancellation, application crash, and process shutdown. Confirm every successful PowerSetRequest has one matching clear and that the request disappears after the scenario ends. Test when request creation fails and ensure the user receives accurate status rather than a false guarantee.
Power requests should be rare, short, and explainable. Match the request type to the work, give it a reason, balance the reference count, clear it promptly, and make the work resumable when user power policy interrupts it.
Related:
Sources: