A stable custom KeyPres event for worksheet cells

Jaafar Tribak

Well-known Member
Joined
Dec 5, 2002
Messages
9,779
Office Version
  1. 2016
Platform
  1. Windows
http://www.box.net/shared/ukx1l761taWorkbook demo

Hi all.

Ever wanted to have a keyPress event for worksheet cells just like those provided by ActiveX textbox controls so you can detect the keys as they are being typed in and act accordingly ?

Workarounds i've used so far involved using a keyboard hook, timers or continuos loops all of which tie up and slow down the application and can potentially crash the application if the project is reset while the code is running.

I've written this small dll whose bytes are stored within the workbook in a hidden worksheet. There is no need to register the dll (its all done on the fly) and more importantly the application seems to remain stable and no crashing is feared even if the code errors out or the VBE is reset !

The only requirement is that the Event procedure(OnKeyPress) MUST be located in the workbook module and MUST be Public to be seen by the dll code.

Code in the workbook module :

Code:
Option Explicit
 
Private Const DCOM_DLL_PATH_NAME As String _
= "C:\WINDOWS\system32\DirectCOM.dll"
 
Private Const JAAFAR_DLL_PATH_NAME As String _
= "C:\KeyPressWatcher.dll"

'CreateObject-Replacement (FileBased)
Private Declare Function GETINSTANCE Lib "DirectCom" _
(FName As String, ClassName As String) As Object

Private Declare Function UNLOADCOMDLL Lib "DirectCom" _
(FName As String, ClassName As String) As Long

Private oKeyPressInstance As Object


'=================================================================
'KeyPress custom event.
'Event Procedure Must be PUBLIC !!! and located in the workbook module.
'Use the ByRef Cancel argument to prevent the drop operation.

'Allow only Alpha characters in cell A1 of Sheets("test")
Public Sub OnKeyPress _
(ByVal Target As Range, ByVal KeyCode As Long, ByRef Cancel As Boolean)
    
    If ActiveSheet Is Sheets("test") Then
        If Target.Address = Range("a1").Address Then
            If IsNumeric(Chr(KeyCode)) Then
                MsgBox "No numeric characters are allowed in the range : " & _
                vbNewLine & Target.Address
                Cancel = True
            End If
        End If
    End If
    
End Sub
'=====================================================================


Private Sub Workbook_Open()

        'Create the DirectCom & KeyPressWatcher dlls.
        Call CreateDlls

        'load an instance of the 'KeyPressWatcher.dll' Class.
        Set oKeyPressInstance = _
        GETINSTANCE(JAAFAR_DLL_PATH_NAME, "KeyPressClass")

        If Not oKeyPressInstance Is Nothing Then
            'start watching user key strokes.
            Call oKeyPressInstance.Start(ThisWorkbook)
        Else
            MsgBox "Unable to load the " & _
            "'KeyPressWatcher' dll.", vbInformation
        End If

End Sub

Private Sub Workbook_BeforeClose(Cancel As Boolean)

    
    If Not oKeyPressInstance Is Nothing Then
        oKeyPressInstance.Finish
        Set oKeyPressInstance = Nothing
    End If

    UNLOADCOMDLL JAAFAR_DLL_PATH_NAME, "KeyPressClass"

    On Error Resume Next
    
    If Len(Dir(JAAFAR_DLL_PATH_NAME)) <> 0 Then
        Kill JAAFAR_DLL_PATH_NAME
    End If

End Sub



