Loupe

#Windows : s’assurer que mon PC ne passe pas en veille lorsque mon application effectue un traitement #UWP

Sur du développement Windows classique ‘'(WPF) ou plus moderne (WIndows Store, UWP), il  peut être intéressant de s’assurer que le PC ne passe pas en veille pendant que votre application est en train de faire un traitement. Il existe des possibilités pour ces deux scénarios.

 

Application moderne (Windows Store, UWP)

Dans une application du Store, si votre application n’est pas au premier plan, si votre PC se verrouille, etc, tout traitement est stoppé net par Windows qui gère le cycle de vie de l’application.

Afin de pallier à cette problématique, il est possible d’utiliser la classe DisplayRequest et demander au PC de ne pas éteindre l’écran en appellant la méthode RequestActive.

Une fois votre traitement terminé, il faut rendre la main en appelant la méthode RequestRelease. Dans l’exemple ci-dessous je gère aussi les différents cas où l’application passe en mode suspendu (comme indiqué dans la documentation).

 

DisplayRequest _displayRequest;

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo(e);

    // On s'abonne
    Application.Current.Suspending += OnApplicationSuspending;
    Application.Current.Resuming += OnApplicationResuming;

    _displayRequest = new DisplayRequest();
    _displayRequest.RequestActive();
}

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    // on se désabonne
    Application.Current.Suspending -= OnApplicationSuspending;
    Application.Current.Resuming -= OnApplicationResuming;

    ReleaseScreen();
}

void OnApplicationSuspending(object s, SuspendingEventArgs e)
{
    ReleaseScreen();
}

void ReleaseScreen()
{
    try { _displayRequest?.RequestRelease(); }
    catch (Exception)
    {
        // Can be possible, but ignore it
    }
}

void OnApplicationResuming(object s, object o)
{
    _displayRequest = new DisplayRequest();
    _displayRequest.RequestActive();
}

 

En exécutant la commande “powercfg /requests” dans une ligne de commande il est alors possible de voir que l’application demande à l’OS de rester éveillé :

Sans titre

 

Application traditionnelle (WPF, WinForms, etc.)

Dans une application traditionnelle, nous avons accès aux APIs Windows et il est possible de les appeler directement.

Pour cela, il faudra définir ces méthodes :

  • PowerCreateRequest : permet de créer une requête d’accès au contrôle de l’alimentation
  • PowerSetRequest : permet d’appliquer une requête,
  • PowerClearRequest : permet de supprimer une requête,
  • CloseHandle : méthode classique de libération d’une ressource

 

Il est aussi possible d’indiquer une raison pour laquelle il faut que l’écran/le PC reste allumé en utilisant la propriété SimpleReasonString de la requête.

 

Voici le bout de code que j’utilise (adaptation de celui-ci) :

 public class PowerManagementService
    {
        #region prevent screensaver, display dimming and automatically sleeping
        static POWER_REQUEST_CONTEXT _PowerRequestContext;
        static IntPtr _PowerRequest = IntPtr.Zero; //HANDLE

        // Availability Request Functions
        [DllImport("kernel32.dll")]
        static extern IntPtr PowerCreateRequest(ref POWER_REQUEST_CONTEXT Context);

        [DllImport("kernel32.dll")]
        static extern bool PowerSetRequest(IntPtr PowerRequestHandle,
            PowerRequestType RequestType);

        [DllImport("kernel32.dll")]
        static extern bool PowerClearRequest(IntPtr PowerRequestHandle,
            PowerRequestType RequestType);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true,
            ExactSpelling = true)]
        internal static extern int CloseHandle(IntPtr hObject);

        // Availablity Request Enumerations and Constants
        enum PowerRequestType
        {
            PowerRequestDisplayRequired = 0,
            PowerRequestSystemRequired,
            PowerRequestAwayModeRequired,
            PowerRequestMaximum
        }

        const int POWER_REQUEST_CONTEXT_VERSION = 0;
        const int POWER_REQUEST_CONTEXT_SIMPLE_STRING = 0x1;
        const int POWER_REQUEST_CONTEXT_DETAILED_STRING = 0x2;

        // Availablity Request Structures
        // Note:  Windows defines the POWER_REQUEST_CONTEXT structure with an
        // internal union of SimpleReasonString and Detailed information.
        // To avoid runtime interop issues, this version of 
        // POWER_REQUEST_CONTEXT only supports SimpleReasonString.  
        // To use the detailed information,
        // define the PowerCreateRequest function with the first 
        // parameter of type POWER_REQUEST_CONTEXT_DETAILED.
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct POWER_REQUEST_CONTEXT
        {
            public UInt32 Version;
            public UInt32 Flags;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string
                SimpleReasonString;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct PowerRequestContextDetailedInformation
        {
            public IntPtr LocalizedReasonModule;
            public UInt32 LocalizedReasonId;
            public UInt32 ReasonStringCount;
            [MarshalAs(UnmanagedType.LPWStr)]
            public string[] ReasonStrings;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        public struct POWER_REQUEST_CONTEXT_DETAILED
        {
            public UInt32 Version;
            public UInt32 Flags;
            public PowerRequestContextDetailedInformation DetailedInformation;
        }
        #endregion

        public static void EnableConstantPower(string reason,bool alsoEnableConstantDisplay = false)
        {
            try
            {
                if (_PowerRequest != null)
                {
                    GoBackToNormalMode();
                }

                // Set up the diagnostic string
                _PowerRequestContext.Version = POWER_REQUEST_CONTEXT_VERSION;
                _PowerRequestContext.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING;
                _PowerRequestContext.SimpleReasonString = reason ;

                // Create the request, get a handle
                _PowerRequest = PowerCreateRequest(ref _PowerRequestContext);

                // Set the request
                PowerSetRequest(_PowerRequest, PowerRequestType.PowerRequestSystemRequired);
                if (alsoEnableConstantDisplay)
                {
                    PowerSetRequest(_PowerRequest, PowerRequestType.PowerRequestDisplayRequired);
                }
            }
            catch (Exception)
            {
                // IGNORE
            }
        }

        public static void GoBackToNormalMode()
        {
            if (_PowerRequest != IntPtr.Zero)
            {
                try
                {
                    // Clear the request
                    PowerClearRequest(_PowerRequest,
                        PowerRequestType.PowerRequestSystemRequired);

                    PowerClearRequest(_PowerRequest,
                        PowerRequestType.PowerRequestDisplayRequired);

                    CloseHandle(_PowerRequest);
                    _PowerRequest = IntPtr.Zero;
                }
                catch (Exception)
                {
                    // IGNORE
                }
            }
        }
    }

Sans titre2

 

 

L’utilisation de la classe PowerManagementService est alors ultra-simple :

PowerManagementService.EnableConstantPower(reason: "j'ai poney");

 

À noter qu'une fois votre application éteinte, la demande est automatiquement supprimée par l'OS.

Bon code !

Photo de profil

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus