Deshabilitar Alt+Tab en Windows XP
También en NT/2000 además de las teclas: Ctrl+Esc y Alt+Esc



Publicado: 09/Mar/2003
Actualizado: 15/Oct/2004

15/Oct/2004:
Pulsa aquí si quieres ver cómo hacerlo en .NET (VB y C#)
 

 


Como sabrás, sobre todo si lo has intentado, en Windows NT/2000/XP no se puede deshabilitar la combinación de teclas Ctrl+Alt+Supr, esto es por razones de seguridad, ya que es el propio sistema operativo el que se encarga de que esa combinación de teclas no sea interceptable. La razón principal es que esa es la combinación de teclas que se utiliza antes de iniciar una sesión, al menos en los servidores Windows siempre es así, ya que tanto en Windows XP como en Windows 2000 Pro, podemos indicar que no nos pregunte por el usuario que inicia el equipo y por tanto no se mostraría la pantalla que nos indica que pulsemos esa combinación antes de indicar nuestro nombre y contraseña.

Por eso, salvo que alguien encuentre un código de cómo hacerlo, y si lo encuentra es posible que no lo difunda... no es ese mi caso, pero... para que no salte el listillo de siempre y diga: "yo se cómo evitar que se pulsen esas teclas".

Antes de que alguien lo diga, voy a decirlo... en Windows 9x sí es posible deshabilitar esa combinación de teclas, la forma de hacerlo la tienes en este link: ¿Cómo evitar el uso de CTRL+ALT+SUPR y ALT+TAB?

Un poco de historia para alargar el artículo:
Ayer estaba revisando las consultas de los grupos de noticias, como suelo hacer casi todos los días, y me encontré con un mensaje que preguntaba precisamente cómo evitar esa triple combinación de teclas en los Windows tipo NT, la respuesta que le dieron fue de que buscara en Google (indicando el grupo de noticias microsoft.public.vb.es) ya que en muchas ocasiones se había dado respuesta a esa pregunta. Como un servidor no se las sabe todas, pues seguí el susodicho link y busqué en Google y uno de los mensajes que encontré fue precisamente mío, con fecha de Enero del 2000, (del que yo, ni me acordaba), el link a ese "hilo" es este: Re- aclaración sobre inhabilitar CTRL+ALT+SUPR y ALT+TAB. Aunque no daba la solución, pero si mostraba un código creado en C++ que "casi" hacía eso... realmente sólo evitaba las tres combinaciones que indico en el título de este artículo. Así que, me puse a convertirlo a Visual Basic (clásico, para que el personal no se me queje de que ahora sólo publico cosas sobre punto NET) y el resultado es el que te presento aquí.

Espero que te sea de utilidad, que de eso es de lo que al fin y al cabo se trata y también te invito a que hagas pruebas con otras combinaciones de teclas, para que practiques.

Una cosa muy importante es que en el código que te muestro se hace un "gancho" (hook) al teclado, por tanto no te recomiendo que hagas las pruebas en el IDE de Visual Basic, ya que si por casualidad se produjera un error mientras el gancho está activo, es posible que se te quede colgado el sistema... o al menos el IDE y pierdas lo que no hayas guardado. Avisado estás.

 

Funciones del API usadas en este artículo:
Te resumo las funciones del API y técnicas que se utilizan en este artículo para que tengas un visión general de lo que el código te va a mostrar.
 

Función Comentario
SetWindowsHookEx   para crear un gancho
UnhookWindowsHookEx para quitar el gancho creado con SetWindowsHookEx
CallNextHookEx para llamar al siguiente gancho
GetAsyncKeyState para saber el estado de pulsación de una tecla
CopyMemory para copiar una estructura a partir de un puntero
   
Constante Valor Comentario
WH_KEYBOARD_LL 13& para indicar a SetWindowsHookEx que el gancho será para el teclado
VK_TAB 9& la tecla TAB
VK_CONTROL &H11 la tecla Control (CTRL)
VK_ESCAPE &H1B la tecla Esc (Escape)
LLKHF_ALTDOWN &H20 usada para indicar si también está pulsada la tecla ALT
HC_ACTION 0& si la función del gancho recibe este código es cuando hay que actuar...

También se utiliza la siguiente estructura, que es la que recibe la información de la tecla pulsada, te muestro la información indicada en la documentación que acompaña al Visual Basic 6.0:

typedef struct tagKBDLLHOOKSTRUCT {
    DWORD vkCode;      // virtual key code
    DWORD scanCode;    // scan code
    DWORD flags;       // flags
    DWORD time;        // time stamp for this message
    DWORD dwExtraInfo; // extra info from the driver or keybd_event
}

En esta misma documentación puedes encontrar información sobre el resto de las funciones del API, así como de la forma en que habrá que declarar la función que será llamada cada vez que se pulse una tecla, en esta ocasión, la función tiene este formato:

LRESULT CALLBACK LowLevelKeyboardProc(
  int
nCode,     // hook code
  WPARAM wParam, // message identifier
  LPARAM lParam  // pointer to structure with message data
);

Ahora veremos cómo declararla y usarla desde una aplicación de VB6 o VB5, pero no anteriores, ya que se utiliza AddressOf y esa instrucción no existe en las versiones anteriores a Visual Basic 5.0

Vamos a crear un nuevo proyecto, al cual añadiremos un módulo BAS.
En el formulario que se incluye, añade dos botones que tendrán estos nombres: cmdHook y cmdUnHook, desde estos botones se activará y desactivará la intercepción de las teclas.
En el módulo BAS incluiremos las declaraciones del API y demás constantes y tipos, así como la función que será llamada por el sistema operativo cuando el gancho del teclado esté activo.
Según la documentación el código incluido en esa función no debería alargarse más de lo debido, por tanto, en esa función haz sólo lo que debas hacer y no lo alargues innecesariamente.

Los pasos que daremos para activar/desactivar el gancho serán:
1- Iniciar el gancho del teclado (podemos hacerlo al iniciarse la aplicación, pero en este ejemplo se hará cuando pulsemos en el botón cmdHook.
2- Quitar el gancho del teclado cuando la aplicación se cierre. Aquí además de eso, vamos a quitar el mencionado gancho cuando pulsemos en el botón cmdUnHook.

Una vez activado el gancho, cada vez que se pulse una tecla, se filtrará por la función gancho que tenemos en el módulo BAS a la que he llamado LLKeyBoardProc, en esa función se comprobarán las combinaciones de teclas y se devolverá un valor 1, si queremos que sean "obviadas" por el sistema, en caso de que no sea una de las teclas que queremos interceptar, la función debe devolver el valor indicado por CallNextHookEx, de forma que se pueda si hay otras aplicaciones que también han puesto un gancho, sean llamadas.

Todo esto lo verás en el código, así que, no me enrollo más y veamos ese código.

El código
Empezaremos por el código del formulario que es mucho más simple.

'------------------------------------------------------------------------------
' Formulario de prueba para desactivar algunas teclas especiales    (08/Mar/03)
' en Windows NT/2000/XP
'
' ©Guillermo 'guille' Som, 2003
'------------------------------------------------------------------------------
Option Explicit

Private Sub cmdCerrar_Click()
    Unload Me
End Sub

Private Sub cmdHook_Click()
    ' iniciar el gancho para el teclado
    HookKeyB App.hInstance
End Sub

Private Sub cmdUnHook_Click()
    ' quitar el gancho del teclado
    UnHookKeyB
End Sub

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
    ' al cerrar el formulario, quitar el gancho del teclado
    UnHookKeyB
End Sub

Como puedes comprobar, en la aplicación también he añadido un botón para cerrarla.

Ahora veamos el código a incluir en el módulo BAS:

'------------------------------------------------------------------------------
' Para bloquear algunas teclas en Windows NT/2000/XP                (08/Mar/03)
' Para NT debe tener el SP3 como mínimo
'
' ¡¡¡ NO FUNCIONA para Ctrl+Alt+Supr !!!
'
' En este ejemplo se bloquean las siguientes teclas:
'   Ctrl+Esc, Alt+Tab y Alt+Esc
'
' ©Guillermo 'guille' Som, 2003
'------------------------------------------------------------------------------
Option Explicit

' para guardar el gancho creado con SetWindowsHookEx
Private mHook As Long

'
' para indicar a SetWindowsHookEx que tipo de gancho queremos instalar
Private Const WH_KEYBOARD_LL As Long = 13&
' este es para el ratón
'Private Const WH_MOUSE_LL As Long = 14&
'
Private Type tagKBDLLHOOKSTRUCT
    vkCode As Long
    scanCode As Long
    flags As Long
    time As Long
    dwExtraInfo As Long
End Type
'
Private Const VK_TAB As Long = &H9
Private Const VK_CONTROL As Long = &H11     ' tecla Ctrl
'Private Const VK_MENU As Long = &H12        ' tecla Alt
Private Const VK_ESCAPE As Long = &H1B
'Private Const VK_DELETE As Long = &H2E      ' tecla Supr (Del)
'
Private Const LLKHF_ALTDOWN As Long = &H20&
'
' códigos para los ganchos (la acción a tomar en el gancho del teclado)
Private Const HC_ACTION As Long = 0&


'-----------------------------
' Funciones del API de Windows
'-----------------------------

' para asignar un gancho (hook)
Private Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" _
    (ByVal idHook As Long, ByVal lpfn As Long, _
    ByVal hMod As Long, ByVal dwThreadId As Long) As Long

' para quitar el gancho creado con SetWindowsHookEx
Private Declare Function UnhookWindowsHookEx Lib "user32" _
    (ByVal hHook As Long) As Long
    
' para llamar al siguiente gancho
Private Declare Function CallNextHookEx Lib "user32" _
    (ByVal hHook As Long, ByVal nCode As Long, _
    ByVal wParam As Long, ByVal lParam As Long) As Long

' para saber si se ha pulsado en una tecla
Private Declare Function GetAsyncKeyState Lib "user32" _
    (ByVal vKey As Long) As Integer

' para copiar la estructura en un long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
    (Destination As Any, Source As Any, ByVal Length As Long)



' La función a usar para el gancho del teclado
Public Function LLKeyBoardProc(ByVal nCode As Long, _
                               ByVal wParam As Long, _
                               ByVal lParam As Long _
                               ) As Long
    Dim pkbhs As tagKBDLLHOOKSTRUCT
    Dim ret As Long
    '
    ret = 0
    '
    ' copiar el parámetro en la estructura
    CopyMemory pkbhs, ByVal lParam, Len(pkbhs)
    '
    If nCode = HC_ACTION Then
        '
        ' si se pulsa Ctrl+Esc
        If pkbhs.vkCode = VK_ESCAPE Then
            If (GetAsyncKeyState(VK_CONTROL) And &H8000) Then
                ret = 1
            End If
        End If
        '
        ' si se pulsa Alt+Tab
        If pkbhs.vkCode = VK_TAB Then
            If (pkbhs.flags And LLKHF_ALTDOWN) <> 0 Then
                ret = 1
            End If
        End If
        '
        ' si se pulsa Alt+Esc
        If pkbhs.vkCode = VK_ESCAPE Then
            If (pkbhs.flags And LLKHF_ALTDOWN) <> 0 Then
                ret = 1
            End If
        End If
        '
    End If
    '
    If ret = 0 Then
        ret = CallNextHookEx(mHook, nCode, wParam, lParam)
    End If
    '
    LLKeyBoardProc = ret
    '
    '

    ' El código C++ en el que he basado (o casi) el de VB

'LRESULT CALLBACK LowLevelKeyboardProc (INT nCode, WPARAM wParam, LPARAM lParam)
'{
'    // By returning a non-zero value from the hook procedure, the
'    // message does not get passed to the target window
'    KBDLLHOOKSTRUCT *pkbhs = (KBDLLHOOKSTRUCT *) lParam;
'    BOOL bControlKeyDown = 0;
'
'    switch (nCode)
'    {
'        case HC_ACTION:
'        {
'            // Check to see if the CTRL key is pressed
'            bControlKeyDown = GetAsyncKeyState (VK_CONTROL) >> ((sizeof(SHORT) * 8) - 1);
'
'            // Disable CTRL+ESC
'            if (pkbhs->vkCode == VK_ESCAPE && bControlKeyDown)
'                return 1;
'
'            // Disable ALT+TAB
'            if (pkbhs->vkCode == VK_TAB && pkbhs->flags & LLKHF_ALTDOWN)
'                return 1;
'
'            // Disable ALT+ESC
'            if (pkbhs->vkCode == VK_ESCAPE && pkbhs->flags & LLKHF_ALTDOWN)
'                return 1;
'
'            break;
'        }
'
'default:
'            break;
'    } return CallNextHookEx (hHook, nCode, wParam, lParam);
'}

End Function



Public Sub HookKeyB(ByVal hMod As Long)
    ' instalar el gancho para el teclado
    ' hMod será el valor de App.hInstance de la aplicación
    mHook = SetWindowsHookEx(WH_KEYBOARD_LL, AddressOf LLKeyBoardProc, hMod, 0&)
End Sub

Public Sub UnHookKeyB()
    ' desinstalar el gancho para el teclado
    ' Es importante hacerlo antes de finalizar la aplicación,
    ' normalmente en el evento Unload o QueryUnload
    If mHook <> 0 Then
        UnhookWindowsHookEx mHook
    End If
End Sub

Te he dejado algunas declaraciones de constantes comentadas, para que sepas los valores y puedas intentar otras cosas.
También he dejado el código C/C++ en el que está basado el código de VB, el cual era el que puse en el mensaje ese que te comenté al principio, el cual a su vez estaba tomado de un artículo de la Knowledge Base titulado: HOWTO: Disable Task Switching on Win32 Platforms.

Para terminar, decirte que si este código lo usas en Windows NT, debes tener como mínimo el SP3.

Espero que te sea de utilidad.

Nos vemos.
Guillermo


Volver a la página del API

ir al índice