Link to home
Start Free TrialLog in
Avatar of njitram
njitram

asked on

speed up SetWindowsHookEx, or alternative?

Hello,

I have a program that runs in the background and uses Windows Hooks to look at the keyboard/mouse usage.

I use SetWindowsHookEx(), listening to WH_KEYBOARD_LL and WH_MOUSE_LL. However hooks tend to slow down systems pretty much, especially when playing games.

My question, is there a way to speed up processing these hooks, or are there better alternatives (besides DirectInput), I appriciate any feedback!
Avatar of grg99
grg99

The hook itself is mighty speedy;  it's waht you do in thehook procedure that can slow  things down.

How about you show us the code?
Avatar of njitram

ASKER

Alright, below is my code. It does quite alot when it's called, and everything works, it's just slow when playing games such as Half-life. I switched this part over from a DirectInput program, and in there it worked perfectly fast.

I hook it with:

g_hKbHook            = ::SetWindowsHookEx(WH_KEYBOARD_LL,(HOOKPROC)kbProc, AfxGetApp()->m_hInstance, 0);


LRESULT CALLBACK
kbProc (int iCode, WPARAM wParam, LPARAM lParam)
{
    static HWND activeWnd = 0;

    if (iCode < 0) {
       return CallNextHookEx(g_hKbHook, iCode, wParam,lParam);
    }
    else {
        if(!pFrame->m_pAppWnd->state.bCountkeys)
        {
             return 0;
        }

        if (iCode == HC_ACTION)
        {
            KBDLLHOOKSTRUCT kbDllHookStruct = *(KBDLLHOOKSTRUCT*)lParam;
            if (wParam == WM_SYSKEYDOWN || wParam == WM_KEYDOWN)
            {
                if ((kbDllHookStruct.flags & LLKHF_INJECTED) != 0)
                {
                    return CallNextHookEx(g_hKbHook, iCode, wParam, lParam);
                }

                if ((GetKeyState(kbDllHookStruct.vkCode) & 0x8000) == 0)
                {
                    pFrame->KeyCount.getAccess();
                    pFrame->KeyCount.increment();
                    pFrame->KeyCount.releaseAccess();

                    pFrame->m_tray.SetIcon(IDI_TRAYICON_KEYDOWN);

                    pFrame->bKeyHitSinceSave = TRUE;

                    UINT tempkeys = 0;
                    CQuickCrypt crypt, crypto;
                    CString str;

                    str.Format("%s", gKeysHit);
                    crypt.SetCipher(str);
                    tempkeys = _ttoi(crypt.DeCipher1());
                    tempkeys++;
                              
                    str.Format("%ld", tempkeys);
                    crypto.SetPlain(str);
                    strcpy(gKeysHit, crypto.Cipher1());

                    gTotalKeys++;

                    if(pFrame->m_pAppWnd->state.bKeepFreq) {
                        Keys.IncrementHitCount(kbDllHookStruct.vkCode);
                        pFrame->m_pAppWnd->state.keysSinceLR++;
                    }
                }
            }
            if(wParam == WM_KEYUP)
            {
                pFrame->m_tray.SetIcon(IDI_TRAYICON);
                pFrame->SetTrayToolTip(TRUE);
            }
        }
    }

    return CallNextHookEx(g_hKbHook, iCode, wParam, lParam);
}
There is some code that might get improved:

>>>  pFrame->KeyCount.getAccess();
>>>  pFrame->KeyCount.increment();
>>>  pFrame->KeyCount.releaseAccess();

Maybe you can use InterlockedIncrement to have an atomic increment instead.

>>> pFrame->m_tray.SetIcon(IDI_TRAYICON_KEYDOWN);

That is a SendMessage to another window that shouldn't be done in a hook function. If the hook function is the only place where tray icons are going to be changed, you may hold the status of the tray in a static variable and call SetIcon only if there is a change. Or have a PostMessage instead of a SendMessage (get the internal call from afxcmn2.inl).

I don't know how much time the encrypting of the counter takes, but you may prove if you really need string encrypting for that. If you only need to hide a counter take an algorithm like that:

int hideNumber(int i)
{
    unsigned int x = i, y = 0;    
    for (int n = 0; n < 32; n++) if (x & (1<<(31-n))) y |= (1<<n);
    return (int)y;
}
int showNumber(int i)
{
    unsigned int x = 0, y = i;
    for (int n = 0; n < 32; n++) if (y & (1<<n)) x |= (1<<(31-n));
    return (int)x;
}

Regards, Alex
Avatar of njitram

ASKER

I think I know more about where the slowness comes from. I pasted the code for the keyboard calls above, only I also have mouse calls, registered with WH_MOUSE_LL.