'Create the 'DirectCom' & 'KeyPressWatcher' dll from the
'Bytes stored in the '"DllBytes" hidden worksheet.
Private Sub CreateDlls()
 
    Dim Bytes() As Byte
    Dim lFileNum As Integer
    Dim aVar
    Dim x As Long
 
    On Error Resume Next
    
    If Len(Dir(JAAFAR_DLL_PATH_NAME)) = 0 Then
    
       With Worksheets("DllBytes")
           aVar = .Range(.Cells(1, 1), .Cells(.Rows.Count, 1).End(xlUp)).Value
       End With
     
       ReDim Bytes(LBound(aVar) To UBound(aVar))
       For x = LBound(aVar) To UBound(aVar)
           Bytes(x) = CByte(aVar(x, 1))
       Next
    
       lFileNum = FreeFile
       Open JAAFAR_DLL_PATH_NAME For Binary As #lFileNum
           Put #lFileNum, 1, Bytes
       Close lFileNum
    
    End If
    

    If Len(Dir(DCOM_DLL_PATH_NAME)) = 0 Then
    
        Erase Bytes
        
        With Worksheets("dllBytes")
            aVar = .Range(.Cells(1, 2), .Cells(.Rows.Count, 2).End(xlUp)).Value
        End With
        
        ReDim Bytes(LBound(aVar) To UBound(aVar))
        For x = LBound(aVar) To UBound(aVar)
            Bytes(x) = CByte(aVar(x, 1))
        Next
        lFileNum = FreeFile
        Open DCOM_DLL_PATH_NAME For Binary As #lFileNum
            Put #lFileNum, 1, Bytes
        Close lFileNum
        
    End If
    

End Sub
Tested on Excel 2007 Win 7 only. Any feedback is much appreciated.
 
Last edited:

Excel Facts

What is the fastest way to copy a formula?
If A2:A50000 contain data. Enter a formula in B2. Select B2. Double-click the Fill Handle and Excel will shoot the formula down to B50000.
Jaafar

You probably know this already, but you can paste into the cell.

Probably not really relevant either, just thought it might be of some interest.

The only other thing I noticed from my extensive 2 min test was I couldn't seem to enter anything in the cell at first.

After some time it seemed to work.
 
Upvote 0
Jaafar

You probably know this already, but you can paste into the cell.

Probably not really relevant either, just thought it might be of some interest.

The only other thing I noticed from my extensive 2 min test was I couldn't seem to enter anything in the cell at first.

After some time it seemed to work.

Norie,

Thanks for taking the time.

I know the user can still copy and paste into the cell aa well as use the AutoFill but I was thinking if this proved stable enough, I would go ahead and try to incorporate an OnPaste & OnAutoFill custom events in the same dll using a similar approach.

As for the initial delay, It is most likely due to the small time required to load the dll but this problem subsides afterwards.
 
Upvote 0
Jaafar

I think I knew the copy/paste wasn't really a problem.:)
 
Upvote 0
Hi @Jaafar Tribak
Although the post is too old, but I know you are still active in the forum. I know you are an active and creative programmer. So I want to share this even though it is late.

------------------------------------------------------------------

I have a suggestion for you about getting the value of the Excel cell being entered from the keyboard.

First, you need to use the SetWinEventHook API to create a stream to listen to window action events. Below are the Enumrates and declarations of this API.

EVENT_OBJECT_FOCUS - this event indicates that an input object window has just been accessed.
EVENT_SYSTEM_CAPTURESTART - this event indicates a certain action.

When creating the event, I temporarily used SaveSetting to save the idHookEvent, to make it easier to get the value to end the events.

When events are called, use the timer API to redirect out of the event stream, calling a function address to avoid conflicts. I see you used Swap Address to a FakeProc to avoid crashing the process.

VBA Code:
Const projectName = "EditFormulaPrefix"

#If VBA7 = 0 Then
  Public Enum LongLong:[_]:End Enum
  Public Enum LongPtr:[_]:End Enum
#End If
Public Const PtrNull As LongPtr = 0

Public Enum WinEventObjectId
  OBJID_SELF = 0
  OBJID_SYSMENU = -1
  OBJID_TITLEBAR = -2
  OBJID_MENU = -3
  OBJID_CLIENT = -4
  OBJID_VSCROLL = -5
  OBJID_HSCROLL = -6
  OBJID_SIZEGRIP = -7
  OBJID_CARET = -8
  OBJID_CURSOR = -9
  OBJID_ALERT = -10
  OBJID_SOUND = -11
  OBJID_QUERYCLASSNAMEIDX = -12
  OBJID_NATIVEOM = -16
End Enum
Enum SetWinEventHookFlags
  WINEVENT_OUTOFCONTEXT = 0
  WINEVENT_SKIPOWNTHREAD = 1
  WINEVENT_SKIPOWNPROCESS = 2
  WINEVENT_INCONTEXT = 4
End Enum

