Solved

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

Posted on 2008-06-11
12
964 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

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

On Demand Webinar: Networking for the Cloud Era

Did you know SD-WANs can improve network connectivity? Check out this webinar to learn how an SD-WAN simplified, one-click tool can help you migrate and manage data in the cloud.

Question has a verified solution.

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

Introduction: Dialogs (1) modal - maintaining the database. Continuing from the ninth article about sudoku.   You might have heard of modal and modeless dialogs.  Here with this Sudoku application will we use one of each type: a modal dialog …
This article shows you how to optimize memory allocations in C++ using placement new. Applicable especially to usecases dealing with creation of large number of objects. A brief on problem: Lets take example problem for simplicity: - I have a G…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

690 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