Lorsque l’utilisateur ferme sa session ou lorsqu’il arrête sa machine, Windows quitte termine toutes les applications ouvertes. Certaines applications souhaitent exécuter du code avant de se terminer afin de ne pas perdre l’état courant (un document en cours d’écriture par exemple).

Pour cela Windows envoie un message à toutes les applications :

  • WM_QUERYENDSESSION lors de la fermeture de la session
  • WM_ENDSESSION lors de l’arrêt de la machine

Si l’application ne traite pas ces messages, Windows ferme la ferme. Il est cependant possible d’intercepter ces messages et de bloquer l’arrêt de la machine ou la fermeture de session.

Windows fournit 3 méthodes :

La dernière méthode permet de déterminer l’ordre dans lequel Windows envoie les messages WM_ENDSESSION et WM_QUERYENDSESSION aux applications. Cela sert dans le cas où une application A dépend d’une application B. L’application A souhaite être informée avant l’application B que la machine est en train de s’arrêter pour potentiellement bloquer cet arrêt. Ainsi l’application B reste vivante tant que l’application A bloque l’arrêt.

La priorité se défini avec un entier compris entre 0 et 0x4FF. 0x4FF est la plus prioritaire (appelée en premier), 0 est la moins prioritaire (appelée en dernier). Par défaut les applications ont la priorité 280.

Note : Les zones [000-0FF] et [400-4FF] sont réservées pour Windows et ne doivent pas être utilisées par vos applications. La priorité la plus élevée pour une application est donc 3FF et la plus basse 100.

Voici un petit exemple de code pour montrer comment utiliser ces méthodes :

public partial class MainForm : Form     
{     
   public const int WM_QUERYENDSESSION = 0x0011;     
   public const int WM_ENDSESSION = 0x0016;     
   public const uint SHUTDOWN_NORETRY = 0x00000001;    
   [DllImport("user32.dll", SetLastError = true)]     
   static extern bool ShutdownBlockReasonCreate(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string reason);    
   [DllImport("user32.dll", SetLastError = true)]     
   static extern bool ShutdownBlockReasonDestroy(IntPtr hWnd);    
   [DllImport("kernel32.dll")]     
   static extern bool SetProcessShutdownParameters(uint dwLevel, uint dwFlags);    
   public MainForm()     
   {     
   InitializeComponent();    
   // Défini la priorité de l'application (0x3FF = la plus prioritaire)     
   SetProcessShutdownParameters(0x3FF, SHUTDOWN_NORETRY);     
   }    
   protected override void WndProc(ref Message m)     
   {     
   if (m.Msg == WM_QUERYENDSESSION || m.Msg == WM_ENDSESSION)     
   {     
   // Bloque l'arrêt de Windows     
   ShutdownBlockReasonCreate(this.Handle, "I want to live!");     
   ThreadPool.QueueUserWorkItem(o =>     
   {     
   // Simule un traitement long     
   Thread.Sleep(5000);     
   this.BeginInvoke((Action)(() =>     
   {     
   // Met à jour le message affiché à l'utilisateur     
   // Cette méthode doit être appelée sur le même thread que     
   // celui qui a crée le Handle d'où BeginInvoke     
   ShutdownBlockReasonCreate(this.Handle, "Now I must die!");     
   
   // Autorise Windows à s'arrêter (au moins pour cette application)     
   ShutdownBlockReasonDestroy(this.Handle);     
   }));     
   });    
   return;     
   }    
   base.WndProc(ref m);     
   }     
}

Ne ratez plus aucune actualité avec la newsletter mensuelle de SoftFluent

Newsletter SoftFluent