Keyboard Hook to be used as shortcut to fire up macro outside excel

mikerek

New Member
Joined
Feb 14, 2017
Messages
11
Hello guys!

Please tell me if it is possible initialize procedure outside inactive Excel. I mean, the file is opened, and some userform is loaded. Is it somehow possible (e.g. hooking keyboard) to assign to procedure/userfrom_button, a windows shortcut (e.g. alt+1)? The macro is being used for printscreening current window (using keybd_event), but I need it improved to use only shortcut instead of clicking userform button.

Can you please advise me on that?
Thank You!
 
Okay, but besides those minor advantages... :-o

Seriously, I did not know of the RegisterHotKey function. I tried it and it is 'the bee's knees'! Seems hugely safer and as you say, easier to code as well.

Thank you Jaafar; that is definitely one to save for future use :bow:

Mark

Mark,

I am glad you found this useful and thank you so much for the positive feedback.
 
Upvote 0

Excel Facts

Move date out one month or year
Use =EDATE(A2,1) for one month later. Use EDATE(A2,12) for one year later.
Jaafar thanks I will try it for a while, but I did it using GetAsyncKeyState API, works perfectly for me:

Code:
#If VBA7 Then
    'declare virtual key event listener
    Private Declare PtrSafe Function GetAsyncKeyState Lib "user32" _
            (ByVal vKey As Long) As Integer
#Else
    'declare virtual key event listener
    Private Declare Function GetAsyncKeyState Lib "user32" _
            (ByVal vKey As Long) As Integer
#End If
 
Private Const VK_0 = &H30
 
'keyboard events
Private Const KEYEVENTF_KEYUP = &H2
Private Const VK_ALT = &H12
Private Const VK_TAB = &H9
Public Const VK_SNAPSHOT = &H2C
 
 
Sub main()

 
Do Until GetAsyncKeyState(VK_0)
    DoEvents
    If GetAsyncKeyState(VK_0) Then
        'if 0 key is pressed
        'Speak the time
        Call SS
    End If
Loop
End Sub
 
Upvote 0
Alternatively, you can use the RegisterHotKey API which is easier to code , less costly on the system and less prone to dangerous crashes than using a system wide low level hook .

Here is an example that shows how to invoke a Macro associated with a userfom by pressing ALT+ 1 regardless of whether the focus is currently on excel, the userform or on another application :

To test this; add a new userform to the project and place this code in its module :
Code:
Private Type POINTAPI
    x As Long
    y As Long
End Type

#If VBA7 Then
    Private Type MSG
        hwnd As LongPtr
        message As Long
        wParam As LongPtr
        lParam As LongPtr
        time As Long
        pt As POINTAPI
    End Type

    Private Declare PtrSafe Function RegisterHotKey Lib "user32" (ByVal hwnd As LongPtr, ByVal id As Long, ByVal fsModifiers As Long, ByVal vk As Long) As Long
    Private Declare PtrSafe Function UnregisterHotKey Lib "user32" (ByVal hwnd As LongPtr, ByVal id As Long) As Long
    Private Declare PtrSafe Function PeekMessage Lib "user32" Alias "PeekMessageA" (lpMsg As MSG, ByVal hwnd As LongPtr, ByVal wMsgFilterMin As Long, ByVal wMsgFilterMax As Long, ByVal wRemoveMsg As Long) As Long
    Private Declare PtrSafe Function WaitMessage Lib "user32" () As Long
#Else
    Private Type MSG
        hwnd As Long
        message As Long
        wParam As Long
        lParam As Long
        time As Long
        pt As POINTAPI
    End Type
    
    Private Declare Function RegisterHotKey Lib "user32" (ByVal hwnd As Long, ByVal id As Long, ByVal fsModifiers As Long, ByVal vk As Long) As Long
    Private Declare Function UnregisterHotKey Lib "user32" (ByVal hwnd As Long, ByVal id As Long) As Long
    Private Declare Function PeekMessage Lib "user32" Alias "PeekMessageA" (lpMsg As MSG, ByVal hwnd As Long, ByVal wMsgFilterMin As Long, ByVal wMsgFilterMax As Long, ByVal wRemoveMsg As Long) As Long
    Private Declare Function WaitMessage Lib "user32" () As Long
#End If

Private Const MOD_ALT = &H1
Private Const NUMPAD1 = &H61
Private Const PM_REMOVE = &H1
Private Const WM_HOTKEY = &H312

Private bCancel As Boolean

