Cool custom Recorder that logs Mouse and keyboard input !!!

Jaafar Tribak

Well-known Member
Joined
Dec 5, 2002
Messages
9,604
Office Version
  1. 2016
Platform
  1. Windows
Hi all,

This is for having some fun with XL but I guess it can also be taken advantage of and be very useful in some scenarios.It was a nightmare putting the code together.(i had endless crashes)

As the title implies, this is similar to the Macro recorder but obviously not as effective as all it does is record the mouse & keyboard input.

Another bonus is that the recorded actions can be saved to disk and be retrieved later after closing excel. In fact it works system wide not just within XL.

Anyway, have some fun and tell me what you think.:)

workbook example:
http://www.savefile.com/files/2039238

Code in a Standard Module :


Code:
Option Base 0
Option Explicit
 
Private Type EVENTMSG
 
    message As Long
    paramL As Long
    paramH As Long
    time As Long
    hwnd As Long
 
End Type
 
Private Type Msg
    uCode As Long
    message As Long
    paramL As Long
    paramH As Long
    time As Long
    hwnd As Long
 
End Type
 
Private Declare Function GetWindowLong Lib _
"user32" Alias "GetWindowLongA" _
(ByVal hwnd As Long, ByVal nIndex As Long) As Long
 
Private Declare Function SetWindowsHookEx Lib _
"user32" Alias "SetWindowsHookExA" _
(ByVal idHook As Long, ByVal lpfn As Long, _
ByVal hmod As Long, ByVal dwThreadId As Long) As Long
 
Private Declare Function UnhookWindowsHookEx Lib "user32" _
(ByVal hHook As Long) As Long
 
Private Declare Function CallNextHookEx Lib "user32" _
(ByVal hHook As Long, ByVal ncode As Long, _
ByVal wParam As Long, lParam As Any) As Long
 
Private Declare Function GetTickCount Lib "kernel32" () As Long
 
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
 
Private Const WH_JOURNALPLAYBACK = 1
Private Const WH_JOURNALRECORD = 0
Private Const HC_ACTION = 0
Private Const HC_GETNEXT = 1
Private Const HC_SKIP = 2
Private Const HC_NOREMOVE = 3
Private Const HC_NOREM = HC_NOREMOVE
Private Const HC_SYSMODALOFF = 5
Private Const HC_SYSMODALON = 4
Private Const VK_CANCEL = &H3
Private Const WM_KEYDOWN = &H100
Private Const WM_MOUSEMOVE = &H200
Private Const GWL_HINSTANCE = (-6)
Private Const LogPath As String = "C:\Test\"
Private Const LogFile As String = "LogFile.DAT"
 
Private hRecordHook As Long
Private hPlaybackHook As Long
Private MsgArray() As Long
Private StartTime As Long
Private CurrMSG As Long
Private bSlowSpeed As Boolean
Private bIsHooked As Boolean
Private IsPBHooked As Boolean
Private bDone As Boolean
 
Private Sub SetJournalHook()
 
    If bIsHooked Then
        MsgBox "The recorder is already activated.", vbInformation
    Else
        StartTime = GetTickCount
        hRecordHook = SetWindowsHookEx _
        (WH_JOURNALRECORD, AddressOf JournalProc, GetAppInstance, 0)
        bIsHooked = True
    End If
 
End Sub
 
Private Sub RemoveJournalHook()
 
    Dim lRetVal As Long
    lRetVal = UnhookWindowsHookEx(hRecordHook)
    bIsHooked = False
    MsgBox "Key & Mouse recorder stopped.", vbInformation
 
End Sub
 
Private Function JournalProc _
(ByVal uCode As Long, ByVal wParam As Long, lParam As EVENTMSG) As Long
 
    If uCode = HC_ACTION Then
 
        If lParam.message = WM_KEYDOWN And _
        GetLowByte(lParam.paramL) = VK_CANCEL Then
            Call RemoveJournalHook
        End If
 
        MsgArray(0, UBound(MsgArray, 2)) = uCode
        MsgArray(1, UBound(MsgArray, 2)) = lParam.message
        MsgArray(2, UBound(MsgArray, 2)) = lParam.hwnd
        MsgArray(3, UBound(MsgArray, 2)) = lParam.paramH
        MsgArray(4, UBound(MsgArray, 2)) = lParam.paramL
        MsgArray(5, UBound(MsgArray, 2)) = (lParam.time - StartTime)
 
        ReDim Preserve MsgArray(6, (UBound(MsgArray, 2) + 1)) As Long
 
        ElseIf uCode = HC_SYSMODALON Then
 
        ElseIf uCode = HC_SYSMODALOFF Then
 
    End If
 
    JournalProc = CallNextHookEx(hRecordHook, uCode, wParam, lParam)
 
