KeyToggleStart: Yet another usage for keyboard hook

Hello Windows Mobile Users

recently the following was requested:

How can I start an application by just hitting some keys in sequence?

The answer: Just use a keyboard hook.

So I started to code this hook tool based on my KeyToggleBoot2 code. There was not too much to change. The new tool is called KeyToggleStart and it is configured by the registry:

            REGEDIT4

            [HKEY_LOCAL_MACHINE\Software\Intermec\KeyToggleStart]
            "ForbiddenKeys"=hex:\
                  72 73 00
            ;max 10 keys!
            "KeySeq"="123"
            "Timeout"=dword:00000003
            "LEDid"=dword:00000001
            "Exe"="\\Windows\\iexplore.exe"
            "Arg"=""

Reg keys meaning:

Forbiddenkeys is just an addon feature: key codes entered in this list will not be processed any more by your Windows Mobile device. For example, to disable the use of the F3(VK_TTALK) and F4 (VK_TEND) keys you have to enter a binary list of 0x72,0x73,0x00 (the zero is needed to terminate the list).

KeySeq list the char sequence you want to use to start an application. For example if this is the string “123”, everytime you enter 123 in sequence within the given time, the application defined will be started.

TimeOut is the time in seconds you have to enter the sequence. So do not use a long key sequence as “starteiexplorenow” and a short timeout (except you are a very fast type writer). The timeout is started with the first char matching and ends after the time or when you enter a non-matching char of the sequence.

With LEDid you can specify a LED index number. LED’s on Windows Mobile are controlled by an index number, each LED has one or more ID assigned to it. So, with LEDid you can control, which LED will lit, when the matching process is running. You can even find an ID to control a vibration motor, if your Windows Mobile device is equipped with one.

The Exe registry string value is used to specify which application will be started when the key sequence is matched.

If the application you want have to be started needs some arguments, you can enter these using the Arg registry value.

When you start the KeyToggleStart tool, you will not see any window except for a notification symbol on your Start/Home screen of the device.

If you tap this icon (redirection sign) you have the chance to end the hook tool.

The code itself is nothing special. Only the sequence matching code is a little bit special. As the chars in the sequence do not match keystrokes in a ont-to-one manner, I have to check the shift state of the keyboard and if the current char of the seqeunce is a char needing a shift. For example: the asterisk char (“*”) is a combination of the “8” key plus the Shift key. So, if you have the “*” in the char sequence, the code has to check for the “8” key and if the keyboard is in shift state:

// The command below tells the OS that this EXE has an export function so we can use the global hook without a DLL
__declspec(dllexport) LRESULT CALLBACK g_LLKeyboardHookCallback(
   int nCode,      // The hook code
   WPARAM wParam,  // The window message (WM_KEYUP, WM_KEYDOWN, etc.)
   LPARAM lParam   // A pointer to a struct with information about the pressed key
)
{
    /*    typedef struct {
        DWORD vkCode;
        DWORD scanCode;
        DWORD flags;
        DWORD time;
        ULONG_PTR dwExtraInfo;
    } KBDLLHOOKSTRUCT, *PKBDLLHOOKSTRUCT;*/

    // Get out of hooks ASAP; no modal dialogs or CPU-intensive processes!
    // UI code really should be elsewhere, but this is just a test/prototype app
    // In my limited testing, HC_ACTION is the only value nCode is ever set to in CE
    static int iActOn = HC_ACTION;
    static bool isShifted=false;

#ifdef DEBUG
    static TCHAR str[MAX_PATH];
#endif

    PKBDLLHOOKSTRUCT pkbhData = (PKBDLLHOOKSTRUCT)lParam;
    //DWORD vKey;
    if (nCode == iActOn)
    {
        //only process unflagged keys
        if (pkbhData->flags != 0x00)
            return CallNextHookEx(g_hInstalledLLKBDhook, nCode, wParam, lParam);
        //check vkCode against forbidden key list
        if(pForbiddenKeyList!=NULL)
        {
            BOOL bForbidden=false;
            int j=0;
            do{
                if(pForbiddenKeyList[j]==(BYTE)pkbhData->vkCode)
                {
                    bForbidden=true;
                    DEBUGMSG(1, (L"suppressing forbidden key: 0x%0x\n",pkbhData->vkCode));
                    continue;
                }
                j++;
            }while(!bForbidden && pForbiddenKeyList[j]!=0x00);
            if(bForbidden){
                return true;
            }
        }

        SHORT sShifted = GetAsyncKeyState(VK_SHIFT);
        if((sShifted & 0x800) == 0x800)
            isShifted = true;
        else
            isShifted = false;

        //check and toggle for Shft Key
        //do not process shift key
        if (pkbhData->vkCode == VK_SHIFT){
            DEBUGMSG(1, (L"Ignoring VK_SHIFT\n"));
            return CallNextHookEx(g_hInstalledLLKBDhook, nCode, wParam, lParam);
        }

        //################################################################
        //check if the actual key is a match key including the shift state
        if ((byte)pkbhData->vkCode == (byte)szVKeySeq[iMatched]){
            DEBUGMSG(1 , (L"==== char match\n"));
            if (bCharShiftSeq[iMatched] == isShifted){
                DEBUGMSG(1 , (L"==== shift match\n"));
            }
            else{
                DEBUGMSG(1 , (L"==== shift not match\n"));
            }
        }

        if( wParam == WM_KEYUP ){
            DEBUGMSG(1, (L"---> szVKeySeq[iMatched] = 0x%02x\n", (byte)szVKeySeq[iMatched]));

            if ( ((byte)pkbhData->vkCode == (byte)szVKeySeq[iMatched]) && (isShifted == bCharShiftSeq[iMatched]) ) {

                //the first match?
                if(iMatched==0){
                    //start the timer and lit the LED
                    LedOn(LEDid,1);
                    tID=SetTimer(NULL, 0, matchTimeout, (TIMERPROC)Timer2Proc);
                }
                iMatched++;

                DEBUGMSG(1, (L"iMatched is now=%i\n", iMatched));
                //are all keys matched
                if (iMatched == iKeyCount){
                    //show modeless dialog
                    DEBUGMSG(1, (L"FULL MATCH, starting ...\n"));
                    PostMessage(g_hWnd, WM_SHOWMYDIALOG, 0, 0);
                    //reset match pos and stop timer
                    DEBUGMSG(1, (L"FULL MATCH: Reset matching\n"));
                    LedOn(LEDid,0);
                    iMatched=0; //reset match pos
                    KillTimer(NULL, tID);
                    //return CallNextHookEx(g_hInstalledLLKBDhook, nCode, wParam, lParam);
                }
                //return -1; //do not forward key?
            }
            else
            {
                KillTimer(NULL, tID);
                LedOn(LEDid,0);
                iMatched=0; //reset match pos
                DEBUGMSG(1, (L"FULL MATCH missed. Reseting matching\n"));
            }
        } //if wParam == WM_KEY..
    }
    return CallNextHookEx(g_hInstalledLLKBDhook, nCode, wParam, lParam);
}

Enjoy the code

Download VS2008 Source code for Windows Mobile 6.5.3 SDK KeyToggleStart (1627 downloads )

Download KeyToggleStart executable (Windows Mobile 6) KeyToggleStart WM6 Executable (1507 downloads )

Downloads updated 21. Aug 2019