Un lugar de encuentro para los programadores de habla hispana

Un lugar de encuentro para los programadores de habla hispana

Buscar

Entrar

Puedes acceder utilizando tu cuenta de usuario del foro.

Si no dispone de cuenta, puede crear una accediendo al formulario de registro del foro.



Capturar teclas de todas las aplicaciones Windows Imprimir
Ideas - Delphi

Mucha gente me pregunta acerca de la posibilidad de que nuestra aplicacion Delphi capture las pulsaciones de teclas del usuario, aunque el usuario no las haga estando nuestra aplicacion activa. Por supuesto... lo primero que hacemos es dar una vuelta por el evento OnKeyPress de la form, y claro, sin obtener resultados positivos, incluso poniendo la propiedad KeyPreview de la form a true... Esto ocurre porque nuestra aplicación sólo recibirá mensajes de pulsaciones de teclado cuando es ella quien tiene el foco. El siguiente paso para resolver este 'reto' es la de pelearse con los hooks de teclado. Un Hook (en español algo así como 'gancho') no es más que un mecanismo que nos permitirá espiar el tráfico de mensajes entre Windows y las aplicaciones.

Instalar un hook en nuestra aplicación es algo relativamente sencillo, pero claro, si lo instalamos en nuestra aplicación, tan sólo 'espiaremos' los mensajes que Windows envie a nuestra aplicacion, con lo que tampoco habremos resuelto el problema.

¿Cual es la solución entonces?, pues la solución pasa por instalar un Hook pero a nivel de sistema, es decir, un 'gancho' que capture todos los mensajes que circulen hacia Windows.

El instalar un hook a nivel de sistema tiene una gran complicación añadida, que es el hecho de que la función a la que llama el hook ha de estar contenida en una DLL, no en nuestra aplicación Delphi. Esta condición, nos obligará, en primer lugar a construirnos una DLL, y en segundo lugar a construirnos algun invento para comunicar la DLL con nuestra aplicación.

En este truco tienes un ejemplo de captura de teclado mediante un Hook de teclado a nivel de sistema. El ejemplo consta de dos proyectos, uno para la DLL y otro para la aplicación de ejemplo.

El funcionamiento es el siguiente: Creamos una DLL con dos funciones que exportaremos, una para instalar el hook y otra para desinstalarlo. Hay una tercera funcion que es la que ejecutará el hook una vez instalado (CallBack). En ella, lo que haremos es enviar los datos del mensaje capturado a nuestra aplicacion.

La DLL debe saber en todo momento el handle de la aplicacion receptora, así que haremos que lo lea de un fichero mapeado en memoria que crearemos desde la propia aplicacion. Enviaremos los datos desde la DLL a la aplicacion a través de un mensaje de usuario.

Bien, vamos con el ejemplo:

La DLL que instala el Hook: Crea el esqueleto de una DLL (File - New - DLL) Cambia el código del proyecto por éste otro:

 
library Project1;
  {
  Demo de Hook de teclado a nivel de sistema, Radikal.
  Como lo que queremos es capturar las teclas pulsadas en cualquier parte
  de Windows, necesitamos instalar la funcion CallBack a la que llamará
  el Hook en una DLL, que es ésta misma.
  }
 
uses Windows, Messages;
 
const
  CM_MANDA_TECLA = WM_USER + $1000;
 
var
  HookDeTeclado     : HHook;
  FicheroM    : THandle;
  PReceptor   : ^Integer;
 
{Esta es la funcion CallBack a la cual llamará el hook.}
function CallBackDelHook( Code    : Integer;
                          wParam  : WPARAM;
                          lParam  : LPARAM
                        )         : LRESULT; stdcall;
begin
  {Si una tecla fue pulsada o liberada}
  if code=HC_ACTION then
  begin
    {Miramos si existe el fichero}
    FicheroM:=OpenFileMapping(FILE_MAP_READ,False,'ElReceptor');
    {Si no existe, no enviamos nada a la aplicacion receptora}
    if FicheroM<>0 then
    begin
      PReceptor:=MapViewOfFile(FicheroM,FILE_MAP_READ,0,0,0);
      PostMessage(PReceptor^,CM_MANDA_TECLA,wParam,lParam);
      UnmapViewOfFile(PReceptor);
      CloseHandle(FicheroM);
    end;
  end;
    {Llamamos al siguiente hook de teclado de la cadena}
    Result := CallNextHookEx(HookDeTeclado, Code, wParam, lParam)
end;
 