End Function
 
Private Sub SetPlaybackHook()
 
    If Sheets(1).Shapes("Opt1").ControlFormat.Value = 1 Then
        bSlowSpeed = True
    Else
        bSlowSpeed = False
    End If
    If IsPBHooked Then
        MsgBox "Playback is already active", vbInformation
    Else
        StartTime = GetTickCount
        CurrMSG = 1
        hPlaybackHook = SetWindowsHookEx(WH_JOURNALPLAYBACK, _
        AddressOf JournalPlaybackProc, GetAppInstance, 0)
        IsPBHooked = True
        Application.StatusBar = "Playing Back..."
    End If
 
End Sub
 
Private Sub RemovePlaybackHook()
 
    Dim lRetVal As Long
 
    lRetVal = UnhookWindowsHookEx(hPlaybackHook)
    IsPBHooked = False
    Application.StatusBar = False
    MsgBox "Key & Mouse PlayBack finished.", vbInformation
 
End Sub
 
Public Function JournalPlaybackProc _
(ByVal uCode As Long, ByVal wParam As Long, lParam As EVENTMSG) As Long
 
    If uCode = HC_GETNEXT Then
    If bSlowSpeed Then Sleep 1
        If CurrMSG >= UBound(MsgArray, 2) Then
            bDone = True
            Call RemovePlaybackHook
        Else
            lParam.message = MsgArray(1, CurrMSG)
            lParam.hwnd = MsgArray(2, CurrMSG)
            lParam.paramH = MsgArray(3, CurrMSG)
            lParam.paramL = MsgArray(4, CurrMSG)
            lParam.time = MsgArray(5, CurrMSG) - MsgArray(5, CurrMSG - 1)
            JournalPlaybackProc = MsgArray(5, CurrMSG) - MsgArray(5, CurrMSG - 1)
        End If
    ElseIf uCode = HC_SKIP Then
        If CurrMSG >= UBound(MsgArray, 2) Then
            Call RemovePlaybackHook
            bDone = True
        End If
        CurrMSG = CurrMSG + 1
    ElseIf uCode = HC_NOREMOVE Then
    ElseIf uCode = HC_SYSMODALON Then
    ElseIf uCode = HC_SYSMODALOFF Then
    End If
    JournalPlaybackProc = CallNextHookEx(hPlaybackHook, uCode, wParam, lParam)
 
End Function
 
Private Sub StartRecording()
 
    ReDim MsgArray(6, 1) As Long
    Call SetJournalHook
    Application.StatusBar = "Recording..."
 
End Sub
 
Private Sub StopRecording()
 
    Call RemoveJournalHook
    Application.StatusBar = False
 
End Sub
 
Private Sub SaveTheEvents()
 
    On Error Resume Next
 
    Kill LogFile
    RmDir LogPath
    MkDir LogPath
    On Error GoTo 0
 
    Call SaveEvents(LogPath & LogFile, MsgArray)
 
End Sub
 
Private Sub SaveEvents _
(ByVal sFilename As String, ByRef MsgArray() As Long)
 
    Dim iFile As Integer
    Dim lIndex As Long
    Dim i As Long
    Dim tTemp() As Msg
 
    iFile = FreeFile
    ReDim tTemp(UBound(MsgArray, 2))
 
    Open sFilename For Random Access Write As iFile
 
    For lIndex = LBound(MsgArray, 2) To UBound(MsgArray, 2)
        tTemp(lIndex).uCode = MsgArray(0, lIndex)
        tTemp(lIndex).message = MsgArray(1, lIndex)
        tTemp(lIndex).hwnd = MsgArray(2, lIndex)
        tTemp(lIndex).paramH = MsgArray(3, lIndex)
        tTemp(lIndex).paramL = MsgArray(4, lIndex)
        tTemp(lIndex).time = MsgArray(5, lIndex)
        Put #iFile, , tTemp(lIndex)
    Next
 
    Close iFile
 
End Sub
 
Private Sub LoadTheEvents()
 
    Call LoadEvents(LogPath & LogFile)
    bDone = False
    Call SetPlaybackHook
    While Not bDone
        DoEvents
    Wend
 
End Sub
 
Private Sub LoadEvents(ByVal sFilename As String)
 
    Dim iFile As Integer
    Dim lIndex As Long
    Dim tTemp() As Msg
 
    If Len(Dir(sFilename)) = 0 Then Exit Sub
    iFile = FreeFile
    Open sFilename For Random Access Read As iFile
    ReDim MsgArray(6, 1) As Long
    While Not EOF(iFile)
        ReDim Preserve tTemp(lIndex)
        Get #iFile, , tTemp(lIndex)
        MsgArray(0, lIndex) = tTemp(lIndex).uCode
        MsgArray(1, lIndex) = tTemp(lIndex).message
        MsgArray(2, lIndex) = tTemp(lIndex).hwnd
        MsgArray(3, lIndex) = tTemp(lIndex).paramH
        MsgArray(4, lIndex) = tTemp(lIndex).paramL
        MsgArray(5, lIndex) = tTemp(lIndex).time
        ReDim Preserve MsgArray(6, (UBound(MsgArray, 2) + 1)) As Long
        lIndex = lIndex + 1
    Wend
    Close iFile
 
