SystemWideHook




Option Explicit
Public Enum HookFlags
HFMouseDown = 1
HFMouseUp = 2
HFMouseMove = 4
HFKeyDown = 8
HFKeyUp = 16
End Enum
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 CallNextHookEx Lib "user32" _
(ByVal hHook As Long, ByVal nCode As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function UnhookWindowsHookEx Lib "user32" _
(ByVal hHook As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(lpDest As Any, lpSource As Any, ByVal cBytes As Long)
Private Declare Function GetAsyncKeyState% Lib "user32" (ByVal vKey As Long)
Private Declare Function GetForegroundWindow& Lib "user32" ()
Private Declare Function GetWindowThreadProcessId& Lib "user32" _
(ByVal hwnd As Long, lpdwProcessId As Long)
Private Declare Function GetKeyboardLayout& Lib "user32" (ByVal dwLayout As Long)
Private Declare Function MapVirtualKeyEx Lib "user32" Alias "MapVirtualKeyExA" _
(ByVal uCode As Long, ByVal uMapType As Long, ByVal dwhkl As Long) As Long
Private Declare Function SetWindowPos Lib "user32" _
(ByVal hwnd As Long, ByVal hWndInsertAfter As Long, _
ByVal x As Long, ByVal y As Long, ByVal cx As Long, _
ByVal cy As Long, ByVal wFlags As Long) As Long
Private Const SWP_NOSIZE = &H1
Private Const SWP_NOMOVE = &H2
Private Const SWP_NOREDRAW = &H8
Private Const WM_KEYDOWN = &H100
Private Const WM_KEYUP = &H101
Private Const WM_MOUSEMOVE = &H200
Private Const WM_LBUTTONDOWN = &H201
Private Const WM_LBUTTONUP = &H202
Private Const WM_LBUTTONDBLCLK = &H203
Private Const WM_RBUTTONDOWN = &H204
Private Const WM_RBUTTONUP = &H205
Private Const WM_RBUTTONDBLCLK = &H206
Private Const WM_MBUTTONDOWN = &H207
Private Const WM_MBUTTONUP = &H208
Private Const WM_MBUTTONDBLCLK = &H209
Private Const WM_MOUSEWHEEL = &H20A
Private Const WH_JOURNALRECORD = 0
Type EVENTMSG
wMsg As Long
lParamLow As Long
lParamHigh As Long
' msgTime As Long

' hWndMsg As Long

End Type
Dim EMSG As EVENTMSG
Dim hHook As Long, frmHooked As Form, hFlags As Long
Public Function HookProc(ByVal nCode As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
If nCode < 0 Then
HookProc = CallNextHookEx(hHook, nCode, wParam, lParam)
Exit Function
End If
Dim i%, j%
CopyMemory EMSG, ByVal lParam, Len(EMSG)
Select Case EMSG.wMsg
Case WM_KEYDOWN
If (hFlags And HFKeyDown) = HFKeyDown Then
If GetAsyncKeyState(vbKeyShift) Then j = 1
If GetAsyncKeyState(vbKeyControl) Then j = 2
If GetAsyncKeyState(vbKeyMenu) Then j = 4

If (EMSG.lParamLow And &HFF) > 31 Then
frmHooked.System_KeyDown MapVirtualKeyEx(EMSG.lParamLow And &HFF, 2, _
GetKeyboardLayout(GetWindowThreadProcessId(GetForegroundWindow, 0))), j
Else
frmHooked.System_KeyDown EMSG.lParamLow And &HFF, j
End If
End If
Case WM_KEYUP
If (hFlags And HFKeyUp) = HFKeyUp Then
If GetAsyncKeyState(vbKeyShift) Then j = 1
If GetAsyncKeyState(vbKeyControl) Then j = 2
If GetAsyncKeyState(vbKeyMenu) Then j = 4
If (EMSG.lParamLow And &HFF) > 31 Then
frmHooked.System_KeyUp MapVirtualKeyEx(EMSG.lParamLow And &HFF, 2, _
GetKeyboardLayout(GetWindowThreadProcessId(GetForegroundWindow, 0))), j
Else
frmHooked.System_KeyUp EMSG.lParamLow And &HFF, j
End If
End If
Case WM_MOUSEWHEEL
Debug.Print "MouseWheel"
Case WM_MOUSEMOVE
If (hFlags And HFMouseMove) = HFMouseMove Then
If GetAsyncKeyState(vbKeyLButton) Then i = 1
If GetAsyncKeyState(vbKeyRButton) Then i = 2
If GetAsyncKeyState(vbKeyMButton) Then i = 4
If GetAsyncKeyState(vbKeyShift) Then j = 1
If GetAsyncKeyState(vbKeyControl) Then j = 2
If GetAsyncKeyState(vbKeyMenu) Then j = 4
frmHooked.System_MouseMove i, j, CSng(EMSG.lParamLow), CSng(EMSG.lParamHigh)
End If
Case WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_MBUTTONDOWN
If (hFlags And HFMouseDown) = HFMouseDown Then
If GetAsyncKeyState(vbKeyShift) Then i = 1
If GetAsyncKeyState(vbKeyControl) Then i = 2
If GetAsyncKeyState(vbKeyMenu) Then i = 4
frmHooked.System_MouseDown 2 ^ ((EMSG.wMsg - 513) _
/ 3), i, CSng(EMSG.lParamLow), CSng(EMSG.lParamHigh)
End If
Case WM_LBUTTONUP, WM_RBUTTONUP, WM_MBUTTONUP
If (hFlags And HFMouseUp) = HFMouseUp Then
If GetAsyncKeyState(vbKeyShift) Then i = 1
If GetAsyncKeyState(vbKeyControl) Then i = 2
If GetAsyncKeyState(vbKeyMenu) Then i = 4
frmHooked.System_MouseUp 2 ^ ((EMSG.wMsg - 514) _
/ 3), i, CSng(EMSG.lParamLow), CSng(EMSG.lParamHigh)
End If
End Select
Call CallNextHookEx(hHook, nCode, wParam, lParam)
End Function

Public Sub SetHook(fOwner As Form, flags As HookFlags)
hHook = SetWindowsHookEx(WH_JOURNALRECORD, AddressOf HookProc, 0, 0)
Set frmHooked = fOwner
hFlags = flags
Window_SetAlwaysOnTop frmHooked.hwnd, True
End Sub

Public Sub RemoveHook()
UnhookWindowsHookEx hHook
Window_SetAlwaysOnTop frmHooked.hwnd, False
Set frmHooked = Nothing
End Sub

Private Function Window_SetAlwaysOnTop(hwnd As Long, bAlwaysOnTop As Boolean) As Boolean
Window_SetAlwaysOnTop = SetWindowPos(hwnd, -2 - _
bAlwaysOnTop, 0, 0, 0, 0, SWP_NOREDRAW Or SWP_NOSIZE Or SWP_NOMOVE)
End Function

'---End of bas module code---

'Add two multiline TextBoxes (better with vertical scrollbar)

'and one Label at form

Private Sub Form_Load()
SetHook Me, HFMouseDown + HFMouseUp + HFMouseMove + HFKeyDown + HFKeyUp
Text1 = "Mouse activity log:"
Text2 = "Keyboard activity log:"
End Sub

Public Sub System_KeyDown(KeyCode As Integer, Shift As Integer)
Dim s As String
If KeyCode > 31 Then
s = LCase(Chr$(KeyCode))
Else
s = "ASCII code " & KeyCode
End If
If Shift = vbShiftMask Then s = UCase(s): s = s & " + Shift "
If Shift = vbCtrlMask Then s = s & " + Ctrl "
If Shift = vbAltMask Then s = s & " + Alt "
Text2 = Text2 & vbCrLf & s & " down"
End Sub

Public Sub System_KeyUp(KeyCode As Integer, Shift As Integer)
Dim s As String
If KeyCode > 31 Then
s = LCase(Chr$(KeyCode))
Else
s = "ASCII code " & KeyCode
End If
If Shift = vbShiftMask Then s = UCase(s): s = s & " + Shift "
If Shift = vbCtrlMask Then s = s & " + Ctrl "
If Shift = vbAltMask Then s = s & " + Alt "
Text2 = Text2 & vbCrLf & s & " up"
End Sub

Public Sub System_MouseDown(Button As Integer, Shift As Integer, x As Single, y As Single)
Dim s As String
If Button = vbLeftButton Then s = "Left Button "
If Button = vbRightButton Then s = "Right Button "
If Button = vbMiddleButton Then s = "Middle Button "
If Shift = vbShiftMask Then s = s & "+ Shift "
If Shift = vbCtrlMask Then s = s & "+ Ctrl "
If Shift = vbAltMask Then s = s & "+ Alt "
Text1 = Text1 & vbCrLf & s & "Down at pos (pixels): " & CStr(x) & " , " & CStr(y)
End Sub

Public Sub System_MouseUp(Button As Integer, Shift As Integer, x As Single, y As Single)
Dim s As String
If Button = vbLeftButton Then s = "Left Button "
If Button = vbRightButton Then s = "Right Button "
If Button = vbMiddleButton Then s = "Middle Button "
If Shift = vbShiftMask Then s = s & "+ Shift "
If Shift = vbCtrlMask Then s = s & "+ Ctrl "
If Shift = vbAltMask Then s = s & "+ Alt "
Text1 = Text1 & vbCrLf & s & "Up at pos (pixels): " & CStr(x) & " , " & CStr(y)
End Sub

Public Sub System_MouseMove(Button As Integer, Shift As Integer, x As Single, y As Single)
Dim s As String
If Button = vbLeftButton Then s = "Left Button "
If Button = vbRightButton Then s = "Right Button "
If Button = vbMiddleButton Then s = "Middle Button "
If Shift = vbShiftMask Then s = s & "+ Shift "
If Shift = vbCtrlMask Then s = s & "+ Ctrl "
If Shift = vbAltMask Then s = s & "+ Alt "
Label1 = "Mouse info" & vbCrLf & "X = " & x & " Y= " & y & vbCrLf
If s <> "" Then Label1 = Label1 & "Extra Info: " & vbCrLf & s & "pressed"
End Sub

Private Sub Form_Unload(Cancel As Integer)
RemoveHook
End Sub

Inputs:
Form to receive hook notification and hook flags
Returns:
Mouse/keyboard events with appropriate parameters

Assumes:
Though MSDN says that WH_JOURNALRECORD hook is
thread defined, in w95/98 it allow system-wide
hook when set ThreadID parameter of hook = 0.
To run this code you need form with two multiline
textboxes (Text1 and Text2) and one label (Label1).

Side Effects:
Works only with w95/98. Don't work with NT/2000.
This code use hook, don't stop sample from IDE,
use Form [x] button.












( systemwidehook.html )- by Paolo Puglisi - Modifica del 17/12/2023