App che si rompono alla ripresa dalla sospensione — Come funzionano gli eventi di alimentazione di Windows e come costruire app aziendali che li sopravvivono
· Go Komura · Windows, Gestione dell'alimentazione, Sviluppo Windows, Applicazioni aziendali, Controllo dispositivi, Risoluzione dei problemi, Win32 API
«Ho chiuso il portatile, l’ho aperto la mattina dopo, e l’app aziendale era piena di errori.» «L’app di monitoraggio delle attrezzature perde dati solo dopo pranzo.» «Lo strumento residente che esporta su Excel a volte si ferma su un errore di connessione.» — Questi ticket condividono un unico sospettato. La sospensione.
Le app aziendali dell’epoca in cui i PC desktop erano il mainstream erano scritte sull’assunto non dichiarato che «il PC resta acceso». Il campo di battaglia principale oggi è il portatile, e per default va in sospensione dopo pochi minuti di inattività. Su una macchina capace di Modern Standby, le semantiche stesse della sospensione sono cambiate rispetto al modello tradizionale. Rivolto agli sviluppatori che scrivono app aziendali e software di controllo delle attrezzature su Windows, questo articolo organizza, a partire dalle fonti primarie, ciò che il sistema operativo notifica a un’app prima e dopo la sospensione, cosa si rompe, e come scrivere un’app che sopravvive alla ripresa.
1. Prima di tutto, la conclusione
- La sospensione è un evento che l’app «non ha diritto di rifiutare». Viene notificata poco prima con
WM_POWERBROADCAST(PBT_APMSUSPEND), ma il periodo di grazia è di circa 2 secondi, e in una sospensione di emergenza la notifica non arriva nemmeno.12 - Alla ripresa dalla sospensione arriva PBT_APMRESUMEAUTOMATIC, e su una ripresa avviata dall’utente arriva anche PBT_APMRESUMESUSPEND. Il lavoro necessario come la riconnessione appartiene di regola al primo. Le transizioni dentro e fuori dall’idle a basso consumo di Modern Standby, però, non sempre si allineano a queste notifiche, quindi trattatele come un aiuto.34
- Progettate nell’assunto che connessioni TCP, porte seriali e handle di dispositivo non sopravvivono attraverso la ripresa. La logica di riconnessione che li ricostruisce su una notifica di ripresa o su un errore di comunicazione è l’evento principale.
- Fate attenzione ai timer e alla gestione del tempo. Il lavoro periodico si ferma durante la sospensione, e come scatta subito dopo la ripresa differisce per API di timer e runtime. Succede anche un «salto enorme del tempo trascorso», quindi l’approccio sicuro è ricostruire la pianificazione alla ripresa.
- Sopprimete la sospensione in modo esplicito per gli intervalli che non volete attraversare in sospensione. Usate
SetThreadExecutionState(ES_SYSTEM_REQUIRED) o una power request (PowerSetRequest), e cancellatela sempre a lavoro finito.45 - Su una macchina Modern Standby il sistema gira ancora in modo intermittente durante la sospensione, ma le app desktop sono in pausa. Non potete tenere l’aspettativa che «la nostra app dovrebbe continuare a girare durante la sospensione».6
- Gli strumenti standard di indagine sono
powercfg(/requests, /lastwake, /sleepstudy) e Kernel-Power nel registro eventi.
2. Cosa succede intorno alla sospensione — Il flusso degli eventi di alimentazione
Il sistema operativo trasmette i cambiamenti di stato di alimentazione a ogni app come un messaggio WM_POWERBROADCAST.2 Ci sono tre eventi principali che coinvolgono sospensione e ripresa.
| Evento | Significato |
|---|---|
| PBT_APMSUSPEND | Sta per entrare in sospensione (l’ultima occasione per prepararsi) |
| PBT_APMRESUMEAUTOMATIC | Ripresa (arriva sempre alla ripresa) |
| PBT_APMRESUMESUSPEND | Ripresa causata da un’azione dell’utente (questa è condizionale) |
PBT_APMSUSPEND è la notifica subito prima della sospensione, e qui potete prepararvi chiudendo i file e salvando lo stato. Ci sono però due condizioni. Prima, il tempo concesso per l’elaborazione è di circa 2 secondi per app, e se lo superate il sistema procede senza aspettare.1 Seconda, in una sospensione di emergenza come una batteria criticamente scarica, va in sospensione immediatamente senza notifica anticipata.2 Un progetto che «deve finire prima della sospensione» non regge. Trattate la notifica come un’occasione per «farlo se ci riuscite», e mettete il lavoro principale sul lato della ripresa.
Il lato della ripresa è in due stadi. PBT_APMRESUMEAUTOMATIC arriva alla ripresa da una transizione di sospensione. In più, se la macchina è ripresa per un’azione dell’utente come il pulsante di accensione o la pressione di un tasto (o in seguito è stata rilevata la presenza dell’utente), segue PBT_APMRESUMESUSPEND. Viceversa, una ripresa non presidiata per un wake remoto in rete o per manutenzione consegna solo PBT_APMRESUMEAUTOMATIC.3 Questi due stadi sono di per sé un indizio su come spezzare il lavoro — fate il recupero meccanico come ricostruire le connessioni su PBT_APMRESUMEAUTOMATIC, e fate le azioni rivolte all’utente come gli aggiornamenti dello schermo o un prompt di nuovo login su PBT_APMRESUMESUSPEND.
sequenceDiagram
accTitle: Flusso di notifica per sospensione e ripresa
accDescr: PBT_APMSUSPEND arriva subito prima della sospensione con circa 2 secondi di grazia; alla ripresa, PBT_APMRESUMEAUTOMATIC arriva sempre, e PBT_APMRESUMESUSPEND segue solo per una ripresa avviata dall'utente
participant OS as OS
participant A as App
OS->>A: PBT_APMSUSPEND (circa 2 secondi di grazia)
A->>A: Salva lo stato e chiudi le connessioni
Note over OS: Sospensione (il codice non gira)
OS->>A: PBT_APMRESUMEAUTOMATIC (arriva alla ripresa)
A->>A: Riconnetti e ripristina lo stato
OS->>A: PBT_APMRESUMESUSPEND (solo ripresa avviata dall'utente)
A->>A: Aggiornamenti dello schermo e altro lavoro rivolto all'utente
Figura 1: Le notifiche sono solo «una parola poco prima, e una o due parole dopo la ripresa». La protagonista del recupero è il lavoro sul lato della ripresa.
flowchart TB
accTitle: Differenza tra sospensione ordinaria e sospensione di emergenza
accDescr: La sospensione ordinaria consegna PBT_APMSUSPEND poco prima con circa 2 secondi per prepararsi, ma una sospensione di emergenza come batteria critica si ferma senza notifica anticipata, quindi un progetto che dipende dalla notifica anticipata non regge
n2["Sospensione ordinaria"] --> pre["PBT_APMSUSPEND (circa 2 s di grazia)"]
pre --> s1["Preparati, poi fermati"]
e2["Sospensione di emergenza (batteria critica)"] --> s2["Stop senza notifica anticipata"]
s2 -.-> l2["Un progetto che assume che la notifica arriverà non regge"]
Figura 2: Una sospensione di emergenza arriva senza preavviso. Quindi la preparazione è «un extra se ci riuscite», e il lavoro principale va sul lato della ripresa.
Si noti che WM_POWERBROADCAST non distingue il tipo di stato a basso consumo (sospensione versus ibernazione).4 L’astrazione giusta per l’app è trattarlo come un solo tipo di evento: «si è fermato, ed è tornato». I servizi senza finestra e le app console possono ricevere le stesse notifiche usando RegisterSuspendResumeNotification in forma di callback (DEVICE_NOTIFY_CALLBACK).7
flowchart TB
accTitle: Suddivisione del lavoro sui due stadi di ripresa
accDescr: Mettete il recupero meccanico come la riconnessione su PBT_APMRESUMEAUTOMATIC, che arriva alla ripresa; mettete il lavoro rivolto all'utente come gli aggiornamenti dello schermo o un prompt di nuovo login su PBT_APMRESUMESUSPEND, che arriva solo su una ripresa avviata dall'utente
ra["PBT_APMRESUMEAUTOMATIC (alla ripresa)"] --> m["Recupero meccanico"]
rs["PBT_APMRESUMESUSPEND (ripresa avviata dall'utente)"] --> u["Lavoro rivolto all'utente"]
m -.-> m1["Riconnetti e riapri gli handle"]
u -.-> u1["Aggiornamenti dello schermo e prompt di login"]
Figura 3: Il secondo non arriva in una ripresa non presidiata, quindi mettere il recupero necessario sul secondo lo farà perdere.
3. Modern Standby — Il significato di «sospensione» è cambiato
Un altro fatto moderno da interiorizzare è Modern Standby. La sospensione S3 tradizionale era un modello semplice che «fermava il sistema nel suo insieme»; la sospensione su una macchina Modern Standby è un modello da smartphone in cui il sistema continua a girare in modo intermittente dopo che lo schermo si spegne.
Ciò che conta per un’app aziendale qui è che le app desktop sono messe in pausa dal Desktop Activity Moderator (DAM) al primo stadio dell’ingresso in sospensione.6 Il sistema stesso gira ancora di tanto in tanto per tenere su la rete e ricevere notifiche, ma i componenti che ne beneficiano sono quelli che partecipano a questo meccanismo — il codice ordinario di un’app desktop non gira. Quindi dal punto di vista dello sviluppatore la conclusione è la stessa per Modern Standby e per S3 — progettate nell’assunto che il vostro codice non gira durante la sospensione.
flowchart TB
accTitle: Differenza tra sospensione tradizionale e Modern Standby
accDescr: La sospensione S3 tradizionale ferma il sistema nel suo insieme, mentre sotto Modern Standby il sistema gira ancora in modo intermittente dopo che lo schermo si spegne. Le app desktop sono messe in pausa dal DAM in entrambi i casi, quindi il codice dell'app non gira
s3["Sospensione S3 tradizionale: si ferma tutto il sistema"] --> conc["Il codice dell'app non gira"]
ms["Modern Standby: il sistema gira in modo intermittente"] --> dam["Le app desktop sono in pausa per il DAM"]
dam --> conc
Figura 4: Il modello è cambiato, ma per un’app desktop la conclusione è la stessa: «non potete girare durante la sospensione».
Un’altra cautela è quanto poco potete fare affidamento sulle notifiche. Sotto Modern Standby, le transizioni dentro e fuori dall’idle a basso consumo non si allineano alla transizione di sospensione tradizionale, e una connessione può già essere spezzata senza che una notifica sia mai arrivata. Trattate la notifica di ripresa come un aiuto, e mettete la riconnessione innescata dal rilevamento di errore (capitolo 5) sul percorso principale di recupero.
Un’altra differenza è la sensazione «scivolosa» del comportamento. Raggiungere le profondità della sospensione è a stadi, e i tempi di disconnessioni e fermate non sono netti come sotto S3. Anche la distinzione tra «lo schermo si è appena spento» e «è andato in sospensione» è difficile da vedere per l’utente, quindi quando prendete un sintomo dovete confermare «hanno chiuso il coperchio» e «per quanti minuti è rimasto inattivo».
4. Cosa si rompe — Sintomi classici
La connessione TCP è morta. Durante la sospensione, l’altra parte, NAT e i firewall trattano il vostro silenzio come un timeout e scartano la connessione. Peggio, il socket da questa parte non sa dell’errore, quindi fallisce solo quando inviate o ricevete dopo la ripresa. O peggio ancora, un’attesa di ricezione non dà mai errore (è per questo che serve un keepalive). Le connessioni a database e i WebSocket hanno la stessa forma.
Gli handle di porta seriale e di dispositivo USB diventano non validi. Un dispositivo collegato via USB può sembrare, alla ripresa, come se fosse stato «staccato e riallacciato» una volta, e l’handle che avevate aperto inizia a restituire errori. È il pattern tipico di un’app di controllo delle attrezzature che «ottiene un errore di comunicazione solo dopo pranzo». Il progetto di riconnessione è trattato anche nell’articolo sulla comunicazione seriale.
La continuità del tempo si spezza. Il lavoro guidato da timer come «sondare ogni 10 secondi» non scatta durante la sospensione. Come scatta subito dopo la ripresa (il lavoro scaduto scatta una volta immediatamente, non succede niente fino al periodo successivo, e così via) differisce per l’API di timer e il runtime che state usando, quindi non lasciate la gestione dei tick persi al comportamento implicito — l’approccio sicuro è ricostruire la pianificazione sulla notifica di ripresa. Inoltre, i calcoli di tempo trascorso (la differenza dal timestamp precedente) diventano all’improvviso «8 ore di valore», e i calcoli di media o i giudizi di timeout si rompono. Un lavoro pianificato come «esegui ogni notte alle 2» semplicemente non gira se il PC è in sospensione a quell’ora (svegliatelo con la funzione di riattivazione dalla sospensione dell’Utilità di pianificazione se vi serve).
flowchart TB
accTitle: Tre forme in cui la continuità del tempo si spezza
accDescr: Il lavoro periodico si ferma durante la sospensione e lo scatto post-ripresa differisce per API, quindi ricostruite la pianificazione alla ripresa; la differenza dal timestamp precedente diventa enorme dopo la ripresa, quindi mettete una guardia; il lavoro pianificato non gira se la macchina è in sospensione, quindi considerate la riattivazione dell'Utilità di pianificazione
t1["Lavoro periodico: si ferma"] -.-> g1["Ricostruisci la pianificazione"]
t2["Tempo trascorso: esplode"] -.-> g2["Guardia sulle differenze anomale"]
t3["Pianificato: non è mai partito"] -.-> g3["Riattivazione dalla sospensione"]
g1 ~~~ t2
g2 ~~~ t3
Figura 5: Scrivete la gestione di timer e tempo nell’assunto che «il tempo salta». Ognuna delle tre forme ha un tipo di contromisura.
flowchart TB
accTitle: Tre cose che si rompono attraverso la sospensione
accDescr: Attraverso la sospensione, una connessione TCP è stata scartata da un timeout dall'altra parte, l'handle di un dispositivo USB è invalidato come una riconnessione, e il lavoro basato sul tempo trascorso osserva un salto enorme. Recuperate ciascuna con riconnessione, riapertura e una guardia sulla differenza
sleep["Intervallo di sospensione"] --> tcp["TCP: il peer l'ha scartata"]
sleep --> more{"USB o tempo trascorso?"}
more --> usb["USB: handle non valido"]
more --> time["Tempo trascorso: un salto"]
tcp -.-> r1["Rileva + riconnetti"]
usb -.-> r2["Riapri il dispositivo"]
time -.-> r3["Guardia sulle differenze anomale"]
Figura 6: Ciò che si rompe cade in tre famiglie — «connessioni», «handle» e «continuità del tempo» — e ciascuna ha un tipo di recupero consolidato.
Ri-autenticazione alle risorse condivise. Unità di rete e VPN spesso devono essere ristabilite dopo la ripresa, e c’è una «valle di avvio» di pochi-decine di secondi subito dopo la ripresa in cui l’accesso fallisce. È più sicuro non ritentare tutto in una volta subito dopo la ripresa, ma aspettare un poco e ritentare a stadi.
5. Costruire app che sopravvivono alla ripresa
Il principio è una cosa sola. Assumete che «connessioni e handle non sopravvivono attraverso la sospensione», e strutturate l’app così da potervi sempre riprendere.
Rilevate la ripresa e recuperate. Quando la WM_POWERBROADCAST della finestra di primo livello riceve PBT_APMRESUMEAUTOMATIC, scartate le connessioni che tenete e ricostruitele. Il punto è non fare affidamento sulla sola notifica di ripresa. Notifiche perse e comunicazioni che avvengono prima della notifica sono entrambe reali, quindi accoppiatela sempre a un percorso che «riconnette quando viene rilevato un errore di comunicazione», e trattate la notifica di ripresa come un trigger che si limita ad avviare quello prima.
// 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);
}
Rendete il lavoro di riconnessione stesso idempotente (sicuro qualunque sia il numero di chiamate), ritentate in caso di fallimento con backoff esponenziale, e nello stato stazionario rilevate presto una connessione morta con un keepalive — mettete quelle tre cose insieme come un insieme, e sopravviverete non solo alla ripresa dalla sospensione ma anche a un breve calo di rete o a un reboot del dispositivo.
flowchart TB
accTitle: Progetto di riconnessione resistente alla ripresa
accDescr: La notifica di ripresa, un errore di comunicazione e un fallimento del keepalive confluiscono tutti nello stesso lavoro di riconnessione idempotente, che ritenta con backoff esponenziale in caso di fallimento
e1["Notifica di ripresa (PBT_APMRESUMEAUTOMATIC)"] --> r["Lavoro di riconnessione idempotente"]
e2["Rilevamento di errore di comunicazione"] --> r
e3["Fallimento del keepalive"] --> r
r --> ok{"Riuscito?"}
ok -->|"sì"| run["Di nuovo in funzionamento normale"]
ok -->|"no"| back["Ritenta dopo backoff esponenziale"]
back --> r
Figura 7: Concentrate la riconnessione in un unico percorso idempotente, ed entrate nella stessa strada dalla notifica di ripresa, dal rilevamento di errore o dal keepalive.
Rivedete la gestione del tempo. Per il lavoro che usa «il tempo trascorso dall’ultima volta», mettete una guardia che invalida l’intervallo quando rileva una differenza anormalmente grande (non piegatela in una media, non trattatela come un timeout). Misurare il tempo trascorso attraverso la ripresa richiede di tenere una distinzione tra un orologio che avanza durante la sospensione (tempo di calendario) e il tempo realmente speso sul lavoro.
Sopprimete la sospensione in modo esplicito per gli intervalli che non volete attraversare in sospensione. Durante un lavoro che non deve essere attraversato dalla sospensione — una migrazione di dati, una comunicazione continua con un dispositivo, e così via — potete tenere il sistema sveglio con SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED) (aggiungete ES_DISPLAY_REQUIRED se volete anche tenere lo schermo acceso).45 Un metodo più corretto è l’API delle power request (PowerCreateRequest + PowerSetRequest), che può attaccare una stringa di motivo, e powercfg /requests mostrerà allora «chi lo sta bloccando e perché».8 Si noti che la soppressione via SetThreadExecutionState è per thread, e la cancellate dallo stesso thread che l’ha impostata. Per un lavoro che cambia thread, come async/await, usate il lato power request, che è gestito da un handle. Ci sono cautele. Prima, ciò che queste sopprimono è la sospensione automatica da inattività. Non possono fermare un’azione esplicita dell’utente come chiudere il coperchio o scegliere Sospensione dal menu Start, quindi non potete saltare il progetto di riconnessione di questo capitolo nemmeno mentre la soppressione è attiva. Seconda, a batteria su una macchina Modern Standby, anche queste power request vengono tagliate qualche tempo dopo che scade il timeout di sospensione. Il lavoro che non può essere interrotto deve essere garantito dall’alimentazione AC o dalle operazioni.8 Terza, cancellatela sempre a lavoro finito. Una cancellazione mancata diventa un nuovo bug: «questo PC, per qualche motivo, non va in sospensione».
flowchart TB
accTitle: Due mezzi per sopprimere la sospensione
accDescr: Che usiate il comodo SetThreadExecutionState o l'API delle power request che può attaccare una stringa di motivo ed è visibile a un amministratore via powercfg, cancellatela sempre a lavoro finito
need["Intervallo di lavoro che non deve essere attraversato dalla sospensione"] --> a["SetThreadExecutionState"]
need --> b["Power request (PowerSetRequest)"]
a -.-> a1["Comodo — solo flag"]
b -.-> b1["Con un motivo — visibile in powercfg"]
a --> off["Cancellala sempre a lavoro finito"]
b --> off
Figura 8: Per entrambi i mezzi, «cancellarla a fine lavoro» è una condizione assoluta. Una power request che può rendere visibile il motivo è più gentile verso le operazioni.
I servizi e le app senza finestra ricevono notifiche a callback con RegisterSuspendResumeNotification (DEVICE_NOTIFY_CALLBACK).7 Se il funzionamento continuo è un requisito genuino, la soluzione di fondo è rivedere un progetto che tiene il lavoro residente su un PC client che va in sospensione, e spostarlo sul lato server o su una macchina operata senza sospensione.
6. Indagine — powercfg e il registro eventi
L’indagine intorno all’alimentazione è ben servita dagli strumenti che arrivano con il sistema operativo.
- Non va in sospensione:
powercfg /requestselenca i processi e i driver che hanno emesso una power request. «L’app ha dimenticato di cancellareSetThreadExecutionState» compare anche qui. - Si sveglia da sola:
powercfg /lastwakemostra il motivo della riattivazione più recente, epowercfg /waketimersmostra i timer attualmente riservati per svegliare la macchina. - Qualità di Modern Standby:
powercfg /sleepstudygenera un rapporto di consumo energetico e attività per intervallo di sospensione.9 - Confermare la linea temporale: La fonte Kernel-Power nel registro eventi (Sistema) tiene i record dell’ingresso in sospensione e della ripresa. Confrontarli con il log dell’app vi consente di confermare in modo oggettivo se «c’è stata una ripresa appena prima dell’errore».
flowchart TB
accTitle: Mappatura dei sintomi di problemi di alimentazione sui comandi di indagine
accDescr: Per un sintomo di non-va-in-sospensione, trovate chi tiene una power request con powercfg /requests; per un sintomo di si-sveglia-da-sola, trovate il motivo della riattivazione con /lastwake e /waketimers; per una linea temporale, usate Kernel-Power nel registro eventi
s1["Non va in sospensione"] --> c1["powercfg /requests"]
s2["Si sveglia da sola"] --> c2["powercfg /lastwake e /waketimers"]
s3["Voglio confermare la linea temporale"] --> c3["Kernel-Power nel registro eventi"]
c1 -.-> note["Compare anche una soppressione della sospensione dimenticata"]
Figura 9: I sintomi si mappano sui comandi di indagine in tre famiglie. Prima confermate «è appena andato in sospensione», poi spezzate.
Nella gestione dei ticket, già chiedere per prima cosa «il PC era in sospensione poco prima (hanno chiuso il coperchio)» accelera moltissimo l’isolamento.
7. Riepilogo
- La sospensione non si può rifiutare. La notifica anticipata (PBT_APMSUSPEND) è best-effort con circa 2 secondi di grazia, e non arriva in un’emergenza. Mettete il progetto principale sul lato della ripresa.
- Le notifiche di ripresa sono PBT_APMRESUMEAUTOMATIC (alla ripresa dalla sospensione) + PBT_APMRESUMESUSPEND (su un’azione dell’utente). Tenete la riconnessione innescata dagli errori sul percorso principale per il caso in cui la notifica non arrivi.
- Assumete che connessioni e handle non sopravvivono attraverso la ripresa, e implementate l’insieme a tre pezzi di una riconnessione idempotente + backoff esponenziale + un keepalive.
- Mettete una guardia contro le «differenze anomale» sul lavoro basato sul tempo trascorso. Progettate il lavoro pianificato nell’assunto che non gira durante la sospensione.
- Per gli intervalli che non devono essere attraversati dalla sospensione, sopprimete la sospensione in modo esplicito con
SetThreadExecutionStateo una power request, e cancellatela sempre a fine lavoro. - L’indagine è
powercfg(/requests, /lastwake, /sleepstudy) e il registro eventi Kernel-Power. Nella gestione dei ticket, chiedete per prima cosa «è andato in sospensione poco prima».
Dal punto di vista dell’app, la sospensione è un evento in cui «il tempo salta senza preavviso, le connessioni con l’intorno sono tagliate, e poi torna». Se avete intessuto questo nel progetto come parte del quotidiano, piuttosto che come una situazione anomala, è ciò che separa la stabilità di un’app aziendale nell’era del portatile.
Articoli correlati
- Pitfall delle app di comunicazione seriale - Riconnessione e progettazione dei log
- Lo spegnimento di Windows visto dalla tua app — Sopravvivere correttamente a notifiche di uscita, riavvii e interruzioni di corrente
- Che cos’è la modalità efficienza di Windows? - L’icona della foglia verde e come disattivarla
- Perché preferire l’attesa su evento a Sleep(1) su Windows
- Che cos’è davvero «Non risponde» — Come Windows decide che un’app è bloccata, e come progettare app che non lo sono
Aree di consulenza correlate
KomuraSoft LLC si occupa di indagini sulle cause di bug come «la comunicazione si spezza dopo la ripresa dalla sospensione» e «la connessione al dispositivo cade dopo pranzo», del retrofit di logica di riconnessione e gestione degli eventi di alimentazione su app esistenti, e di revisioni di progetto di app aziendali e software di controllo delle attrezzature che assumono il funzionamento su portatile.
- Indagine sui difetti e analisi delle cause
- Sviluppo di app Windows
- Consulenza tecnica e revisione della progettazione
- Contattaci
Riferimenti
-
Microsoft Learn, PBT_APMSUSPEND event. Sul fatto che questo è l’evento che arriva subito prima che il computer entri nello stato di sospensione; sul fatto che l’app è tenuta a finire il lavoro necessario per salvare i dati; e sul fatto che il sistema concede circa 2 secondi per gestire questa notifica, e un’app che continua oltre è soggetta a interruzione. ↩ ↩2
-
Microsoft Learn, System Power Management Events. Sul fatto che il sistema trasmette in anticipo i cambiamenti di modalità operativa come la sospensione; sul fatto che PBT_APMSUSPEND viene notificato prima della sospensione da inattività così da potersi preparare chiudendo i file e salvando i dati; sul fatto che una sospensione di emergenza (batteria critica e simili) non dà notifica anticipata; sul fatto che la gestione di questo messaggio è concessa per un massimo di 2 secondi per app e viene interrotta dopo il timeout; e sul fatto che ogni app viene notificata alla ripresa. ↩ ↩2 ↩3
-
Microsoft Learn, PBT_APMRESUMESUSPEND event. Sul fatto che viene inviato dopo PBT_APMRESUMEAUTOMATIC su una ripresa avviata dall’utente o quando in seguito viene rilevato un input dell’utente; sul fatto che per una ripresa da una causa esterna come un wake remoto viene inviato solo PBT_APMRESUMEAUTOMATIC; e sul fatto che l’app è tenuta a riaprire i file chiusi al momento della sospensione e a prepararsi all’input dell’utente. ↩ ↩2
-
Microsoft Learn, WM_POWERBROADCAST message. Sul fatto che PBT_APMRESUMEAUTOMATIC viene sempre inviato alla ripresa, con PBT_APMRESUMESUSPEND inviato anche su una ripresa da input dell’utente; sul fatto che questo messaggio non distingue il tipo di stato a basso consumo; sul fatto che i dettagli delle transizioni di stato di alimentazione sono registrati nel registro eventi di sistema; e sul fatto che si chiama SetThreadExecutionState per impedire al sistema di entrare in uno stato a basso consumo. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, SetThreadExecutionState function (winbase.h). Sul fatto che ES_SYSTEM_REQUIRED e ES_DISPLAY_REQUIRED possono sopprimere la sospensione da inattività del sistema e lo spegnimento dello schermo; e sul fatto che si dichiara una soppressione continua con ES_CONTINUOUS e la si cancella chiamando ES_CONTINUOUS da solo a lavoro finito. ↩ ↩2
-
Microsoft Learn, Prepare software for modern standby. Sul fatto che il Desktop Activity Moderator (DAM) mette in pausa le app desktop al primo stadio della transizione in Modern Standby; e sul fatto che il sistema poi si muove a stadi in una fase a basso consumo e in una fase di resilienza, con solo i componenti consentiti che girano in modo intermittente. ↩ ↩2
-
Microsoft Learn, RegisterSuspendResumeNotification function (winuser.h). Sul fatto che questa è l’API che registra per ricevere le notifiche di sospensione/ripresa, e sul fatto che specificare DEVICE_NOTIFY_CALLBACK consente a un’app o a un servizio senza finestra di ricevere la notifica tramite un callback, oltre alla consegna del messaggio a un handle di finestra. ↩ ↩2
-
Microsoft Learn, PowerSetRequest function (winbase.h). Sul fatto che si può impostare un tipo di richiesta come system o display stay-awake su un oggetto power-request creato con PowerCreateRequest; sul fatto che si può attaccare una stringa di motivo diagnostico; e sul fatto che le power request in essere sono enumerabili con powercfg /requests. ↩ ↩2
-
Microsoft Learn, Modern standby SleepStudy. Sul fatto che il rapporto generato da powercfg /sleepstudy consente di ispezionare, per intervallo di Modern Standby, consumo energetico, attività e il motivo della riattivazione (pulsante di accensione, input dell’utente, un wake timer, e così via). ↩
Articoli correlati
Articoli recenti con gli stessi tag per approfondire argomenti vicini.
DllMain e il loader lock — Il vero motivo per cui vi dicono di «non fare niente nell'inizializzazione della DLL»
Perché non dovete chiamare LoadLibrary o sincronizzarvi con altri thread da DllMain. A partire dalle fonti primarie, l'articolo spiega co...
Che cos'è davvero «Non risponde» — Come Windows decide che un'app è bloccata, e come progettare app che non lo sono
Il «Non risponde» di Windows è un meccanismo in cui il sistema operativo giudica che una finestra non ha prelevato un messaggio per 5 sec...
L'API thread pool Win32 — Concorrenza senza creare thread, tramite CreateThreadpoolWork
State spargendo chiamate CreateThread per tutto il codice nativo? Questo articolo spiega l'API thread pool Win32 ridisegnata in Vista — i...
Named pipe in pratica — L'IPC standard di Windows, dalla progettazione alla sicurezza
Guida pratica alle named pipe, il meccanismo standard di comunicazione tra processi su Windows. L'articolo organizza, a partire dalle fon...
Sveglie spurie — Perché le condition variable si svegliano «senza essere notificate» e come attendere correttamente su Windows
L'attesa di una condition variable può ritornare anche quando non è arrivata nessuna notifica (una sveglia spuria). L'articolo spiega, a ...
Argomenti correlati
Queste pagine collocano l’argomento in un contesto più ampio di servizi e decisioni.
Argomenti tecnici Windows
Portale su sviluppo Windows, analisi dei problemi e valorizzazione delle risorse esistenti.
Servizi collegati all’argomento
L’articolo è direttamente collegato ai servizi seguenti.
Sviluppo di applicazioni Windows
Applicazioni aziendali, integrazione di dispositivi e strumenti di comunicazione, dai requisiti allo sviluppo.
Domande frequenti
Domande che ricorrono nelle consulenze sull’argomento dell’articolo.
- Un'app può venire a conoscenza della sospensione in anticipo e rifiutarla?
- Su Windows attuale potete ricevere la notifica, ma non potete rifiutarla. Subito prima della sospensione, un messaggio WM_POWERBROADCAST consegna un evento PBT_APMSUSPEND, e qui potete prepararvi chiudendo i file e salvando lo stato, ma il tempo concesso per l'elaborazione è di circa 2 secondi per app, e se lo superate il sistema procede senza aspettare. In una sospensione di emergenza come una batteria criticamente scarica, la notifica anticipata stessa non arriva. Un progetto che «deve finire prima della sospensione» quindi non regge; serve un progetto che possa riprendersi alla ripresa, qualunque sia il momento del taglio. Per un tratto di lavoro che davvero non volete attraversare in sospensione, sopprimete la sospensione in modo esplicito con SetThreadExecutionState o una power request (PowerSetRequest).
- Come rilevo che la macchina è ripresa?
- Se l'app ha una finestra, gestite WM_POWERBROADCAST. Alla ripresa dalla sospensione arriva PBT_APMRESUMEAUTOMATIC, e se la ripresa è stata causata da un'azione dell'utente (il pulsante di accensione o la pressione di un tasto), segue PBT_APMRESUMESUSPEND. Una ripresa non presidiata che torna subito in sospensione consegna solo PBT_APMRESUMEAUTOMATIC, quindi la suddivisione di base è mettere il lavoro necessario come la riconnessione sul lato PBT_APMRESUMEAUTOMATIC e il lavoro rivolto all'utente come gli aggiornamenti dello schermo sul lato PBT_APMRESUMESUSPEND. I servizi senza finestra e le app console possono ricevere le stesse notifiche tramite un callback usando RegisterSuspendResumeNotification con DEVICE_NOTIFY_CALLBACK.
- Posso tenere l'app in esecuzione durante la sospensione?
- Di regola, no. Durante la sospensione, l'esecuzione della CPU stessa si ferma (su una macchina Modern Standby, le app desktop sono messe in pausa dal Desktop Activity Moderator), e il codice dell'app non gira. Ci sono due scelte. Una è sopprimere la sospensione solo mentre il lavoro è in corso. Specificare ES_SYSTEM_REQUIRED con SetThreadExecutionState, o emettere una power request con PowerCreateRequest/PowerSetRequest, sopprime la sospensione automatica da inattività per quell'intervallo (lo potete confermare con powercfg /requests). Questo non può comunque fermare un'azione esplicita di sospensione come la chiusura del coperchio da parte dell'utente, quindi dovete restare pronti alla ripresa anche mentre la soppressione è attiva. L'altra è accettare la sospensione e progettare per «recuperare dopo la ripresa». Per un lavoro pianificato come un batch notturno, potete anche svegliare il PC con «Riattiva il computer per eseguire questa attività» dell'Utilità di pianificazione. Il lavoro che deve davvero girare in continuo appartiene a un server o a un servizio configurato per non sospendersi.
- Perché le connessioni TCP e le porte seriali smettono di funzionare dopo la ripresa?
- Perché anche gli adattatori di rete e i dispositivi USB scendono in uno stato a basso consumo durante la sospensione. La connessione TCP è già stata scartata dall'altra parte o da un timeout NAT o del firewall, e invio/ricezione dopo la ripresa restituiscono un errore (spesso non ve ne accorgete finché non dà errore). Adattatori USB-seriale e simili a volte sono trattati come una rimozione e reinserimento del dispositivo alla ripresa, e l'handle che avevate aperto diventa non valido. Per entrambi, l'assunzione corretta è che «handle e connessioni non sopravvivono attraverso la ripresa», e la risposta giusta è implementare una logica di riconnessione che ricostruisce la connessione su una notifica di ripresa o su un errore di comunicazione. Combinare un keepalive periodico con retry che usano backoff esponenziale in caso di fallimento è il pattern consolidato.
- Come indago una sospensione inattesa o una ripresa inattesa?
- Il comando powercfg è il primo strumento. Nella direzione «non va in sospensione», powercfg /requests elenca quali processi e driver hanno emesso una power request che sta bloccando la sospensione. Nella direzione «si sveglia da sola», powercfg /lastwake mostra il motivo della riattivazione più recente e powercfg /waketimers mostra i timer attualmente riservati per svegliare la macchina. Su una macchina Modern Standby, powercfg /sleepstudy produce un rapporto di consumo e attività durante la sospensione. La cronologia di sospensione e ripresa è registrata anche nel registro eventi (la fonte Kernel-Power nel registro Sistema), quindi potete confermare su una linea temporale «quando è andata in sospensione, e quando e perché si è svegliata».
Profilo dell’autore
Pagina di presentazione dell’autore dell’articolo.
Go Komura
Rappresentante di KomuraSoft LLC
Specializzato nello sviluppo di software Windows, nella consulenza tecnica e nell’analisi dei malfunzionamenti, soprattutto nei progetti con sistemi esistenti e guasti difficili da riprodurre.