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!
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!
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_KEYB OARD_LL,(H OOKPROC)kb Proc, 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->sta te.bCountk eys)
{
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(kbDllHookStr uct.vkCode ) & 0x8000) == 0)
{
pFrame->KeyCount.getAccess ();
pFrame->KeyCount.increment ();
pFrame->KeyCount.releaseAc cess();
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->stat e.bKeepFre q) {
Keys.IncrementHitCount(kbD llHookStru ct.vkCode) ;
pFrame->m_pAppWnd->state.k eysSinceLR ++;
}
}
}
if(wParam == WM_KEYUP)
{
pFrame->m_tray.SetIcon(IDI _TRAYICON) ;
pFrame->SetTrayToolTip(TRU E);
}
}
}
return CallNextHookEx(g_hKbHook, iCode, wParam, lParam);
}
I hook it with:
g_hKbHook = ::SetWindowsHookEx(WH_KEYB
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->sta
{
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(kbDllHookStr
{
pFrame->KeyCount.getAccess
pFrame->KeyCount.increment
pFrame->KeyCount.releaseAc
pFrame->m_tray.SetIcon(IDI
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->stat
Keys.IncrementHitCount(kbD
pFrame->m_pAppWnd->state.k
}
}
}
if(wParam == WM_KEYUP)
{
pFrame->m_tray.SetIcon(IDI
pFrame->SetTrayToolTip(TRU
}
}
}
return CallNextHookEx(g_hKbHook, iCode, wParam, lParam);
}
There is some code that might get improved:
>>> pFrame->KeyCount.getAccess ();
>>> pFrame->KeyCount.increment ();
>>> pFrame->KeyCount.releaseAc cess();
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
>>> pFrame->KeyCount.getAccess
>>> pFrame->KeyCount.increment
>>> pFrame->KeyCount.releaseAc
Maybe you can use InterlockedIncrement to have an atomic increment instead.
>>> pFrame->m_tray.SetIcon(IDI
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
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_MOUS E_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(kbDllHookStr uct.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?
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_MOUS
// 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(kbDllHookStr
{
// 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
Regards, Alex
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_hMouseHoo k, 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_hMouseHoo k, 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...
LRESULT CALLBACK
mouseProc(int iCode, WPARAM wParam, LPARAM lParam)
{
if (iCode < 0) {
return CallNextHookEx(g_hMouseHoo
}
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_hMouseHoo
}
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
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
Regards, Alex
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.
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
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
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).
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
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
ASKER
No change when I put the returns to 0, I'm really out of ideas...do you have any left?
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
How about you show us the code?