Bloquear sesión con API Win32

Usaremos la API de Windows para lograr bloquear la sesión de usuario de manera programática, en el sistema operativo Windows al presionar la combinación de teclas Crtl + Alt + Supr y luego presionar la opción bloquear logramos lo que ahora realizaremos desde programación C/C++, también veremos como podemos cerrar la sesión.

Bloquear sesión de usuario

Para bloquear la sesión de usuario usaremos la función LockWorkStation() esta devuelve un booleano que indica el resultado de la operación, veamos un ejemplo sencillo:

#include <windows.h>
#include <stdio.h>

#pragma comment( lib, "user32.lib" )

void main()
{
    if( !LockWorkStation() )
        printf ("LockWorkStation failed with %d\n", GetLastError());
}

Si el código se ejecuta correctamente veremos nuestra correspondiente pantalla de bloqueo.

bloquear o cerrar sesión de usuario

Si deseamos que nuestra aplicación reciba una notificación cuando el usuario inicie la sesión o la bloquee ya sea utilizando el código o el método tradicional debemos utilizar la función WTSRegisterSessionNotification() que devuelve FALSE si hay error.

WTSRegisterSessionNotification(hwnd, NOTIFY_FOR_THIS_SESSION);

Debemos indicar el manejador de ventana, luego NOTIFY_FOR_THIS_SESSION si deseamos recibir notificaciones de la sesión asociada a la ventana, usaremos NOTIFY_FOR_ALL_SESSIONS para recibir notificaciones de todas las sesiones.

Para manejar la notificación debemos capturar el mensaje WM_WTSSESSION_CHANGE verificamos el parámetro wParam para determinar cual ha sido el cambio en la sesión que produjo el mensaje, por ejemplo WTS_SESSION_UNLOCK indica que la sesión ha sido desbloqueada. 

case WM_WTSSESSION_CHANGE:
    switch (wParam)
    {
    case WTS_SESSION_UNLOCK:
        // sistema desbloqueado
        break;
    }
    break;

Algunos de los valores que puede tomas wParam son:

  • WTS_SESSION_LOCK si la sesión ha sido bloqueada.
  • WTS_SESSION_LOGON inicio de sesión
  • WTS_SESSION_LOGOFF cierre de sesión.
  • WTS_CONSOLE_CONNECT, WTS_CONSOLE_DISCONNECT, etc.

Cerrar sesión de usuario

En caso de que, lo que deseemos hacer sea cerrar la sesión utilizaremos entonces la función ExitWindowsEx() esta nos sirve no solo para lo que acabamos de mencionar, también podemos usarla para apagar o reiniciar la PC.

Para cerrar la sesión hacemos esto:

ExitWindowsEx(EWX_LOGOFF, 0);

El primer parámetro indica lo que deseamos hacer, puede ser: EWX_LOGOFF, EWX_POWEROFF, EWX_REBOOT, EWX_RESTARTAPPS, EWX_SHUTDOWN, los nombres bastante descriptivos, puedes ver la documentación de referencia de la función para más detalles. 

Si deseamos utilizar un modo distinto a EWX_LOGOFF debemos primero adquirir los privilegios adecuados, más específicamente SE_SHUTDOWN_NAME.

Aplicando los conocimientos adquiridos creamos esta pequeña aplicación que nos permite bloquear la sesión o cerrarla, además recibe una notificación cuando el usuario inicia la sesión.

#include <Windows.h>
#include <WtsApi32.h>

#pragma comment( lib, "Wtsapi32.lib" )
#pragma comment( lib, "user32.lib" )

