Option Explicit
'\\Class that simulates a mouse move event
'\\for worksheet cells.
'\\in order to minimise the timer effect on
'\\performance,the class opens a new excel
'\\instance dinamically and runs the timer code from it.
'\\a callback like procedure is also used
'\\to run any custome routine designed by the user.
'\\this callback signature simulates that of other known MS events
'\\xtra care must be taken when editing the callback routine
'\\any mistakes will potentially crash the app !
'\\error handling is therefore vital
'\\Note: this code uses the VBE so it requires that the Macro
'\\Security "Trusted Sources" be enabled.
Private sCode As String
Private oNewXLapp As Excel.Application
Private oNewWbk As Workbook
Private Const vbext_ct_StdModule As Long = 1
Private sSheetName As String
Private sCallBackProc As String
Private bMouseEventEnabled As Boolean
Private sMsg As String
Public WithEvents WbEvent As Workbook
Public Sub Execute()
'\\do not open more than one XL instance
'\\or you will end up with numerous conflicting timers !
If Not Me.IsMouseEventEnabled Then
'\\assign this workbook to WbEvent prop
'\\to close the the new XL instance if the user closes
'\\the workbook before terminating the Class !
Set Me.WbEvent = ThisWorkbook
'\\set this boolean property\flag
Me.IsMouseEventEnabled = True
'\\store the timer code in a string
sCode = "Declare Function SetTimer Lib ""user32"""
sCode = sCode & "(ByVal hwnd As Long, ByVal nIDEvent As Long,"
sCode = sCode & "ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long" & vbCrLf
sCode = sCode & "" & vbCrLf
sCode = sCode & "Declare Function KillTimer Lib ""user32"""
sCode = sCode & "(ByVal hwnd As Long, ByVal nIDEvent As Long) As Long" & vbCrLf
sCode = sCode & "" & vbCrLf
sCode = sCode & "Declare Function GetTickCount Lib ""kernel32"""
sCode = sCode & "Alias ""GetTickCount"" () As Long" & vbCrLf
sCode = sCode & "" & vbCrLf
sCode = sCode & "Declare Function GetCursorPos Lib ""user32"""
sCode = sCode & "(lpPoint As POINTAPI) As Long" & vbCrLf
sCode = sCode & "" & vbCrLf
sCode = sCode & "Type POINTAPI" & vbCrLf
sCode = sCode & " x as Long" & vbCrLf
sCode = sCode & " y as Long" & vbCrLf
sCode = sCode & "End Type" & vbCrLf
sCode = sCode & "" & vbCrLf
sCode = sCode & "Dim lCurPos As POINTAPI" & vbCrLf
sCode = sCode & "Dim bTimerOn As Boolean" & vbCrLf
sCode = sCode & "Dim lTimerId As Long" & vbCrLf
sCode = sCode & "Dim lHwnd As Long" & vbCrLf
sCode = sCode & "Dim oNewRange As Range" & vbCrLf
sCode = sCode & "Dim oWB As WorkBook" & vbCrLf
sCode = sCode & "" & vbCrLf
sCode = sCode & "Sub StartTimer()" & vbCrLf
sCode = sCode & " Set oWB = GetObject(" & Chr(34)
sCode = sCode & ThisWorkbook.FullName & Chr(34) & ")" & vbCrLf
sCode = sCode & " If Not bTimerOn Then" & vbCrLf
sCode = sCode & " lTimerId = SetTimer"
sCode = sCode & "(0, 0 , 10, AddressOf TimerProc)" & vbCrLf
sCode = sCode & " bTimerOn = True" & vbCrLf
sCode = sCode & " End If" & vbCrLf
sCode = sCode & "End Sub" & vbCrLf
sCode = sCode & "" & vbCrLf
sCode = sCode & "Sub TimerProc()" & vbCrLf
sCode = sCode & " On Error Resume Next" & vbCrLf
sCode = sCode & " GetCursorPos lCurPos" & vbCrLf
sCode = sCode & " Set oNewRange = oWb.Parent.ActiveWindow.RangeFromPoint"
sCode = sCode & "(lCurPos.x, lCurPos.Y)" & vbCrLf
'\\run procedure on one worksheet only
sCode = sCode & "If oWb.ActiveSheet.Name = " & Chr(34)
sCode = sCode & sSheetName & Chr(34) & " Then" & vbCrLf
'\\ensure mouse is pointing to a cell to avoid an error in callback
sCode = sCode & "If TypeName(oNewRange)=""Range"" Then " & vbCrLf
'\\run the callback from here !!
sCode = sCode & "oWb.Parent.Run oWb.Name & " & Chr(34) & "!"
sCode = sCode & sCallBackProc & Chr(34)
sCode = sCode & ",oNewRange, lCurPos.x, lCurPos.Y" & vbCrLf
sCode = sCode & " End If" & vbCrLf
sCode = sCode & " End If" & vbCrLf
sCode = sCode & "End Sub" & vbCrLf
sCode = sCode & "" & vbCrLf
'\\without this, the timer would not stop !
sCode = sCode & "Sub StopTimer()" & vbCrLf
sCode = sCode & " If bTimerOn Then" & vbCrLf
sCode = sCode & " KillTimer 0, lTimerId" & vbCrLf
sCode = sCode & " bTimerOn = False" & vbCrLf
sCode = sCode & " End If" & vbCrLf
sCode = sCode & "End Sub" & vbCrLf
'\\now, open a new invisible XL app and place the
'\\the contents of the string into a new module
'\\ideally,this would have been done via a VB script
'\\but VBS do not support API declarations
'\\
Set oNewXLapp = CreateObject("Excel.Application")
Set oNewWbk = oNewXLapp.Workbooks.Add
'\handle error if access to the VBE is NOT trusted
On Error Resume Next
oNewWbk.VBProject.VBComponents.Add _
(vbext_ct_StdModule).CodeModule.AddFromString sCode
If InStr(1, Err.Description, "not trusted", vbTextCompare) <> 0 Then
sMsg = "To use this 'MouseMoveEvent Class' "
sMsg = sMsg & "you must tick " & vbCrLf
sMsg = sMsg & "the 'Trust Access to Visual Basic Project' CheckBox " & vbCrLf
sMsg = sMsg & "via Tools\Macro\Security\Trusted Sources TAB, " & vbCrLf
sMsg = sMsg & "close Excel and reopen it again to take effect."
MsgBox Err.Description & vbCrLf _
& vbCrLf & sMsg, vbExclamation
With oNewXLapp
.DisplayAlerts = False
.Quit
End With
End
Else
'\\run the code to start the timer from the newly created wbk
oNewXLapp.Run oNewWbk.Name & "!StartTimer"
End If
End If
End Sub
Public Sub Disable()
On Error Resume Next
If Me.IsMouseEventEnabled Then
Me.IsMouseEventEnabled = False
'\\here,we run the StopTimer routine located
'\\ in the invisible XL instance
oNewXLapp.Run oNewWbk.Name & "!StopTimer"
'\\cleanup
With oNewXLapp
.DisplayAlerts = False
.Quit
End With
Set oNewXLapp = Nothing
Set oNewWbk = Nothing
'\\run the callback one more final time to ensure that all the old
'\\cell settings are restored in case the callback had chnged them
Application.Run ThisWorkbook.Name & "!" & sCallBackProc, Nothing, 0, 0
End If
End Sub
Public Property Get WorkSheetName() As String
WorkSheetName = sSheetName
End Property
Public Property Let WorkSheetName(ByVal vNewValue As String)
sSheetName = vNewValue
End Property
Public Property Get CallBackProcedure() As String
CallBackProcedure = sCallBackProc
End Property
Public Property Let CallBackProcedure(ByVal vNewValue As String)
sCallBackProc = vNewValue
End Property
Public Property Get IsMouseEventEnabled() As Boolean
IsMouseEventEnabled = bMouseEventEnabled
End Property
Public Property Let IsMouseEventEnabled(ByVal vNewValue As Boolean)
bMouseEventEnabled = vNewValue
End Property
Private Sub Class_Terminate()
Me.Disable
End Sub
Private Sub WbEvent_BeforeClose(Cancel As Boolean)
Me.Disable
End Sub