'https://learn.microsoft.com/en-us/windows/win32/winauto/event-constants
Public Enum winEvent: uint
  EVENT_MIN = &H1
  EVENT_OBJECT_CREATE = &H8000  ' hwnd ID idChild is created item
  EVENT_OBJECT_DESTROY = &H8001  ' hwnd ID idChild is destroyed item
  EVENT_OBJECT_SHOW = &H8002  ' hwnd ID idChild is shown item
  EVENT_OBJECT_HIDE = &H8003  ' hwnd ID idChild is hidden item
  EVENT_OBJECT_REORDER = &H8004  ' hwnd ID idChild is parent of zordering children
  EVENT_OBJECT_FOCUS = &H8005  ' hwnd ID idChild is focused item
  EVENT_OBJECT_SELECTION = &H8006  ' hwnd ID idChild is selected item (if only one), or idChild is OBJID_WINDOW if complex
  EVENT_OBJECT_SELECTIONADD = &H8007  ' hwnd ID idChild is item added
  EVENT_OBJECT_SELECTIONREMOVE = &H8008  ' hwnd ID idChild is item removed
  EVENT_OBJECT_SELECTIONWITHIN = &H8009  ' hwnd ID idChild is parent of changed selected items
  EVENT_OBJECT_STATECHANGE = &H800A  ' hwnd ID idChild is item w/ state change
  EVENT_OBJECT_LOCATIONCHANGE = &H800B  ' hwnd ID idChild is moved/sized item
  EVENT_OBJECT_NAMECHANGE = &H800C  ' hwnd ID idChild is item w/ name change
  EVENT_OBJECT_DESCRIPTIONCHANGE = &H800D  ' hwnd ID idChild is item w/ desc change
  EVENT_OBJECT_VALUECHANGE = &H800E  ' hwnd ID idChild is item w/ value change
  EVENT_OBJECT_PARENTCHANGE = &H800F  ' hwnd ID idChild is item w/ new parent
  EVENT_OBJECT_HELPCHANGE = &H8010  ' hwnd ID idChild is item w/ help change
  EVENT_OBJECT_DEFACTIONCHANGE = &H8011  ' hwnd ID idChild is item w/ def action change
  EVENT_OBJECT_ACCELERATORCHANGE = &H8012  ' hwnd ID idChild is item w/ keybd accel change
  EVENT_OBJECT_INVOKED = &H8013  ' hwnd ID idChild is item invoked
  EVENT_OBJECT_TEXTSELECTIONCHANGED = &H8014  ' hwnd ID idChild is item w? test selection change
  EVENT_OBJECT_CONTENTSCROLLED = &H8015
  EVENT_SYSTEM_FOREGROUND = &H3&
  EVENT_SYSTEM_MENUSTART = &H4
  EVENT_SYSTEM_MENUEND = &H5
  EVENT_SYSTEM_MENUPOPUPSTART = &H6
  EVENT_SYSTEM_MENUPOPUPEND = &H7
  EVENT_SYSTEM_CAPTURESTART = &H8     ' hwnd ID idChild is item w/ mouse capture
  EVENT_SYSTEM_ARRANGMENTPREVIEW = &H8016
  EVENT_SYSTEM_MOVESIZESTART = &HA
  EVENT_SYSTEM_MOVESIZEEND = &HB  'The movement or resizing of a window has finished. This event is sent by the system, never by servers.
  EVENT_SYSTEM_SWITCHSTART = &H14
  EVENT_SYSTEM_SWITCHEND = &H15
  EVENT_SYSTEM_MINIMIZESTART = &H16 ' A window object is about to be minimized.
  EVENT_SYSTEM_MINIMIZEEND = &H17 ' A window object is about to be restored.
  EVENT_SYSTEM_END = &HFF
  EVENT_OBJECT_END = &H80FF
  EVENT_AIA_START = &HA000
  EVENT_AIA_END = &HAFFF
End Enum
#If VBA7 Then
Private Declare PtrSafe Function SetWinEventHook Lib "user32.dll" (ByVal EventMin As LongPtr, ByVal EventMax As LongPtr, ByVal hmodWinEventProc As LongPtr, ByVal lpfnWinEventProc As LongPtr, ByVal idProcess As Long, ByVal idThread As Long, ByVal dwFlags As LongPtr) As LongPtr
Public Declare PtrSafe Function UnhookWinEvent Lib "user32.dll" (ByVal hWinEventHook As LongPtr) As Long
#Else
Private Declare Function SetWinEventHook Lib "user32.dll" (ByVal EventMin As Long, ByVal EventMax As Long, ByVal hmodWinEventProc As Long, ByVal lpfnWinEventProc As Long, ByVal idProcess As Long, ByVal idThread As Long, ByVal dwFlags As Long) As Long
Public Declare Function UnhookWinEvent Lib "user32.dll" (ByVal hWinEventHook As Long) As Long
#End If

