Les applications qui cassent à la reprise de veille — comment fonctionnent les événements d'alimentation Windows et comment construire des applications métier qui y survivent
· Go Komura · Windows, Gestion de l'alimentation, Développement Windows, Applications métier, Contrôle de périphériques, Investigation de bugs, Win32 API
« J’ai fermé le portable, je l’ai ouvert le lendemain matin, et l’application métier était pleine d’erreurs. » « L’application de surveillance d’équipement perd des données uniquement après le déjeuner. » « L’outil résident qui exporte vers Excel s’arrête parfois sur une erreur de connexion. » — Ces tickets partagent un seul suspect. La veille.
Les applications métier de l’époque où les PC de bureau étaient le courant dominant ont été écrites sur l’hypothèse tacite que « le PC reste allumé ». Le champ de bataille principal aujourd’hui est le portable, et par défaut il se met en veille après quelques minutes d’inactivité. Sur une machine compatible Modern Standby, la sémantique même de la veille a changé par rapport au modèle traditionnel. Destiné aux développeurs qui écrivent des applications métier et des logiciels de contrôle d’équipement sous Windows, cet article organise, à partir des sources primaires, ce que l’OS notifie à une application avant et après la veille, ce qui casse, et comment écrire une application qui survit à la reprise.
1. La conclusion, d’abord
- La veille est un événement que l’application n’a « pas le droit de refuser ». Vous êtes notifié juste avant avec
WM_POWERBROADCAST(PBT_APMSUSPEND), mais le délai de grâce est d’environ 2 secondes, et lors d’une suspension d’urgence la notification n’arrive même pas.12 - À la reprise depuis la suspension, PBT_APMRESUMEAUTOMATIC arrive, et lors d’une reprise initiée par l’utilisateur PBT_APMRESUMESUSPEND arrive aussi. Le travail requis tel que la reconnexion appartient en règle générale au premier. Les transitions d’entrée et de sortie de l’inactivité basse consommation de Modern Standby ne s’alignent toutefois pas toujours sur ces notifications, donc traitez les notifications comme une aide.34
- Concevez en partant du principe que les connexions TCP, les ports série et les handles de périphérique ne survivent pas à travers la reprise. Une logique de reconnexion qui les reconstruit à une notification de reprise ou à une erreur de communication est l’événement principal.
- Surveillez les minuteries et le traitement du temps. Le travail périodique s’arrête pendant la veille, et la façon dont il se déclenche immédiatement après la reprise diffère selon l’API de minuterie et le runtime. Un « énorme saut du temps écoulé » se produit aussi, donc l’approche sûre est de reconstruire le planning à la reprise.
- Supprimez la veille explicitement pour les intervalles que vous ne voulez pas interrompre. Utilisez
SetThreadExecutionState(ES_SYSTEM_REQUIRED) ou une demande d’alimentation (PowerSetRequest), et levez-la toujours lorsque le travail se termine.45 - Sur une machine Modern Standby le système tourne encore par intermittence pendant la veille, mais les applications de bureau sont mises en pause. Vous ne pouvez pas tenir l’attente que « notre application devrait continuer de tourner pendant la veille ».6
- Les outils d’investigation standard sont
powercfg(/requests, /lastwake, /sleepstudy) et Kernel-Power dans le journal d’événements.
2. Ce qui se passe autour de la veille — le flux des événements d’alimentation
L’OS diffuse les changements d’état d’alimentation à chaque application sous la forme d’un message WM_POWERBROADCAST.2 Il y a trois événements principaux qui impliquent la veille et la reprise.
| Événement | Signification |
|---|---|
| PBT_APMSUSPEND | Sur le point d’entrer en veille (la dernière chance de se préparer) |
| PBT_APMRESUMEAUTOMATIC | A repris (arrive toujours à la reprise) |
| PBT_APMRESUMESUSPEND | Reprise causée par une action de l’utilisateur (celui-ci est conditionnel) |
PBT_APMSUSPEND est la notification juste avant la veille, et ici vous pouvez vous préparer en fermant des fichiers et en enregistrant l’état. Il y a toutefois deux conditions. Première, le temps accordé au traitement est d’environ 2 secondes par application, et si vous le dépassez le système poursuit sans attendre.1 Deuxième, lors d’une suspension d’urgence telle qu’une batterie critiquement faible, il se met en veille immédiatement sans notification préalable.2 Une conception qui « doit terminer avant la veille » ne tient pas. Traitez la notification comme une chance de « le faire si vous y arrivez », et placez le travail principal du côté de la reprise.
Le côté reprise se fait en deux étapes. PBT_APMRESUMEAUTOMATIC arrive à la reprise depuis une transition de suspension. En plus de cela, si la machine a repris à cause d’une action de l’utilisateur telle que le bouton d’alimentation ou une touche (ou si la présence de l’utilisateur a été détectée ensuite), PBT_APMRESUMESUSPEND suit. Inversement, une reprise sans surveillance pour un réveil distant sur le réseau ou pour de la maintenance ne délivre que PBT_APMRESUMEAUTOMATIC.3 Ces deux étapes sont elles-mêmes un indice pour scinder le travail — faites la récupération mécanique telle que reconstruire les connexions sur PBT_APMRESUMEAUTOMATIC, et faites les actions visibles par l’utilisateur telles que les mises à jour d’écran ou une invite de reconnexion sur PBT_APMRESUMESUSPEND.
sequenceDiagram
accTitle: Flux de notification pour la veille et la reprise
accDescr: PBT_APMSUSPEND arrive juste avant la veille avec environ 2 secondes de grâce ; à la reprise, PBT_APMRESUMEAUTOMATIC arrive toujours, et PBT_APMRESUMESUSPEND ne suit que pour une reprise initiée par l'utilisateur
participant OS as OS
participant A as Application
OS->>A: PBT_APMSUSPEND (environ 2 secondes de grâce)
A->>A: Enregistrer l'état et fermer les connexions
Note over OS: Veille (le code ne s'exécute pas)
OS->>A: PBT_APMRESUMEAUTOMATIC (arrive à la reprise)
A->>A: Reconnecter et restaurer l'état
OS->>A: PBT_APMRESUMESUSPEND (reprise initiée par l'utilisateur seulement)
A->>A: Mises à jour d'écran et autre travail visible
Figure 1 : les notifications ne sont que « un mot juste avant, et un ou deux mots après la reprise ». La vedette de la récupération est le travail du côté de la reprise.
flowchart TB
accTitle: Différence entre la veille ordinaire et la suspension d'urgence
accDescr: La veille ordinaire délivre PBT_APMSUSPEND juste avant avec environ 2 secondes pour se préparer, mais une suspension d'urgence telle qu'une batterie critique s'arrête sans notification préalable, donc une conception qui dépend de la notification préalable ne tient pas
n2["Veille ordinaire"] --> pre["PBT_APMSUSPEND (environ 2 s de grâce)"]
pre --> s1["Se préparer, puis s'arrêter"]
e2["Suspension d'urgence (batterie critique)"] --> s2["S'arrêter sans notification préalable"]
s2 -.-> l2["Une conception qui suppose la notification ne tient pas"]
Figure 2 : une suspension d’urgence arrive sans avertissement. Donc la préparation est « un bonus si vous y arrivez », et le travail principal va du côté de la reprise.
Notez que WM_POWERBROADCAST ne distingue pas le type d’état basse consommation (veille versus mise en veille prolongée).4 La bonne abstraction pour l’application est de le traiter comme un seul type d’événement : « ça s’est arrêté, et ça est revenu ». Les services sans fenêtre et les applications console peuvent recevoir les mêmes notifications en utilisant RegisterSuspendResumeNotification sous forme de callback (DEVICE_NOTIFY_CALLBACK).7
flowchart TB
accTitle: Scission du travail entre les deux étapes de reprise
accDescr: Placez la récupération mécanique telle que la reconnexion sur PBT_APMRESUMEAUTOMATIC, qui arrive à la reprise ; placez le travail visible par l'utilisateur tel que les mises à jour d'écran ou une invite de reconnexion sur PBT_APMRESUMESUSPEND, qui n'arrive que lors d'une reprise initiée par l'utilisateur
ra["PBT_APMRESUMEAUTOMATIC (à la reprise)"] --> m["Récupération mécanique"]
rs["PBT_APMRESUMESUSPEND (reprise utilisateur)"] --> u["Travail visible par l'utilisateur"]
m -.-> m1["Reconnecter et rouvrir les handles"]
u -.-> u1["Mises à jour d'écran et invite de reconnexion"]
Figure 3 : le second n’arrive pas lors d’une reprise sans surveillance, donc placer la récupération requise sur le second la manquera.
3. Modern Standby — le sens de « veille » a changé
Un autre fait moderne à intégrer est Modern Standby. La veille S3 traditionnelle était un modèle simple qui « arrêtait le système dans son ensemble » ; la veille sur une machine Modern Standby est un modèle de type smartphone dans lequel le système continue de tourner par intermittence après que l’écran s’éteint.
Ce qui compte pour une application métier ici, c’est que les applications de bureau sont mises en pause par le Desktop Activity Moderator (DAM) dès la première étape d’entrée en veille.6 Le système lui-même tourne encore de temps en temps pour maintenir le réseau et recevoir des notifications, mais les composants qui en bénéficient sont ceux qui participent à ce mécanisme — le code ordinaire d’une application de bureau ne s’exécute pas. Donc du point de vue d’un développeur, la conclusion est la même pour Modern Standby et pour S3 — concevez en partant du principe que votre code ne s’exécute pas pendant la veille.
flowchart TB
accTitle: Différence entre la veille traditionnelle et Modern Standby
accDescr: La veille S3 traditionnelle arrête le système dans son ensemble, tandis que sous Modern Standby le système tourne encore par intermittence après que l'écran s'éteint. Les applications de bureau sont mises en pause par le DAM dans les deux cas, donc le code de l'application ne s'exécute pas
s3["Veille S3 traditionnelle : tout le système s'arrête"] --> conc["Le code de l'application ne s'exécute pas"]
ms["Modern Standby : le système tourne par intermittence"] --> dam["Les applications de bureau sont mises en pause par le DAM"]
dam --> conc
Figure 4 : le modèle a changé, mais pour une application de bureau la conclusion est la même : « vous ne pouvez pas tourner pendant la veille ».
Une autre prudence est le peu de fiabilité des notifications. Sous Modern Standby, les transitions d’entrée et de sortie de l’inactivité basse consommation ne s’alignent pas sur la transition de suspension traditionnelle, et une connexion peut déjà être rompue sans qu’aucune notification n’arrive jamais. Traitez la notification de reprise comme une aide, et placez la reconnexion déclenchée par la détection d’erreur (chapitre 5) sur le chemin principal de récupération.
Une autre différence est le sentiment « glissant » du comportement. Atteindre les profondeurs de la veille se fait par étapes, et le moment des déconnexions et des arrêts n’est pas aussi net que sous S3. La distinction entre « l’écran vient juste de s’éteindre » et « il s’est mis en veille » est aussi difficile à voir pour l’utilisateur, donc lorsque vous prenez un symptôme il faut confirmer « ont-ils fermé le capot » et « combien de minutes a-t-il été laissé inactif ».
4. Ce qui casse — symptômes classiques
La connexion TCP est morte. Pendant la veille, l’autre côté, le NAT et les pare-feu traitent votre silence comme un délai d’expiration et abandonnent la connexion. Pire, le socket de ce côté ne sait pas l’erreur, donc elle n’échoue que lorsque vous envoyez ou recevez après la reprise. Ou pire encore, une attente de réception n’erre jamais du tout (c’est pourquoi il faut un keepalive). Les connexions de base de données et les WebSockets ont la même forme.
Les handles de port série et de périphérique USB deviennent invalides. Un périphérique connecté en USB peut, à la reprise, sembler avoir été « débranché puis rebranché » une fois, et le handle que vous aviez ouvert commence à renvoyer des erreurs. C’est le schéma typique d’une application de contrôle d’équipement qui « n’obtient une erreur de communication qu’après le déjeuner ». La conception de reconnexion est aussi couverte dans l’article sur la communication série.
La continuité du temps se rompt. Le travail piloté par minuterie tel que « interroger toutes les 10 secondes » ne se déclenche pas pendant la veille. La façon dont il se déclenche immédiatement après la reprise (le travail échu se déclenche une fois immédiatement, rien ne se passe jusqu’à la période suivante, etc.) diffère selon l’API de minuterie et le runtime que vous utilisez, donc ne laissez pas le traitement des ticks manqués à un comportement implicite — l’approche sûre est de reconstruire le planning à la notification de reprise. Aussi, les calculs de temps écoulé (la différence par rapport à l’horodatage précédent) deviennent soudain « l’équivalent de 8 heures », et les calculs de moyenne ou les jugements de délai d’expiration cassent. Un travail planifié tel que « s’exécuter chaque nuit à 2 h » ne s’exécute simplement pas si le PC est en veille à ce moment (réveillez-le avec la fonction de réveil depuis la veille du Planificateur de tâches si vous en avez besoin).
flowchart TB
accTitle: Trois formes dans lesquelles la continuité du temps se rompt
accDescr: Le travail périodique s'arrête pendant la veille et le déclenchement après reprise diffère selon l'API, donc reconstruisez le planning à la reprise ; la différence par rapport à l'horodatage précédent devient énorme après la reprise, donc protégez-la ; le travail planifié ne s'exécute pas si la machine est en veille, donc envisagez le réveil du Planificateur de tâches
t1["Travail périodique : s'arrête"] -.-> g1["Reconstruire le planning"]
t2["Temps écoulé : explose"] -.-> g2["Protéger les écarts anormaux"]
t3["Planifié : n'a jamais tourné"] -.-> g3["Réveil depuis la veille"]
g1 ~~~ t2
g2 ~~~ t3
Figure 5 : écrivez le traitement des minuteries et du temps en partant du principe que « le temps saute ». Chacune des trois formes a un type de contre-mesure.
flowchart TB
accTitle: Trois choses qui cassent à travers la veille
accDescr: À travers la veille, une connexion TCP a été abandonnée par un délai d'expiration de l'autre côté, le handle d'un périphérique USB est invalidé comme une reconnexion, et le travail basé sur le temps écoulé observe un énorme saut de temps. Récupérez chacun avec reconnexion, réouverture et une garde d'écart
sleep["Intervalle de veille"] --> tcp["TCP : le pair l'a abandonnée"]
sleep --> more{"USB ou temps écoulé ?"}
more --> usb["USB : handle invalide"]
more --> time["Temps écoulé : un saut"]
tcp -.-> r1["Détecter + reconnecter"]
usb -.-> r2["Rouvrir le périphérique"]
time -.-> r3["Protéger les écarts anormaux"]
Figure 6 : ce qui casse tombe en trois familles — « connexions », « handles » et « continuité du temps » — et chacune a un type de récupération établi.
La réauthentification aux ressources partagées. Les lecteurs réseau et les VPN ont souvent besoin d’être rétablis après la reprise, et il y a une « vallée de démarrage » de quelques secondes à plusieurs dizaines de secondes juste après la reprise dans laquelle l’accès échoue. Il est plus sûr de ne pas tout réessayer à la fois immédiatement après la reprise, mais d’attendre un peu et de réessayer par étapes.
5. Construire des applications qui survivent à la reprise
Le principe tient en une chose. Partez du principe que « les connexions et les handles ne survivent pas à travers la veille », et structurez l’application de sorte que vous puissiez toujours récupérer.
Détectez la reprise et récupérez. Lorsque WM_POWERBROADCAST de la fenêtre de premier niveau reçoit PBT_APMRESUMEAUTOMATIC, abandonnez les connexions que vous détenez et reconstruisez-les. Le point est de ne pas s’appuyer sur la seule notification de reprise. Les notifications manquées et la communication qui se produit avant la notification sont toutes deux réelles, donc couplez-la toujours à un chemin qui « se reconnecte lorsqu’une erreur de communication est détectée », et traitez la notification de reprise comme un déclencheur qui ne fait que commencer cela plus tôt.
// C#: funnel both the resume notification and communication errors into the same reconnect path
protected override void WndProc(ref Message m)
{
const int WM_POWERBROADCAST = 0x0218;
const int PBT_APMRESUMEAUTOMATIC = 0x0012;
if (m.Msg == WM_POWERBROADCAST && (int)m.WParam == PBT_APMRESUMEAUTOMATIC)
{
_connectionManager.RequestReconnect(); // idempotent reconnect request
}
base.WndProc(ref m);
}
Rendez le travail de reconnexion lui-même idempotent (sûr peu importe le nombre de fois qu’il est appelé), réessayez en cas d’échec avec un backoff exponentiel, et à l’état stable détectez tôt une connexion morte avec un keepalive — mettez ces trois-là ensemble comme un ensemble, et vous survivrez non seulement à la reprise depuis la veille mais aussi à une brève coupure réseau ou à un redémarrage de périphérique.
flowchart TB
accTitle: Conception de reconnexion résiliente à la reprise
accDescr: La notification de reprise, une erreur de communication et un échec de keepalive convergent tous vers le même travail de reconnexion idempotent, qui réessaie avec un backoff exponentiel en cas d'échec
e1["Notification de reprise (PBT_APMRESUMEAUTOMATIC)"] --> r["Travail de reconnexion idempotent"]
e2["Détection d'erreur de communication"] --> r
e3["Échec de keepalive"] --> r
r --> ok{"Réussi ?"}
ok -->|"oui"| run["Retour au fonctionnement normal"]
ok -->|"non"| back["Réessayer après backoff exponentiel"]
back --> r
Figure 7 : concentrez la reconnexion en un seul chemin idempotent, et entrez sur la même route depuis la notification de reprise, la détection d’erreur ou le keepalive.
Revisitez le traitement du temps. Pour le travail qui utilise « le temps écoulé depuis la dernière fois », mettez une garde qui invalide l’intervalle lorsqu’elle détecte un écart anormalement grand (ne le pliez pas dans une moyenne, ne le traitez pas comme un délai d’expiration). Mesurer le temps écoulé à travers la reprise exige de garder une distinction entre une horloge qui avance pendant la veille (l’heure murale) et le temps réellement passé au travail.
Supprimez la veille explicitement pour les intervalles que vous ne voulez pas interrompre. Pendant un travail qui ne doit pas être interrompu par la veille — une migration de données, une communication continue avec un périphérique, etc. — vous pouvez garder le système éveillé avec SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED) (ajoutez ES_DISPLAY_REQUIRED si vous voulez aussi garder l’écran allumé).45 Une méthode plus propre est l’API de demande d’alimentation (PowerCreateRequest + PowerSetRequest), qui peut attacher une chaîne de motif, et powercfg /requests montrera alors « qui le bloque et pourquoi ».8 Notez que la suppression via SetThreadExecutionState est par thread, et vous la levez depuis le même thread qui l’a définie. Pour un travail qui change de thread, tel que async/await, utilisez le côté demande d’alimentation, qui est géré par un handle. Il y a des précautions. Première, ce que celles-ci suppriment est la veille automatique d’inactivité. Elles ne peuvent pas arrêter une action explicite de l’utilisateur telle que fermer le capot ou choisir Veille dans le menu Démarrer, donc vous ne pouvez pas sauter la conception de reconnexion de ce chapitre même pendant que la suppression est active. Deuxième, sur batterie sur une machine Modern Standby, ces demandes d’alimentation sont aussi coupées un certain temps après que le délai d’expiration de la veille s’écoule. Un travail qui ne peut pas être interrompu doit être garanti par l’alimentation secteur ou par l’exploitation.8 Troisième, levez-la toujours lorsque le travail se termine. Un oubli de levée devient un nouveau bug : « ce PC, pour une raison quelconque, ne se met pas en veille ».
flowchart TB
accTitle: Deux moyens de supprimer la veille
accDescr: Que vous utilisiez le commode SetThreadExecutionState ou l'API de demande d'alimentation qui peut attacher une chaîne de motif et est visible pour un administrateur via powercfg, levez-la toujours lorsque le travail se termine
need["Intervalle de travail qui ne doit pas être interrompu"] --> a["SetThreadExecutionState"]
need --> b["Demande d'alimentation (PowerSetRequest)"]
a -.-> a1["Commode — drapeaux seulement"]
b -.-> b1["Avec un motif — visible dans powercfg"]
a --> off["Levez-la toujours à la fin du travail"]
b --> off
Figure 8 : pour l’un ou l’autre moyen, « levez-la lorsque vous avez fini » est une condition absolue. Une demande d’alimentation qui peut rendre le motif visible est plus gentille pour l’exploitation.
Les services et les applications sans fenêtre reçoivent des notifications par callback avec RegisterSuspendResumeNotification (DEVICE_NOTIFY_CALLBACK).7 Si le fonctionnement continu est une exigence réelle, la solution de fond est de revisiter une conception qui garde le travail résident sur un PC client qui se met en veille, et de le déplacer côté serveur ou vers une machine exploitée sans veille.
6. Investigation — powercfg et le journal d’événements
L’investigation autour de l’alimentation est bien servie par les outils livrés avec l’OS.
- Elle ne se met pas en veille :
powercfg /requestsliste les processus et pilotes qui ont émis une demande d’alimentation. « L’application a oublié de leverSetThreadExecutionState» apparaît ici aussi. - Elle se réveille toute seule :
powercfg /lastwakemontre la raison du réveil le plus récent, etpowercfg /waketimersmontre les minuteries actuellement réservées pour réveiller la machine. - Qualité de Modern Standby :
powercfg /sleepstudygénère un rapport de consommation d’énergie et d’activité par intervalle de veille.9 - Confirmer la chronologie : la source Kernel-Power dans le journal d’événements (Système) conserve des enregistrements d’entrée en veille et de reprise. Les apparier au journal de l’application vous laisse confirmer objectivement si « il y a eu une reprise juste avant l’erreur ».
flowchart TB
accTitle: Correspondance des symptômes d'alimentation aux commandes d'investigation
accDescr: Pour un symptôme de non-veille, trouvez qui détient une demande d'alimentation avec powercfg /requests ; pour un symptôme de réveil spontané, trouvez la raison du réveil avec /lastwake et /waketimers ; pour une chronologie, utilisez Kernel-Power dans le journal d'événements
s1["Elle ne se met pas en veille"] --> c1["powercfg /requests"]
s2["Elle se réveille toute seule"] --> c2["powercfg /lastwake et /waketimers"]
s3["Vouloir confirmer la chronologie"] --> c3["Kernel-Power dans le journal d'événements"]
c1 -.-> note["Une suppression de veille oubliée apparaît aussi"]
Figure 9 : les symptômes correspondent aux commandes d’investigation en trois familles. Confirmez d’abord « vient-elle de se mettre en veille », puis scindez.
Dans le traitement des tickets, demander d’abord « le PC était-il en veille juste avant (ont-ils fermé le capot) » accélère grandement l’isolement.
7. Résumé
- La veille ne peut pas être refusée. La notification préalable (PBT_APMSUSPEND) est au mieux effort avec environ 2 secondes de grâce, et elle ne vient pas en urgence. Placez la conception principale du côté de la reprise.
- Les notifications de reprise sont PBT_APMRESUMEAUTOMATIC (à la reprise depuis la suspension) + PBT_APMRESUMESUSPEND (sur une action de l’utilisateur). Gardez la reconnexion pilotée par erreur sur le chemin principal pour le cas où la notification n’arrive pas.
- Partez du principe que les connexions et les handles ne survivent pas à travers la reprise, et implémentez l’ensemble en trois pièces d’une reconnexion idempotente + backoff exponentiel + un keepalive.
- Mettez une garde contre les « écarts anormaux » sur le travail basé sur le temps écoulé. Concevez le travail planifié en partant du principe qu’il ne s’exécute pas pendant la veille.
- Pour les intervalles qui ne doivent pas être interrompus, supprimez la veille explicitement avec
SetThreadExecutionStateou une demande d’alimentation, et levez-la toujours lorsque vous avez fini. - L’investigation est
powercfg(/requests, /lastwake, /sleepstudy) et le journal d’événements Kernel-Power. Dans le traitement des tickets, demandez d’abord « s’est-il mis en veille juste avant ».
Du point de vue de l’application, la veille est un événement dans lequel « le temps saute sans avertissement, les connexions avec l’entourage sont coupées, puis ça revient ». Que vous ayez tissé cela dans la conception comme une part de la vie quotidienne, plutôt que comme une situation anormale, est ce qui sépare la stabilité d’une application métier à l’ère du portable.
Articles connexes
- Pièges des applications de communication série — de la reconnexion à la conception des journaux
- L’arrêt de Windows vu depuis l’application — survivre correctement aux notifications de fermeture, aux redémarrages et aux coupures d’alimentation
- Qu’est-ce que le mode Efficacité de Windows ? — L’icône de feuille verte et comment le désactiver
- Pourquoi préférer l’attente sur événement à Sleep(1) sous Windows
- Ce qu’est vraiment « Ne répond pas » — comment Windows décide qu’une application est bloquée, et comment concevoir des applications qui ne le sont pas
Domaines de conseil associés
KomuraSoft LLC prend en charge l’investigation des causes profondes de bugs tels que « la communication se rompt après la reprise depuis la veille » et « la connexion au périphérique tombe après le déjeuner », le rétrofit d’une logique de reconnexion et de la gestion des événements d’alimentation sur des applications existantes, et les revues de conception d’applications métier et de logiciels de contrôle d’équipement qui supposent un fonctionnement sur portable.
- Investigation de bugs et analyse des causes
- Développement d’applications Windows
- Conseil technique et revue de conception
- Contact
Références
-
Microsoft Learn, PBT_APMSUSPEND event. Sur le fait qu’il s’agit de l’événement qui arrive juste avant que l’ordinateur n’entre dans l’état de suspension ; sur le fait que l’application est censée terminer le travail nécessaire pour enregistrer les données ; et sur le fait que le système accorde environ 2 secondes pour traiter cette notification, une application qui continue au-delà étant sujette à interruption. ↩ ↩2
-
Microsoft Learn, System Power Management Events. Sur le système qui diffuse à l’avance les changements de mode de fonctionnement tels que la veille ; sur PBT_APMSUSPEND qui est notifié avant la veille d’inactivité afin que vous puissiez vous préparer en fermant des fichiers et en enregistrant des données ; sur une suspension d’urgence (batterie critique et analogues) qui ne donne aucune notification préalable ; sur le fait que le traitement de ce message est autorisé un maximum de 2 secondes par application et est coupé après le délai d’expiration ; et sur le fait que chaque application est notifiée à la reprise. ↩ ↩2 ↩3
-
Microsoft Learn, PBT_APMRESUMESUSPEND event. Sur le fait que ceci est envoyé après PBT_APMRESUMEAUTOMATIC lors d’une reprise initiée par l’utilisateur ou lorsqu’une entrée utilisateur est détectée ensuite ; sur le fait que seul PBT_APMRESUMEAUTOMATIC est envoyé pour une reprise d’une cause externe telle qu’un réveil distant ; et sur le fait que l’application est censée rouvrir les fichiers fermés au moment de la veille et se préparer à l’entrée utilisateur. ↩ ↩2
-
Microsoft Learn, WM_POWERBROADCAST message. Sur le fait que PBT_APMRESUMEAUTOMATIC est toujours envoyé à la reprise, avec PBT_APMRESUMESUSPEND envoyé aussi lors d’une reprise depuis une entrée utilisateur ; sur le fait que ce message ne distingue pas le type d’état basse consommation ; sur le fait que les détails des transitions d’état d’alimentation sont enregistrés dans le journal d’événements système ; et sur l’appel à SetThreadExecutionState pour empêcher le système d’entrer dans un état basse consommation. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, SetThreadExecutionState function (winbase.h). Sur le fait que ES_SYSTEM_REQUIRED et ES_DISPLAY_REQUIRED peuvent supprimer la veille d’inactivité du système et l’extinction de l’affichage ; et sur le fait de déclarer une suppression continue avec ES_CONTINUOUS et de la lever en appelant ES_CONTINUOUS seul lorsque vous avez fini. ↩ ↩2
-
Microsoft Learn, Prepare software for modern standby. Sur le Desktop Activity Moderator (DAM) qui met en pause les applications de bureau dès la première étape de la transition vers Modern Standby ; et sur le système qui passe ensuite par étapes dans une phase basse consommation et une phase de résilience, seuls les composants autorisés tournant par intermittence. ↩ ↩2
-
Microsoft Learn, RegisterSuspendResumeNotification function (winuser.h). Sur le fait qu’il s’agit de l’API qui s’enregistre pour recevoir les notifications de suspension/reprise, et sur le fait de spécifier DEVICE_NOTIFY_CALLBACK afin qu’une application sans fenêtre ou un service puisse recevoir la notification via un callback en plus de la livraison de message à un handle de fenêtre. ↩ ↩2
-
Microsoft Learn, PowerSetRequest function (winbase.h). Sur le fait de pouvoir définir un type de demande tel que maintien éveillé du système ou de l’affichage sur un objet de demande d’alimentation créé avec PowerCreateRequest ; sur le fait de pouvoir attacher une chaîne de motif de diagnostic ; et sur le fait que les demandes d’alimentation en cours sont énumérables avec powercfg /requests. ↩ ↩2
-
Microsoft Learn, Modern standby SleepStudy. Sur le rapport généré par powercfg /sleepstudy qui vous laisse inspecter, par intervalle Modern Standby, la consommation d’énergie, l’activité et la raison du réveil (bouton d’alimentation, entrée utilisateur, une minuterie de réveil, etc.). ↩
Articles associés
Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.
DllMain et le verrou du chargeur — la vraie raison pour laquelle on vous dit de « ne rien faire dans l'initialisation d'une DLL »
Pourquoi il ne faut pas appeler LoadLibrary ni synchroniser avec d'autres threads depuis DllMain. En s'appuyant sur les sources primaires...
Ce qu'est vraiment « Ne répond pas » — comment Windows décide qu'une application est bloquée, et comment concevoir des applications qui ne le sont pas
Le « Ne répond pas » de Windows est un mécanisme dans lequel l'OS juge qu'une fenêtre n'a pas récupéré de message pendant 5 secondes et l...
L'API du pool de threads Win32 — de la concurrence sans créer de threads, via CreateThreadpoolWork
Vous semez des appels CreateThread partout dans votre code natif ? Cet article explique l'API du pool de threads Win32 refondue sous Vist...
Tubes nommés en pratique — l'IPC standard de Windows, de la conception à la sécurité
Guide pratique des tubes nommés, mécanisme standard de communication inter-processus sous Windows. Cet article organise, à partir des sou...
Réveils parasites — pourquoi les variables de condition se réveillent « sans avoir été notifiées » et comment attendre correctement sous Windows
L'attente d'une variable de condition peut revenir même lorsqu'aucune notification n'est arrivée (un réveil parasite). Cet article expliq...
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.
- Une application peut-elle apprendre la veille à l'avance et la refuser ?
- Sous Windows actuel, vous pouvez recevoir la notification, mais vous ne pouvez pas la refuser. Juste avant la veille, un message WM_POWERBROADCAST délivre un événement PBT_APMSUSPEND, et ici vous pouvez vous préparer en fermant des fichiers et en enregistrant l'état, mais le temps accordé au traitement est d'environ 2 secondes par application, et si vous le dépassez le système poursuit sans attendre. Lors d'une suspension d'urgence telle qu'une batterie critiquement faible, la notification préalable elle-même n'arrive pas. Une conception qui « doit terminer avant la veille » ne tient donc pas ; il faut une conception capable de se rétablir à la reprise, peu importe le moment de la coupure. Pour un intervalle de travail que vous ne voulez vraiment pas interrompre par la veille, supprimez-la explicitement avec SetThreadExecutionState ou une demande d'alimentation (PowerSetRequest).
- Comment détecter que la machine a repris ?
- Si l'application a une fenêtre, gérez WM_POWERBROADCAST. À la reprise depuis la suspension, PBT_APMRESUMEAUTOMATIC arrive, et si la reprise a été causée par une action de l'utilisateur (le bouton d'alimentation ou une touche), PBT_APMRESUMESUSPEND le suit. Une reprise sans surveillance qui se remet en veille immédiatement ne délivre que PBT_APMRESUMEAUTOMATIC, donc la scission de base consiste à placer le travail requis tel que la reconnexion du côté PBT_APMRESUMEAUTOMATIC et le travail visible par l'utilisateur tel que les mises à jour d'écran du côté PBT_APMRESUMESUSPEND. Les services sans fenêtre et les applications console peuvent recevoir les mêmes notifications via un callback en utilisant RegisterSuspendResumeNotification avec DEVICE_NOTIFY_CALLBACK.
- Puis-je garder l'application en cours d'exécution pendant la veille ?
- En règle générale, non. Pendant la veille, l'exécution du CPU elle-même s'arrête (sur une machine Modern Standby, les applications de bureau sont mises en pause par le Desktop Activity Moderator), et le code de l'application ne s'exécute pas. Il y a deux choix. L'un consiste à supprimer la veille uniquement pendant que le travail est en cours. Spécifier ES_SYSTEM_REQUIRED avec SetThreadExecutionState, ou émettre une demande d'alimentation avec PowerCreateRequest/PowerSetRequest, supprime la veille automatique d'inactivité pendant cet intervalle (vous pouvez le confirmer avec powercfg /requests). Cela ne peut toujours pas arrêter une action de veille explicite telle que l'utilisateur qui ferme le capot, donc vous devez encore être prêt pour la reprise même pendant que la suppression est active. L'autre consiste à accepter la veille et à concevoir pour « rattraper après la reprise ». Pour un travail planifié tel qu'un lot nocturne, vous pouvez aussi réveiller le PC avec « Réveiller l'ordinateur pour exécuter cette tâche » du Planificateur de tâches. Un travail qui a vraiment besoin de tourner en continu appartient à un serveur ou à un service configuré pour ne pas se mettre en veille.
- Pourquoi les connexions TCP et les ports série cessent-ils de fonctionner après la reprise ?
- Parce que les adaptateurs réseau et les périphériques USB passent aussi dans un état basse consommation pendant la veille. La connexion TCP a déjà été abandonnée par l'autre côté ou par un délai d'expiration NAT ou de pare-feu, et l'envoi/réception après la reprise renvoie une erreur (vous ne le remarquez souvent que lorsqu'elle se produit). Les adaptateurs USB-série et analogues sont parfois traités comme un retrait puis une réinsertion de périphérique à la reprise, et le handle que vous aviez ouvert devient invalide. Dans les deux cas, la bonne hypothèse est que « les handles et les connexions ne survivent pas à travers la reprise », et la bonne réponse est d'implémenter une logique de reconnexion qui reconstruit la connexion à une notification de reprise ou à une erreur de communication. Combiner un keepalive périodique avec des nouvelles tentatives qui utilisent un backoff exponentiel en cas d'échec est le schéma établi.
- Comment investiguer une veille inattendue ou une reprise inattendue ?
- La commande powercfg est le premier outil. Dans le sens « elle ne se met pas en veille », powercfg /requests liste quels processus et pilotes ont émis une demande d'alimentation qui bloque la veille. Dans le sens « elle se réveille toute seule », powercfg /lastwake montre la raison du réveil le plus récent et powercfg /waketimers montre les minuteries actuellement réservées pour réveiller la machine. Sur une machine Modern Standby, powercfg /sleepstudy produit un rapport de consommation et d'activité pendant la veille. L'historique de veille et de reprise est aussi enregistré dans le journal d'événements (la source Kernel-Power dans le journal Système), donc vous pouvez confirmer sur une chronologie « quand elle s'est mise en veille, et quand et pourquoi elle s'est réveillée ».
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.