App che si rompono alla ripresa dalla sospensione — Come funzionano gli eventi di alimentazione di Windows e come costruire app aziendali che li sopravvivono

· · 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.

Flusso di notifica per sospensione e ripresaPBT_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'utenteAppOSAppOSSospensione (il codice non gira)PBT_APMSUSPEND (circa 2 secondi di grazia)Salva lo stato e chiudi le connessioniPBT_APMRESUMEAUTOMATIC (arriva alla ripresa)Riconnetti e ripristina lo statoPBT_APMRESUMESUSPEND (solo ripresa avviata dall'utente)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.

Differenza tra sospensione ordinaria e sospensione di emergenzaLa 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 reggeSospensione ordinariaPBT_APMSUSPEND (circa 2 s di grazia)Preparati, poi fermatiSospensione di emergenza (batteria critica)Stop senza notifica anticipataUn 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

Suddivisione del lavoro sui due stadi di ripresaMettete 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'utentePBT_APMRESUMEAUTOMATIC (alla ripresa)Recupero meccanicoPBT_APMRESUMESUSPEND (ripresa avviata dall'utente)Lavoro rivolto all'utenteRiconnetti e riapri gli handleAggiornamenti 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.

Differenza tra sospensione tradizionale e Modern StandbyLa 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 giraSospensione S3 tradizionale: si ferma tutto il sistemaIl codice dell'app non giraModern Standby: il sistema gira in modo intermittenteLe app desktop sono in pausa per il DAM

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).

Tre forme in cui la continuità del tempo si spezzaIl 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 pianificazioneLavoro periodico: si fermaRicostruisci la pianificazioneTempo trascorso: esplodeGuardia sulle differenze anomalePianificato: non è mai partitoRiattivazione dalla sospensione

Figura 5: Scrivete la gestione di timer e tempo nell’assunto che «il tempo salta». Ognuna delle tre forme ha un tipo di contromisura.

Tre cose che si rompono attraverso la sospensioneAttraverso 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 differenzaIntervallo di sospensioneTCP: il peer l'ha scartataUSB o tempo trascorso?USB: handle non validoTempo trascorso: un saltoRileva + riconnettiRiapri il dispositivoGuardia 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.

Progetto di riconnessione resistente alla ripresaLa 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 fallimentonoNotifica di ripresa (PBT_APMRESUMEAUTOMATIC)Lavoro di riconnessione idempotenteRilevamento di errore di comunicazioneFallimento del keepaliveRiuscito?Di nuovo in funzionamento normaleRitenta dopo backoff esponenziale

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».

Due mezzi per sopprimere la sospensioneChe 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 finitoIntervallo di lavoro che non deve essere attraversato dalla sospensioneSetThreadExecutionStatePower request (PowerSetRequest)Comodo — solo flagCon un motivo — visibile in powercfgCancellala sempre a lavoro finito

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 /requests elenca i processi e i driver che hanno emesso una power request. «L’app ha dimenticato di cancellare SetThreadExecutionState» compare anche qui.
  • 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.
  • Qualità di Modern Standby: powercfg /sleepstudy genera 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».
Mappatura dei sintomi di problemi di alimentazione sui comandi di indaginePer 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 eventiNon va in sospensionepowercfg /requestsSi sveglia da solapowercfg /lastwake e /waketimersVoglio confermare la linea temporaleKernel-Power nel registro eventiCompare 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.

  • 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 SetThreadExecutionState o 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

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.

Riferimenti

  1. 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

  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

  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

  4. 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

  5. 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

  6. 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

  7. 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

  8. 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

  9. 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 recenti con gli stessi tag per approfondire argomenti vicini.

Queste pagine collocano l’argomento in un contesto più ampio di servizi e decisioni.

L’articolo è direttamente collegato ai servizi seguenti.

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.

Torna al blog