Private Sub UserForm_Activate()
    
   [COLOR=#008000] '============================================================
    'run any other pre-existing code here before hooking the keys.
    '============================================================[/COLOR]
    
    [COLOR=#008000]'Hook the Alt + 1 keys combination.[/COLOR]
    bCancel = False
    Call RegisterHotKey(Application.hwnd, &HBFFF&, MOD_ALT, vbKey1)
    Call RegisterHotKey(Application.hwnd, &HBFFF&, MOD_ALT, NUMPAD1)

    Call Key_Listener
End Sub

Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    bCancel = True
End Sub

Private Sub Key_Listener()
    Dim message As MSG
    
    On Error GoTo Oops
    
    Do While Not bCancel
        WaitMessage
            If PeekMessage(message, Application.hwnd, WM_HOTKEY, WM_HOTKEY, PM_REMOVE) Then
                [COLOR=#008000]'=========================================================
                'Comment out this line if you don't wish to activate excel.[/COLOR]
                VBA.AppActivate Application.Caption
               [COLOR=#008000] '=========================================================
                'Execute the 'Test' procedure.[/COLOR]
                Call Test
            End If
        DoEvents
    Loop
    
Oops:
    Call UnregisterHotKey(Application.hwnd, &HBFFF&)
End Sub


Private Sub Test()
    MsgBox "Procedure invoked !", vbApplicationModal
End Sub

Now, once the userform is loaded, try pressing the ALT+ 1 key combination and the 'Test' Macro should execute regardless of the currently active application/Window.


ohhhhh mate it made my day. Way stable than my way. I will try this one. Thank you so much!
 
Upvote 0
One more question guys,
is it possible (using RegisterHotKey) to invoke different procedures using different keystrokes? I mean, can I call procedure x using alt+1 and procedure y using alt+2?
 
Upvote 0
Yes. Using Jaafar's code:

Rich (BB code):
Option Explicit
  
Private Type POINTAPI
    x As Long
    y As Long
End Type
  
#If VBA7 Then
    Private Type MSG
        hWnd As LongPtr
        message As Long
        wParam As LongPtr
        lParam As LongPtr
        time As Long
        pt As POINTAPI
    End Type


    Private Declare PtrSafe Function RegisterHotKey Lib "user32" (ByVal hWnd As LongPtr, ByVal id As Long, ByVal fsModifiers As Long, ByVal vk As Long) As Long
    Private Declare PtrSafe Function UnregisterHotKey Lib "user32" (ByVal hWnd As LongPtr, ByVal id As Long) As Long
    Private Declare PtrSafe Function PeekMessage Lib "user32" Alias "PeekMessageA" (lpMsg As MSG, ByVal hWnd As LongPtr, ByVal wMsgFilterMin As Long, ByVal wMsgFilterMax As Long, ByVal wRemoveMsg As Long) As Long
    Private Declare PtrSafe Function WaitMessage Lib "user32" () As Long
#Else
    Private Type MSG
        hWnd As Long
        message As Long
        wParam As Long
        lParam As Long
        time As Long
        pt As POINTAPI
    End Type
    
    Private Declare Function RegisterHotKey Lib "user32" (ByVal hWnd As Long, ByVal id As Long, ByVal fsModifiers As Long, ByVal vk As Long) As Long
    Private Declare Function UnregisterHotKey Lib "user32" (ByVal hWnd As Long, ByVal id As Long) As Long
    Private Declare Function PeekMessage Lib "user32" Alias "PeekMessageA" (lpMsg As MSG, ByVal hWnd As Long, ByVal wMsgFilterMin As Long, ByVal wMsgFilterMax As Long, ByVal wRemoveMsg As Long) As Long
    Private Declare Function WaitMessage Lib "user32" () As Long
#End If


Private Const MOD_ALT = &H1
Private Const MOD_CONTROL = &H2
Private Const MOD_SHIFT = &H4
Private Const PM_REMOVE = &H1
Private Const WM_HOTKEY = &H312
  
Private bCancel As Boolean
  
Private Sub UserForm_Activate()
    
    '============================================================
    'run any other pre-existing code here before hooking the keys.
    '============================================================
    
    'Hook the Alt + 1 keys combination.
    bCancel = False
    Call RegisterHotKey(Application.hWnd, &HBFFF&, MOD_ALT, vbKey1)
    Call RegisterHotKey(Application.hWnd, &HBFFF&, MOD_ALT, vbKeyNumpad1)
    Call RegisterHotKey(Application.hWnd, &HBFFE&, MOD_ALT, vbKey2)
    Call RegisterHotKey(Application.hWnd, &HBFFE&, MOD_ALT, vbKeyNumpad2)
    Call Key_Listener
End Sub


Private Sub Key_Listener()
    Dim message As MSG
    
    On Error GoTo Oops
    
    Do While Not bCancel
        WaitMessage
            If PeekMessage(message, Application.hWnd, WM_HOTKEY, WM_HOTKEY, PM_REMOVE) Then
                '=========================================================
                'Comment out this line if you don't wish to activate excel.
                'VBA.AppActivate Application.Caption
                '=========================================================
                Select Case message.wParam
                Case &HBFFF&
                  'Execute the 'Test' procedure.
                  Call Test
                Case &HBFFE&
                  Call Test2
                End Select
            End If
        DoEvents
    Loop
    
Oops:
    Call UnregisterHotKey(Application.hWnd, &HBFFF&)
    Call UnregisterHotKey(Application.hWnd, &HBFFE&)
    
End Sub


Sub Test()
  Sheet1.Cells(1).Value = Sheet1.Cells(1).Value + 1
End Sub


Sub Test2()
  Sheet1.Cells(2).Value = Sheet1.Cells(2).Value + 5
End Sub


Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
  bCancel = True
End Sub
 
Last edited:
Upvote 0
Thanks for reply, unfortunately it is running only test() regardless of used keystroke. Could you please explain the difference between &HBFFF& and &HBFFE&?
 
Upvote 0
Thanks for reply, unfortunately it is running only test() regardless of used keystroke. Could you please explain the difference between &HBFFF& and &HBFFE&?

If you modified the code you presumably placed into your project, please copy/paste the example code into a new workbook/new userform's module. I just copied the code from post #15 into a new userform, changed ShowModal to false and ran it. I pressed ALT + 1 and then ALT +2 three times each, and 3 and 15 are now in cells A1 and B1 as expected.

As to your question about the id argument, see [FONT=&quot]An application must specify an id value in the range 0x0000 through 0xBFFF. .

Hope that helps,

Mark

[/FONT]
 
Upvote 0
Mark,
you were right, the code works when I just copied it. Yes, you have helped me a lot.
Thank you so much guys!
 
Upvote 0

Forum statistics

Threads
1,223,214
Messages
6,170,772
Members
452,353
Latest member
strainu

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