Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

Recording keyboard key strokes

Posted on 2011-03-21
23
Medium Priority
?
2,099 Views
Last Modified: 2013-12-17
Hi Experts.

I'm a bit stuck here so I would really appreciate some help.

I'm working on a macro program that will forward certain keyboard key strokes to other keys. So let's say if I press "T" I want "U" to be returned instead.
My GUI has a text box that users can click in and specify which button will that "T" key press be forwarded to. Key press event :

        private void txtTrigger_KeyPress(object sender, KeyPressEventArgs e)
        {
              txtTrigger.Text = e.KeyChar.ToString();
        }
Then I save that Textbox's INT ascii value to the XML file as [112] and next time a program user hits "T" - 112 gets converted back to a string and sent back:

ConfigFile.Trigger = Char.ConvertToUtf32(txtTrigger.Text,0);

SendKeys.SendWait(SpellToTrigger);

Awesome, works great with LETTERS ONLY.
 
I can't figuire out how to record and return other keys such as F1-12, CTRL, ALT, TAB, Backpsace...etc. They don't seem to trigger TextBox KeyPress events at all. Do I really have to go though every single Function and other keys on my keyboard wtih if (e.KeyCode == Keys.F9) individually ?

I tried using the KeyDown instead of KeyPress event and Function key presses do get picked up but KeyValues returned back to a user are incorrect:

            int pressedKey = e.KeyValue;
            txtTrigger.Text = char.ConvertFromUtf32(pressedKey);

I press F8 and W appears in that txtTrigger. F5 shows up as letter T.
Shift, Control and some other keys appear as .

Any help ? All I want to do is setup something like you might have seen in computer games when you setup your input bindings where you specify what key your action is forwared to.

