// Copyright (C) 2025, The Duplicati Team // https://duplicati.com, hello@duplicati.com // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. #nullable enable using System; using System.Runtime.InteropServices; using System.Runtime.Versioning; using Duplicati.Library.Interface; namespace Duplicati.Library.WindowsModules; /// /// Provides power management functionality for Windows using the powrprof callback API (Windows 8+). /// [SupportedOSPlatform("windows")] public sealed class PowerManagementModule : IPowerModeProvider, IDisposable { /// /// Registration handle returned from PowerRegisterSuspendResumeNotification. /// private IntPtr _registrationHandle = IntPtr.Zero; /// /// Keep a reference to the delegate to prevent it from being garbage collected. /// private DEVICE_NOTIFY_CALLBACK_ROUTINE? _callbackRef; /// public Action? OnResume { get; set; } /// public Action? OnSuspend { get; set; } /// /// Initializes a new instance. Required for reflection-based loading. /// public PowerManagementModule() : this(null) { } /// /// Initializes a new instance. The parameter is ignored in this implementation. /// /// Unused. Present for compatibility with previous constructor. public PowerManagementModule(Guid? _) { RegisterSuspendResumeCallback(); } /// /// Registers the suspend/resume callback using powrprof (Windows 8+). /// private void RegisterSuspendResumeCallback() { _callbackRef = new DEVICE_NOTIFY_CALLBACK_ROUTINE(SuspendResumeCallback); var parameters = new DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS { Callback = _callbackRef, Context = IntPtr.Zero }; // DEVICE_NOTIFY_CALLBACK delivers notifications via the provided delegate. uint status = PowerRegisterSuspendResumeNotification(DEVICE_NOTIFY_CALLBACK, ref parameters, out _registrationHandle); // If registration fails, we keep a no-op provider (no window fallback by design). // STATUS_SUCCESS is 0. if (status != STATUS_SUCCESS) { _registrationHandle = IntPtr.Zero; } } /// /// Constant indicating successful operation. /// private const uint STATUS_SUCCESS = 0; /// /// Callback invoked by the system for suspend/resume notifications. /// /// User-provided context (unused). /// Power event type (e.g., PBT_APMSUSPEND, PBT_APMRESUMEAUTOMATIC). /// Additional info (unused). /// STATUS_SUCCESS (0) on success. private uint SuspendResumeCallback(IntPtr context, uint type, IntPtr setting) { switch (type) { case PBT_APMSUSPEND: OnSuspend?.Invoke(); break; case PBT_APMRESUMEAUTOMATIC: case PBT_APMRESUMESUSPEND: OnResume?.Invoke(); break; } return STATUS_SUCCESS; } /// public void Dispose() { if (_registrationHandle != IntPtr.Zero) { PowerUnregisterSuspendResumeNotification(_registrationHandle); _registrationHandle = IntPtr.Zero; } _callbackRef = null; } /// /// Power broadcast event for system suspend. /// private const uint PBT_APMSUSPEND = 0x0004; /// /// Power broadcast event for automatic resume from suspend. /// private const uint PBT_APMRESUMEAUTOMATIC = 0x0012; /// /// Power broadcast event for resume from suspend. /// private const uint PBT_APMRESUMESUSPEND = 0x0007; /// /// Flag indicating that the recipient is a callback routine. /// private const uint DEVICE_NOTIFY_CALLBACK = 2; /// /// Structure used to subscribe to suspend/resume notifications via callback. /// [StructLayout(LayoutKind.Sequential)] private struct DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS { /// /// The callback routine to receive notifications. /// public DEVICE_NOTIFY_CALLBACK_ROUTINE Callback; /// /// User-defined context passed to the callback. /// public IntPtr Context; } /// /// Callback routine signature for device/power notifications. /// Return STATUS_SUCCESS (0) on success. /// [UnmanagedFunctionPointer(CallingConvention.Winapi)] private delegate uint DEVICE_NOTIFY_CALLBACK_ROUTINE(IntPtr Context, uint Type, IntPtr Setting); /// /// Registers to receive power suspend/resume notifications via a callback. /// /// Must be DEVICE_NOTIFY_CALLBACK for callback delivery. /// Callback and context parameters. /// Out registration handle. /// STATUS_SUCCESS (0) on success. [DllImport("powrprof.dll", SetLastError = true)] private static extern uint PowerRegisterSuspendResumeNotification( uint Flags, ref DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS Parameters, out IntPtr Handle); /// /// Unregisters a previous suspend/resume notification registration. /// /// The registration handle. /// STATUS_SUCCESS (0) on success. [DllImport("powrprof.dll", SetLastError = true)] private static extern uint PowerUnregisterSuspendResumeNotification(IntPtr Handle); }