Avatar of ramavorray
ramavorray
Flag for United Kingdom of Great Britain and Northern Ireland asked on

How to Detect WM_KEYUP and WM_KEYDOWN in a KeyboardHook procedure using WPARAM and LPARAM

I have a KeyboardHook (as a DLL) which senses all keyboard activity and it is of type WH_KEYBOARD

The keyboard hook is working well and the keyboard callback procedure is called with parameters WPARAM and LPARAM

I am able to sense the key pressed (like VK_A, VK_DOWN) but I am not able to sense KEYUP/KEYDOWN

I tried many alternatives including code found online (even on EE) but it doesn't work.

When a key is hit my Keyboard Callback Procedure is being called multiple times instead of only one time (that is, when key is pressed)

Code snippets are attached.

I am using Visual Studio 2005 and running on Windows XP Professional 32bit system
//Keyboard Hook Procedure inside in the DLL which posts a message to my application
KEYDLL3_API LRESULT CALLBACK hookproc(int ncode,WPARAM wparam,LPARAM lparam)
{
	if(ncode>=0)
	{
		if((lparam & 0x80000000) == 0x00000000)//Check whether key was pressed(not released).
		{
			hwnd = FindWindow("#32770","Keylogger Exe");//Find application window handle
			PostMessage(hwnd,WM_USER+755,wparam,lparam);//Send info to app Window.
		}
	}
	return ( CallNextHookEx(hook,ncode,wparam,lparam) );//pass control to next hook in the hook chain.
}
 
 
 
