|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|
|
|
Utilisation de la file de messages
|
||||||||||||||
|
Contrôle du clavier et de la souris |
||||||||||||||
|
Problèmes liés à la simulation
|
||||||||||||||
|
|
|
Depuis ces dernières années, les applications multimédias fleurissent à foison : lecteurs MP3, DVD, DivX et autres logiciels de TV sont désormais monnaie courrante sur les ordinateurs personnels qui autrefois étaient destinnés à installer Office et le dernier jeu à la mode. Si les interfaces sont de plus en plus conviviales, elles nécessitent toujours que l'utilisateur vienne interagir soit avec le clavier, soit avec la souris pour réaliser les opérations usuelles telles que lecture, pause, rembobinage, changement de chaines, etc ... Nous avons donc vu apparaître des claviers multimédia aux merveilleuses touches complémentaires pour s'occuper de ces fonctions (comme si 105 touches c'était pas assez ! ;-) ). Cependant, si on regarde bien, la prise en charge de ces touches par les applications n'est pas toujours implémentée et quand elle l'est, elle ne l'est pas toujours correctement. En effet, il arrive que certaines applications réagissent ou non suivant le média lu ou suivant le temps écoulé depuis la dernière "vraie" action du clavier ou de la souris. Les développeurs ont donc eu l'idée de simuler les racourcis claviers des applications multimédia pour mieux se faire comprendre des applications. Cependant, il n'est pas toujours évident de réaliser cette simulation et c'est pourquoi nous allons diviser cette étude en deux parties : simulation par la file de message de Windows et prendre le contrôle du clavier et de la souris |
|
Le principal moyen de communication utilisé par Windows et les applications écrites pour ce système d'exploitation est la file de message associée à chaque fenêtre. En effet, chaque fois qu'un évènement se produit, Windows le traite en envoyant un message, soit à lui-même, soit à un autre programme. Le programme procède alors au traitement de ce message si celui-ci a un interêt. Nous allons étudier l'exemple suivant : vous êtes dans votre navigateur favoris et vous tapez l'adresse : "http://www.developpez.net/forums" puis vous validez avec la touche ENTREE ![]() A chaque pression d'une touche, Windows va envoyer trois messages au navigateur : WM_KEYDOWN qui va indiquer qu'une touche a été enfoncée, WM_CHAR qui va préciser qu'un caractère à été donné à l'application et finalement WM_KEYUP qui va indiquer que la touche a été relachée. Chacun des ces messages est placé dans la file de messages du navigateur qui va les analyser un à un. Imaginons qu'il ne se préoccupe que du message WM_CHAR et qu'il ignore les autres. A chacun de ces messages, il va analyser les informations fournies par windows en complément du message et ajouter le caractère indiqué à la chaine de caractères représentant l'URL de la ressource désirée. Au moment ou il va recevoir le caractère "ENTREE", il va lancer la recherche. L'interêt d'une telle étude est de comprendre par quel moyen on va pouvoir dire a une application multimédia (ou une autre, mais l'interêt serait plus limité) "l'utilisateur a pressé la touche X : lance la lecture". En effet, vous l'avez surement remarqué, la majorité des applications met à notre disposition des racourcis clavier pour accéder rapidement aux principales fonctionnalités ; nous allons les simuler afin de contrôler les applications. |
|
Simuler l'interaction de l'utilisateur par la file de messages nécessite deux étapes que nous allons détailler ci-après : déterminer la cible et envoyer le message. Tout d'abord, il faut déterminer le handle de l'application à laquelle on veut envoyer un message. Un handle est une sorte d'identifiant utilisé par Windows pour identifier chaque composant manipulé par l'interface. Deux possibilités s'offrent à nous : envoyer le message à une fenêtre précise ou envoyer le message à la fenêtre au premier plan. Récupérer le handle de la fenêtre au premier planC'est la chose la plus simple. Cela se fait par un appel à la fonction GetForegroundWindow(). Cette fonction ne prend pas de paramètre et renvoie une valeur de type HWND (handle de fenêtre). Cette valeur vaut NULL en C ou nil en Pascal si la fonction a échoué. Voici un exemple d'utilisation en langage C : #include <windows.h>
#include <stdio.h>
int main(int argc, char * argv[])
{
HWND hFore = GetForegroundWindow();
printf("le handle de la fenêtre au premier plan est : 0x%X",hFore);
return 0;
} Récupérer le handle d'une fenêtre préciseCela nécessite de connaître le nom de la "classe" de composant à laquelle la fenêtre appartient. Le nom de classe ou class name correspond à un identifiant qu'on associe à une fenêtre pour la distinguer d'une autre. Par exemple, WinAMP dans ses version 1.x et 2.x avait pour nom de classe "Winamp v1.x" ; le nom de classe du lecteur Windows Media est "Media Player 2". Pour récupérer le handle d'une fenêtre dont on connait le class name, on utilise la fonction FindWindow(). Cette fonction prend deux paramètres : un pointeur vers une chaine de caractères contenant le class name de la fenêtre recherchée (char * en C, character^ en Pascal) et un pointeur vers une chaine de caractères contenant le titre de la fenêtre recherchée (optionel, mettre NULL en C ou nil en Pascal). Elle renvoie une valeur de type HWND (handle de fenêtre). Cette valeur vaut NULL en C ou nil en Pascal si la fonction a échoué. Voici un exemple d'utilisation en langage C : #include <windows.h>
#include <stdio.h>
int main(int argc, char * argv[])
{
HWND hMediaplayer = FindWindow("Media Player 2",NULL);
printf("le handle de la fenêtre de media player 2 est : 0x%X",hMediaplayer);
return 0;
}
Nous allons maintenant voir quel message envoyer et comment l'envoyer. Les différents messages pour le clavier et la sourisIl existe trois messages pour répertorier les actions du clavier, trois messages pour chacun des trois boutons de la souris et un message pour la molette. Le clavier : WM_KEYDOWN
: c'est le premier message qu'envoie Windows à une application
lorsqu'on enfonce une touche du clavier. Nous avons donc trois messages à notre disposition pour simuler des actions au clavier. En général, l'utilisation de WM_KEYDOWN est suffisante, mais il arrive avec certaines applications (WinAMP 3, Lecteur Windows Media, ...) que ce ne soit pas suffisant. En effet, certaines applications prennent en compte l'envoi du premier message puis ignorent les suivants ; pour éviter cela on peut, quand cela est nécessaire, simuler également le relâchement de la touche avec WM_KEYUP. Il est déconseillé de le faire dans tous les cas car il arrive régulièrement que cela ait pour effet de dédoubler l'action et le résultat ne sera donc pas celui attendu. La souris : WM_LBUTTONDOWN
: ce message correspond à l'enfoncement du bouton gauche de la
souris. Chacun des trois premiers messages a son équivalement pour les bouton du milieu (mollette) et de droite en remplaçant respectivement L par M ou R. L'interêt de ces trois messages est assez limité car pour simuler le clique sur un bouton, il faut connaître son identificateur (unique et propre à l'application à laquelle il appartient) et c'est un message du type WM_COMMAND qu'il faut générer. Par contre, WM_MOUSEWHELL peut s'avérer utile car il est fréquent que la molette de la souris permette de contrôler le volume ou de faire défiler des images. Comment envoyer un messageL'objectif de cette partie est plus d'expliciter les paramètres qu'on va passer à la fonction PostMessage() que d'expliquer son fonctionnement. Si j'ai choisi d'utiliser PostMessage() plutôt que SendMessage(), c'est parceque j'ai considéré qu'on n'attendait pas de résultat de la part de l'application qu'on cherche à commander. Le clavier :Les messages utilisés pour manipuler le clavier utilisent tous les même paramètres : le code de la touche d'une part et des informations de répétition et / ou d'état des touches systèmes qui ne sont généralement pas utilisées par les applications d'autre part. Nous n'allons détailler que le premier puisque le second ne nous avancera pas dans la résolution de notre problème et que toutes les informations sont disponnibles dans le SDK Microsoft (Win32 SDK). Le premier paramêtre est donc le code "virtuel" de la touche pressée. Les codes sont des valeurs prédéfinies dans les entêtes windows et commencent par VK. Voici un tableau récapitulatif :
Il arrive que les éditeurs de compilateurs ne définissent pas les constantes VK_A à VK_Z et cela pour la simple et bonne raison que c'est en réalité la valeur du caractère majuscule correspondant. Dans ce cas, il suffit de remplacer VK_A par 'A' et ainsi de suite pour les atres lettres de l'alphabet. Voyons un exemple d'utilisation de la fonction PostMessage(). Ce programme va rechercher une instance du lecteur Windows Media et lancer la lecture en simulant la pression de la barre d'espace puis le mettre en pause: #include <windows.h>
int main(int argc, char * argv[])
{
HWND hMPlayer = FindWindow("Media Player 2",NULL);
SetForegroundWindow(hMPlayer);
Sleep(100);
PostMessage(hMPlayer,WM_KEYDOWN,VK_SPACE,0);
Sleep(5000);
PostMessage(hMPlayer,WM_KEYUP,VK_SPACE,0);
return 0;
} Nous savons maintenant simuler des actions du clavier, mais il n'est cependant toujours pas possible de simuler la pression d'un raccourcis clavier plus complexe qu'une touche tel que ALT + ENTREE. La souris :Comme je l'ai dit dans la partie précédente, l'interêt des évênements WM_LBUTTONDOWN, WM_LBUTTONUP et WM_LBUTTONDBLCLK est minime dans notre démarche, nous n'allons donc étudier que l'utilisation du message WM_MOUSEWHEEL. Le message
WM_MOUSEWHEEL fournit quatre informations de 16 bits chacunes réparties
entre les mots hauts et bas des deux arguments (32 bits chacuns) de PostMessage()
restants à notre disposition. Voyons quelles sont les informations
à fournir : Voyons maintenant comment construire les arguments de PostMessage() à partir de ces informations. La position du curseur doit être placée dans le second paramètre et les autres informations dans le premier. L'exemple ci-dessous montre comment le faire en C : int iPosX,iPosY; int iMasqueTouches; int iDeplMolette; DWORD dwWParam,dwLParam; HWND hWindow; // initialisation des variables avec les valeurs souhaitées dwLParam = iPosY << 16 | iPosX; // on effectue un décallage de 16 bits vers la gauche dwWParam = iDeplMolette << 16 | iMasqueTouches; PostMessage(hWindow,WM_MOUSEWHEEL, dwWParam, dwLParam);
Une autre solution peut consister à utiliser une structure : struct _dword {
short w1;
short w2;
} dwWParam, dwLParam;
int iPosX,iPosY;
int iMasqueTouches;
int iDeplMolette;
HWND hWindow;
// initialisation des variables avec les valeurs souhaitées
dwLParam.w1 = iPosY;
dwLParam.w2 = iPosX;
dwWParam.w1 = iDeplMolette;
dwWParam.w2 = iMasqueTouches;
PostMessage(hWindow,WM_MOUSEWHEEL, (DWORD)(dwWParam), (DWORD)(dwLParam)); Voilà, nous avons maintenant tous les éléments dont nous avions besoin pour simuler des actions simples à l'attention d'une application déterminée. Cependant, cela n'est pas suffisant car on ne peut toujours pas faire de simulation globale, ni déplacer la souris : il n'est donc pas possible d'interfacer un périphérique multimédia tel qu'un joystick avec la sourris et le clavier. C'est pourquoi nous allons voir dans une deuxième partie comment simuler le clavier et la souris à un niveau beaucoup plus proche de la machine. |
|
Comme nous l'avons vu dans la partie précédente, il n'est pas toujours possible d'obtenir le résultat attendu avec la file de messages de Windows. On va donc devoir intervenir à un niveau beaucoup plus proche du matériel. En effet, nous allons agir comme le ferait un pilote de périphérique pour la souris ou le clavier. Vous devez être en train de vous dire houlala ... il va nous sortir des commandes en assembleur de derrière les fagots. Rassurez-vous, il n'en est rien : nous n'utiliserons que des fonctions de l'API Win32. Nous allons comme à l'habitude commencer par traiter le sujet du clavier, puis nous verrons que le fonctionnement de la souris y ressemble beaucoup. Le clavier dans tous ses étatsDans cette partie, nous allons voir comment faire croire au système qu'une touche est pressée ou non. Si je parle ici de système et non pas d'application, c'est parceque la fonction que nous allons utiliser n'a pas conscience d'une telle chose. Pour elle, il n'y à qu'un seul logiciel en cours : Windows ; et elle se contente de lui indiquer les modification d'état du clavier. Il faudra donc prendre en compte lors des futurs développements qu'il n'est pas possible de dire "je veux simuler telle action pour telle fenêtre". Pour comprendre le fonctionnement du principe que nous allons voir, il faut considérer que le programme qu'on est en train de réaliser est en réalité le pilote d'un périphérique réel et agir en tant que tel. Les pilotes de périphérique des claviers utilisent la fonction keybd_event() pour indiquer au système l'état des différentes touches. Comme son nom l'indique, cette fonction ne signalera au système que les changements d'état des touches. Il n'y aurra donc pas à intercepter les significations d'état que pourraient être amenés à faire d'autres pilotes de périphériques si le système avait voulu qu'on l'informe toutes les n millisecondes de l'état de chacune des touches. Voyons maintenant quels sont les paramètres que la fonction keybd_event() attend. La fonction keybd() prend quatre paramètres : le code touche virtuel (ENTREE, A, F1, ...), le code matériel de la touche (touche n° 1, n°2, ...), un masque indiquant si la touche est une touche étendue et si la touche est relâchée et finalement un double-mot contenant des information supplémentaires. Les codes de touches virtuels ont été présentés dans la partie précédente. Les codes matériels de touche ne sont pas utiles pour nous car ils correspondent à une étape antérieure des pilotes de périphérique ; nous le laisserons donc à 0. Le masque est composé à partir de KEYEVENTF_EXTENDEDKEY qui indique que la touche est soit AltGr soit Ctrl droite, et KEYEVENTF_KEYUP qui indique que la touche est relâchée. Finalement le dernier paramètre est une donnée complémentaire qui ne nous sert à rien non plus, on le laissera donc à 0. Voici un exemple dans lequel on considère que par exemple le lecteur Windows Media est en train de lire une video. Notre programme exemple va simuler la pression de Alt + Entree pour faire passer le lecteur en mode plein écran. #include <windows.h>
int main(int argc, char *argv[])
{
SetForegroundWindow(FindWindow("Media Player 2",NULL));
Sleep(100);
keybd_event(VK_LMENU,0,0,0);
keybd_event(VK_RETURN,0,0,0);
keybd_event(VK_RETURN,0,KEYEVENTF_KEYUP,0);
keybd_event(VK_LMENU,0,KEYEVENTF_KEYUP,0);
return 0;
}
Les poules picorent, la souris cliquetteDe la même façon que précédemment, nous allons faire croire au système qu'il y a eu un click ou un mouvement de souris. Contrairement au cas du clavier, nous allons avoir besoins de plusieurs fonctions. Nous allons donc partager cette partie en deux thèmes : le déplacement du curseur d'une part et les clicks et autres tours de molettes d'autre part. Déplacer le curseur :Pour déplacer le curseur, nous allons utiliser la fonction SetCursorPos(). Cependant, si on veut déplacer le curseur, il va d'abord falloir déterminer sa position actuelle puis indiquer à Windows quelle est sa nouvelle position ; c'est ce que nous allons faire avec la fonction GetCursorPos(). La fonction GetCursorPos() prends en paramètre un pointeur sur une structure POINT qui contient deux entiers longs (type LONG de l'API) : la position x et y du curseur. La fonction SetCursorPos() prends en paramètre deux entiers les coordonnées x et y ou placer le curseur. Le point d'origine est le coin supérieur gauche de l'écran. Voici un exemple qui va récupérer la position du curseur et le déplacer en fonction de la touche flèchée enfoncée : #include <windows.h>
int main(int argc, char *argv[])
{
POINT pt;
BOOL bContinue = TRUE;
const SHORT Mask = 32768;
while (bContinue)
{
if (GetKeyState(VK_ESCAPE) & Mask)
bContinue = FALSE;
GetCursorPos(&pt);
if (GetKeyState(VK_UP) & Mask)
pt.y -= 1;
if (GetKeyState(VK_DOWN) & Mask)
pt.y += 1;
if (GetKeyState(VK_LEFT) & Mask)
pt.x -= 1;
if (GetKeyState(VK_RIGHT) & Mask)
pt.x += 1;
SetCursorPos(pt.x,pt.y);
Sleep(10);
}
return 0;
} Simuler les clicks et la mollettePour simuler les clicks et la molette, nous allons utiliser une fonction très semblable à celle utilisée pour le clavier : mouse_event(). Cette fonction prend cinq paramètres. Le premier est un double-mot qui contient des bits drapeaux pour indiquer l'évènement qui a eu lieu. Le second et le troisième paramètre sont les coordonnées x et y du curseur au moment ou l'évènement a eu lieu. Le quatrième paramètre est la valeur de la rotation de la mollette. Le dernier paramètre est une information complémentaire sur 32 bits dont nous ne nous servirons pas. Revenons
un instant sur les drapeaux qui décrivent l'évènement.
Voyons maintenant un exemple. Nous allons complêter l'exemple précédent en ajoutant les fonctionnalités suivantes : les touches 1, 2 et 3 du pavé numérique seront les trois boutons de la souris, le - du pavé numérique sera la mollette vers le haut et le + la mollette vers le bas. #include <windows.h>
int main(int argc, char *argv[])
{
POINT pt;
BOOL bContinue = TRUE;
SHORT Mask = 32768;
DWORD dwEventFlags;
DWORD dwData;
while (bContinue)
{
dwData = 0;
dwEventFlags = MOUSEEVENTF_ABSOLUTE;
if (GetKeyState(VK_ESCAPE) & Mask)
bContinue = FALSE;
GetCursorPos(&pt);
if (GetKeyState(VK_NUMPAD1) & Mask)
{
if (!(GetKeyState(VK_LBUTTON) & Mask))
dwEventFlags |= MOUSEEVENTF_LEFTDOWN;
}
else if (GetKeyState(VK_LBUTTON) & Mask)
// Ce test est nécessaire pour s'assurer qu'on n'envoie pas plus
// de relevés de touche que d'abaissements
dwEventFlags |= MOUSEEVENTF_LEFTUP;
if (GetKeyState(VK_NUMPAD2) & Mask)
{
if (!(GetKeyState(VK_MBUTTON) & Mask))
dwEventFlags |= MOUSEEVENTF_MIDDLEDOWN;
}
else if (GetKeyState(VK_MBUTTON) & Mask)
dwEventFlags |= MOUSEEVENTF_MIDDLEUP;
if (GetKeyState(VK_NUMPAD3) & Mask)
{
if (!(GetKeyState(VK_RBUTTON) & Mask))
dwEventFlags |= MOUSEEVENTF_RIGHTDOWN;
}
else if GetKeyState(VK_R3BUTTON) & Mask)
dwEventFlags |= MOUSEEVENTF_RIGHTUP;
if (GetKeyState(VK_SUBTRACT) & Mask)
{
dwEventFlags |= MOUSEEVENTF_WHEEL;
dwData += WHEEL_DELTA;
}
else if (GetKeyState(VK_ADD) & Mask)
{
dwEventFlags |= MOUSEEVENTF_WHEEL;
dwData -= WHEEL_DELTA;
}
if (GetKeyState(VK_UP) & Mask)
pt.y -= 1;
if (GetKeyState(VK_DOWN) & Mask)
pt.y += 1;
if (GetKeyState(VK_LEFT) & Mask)
pt.x -= 1;
if (GetKeyState(VK_RIGHT) & Mask)
pt.x += 1;
SetCursorPos(pt.x,pt.y);
mouse_event(dwEventFlags,pt.x,pt.y,dwData,0);
Sleep(10);
}
return 0;
} Maintenant que nous avons vu touts les moyebs mis à notre disposition pour résoudre les problèmes que nous pose le contrôle d'applications tierces, il ne nous reste plus qu'à résoudre certains problèmes qui peuvent apparaître dans leur mise en oeuvre. |
|
L'objectif d'une application qui contrôle le clavier ou la souris, n'est pas toujours de simuler un comportement, mais peut également être de réagir à un comportement de l'utilisateur. Par ailleurs, il peut être utile d'étudier le comportement d'une application pour mieux la contrôler par la suite. Nous allons voir comment faire cela au travers des hooks. Nous verrons tout d'abord ce qu'est un hook, les différents types de hooks que propose Windows, les prérequis pour la mise en place d'un hook, puis comment les mettre en place. Qu'est-ce qu'un hook ?Un hook est un moyen de contrôler ce qui transite par le système de gestion de messages de Windows : c'est comme si on dénudait un fil électrique et qu'on y branchait un appareil de mesure pour voir ce qui s'y passe. La mise en place d'un hook va permettre de demander au système d'exploitation de faire un appel à une fonction de notre cru à chaque fois qu'il rencontrera un certain type de message et avant de le transmettre à l'application cible. Les différents types de hooksL'API Win32 propose actuellement 11 types de hooks dont deux sont spécialisés en deux sous types chacun soit un total de 13 hooks possibles. Chaque type de hook permet à une application de surveiller différents aspects du mécanisme de gestion des messages de Windows. Voici un bref descriptif de chacun d'entre eux :
Ce tutoriel étant destiné à la gestion du clavier et de la souris, nous ne verrons que les hooks WH_MOUSE et WH_KEYBOARD, mais les autres hooks s'utilisent exactement de la même façon. Prérequis à la mise en place d'un hookDeux problèmes se posent lorsqu'on veut créer un hook. Premièrement, si celui-ci est destiné à observer les événement du système complet, on ne peut pas implémenter la fonction associée à ce crochet dans un programme séparé mais on est obligé de le faire dans une DLL séparée. Exactement comme un programme normal qui ne peut se servir d'une fonction développée par un tiers que si elle se trouve dans une DLL (et autres ActiveX...) et non dans un programme normal. Deuxièmement, la fonction qui gère le hook doit transmettre le message qu'elle a reçu dans la chaîne et pour cela elle a besoin du handle du hook ; hors, si on pourrait être tenté de stocker cette information de manière globale, cela ne résoud en rien le problème car il existe en réalité une instance de la DLL par processus recevant des messages ce qui implique que le handle ne serait connu que du processus appelant et que cela provoquerait des erreurs dans les autres applications. On va donc devoir créer une zone de mémoire nommée afin que toutes les instances de la DLL puissent accéder à ce handle de hook. Dans l'exemple exposé dans la partie suivante, on considérera qu'on évolue sous Borland C++ Builder et qu'on a utilisé l'expert DLL. Pour des informations complémentaires concernant la création de DLL dans BC++ Builder, voyez ce tutoriel de LFE Mise en place d'un hookPour mettre en place un hook, il suffit d'un appel à la fonction SetWindowsHookEx() et pour l'arrêter nous ferrons appel à UnhookWindowsHookEx(). SetWindowsHookEx() prend quatre arguments : le type de hook à mettre en place, le pointeur sur la fonction à utiliser pour le hook, l'instance de la DLL contenant la fonction de hook et finalement l'identifiant du thread à hooker. Elle renvoie le handle du hook créé qui a pour la valeur NULL en cas d'échec. UnhookWindowsHookEx() prends comme seul paramètre le handle du hook à défaire et renvoie la constante FALSE si elle échoue. Voyons le prototype de la fonction de hook : LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam); Pour chaque type de hook, ces trois paramètres ont une signification particulière : nCode est un indicateur pour l'action a effectuer et wParam et lParam contiennent les informations du message qui a été envoyé. La valeur renvoyée par cette fonction doit être celle renvoyée par la fonction de propagation du message, CallNextHookEx(). Cette fonction prends en paramètres le handle du hook qu'on vient de gérer, suivi des arguments reçus par HookProc(), éventuellement modifiés. Si le message est intercepté par le hook (pas d'appel à CallNextHookEx()), la valeur renvoyée par HookProc() devrait être nulle (0L en C). Détaillons maintenant le contenu des paramètres pour les fonctions associées à WH_KEYBOARD et à WH_MOUSE. -
WH_KEYBOARD : -
WH_MOUSE :
Voyons maintenant un exemple de DLL permettant le hook du clavier et de la souris. Celle-ci sera composé de cinq fonctions : la fonction d'inistialisation (fermeture) de la DLL qui attachera (détachera) la zone de mémoire partagée, la fonction d'initialisation des hooks, la fonction de destruction des hooks et les deux fonctions de crochetage (clavier et souris). Voici le code de la DLL. Le fichier compilé sera nommé "hooksClSo.dll". #include <windows.h> // Définition de la structure de la zone mémoire partagée
#ifndef appliH
#define appliH
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
typedef void (*TInitFunc)(HWND);
typedef void (*TEndFunc)();
#define WMAP_KEYBDHOOKMSG WM_APP + 1
class TForm1 : public TForm
{
__published: // Composants gérés par l'EDI
TCheckBox *CheckBox1;
TLabel *Label1;
void __fastcall CheckBox1Click(TObject *Sender);
void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
private: // Déclarations utilisateur
TInitFunc InitHooks; // fonction d'initialisation des hooks
TEndFunc EndHooks; // fonction de suppression des hooks
HINSTANCE hinstDLL; // instance de la DLL
bool bHook; // Les hooks ont-ils été initialisés ?
int nbEntree; // compteur de pressions pour le gestionnaire de messages
public: // Déclarations utilisateur
void __fastcall PressionEntree(TWMNoParams &p);
__fastcall TForm1(TComponent* Owner);
protected: // déclaration du gestionaire de messages
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER(WMAP_KEYBDHOOKMSG, TWMNoParams, PressionEntree)
END_MESSAGE_MAP(TForm)
};
extern PACKAGE TForm1 *Form1;
#endif
Voyons maintenant du côté du source : #include <vcl.h>
#include "appli.h"
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
bHook = false; // Les hooks ne sont pas initialisés
hinstDLL = LoadLibrary("hooksClSo.dll"); // Chargement de la librairie
if (!hinstDLL) // Erreur lors du chargement de la librairie ?
Application->MessageBox("Impossible de charger la librairie.","gloups",MB_OK);
else
{
// On récupère les adresses des fonctions
InitHooks = (TInitFunc)GetProcAddress(hinstDLL, "_InitHook");
EndHooks = (TEndFunc)GetProcAddress(hinstDLL, "_EndHook");
}
}
void __fastcall TForm1::CheckBox1Click(TObject *Sender)
{
if (!hinstDLL) // La librairie n'est pas chargée, inutile de continuer
Close();
else if (CheckBox1->Checked) // Activer les hooks
{
nbEntree = 0; // On réinitialise le compteur
InitHooks(Form1->Handle); // On initialise les hooks
}
else // Désactiver les hooks
EndHooks(); // On supprime les hooks
}
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
if (bHook) // Si les hooks sont actifs, on les supprime avant de quitter
EndHooks();
Action = caFree;
}
void __fastcall TForm1::PressionEntree(TWMNoParams &p)
{
nbEntree++; // On a reçu un message du hook : on incrémente et on affiche le compteur
Label1->Caption = AnsiString("Pressions de ENTREE : ") + nbEntree;
} Une fois compilé, cet exemple permettra de compter les pressions et relâchements de la touche ENTREE, et jouera le son "par défaut" de Windows à chaque click de souris. |
Copyright © 2000-2012 - www.developpez.com