I tried to remove the registering of the mouse handle, just for fun, but it turns out, removing this helped a great deal. So I'm kinda thinking something is wrong with the way I handle the mouse.

Below is the code for the mouse handling. This is only supposed to catch mouse buttons.

// Registering the mouse hook
g_hMouseHook      = ::SetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC)mouseProc, AfxGetApp()->m_hInstance, 0);

// mouse handling
LRESULT CALLBACK
kbProc (int iCode, WPARAM wParam, LPARAM lParam)
{
        if (iCode < 0) {
              return CallNextHookEx(g_hKbHook, iCode, wParam,lParam);
        }
        else {
                if (iCode == HC_ACTION)
                {
                      KBDLLHOOKSTRUCT kbDllHookStruct = *(KBDLLHOOKSTRUCT*)lParam;
                     if (wParam == WM_SYSKEYDOWN || wParam == WM_KEYDOWN)
                     {
                           if ((kbDllHookStruct.flags & LLKHF_INJECTED) != 0)
                           {
                                  return CallNextHookEx(g_hKbHook, iCode, wParam, lParam);
                           }

                           if ((GetKeyState(kbDllHookStruct.vkCode) & 0x8000) == 0)
                           {
                                   // handle a clicked mouse button
                            }
                     }
                     if(wParam == WM_KEYUP)
                     {
                            // when the button goes up
                     }
               }
        }

        return CallNextHookEx(g_hKbHook, iCode, wParam, lParam);
}

Is there anything wrong with this?
Looks like a mismatch. Please post mouseProc and not kbProc.

Regards, Alex
Avatar of njitram

ASKER

Woops, sorry, no idea where that came from...below the mouseProc;

LRESULT CALLBACK
mouseProc(int iCode, WPARAM wParam, LPARAM lParam)
{
    if (iCode < 0) {
        return CallNextHookEx(g_hMouseHook, iCode, wParam, lParam);
    }
    else {
        if (iCode == HC_ACTION)
        {
            MSLLHOOKSTRUCT msllHookStruct = *(MSLLHOOKSTRUCT*)lParam;
            if (wParam == WM_LBUTTONDOWN || wParam == WM_RBUTTONDOWN || wParam == WM_XBUTTONDOWN ||
                wParam == WM_MBUTTONDOWN)
            {
                if ((msllHookStruct.flags & LLMHF_INJECTED) != 0)
                {
                    return CallNextHookEx(g_hKbHook, iCode, wParam, lParam);
                }

                // handle mouse down
            }
            if (wParam == WM_LBUTTONUP || wParam == WM_RBUTTONUP || wParam == WM_XBUTTONUP ||
                wParam == WM_MBUTTONUP)
            {
                 // handle mouse up
            }
        }
    }
    return CallNextHookEx(g_hMouseHook, iCode, wParam, lParam);
}

The functions inside the handling are about the same as the keyboard, but considering those work perfectly fast in the keyboard proc now..I thought I'd save some room...
No, that code couldn't be slow as it handles only mouse button clicks (and not mouse moves as i guessed before your post).

However, it will be called for any mouse move message as well. So little times may add to something that is measurable, though i don't believe it. But you may use a static counter to find out, how often it is called.

Is there another mouse hook somewhere?

Regards, Alex
You also could comment the handling of the mouse clicks, to see whether the mouse hook itself or your mouse handling slows down.

Regards, Alex
Avatar of njitram

ASKER

There is not a second mouse hook somewhere, I'm only using 2 hooks, one for the keyboard and one for the mouse.

I tried putting a small counter which counted the times mouseProc was called, well, that was alot. ;) So I figure it gets called everytime the mouse moves/clicks.

I also tried to empty the code that handles the mouse clicks to a single 'clicks++;', but it still continued to go slow.
>> that was alot. ;)

How often a second? And do you have to move the mouse for that?

You could try to return not calling CallNextHookEx. Only to see if these calls are the reason for the slowness.

Regards, Alex


Avatar of njitram

ASKER

I'd say every pixel, inch, or whatever is used to measure mouse distances to see when another message is posted.

And when I remove the return's, the mouse stops responding totally (Including in Windows).
>>> the mouse stops responding totally

You *have* to call CallNextHookEx if nCode < 0. Return 0 all other returns, as a non-zero return means you've processed the message, thus the message will not get processed by the target window.

Regards, Alex

Avatar of njitram

ASKER

No change when I put the returns to 0, I'm really out of ideas...do you have any left?
ASKER CERTIFIED SOLUTION
Avatar of modulo
modulo

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial