Solved

How to simulate two WM_CHAR upon a button key when hooking those messages?

Posted on 2008-06-11
12
958 Views
Last Modified: 2013-11-20
Hi,
I have a hook dll ( code snippet below ). I would like to generate to characters upon a press of a single button. This sample tries to generate 'x' and 'y' upon pressing 'z'. For some reason, only the second char is typed.

Thanks
Udi Raz
// HookDll.cpp
//
// This is the DLL which sets a hook which
//      substitutes the letter 'y' when the user types
//      the letter 'x'
//
 
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
 
HINSTANCE   hMod;
HHOOK       hHook;
bool        g_ignoreChar;
 
BOOL APIENTRY DllMain(  HANDLE hModule, 
                        DWORD  ul_reason_for_call, 
                        LPVOID lpReserved)
{
    hMod	= (HINSTANCE) hModule;
    g_ignoreChar = false;
    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;
 
		switch( pMsg->message )
		{
		case WM_KEYDOWN:
			if (pMsg->wParam == 'Z')
			{
				g_ignoreChar = true;
				pMsg->wParam  = 'x';
				pMsg->message = WM_CHAR;
				CallNextHookEx(hHook, code, wParam, lParam);
				pMsg->wParam  = 'y';
				return CallNextHookEx(hHook, code, wParam, lParam);
										}
			break;
		case WM_KEYUP:
			if (pMsg->wParam == 'Z')
				return 0;
			else
				break;
		case WM_CHAR:
			if ( g_ignoreChar == true)
			{
				g_ignoreChar = false;
				return 0;
			}
			else
				break;
		}
	}
 
	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

Open in new window

0
Comment
Question by:UdiRaz
  • 6
  • 6
12 Comments
 
LVL 19

Expert Comment

