[Dica-Delphi] Capturar teclas digitadas em qualquer local do windows
Autor: Leonam J. Lima | Publicado: 2011-08-08T06:53:00-07:00

Olá pessoal, vai uma dica bem bacana pra quem quer colocar uma especie de atalho dentro do sistema e que funciona com o mesmo minimizado/correndo em segundo plano.

{
O funcionamento eh o seguinte:
# Criamos uma DLL com duas funcoes que exportaremos, uma para instalar o hook e outra para desinstala-lo.

# Ha uma terceira funcao que eh a que executará o hook uma vez instalado (CallBack). Nela, o que faremos eh enviar os dados da mensagem capturado a nossa aplicacao.

A DLL deve saber em todo momento o handle da aplicacao receptora, de modo que faremos que o leia de um arquivo mapeado na memoria que criaremos desde a propria aplicacao.
Eis um exemplo de uso de arquivos mapeados na memoria:

- Compartilhar dados entre dois aplicativos Delphi

Enviaremos os dados desde a DLL para a aplicacao através de uma mensagem do usuario. Tambem se usa esta técnica da seguinte maneira:

- Prevenir duas execucoes simultaneas da sua aplicacao

Bem, vamos ao exemplo:


****** A DLL QUE INSTALA O HOOK: ******

1. Crie o esqueleto de uma DLL (File / New / DLL);

2. Troque o codigo do projeto por este outro abaixo:
}


////// AQUI COMECA O CODIGO DA DLL//////

library HookTeclado;

{
Demonstracao de Hook de teclado a nivel de sistema, Radikal.
Como o que queremos é capturar as teclas pressionadas em qualquer parte
do Windows, precisamos instalar a função CallBack que chamará
o Hook em uma DLL, que é esta mesma.
}

uses
&nbsp; Windows,
&nbsp; Messages;

const
&nbsp; CM_MANDA_TECLA = WM_USER + $1000;

var
&nbsp; HookDeTeclado &nbsp; &nbsp; : HHook;
&nbsp; ArquivoM &nbsp; &nbsp;: THandle;
&nbsp; PReceptor &nbsp; : ^Integer;

function CallBackDelHook( Code &nbsp; &nbsp;: Integer;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; wParam &nbsp;: WPARAM;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; lParam &nbsp;: LPARAM
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ) &nbsp; &nbsp; &nbsp; : LRESULT; stdcall;

{Esta é a funcao CallBack que chamará o hook.}
begin
&nbsp; {Se uma tecla foi pressionada ou liberada}
&nbsp; if code=HC_ACTION then begin
&nbsp; &nbsp;{Observamos se o arquivo existe}
&nbsp; &nbsp;ArquivoM:=OpenFileMapping(FILE_MAP_WRITE,False,'OReceptor');
&nbsp; &nbsp;{Se nao existe, nao enviamos nada para a aplicacao receptora}
&nbsp; &nbsp;if ArquivoM&lt;&gt;0 then begin
&nbsp; &nbsp; &nbsp;PReceptor:=MapViewOfFile(ArquivoM,FILE_MAP_WRITE,0,0,0);
&nbsp; &nbsp; &nbsp;PostMessage(PReceptor^,CM_MANDA_TECLA,wParam,lParam);
&nbsp; &nbsp; &nbsp;UnmapViewOfFile(PReceptor);
&nbsp; &nbsp; &nbsp;CloseHandle(ArquivoM);
&nbsp; &nbsp;end;
&nbsp; end;
&nbsp; {Chamamos ao seguinte hook de teclado da cadeia}
&nbsp; Result := CallNextHookEx(HookDeTeclado, Code, wParam, lParam)
end;

procedure HookOn; stdcall;
{Procedure que instala o hook}
begin
&nbsp; HookDeTeclado:=SetWindowsHookEx(WH_KEYBOARD, @CallBackDelHook, HInstance , 0);
end;

procedure HookOff; &nbsp;stdcall;
begin
&nbsp; {procedure para desinstalar o hook}
&nbsp; UnhookWindowsHookEx(HookDeTeclado);
end;

exports
&nbsp; {Exportamos as procedures...}
&nbsp; HookOn,
&nbsp; HookOff;

begin
end.

////// AQUI TERMINA O CODIGO DA DLL//////

{
3. Salve o projeto com o nome 'HookTeclado.dpr' e compile-o, para gerar a DLL.

É provavel que o Delphi apresente a mensagem de erro:

"Cannot debug project unless a host application is defined. Use the Run|Parameters... dialog box."

Caso esta mensagem apareca, abra o menu Run / Parameters, e preencha o campo *Host Application* com o endereco do executavel do programa que utilizarah esta DLL. Caso o programa ainda nao tenha sido criado, apenas salve a DLL.

MAS LEMBRE-SE!! Apos criar o programa (que faremos na sequencia), retorne ao projeto da DLL e faca isso, caso contrario, o programa NAO FUNCIONARÁ!!


****** APLICACAO QUE RECEBE DADOS DO HOOK ******

1. Crie uma nova aplicacao;

2. Inclua um TMemo(Memo1) no Form1

3. Troque o codigo da Unit do Form por este outro abaixo

}


////// AQUI COMECA O CODIGO DA APLICACAO //////