{Procedure que instala el hook}
procedure HookOn; stdcall;
begin
  HookDeTeclado:=SetWindowsHookEx(WH_KEYBOARD, @CallBackDelHook, HInstance , 0);
end;
 
{procedure para desinstalar el hook}
procedure HookOff;  stdcall;
begin
  UnhookWindowsHookEx(HookDeTeclado);
end;
 
{Exportamos las procedures...}
exports
  HookOn,
  HookOff;
begin
end.
 
 
 

Ahora graba el proyecto con el nombre: 'HookTeclado.dpr' y la compilas (Project - Build All), y habrás generado la DLL del proyecto.

Aplicacion que recibe datos del Hook

Crea una nueva aplicacion y pon un TMemo (Memo1) en el form. Cambia el código de la unit de la form por éste otro:

 
unit Unit1;
 
interface
 
uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;
 
const
  NombreDLL       = 'HookTeclado.dll';
  CM_MANDA_TECLA  = WM_USER + $1000;
 
type
  THookTeclado=procedure; stdcall;
 
type
  TForm1 = class(TForm)
    Label1: TLabel;
    Memo1: TMemo;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    FicheroM       : THandle;
    PReceptor      : ^Integer;
    HandleDLL      : THandle;
    HookOn,
    HookOff        : THookTeclado;
    procedure LlegaDelHook(var message: TMessage); message  CM_MANDA_TECLA;
  public
    { Public declarations }
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.DFM}
 
procedure TForm1.FormCreate(Sender: TObject);
  begin
  {No queremos que el Memo maneje el teclado...}
  Memo1.ReadOnly:=TRUE;
  HandleDLL:=LoadLibrary( PChar(ExtractFilePath(Application.Exename)+NombreDLL ) );
  if HandleDLL = 0 then raise Exception.Create('No se pudo cargar la DLL');
  @HookOn :=GetProcAddress(HandleDLL, 'HookOn');
  @HookOff:=GetProcAddress(HandleDLL, 'HookOff');
 
  if not assigned(HookOn) or not assigned(HookOff)  then
    raise Exception.Create('No se encontraron las funciones en la DLL'+#13+
                           'Cannot find the required DLL functions');
 
  {Creamos el fichero de memoria}
  FicheroM:=CreateFileMapping( $FFFFFFFF, nil, PAGE_READWRITE, 0, SizeOf(Integer), 'ElReceptor');
 
  {Si no se cre&oacute; el fichero, error}
  if FicheroM=0 then
  raise Exception.Create( 'Error al crear el fichero'+'/Error while create file');
 
  {Direccionamos nuestra estructura al fichero de memoria}
  PReceptor:=MapViewOfFile(FicheroM,FILE_MAP_WRITE,0,0,0);
 
  {Escribimos datos en el fichero de memoria}
  PReceptor^:=Handle;
  HookOn;
end;
 
procedure TForm1.LlegaDelHook(var message: TMessage);
var
  NombreTecla : array[0..100] of char;
  Accion      : string;
begin
  {Traducimos de Virtual key Code a TEXTO}
  GetKeyNameText(Message.LParam,@NombreTecla,100);
 
  {Miramos si la tecla fu&eacute; pulsada, soltada o repetida}
  if ((Message.lParam shr 31) and 1)=1 then
    Accion:='Soltada' {Released}
  else
    if ((Message.lParam shr 30) and 1)=1 then
      Accion:='Repetida'
    else
      Accion:='Pulsada';
 
  Memo1.Lines.Append(Accion+' tecla: '+String(NombreTecla));
end;
 
procedure TForm1.FormDestroy(Sender: TObject);
begin
  {Desactivamos el Hook}
  if Assigned(HookOff) then HookOff;
 
  {Liberamos la DLL}
 
  if HandleDLL<>0 then
    FreeLibrary(HandleDLL);
 
  {Cerramos la vista del fichero y el fichero}
  if FicheroM<>0 then
  begin
    UnmapViewOfFile(PReceptor);
    CloseHandle(FicheroM);
  end;
end;
 
end.
 
 

Graba el proyecto en el mismo directorio del proyecto de la DLL y compila la aplicación.

Si has seguido los pasos hasta aqui, deberás tener en el directorio de los dos proyectos una DLL (HookTeclado.DLL) y el ejecutable de la aplicacion receptora. Ejecutalo, y verás como en el Memo1 irán apareciendo todas las teclas que pulses en Windows.

Comentarios (0)
Para escribir un comentario debes estar registrado