Hardware, Software & Product Development | Sparx EngineeringHardware, Software & Product Development | Sparx EngineeringHardware, Software & Product Development | Sparx EngineeringHardware, Software & Product Development | Sparx Engineering
  • Home
  • Expertise
    • Software Engineering
    • Electrical Engineering
    • Chemical Products and Services
    • Biomedical Engineering
    • Mechanical Engineering
    • Production Management
    • Automation
    • Industrial Design
  • Blog
  • About Us
NextPrevious

Capturing Keypress With Windows Message Filter

By bjohnson | Software | 3 comments | 16 October, 2014 | 0

Recently I ran in to a situation where I had to capture keystrokes at the application level and launch an action based on the keystroke.  I tried using the ‘keyPress’ event for the main form, but it was hit or miss based on which control had the focus.  The form was very large with many controls, so I didn’t want to have to create multiple event handlers and link everything together.

Another option was to use a global keyboard hook.  This worked reasonably well, except for the fact that any key press while the application was running (even in the background) would be processed by the application.  If I minimized the application and opened an internet browser, for example, every key pressed while using the browser was also being processed by the application. I ended up finding a solution that was global to my application, but not global to the entire machine.

Windows base applications are event driven meaning that an application waits to be told what is going on in the windows environment.  Once a windows event occurs it is passed on to the windows applications using Windows Messages.  So every key pressed, or mouse button clicked is then relayed to the application using these messages.   An Application Message Filter can be applied to catch that message and process it before the application acts on it.  Here is my example of how to add an Application Message Filter.

First, create a new Visual Studio project.  I used a basic C# Windows Forms application.  Add a new class called ‘ApplicationKeyboardHook’ which inherits from the IMessageFilter class.

[csharp]

class ApplicationKeyboardHook : IMessageFilter

{

public const int WM_KEYDOWN = 0x0100;

public const int WM_KEYUP = 0x0101;

public delegate void KeyboardEventHandler(object sender, KeyboardEventArgs e);

public KeyboardEventHandler KeyboardEvent;

public ApplicationKeyboardHook()

{

}

public bool PreFilterMessage(ref Message m)

{

if (m.Msg == ApplicationKeyboardHook.WM_KEYDOWN)

{

Keys latestKey = (Keys)m.WParam.ToInt32();

System.Diagnostics.Trace.WriteLine("Key Pressed: ‘"; + latestKey.ToString() + ”’");

if (KeyboardEvent != null)

{

KeyboardEvent(this, new KeyboardEventArgs(latestKey));

}

}

return false;

}

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]

private static extern Int16 GetKeyState(Keys virtualKeyCode);

}

[/csharp]

The above class has a function called ‘PreFilterMessage.’ This is where the message will be intercepted and acted on before it is passed on to the application. In the function, I check to see if the message is the ‘keydown’ event. If so, I grab the parameter that has the key that was pressed and raise the event to pass the key to the subscribing class. In this case, it’s the main form.

I also wrote a quick event argument class so I can pass exactly what I need and I can handle the event in the calling form without having to parse any data. The event argument class looks like this:

[csharp]

public class KeyboardEventArgs : EventArgs

{

public Keys LastKey;

public KeyboardEventArgs()

{

}

public KeyboardEventArgs(Keys Key)

{

LastKey = Key;

}

}

[/csharp]

Once the keyboard hook class is set up, we can use it in our main form.  To use the class, simply create an instance of the keyboard hook in the form and subscribe to the KeyboardEvent. To designate the keyboard hook as a pre-message filter, you must add the keyboard hook instance to the application.

[csharp]

ApplicationKeyboardHook kbHook = new ApplicationKeyboardHook();

//subscribe to keyboard event

kbHook.KeyboardEvent += ProcessPressedKey;

Application.AddMessageFilter(kbHook);

[/csharp]

Don’t forget to create the handler function ProcessPressedKey. For this example, I am just writing it out to the output window.

[csharp]

private void ProcessPressedKey(object sender, KeyboardEventArgs e)

{

Debug.WriteLine("Key Pressed = ‘" + e.LastKey.ToString() + "’" + Environment.NewLine);

}

[/csharp]

 

******Edit:

To determine case or special characters pressed you could simply check the shift key state while processing each non-shift key press. The KeyboardEventArg class can be modified to include a shift key flag.

[csharp]

public class KeyboardEventArgs : EventArgs

{

public Keys LastKey;

public bool bShiftKey = false;

public KeyboardEventArgs()

{

}

public KeyboardEventArgs(Keys Key)

{

LastKey = Key;

}

public KeyboardEventArgs(Keys Key, bool bShift)

{

LastKey = Key;

bShiftKey = bShift;

}

}