unit Unit1;

interface

uses
&nbsp; Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

const
&nbsp; NomeDLL &nbsp; &nbsp; &nbsp; = 'HookTeclado.dll';
&nbsp; CM_MANDA_TECLA &nbsp;= WM_USER + $1000;


type
&nbsp; THookTeclado=procedure; stdcall;

type
&nbsp; &nbsp; TForm1 = class(TForm)
&nbsp; &nbsp; Memo1: TMemo;
&nbsp; &nbsp; procedure FormCreate(Sender: TObject);
&nbsp; &nbsp; procedure FormDestroy(Sender: TObject);

private
&nbsp; &nbsp; { Private declarations }
&nbsp; &nbsp; ArquivoM &nbsp; &nbsp; &nbsp; : THandle;
&nbsp; &nbsp; PReceptor &nbsp; &nbsp; &nbsp;: ^Integer;
&nbsp; &nbsp; HandleDLL &nbsp; &nbsp; &nbsp;: THandle;
&nbsp; &nbsp; HookOn,
&nbsp; &nbsp; HookOff &nbsp; &nbsp; &nbsp; &nbsp;: THookTeclado;

&nbsp; &nbsp; procedure LlegaDelHook(var message: TMessage); message &nbsp;CM_MANDA_TECLA;

public
&nbsp; &nbsp; { Public declarations }
end;

var
&nbsp; Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
&nbsp; {Nao queremos que o memo seja editado conforme a tecla pressionada}
&nbsp; Memo1.ReadOnly:=TRUE;

&nbsp; HandleDLL:=LoadLibrary( PChar(ExtractFilePath(Application.Exename)+NomeDLL ) );
&nbsp; if HandleDLL = 0 then raise Exception.Create('Não foi possível carregar a DLL!');

&nbsp; @HookOn :=GetProcAddress(HandleDLL, 'HookOn');
&nbsp; @HookOff:=GetProcAddress(HandleDLL, 'HookOff');

&nbsp; IF not assigned(HookOn) or
&nbsp; &nbsp; &nbsp;not assigned(HookOff) &nbsp;then
&nbsp; &nbsp; &nbsp;raise Exception.Create('Não foram encontradas as funções na DLL');

&nbsp; {Criamos o arquivo de memoria}
&nbsp; ArquivoM:=CreateFileMapping( $FFFFFFFF,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;nil,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;PAGE_READWRITE,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;0,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;SizeOf(Integer),
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;'ElReceptor');

&nbsp; &nbsp; {Se o arquivo nao se criou, erro}
&nbsp; &nbsp; if ArquivoM=0 then
&nbsp; &nbsp; &nbsp; raise Exception.Create( 'Erro ao criar o arquivo');

&nbsp; &nbsp; {Direcionamos nossa estrutura ao arquivo de memoria}
&nbsp; &nbsp; PReceptor:=MapViewOfFile(ArquivoM,FILE_MAP_WRITE,0,0,0);

&nbsp; &nbsp; {Escrevemos dados no arquivo de memoria}
&nbsp; &nbsp; PReceptor^:=Handle;
&nbsp; &nbsp; HookOn;
end;

procedure TForm1.LlegaDelHook(var message: TMessage);
var
&nbsp; &nbsp;NomeTecla : array[0..100] of char;
&nbsp; &nbsp;Acao &nbsp; &nbsp; &nbsp;: string;
begin
&nbsp; {Traduzimos de "Virtual key Code" a "TEXTO"}
&nbsp; GetKeyNameText(Message.LParam,@NomeTecla,100);
&nbsp; {Observamos se a tecla foi pressionada, soltada o repetida}
&nbsp; if ((Message.lParam shr 31) and 1)=1 then
&nbsp; &nbsp; Acao:='Solta'
&nbsp; else
&nbsp; &nbsp; if ((Message.lParam shr 30) and 1)=1 then
&nbsp; &nbsp; &nbsp; Acao:='Repetida'
&nbsp; &nbsp; else Acao:='Pressionada';

&nbsp; Memo1.Lines.Append( Acao+
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ' tecla: '+
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; String(NomeTecla) );
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
&nbsp; {Desativamos o Hook}
&nbsp; if Assigned(HookOff) then
&nbsp; &nbsp; HookOff;

&nbsp; {Liberamos a DLL}
&nbsp; if HandleDLL&lt;&gt;0 then
&nbsp; &nbsp; FreeLibrary(HandleDLL);

&nbsp; {Fechamos a vista do arquivo e o arquivo}
&nbsp; if ArquivoM&lt;&gt;0 then begin
&nbsp; &nbsp; UnmapViewOfFile(PReceptor);
&nbsp; &nbsp; CloseHandle(ArquivoM);
&nbsp; end;

end;

end.

////// AQUI TERMINA O CODIGO DA APLICACAO //////

{
Grave o projeto no mesmo diretorio da DLL e compile-o.

Se voce seguiu todos os passos ateh aqui, deverah ter no diretorio dos projetos uma DLL (HookTeclado.DLL) e o executavel da aplicacao receptora.
Execute a aplicacao e observe como o Memo1 armazena o historico das teclas pressionadas em qualquer parte do Windows.
}

Referência: http://www.planetadelphi.com.br/