//the keyboard callback function in my application which received 
//WPARAM and LPARAM. During the multiple number of times this function 
//is getting called it is always going to the line if(w == VK_SPACE || w == VK_DOWN). It is not resulting any KEYUP/KEYREPEAT events
LRESULT CKeyexeDlg::processkey(WPARAM w, LPARAM l)//This block processes the keystroke info.
{
	if (l & 0x80000000) // check bit 31 for up/down
	{
		//KEY UP
	}
	else
	{
		if (l & 0x40000000) // check bit 30 for previous up/down
			//KEY REPEAT
 
		else
		{
			if(w == VK_SPACE || w == VK_DOWN)
			{
				//the virtual character
			}
		}
	}
 
//I tried the other alternative. But it doesn't even enter into the switch block.
	LPMSG m=(LPMSG)l;
	switch(m->message)
	{
	case WM_KEYDOWN:
		l=l;
		break;
	case WM_KEYUP:
		break;
	case WM_CHAR:
		break;
	};

Open in new window

Microsoft DevelopmentC++

Avatar of undefined
Last Comment
ramavorray

8/22/2022 - Mon
jkr

Try the following:
//Keyboard Hook Procedure inside in the DLL which posts a message to my application
KEYDLL3_API LRESULT CALLBACK hookproc(int ncode,WPARAM wparam,LPARAM lparam)
{
    PMSG    pmsg    =   ( PMSG) lparam;
 
    if  (   0   >   ncode   ||  PM_NOREMOVE ==  wparam) 
        {
            return  (   CallNextHookEx  (   hook,
                                            ncode,
                                            wparam,
                                            lparam
                                        )
                    );
        }
 
    if  (       WM_KEYDOWN  ==  pmsg->message
            ||  WM_KEYUP    ==  pmsg->message   
        )
        {
           // check if you get here
        }
 
    return  (   CallNextHookEx  (   hook,
                                    ncode,
                                    wparam,
                                    lparam
                                )
            );
}

Open in new window

ramavorray

ASKER
No, It is not reaching the point you indicated. I tried two alternatives. Please see the code below.
//Tried this as you indicated. No messages are Posted at all, either for keyup or keydown.
KEYDLL3_API LRESULT CALLBACK hookproc(int ncode,WPARAM wparam,LPARAM lparam)
{
	if(0   >   ncode   ||  PM_NOREMOVE ==  wparam)
		return ( CallNextHookEx(hook,ncode,wparam,lparam) );//pass control to next hook in the hook chain.
 
	PMSG msg = (PMSG) lparam;
	if(msg->message == WM_KEYDOWN || msg->message == WM_KEYUP)
	{
		hwnd = FindWindow("#32770","Keylogger Exe");//Find application window handle
		PostMessage(hwnd,WM_USER+755,wparam,lparam);//Send info to app Window.
	}
 
	return ( CallNextHookEx(hook,ncode,wparam,lparam) );//pass control to next hook in the hook chain.
}
 
//thought of checking where the control is going and tested with. Messages are posted. So, the checks of WM_KEYUP/DOWN are not being sensed!!! and I am not understanding why!
KEYDLL3_API LRESULT CALLBACK hookproc(int ncode,WPARAM wparam,LPARAM lparam)
{
	if(0   >   ncode   ||  PM_NOREMOVE ==  wparam)
		return ( CallNextHookEx(hook,ncode,wparam,lparam) );//pass control to next hook in the hook chain.
 
	PMSG msg = (PMSG) lparam;
	if(msg->message == WM_KEYDOWN || msg->message == WM_KEYUP)
	{
		//hwnd = FindWindow("#32770","Keylogger Exe");//Find application window handle
		//PostMessage(hwnd,WM_USER+755,wparam,lparam);//Send info to app Window.
	}
 
	hwnd = FindWindow("#32770","Keylogger Exe");//Find application window handle
	PostMessage(hwnd,WM_USER+755,wparam,lparam);//Send info to app Window.
 
	return ( CallNextHookEx(hook,ncode,wparam,lparam) );//pass control to next hook in the hook chain.
}
 
//For your reference below is the function which installs hook in DLL
KEYDLL3_API void installhook(HWND h)
{
	hook = NULL;
	hwnd = h;
	hook = SetWindowsHookEx(WH_KEYBOARD,hookproc,hinstance,NULL);
	if(hook==NULL)
		MessageBox(NULL,"Unable to install hook","Error!",MB_OK);
}
 
//in my application which receives event message is mapped as
#define WM_KEYSTROKE (WM_USER + 755)
 
//all are defined well and working well, except the sensing of
//WM_KEYUP / WM_KEYDOWN

Open in new window

jkr

Hmm, stupid question - how did you check that this point is not reached? I took the above from a working project that successfully detects the keypresses (the purpose was to remap some keys, and that works fine)
All of life is about relationships, and EE has made a viirtual community a real community. It lifts everyone's boat
William Peck
ramavorray

ASKER
If that point is reached, the procedure in my application which should be called as a result of the posted message should be called.

I can say that this point is not reached as the hook procedure in my application is not called.

It was called in the second situation (where I had put postmessage outside the if...block)

Please get the source code http://www.cs.ucl.ac.uk/staff/R.Vorray/Keyexe.zip A zip file which contains hook DLL project and exe project. please check. LINE NUMBER 241 in KeyExeDlg.cpp in KeyExe project should be reached when everything is working.

Please put the generated Debug DLL in the Debug folder of KeyExe. All include paths are arranged correct.
jkr

Hm, try to just place an

OutputDebugString("Keypress detected\n");

inside the 'if' body and check the output with DebugView (http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx). Chances are that something goes wrong when communicating the event.
ASKER CERTIFIED SOLUTION
JohnGaby

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
GET A PERSONALIZED SOLUTION
Ask your own question & get feedback from real experts
Find out why thousands trust the EE community with their toughest problems.
ramavorray

ASKER
But when WH_GETMESSAGE is used wParam cannot be used to get virtual keycodes!

I cannot do checks like, if(wParam == VK_SPACE)

Also I need to use ToUnicodeEx(...) function to translate the wParam value into a unicode character based on current keyboard layout.

This no longer works when WH_GETMESSAGE is used!!!?
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
JohnGaby

For the WH_GETMESSAGE hook, the lParam points to a MSG structure.  If you look in that structure for (message == WM_KEYDOWN), then for that message, the wParam of that structure will contain the virtual key code, and the lParam of that structure will contain the key data.

Is there something here that you need that I am missing?
ramavorray

ASKER
when typecasted lparam into PMSG in the hook handler procedure inside my applicatin, it is giving me Access Violation exception!

Please see the code below and please consider me a beginner in this regard.
//This is what I am doing inside my hook handler
LRESULT CAI_TalapatraDlg::ProcessKeyboard(WPARAM w, LPARAM l)
{
  PMSG msg = (PMSG)lparam;
  if(msg->message == WM_KEYDOWN)   //access violation error with message.
  //debugger shows invalid data for msg
  {
        if(msg->wParam == VK_SPACE || msg->wParam == VK_DOWN)
		//do stuff
  }
}
 
 
//here is the message posting function inside the DLL. Here everything works fine and the procedure above inside my application is called only once, but not able to process the virtual key code.
KEYHOOK_API LRESULT CALLBACK Hookproc(int nCode, WPARAM wparam, LPARAM lparam)
{
	if(0   >   nCode   ||  PM_NOREMOVE ==  wparam)
                return ( CallNextHookEx(hook,nCode,wparam,lparam) );//pass control to next hook in the hook chain.
 
    PMSG msg = (PMSG) lparam;
    if(msg->message == WM_KEYDOWN)
    {
		hwnd=FindWindow(NULL, _T("AI Talapatra"));
		if(hwnd!=NULL)
			PostMessage(hwnd, WM_USER+3, wparam, lparam);
	}
 
	return (CallNextHookEx(hook, nCode, wparam, lparam));
}

Open in new window

JohnGaby

Did you change your call to CreateWindowsHookEx to use WH_GETMESSAGE instead of WH_KEYBOARD?
Experts Exchange has (a) saved my job multiple times, (b) saved me hours, days, and even weeks of work, and often (c) makes me look like a superhero! This place is MAGIC!
Walt Forbes
ramavorray

ASKER
sorry there was a typo in the code in previous comment. Please consider code below for the first half.
//This is what I am doing inside my hook handler
LRESULT CAI_TalapatraDlg::ProcessKeyboard(WPARAM w, LPARAM l)
{
  PMSG msg = (PMSG)l;
  if(msg->message == WM_KEYDOWN)   //access violation error with message.
  //debugger shows invalid data for msg
  {
        if(msg->wParam == VK_SPACE || msg->wParam == VK_DOWN)
                //do stuff
  }
}

Open in new window

ramavorray

ASKER
Yes. now I am using WH_GETMESSAGE
KEYHOOK_API void Installhook(HWND h)
{
	hook = NULL;
	hwnd = h;
	hook = SetWindowsHookEx(WH_GETMESSAGE, Hookproc, hinstance, NULL);
 
	if(hook == NULL)
		MessageBox(NULL, _T("Unable to Install Hook"), _T("Error"), MB_OK);
}

Open in new window

ramavorray

ASKER
This is what debugger is showing inside the function ProcessKeyboard(WPARAM w, LPARAM l) inside my application.

This conversion is working well in the DLL but not inside my application.


msg              0x0023f7e8 {msg=??? wp=??? lp=???}      tagMSG *
hwnd      CXX0030: Error: expression cannot be evaluated      
message      CXX0030: Error: expression cannot be evaluated      
wParam      CXX0030: Error: expression cannot be evaluated      
lParam      CXX0030: Error: expression cannot be evaluated      
time              CXX0030: Error: expression cannot be evaluated      
pt              {x=??? y=???}      tagPOINT
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
JohnGaby

You cannot post the pointer to the MSG structure like that.  By the time the posted message reaches your application, the memory to which the pointer is pointing is likely to no longer be valid.  You need to extract the data from the structure and post that to your application.
ramavorray

ASKER
when key hits are made In all other applications except MS-Word my hook handler ProcessKeyboard(...) is being called once, indicating Wm_KEYDOWN; But when I type something in MS-Word ProcessKeyboard is being called four times as it did previously. Why is WM_KEYDOWN not being sensed from within MS-Word?

Now, I am posting msg->wParam and msg->lParam as two parameters inside PostMessage function.

below is the code for HookProc. WM_USER + 3 message is being posted four times from MS_WORD. Any clue on this?
KEYHOOK_API LRESULT CALLBACK Hookproc(int nCode, WPARAM wparam, LPARAM lparam)
{
	if(0   >   nCode   ||  PM_NOREMOVE ==  wparam)
                return ( CallNextHookEx(hook,nCode,wparam,lparam) );//pass control to next hook in the hook chain.
 
    PMSG msg = (PMSG) lparam;
    if(msg->message == WM_KEYDOWN)
    {
		hwnd=FindWindow(NULL, _T("AI Talapatra"));
		if(hwnd!=NULL)
			PostMessage(hwnd, WM_USER+3, msg->wParam, msg->lParam);
	}
 
	return (CallNextHookEx(hook, nCode, wparam, lparam));
}

Open in new window

JohnGaby

I am not sure I can help you any further.  Oddly enough, a while back I was trying to help another user intercept keys, and he too has some difficulty getting it to work with Word.  As I recall, he was able to get the keyup/down messages, but not the WM_CHAR messages.  It worked with all other apps, but not with Word.  Here is the link to that discussion.

https://www.experts-exchange.com/questions/23471531/Word-excel-don't-work-with-my-hooking-dll.html

I am not sure if he ever solved the problem, but perhaps if you posted a comment on that question, he might see it, and answer.
 
This is the best money I have ever spent. I cannot not tell you how many times these folks have saved my bacon. I learn so much from the contributors.
rwheeler23
ramavorray

ASKER
Hi JohnGaby,

I will deal with the MS-Word thing later on. Now another messy problem I am facing with your approach is that I am not able to process SHIFT + key combinations

the wParam is returning 'a' instead of 'A' when I press SHIFT + 'a'

I think it is calling my hook procedure once for SHIFT key press and once from 'a' key press. How to handle this situation?
ramavorray

ASKER
I am using ToUnicodeEx(...) function to translate the wParam value into Unicode characters, as I deal with non-english languages aswell.

I get keyboard state using GetKeyboardState(..) and pass the resulting array as one of the arguments to ToUnicodeEx(...) but keyboardsate array here is not possessing the keydown bit for SHIFT key as by the time it is called for 'a' in SHIFT + 'a' shift key is down!

Any clue in this regard?
ramavorray

ASKER
sorry in my previous comment please read the last line as "...by the time it is called for 'a' in SHIFT+'a' shift key is UP!
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
ramavorray

ASKER
even if pressed 'a' or SHIFT + 'a' buf contains only 'a'. How can I get 'A' here out of ToUnicodeEx(...) function?
LRESULT CAI_TalapatraDlg::ProcessKeyboard(WPARAM w, LPARAM l)
{
 BYTE ks[256];
 WCHAR buf[10];
 UINT scan=0;
 
 GetKeyboardState(ks);
 
 if(ToUnicodeEx(w, scan, ks, buf, 1, 1, m_hklval))
{
        ......//even if pressed 'a' or SHIFT + 'a' buf contains only 'a'
}
 
}

Open in new window

JohnGaby

If you want characters, instead of keys, you should be looking for the WM_CHAR message, rather than the WM_KEYDOWN message.  The WM_KEYDOWN messages returns the raw 'virtual' keys.  The WM_CHAR message is generated when you call the TranslateMessage function in your message loop, and should give you unicode characters if you have compiled using the unicode character set.
ramavorray

ASKER

PMSG msg = (PMSG) lparam;
    if(msg->message == WM_CHAR)
    {
		hwnd=FindWindow(NULL, _T("AI Talapatra"));
		if(hwnd!=NULL)
			PostMessage(hwnd, WM_USER+3, msg->wParam, msg->lParam);
	}
 
//in the above code (when using WM_CHAR) what does wParam and lParam indicate?
 
//How do I specify that my WM_USER+3 message is posted only when Key is down but not otherwise
 
//wParam the unicode character?
 
//then which variable indicates other key hits like VK_SPACE, VK_BACK, etc...

Open in new window

Experts Exchange is like having an extremely knowledgeable team sitting and waiting for your call. Couldn't do my job half as well as I do without it!
James Murphy
ramavorray

ASKER
This is how my current code looks, which sends key hits to my application called "AI Talapatra". Can I just change WM_KEYDOWN to WM_CHAR and wParam is an Unicode Character? If so, how to sense SPACE, BACKSPACE. etc.
KEYHOOK_API LRESULT CALLBACK Hookproc(int nCode, WPARAM wparam, LPARAM lparam)
{
    if(0   >   nCode   ||  PM_NOREMOVE ==  wparam)
               return ( CallNextHookEx(hook,nCode,wparam,lparam) ); //pass control to next hook in the hook chain.
 
    PMSG msg = (PMSG) lparam;
    if(msg->message == WM_KEYDOWN)
    {
		hwnd=FindWindow(NULL, _T("AI Talapatra"));
		if(hwnd!=NULL)
		PostMessage(hwnd, WM_USER+3, msg->wParam, msg->lParam);
    }
 
return (CallNextHookEx(hook, nCode, wparam, lparam));
}

Open in new window

JohnGaby

Here is the manual page for the WM_CHAR message:

http://msdn.microsoft.com/en-us/library/ms646276.aspx

The wParam contains the unicode (UTF16) character.  The lParam is not particularly useful.  If you are looking for the space character, compare (wParam == ' ').  For the backspace look for (wParam == '\b').

Note that you WILL have problems with this message when hooking MS Word (and I believe Excel also).
ramavorray

ASKER
When did so I am getting a numerical value in msg->wParam which is posted to my application. (eg., 3108).

How to translate it into an appropriate character?
⚡ FREE TRIAL OFFER
Try out a week of full access for free.
Find out why thousands trust the EE community with their toughest problems.
JohnGaby

I am not sure what is wrong.  Here is some code that uses the WH_GETMESSAGE hook to hook notepad, and replace the character 'x' with 'y' each time it is typed.  This code works with no problem.  Perhaps you can compare this to what you are doing and figure out what is wrong.

It consists of two pieces.  The first is the code for the dll which sets and process the hook.  The second is for a console app which finds an instance of notepad and calls the dll to set the hook.

// HookDll.cpp
//
// This is the DLL which both sets the hook and
//      subclasses the edit window for the notepad app
//      which is hooked
//
 
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
 
HINSTANCE   hMod;
HHOOK       hHook;
 
BOOL APIENTRY DllMain(  HANDLE hModule, 
                        DWORD  ul_reason_for_call, 
                        LPVOID lpReserved)
{
    hMod	= (HINSTANCE) hModule;
 
    return TRUE;
}
 
#ifdef __cplusplus
extern "C" {
#endif
 
LRESULT CALLBACK HookProc(int code, WPARAM wParam, LPARAM lParam)
{
    LRESULT	ret	= 0;
 
    if (code == HC_ACTION)
    {
        MSG		*	pMsg	= (MSG *) lParam;
 
        if (pMsg->message == WM_CHAR)
        {
            if (pMsg->wParam == 'x')
            {
                pMsg->wParam	= 'y';
            }
        }
    }
 
    return(CallNextHookEx(hHook, code, wParam, lParam));
}
 
__declspec(dllexport) BOOL SetHook(DWORD threadId)
{
    hHook = SetWindowsHookEx(WH_GETMESSAGE, HookProc, hMod, threadId);
 
    return(hHook ? TRUE : FALSE);
}
 
__declspec(dllexport) void ClearHook(void)
{
    if (hHook)
    {
        UnhookWindowsHookEx(hHook);
        hHook	= 0;
    }
}
 
#ifdef __cplusplus
}
#endif
 
// GlobalHook.cpp
//
// This is a console application which find an instance of notepad.exe
//  and sets a hook into it's process
//
 
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <conio.h>
 
#ifdef __cplusplus
extern "C" {
#endif
 
__declspec(dllimport) BOOL SetHook(DWORD threadId);
__declspec(dllimport) void ClearHook(void);
 
#ifdef __cplusplus
}
#endif
 
int _tmain(int argc, _TCHAR* argv[])
{
    HWND	hEdit;
 
    /*
     * Find an instance of notepad to hook
     */
    if (hEdit = FindWindow(_T("Notepad"), 0))
    {
        DWORD	threadId = 0;
        DWORD	processId;
 
        /*
         * Get the thread ID for the window
         */
        if (threadId = GetWindowThreadProcessId(hEdit, &processId))
        {
            printf("Setting hook\n");
 
            /*
             * Set a hook for this thread
             */
            if (SetHook(threadId))
            {
                printf("Hook set, press any key to clear hook\n");
                getch();
                printf("Clearing hook\n");
                ClearHook();
            }
            else
            {
                printf("Cannot set hook\n");
            }
        }
    }
    else
    {
		printf("Cannot find an instance of word\n");
    }
 
    return 0;
}

Open in new window

ramavorray

ASKER
Here you are comparing character with 'x' but similarly can you compare it with '' a Hindi character. Also how can this wParam be converted into a string. ToUnicodeEx(...) function cannot understand the value of wParam.

It is able to understand the value of wParam with WM_KEYDOWN, but with WM_KEYDOWN I am not able to sense SHIFT.

for example, key 'k' gives a character and also SHIFT + K gives another characeter. WM_KEYDOWN is working for sensing single key strokes but not SHIFT + K.

WM_CHAR is giving a numerical value to wParam (because non-english keyboard layout has been loaded) and I am not understanding how to convert it into a string.
JohnGaby

I have not tried this with unicode characters, but according to the manual page, the wParam is the UTF16 unicode character.  This means that it is already converted to unicode (assuming you have compiled your application using the unicode character set).  To compare it to a Hindi character, you would need to know the UTF16 value for the Hindi character.

I cannot tell you much more.  Handling languages other than English (especially ones which require the UTF16 character set) is not my area of expertise.  Perhaps another expert can help you out there.
I started with Experts Exchange in 2004 and it's been a mainstay of my professional computing life since. It helped me launch a career as a programmer / Oracle data analyst
William Peck
ramavorray

ASKER
Complete solution hasn't been found but my original question has been answered.