[/csharp]

And to check the shift key…

[csharp]

public bool PreFilterMessage(ref Message m)

{

if (m.Msg == ApplicationKeyboardHook.WM_KEYDOWN)

{

Keys latestKey = (Keys)m.WParam.ToInt32();

System.Diagnostics.Trace.WriteLine("Key Pressed: ‘" + latestKey.ToString() + "’");

Int16 keyState = GetKeyState(latestKey );

bool bShift = (keyState & 0x8000) == 0 ? "false" : "true";

if (KeyboardEvent != null)

{

KeyboardEvent(this, new KeyboardEventArgs(latestKey, bShift));

return true;

}

}

return false;

}

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]

private static extern Int16 GetKeyState(Keys virtualKeyCode);

[/csharp]

Now that we have the event passed to us with the shift flag, we can process this in our handler in the form1 class. Special character recognition may require a lookup table of associated pairs, but the process would be the same.

[csharp]

private void ProcessPressedKey(object sender, KeyboardEventArgs e)

{

//if looking for case

string strLastKey = e.bShiftKey ? e.LastKey.ToString().ToUpper() : e.LastKey.ToString();

//if looking for a special character

strLastKey = e.bShiftKey ? GetSpecialCharacter(e.LastKey) : e.LastKey.ToString();

Debug.WriteLine("Key Pressed = ‘" + e.LastKey.ToString() + "’" + Environment.NewLine);

}

[/csharp]

I may do a follow up blog post with a clean and easy way to detect multiple simultaneous key pressed events such as “Ctrl+Alt+Del” or “Ctrl+F” if there is any interest.

.NET, Keyboard Hook, KeyPress, Tutorial
bjohnson

bjohnson

More posts by bjohnson

Related Post

  • Reading line-by-line from a serial port (or other byte-oriented stream)

    By Ben Voigt | 8 comments

    With many .NET developers moving from the traditional (and broken) System.IO.Ports.SerialPort DataReceived event handling to either the correct and more efficient BaseStream.BeginRead / BaseStream.EndRead pair I promoted in my last post or the newer BaseStream.ReadAsyncRead more

  • How to Model NPT Threads in Solidworks

    By rmontifar | 2 comments

    National Pipe Thread Taper or NPT threaded pipes and fittings are deployed in a variety of fields where transportation or containment of liquids, gases, steam, or hydraulic fluid is required. The NPT geometry allows internalRead more

  • If you *must* use .NET System.IO.Ports.SerialPort

    By Ben Voigt | 164 comments

    As an embedded developer who writes desktop software mostly for configuration of, and data download from, peripheral devices, I use serial data streams a lot.  Mostly USB virtual serial posts from FTDI, but also theRead more

  • An example of what higher-kinded types could make possible in C#

    By dfohl | 3 comments

    If you’re like me, you spend a lot of time in Visual Studio cranking away at C# code.  You follow the updates to the language with each release, learn the new things that are possibleRead more

  • Talking RS-232 with Cyclone II FPGA, Part 2

    By dfohl | 0 comment

    In my previous blog post on RS-232 with the Altera Cyclone II FPGA, I demonstrated how to create a serial echo by simply connecting the Tx wire and the Rx wire together.  That’s a greatRead more

3 comments

  • Avatar
    Romanx Reply October 30, 2014 at 4:46 am

    Useful post however has some issues when dealing with characters such as _ and – which are Keys.OemMinus and Keys.OemMinus (With Shift Pressed) as it has no idea of previous state. This also means it can’t deal with different cases of character due to the Keys enumeration.

    If you’ve any ideas on how to fix these issues I’d be interested 🙂

    • Brad Johnson
      Brad Johnson Reply October 30, 2014 at 5:26 pm

      Thanks Romanx. I will update the post above with how I handle case and special characters.

    • Brad Johnson
      Brad Johnson Reply October 31, 2014 at 9:55 am

      Update is Posted.

Leave a Comment

Cancel reply

Your email address will not be published. Required fields are marked *

NextPrevious
  • Home
  • Expertise
  • Blog
  • About Us
Sparx Technologies, LLC. dba Sparx Engineering © 2009 - 2020 | All Rights Reserved
  • Home
  • Expertise
    • Software Engineering
    • Electrical Engineering
    • Chemical Products and Services
    • Biomedical Engineering
    • Mechanical Engineering
    • Production Management
    • Automation
    • Industrial Design
  • Blog
  • About Us
Hardware, Software & Product Development | Sparx Engineering