#If -VBA7 And -Win64 Then
  Private Declare PtrSafe Function setTimer Lib "user32" Alias "SetTimer" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As LongPtr, ByVal lpTimerFunc As LongPtr) As LongPtr
  Private Declare PtrSafe Function KillTimer Lib "user32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr) As LongPtr
#ElseIf VBA7 Then
  Private Declare PtrSafe Function setTimer Lib "user32" Alias "SetTimer" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As LongPtr, ByVal lpTimerFunc As LongPtr) As Long
  Private Declare PtrSafe Function KillTimer Lib "user32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr) As Long
#Else
  Private Declare Function SetTimer Lib "user32" (ByVal HWnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
  Private Declare Function KillTimer Lib "user32" (ByVal HWnd As Long, ByVal nIDEvent As Long) As Long
#End If

Sub startWinEventForm()
  On Error Resume Next
  Dim h As LongPtr, mi As LongPtr, ma As LongPtr
  mi = EVENT_OBJECT_FOCUS : ma = EVENT_OBJECT_FOCUS : GoSub a
  mi = EVENT_SYSTEM_CAPTURESTART: ma = EVENT_SYSTEM_CAPTURESTART: GoSub a
  mi = EVENT_SYSTEM_FOREGROUND: ma = EVENT_SYSTEM_FOREGROUND: GoSub a
Exit Sub
a:
  h = SetWinEventHook(mi, ma, PtrNull, AddressOf StaticWinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT Or WINEVENT_SKIPOWNPROCESS)
  If h Then AddHookEvent h ' Saved to Collection Or SaveSettings 
Return
End Sub

Public Sub StaticWinEventProc(ByVal hWinEventHook As LongPtr, ByVal dwEvent As LongPtr, ByVal hwnd As LongPtr, ByVal idObject As Long, _
                              ByVal idChild As Long, ByVal dwEventThread As LongPtr, ByVal dwmsEventTime As LongPtr)
  If Not WinEventEnabled Then Exit Sub
  On Error GoTo k
  If idObject = OBJID_CURSOR And hWinEventHook = EVENT_OBJECT_LOCATIONCHANGE Then
    'OnCursorLocationChanged
    Exit Sub
  End If
  If (idObject <> 0 Or idChild <> 0) Then Exit Sub
  Dim h As LongPtr:
  Select Case dwEvent
  Case EVENT_OBJECT_FOCUS: setTimer hwnd, hwnd, 10, AddressOf StaticTimerProc
  Case EVENT_SYSTEM_CAPTURESTART: setTimer hwnd, hwnd, 10, AddressOf StaticTimerProc
  'Case EVENT_SYSTEM_FOREGROUND: setTimer hwnd, hwnd, 10, AddressOf StaticTimerProc
  Case Else:
  End Select
End Sub

Private Sub StaticTimerProc(ByVal hwnd As LongPtr, ByVal uMsg As LongPtr, ByVal idevent As LongPtr, ByVal dwTime As LongPtr)
  On Error Resume Next
  KillTimer hwnd, idevent
  EditFormulaPrefix
End Sub

Private Function AddHookEvent(ByVal hHook As LongPtr)
  On Error Resume Next
  Dim s$: s = CStr(hHook)
  SaveSetting projectName, vvHookEvent, s, s
  Err.Clear
End Function
Private Function getHookEvent(ByVal hHook$) As LongPtr
  On Error Resume Next
  getHookEvent = GetSetting(projectName, vvHookEvent, hHook, 0)
  Err.Clear
End Function

Private Function killHookWinEvent(ByVal hHook As LongPtr)
  On Error Resume Next
  Dim h As LongPtr, s$: s = CStr(hHook)
  If hHook <> 0 Then UnhookWinEvent hHook
  h = getHookEvent(s): If h <> 0 Then DeleteSetting projectName, vvHookEvent, s
  Err.Clear
End Function

Private Sub killEventHooks()
  On Error Resume Next
  Dim vHook As Variant, h As LongPtr, a, i%
  a = GetAllSettings(projectName, vvHookEvent)
  If IsArray(a) Then
    For i = 0 To UBound(a)
      h = a(i, 0): UnhookWinEvent h
    Next i
    DeleteSetting projectName, vvHookEvent
  End If
  Err.Clear
End Sub

Step two, use the LPenHelper API with the Structure Type fmlaInfo, and enumrates xlCallEditMode. To get the cell value being entered, modified from the keyboard.

VBA Code:
Private Type fmlaInfo
  wPointMode As Long    '' current edit mode.  0 => rest of struct undefined
  cch As Long           '' count of characters in formula
  lpch As LongPtr       '' pointer to formula characters.  READ ONLY!!!
  ichFirst  As Long     '' char offset to start of selection
  ichLast  As Long      '' char offset to end of selection (may be > cch)
  ichCaret  As Long     '' char offset to blinking caret
End Type

Private Enum xlCallEditMode
  XCEM_Undefined = -1
  XCEM_Ready = 0
  XCEM_Enter = 1
  XCEM_Edit = 2
  XCEM_Point = 4
End Enum
#If VBA7 Then
Private Declare PtrSafe Function LPenHelper Lib "XLCALL32.DLL" (ByVal wCode As LongPtr, fmlaInfo As fmlaInfo) As Long
Private Declare PtrSafe Function lstrlenW Lib "kernel32.dll" (ByVal lpString As LongPtr) As Long
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As LongPtr)
#Else
Private Declare Function LPenHelper Lib "XLCALL32.DLL" (ByVal wCode As Long, fmlaInfo As fmlaInfo) As Long
Private Declare Function lstrlenW Lib "kernel32.dll" (ByVal lpString As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
#End If
Sub EditFormulaPrefix()
      Static fx$
      Dim fmla As  fmlaInfo, l&, b() As Byte, s$
      If LPenHelper(&H400E, fmla) <> 0 Then GoTo e1
      Select Case fmla.wPointMode
      Case XCEM_Enter, XCEM_Edit: l = lstrlenW(fmla.lpch) * 2: If l = 0 Then GoTo e1
      Case Else: GoTo e1
      End Select
      ReDim b(0 To CLng(l - 1)): Call CopyMemory(ByVal VarPtr(b(0)), ByVal fmla.lpch, l)
      s = b
      If Fx <> vbNullString And s = Fx Then Goto e1
      Debug.Print "value: "; s
      fx = s
e1:
End Sub

You need to UnHook the IDHooks properly. To avoid Crashing the process.

(Please bear with me because I am Vietnamese, I used Google translate to translate)
 
Upvote 0
Hi Wazzateo

Sorry for not getting back to you sooner. I took some time off.

Using XLCALL32.DLL :: LPenHelper will give you a pointer to the entire text currently displayed in the formula bar, so it is not exactly the same as detecting keyboard characters as they are being pressed.

Retrieving the last entry from the byte array could yield the keyboard character being pressed but won't work with non printable characters unless you cache the ascii code.

Also, since you are already using a Windows timer, there is really no need to install the Accessibility hook (SetWinEventHook). I would just use the timer procedure for detecting the key presses.

Something along these lines will do :
VBA Code:
Sub StartTimer()
    SetTimer Application.hwnd, 0, 0, AddressOf EditFormulaPrefix
End Sub
Sub StopTimer()
    KillTimer Application.hwnd, 0
End Sub

Sub EditFormulaPrefix()
 
    Dim fmla As fmlaInfo, L As Long, b() As Byte, s As String
  
    Call StopTimer
  
    If LPenHelper(&H400E, fmla) <> 0 Then GoTo e1
    Select Case fmla.wPointMode
      Case XCEM_Enter, XCEM_Edit: L = lstrlenW(fmla.lpch) * 2: If L = 0 Then GoTo e1
      Case Else: GoTo e1
    End Select
  
    Static lCharCount As Long
    Static sRngAddr As String
    Dim ss As String
  
    ReDim b(0 To CLng(L - 1)): Call CopyMemory(b(0), ByVal fmla.lpch, L)
    s = Chr(b(UBound(b) - 1&))
    If lCharCount = fmla.cch And (sRngAddr = ActiveWindow.RangeSelection.Address) Then GoTo e1
    lCharCount = fmla.cch: sRngAddr = ActiveWindow.RangeSelection.Address
    Debug.Print "Character Pressed: "; s
e1:
    Call StartTimer

End Sub


I guess the proper way to handle keyborad strokes is by intalling a WH_KEYBOARD_LL hook. This hook will fire before the key stroke reaches the application and it will detect all keys including the ESC and DEL keys. However, this kind of hook is not safe in vba. Better to have this hook running in a dll or in a seperate Excel instance.
 
Upvote 0
Jaafar,

If the WH_KEYBOARD_LL event is set from another Excel instance, VBA also does not have enough time to finish calculating a task before a new message is sent.

Creating a Dll is not feasible with certain computers (computers used at the company, ...).

But with LPenHelper, From a separate Excel instance is it possible to call XLCALL32.DLL::LPenHelper at the main Excel instance, Jaafar?
 
Upvote 0
But with LPenHelper, From a separate Excel instance is it possible to call XLCALL32.DLL::LPenHelper at the main Excel instance, Jaafar?
I don't see any need to run XLCALL32.DLL::LPenHelper from a separate excel instance. LPenHelper works fine from within the single main calling instance.

The reason I said WH_KEYBOARD_LL is the proper approach is because it traps keystrokes before reaching the target application so you can abort them if required.

Does the following tester code work for you? And also, can you tell me if you are running Excel x32 bit or x64 bit?

In a Standard Module:
VBA Code:
Option Explicit

#If Win64 Then
    Const NULL_PTR = 0^
#Else
    Const NULL_PTR = 0&
#End If

Type KBDLLHOOKSTRUCT
    vkCode As Long
    scanCode As Long
    flags As Long
    time As Long
    dwExtraInfo As LongPtr
End Type

Declare PtrSafe Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As LongPtr, ByVal hmod As LongPtr, ByVal dwThreadId As Long) As LongPtr
Declare PtrSafe Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As LongPtr) As Long
Declare PtrSafe Function CallNextHookEx Lib "user32" (ByVal hHook As LongPtr, ByVal ncode As Long, ByVal wParam As LongPtr, lParam As Any) As LongPtr
Declare PtrSafe Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleW" (ByVal lpModuleName As LongPtr) As LongPtr
Declare PtrSafe Function GetCurrentThreadId Lib "kernel32" () As Long

