Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Basic Keystroke recorder and player


  • Please log in to reply
15 replies to this topic
hlbnet
  • Members
  • 19 posts
  • Last active: Apr 24 2009 08:02 AM
  • Joined: 23 Nov 2008
(I use AutoHotKey for a few month now ... and I don't understand how I could use a keyboard without it before. Basically, I use AutoHotKey enhance my productivity using hotkeys.)

Here is my problem : I need a Basic Keystroke Recorder and Player.
I searched the forums for this. I found that several users asked for this functionality.
And the answer was always : use AutoScriptWriter (or similar tool). But, it is not the appropriate answer.

What is a "Basic keystroke recorder and player" ?

A basic keystroke recorder and player has NO graphical user interface. It works only with 2 keyboard hotkeys. One hotkey is called the "record macro" key, the other is called the "play macro" key.

- The "record macro" key is used to start and stop macro recording.
- The "play macro" key is used to send the recorded keystrokes to the active window.

Here is how a user uses these two hotkeys :
First, the user presses the "record" key.
Then, the user presses various keys (except the record key), with modifiers (no need to record mouse clicks or movements).
Finally, the user presses the "record" key again.

At this time, the "macro" has been stored in a variable in memory (no need to store it in a file) and is ready to be played.

Then, the user put the focus in any window and press the "play macro" key. The effect is that the keystrokes that were stored during the recording are send to the active window (the window that is active when the "play macro" key is pressed, not the window where the script was recorded).

Details :
- The macro can be -and will be- played several times. This is the main reason to create a macro.
- The last recorded macro overwrites previous ones. The user can only play the last macro he has recorded.
- During the recording of a macro, the keystrokes must not be blocked. They must be "executed" and stored at the same time.

What is very important here is that the user has no additionnal work to do than just using the two hotkeys ! No need to launch a tool, click on various buttons, no need to copy-paste/store/load/execute scripts. This the main requirement, necessary for productivity !

I would linke to implement a "Basic keystroke recorder and player".
I think it is possible to do it with AutoHotKey, but I don't know how to start.

Any advice ?

Z_Gecko
  • Guests
  • Last active:
  • Joined: --
start with one or some of the many existing script-recorders
and modify it to your needs.
http://www.autohotke...814.html#150814
http://www.autohotke...052.html#141052
http://www.autohotke...415.html#204415
http://www.autohotke...663.html#104663
http://www.autohotke...403.html#103403

hlbnet
  • Members
  • 19 posts
  • Last active: Apr 24 2009 08:02 AM
  • Joined: 23 Nov 2008
All these scripts are complex and long. I suppose they have a lot of functionality that I do not need. It is very hard to extract the few instructions I really need.

I tried to solve the problem using my current -very basic- skills in AutoHotKey. Currently, I only record four keys : Ctrl, Shift, a and b.

#SingleInstance Force
#UseHook On
Suspend On
Recording=0
Keystrokes=

;Key Escape : Start/Stop macro recording.
Escape::
Suspend Off
If Recording=0
{
    Recording=1
    Keystrokes=
}
Else
{
    Recording=0
    Suspend On
}
Exit

;Key Ctrl-y : Play macro.
^y::
Suspend Off
SendInput %Keystrokes%
Suspend On
Exit

~LCtrl::Keystrokes=%Keystrokes%{LCtrl Down}
~LCtrl Up::Keystrokes=%Keystrokes%{LCtrl Up}

~RCtrl::Keystrokes=%Keystrokes%{RCtrl Down}
~RCtrl Up::Keystrokes=%Keystrokes%{RCtrl Up}

~LShift::Keystrokes=%Keystrokes%{LShift Down}
~LShift Up::Keystrokes=%Keystrokes%{LShift Up}

~*a::Keystrokes=%Keystrokes%{a Down}
~*a Up::Keystrokes=%Keystrokes%{a Up}

~*b::Keystrokes=%Keystrokes%{b Down}
~*b Up::Keystrokes=%Keystrokes%{b Up}

Am I on the right track ?
Is there a simpler way to capture keystrokes ?
Thanks by advance.

EDIT: I used a key of my french keyboard. Replaced with Escape to be sure it works on any keyboard.

RTFM
  • Guests
  • Last active:
  • Joined: --

Then, the user presses various keys (except the record key), with modifiers (no need to record mouse clicks or movements).

---> Input. I guess afterwards you've to convert the kept keystrokes before you're able to send them.

Z_Gecko
  • Guests
  • Last active:
  • Joined: --

Am I on the right track ?

Not really.

All these scripts are complex and long.

That´s because a script writer is not an easy thing.

Is there a simpler way to capture keystrokes ?

No, Hotkeys are the "easiest" way, to record keystrokes, but not the best.
I personally would suggest a mouse- and keyboard-hook as shown by Sean.
http://www.autohotke...490.html#127490

hlbnet
  • Members
  • 19 posts
  • Last active: Apr 24 2009 08:02 AM
  • Joined: 23 Nov 2008
I tried to use the SetWindowsHookEx function to hook the keyboard to implement my macro recorder, as suggested by Z_Gecko.

Using this technique, I could get the scan code and virtual key code of any keystroke. That is very useful information.

Unfortunately, I miss one information : I don't know if the key that I receive is pressed or released.

When a key is pressed and released, the hook is actually called twice ... but I do not know which invocation correspond to the keypress and which correspond to the release.

Any idea ?

hlbnet
  • Members
  • 19 posts
  • Last active: Apr 24 2009 08:02 AM
  • Joined: 23 Nov 2008
Here is a partial implementation, using the keyboard hook.
- The "record macro" key is Escape (to start and stop macro recording)
- The "play macro" key is Ctrl-y

At the end of the script, instead of actually playing the macro using SendInput, I just show the stored keystrokes in a message box.

As you can see, I miss the Up/Down modifiers because I don't know how to retrieve the event type (key pressed or released).

Try this:
- Launch the script.
- Type Escape.
- Type abc
- Type Escape
- Type Ctrl-y

#SingleInstance Force
#UseHook On

; Variable set to 1 during macro recording.
Recording=0

; Variable used to store keystrokes during recording.
Keystrokes=

; Do not forget to release the hook on exit.
OnExit, Unhook

Return

Unhook:
If Recording=1
    UnhookWindowsHookEx(hHookKeybd)
ExitApp

; Function called on each keyboard event during macro recording
Keyboard(nCode, wParam, lParam)
{
   Global Keystrokes
   ; I don't know what nCode is. I have just copied from Sean's code.
   If !nCode
   {
       ; Get the virtual key code and the scan code from the key event.
       vk:=NumGet(lParam+0,0)
       sc:=NumGet(lParam+0,4)
       If ( vk != 27 )
       {
           ; Store the keystroke (except the record start/stop key)
           vk0:=HexDigit(vk,0)
           vk1:=HexDigit(vk,1)
           sc0:=HexDigit(sc,0)
           sc1:=HexDigit(sc,1)
           sc2:=HexDigit(sc,2)
           ; FIXME : I should check if the key is Pressed or Release. Don't know how.
           Keystrokes=%Keystrokes%{vk%vk1%%vk0%sc%sc2%%sc1%%sc0%}
       }
   }
   Return CallNextHookEx(nCode, wParam, lParam)
}

; Extract an hexadecimal digit from a number
HexDigit( number, position )
{
    If position=0
        number := Mod(number,16)
    Else If position=1
        number := Mod(number//16,16)
    Else If position=2
        number := Mod(number//256,16)

    If number<10
        Return Chr(48+number)

    Return Chr(55+number)
}

SetWindowsHookEx(idHook, pfn)
{
   Return DllCall("SetWindowsHookEx", "int", idHook, "Uint", pfn, "Uint", DllCall("GetModuleHandle", "Uint", 0), "Uint", 0)
}

UnhookWindowsHookEx(hHook)
{
   Return DllCall("UnhookWindowsHookEx", "Uint", hHook)
}

CallNextHookEx(nCode, wParam, lParam, hHook = 0)
{
   Return DllCall("CallNextHookEx", "Uint", hHook, "int", nCode, "Uint", wParam, "Uint", lParam)
}


; Key Escape => Start/Stop macro recording.
Escape::
If Recording=0
{
    Recording=1
    Keystrokes=
    hHookKeybd := SetWindowsHookEx(WH_KEYBOARD_LL:=13, RegisterCallback("Keyboard", "Fast"))
}
Else
{
    Recording=0
    UnhookWindowsHookEx(hHookKeybd)
}
Exit

; Key Ctrl-y => Play macro.
; Should be "SendInput" instead of "MsgBox"
^y::MsgBox %Keystrokes%

Is there a solution to know if a key is pressed or released in the keyboard hook ?

RTFM
  • Guests
  • Last active:
  • Joined: --
GetKeyState

hlbnet
  • Members
  • 19 posts
  • Last active: Apr 24 2009 08:02 AM
  • Joined: 23 Nov 2008
I tried using GetKeyState inside the keyboard hook, but it doesn't work well. The returned state is not always correct.

I suppose it is because when the keyboard function is executed, interpretation of keystrokes by AutoHotKey itselft did not yet occured (?).

I was assuming that I could retrieve the type of event (keypress or keyrelease) from the parameters passed to the keyboard function that intercepts any keyboard events.

I am stuck !

hlbnet
  • Members
  • 19 posts
  • Last active: Apr 24 2009 08:02 AM
  • Joined: 23 Nov 2008
I have found what I was looking for.
Now, my Basic Keystroke Recorder and Player seems to work fine.
- The "record macro" key is Escape.
- The "play macro" key is Ctrl-y

Here is the code:
#SingleInstance Force
#UseHook On

; Variable set to 1 during macro recording.
Recording=0

; Variable used to store keystrokes during recording.
Keystrokes=

; Do not forget to release the hook on exit.
OnExit, Unhook

Return

Unhook:
If Recording=1
    UnhookWindowsHookEx(hHookKeybd)
ExitApp

; Function called on each keyboard event during macro recording
Keyboard(nCode, wParam, lParam)
{
   Global Keystrokes
   ; I don't know what nCode is. I have just copied from Sean's code.
   If !nCode
   {
       ; Get the virtual key code and the scan code from the key event.
       vk:=NumGet(lParam+0,0)
       If ( vk != 27 )
       {
           ; Store the keystroke (except the record start/stop key)
           vk0:=HexDigit(vk,0)
           vk1:=HexDigit(vk,1)

           sc:=NumGet(lParam+0,4)
           sc0:=HexDigit(sc,0)
           sc1:=HexDigit(sc,1)
           sc2:=HexDigit(sc,2)

           ext:=NumGet(lParam+0,8)
           If ext & 128
               upDown=Up
           Else
               upDown=Down

           Keystrokes=%Keystrokes%{vk%vk1%%vk0%sc%sc2%%sc1%%sc0% %upDown%}
       }
   }
   Return CallNextHookEx(nCode, wParam, lParam)
}

; Extract an hexadecimal digit from a number
HexDigit( number, position )
{
    If position=0
        number := Mod(number,16)
    Else If position=1
        number := Mod(number//16,16)
    Else If position=2
        number := Mod(number//256,16)

    If number<10
        Return Chr(48+number)

    Return Chr(55+number)
}

SetWindowsHookEx(idHook, pfn)
{
   Return DllCall("SetWindowsHookEx", "int", idHook, "Uint", pfn, "Uint", DllCall("GetModuleHandle", "Uint", 0), "Uint", 0)
}

UnhookWindowsHookEx(hHook)
{
   Return DllCall("UnhookWindowsHookEx", "Uint", hHook)
}

CallNextHookEx(nCode, wParam, lParam, hHook = 0)
{
   Return DllCall("CallNextHookEx", "Uint", hHook, "int", nCode, "Uint", wParam, "Uint", lParam)
}

; Key Escape => Start or Stop macro recording.
Escape::
If Recording=0
{
    Recording=1
    Keystrokes=
    hHookKeybd := SetWindowsHookEx(WH_KEYBOARD_LL:=13, RegisterCallback("Keyboard", "Fast"))
}
Else
{
    Recording=0
    UnhookWindowsHookEx(hHookKeybd)
}
Exit

; Key Ctrl-y => Play macro.
^y::SendInput %Keystrokes%

To try it, you can do the following :
- Launch the script.
- Open a notepad and copy the following text in it :
1 January
2 February
3 March
4 April
5 May
6 June
7 July
8 August
9 September
10 October
11 November
12 December
- Put your cursor at begining of the first line.
- Type the Escape key to start macro recording.
- Using only keyboard actions (Keys: End, Arrows, Home, etc) place a dot at the end of the first line and then put your cursor at begining of the second line.
- After that, type the Escape key to stop macro recording.
- And finally, type Ctrl-y several times.
=> The action that adds a dot at end of line is executed for each line.

I will continue to test, because I am not sure it works when another AutoHotKey script is running concurently and intercepts/remap keys.

Thank a lot to Z_Gecko and RTFM for advices. It was useful.

hlbnet
  • Members
  • 19 posts
  • Last active: Apr 24 2009 08:02 AM
  • Joined: 23 Nov 2008
My script works well when used alone, but has a problem when another AHK script is running and executing hotkeys.

The problem is that both the keystrokes actually typed by the user and the keystrokes sent by AHK are stored to be replayed.

The result is funny.
For example, I tried with the following dummy script running at the same time as the macro recorder :
#SingleInstance Force
m::m

This code just replace m by m. This should have no effect.

If I type the following to record a macro :
- Key Escape to start macro recording.
- Key m to trigger the dummy hotkey.
- Key Escape to stop macro recording.

Then, each time I do Ctrl-y to replay the macro, I get two "m"s.

I could use my macro recorder and player without any other AHK script running, but that is a big problem for me. I have fully remapped my keyboard using AHK and I cannot work without my hotkeys now.

Once again, I am stuck !
Any idea ?

evan
  • Members
  • 125 posts
  • Last active: Feb 25 2011 03:05 AM
  • Joined: 19 Feb 2009
maybe u can add a code that checks the typing speed
for example, if the time between 2 key history is less than 5ms, then ignore

Failed User
  • Guests
  • Last active:
  • Joined: --
trying to use a key stroker "f1" and repeat on a program.... Had this technology working in 2001 not sure nearly 20 years later why this is so complicated. uninstalling

newuser1987
  • Guests
  • Last active:
  • Joined: --
Sorry for the necro everyone, but I just found hlbnet's script and it works extremely well for what I (well, actually, my coworker) need. However, I've noticed that if
Escape:: 
If Recording=0 
{ 
...coding stuff
is changed to
^F12:: 
If Recording=0 
{ 
...coding stuff
or if I use any hotkey with a modifier, the modifier gets stuck down until I press it again. Since all this code is way above my head, does anyone know how to fix it?

tweetyhack
  • Guests
  • Last active:
  • Joined: --
I wrote one in AutoIT but it wasn't working perfectly, it captures keys that were typed before I started recording. Thought I'd give it a try here.