by:alb66
ID: 21766804
Try to handle it int the WM_CHAR case:

                case WM_CHAR:
                        if (pMsg->wParam == 'Z')
                        {
                                pMsg->wParam  = 'x';
                                CallNextHookEx(hHook, code, wParam, lParam);
                                pMsg->wParam  = 'y';
                                return CallNextHookEx(hHook, code, wParam, lParam);
0
 

Author Comment

by:UdiRaz
ID: 21773032
Thanks for your response, but the code you suggested doesn't work. Only y is typed.
0
 
LVL 19

Expert Comment

by:alb66
ID: 21776808
It seems to me that your strategy is wrong.
Calling CallNextHookEx doesn't produce any effect, unless there is another hook in the hook chain.
You simply modify the message two times and, of course, the second modify (y) win.

I think that you should create a second message.
Try the following.
LRESULT CALLBACK HookProc(int code, WPARAM wParam, LPARAM lParam)
{
	LRESULT ret = 0;
 
	if (code == HC_ACTION)
	{
		MSG * pMsg = (MSG *) lParam;
 
		switch( pMsg->message )
		{
		case WM_CHAR:
			if ( pMsg->wParam == 'Z' )
			{
				pMsg->wParam  = 'x';
				CallNextHookEx(hHook, code, wParam, lParam);
 
				if ( wParam == 0 )
					::PostMessage( pMsg->hwnd, WM_CHAR, 'y', pMsg->lParam );				
				
				return TRUE;
			}
			break;
		}
	}
 
	return(CallNextHookEx(hHook, code, wParam, lParam));
}

Open in new window

0
Announcing the Most Valuable Experts of 2016

MVEs are more concerned with the satisfaction of those they help than with the considerable points they can earn. They are the types of people you feel privileged to call colleagues. Join us in honoring this amazing group of Experts.

 

Author Comment

by:UdiRaz
ID: 21787941
alb66 : thanks for your help, But I still have some problems:
1. your example works well on word document. On notepad, I had to remove the line if ( wParam == 0 )

2. I have to explain my motivation is this feature : In real life, I am replacing Hindi characters. Unfortunatly, Unlike english ,there are some hindi letters that are made of more then one character. What I do to replace the original key stroke with the characters I want is to hook the WM_KEYDOWN, simulate some WM_CHAR messages and don't pass the next WM_CHAR and WM_KEYUP. So I tried to modify your example to type x and y upon WM_KEYDOWN of 'Z'. The code snippet below works on notepad, doesn't work on word
LRESULT CALLBACK HookProc(int code, WPARAM wParam, LPARAM lParam)
{
	LRESULT ret = 0;
 
	if (code == HC_ACTION)
	{
		MSG * pMsg = (MSG *) lParam;
 
		switch( pMsg->message )
		{
		case WM_KEYDOWN:
			if (pMsg->wParam == 'Z')
			{
				g_ignoreChar = true;
				::SendMessage( pMsg->hwnd, WM_CHAR, 'y', pMsg->lParam );
				pMsg->wParam  = 'x';
				pMsg->message = WM_CHAR;
			}
			break;
		case WM_KEYUP:
			if (pMsg->wParam == 'Z')
				return 0;
			else
				break;
		case WM_CHAR:
			if ( g_ignoreChar == true)
			{
				g_ignoreChar = false;
				return 0;
			}
			else
				break;
		}
	}
 
	return(CallNextHookEx(hHook, code, wParam, lParam));
}

Open in new window

0
 
LVL 19

Expert Comment

by:alb66
ID: 21791289
Isn't there a Hindi keyboard?
0
 

Author Comment

by:UdiRaz
ID: 21791339
There is a hindi keyboard layout. You can add hindi like any other language. The thing is that there are several layouts for hindi and microsoft supports only one. Our clients would like a different layout and that is why I am trying to simulate it.

It is working well when I am subclassing a window withing my application.
It is working well when I am subcalssing other process windows using a hook dll with WH_CALLWNDPROC hook to hook WM_SETFOCUS of a window and then SetWindowLong(hEdit, GWL_WNDPROC, (LONG) pOldWndProc) to subclass the window

It is not working on word document and this is a problem for me.
It is working on word document also when I hook GETMESSAGE and catch WM_KEYDOWN, WM_KEYUP and WM_CHAR but only for replacing a single character.

I must make it work on word and the only problem I have simplified to how send two WM_CHAR messages upon a single WM_KEYDOWN message. Word ignore the PostMessage.

Thanks
0
 
LVL 19

Expert Comment

by:alb66
ID: 21791394
0
 

Author Comment

by:UdiRaz
ID: 21791910
I can't create a custom layout since win32 apis such as SetThreadLocale, LoadKeyboardLayout, ActivateKeyboardLayout does not work with the locale id that the new layout. Funny thing is that GetKeyboardLayout does return a new HKL.

Anyway, I will still have to stragle the windows messages and inject my own characters.
Can you please try to send two ( english ) character upon a single WM_KEYDOWN ?

Thanks in advance
0
 
LVL 19

Accepted Solution

by:
alb66 earned 500 total points
ID: 21792482
The code attached works well in Word, notepad, VS and a sample MFC application.

It doesn't manage the key repeat. If you comment the bEnabled test it also manage the repeat , but it doesn't works in word (word receive 5 WM_CHAR message every WM_KEYDOWN  !?!?!? - very strange - !?!?!? ).

Anyway, in my opinion, a custom keyboard layout is the best solution. Modifying messages via a global hook is a very intrusive programming tecnique, and I don't exclude that you may encounter some other drawbacks.
LRESULT CALLBACK HookProc(int code, WPARAM wParam, LPARAM lParam)
{
	static bool bEnabled = true;
 
	if (code == HC_ACTION)
	{
		MSG * pMsg = (MSG *) lParam;
 
		switch( pMsg->message )
		{
		case WM_KEYDOWN:
			if (pMsg->wParam == 'Z')
			{
				pMsg->message = WM_NULL;
				
				if (( pMsg->lParam & 0xFFFF ) == 1 && bEnabled )
				{
					::PostMessage( pMsg->hwnd, WM_CHAR, 'x', pMsg->lParam );
					::PostMessage( pMsg->hwnd, WM_CHAR, 'y', pMsg->lParam );
 
					bEnabled = false;
				}
			}
			break;
 
		case WM_KEYUP:
			if (pMsg->wParam == 'Z')
			{
				bEnabled = true;
 
				pMsg->message = WM_NULL;
			}
		}
	}
 
	return(CallNextHookEx(hHook, code, wParam, lParam));
}

Open in new window

0
 

Author Comment

by:UdiRaz
ID: 21792517
what is "the key repeat" ?
0
 
LVL 19

Expert Comment

by:alb66
ID: 21792694
Usually when you press a key, the character is automatically repeated until you release the key. In my solution the charater is written only one time; I do that because of the Word strange behavior.
0
 

Author Closing Comment

by:UdiRaz
ID: 31466249
thanks a lot !!!
0

Featured Post

Free Tool: Postgres Monitoring System

A PHP and Perl based system to collect and display usage statistics from PostgreSQL databases.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction: Finishing the grid – keyboard support for arrow keys to manoeuvre, entering the numbers.  The PreTranslateMessage function is to be used to intercept and respond to keyboard events. Continuing from the fourth article about sudoku. …
Introduction: The undo support, implementing a stack. Continuing from the eigth article about sudoku.   We need a mechanism to keep track of the digits entered so as to implement an undo mechanism.  This should be a ‘Last In First Out’ collec…
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.

791 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question