Dim KeyboardHandle As LongPtr


Sub HookKeyboard()

    Const WH_KEYBOARD_LL = 13&
 
    If KeyboardHandle = NULL_PTR Then
        KeyboardHandle = SetWindowsHookEx(WH_KEYBOARD_LL, AddressOf KeyboardCallback, NULL_PTR, 0&)
        Debug.Print "hHook = " & KeyboardHandle & _
                    vbCrLf & "Err.LastDllError = " & Err.LastDllError ' <== Success --Valid hook handle
    End If
 
End Sub

Sub UnhookKeyboard()
 
    If KeyboardHandle <> NULL_PTR Then
        UnhookWindowsHookEx KeyboardHandle
        KeyboardHandle = NULL_PTR
    End If
 
End Sub


Private Function KeyboardCallback(ByVal Code As Long, ByVal wParam As LongPtr, _
        ByVal lParam As LongPtr) As LongPtr   'KBDLLHOOKSTRUCT) As LongPtr
 
    Const HC_ACTION = 0&
    If Code = HC_ACTION Then
        Debug.Print "Keyboard Messages detected !!!", Now
    End If
 
    KeyboardCallback = CallNextHookEx(ByVal KeyboardHandle, ByVal Code, ByVal wParam, ByVal lParam)
 
End Function
 
Upvote 0
Jaafar,

I use Excel x64. I understand how WH_KEYBOARD_LL works globally, to listen for input events from the physical keyboard. The listening event is not only Excel specific. I have tried to initialize another Excel session with WH_KEYBOARD_LL to catch the event at the main thread, but no good result. Maybe, I should try again.
 
Upvote 0

Forum statistics

Threads
1,223,243
Messages
6,170,964
Members
452,371
Latest member
Frana

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