mirror of
https://github.com/duplicati/duplicati.git
synced 2025-11-28 11:30:24 +08:00
194 lines
No EOL
6.9 KiB
C#
194 lines
No EOL
6.9 KiB
C#
// 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;
|
|
|
|
/// <summary>
|
|
/// Provides power management functionality for Windows using the powrprof callback API (Windows 8+).
|
|
/// </summary>
|
|
[SupportedOSPlatform("windows")]
|
|
public sealed class PowerManagementModule : IPowerModeProvider, IDisposable
|
|
{
|
|
/// <summary>
|
|
/// Registration handle returned from PowerRegisterSuspendResumeNotification.
|
|
/// </summary>
|
|
private IntPtr _registrationHandle = IntPtr.Zero;
|
|
|
|
/// <summary>
|
|
/// Keep a reference to the delegate to prevent it from being garbage collected.
|
|
/// </summary>
|
|
private DEVICE_NOTIFY_CALLBACK_ROUTINE? _callbackRef;
|
|
|
|
/// <inheritdoc />
|
|
public Action? OnResume { get; set; }
|
|
|
|
/// <inheritdoc />
|
|
public Action? OnSuspend { get; set; }
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance. Required for reflection-based loading.
|
|
/// </summary>
|
|
public PowerManagementModule() : this(null)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance. The parameter is ignored in this implementation.
|
|
/// </summary>
|
|
/// <param name="_">Unused. Present for compatibility with previous constructor.</param>
|
|
public PowerManagementModule(Guid? _)
|
|
{
|
|
RegisterSuspendResumeCallback();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Registers the suspend/resume callback using powrprof (Windows 8+).
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constant indicating successful operation.
|
|
/// </summary>
|
|
private const uint STATUS_SUCCESS = 0;
|
|
|
|
/// <summary>
|
|
/// Callback invoked by the system for suspend/resume notifications.
|
|
/// </summary>
|
|
/// <param name="context">User-provided context (unused).</param>
|
|
/// <param name="type">Power event type (e.g., PBT_APMSUSPEND, PBT_APMRESUMEAUTOMATIC).</param>
|
|
/// <param name="setting">Additional info (unused).</param>
|
|
/// <returns>STATUS_SUCCESS (0) on success.</returns>
|
|
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;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void Dispose()
|
|
{
|
|
if (_registrationHandle != IntPtr.Zero)
|
|
{
|
|
PowerUnregisterSuspendResumeNotification(_registrationHandle);
|
|
_registrationHandle = IntPtr.Zero;
|
|
}
|
|
|
|
_callbackRef = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Power broadcast event for system suspend.
|
|
/// </summary>
|
|
private const uint PBT_APMSUSPEND = 0x0004;
|
|
/// <summary>
|
|
/// Power broadcast event for automatic resume from suspend.
|
|
/// </summary>
|
|
private const uint PBT_APMRESUMEAUTOMATIC = 0x0012;
|
|
/// <summary>
|
|
/// Power broadcast event for resume from suspend.
|
|
/// </summary>
|
|
private const uint PBT_APMRESUMESUSPEND = 0x0007;
|
|
|
|
/// <summary>
|
|
/// Flag indicating that the recipient is a callback routine.
|
|
/// </summary>
|
|
private const uint DEVICE_NOTIFY_CALLBACK = 2;
|
|
|
|
/// <summary>
|
|
/// Structure used to subscribe to suspend/resume notifications via callback.
|
|
/// </summary>
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS
|
|
{
|
|
/// <summary>
|
|
/// The callback routine to receive notifications.
|
|
/// </summary>
|
|
public DEVICE_NOTIFY_CALLBACK_ROUTINE Callback;
|
|
/// <summary>
|
|
/// User-defined context passed to the callback.
|
|
/// </summary>
|
|
public IntPtr Context;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Callback routine signature for device/power notifications.
|
|
/// Return STATUS_SUCCESS (0) on success.
|
|
/// </summary>
|
|
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
|
private delegate uint DEVICE_NOTIFY_CALLBACK_ROUTINE(IntPtr Context, uint Type, IntPtr Setting);
|
|
|
|
/// <summary>
|
|
/// Registers to receive power suspend/resume notifications via a callback.
|
|
/// </summary>
|
|
/// <param name="Flags">Must be DEVICE_NOTIFY_CALLBACK for callback delivery.</param>
|
|
/// <param name="Parameters">Callback and context parameters.</param>
|
|
/// <param name="Handle">Out registration handle.</param>
|
|
/// <returns>STATUS_SUCCESS (0) on success.</returns>
|
|
[DllImport("powrprof.dll", SetLastError = true)]
|
|
private static extern uint PowerRegisterSuspendResumeNotification(
|
|
uint Flags,
|
|
ref DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS Parameters,
|
|
out IntPtr Handle);
|
|
|
|
/// <summary>
|
|
/// Unregisters a previous suspend/resume notification registration.
|
|
/// </summary>
|
|
/// <param name="Handle">The registration handle.</param>
|
|
/// <returns>STATUS_SUCCESS (0) on success.</returns>
|
|
[DllImport("powrprof.dll", SetLastError = true)]
|
|
private static extern uint PowerUnregisterSuspendResumeNotification(IntPtr Handle);
|
|
} |