End Sub
 
Private Function GetLowByte(ByRef lValue As Long) As Long
 
    GetLowByte = (lValue And &HFF&)
 
End Function
 
Private Function GetAppInstance() As Long
 
    GetAppInstance = GetWindowLong(Application.hwnd, GWL_HINSTANCE)
 
End Function
 
Public Sub SafeExit()
 
    If bIsHooked Or IsPBHooked Then
        Call RemoveJournalHook
        Call RemovePlaybackHook
    End If
 
End Sub


Please be careful when testing/editing the code as it has a potential for crashing XL.

Regards.
 
Last edited:

Excel Facts

Which came first: VisiCalc or Lotus 1-2-3?
Dan Bricklin and Bob Frankston debuted VisiCalc in 1979 as a Visible Calculator. Lotus 1-2-3 debuted in the early 1980's, from Mitch Kapor.
You seem to have a penchant for solutions that crash Excel http://www.mrexcel.com/forum/showthread.php?t=374673

Why don't you just smash your laptop against a wall, cut out the middleman?

Did you try the workbook demo and crashed excel ?

The code makes use of the Win32 API and uses a jourrnal hook. This makes it possible to do things which are impossible otherwise and offers many learning possibilities.On the other hand , not handling errors properly can be potentially dangerous and editing the code is very difficult as the VBA debugger no longer works.

I wrote/tested the code in XL2003 on Win XP and worked quite well. I am not sure about other versions but I would love to know if it works in them too.

Regards.
 
Upvote 0
Did anybody test the workbook demo provided in the link ?

I would like to know if / how it works in other than XL2003 and/or Windows XP in order to see if some more improvements to the code are needed.

In my machine,working on some XL Modal dialogues such as the one for Data Validation doesn't get recorded. This is part of the JOURNALRECORD hook according to the msdn-microsoft. Strangely enough, working on the Format Cell dialogue Does get recorded.

Regards.
 
Upvote 0
Did you try the workbook demo and crashed excel ?

The code makes use of the Win32 API and uses a jourrnal hook. This makes it possible to do things which are impossible otherwise and offers many learning possibilities.On the other hand , not handling errors properly can be potentially dangerous and editing the code is very difficult as the VBA debugger no longer works.

I wrote/tested the code in XL2003 on Win XP and worked quite well. I am not sure about other versions but I would love to know if it works in them too.

Regards.

It was you who said it has the potential to crash Excel. As it is a very un-needed function, why we would we try that with that possible consequence?
 
Upvote 0
Why don't you just smash your laptop against a wall, cut out the middleman?

It was you who said it has the potential to crash Excel. As it is a very un-needed function, why we would we try that with that possible consequence?


Wow! thanks for the courteous and encouraging manner with which you handled the effort I put into this work.

I code in Excel and post here for the sole purpose of learning .I like to experiment with new techniques to expand my knowledge and one must understand that this trial and error based approach, is part of the learning process which comes inevitably with some risk. that's the only reason why I always make a point of warning users if I know that there are any potential risks when implementing my code.

No hard feelings xld. :)

Regards.
 
Last edited:
Upvote 0
TAKE2

Hi all,

I have tested the workbook in my machine on (OS: Prof Win XP Version 2002 - SP2) Excel2003 and it works as expected.

However, on Win XP SP3 , the code freezes and only works if the VBIDE is first STOPPED & RESET before running the code ! This is wierd !!

In order to overcome this problem, I tried inserting a Custom DoEvents within the JournalPlaybackProc procedure that uses the PeekMessage to check for messages in the mssg queue and the DispatchMessage to send the msgs to the target window. Strangely enough, the code doesn't freeze the application anymore BUT only the Mouse movements are played back as if the keyboard input wasn't recorded!

Anyway, here is an update of the test workbook.

http://www.savefile.com/files/2042691

Please, try the updated workbook in the above link. If the application hangs when starting the recorder, restart XL , reopen the Wbook and before starting the Recorder , STOP and RESET the VBIDE first.

Any feedback will be much appreciated as I am trying to make this Recording thing in excel as stable & as reliable as possible . TIA
 
Upvote 0

Forum statistics

Threads
1,214,832
Messages
6,121,843
Members
449,051
Latest member
excelquestion515

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top