LoginEn ocasiones resulta muy 煤til detectar desde tu aplicaci贸n Windows el hecho de que el usuario actual se est茅 conectando o desconectando a la sesi贸n actual, o simplemente que se haya bloqueado el escritorio. Por ejemplo, si el usuario tiene el escritorio bloqueado es una tonter铆a mostrarle mensajes o globos de notificaci贸n ya que no va a haber nadie para verlos 驴no?

驴C贸mo podemos detectar estas circunstancias?

La API de .NET dispone de un espacio de nombres especial llamado Microsoft.Win32 que contiene clases que encapsulan funcionalidad atada al sistema operativo Windows. Dentro de este espacio de nombres existe una clase llamada SystemEvents que ofrece multitud de eventos est谩ticos que nos permiten responder a diversas cuestiones que ocurren en el sistema operativo: cambio de los ajustes de pantalla, agregar o quitar fuentes, que el equipo se est谩 apagando o suspendiendo, cambios de preferencia de usuario, cambio de la hora del sistema, etc... Es realmente 煤til, y utiliza por debajo llamadas a la API de Windows, evit谩ndonos tener que conocerlas al detalle y facilit谩ndonos su uso.

Para la pregunta que nos ocupa en esta ocasi贸n la clase define un evento llamado SessionSwitch que es llamado autom谩ticamente cuando cambia el estado de "logueado" del usuario actual. Este evento es un delegado de tipo SessionSwitchEventHandler, por lo que deberemos definir una funci贸n apropiada para este tipo de delegado, la cual toma como argumentos un objeto (como origen del mismo, como siempre) y otro de tipo SessionSwitchEventArgs.

As铆, que una vez a帽adido el espacio de nombres Microsoft.Win32 a nuestra clase, podemos definir un m茅todo como este:

   1: public static void OnSessionSwitch (Object sender, SessionSwitchEventArgs e)
   2: {
   3:     //C贸digo para responder al evento
   4: }

y asignarlo al evento SessionSwitch de la siguiente manera (por ejemplo en el m茅todo Main de nuestra aplicaci贸n):

   1: SystemEvents.SessionSwitch += new SessionSwitchEventHandler(OnSessionSwitch);

Con esto, cuando cambie el estado de login del usuario se llamar谩 a nuestro m茅todo OnSessionSwitch.

El 煤nico miembro interesante del segundo par谩metro del evento (llamado "e" en el ejemplo) es Reason, que devuelve un tipo enumerado SessionSwitchReason y nos indica el motivo para haber lanzado el evento. As铆 e.Reason puede tomar los siguientes valores:

Valor Descripci贸n
ConsoleConnect Se ha conectado una sesi贸n desde la consola.
ConsoleDisconnect Se ha desconectado una sesi贸n desde la consola.
RemoteConnect Se ha conectado una sesi贸n desde una conexi贸n remota.
RemoteDisconnect Se ha desconectado una sesi贸n desde una conexi贸n remota.
SessionLogon Un usuario ha iniciado una sesi贸n.
SessionLogoff Un usuario ha cerrado una sesi贸n.
SessionLock Se ha bloqueado una sesi贸n.
SessionUnlock Se ha desbloqueado una sesi贸n.
SessionRemoteControl Una sesi贸n ha cambiado su estado a o desde el modo controlado remoto.

He marcado en negrita las m谩s comunes.

Por lo tanto es muy sencillo actuar en funci贸n de este valor y bloquear los mensajes, por ejemplo, cuando se bloquee la sesi贸n o se desconecte remotamente el usuario (cuando se conecta a trav茅s de escritorio remoto),  restaurarlos nuevamente cuando se vuelva a conectar o se desbloquee.

C贸mo detectar el estado actual con la API de Windows

Todo lo anterior est谩 muy bien y es muy sencillo, pero 驴qu茅 pasa si queremos determinar el estado actual de "logueo" y no s贸lo detectar cuando cambie?. Por ejemplo, para determinar si nuestro programa se ha lanzado desde una sesi贸n de Terminal Server o con la sesi贸n local bloqueada (por ejemplo con una tarea programada).

