Veille, mise en veille prolongée, Modern Standby et applications longue durée — concevoir pour éviter « ça s'était arrêté pendant la nuit »
· Go Komura · Veille, Modern Standby, Gestion de l'alimentation, SetThreadExecutionState, Longue durée, Minuteur, Application résidente, C#, .NET, Investigation de bugs, Développement Windows, Conseil technique
« J’étais sûr que l’application de surveillance avait tourné toute la nuit, mais en la consultant le matin, le graphique s’était arrêté net à 1 h du matin. » « L’outil de collecte fonctionnait parfaitement depuis des mois sur un PC de bureau, mais dès qu’on est passé à un portable, des trous sont apparus dans les enregistrements. » — Parmi tous les symptômes qui reviennent dans les consultations sur les applications métier longue durée, c’est le grand classique. On regarde les journaux : aucune exception, aucun plantage — simplement plusieurs heures d’enregistrements totalement absentes. Bien plus souvent qu’on ne le pense, le coupable n’est pas un bug de l’application, mais la gestion de l’alimentation de Windows.
Ce qui complique les choses, c’est que le mot « veille » ne désigne pas une seule et même chose. La veille S3 traditionnelle, la mise en veille prolongée (S4) et Modern Standby (S0 low power idle) — désormais la norme sur les portables récents — se présentent chacune différemment du point de vue d’une application, aussi bien dans la façon dont elles l’arrêtent que dans les contre-mesures qui fonctionnent. Modern Standby en particulier est souvent mal compris à cause de sa présentation comme « le système continue de tourner même pendant la veille », mais en réalité, les applications de bureau sont arrêtées de façon encore plus systématique que sous les anciens modèles.
Cet article couvre le strict nécessaire sur les types de veille et le comportement d’une application pendant la veille, puis détaille comment choisir entre trois conceptions — empêcher la veille, concevoir en tenant compte de la veille, et réveiller la machine à une heure fixe — avec des exemples d’implémentation et un tableau de décision.
1. La conclusion d’abord
- Windows dispose de trois modèles de veille — la veille S3 traditionnelle, la mise en veille prolongée (S4) et Modern Standby (S0 low power idle) — et une machine compatible Modern Standby ne prend pas en charge S1 à S3. Vous pouvez déterminer lequel votre PC utilise avec
powercfg /a.123 - Les applications de bureau ne continuent pas non plus de tourner pendant Modern Standby. Le Desktop Activity Moderator (DAM) suspend les threads des processus de bureau (les services de la session 0 sont quant à eux limités/throttlés). Ne concevez pas en partant du principe que « c’est du S0, donc ça doit continuer de tourner ».4
- Aucun thread ne s’exécute pendant la veille, et la façon dont l’échéance d’un minuteur compte le temps diffère aussi selon la génération d’API. Depuis Windows 8, les minuteurs et attentes relatifs (la forme relative de
SetWaitableTimer,SleepEx, etc.) ne comptent pas le temps passé en veille et reportent la durée restante après la reprise.56 Les minuteurs .NET, jusqu’à .NET 10, sont implémentés pour compter le temps de veille (donc si l’échéance est dépassée pendant la veille, ils se déclenchent juste après la reprise), et à partir de .NET 11, ils passent à un mode qui ne le compte pas.6 Les API de temps écoulé sont elles aussi un mélange de celles qui comptent le temps de veille (GetTickCount, QueryPerformanceCounter — la base de Stopwatch) et de celles qui ne le comptent pas (QueryUnbiasedInterruptTime).78 - Une connexion TCP devenue inactive pendant la veille peut finir abandonnée silencieusement par le délai d’inactivité d’un équipement intermédiaire — un NAT, un pare-feu, un répartiteur de charge (par exemple, le comportement par défaut d’Azure Load Balancer est d’abandonner la connexion au bout de 4 minutes, sans notification). Concevez en partant du principe qu’une reconnexion sera nécessaire après la reprise.94
- La bonne façon d’empêcher la veille est
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED). Cela n’agit toutefois que sur la veille automatique déclenchée par un délai d’inactivité — cela ne peut pas empêcher une veille explicitement déclenchée par l’utilisateur via le bouton d’alimentation ou la fermeture du capot. Ne l’activez que pour la section réellement nécessaire, et levez-le systématiquement ensuite.1011 - Vous pouvez vérifier si la suppression est active avec
powercfg /requests. La famille d’APIPowerCreateRequestpermet d’attacher une chaîne de motif à la demande, qui apparaît dans cette liste — ce qui facilite grandement les investigations sur le terrain.121314 - Si vous concevez plutôt en tenant compte de la veille, détectez-la en .NET avec
SystemEvents.PowerModeChanged, ou en Win32 avecWM_POWERBROADCAST(PBT_APMSUSPEND / PBT_APMRESUMEAUTOMATIC). Le délai de grâce pour la notification de suspension n’est que d’environ 2 secondes par application, et en cas de batterie critique, le système peut se mettre en veille sans aucune notification.15161718 - Pour exécuter quelque chose de façon fiable à une heure fixe, l’option « Réveiller l’ordinateur pour exécuter cette tâche » (WakeToRun) du Planificateur de tâches est le premier choix. Elle maintient le système éveillé jusqu’à la fin de la tâche. Elle dépend cependant de l’autorisation des minuteries de réveil dans les options d’alimentation, ce qui rend indispensable la vérification sur le terrain que la machine se réveille réellement.1920
2. La veille Windows n’est pas une chose unique
Commençons par les trois états de base, nécessaires avant de réfléchir aux contre-mesures.
| État | Nom courant | Contenu | Du point de vue de l’application |
|---|---|---|---|
| S3 | Veille traditionnelle | CPU arrêté, seule la RAM reste alimentée pour conserver l’état | Aucun calcul ne s’exécute1 |
| S4 | Mise en veille prolongée | Contenu de la mémoire écrit dans un fichier de mise en veille prolongée, puis coupure de l’alimentation | Identique ci-dessus ; la reprise restaure depuis ce fichier1 |
| S0 low power idle | Modern Standby | Le système continue de fonctionner partiellement à basse consommation et reprend instantanément | Les applications de bureau sont arrêtées par le DAM24 |
S3 et S4 sont tous deux « un état où le système n’exécute aucun calcul et paraît, en toute apparence, éteint ».1 Modern Standby, à l’inverse, est un modèle plus proche de celui d’un smartphone : il reste en attente à basse consommation en maintenant la connexion réseau même écran éteint, et reprend en moins d’une seconde depuis le bouton d’alimentation. Les machines compatibles Modern Standby n’utilisent pas S1 à S3.221
Ce qui compte ici, c’est le Desktop Activity Moderator (DAM) présent sur les machines compatibles Modern Standby. Le DAM est le mécanisme qui réduit l’exécution des applications de bureau à un niveau équivalent à la veille S3 lorsque le système entre en veille prolongée légère : les processus d’une session interactive voient chacun de leurs threads suspendus, tandis que les services de la session 0 sont limités/throttlés (suspendus la majeure partie du temps, ne s’exécutant que par intermittence).4 Autrement dit, l’attente selon laquelle « les applications continuent de tourner pendant la veille parce que c’est du Modern Standby » est exactement l’inverse de la réalité — du point de vue de l’application, elle s’arrête tout comme sous S3 — et, contrairement à S3, parce que « le système lui-même tourne alors que seule l’application est arrêtée », il est plus sûr de comprendre cela comme quelque chose qui se manifeste par une dérive de l’horloge et un comportement de minuteur incohérent.4
Vous pouvez vérifier quel modèle utilise votre PC (ou celui d’un client sur site), sans droits administrateur, avec la commande suivante.3
> powercfg /a
Les états de veille suivants sont disponibles sur ce système :
Veille (S0 faible consommation) Connecté au réseau
Mise en veille prolongée
...
Si l’affichage indique Veille (S3), la machine est une machine S3 ; s’il indique S0 faible consommation, c’est une machine Modern Standby. Chaque fois qu’un client signale « des enregistrements ont commencé à manquer après le passage à un portable », c’est la première chose à vérifier.
3. Que devient l’application pendant la veille et après la reprise
3.1 Threads et minuteurs
Aucun thread ne s’exécute pendant la veille (S3/S4, et pendant une suspension par le DAM).14 Ce qu’il est facile de négliger, c’est la façon dont l’« échéance » d’un minuteur ou d’une attente compte le temps de veille, et cela diffère selon la couche d’API utilisée.
- Un minuteur relatif Win32 (la forme relative de
SetWaitableTimer/SetWaitableTimerEx) comptait le temps passé dans les états à basse consommation sous Windows 7 et versions antérieures (le compte à rebours continuait d’avancer même pendant la veille), mais ne le fait plus sous Windows 8 et versions ultérieures. Un minuteur relatif qui traverse une veille ne se déclenche qu’après avoir attendu le temps restant suivant la reprise.5 - De même, les API d’attente qui prennent un délai d’expiration (
SleepEx,WaitForMultipleObjectsEx, etc.) cessent de compter le temps hors fonctionnement tel que la veille, depuis Windows 8. Le temps restant est reporté au-delà de la veille.6 - Le moment où se déclenche un minuteur managé .NET (
System.Threading.Timeret assimilés) dépend de la version du runtime. En effet,Environment.TickCount64inclut le temps de veille jusqu’à .NET 10 (il se base sur GetTickCount64) et cesse de l’inclure à partir de .NET 11 (en passant à une base QueryUnbiasedInterruptTime) ; la documentation du changement elle-même avertit qu’il « peut exister du code qui ne déclenche plus le minuteur immédiatement après la reprise » en conséquence.6
Autrement dit, si « une application mesurant avec un System.Threading.Timer de 10 secondes se met en veille pendant 8 heures », elle ne déclenchera dans tous les cas jamais 8 heures de tics d’un coup — mais le fait qu’elle se déclenche immédiatement après la reprise, ou qu’elle attende d’abord son temps restant, dépend de la couche d’API et de la version du runtime. Dans tous les cas, les échantillons pendant la veille sont perdus. Plutôt que de construire votre logique de reprise sur l’hypothèse « ça se déclenche juste après la reprise », il est plus sûr de replanifier explicitement en utilisant l’événement de reprise couvert au chapitre 5.
3.2 La mesure du temps écoulé dérive
Les API de temps écoulé sont un mélange de celles qui incluent le temps de veille et de celles qui ne l’incluent pas.
| API | Temps de veille |
|---|---|
| GetTickCount / GetTickCount64 | Inclus7 |
| QueryPerformanceCounter (base de Stopwatch en .NET) | Inclus (veille, mise en veille prolongée, connected standby)8 |
| QueryUnbiasedInterruptTime | Non inclus (uniquement le temps en état de fonctionnement)227 |
| Environment.TickCount / TickCount64 | Inclus jusqu’à .NET 10 (basé sur GetTickCount64) ; changé pour ne plus être inclus à partir de .NET 11 (basé sur QueryUnbiasedInterruptTime)6 |
Un code qui dit « prendre l’échantillon suivant une fois que Stopwatch indique 10 secondes écoulées » finit par signifier « 8 heures et 10 secondes se sont écoulées » dès qu’il traverse une veille, et à l’inverse, les vérifications de temps écoulé basées sur TickCount changent de comportement en passant à .NET 11. Garder une répartition claire des rôles — utiliser Stopwatch pour « combien de temps une opération a pris », et DateTime/DateTimeOffset pour « l’heure de l’horloge murale à laquelle quelque chose doit se produire ensuite », en réancrant cette référence lors de l’événement de reprise (chapitre 5) — vous évite d’être déstabilisé aussi bien par la veille que par une mise à jour du runtime. La conception des minuteurs à courte période elle-même est traitée dans Pourquoi préférer l’attente d’événements à Sleep(1) sous Windows.
3.3 Une connexion TCP meurt « silencieusement »
Pendant la veille, comme l’application ne peut pas communiquer, la connexion devient inactive. Le problème vient des équipements sur le trajet. Les équipements intermédiaires comme les NAT, pare-feu et répartiteurs de charge abandonnent un flux inactif au bout d’un délai d’expiration, et dans de nombreuses configurations, ils l’abandonnent silencieusement, sans avertir aucune des deux extrémités. Le comportement par défaut d’Azure Load Balancer, par exemple, est d’« abandonner silencieusement un flux dès qu’il atteint le délai d’inactivité (4 minutes par défaut) ».9 Les routeurs internes de l’entreprise et les piles TCP embarquées des équipements sur le terrain ont des délais similaires.
En conséquence, le socket de l’application semble parfaitement normal juste après la reprise, puis soit le prochain envoi échoue immédiatement, soit il reste bloqué en attente d’une réponse jusqu’au délai d’expiration. La documentation du DAM note elle-même explicitement qu’il faut tenir compte de l’effet de la suspension de processus sur la durée de vie d’une connexion et sur une poignée de main (handshake) en cours.4 De plus, sur une machine Modern Standby fonctionnant sur batterie, l’activité réseau pendant la veille est, par défaut, entièrement mise en pause (Adaptive Connected Standby).23 La pratique standard consiste à considérer la connexion comme suspecte et à la détruire/reconnecter dès la réception d’un événement de reprise. Pour diagnostiquer une connexion qui « semble vivante mais est en réalité morte », voir aussi Diagnostiquer pourquoi la retransmission TCP bloque la communication d’une caméra industrielle.
4. Faire en sorte que ça ne se mette pas en veille — SetThreadExecutionState et les demandes d’alimentation
Lorsque vous ne pouvez pas vous permettre la veille pendant les quelques heures que dure une mesure, l’outil approprié est SetThreadExecutionState. Appelez-le depuis 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>À appeler au démarrage d'une mesure : supprime la veille automatique (à appeler depuis le même thread que End)</summary>
public static void Begin() =>
SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS |
EXECUTION_STATE.ES_SYSTEM_REQUIRED);
/// <summary>À toujours appeler à la fin d'une mesure : lève la suppression (à appeler depuis le même thread que Begin)</summary>
public static void End() =>
SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS);
}
Voici les points précis à garder à l’esprit.
- Ajoutez
ES_CONTINUOUSet l’effet persiste jusqu’à ce que vous rappeliez la fonction avecES_CONTINUOUSdéfini. Sans cela, l’appel ne fait que réinitialiser une seule fois le minuteur d’inactivité ; si vous voulez que cela persiste, il faut continuer à appeler périodiquement la fonction.10 ES_SYSTEM_REQUIREDsupprime la veille du système, etES_DISPLAY_REQUIREDsupprime l’extinction de l’écran. Pour une mesure en arrière-plan, le premier suffit à lui seul. Définir aussiES_DISPLAY_REQUIREDalors qu’il est acceptable que l’écran s’éteigne n’est qu’un gaspillage d’énergie.10- Cela ne peut pas empêcher la veille provoquée par l’utilisateur appuyant sur le bouton d’alimentation ou fermant le capot. Cette fonction n’agit que sur la veille automatique déclenchée par un délai d’inactivité. La documentation officielle elle-même indique clairement qu’une action explicite de l’utilisateur doit être respectée.10
- Le système tient un compte des threads ayant appelé
SetThreadExecutionState, et il entre en veille dès que ce compte atteint zéro et qu’il n’y a aucune entrée utilisateur.11 Si le processus se termine anormalement, la suppression disparaît avec lui, donc il n’y a pas lieu de s’inquiéter de se retrouver avec « un PC qui ne se met plus jamais en veille parce que quelqu’un a oublié de lever la suppression » — dans le pire des cas, un redémarrage résout le problème — mais, par la même logique, cela n’offre aucune résilience aux plantages en soi : si le processus plante, la suppression de veille qu’il fournissait disparaît purement et simplement avec lui, sans plus rien pour protéger une mesure encore en cours. - Comme son nom l’indique, ce que cette fonction définit, c’est l’état d’exécution du thread appelant.10 Définissez-la et levez-la depuis le même thread. Comme une continuation
async/awaitpeut s’exécuter sur un thread de pool différent, une implémentation qui appelleBegin()etEnd()de part et d’autre d’unawaitpeut se retrouver avec l’appel de levée aboutissant sur un thread différent de celui ayant posé la suppression, la manquant complètement, et laissant la suppression en place tant que le thread d’origine reste vivant. Il est plus sûr de rattacher ces appels à un thread dont l’identité est garantie, comme le thread d’interface utilisateur ; si votre conception doit traverser des threads, utilisez plutôt l’API de demande d’alimentation basée sur des handles évoquée ci-après.
Depuis Windows 7, il existe aussi une API plus récente pour le même objectif : les demandes d’alimentation (PowerCreateRequest / PowerSetRequest / PowerClearRequest). Son avantage pratique est qu’elle permet de passer une chaîne de motif via REASON_CONTEXT lors de la création de la demande ; outre PowerRequestSystemRequired, les types de demande incluent PowerRequestExecutionRequired, qui supprime la suspension de processus sur une machine Modern Standby.1413 La bonne pratique officielle est « Set juste avant que le scénario ne commence, Clear dès qu’il se termine, et nettoyer le handle avant que le processus ne se termine ».13
Les machines Modern Standby ont toutefois une restriction importante : sur un système Modern Standby alimenté par batterie, les demandes SystemRequired/ExecutionRequired sont coupées 5 minutes après le moment où le délai d’expiration de la veille aurait normalement été atteint. Et quelle que soit la source d’alimentation, une demande se termine dès qu’une veille est déclenchée par une action de l’utilisateur — le bouton d’alimentation, la fermeture du capot, ou l’option Veille du menu Démarrer.13 Autrement dit, « continuer de tourner sur un portable même capot fermé, même sur batterie » n’est tout simplement pas quelque chose qu’une application peut obtenir par ses propres efforts. Ce type d’exigence doit être garanti par les réglages d’alimentation et la politique d’exploitation à la place — des réglages empêchant la veille à la fermeture du capot, ou le maintien sur alimentation secteur.
Vous pouvez vérifier si la suppression est effectivement active depuis une invite de commande élevée.
> powercfg /requests
SYSTEM :
[PROCESS] \Device\HarddiskVolume3\Apps\SensorLogger.exe
powercfg /requests est une commande qui liste les demandes d’alimentation bloquant actuellement la veille ou l’extinction de l’écran, et elle est utile aussi bien pour enquêter sur « un PC qui mystérieusement ne se met jamais en veille » que pour confirmer la suppression de sa propre application.12 Sachez, à l’inverse, qu’un administrateur peut utiliser powercfg /requestsoverride pour configurer l’ignorance des demandes d’un processus spécifique.12 Le principe de conception sous-jacent est que l’API de suppression est une « demande », pas une garantie absolue.
Un dernier mot sur les bonnes pratiques. Une implémentation qui laisse ES_CONTINUOUS | ES_SYSTEM_REQUIRED défini pendant toute la durée d’exécution de l’application signifie qu’une application résidente écrase en permanence le plan d’alimentation choisi par l’utilisateur. Sur un portable, cela draine la batterie, et sur un PC partagé, cela affecte aussi les autres usages de la machine. Le principe est de limiter la suppression uniquement à « la fenêtre pendant laquelle l’opération qui ne tolère pas la veille est réellement en cours » (l’exemple de la documentation officielle elle-même est « le définir au démarrage de l’enregistrement, le lever à la fin de l’enregistrement »10). Notons au passage qu’il existe aussi PowerToys Awake comme solution temporaire qu’un utilisateur peut appliquer lui-même, et qui fonctionne aussi en interne sur le même mécanisme — un thread demandant un état d’exécution.24 C’est également un point de repère utile pour décider où tracer la ligne entre implémenter la suppression dans l’application ou la déléguer à un outil d’exploitation.
5. Concevoir en tenant compte de la veille — détection, reconnexion et enregistrement des trous
Pour la plupart des applications de surveillance ou de collecte résidentes, coexister avec la veille s’avère être la conception la plus saine, plutôt que de l’interdire purement et simplement. Ce qu’il faut, ce sont trois choses : savoir avant que cela n’arrive, savoir quand ça reprend, et reconstruire l’état après la reprise.
En .NET, Microsoft.Win32.SystemEvents.PowerModeChanged est le point d’entrée.15
using Microsoft.Win32;
SystemEvents.PowerModeChanged += OnPowerModeChanged;
private void OnPowerModeChanged(object sender, PowerModeChangedEventArgs e)
{
switch (e.Mode)
{
case PowerModes.Suspend:
// Le délai de grâce est court : se limiter à vider les tampons et à enregistrer l'heure d'arrêt de la mesure
_logger.Info("suspend at {0:O}", DateTimeOffset.Now);
_collector.Pause();
break;
case PowerModes.Resume:
// 1) Enregistrer le trou lui-même comme donnée
_logger.Info("resume at {0:O}", DateTimeOffset.Now);
// 2) Considérer la connexion comme morte, la détruire et se reconnecter
_connection.Reset();
// 3) Recalculer la planification par rapport à l'horloge murale et réarmer les minuteurs
_scheduler.Rebase(DateTimeOffset.Now);
// 4) Ne reprendre explicitement la collecte qu'une fois la reconstruction terminée (ne pas la laisser en pause)
_collector.Resume();
break;
}
}
La documentation officielle signale deux mises en garde concernant cet événement : il ne se déclenche pas sans une boucle de messages en cours d’exécution (un service Windows a besoin de quelque chose comme un formulaire caché pour cela), et comme c’est un événement statique, ne pas se désabonner provoque une fuite.15 Une application GUI peut l’utiliser directement, mais pour une application console ou de type service, il faut soit mettre en place sa propre fenêtre de messages pour recevoir le WM_POWERBROADCAST de Win32, soit utiliser PowerRegisterSuspendResumeNotification, qui peut recevoir un rappel sans aucun HWND (c’est aussi le chemin emprunté par les notifications dans un environnement DAM).416
Voyons aussi ce que signifient réellement les événements au niveau Win32.
- PBT_APMSUSPEND : notification juste avant la veille. Il n’y a qu’environ 2 secondes de délai de grâce par application, et le dépasser peut valoir une interruption par le système. Limitez ce que vous faites ici au vidage des tampons et à l’enregistrement de l’heure — n’écrivez jamais quelque chose de long, comme un nettoyage par le réseau.1718
- PBT_APMRESUMEAUTOMATIC : une notification garantie d’arriver à chaque reprise. Placez votre logique de reconnexion et de replanification ici.25
- PBT_APMRESUMESUSPEND : arrive après PBT_APMRESUMEAUTOMATIC lorsque la reprise se fait par action de l’utilisateur (ou une fois que l’utilisateur est de retour). Elle n’arrive pas pour une reprise automatique comme un réveil à distance, donc si vous placez votre « traitement de reprise » ici et uniquement ici, une reprise sans surveillance ne déclenche jamais la reconstruction.26
- De plus, pour une veille critique — par exemple, une coupure de batterie imminente — il n’y a aucune notification préalable du tout.18 Écrivez le traitement côté reprise de façon idempotente, afin qu’il fonctionne correctement même dans un cas où aucun événement de suspension n’a été reçu au départ.
Il y a une autre chose tout aussi importante que le traitement de la reprise lui-même : enregistrer explicitement un trou comme un trou. Les données de collecte qui traversent une veille ne sont pas « une valeur manquante » — c’est « non mesuré, parce que le système était arrêté » — et si vous conservez cet intervalle, avec les horodatages de suspension/reprise, à la fois dans le journal et dans les données elles-mêmes, quiconque regardera plus tard l’espace vide dans le graphique ne le confondra pas avec une véritable défaillance. Ce que les journaux d’une application longue durée devraient capturer est aussi traité dans Enquête sur un plantage longue durée de caméra industrielle — le cas de la fuite de handles.
Par ailleurs, il existe un autre problème du type « on ne s’en est pas rendu compte, mais c’est devenu lent » dans la même famille que la veille : la limitation par le mode Efficacité (EcoQoS) de Windows 11. Voir Qu’est-ce que le mode Efficacité de Windows — l’icône feuille verte et comment le désactiver pour ce sujet.
6. Faire en sorte que ça s’exécute à une heure fixe — le réveil et l’exécution du Planificateur de tâches
Implémenter un traitement piloté par le temps — quelque chose comme « agréger et transférer à 2 h du matin » — avec un minuteur d’application résidente combiné à une suppression de la veille est une mauvaise approche, car cela revient à tuer la veille toute la nuit. Pour ce cas d’usage, l’option « Réveiller l’ordinateur pour exécuter cette tâche » (WakeToRun) du Planificateur de tâches est la vraie réponse.
Une tâche avec WakeToRun activé réveille l’ordinateur depuis la veille ou la mise en veille prolongée à l’heure prévue, et maintient le système éveillé jusqu’à la fin de la tâche (si elle était déjà éveillée, elle demande de même que le système reste éveillé jusqu’à la fin). L’écran peut rester éteint au réveil — c’est normal.19
Il existe cependant des conditions pour que ce réveil se produise réellement. Comme le souligne la documentation officielle de dépannage, cela dépend du fait que « Autoriser les minuteries de réveil » soit activé dans les options d’alimentation et que le réglage de réveil côté BIOS soit également activé, et il n’est absolument pas rare que des portables récents, par conception d’économie d’énergie, soient configurés pour ne pas autoriser de réveil du tout.20 powercfg /waketimers peut lister les minuteries de réveil actuellement actives,12 donc vérifiez toujours sur le matériel réellement déployé que le réveil fonctionne véritablement. Si vous souhaitez réveiller la machine depuis votre propre application, il existe aussi l’option d’un minuteur réveillable — en passant TRUE pour fResume sur SetWaitableTimer. Dans ce cas, après un réveil automatique, le système reste éveillé seulement pendant la durée du minuteur d’inactivité sans surveillance (minimum 2 minutes), et se remet en veille rapidement à moins que l’application ne se déclare « en cours d’utilisation » via SetThreadExecutionState. Si le traitement après le réveil prend du temps, la bonne combinaison consiste à l’associer à la suppression du chapitre 4.27
Les questions de conception opérationnelle comme le compte d’exécution de la tâche, le problème du « se termine en 0x1 », et la prévention des exécutions multiples simultanées sont traitées dans Les tâches du Planificateur de tâches qui ne s’exécutent pas ou se terminent en 0x1 — isoler la cause et concevoir une exploitation fiable.
7. Tableau de décision — supprimer, concevoir pour la reprise, ou réveiller
Les trois conceptions ne s’excluent pas mutuellement — on choisit une approche principale et des approches complémentaires selon le type d’application, et on les combine.
| Type d’application | Premier choix | Combinaison / remarques |
|---|---|---|
| Mesure / collecte de données (mesure continue sur des heures à des jours) | Suppression avec ES_SYSTEM_REQUIRED, limitée à la fenêtre de mesure |
Toujours implémenter aussi le traitement de reprise (la veille déclenchée par l’utilisateur ne peut pas être empêchée10). Sur les machines Modern Standby alimentées par batterie, la coupure à 5 minutes s’applique ; faire de l’alimentation secteur une exigence13 |
| Traitement par lots nocturne / transfert planifié | Planificateur de tâches + WakeToRun19 | Plus économe en énergie et plus fiable qu’un processus résident combiné à une suppression. Nécessite de confirmer l’autorisation des minuteries de réveil20 |
| Agent de surveillance résident / de notification | Concevoir en tenant compte de la veille (détecter via PowerModeChanged → reconnecter, replanifier, enregistrer les trous)15 | Pour une machine dédiée dont le seul objectif est la surveillance, désactiver la veille au niveau du plan d’alimentation plutôt que dans l’application |
| Outil d’affichage de bureau (présentations, affichage de tableau de bord, etc.) | Utiliser ES_DISPLAY_REQUIRED combiné à ES_SYSTEM_REQUIRED, limité à la durée d’affichage10 |
Toujours lever la suppression à la fin de l’affichage. Pour un appareil d’affichage permanent, gérer plutôt via les réglages d’alimentation |
| Contrôle d’équipement 24/7 / PC de ligne | Désactiver entièrement la veille via les réglages d’alimentation (garantie opérationnellement) | Ne pas s’appuyer sur l’API de suppression de l’application comme filet de sécurité. Utiliser powercfg /requests pour des contrôles périodiques12 |
La décision repose sur deux axes : « existe-t-il une fenêtre de temps où il est acceptable que ce soit arrêté ? » (si oui, Planificateur de tâches ; si non, réglages d’alimentation), et « sur le PC de qui cela s’exécute-t-il ? » (plus une application tourne sur un portable personnel ou partagé d’un utilisateur, plus il faut privilégier la conception pour la reprise plutôt que la suppression).
8. Résumé
- La veille se décline en S3, mise en veille prolongée (S4) et Modern Standby (S0 low power idle), et vous pouvez les distinguer avec
powercfg /a. Même sur une machine Modern Standby, les applications de bureau sont suspendues par le DAM, donc l’hypothèse selon laquelle « ça continue de tourner pendant la veille » ne tient pas. - Ni les threads ni les minuteurs n’avancent pendant la veille. Depuis Windows 8, les minuteurs et attentes relatifs ne comptent pas le temps de veille et reportent le reste, et les minuteurs .NET se comportent de la même façon à partir de .NET 11 (jusqu’à .NET 10, ils peuvent se déclencher immédiatement à la reprise). Les API de temps écoulé sont un mélange de celles qui comptent ou non le temps de veille. Suivez le temps écoulé avec Stopwatch et l’heure murale avec DateTime, et réancrez votre référence à la reprise.
- Une connexion TCP meurt silencieusement au délai d’inactivité d’un équipement intermédiaire. La pratique standard consiste à détruire et reconnecter à l’événement de reprise.
- Supprimez avec
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED), limité à la seule fenêtre nécessaire. Cela ne peut pas empêcher une veille déclenchée par l’utilisateur, et sur une machine Modern Standby alimentée par batterie, la coupure survient au bout de 5 minutes. Vérifiez avecpowercfg /requests. - Traitez la reprise avec
SystemEvents.PowerModeChanged/WM_POWERBROADCAST. Comme le délai de grâce de la notification de suspension n’est que d’environ 2 secondes, et que la veille peut survenir sans aucune notification, écrivez le côté reprise de façon idempotente et enregistrez l’intervalle du trou. - Pour une exécution à heure fixe, WakeToRun du Planificateur de tâches est la vraie réponse. Concevez en incluant à la fois le réglage d’alimentation de la minuterie de réveil et la vérification du réveil sur le matériel réel.
Articles connexes
- Les tâches du Planificateur de tâches qui ne s’exécutent pas ou se terminent en 0x1 — isoler la cause et concevoir une exploitation fiable
- Pourquoi préférer l’attente d’événements à Sleep(1) sous Windows
- Diagnostiquer pourquoi la retransmission TCP bloque la communication d’une caméra industrielle
- Enquête sur un plantage longue durée de caméra industrielle — le cas de la fuite de handles
- Qu’est-ce que le mode Efficacité de Windows — l’icône feuille verte et comment le désactiver
Domaines de conseil associés
KomuraSoft LLC (合同会社小村ソフト) prend en charge la conception et l’implémentation d’applications Windows longue durée pour la mesure, la surveillance et la collecte de données, ainsi que l’investigation de défaillances spécifiques aux applications longue durée comme « ça s’était arrêté pendant la nuit » ou « les enregistrements ont des trous ». N’hésitez pas à nous consulter également pour diagnostiquer des symptômes difficiles à reproduire liés à la veille et à la gestion de l’alimentation.
- Développement d’applications Windows
- Investigation de bugs et analyse des causes
- Conseil technique et revue de conception
- Contact
Références
-
Microsoft Learn, System Sleeping States. Sur le fait que S1 à S4 sont des états de veille n’exécutant aucun calcul, que S3 ne conserve que la mémoire tandis que S4 enregistre dans un fichier de mise en veille prolongée, et que powercfg /a permet de lister les états de veille disponibles sur un système. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, System power states. Sur le fait que S0 low power idle (Modern Standby) maintient le système partiellement en fonctionnement à basse consommation, que les systèmes SoC compatibles Modern Standby n’utilisent pas S1 à S3, et qu’aucune notification n’est donnée pour une transition critique. ↩ ↩2 ↩3
-
Microsoft Learn, Overview of Modern Standby Testing and Diagnostics. Sur le fait que powercfg /a permet d’identifier la prise en charge de Modern Standby (via une entrée Standby (S0 Low Power Idle) dans sa sortie). ↩ ↩2
-
Microsoft Learn, Desktop Activity Moderator. Sur le fait que le DAM réduit l’exécution des applications de bureau à un niveau équivalent à S3, que les processus d’une session interactive voient chacun de leurs threads suspendus tandis que la session 0 est limitée, sur la notification WM_POWERBROADCAST avant la suspension, sur l’incohérence entre le comportement des minuteurs/temps de fonctionnement et l’horloge murale, et sur la nécessité de tenir compte de la durée de vie des connexions. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8
-
Microsoft Learn, SetWaitableTimerEx function. Sur le fait qu’un minuteur à temps relatif incluait le temps passé dans un état à basse consommation sous Windows 7 et versions antérieures (le compte à rebours continuait d’avancer même pendant la veille), contre son exclusion à partir de Windows 8 (le compte à rebours n’avance pas pendant la veille). ↩ ↩2
-
Microsoft Learn, Environment.TickCount made consistent with Windows timeout behavior. Sur le changement cassant selon lequel Environment.TickCount/TickCount64 est basé sur GetTickCount64 (incluant le temps de veille) jusqu’à .NET 10 et passe à une base QueryUnbiasedInterruptTime (ne l’incluant pas) à partir de .NET 11, sur le fait que les API d’attente prenant un délai d’expiration (SleepEx/WaitForMultipleObjectsEx) avaient déjà cessé de compter le temps hors fonctionnement depuis Windows 8, et sur le fait que ce changement peut entraîner du code ne déclenchant plus un minuteur immédiatement après la reprise. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Windows Time. Sur le fait que le temps écoulé de GetTickCount/GetTickCount64 inclut le temps passé en veille et en mise en veille prolongée, et que QueryUnbiasedInterruptTime n’inclut que le temps en état de fonctionnement. ↩ ↩2 ↩3
-
Microsoft Learn, Acquiring high-resolution time stamps. Sur le fait que le Stopwatch de System.Diagnostics en code managé utilise le QPC comme base de temps, et que QueryPerformanceCounter renvoie un nombre de tics incluant le temps passé en veille, en mise en veille prolongée et en connected standby. ↩ ↩2
-
Microsoft Learn, Load Balancer TCP Reset and Idle Timeout. Sur le fait que le comportement par défaut d’un répartiteur de charge est d’abandonner silencieusement un flux dès qu’il atteint le délai d’inactivité (4 minutes par défaut), que l’envoi d’une réinitialisation TCP est une fonctionnalité devant être explicitement activée, et que le keep-alive TCP est utilisé comme contre-mesure. ↩ ↩2
-
Microsoft Learn, SetThreadExecutionState function. Sur la signification de ES_CONTINUOUS/ES_SYSTEM_REQUIRED/ES_DISPLAY_REQUIRED, sur le fait qu’un appel sans ES_CONTINUOUS ne fait que réinitialiser une seule fois le minuteur d’inactivité, sur le fait que la veille déclenchée par l’utilisateur ne peut pas être empêchée, et sur l’exemple d’usage consistant à ne le définir que pour la durée de l’opération requise puis à le lever ensuite. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8
-
Microsoft Learn, System Sleep Criteria. Sur le fait que le système compte les applications/threads ayant appelé SetThreadExecutionState, et entre en veille dès que ce compte est nul et qu’il n’y a aucune entrée utilisateur. ↩ ↩2
-
Microsoft Learn, Powercfg command-line options. Sur le fait que /requests liste les demandes d’alimentation bloquant la veille ou l’extinction de l’écran, que /requestsoverride permet de faire ignorer les demandes d’un processus spécifique, et que /waketimers permet de lister les minuteries de réveil actuellement actives. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, PowerSetRequest function. Sur des types de demande comme PowerRequestSystemRequired/PowerRequestExecutionRequired, sur le fait qu’une demande est coupée 5 minutes après le dépassement du délai d’expiration de la veille lorsqu’un système Modern Standby est sur alimentation DC, sur le fait qu’une demande se termine lorsque la veille est déclenchée par une action de l’utilisateur, et sur la bonne pratique consistant à attacher une chaîne de motif et à appeler Set juste avant et Clear juste après. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, PowerCreateRequest function. Sur la création d’un objet de demande d’alimentation avec un REASON_CONTEXT spécifié, et sur sa libération avec CloseHandle une fois qu’il n’est plus nécessaire. ↩ ↩2
-
Microsoft Learn, SystemEvents.PowerModeChanged Event. Sur le fait qu’il s’agit d’un événement se déclenchant à la suspension/reprise, qu’il ne se déclenche pas sans une boucle de messages en cours d’exécution (un service a besoin de quelque chose comme un formulaire caché), et qu’il fuit s’il n’est pas désabonné, s’agissant d’un événement statique. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, WM_POWERBROADCAST message. Sur le fait que les événements de gestion de l’alimentation sont délivrés à une fenêtre sous forme de message WM_POWERBROADCAST (PBT_APMSUSPEND / PBT_APMRESUMEAUTOMATIC / PBT_APMRESUMESUSPEND, etc.). ↩ ↩2
-
Microsoft Learn, PBT_APMSUSPEND event. Sur le fait que ceci est envoyé juste avant la suspension, et que le délai de grâce pour son traitement est d’environ 2 secondes, le système pouvant interrompre un traitement le dépassant. ↩ ↩2
-
Microsoft Learn, System Power Management Events. Sur le fait qu’aucune notification préalable n’est donnée pour une suspension d’urgence telle que celle déclenchée par une batterie critique, et sur le fait que le traitement de la notification de suspension expire au bout de maximum 2 secondes par application. ↩ ↩2 ↩3
-
Microsoft Learn, ITaskSettings::get_WakeToRun method. Sur le fait que WakeToRun réveille l’ordinateur pour exécuter une tâche et le maintient éveillé jusqu’à la fin de celle-ci, et que l’écran peut parfois rester éteint au réveil. ↩ ↩2 ↩3
-
Microsoft Learn, Automatic maintenance. Sur la liste de vérification en cas d’échec d’un réveil planifié, mentionnant le réglage de réveil du BIOS, l’option d’alimentation « Allow Wake Timer » et le réglage WakeToRun d’une tâche, et sur le fait qu’il est courant pour les portables modernes d’être configurés pour ne pas autoriser un réveil S3. ↩ ↩2 ↩3
-
Microsoft Learn, What is Modern Standby. Sur le fait que Modern Standby permet une mise en marche/arrêt instantanée tout en maintenant la connexion réseau sous le modèle S0 low power idle, et que la reprise (du bouton d’alimentation à l’allumage de l’écran) prend moins d’une seconde. ↩
-
Microsoft Learn, QueryUnbiasedInterruptTime function. Sur le fait que le temps d’interruption non biaisé ne compte que le temps en état de fonctionnement et n’inclut pas le temps passé en veille ou en mise en veille prolongée. ↩
-
Microsoft Learn, Modern standby network connectivity. Sur le fait qu’Adaptive Connected Standby met en pause l’activité réseau pendant la veille en fonctionnement sur batterie, sauf s’il existe un scénario qui la requiert. ↩
-
Microsoft Learn, PowerToys Awake utility. Sur le fait qu’il s’agit d’un utilitaire maintenant le PC éveillé sans changer le plan d’alimentation, fonctionnant en générant un thread en arrière-plan qui demande l’état de la machine, et revenant au comportement normal du plan d’alimentation une fois qu’il se termine. ↩
-
Microsoft Learn, PBT_APMRESUMEAUTOMATIC event. Sur le fait qu’il s’agit d’un événement délivré à chaque reprise, n’indiquant pas en soi la présence de l’utilisateur, et sur le fait que PBT_APMRESUMESUSPEND est délivré ensuite dès qu’une activité utilisateur est détectée. ↩
-
Microsoft Learn, PBT_APMRESUMESUSPEND event. Sur le fait que ceci est délivré après PBT_APMRESUMEAUTOMATIC lors d’une reprise déclenchée par l’utilisateur ou où l’utilisateur est détecté, et que seul PBT_APMRESUMEAUTOMATIC est délivré pour un réveil à distance. ↩
-
Microsoft Learn, System Wake-up Events. Sur le fait que définir fResume sur TRUE sur SetWaitableTimer permet au minuteur de réveiller le système, et que le réveil automatique fixe une minuterie d’inactivité sans surveillance d’au moins 2 minutes, après quoi le système se remet en veille sauf si SetThreadExecutionState indique qu’il est en cours d’utilisation. ↩
Articles associés
Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.
MAX_PATH et les pièges des chemins et noms de fichiers Windows — la limite de 260 caractères, les noms réservés, les points de fin et la casse
Un tour d'horizon des limites de chemins et de noms de fichiers à l'origine du classique bug « fichier introuvable ». Cet article couvre ...
Les pièges des lecteurs réseau et des chemins UNC — Travailler avec un serveur de fichiers (dossier partagé) depuis une application métier
Cet article recense les problèmes classiques rencontrés lorsqu'une application métier écrit dans un dossier partagé ou le surveille : pou...
Icônes de la zone de notification et notifications toast dans les applications Windows — les pièges de NotifyIcon et comment choisir la bonne AppNotification
Un guide pratique pour maintenir une application Windows métier résidente dans la zone de notification (system tray) et avertir l'utilisa...
Quand votre application Windows maison est signalée comme un virus — gérer les faux positifs de Microsoft Defender et composer avec l'impact sur les performances
Nous détaillons la marche à suivre officielle lorsque Microsoft Defender signale à tort une application Windows développée en interne com...
Les applications métier fonctionnent-elles sous Windows on Arm ? — La réalité de l'émulation x64 (Prism) et des DLL/COM natifs
Une réponse, destinée aux développeurs et aux services informatiques, à la question « notre application métier fonctionnera-t-elle sous W...
Sujets associés
Ces pages replacent le sujet dans un contexte plus large de services et de décisions.
Thèmes techniques Windows
Portail des sujets sur le développement Windows, l'analyse des incidents et la valorisation des actifs existants.
Services liés à ce sujet
Cet article est directement lié aux services suivants.
Développement d'applications Windows
Applications métier, intégration d'équipements et outils de communication, des besoins au développement.
Questions fréquentes
Questions souvent posées lors d’une consultation sur le sujet de cet article.
- Comment empêcher le PC de se mettre en veille uniquement pendant que mon application s'exécute ?
- L'approche de base consiste à appeler SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED) au démarrage du traitement, puis à le lever avec SetThreadExecutionState(ES_CONTINUOUS) à la fin. N'ajoutez ES_DISPLAY_REQUIRED que si vous devez aussi garder l'écran allumé. Cela n'agit que sur la veille automatique déclenchée par un délai d'inactivité — cela ne peut pas empêcher une veille explicitement déclenchée par l'utilisateur, comme appuyer sur le bouton d'alimentation ou fermer le capot. Vous pouvez vérifier si la suppression est effectivement active avec powercfg /requests.
- Qu'est-ce que Modern Standby et en quoi est-ce différent de la veille traditionnelle ?
- Il s'agit d'un modèle de veille plus récent, aussi appelé S0 low power idle, dans lequel le système reste partiellement actif à basse consommation et peut reprendre instantanément. Une machine compatible Modern Standby ne prend pas en charge l'état de veille S3 traditionnel. Cependant, seule l'activité explicitement autorisée par l'OS continue de « tourner » : les applications de bureau voient chacun de leurs threads suspendus par le Desktop Activity Moderator (DAM), donc du point de vue de l'application, tout s'arrête comme sous S3. Vous pouvez vérifier le mode utilisé par votre PC avec powercfg /a.
- Pourquoi une connexion TCP cesse-t-elle de fonctionner après la reprise depuis la veille ?
- Parce que l'application ne peut pas communiquer pendant la veille, la connexion devient inactive, et les équipements intermédiaires sur le trajet — NAT, pare-feu, répartiteurs de charge — abandonnent le flux au bout d'un délai d'inactivité. Beaucoup d'équipements l'abandonnent silencieusement, sans en avertir aucune des deux extrémités, si bien que le socket de l'application semble parfaitement normal jusqu'au premier envoi ou à la première réception après la reprise, qui échoue immédiatement ou reste bloquée jusqu'au délai d'expiration en attendant une réponse. La pratique standard consiste à considérer la connexion comme suspecte et à la reconstruire dès la réception d'un événement de reprise.
- Pour exécuter de façon fiable un traitement par lots nocturne, vaut-il mieux supprimer la veille ou utiliser le Planificateur de tâches ?
- L'option « Réveiller l'ordinateur pour exécuter cette tâche » (WakeToRun) du Planificateur de tâches est le premier choix. Elle réveille le PC à l'heure prévue et le maintient éveillé jusqu'à la fin de la tâche, ce qui évite d'avoir à tuer la veille toute la nuit. Cela dit, elle ne réveillera pas la machine si les minuteries de réveil sont désactivées dans les options d'alimentation ; il faut donc confirmer que « Autoriser les minuteries de réveil » est activé et vérifier sur le matériel réel que le réveil fonctionne réellement. Supprimer la veille en permanence gaspille de l'énergie pendant toute cette durée et constitue une conception peu élégante qui écrase les propres réglages d'alimentation de l'utilisateur.
Profil de l’auteur
Page de présentation de l’auteur de l’article.
Go Komura
Représentant de KomuraSoft LLC
Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.
Liens publics