Thank you in advance.
0
Comment
Question by:techsuppoprt
[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
  • 11
  • 10
  • 2
23 Comments
 
LVL 8

Expert Comment

by:mac-will
ID: 35184480
You could use a lower level keyboard hook.

http://blogs.msdn.com/b/toub/archive/2006/05/03/589423.aspx
0
 
LVL 12

Expert Comment

by:Mohamed Abowarda
ID: 35185843
If you want to record the keystrokes even if your program is not active on the screen, you need to use GetAsyncKeyState API to frequently check each key state:
http://msdn.microsoft.com/en-us/library/ms646293(v=vs.85).aspx
0
 
LVL 1

Author Comment

by:techsuppoprt
ID: 35191308
Thanks.
I don't think you guys read my question at all though or was it a bit confusing possibly ?

Let me try to rephrase it.
What's the best way to store captured key strokes in the XML file ?

Right now I'm saving the INT keyvalues and doing so won't let me properly capture Function keys and other buttons such as Backspase, Delete, Shift, ALT, Caps Lock .. etc.

I want to be able to capture and record ( in the XML config file ) any key on my keyboard to later that key press be simulated back to the user with something like
SendKeys.SendWait().
0
Basic Security of Your VPC

So, you’ve got this shiny new VPC and a fancy new application configured on your EC2 servers ready to go. This application is only accessible from your computer, which is great for security, but you need your users to be able to access it! So, what’s the easiest way to do this?

 
LVL 8

Expert Comment

by:mac-will
ID: 35193121
This still is not clear to me.

Do you want to capture the keypress event of a textbox or key presses in general from the application?  Obviously pressing shift does not do anything in the keypress event of a textbox since you cannot display a <shift> for example.

Perhaps you could simply use the keyDown or KeyUp events? As these events are triggered even for control keys.

From MSDN:

"The KeyPress event is not raised by noncharacter keys; however, the noncharacter keys do raise the KeyDown and KeyUp events."

http://msdn.microsoft.com/en-us/library/system.windows.forms.control.keypress.aspx

0
 
LVL 8

Expert Comment

by:mac-will
ID: 35193213
If it helps try adding this handler to see what king of info is printed when you press a key.

        private void txtTrigger_KeyDown(object sender, KeyEventArgs e)
        {
            Console.WriteLine("KeyCode : " + e.KeyCode.ToString() + "    Modifiers : " + e.Modifiers.ToString());
        }

Open in new window


And as for storing the info in an xml file why not just store the keycode (as a string)

Here is the back and forth from string to keycode:

Keys mySavedValue = (Keys)Enum.Parse(typeof(Keys), "Alt");

String myValueToSave = e.KeyCode.ToString();

Open in new window

0
 
LVL 1

Author Comment

by:techsuppoprt
ID: 35193300
What I want:
- I have a form with a button and a text box;
- User activates a form text box;
- Once the textbox is active - user presses F6
- "F6" shows up in the textbox control and something gets saves to the XML file
- User presses the button - "F6" key press is simulated.

Does it make sense ?

My problem is not HOW to capture. I know how to do it through Keypress/KeyDown events... it's WHAT to capture. What can I record to the XML file to easily re-play any button on the keyboard, inluding Function, extra keys and key combination ?

I want my program to read the XML file and recognize what key press actions to send back to the user.
0
 
LVL 8

Expert Comment

by:mac-will
ID: 35193477

I will assume that the "User presses the button ..." simulation is for the textbox.


So just like my example above but combining both steps:

        private void txtTrigger_KeyDown(object sender, KeyEventArgs e)
        {
             if( check if its a printable keycode)
             {
                  txtTrigger.Text += e.KeyCode.ToString();
             }
            MySaveToXMLFunction(e.KeyCode.ToString());
        }

Open in new window



To Send a key you could use the SendKeys class or if you need more control use the Windows API:

  private void btnSend_Click(object sender, EventArgs e)
        {
            txtTrigger.Select();
            SendKeys.Send("{" + Keys.F6.ToString() + "}");
       }

Open in new window


Obviously the Keys.F6.ToString() would be whatever you retrieved from you xml file....


0
 
LVL 1

Author Comment

by:techsuppoprt
ID: 35194689

Would you say it's safe to go based on this chart:

http://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys.send.aspx

and simply throw all my saved KeyCode values in SendKey.Send( { SavedKeyCode } ) whenever I need to send that saved key back to the user ?

I assume I'll check for SHIFT/ALT/CTR pressed and modify my saved XML KeyCode with + % or ^ accordingly ?
Going to try it...Let me know if it sounds wrong.
0
 
LVL 8

Expert Comment

by:mac-will
ID: 35198233
That sounds right.  

That is what this class (SendKeys) was made for.  If this meets your needs in terms of what control is receiveing the keys etc. than definitely go this way.  

If for example you want to send keys to the desktop or receive keys when your application does not have focus you would probably want to go with a keyboard hook.
0
 
LVL 1

Author Comment

by:techsuppoprt
ID: 35199922
Errr...Doesn't really work as some KeyCode string values don't match with the actual keys. ( NumPad5 , D6 .. etc). SendKeys.SendWait("NumPad8") actually returns "NumPad8" instead of simulating number 8 being pressed on the NumPad.

Is there  a way to cast/convert KeyEventArgs.KeyCode back to Keys enum and send a keypress event that way ?

Thank you in advance.
ps. I added points.
0
 
LVL 8

Expert Comment

by:mac-will
ID: 35200346
Ok so no, not really.  there is no magic you either have to map the Keys enum to the table in the link above or more easily use the Windows API.

(Are you familiar with PInvoke?)

Here is the all the code you need:
(using System.Runtime.InteropServices;)

        [DllImport("user32.dll")]
        static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);

        void PressKey(byte keyCode)
        {
            const int KEYEVENTF_EXTENDEDKEY = 0x1;
            const int KEYEVENTF_KEYUP = 0x2;
            keybd_event(keyCode, 0x45, KEYEVENTF_EXTENDEDKEY, 0);
            keybd_event(keyCode, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
        }

Open in new window


But this time you save the KeyValue in the press event and send that to the PressKey function.

I think this should work for most keys anyway.
0
 
LVL 8

Expert Comment

by:mac-will
ID: 35201009
0
 
LVL 1

Author Comment

by:techsuppoprt
ID: 35202373
I'm not familiar but I will make myself now :)
Thank you for your help. I need a few final pushes if you don't mind.

But this time you save the KeyValue in the press event and send that to the PressKey function
There is no KeyValue property on the keypress event though... Did you mean KeyChar ?

Will it also recognize/replay ALT/SHIFT/CTR-key cobos ?

Thank you.
0
 
LVL 1

Author Comment

by:techsuppoprt
ID: 35202606
        private void txtTrigger_KeyPress(object sender, KeyPressEventArgs e)
        {
          PressKey((byte)e.KeyChar);
         }

        [DllImport("user32.dll")]
        static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);

        void PressKey(byte keyCode)
        {
            const int KEYEVENTF_EXTENDEDKEY = 0x1;
            const int KEYEVENTF_KEYUP = 0x2;
            keybd_event(keyCode, 0x45, KEYEVENTF_EXTENDEDKEY, 0);
            keybd_event(keyCode, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
        }

Open in new window


^ Doesn't work. it returns some bizarre stuff. Pressing "P" triggers F1. Pressing "E" triggers number 4 ...etc.
0
 
LVL 8

Accepted Solution

by:
mac-will earned 2000 total points
ID: 35207118
Sorry that should have been the KeyValue in the KeyDown/Up event...

Anyway, I notice on MSDN that keybd_event is deprecated so here is a little test form I wrote to show you a better way.

Simply copy all this code into a new file and run the form.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication1
{
    public partial class MacroExampleForm : Form
    {
        private TextBox txtReplayOutput;
        private Button btnRecord;
        private Button btnReplay;
        private Label lblStatus;
        private IContainer components = null;

        struct KeyboardEvent
        {
            public KeyboardEvent(byte _vKey, bool _keyUp)
            {
                vKey = _vKey;
                keyUp = _keyUp;
            }

            public byte vKey;
            public bool keyUp;
        }

        List<KeyboardEvent> recorded_data = new List<KeyboardEvent>();
        bool isRecording = false;

        public MacroExampleForm()
        {
            InitializeComponent();

            this.btnRecord.KeyDown += new KeyEventHandler(MacroExampleForm_KeyDown);
            this.btnRecord.KeyUp += new KeyEventHandler(MacroExampleForm_KeyUp);
        }

        void MacroExampleForm_KeyUp(object sender, KeyEventArgs e)
        {
            if (isRecording)
            {
                recorded_data.Add(new KeyboardEvent((byte)e.KeyValue, true));
            }
        }


        void MacroExampleForm_KeyDown(object sender, KeyEventArgs e)
        {
            if (isRecording)
            {
                recorded_data.Add(new KeyboardEvent((byte)e.KeyValue, false));
            }
        }

        private void btnRecord_Click(object sender, EventArgs e)
        {
            if (isRecording)
            {
                isRecording = false;
                lblStatus.Text = "Not Recording";
            }
            else
            {
                recorded_data.Clear();
                isRecording = true;
                lblStatus.Text = "Recording";
            }
        }

        private void btnReplay_Click(object sender, EventArgs e)
        {
            if (!isRecording && recorded_data.Count > 0)
            {
                foreach (KeyboardEvent keyE in recorded_data)
                {
                    txtReplayOutput.Select();

                    if (keyE.keyUp)
                    {
                        KeyboardSimulator.SendKeyUp(keyE.vKey);
                    }
                    else
                    {
                        KeyboardSimulator.SendKeyDown(keyE.vKey);
                    }
                }
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            this.txtReplayOutput = new System.Windows.Forms.TextBox();
            this.btnRecord = new System.Windows.Forms.Button();
            this.btnReplay = new System.Windows.Forms.Button();
            this.lblStatus = new System.Windows.Forms.Label();
            this.SuspendLayout();
            // 
            // txtReplayOutput
            // 
            this.txtReplayOutput.Location = new System.Drawing.Point(12, 50);
            this.txtReplayOutput.Multiline = true;
            this.txtReplayOutput.Name = "txtReplayOutput";
            this.txtReplayOutput.Size = new System.Drawing.Size(296, 145);
            this.txtReplayOutput.TabIndex = 0;
            // 
            // btnRecord
            // 
            this.btnRecord.Location = new System.Drawing.Point(108, 201);
            this.btnRecord.Name = "btnRecord";
            this.btnRecord.Size = new System.Drawing.Size(97, 49);
            this.btnRecord.TabIndex = 1;
            this.btnRecord.Text = "Record";
            this.btnRecord.UseVisualStyleBackColor = true;
            this.btnRecord.Click += new System.EventHandler(this.btnRecord_Click);
            // 
            // btnReplay
            // 
            this.btnReplay.Location = new System.Drawing.Point(211, 201);
            this.btnReplay.Name = "btnReplay";
            this.btnReplay.Size = new System.Drawing.Size(97, 49);
            this.btnReplay.TabIndex = 2;
            this.btnReplay.Text = "Replay";
            this.btnReplay.UseVisualStyleBackColor = true;
            this.btnReplay.Click += new System.EventHandler(this.btnReplay_Click);
            // 
            // lblStatus
            // 
            this.lblStatus.AutoSize = true;
            this.lblStatus.Location = new System.Drawing.Point(12, 24);
            this.lblStatus.Name = "lblStatus";
            this.lblStatus.Size = new System.Drawing.Size(76, 13);
            this.lblStatus.TabIndex = 3;
            this.lblStatus.Text = "Not Recording";
            // 
            // MacroExampleForm
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(322, 264);
            this.Controls.Add(this.lblStatus);
            this.Controls.Add(this.btnReplay);
            this.Controls.Add(this.btnRecord);
            this.Controls.Add(this.txtReplayOutput);
            this.Name = "MacroExampleForm";
            this.Text = "MacroExampleForm";
            this.ResumeLayout(false);
            this.PerformLayout();

        }
    }


    public static class KeyboardSimulator
    {

        public static void SendKeyDown(byte vKey)
        {
            INPUT[] inputs = new INPUT[1];
            inputs[0].type = INPUT_KEYBOARD;
            inputs[0].ki.wVk = (ushort)vKey;

            uint rval = SendInput(1, ref inputs[0], Marshal.SizeOf(new INPUT()));
            if (rval != 1)
            {
                //TODO - make this more descriptive...
                throw new Exception();
            }
        }

        public static void SendKeyUp(byte vKey)
        {
            INPUT input = new INPUT();
            input.type = INPUT_KEYBOARD;
            input.ki.wVk = (ushort)vKey;
            input.ki.dwFlags = KEYEVENTF_KEYUP;

            uint rval = SendInput(1, ref input, Marshal.SizeOf(new INPUT()));
            if (rval != 1)
            {
                //TODO - make this more descriptive...
                throw new Exception();
            }
        }

        [DllImport("user32.dll", SetLastError = true)]
        static extern uint SendInput(uint nInputs, ref INPUT pInputs, int cbSize);

        public const int INPUT_KEYBOARD = 1;
        public const uint KEYEVENTF_EXTENDEDKEY = 0x0001;
        public const uint KEYEVENTF_KEYUP = 0x0002;
        public const uint KEYEVENTF_UNICODE = 0x0004;
        public const uint KEYEVENTF_SCANCODE = 0x0008;
    }


    [StructLayout(LayoutKind.Sequential)]
    public struct KEYBDINPUT
    {
        public ushort wVk;
        public ushort wScan;
        public uint dwFlags;
        public uint time;
        public IntPtr dwExtraInfo;
    }


    [StructLayout(LayoutKind.Explicit, Size = 28)]
    public struct INPUT
    {
        [FieldOffset(0)]
        public uint type;
        [FieldOffset(4)]
        public KEYBDINPUT ki;
    };
}

Open in new window



Good luck

mac
0
 
LVL 1

Author Comment

by:techsuppoprt
ID: 35242209
Sorry for a delay but I'm still having problems with this that I can't seem to figure out. Everything seems right yet....

So what I'm storing in my XML file are e.KeyValue values returned from a control's key down event. And then use them to simulate key presses. So basically it's this:

        private void txTrigger_KeyDown(object sender, KeyEventArgs e)
        {

            KeyboardSimulator.SendKeyDown((byte)e.KeyValue);
        }

        private void txtTrigger_KeyUp(object sender, KeyEventArgs e)
        {
            KeyboardSimulator.SendKeyUp((byte)e.KeyValue);
        }

Open in new window


Rest of the code is what you've posted above. I didn't make any changes to the KeybardSimulator class at all. Using it exactly as you posted.

Yet what happens is when txtTrigger key click is triggered it gets stuck in unlimited KeyDown loop. So if I press "K" for example I get KKKKKKKKKKKKKKKKKKKKKKKKKKKK...etc.

I can't figure out why and what's different in my code from what you wrote.
0
 
LVL 8

Expert Comment

by:mac-will
ID: 35257934
Hi,

If you are sending a keydown event from the keydown handler you will of course get a loop.

Basically you are saying when 'K' is pressed press it again.

Is this a macro recorder of some sort?

Basically you need to record the various key down and key up events than save this sequence to your xml file and replay the sequence when you need to.

Try getting the example I posted to work without any modifications I think it will explain a lot.

btw the reason for saving both the key down and up events is for modifier keys.

for example this sequence:

shift down
k down
k up
shift up

will yield 'K'
if instead you just recorded key presses (down - up event) for the same sequence you would have recorded k press than shift press which would yield 'k' and not 'K' as intended.
0
 
LVL 1

Author Comment

by:techsuppoprt
ID: 35386360
Account expired at the worst time.
This is not abandoned.
0
 
LVL 12

Expert Comment

by:Mohamed Abowarda
ID: 35386399
@techsuppoprt: You need to interact with the experts to solve the question, you have left the question since the last expert comment at 03/30/11 therefore the question was classified as abandoned.
0
 
LVL 1

Author Comment

by:techsuppoprt
ID: 35387998
Mac, thanks for your help.
I'm about to close this one.
I have one last question i you don't mind.

My key strokes are being forwarded/replayed back to the interface just fine , lets say if I use notepad.
But then I have some programs where it doesn't work.

What could be blocking keys in an application from being sent to its interface ? Any clue ?
0
 
LVL 8

Expert Comment

by:mac-will
ID: 35460106
I would guess it might have something to do with the control that has focus?  Perhaps these application do not propagate key presses as normal to child controls?

Just a guess.
0
 
LVL 1

Author Comment

by:techsuppoprt
ID: 35461893
Alright. Well thank you :)
0
 
LVL 1

Author Comment

by:techsuppoprt
ID: 35469705
0

Featured Post

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

Question has a verified solution.

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

For those of you who don't follow the news, or just happen to live under rocks, Microsoft Research released a beta SDK (http://www.microsoft.com/en-us/download/details.aspx?id=27876) for the Xbox 360 Kinect. If you don't know what a Kinect is (http:…
Real-time is more about the business, not the technology. In day-to-day life, to make real-time decisions like buying or investing, business needs the latest information(e.g. Gold Rate/Stock Rate). Unlike traditional days, you need not wait for a fe…
This tutorial covers a step-by-step guide to install VisualVM launcher in eclipse.
The viewer will learn how to use NetBeans IDE 8.0 for Windows to connect to a MySQL database. Open Services Panel: Create a new connection using New Connection Wizard: Create a test database called eetutorial: Create a new test tabel called ee…

705 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