Para determinar si el usuario actual est谩 trabajando en remoto, bajo una sesi贸n de Terminal Server (o escritorio remoto, es exactamente lo mismo), la API de Windows Forms nos ofrece una propiedad de la clase SystemInformation, por lo que es muy sencillo hacer una funci贸n IsTerminalServer as铆:

   1: public static bool IsTerminalServer()
   2: {
   3:     return System.Windows.Forms.SystemInformation.TerminalServerSession;
   4: }

Para averiguar si la sesi贸n actual est谩 bloqueada en este momento o no, tendremos que recurrir a la API de Windows directamente.

Os dejo aqu铆 el c贸digo necesario extra铆do de una de las primeras versiones internas de mi software Open Source Twitter Followers Monitor (es un proyecto en ingl茅s, por lo que los comentarios, etc... est谩n en este idioma):

   1: using System;
   2: using System.Runtime.InteropServices;
   3:  
   4: namespace TwitterFollowersMonitor
   5: {
   6:     /// <summary>
   7:     /// Common utilities class
   8:     /// </summary>
   9:     class Utils
  10:     {
  11:  
  12: #region "API Calls"
  13:         private const Int32 DESKTOP_SWITCHDESKTOP = 0x100;
  14:         [DllImport("user32", EntryPoint = "OpenDesktopA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
  15:         private static extern Int32 OpenDesktop(string lpszDesktop, Int32 dwFlags, bool fInherit, Int32 dwDesiredAccess);
  16:         [DllImport("user32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
  17:         private static extern Int32 CloseDesktop(Int32 hDesktop);
  18:         [DllImport("user32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
  19:         private static extern Int32 SwitchDesktop(Int32 hDesktop);
  20: #endregion
  21:  
  22:         /// <summary>
  23:         /// Determines if the current session is locked so that I can choose not to show any visual clues (like balloons)
  24:         /// </summary>
  25:         /// <returns></returns>
  26:         public static bool IsWorkstationLocked()
  27:         {
  28:             Int32 p_lngHwnd;
  29:             Int32 p_lngRtn;
  30:             Int32 p_lngErr;
  31:  
  32:             p_lngHwnd = OpenDesktop("Default", 0, false, DESKTOP_SWITCHDESKTOP);
  33:  
  34:             if (p_lngHwnd == 0)
  35:             {
  36:                 return false;
  37:             }
  38:             else
  39:             {
  40:                 p_lngRtn = SwitchDesktop(p_lngHwnd);
  41:                 p_lngErr = Marshal.GetLastWin32Error();
  42:                 if (p_lngRtn == 0)
  43:                 {
  44:                     if (p_lngErr == 0)
  45:                     {
  46:                         p_lngHwnd = CloseDesktop(p_lngHwnd);
  47:                         return true;
  48:                     }
  49:                     else
  50:                     {
  51:                         p_lngHwnd = CloseDesktop(p_lngHwnd);
  52:                         return false;
  53:                     }
  54:                 }
  55:                 else
  56:                 {
  57:                     //Not locked
  58:                     p_lngHwnd = CloseDesktop(p_lngHwnd);
  59:                     return false;
  60:                 }
  61:             }
  62:         }
  63:  
  64:     }
  65: }

Como vemos se necesitan tres funciones de la API de Windows (OpenDesktop, CloseDesktop y SwitchDesktop, declaradas con Interop al principio de la clase. se usan para intentar abrir el escritorio actual y activarlo (s贸lo hay uno, as铆 que si est谩 disponible y no bloqueado deber铆an funcionar las llamadas, fracasando en otro caso).

隆Espero que te sea 煤til!

驴Te ha gustado este post? - Aprende .NET con los cursos on-line tutelados de campusMVP:
   路
Preparaci贸n del examen 70-515: Desarrollo Web con .NET 4.0 (Tutelado por mi)
   路 Desarrollo Web con ASP.NET 4.0 Web Forms (Tutelado por mi)
   ASP.NET 4.0 Web Forms desde cero (Tutelado por mi)
   Desarrollo Web con ASP.NET MVC 3 
   Silverlight 4.0 - Aplicaciones Ricas para Internet (RIA)
   jQuery paso a paso para programadores ASP.NET
   Visual Studio 2010 desde cero

💪🏻 驴Este post te ha ayudado?, 驴has aprendido algo nuevo?
Pues NO te pido que me invites a un caf茅... Te pido algo m谩s f谩cil y mucho mejor