Solved

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

Posted on 2008-06-11
12
953 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
Gigs: Get Your Project Delivered by an Expert

Select from freelancers specializing in everything from database administration to programming, who have proven themselves as experts in their field. Hire the best, collaborate easily, pay securely and get projects done right.

 

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

Gigs: Get Your Project Delivered by an Expert

Select from freelancers specializing in everything from database administration to programming, who have proven themselves as experts in their field. Hire the best, collaborate easily, pay securely and get projects done right.

Question has a verified solution.

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

When writing generic code, using template meta-programming techniques, it is sometimes useful to know if a type is convertible to another type. A good example of when this might be is if you are writing diagnostic instrumentation for code to generat…
Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.

813 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

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now