?
Solved

Keyboard filtering...

Posted on 2003-03-11
11
Medium Priority
?
552 Views
Last Modified: 2013-11-20
I have scanned through previous questions here, but nothing seems to achieve what i want it to.

I am after (once again) a global hook so i can implement a filter that will expand certain characters typed into a series of characters (e.g., tab into 4 spaces.)

I have constructed something similar to the following code, based on previous examples, but fails miserably....

Any suggestions to make the function work (and be relatively robust?)

Cheers,

Dave

LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    if(nCode < 0)
    {      
     CallNextHookEx(hMsghook, nCode, wParam, lParam);
     return 0;
    }

    LPMSG lpMsg = (LPMSG) lParam;

    if(lpMsg->message == WM_CHAR && wParam == PM_REMOVE && lpMsg->wParam == chOldChar)
    {
        // Insert as many characters as needed...
        PostMessage(lpMsg->hwnd,WM_CHAR,chNewChar,lpMsg->lParam);
        PostMessage(lpMsg->hwnd,WM_CHAR,chNewChar,lpMsg->lParam);
        PostMessage(lpMsg->hwnd,WM_CHAR,chNewChar,lpMsg->lParam);
        PostMessage(lpMsg->hwnd,WM_CHAR,chNewChar,lpMsg->lParam);
    }
   
    return CallNextHookEx(hMsghook, nCode, wParam, lParam);
}
0
Comment
Question by:208Fireball
[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
  • 5
11 Comments
 
LVL 11

Expert Comment

by:KurtVon
ID: 8119671
How does it fail miserably?  If nothing happens, you may need to override WM_KEYDOWN and WM_KEYUP as well as WM_CHAR.

Also, it may be that the call to the next hook should be dropped if you process the keystroke.  After all, you don't want the original character to go through, do you?  Just return -1 in that case, this kills the message.
0
 

Author Comment

by:208Fireball
ID: 8123578
Even when returning -1, it seems not to remove the original character. Also, if one of the keys i want to replace the original is the same as the one i am replacing, the thing will go into an endless loop...

Cheers,

Dave
0
 
LVL 11

Expert Comment

by:KurtVon
ID: 8128127
Did you also override WM_KEYDOWN and WM_KEYUP?  I don't know what program you are trying to change, but these messages are processed by many controls.  You need to block the original character in all three cases.  Also, Microsoft suggests returning TRUE to swallow messages.  Sorry about the -1 (which should be the same but . . .)

And of course it is an endless loop.  You either need some flag to indicate the keypress should not be processed, like a static bool that can be set when it is processed, and then cleared if it is set and the message arrives a second time.

Unfortunately, SendMessage is illegal in a hook (may cause a deadlock) or you could just use a SendMessage and call the next hook when you get to the place the character should be.

Oh, and if you override the WM_KEYUP and WM_KEYDOWN what you should do is send them around the WM_CHAR messages you send so that the calls are

::PostMessage(hWnd, WM_KEYDOWN, chNewChar1, lParam);
::PostMessage(hWnd, WM_CHAR, chNewChar1, lParam);
::PostMessage(hWnd, WM_KEYUP, chNewChar1, lParam);
::PostMessage(hWnd, WM_KEYDOWN, chNewChar2, lParam);
...

This prevents Windows from thinking the user is pushing every key simultaneously.
0
Industry Leaders: 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:208Fireball
ID: 8132386
Ok here it is, as the function currently stands. There seem to be a couple of problems: Even though i return TRUE the message is not eaten, and when i am using a static BOOL to stop messages from my insertion characters, it still goes into an endless loop if the character i use is one of the ones i am inserting...

If the character is not one of the ones i am inserting, it does not loop endlessly, but i get a few extra characters after the ones i have pasted!!!

Here's the function with some test chars. (change a to e to see the endless loop!)

Cheers,

Dave

LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{

     if(nCode < 0)
     {      
        CallNextHookEx(hMsghook, nCode, wParam, lParam);
        return 0;
     }    

     if (bProcess == TRUE){

          LPMSG lpMsg = (LPMSG) lParam;

          if(lpMsg->message == WM_CHAR && wParam == PM_REMOVE && lpMsg->wParam == 'a')
          {
               // Lock messages...
               bProcess = FALSE;
               PostMessage(lpMsg->hwnd,WM_KEYDOWN,'c',lpMsg->lParam);
               PostMessage(lpMsg->hwnd,WM_CHAR,'c',lpMsg->lParam);
               PostMessage(lpMsg->hwnd,WM_KEYUP,'c',lpMsg->lParam);

               PostMessage(lpMsg->hwnd,WM_KEYDOWN,'h',lpMsg->lParam);
               PostMessage(lpMsg->hwnd,WM_CHAR,'h',lpMsg->lParam);
               PostMessage(lpMsg->hwnd,WM_KEYUP,'h',lpMsg->lParam);

               PostMessage(lpMsg->hwnd,WM_KEYDOWN,'e',lpMsg->lParam);
               PostMessage(lpMsg->hwnd,WM_CHAR,'e',lpMsg->lParam);
               PostMessage(lpMsg->hwnd,WM_KEYUP,'e',lpMsg->lParam);

               PostMessage(lpMsg->hwnd,WM_KEYDOWN,'e',lpMsg->lParam);
               PostMessage(lpMsg->hwnd,WM_CHAR,'e',lpMsg->lParam);
               PostMessage(lpMsg->hwnd,WM_KEYUP,'e',lpMsg->lParam);

               PostMessage(lpMsg->hwnd,WM_KEYDOWN,'s',lpMsg->lParam);
               PostMessage(lpMsg->hwnd,WM_CHAR,'s',lpMsg->lParam);
               PostMessage(lpMsg->hwnd,WM_KEYUP,'s',lpMsg->lParam);

               PostMessage(lpMsg->hwnd,WM_KEYDOWN,'e',lpMsg->lParam);
               PostMessage(lpMsg->hwnd,WM_CHAR,'e',lpMsg->lParam);
               PostMessage(lpMsg->hwnd,WM_KEYUP,'e',lpMsg->lParam);

               bProcess = TRUE;
          }
          //return CallNextHookEx(hMsghook, nCode, wParam, lParam);
          return TRUE; // Swallow message
     }
     else
     {
          return CallNextHookEx(hMsghook, nCode, wParam, lParam);
     }

}
0
 
LVL 11

Expert Comment

by:KurtVon
ID: 8136850
Well, first of all you need to move that bProcess.  Since the message calls are PostMessage, the hook returns before any of them are called.

Instead you need to set the bProcess and, when the character loops back, *then* ignore it.  If the character appears twice, use an int and ignore it twice.  There's probably a nice way to do this with semaphores, but that's probably a bit more complicated than you need here.

Also, as a alast resort, you can always change the message.  WM_NULL is the NOP message, so just use that.  Not as elegant, but at least we can be sure it will work.

So the inner part of the hook should probably look like


     LPMSG lpMsg = (LPMSG) lParam;

     if(lpMsg->message == WM_CHAR && wParam == PM_REMOVE && lpMsg->wParam == 'a')
     {
         if (bProcess)
         {
              // Lock messages...
              bProcess = FALSE;
              PostMessage(lpMsg->hwnd,WM_KEYDOWN,'c',lpMsg->lParam);
              PostMessage(lpMsg->hwnd,WM_CHAR,'c',lpMsg->lParam);
              PostMessage(lpMsg->hwnd,WM_KEYUP,'c',lpMsg->lParam);

              PostMessage(lpMsg->hwnd,WM_KEYDOWN,'h',lpMsg->lParam);
              PostMessage(lpMsg->hwnd,WM_CHAR,'h',lpMsg->lParam);
              PostMessage(lpMsg->hwnd,WM_KEYUP,'h',lpMsg->lParam);

              PostMessage(lpMsg->hwnd,WM_KEYDOWN,'e',lpMsg->lParam);
              PostMessage(lpMsg->hwnd,WM_CHAR,'e',lpMsg->lParam);
              PostMessage(lpMsg->hwnd,WM_KEYUP,'e',lpMsg->lParam);

              PostMessage(lpMsg->hwnd,WM_KEYDOWN,'e',lpMsg->lParam);
              PostMessage(lpMsg->hwnd,WM_CHAR,'e',lpMsg->lParam);
              PostMessage(lpMsg->hwnd,WM_KEYUP,'e',lpMsg->lParam);

              PostMessage(lpMsg->hwnd,WM_KEYDOWN,'s',lpMsg->lParam);
              PostMessage(lpMsg->hwnd,WM_CHAR,'s',lpMsg->lParam);
              PostMessage(lpMsg->hwnd,WM_KEYUP,'s',lpMsg->lParam);

              PostMessage(lpMsg->hwnd,WM_KEYDOWN,'e',lpMsg->lParam);
              PostMessage(lpMsg->hwnd,WM_CHAR,'e',lpMsg->lParam);
              PostMessage(lpMsg->hwnd,WM_KEYUP,'e',lpMsg->lParam);

              lpMsg->message = WM_NULL;
        }
        else
            bProcess = TRUE;
     }
     return CallNextHookEx(hMsghook, nCode, wParam, lParam);
;
 
0
 

Author Comment

by:208Fireball
ID: 8147994
Cheers for the mods. Once these were implemented, some improvement was seen over the original version. However now, it will not show the original character the first time, but it ONLY shows the original character the second time it is pressed. The third time is like the first.

Still the program is adding bizarre characters to the end as well!

Here is the output after typing 4 'a's:

"cheese3388555555acheese3388555555a"

It's getting closer!

Cheers,

Dave
0
 
LVL 11

Accepted Solution

by:
KurtVon earned 225 total points
ID: 8151928
Hmm.  My mod about teh boolean flag only applies if the string actually contains the character pressed, so the reason every other "a" comes through is because there is no "a" in "cheese".  Try "chease" or comment the boolean out.

As for the numbers, I'm stumped.  You could try commenting out the WM_KEYDOWN and WM_KEYUP since the control might not actually use these anyway.  It's also possible they need the lParam modified slightly, see the MSDN documentation for details on what bits may need t be cleared.  You can use TRACE to see what they are and see if any bits look wrong.
0
 

Author Comment

by:208Fireball
ID: 8155480
Cheers! We are very close to the final answer!

The only problem i can see now is if there is a character in the replacement series of characters that was the same as the original character. I guess i'll be able to do another boolean case, but not quite sure where to put it so that it still processes ok...

Here's the app that now works if the char does not equal one of the replacement chars:

Cheers,

Dave

if(nCode < 0)
     {      
        CallNextHookEx(hMsghook, nCode, wParam, lParam);
        return 0;
     }    

     LPMSG lpMsg = (LPMSG) lParam;

     if(wParam == PM_REMOVE && lpMsg->message == WM_CHAR)
     {
          if (bProcess)
          {
               // Lock messages...
               if (lpMsg->wParam == 'a'){
                    bProcess = FALSE;
                   
                    PostMessage(lpMsg->hwnd,WM_CHAR,'c',lpMsg->lParam);

                    PostMessage(lpMsg->hwnd,WM_CHAR,'h',lpMsg->lParam);
                   
                    PostMessage(lpMsg->hwnd,WM_CHAR,'e',lpMsg->lParam);
                   
                    PostMessage(lpMsg->hwnd,WM_CHAR,'e',lpMsg->lParam);
                   
                    PostMessage(lpMsg->hwnd,WM_CHAR,'s',lpMsg->lParam);
                   
                    PostMessage(lpMsg->hwnd,WM_CHAR,'e',lpMsg->lParam);
                   
                    lpMsg->message = WM_NULL;
               }
          }
          else
          {
               bProcess = TRUE;
          }
     }
     return CallNextHookEx(hMsghook, nCode, wParam, lParam);
0
 
LVL 11

Expert Comment

by:KurtVon
ID: 8159081
Try swapping it the position of the boolean process test:

if(nCode < 0)
    {      
       CallNextHookEx(hMsghook, nCode, wParam, lParam);
       return 0;
    }    

    LPMSG lpMsg = (LPMSG) lParam;

    if(wParam == PM_REMOVE && lpMsg->message == WM_CHAR)
    {
              // Lock messages...
         if (lpMsg->wParam == 'a'){
              if (bProcess)
              {
                   bProcess = FALSE;
                   
                   PostMessage(lpMsg->hwnd,WM_CHAR,'c',lpMsg->lParam);

                   PostMessage(lpMsg->hwnd,WM_CHAR,'h',lpMsg->lParam);
                   
                   PostMessage(lpMsg->hwnd,WM_CHAR,'e',lpMsg->lParam);
                   
                   PostMessage(lpMsg->hwnd,WM_CHAR,'a',lpMsg->lParam);
                   
                   PostMessage(lpMsg->hwnd,WM_CHAR,'s',lpMsg->lParam);
                   
                   PostMessage(lpMsg->hwnd,WM_CHAR,'e',lpMsg->lParam);
                   
                   lpMsg->message = WM_NULL;
              }
              else
              {
                   bProcess = TRUE;
              }
         }
    }
    return CallNextHookEx(hMsghook, nCode, wParam, lParam);


Note that this version has an 'a' in it.  The boolean test should not be used at all if it doesn't.  If there is more than one 'a' then use a numeric counter, set it to teh number of 'a's where the boolean is set to false, decrement it where the boolean is set to true, and only process the character when the numeric value is zero.

I think if you look at it for a while you will see how that works.

0
 

Author Comment

by:208Fireball
ID: 8162681
Ok i should be right now...hope it has been a help for others out there as well!

Cheers,

Dave
0
 

Author Comment

by:208Fireball
ID: 8162687
Thanks for the assistance and sticking with the problem!
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Question has a verified solution.

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

Introduction: Displaying information on the statusbar.   Continuing from the third article about sudoku.   Open the project in visual studio. Status bar – let’s display the timestamp there.  We need to get the timestamp from the document s…
Introduction: Hints for the grid button.  Nested classes, templated collections.  Squash that darned bug! Continuing from the sixth article about sudoku.   Open the project in visual studio. First we will finish with the SUD_SETVALUE messa…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
Michael from AdRem Software explains how to view the most utilized and worst performing nodes in your network, by accessing the Top Charts view in NetCrunch network monitor (https://www.adremsoft.com/). Top Charts is a view in which you can set seve…
Suggested Courses

801 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