#define BTN_LOCK   100
#define BTN_LOGOFF 101
#define TXT_LOCK   102

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR lpCmdLine, int nCmdShow) {
    MSG msg;

    WNDCLASSW wc = { 0 };
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpszClassName = L"Lock";
    wc.hInstance = hInstance;
    wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
    wc.lpfnWndProc = WndProc;
    wc.hCursor = LoadCursor(0, IDC_ARROW);

    RegisterClassW(&wc);

    CreateWindowW(wc.lpszClassName, L"Lock Session",
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        100, 100, 300, 130,
        NULL, NULL, hInstance, NULL);

    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
    case WM_CREATE:
    {
        WTSRegisterSessionNotification(hwnd, NOTIFY_FOR_THIS_SESSION);

        HINSTANCE hinst = ((LPCREATESTRUCT)lParam)->hInstance;

        CreateWindow(L"BUTTON", L"Bloquear",
            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
            10, 10, 120, 30,
            hwnd, (HMENU)BTN_LOCK, hinst, NULL);

        CreateWindow(L"BUTTON", L"LogOff",
            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
            150, 10, 120, 30,
            hwnd, (HMENU)BTN_LOGOFF, hinst, NULL);

        CreateWindow(L"STATIC", L"Presiona para bloquear o cerrar sesion.",
            WS_CHILD | WS_VISIBLE | SS_SIMPLE,
            10, 50, 800, 30,
            hwnd, (HMENU)TXT_LOCK, hinst, NULL);
    }
        break;

    case WM_WTSSESSION_CHANGE:
        switch (wParam)
        {
        case WTS_SESSION_UNLOCK:
            SetWindowText(GetDlgItem(hwnd, TXT_LOCK), L"Sistema desbloqueado...");
            InvalidateRect(hwnd, NULL, TRUE);
            break;

        default: break;
        }
        break;

    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case BTN_LOCK:
            if (!LockWorkStation())
                MessageBox(hwnd, L"Error al bloquear sesion.", L"Lock Error", MB_OK | MB_ICONERROR);
            break;

        case BTN_LOGOFF:
            ExitWindowsEx(EWX_POWEROFF, 0);
            break;

        default: break;
        }
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProcW(hwnd, msg, wParam, lParam);
}

image

Apagar la PC

Este código ha sido copiado de la documentación oficial de Microsoft el mismo utiliza la función previamente explicada para apagar el computador, podemos ver como se adquiere el privilegio requerido.

#include <windows.h>

#pragma comment(lib, "user32.lib")
#pragma comment(lib, "advapi32.lib")

BOOL MySystemShutdown()
{
   HANDLE hToken; 
   TOKEN_PRIVILEGES tkp; 
 
   // Get a token for this process. 
   if (!OpenProcessToken(GetCurrentProcess(), 
        TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) 
      return( FALSE ); 
 
   // Get the LUID for the shutdown privilege. 
   LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, 
        &tkp.Privileges[0].Luid); 
 
   tkp.PrivilegeCount = 1;  // one privilege to set    
   tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 
 
   // Get the shutdown privilege for this process. 
   AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, 
        (PTOKEN_PRIVILEGES)NULL, 0); 
 
   if (GetLastError() != ERROR_SUCCESS) 
      return FALSE; 
 
   // Shut down the system and force all applications to close. 
   if (!ExitWindowsEx(EWX_SHUTDOWN | EWX_FORCE, 
               SHTDN_REASON_MAJOR_OPERATINGSYSTEM |
               SHTDN_REASON_MINOR_UPGRADE |
               SHTDN_REASON_FLAG_PLANNED)) 
      return FALSE; 

   //shutdown was successful
   return TRUE;
}

El código que apaga el sistema es el siguiente:

if (!ExitWindowsEx(EWX_SHUTDOWN | EWX_FORCE, 
               SHTDN_REASON_MAJOR_OPERATINGSYSTEM |
               SHTDN_REASON_MINOR_UPGRADE |
               SHTDN_REASON_FLAG_PLANNED)) 
      return FALSE;

Como primer parámetro indicamos EWX_SHUTDOWN | EWX_FORCE con esto decimos que el sistema debe apagarse y forzar el cierre de todas las aplicaciones abiertas, el último parámetro indica la razón del apagado, visita la documentación para ver los distintos códigos disponibles.

El resto del código es utilizado para obtener el privilegio necesario, sin él la función no tendrá efecto.

Comentarios

Entradas populares de este blog

Conectar SQL Server con Java

Entrenar OpenCV en Detección de Objetos

Procesamiento de imágenes en OpenCV

Acceso a la webcam con OpenCV

Conociendo la clase cv::Mat de OpenCV