Hi,
How can I block all possible combinations with Alt key except Alt+Tab combination?
I can simply block Alt key when it is pressed, but then there is no way to allow Alt+Tab.
Printable View
Hi,
How can I block all possible combinations with Alt key except Alt+Tab combination?
I can simply block Alt key when it is pressed, but then there is no way to allow Alt+Tab.
Why would you need something like this? Do you want to block it in your app or system wide?
Here is an example of using the low level keyboard hook. In the example, you can see where the ALT key is being tested. Though it is being tested with other keys to block that combination, just reverse it, if the key combo is Alt+Tab then let it thru else don't.
Oops, wrong link posted, this is the right one: http://vbnet.mvps.org/code/hooks/low...yboardproc.htm
Ok, that’s my code. It is working, but is it correct? I mean, have I left no mistakes here?
I have found this on msdn:Code:Function LowLevelKeyboardProc(ByVal nCode As Long, ByVal wParam As Long, lParam As KBDLLHOOKSTRUCT) As Long
If nCode = HC_ACTION Then
If CBool(lParam.flags And LLKHF_ALTDOWN) Then
If lParam.vkCode = VK_TAB Then
Debug.Print "Alt+Tab allowed"
LowLevelKeyboardProc = 0
Else
Debug.Print "Alt blocked"
LowLevelKeyboardProc = -1
End If
Exit Function
End If
End If
LowLevelKeyboardProc = CallNextHookEx(0, nCode, wParam, ByVal lParam)
End Function
According to this, my code is no correct or is it?Quote:
If nCode is less than zero, the hook procedure must return the value returned by CallNextHookEx.
If nCode is greater than or equal to zero, and the hook procedure did not process the message, it is highly recommended that you call CallNextHookEx and return the value it returns; otherwise, other applications that have installed WH_KEYBOARD_LL hooks will not receive hook notifications and may behave incorrectly as a result.
But if I use this
my code is not working. I am lost.Code:If lParam.vkCode = VK_F4 Then
...
Do not exit function when you want the key combo to be processed. If you don't pass it to the next hook procedure, that window may not process things correctly. Only exit function if you want to block it.
Now are you using the code from the 2nd link I posted? If so, you shouldn't be using lParam. lParam is a pointer to a KBDLLHOOKSTRUCT structure and in the sample code it clearly shows how to get that structure with CopyMemory. With very minor tweaks to your code, Alt+F4 was blocked easily. Don't take shortcuts.
I did what you told me, but still no luck. I know it is something minor, but what?
P.S. I want to allow Alt+F4, not block it.
Code:Function LowLevelKeyboardProc(ByVal nCode As Long, ByVal wParam As Long, lParam As KBDLLHOOKSTRUCT) As Long
Static kbdllhs As KBDLLHOOKSTRUCT
CopyMemory kbdllhs, ByVal lParam, Len(kbdllhs)
If nCode = HC_ACTION Then
If CBool(kbdllhs.Flags And LLKHF_ALTDOWN) Then
If kbdllhs.vkCode = VK_F4 Then
Debug.Print "Alt allowed"
Else
Debug.Print "Alt blocked"
LowLevelKeyboardProc = -1
Exit Function
End If
End If
End If
LowLevelKeyboardProc = CallNextHookEx(0, nCode, wParam, ByVal lParam)
End Function
Ok, here's a working example.
1. In a NEW project, add a form and bas module
2. In the form, add this code
3. In the module add this. I took out most of the comments and you can get them from the linked example in post #6 above.Code:Option Explicit
Private Sub Form_Load()
HookUnhook
End Sub
Private Sub Form_Unload(Cancel As Integer)
HookUnhook
End Sub
Do NOT execute an END statement while hooking, do not END your project while in debug mode. Close your project normally by clicking on the window's captionbar X button. If you do END, suggest closing your VB instance, saving changes, then re-opening. Any hooks applied must be released.Code:Option Explicit
Private Const WH_KEYBOARD_LL = 13& 'enables monitoring of keyboard
'input events about to be posted
'in a thread input queue
Private Const HC_ACTION = 0& 'wParam and lParam parameters
'contain information about a
'keyboard message
Private Const LLKHF_EXTENDED = &H1& 'test the extended-key flag
Private Const LLKHF_INJECTED = &H10& 'test the event-injected flag
Private Const LLKHF_ALTDOWN = &H20& 'test the context code
Private Const LLKHF_UP = &H80& 'test the transition-state flag
Private Const VK_TAB = &H9 'virtual key constants
Private Const VK_CONTROL = &H11
Private Const VK_ESCAPE = &H1B
Private Type KBDLLHOOKSTRUCT
vkCode As Long 'a virtual-key code in the range 1 to 254
scanCode As Long 'hardware scan code for the key
flags As Long 'specifies the extended-key flag,
'event-injected flag, context code,
'and transition-state flag
time As Long 'time stamp for this message
dwExtraInfo As Long 'extra info associated with the message
End Type
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, _
ByVal lParam As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" _
Alias "RtlMoveMemory" _
(pDest As Any, _
pSource As Any, _
ByVal cb As Long)
Private Declare Function GetAsyncKeyState Lib "user32" _
(ByVal vKey As Long) As Integer
Private m_hDllKbdHook As Long 'private variable holding
'the handle to the hook procedure
Public Sub HookUnhook()
'set and obtain the handle to the keyboard hook
If m_hDllKbdHook Then
Call UnhookWindowsHookEx(m_hDllKbdHook)
m_hDllKbdHook = 0&
Else
m_hDllKbdHook = SetWindowsHookEx(WH_KEYBOARD_LL, _
AddressOf LowLevelKeyboardProc, _
App.hInstance, 0&)
If m_hDllKbdHook = 0& Then MsgBox "Failed to install low-level keyboard hook - " & Err.LastDllError
End If
End Sub
Private Function LowLevelKeyboardProc(ByVal nCode As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
Static kbdllhs As KBDLLHOOKSTRUCT
If nCode = HC_ACTION Then
Call CopyMemory(kbdllhs, ByVal lParam, Len(kbdllhs))
If CBool(kbdllhs.flags And LLKHF_ALTDOWN) Then
Select Case kbdllhs.vkCode
Case VK_TAB
Case vbKeyF4
' add other cases that you want to allow; can be combined on a single line;
' like... Case VK_TAB, vbKey4
Case Else
Debug.Print "Alt blocked"
LowLevelKeyboardProc = 1
Exit Function
End Select
Debug.Print "Alt+key allowed"
End If
End If 'nCode = HC_ACTION
LowLevelKeyboardProc = CallNextHookEx(m_hDllKbdHook, nCode, wParam, lParam)
End Function
THANKS. It works. Except one combination: Alt + Shift. If I add line Case VK_LSHIFT, I get no effect.
Is it because Shift is a special key?
If VK_LShift is allowed, you should notice that it was allowed as soon as you hit Alt+Shift. Now when you tried a 3rd key: Alt+Shift+5 for example, .vkCode will be vbKey5, you won't know if Shift is down or not, and likewise, if a 4th key was held down, you wouldn't know if Shift or 5 was down previously.
One workaround is simply tracking what is down and what was released. The wParam parameter says if key is down or up during the hook event. See this MSDN documentation
There is a combination Alt+Shift (only two keys), which is designed for changing keyboard layout.
Alt+Shift is not working, Alt+F4 is not working (I was wrong, it is unfortunately not working).
If you can, please take a look.
Hmmm, sorry don't have that version of Excel. You probably should have mentioned this VBA related.
Ok, that is another version, for 97-2003.
Almost every code in VB works for VBA. In your code I have changed only one thing: App to Application.
Edit:
Meanwhile I will try to explain what I am trying to show you with that xls file. Let’s say I allow Alt+F1. When I hook keyboard and press that combination, Excel receives only F1 instead of Alt+F1, the same happens with all other combinations - Alt+F4, Alt+Shift to name a few. Interesting but Alt+Tab is working.
Maybe I can play with it later. Policies here prevent me from trying to run your macro. However, I did see something in that mvps example that isn't quite right for your situation.
The hModule and dwThreadId parameters of SetWindowHookEx are wrong if the scope of this hook is your workspace only (i.e., not global).
I don't know if Excel has a App.ThreadID like property, if not, you could use this instead:Quote:
Originally Posted by msdn
Code:' new API
Private Declare Function GetWindowThreadProcessId Lib "user32.dll" (ByVal hWnd As Long, ByRef lpdwProcessId As Long) As Long
m_hDllKbdHook = SetWindowsHookEx(WH_KEYBOARD_LL, _
AddressOf LowLevelKeyboardProc, _
0&, GetWindowThreadProcessId(Application.HWND, 0&))
Excel does have Application.Hinstance property.
I can manage the scope of this hook with this line:
However, scope is not the problem here.Code:If GetActiveWindow = FindWindow("XLMAIN", Application.Caption) Then
If nCode = HC_ACTION Then
....
Your code blocks Alt key (if I press it alone), so logically thinking how is it possible that if Alt is blocked (which is true), combination of Alt+F1 is not blocked? What we get is something like this:
1) I press and hold Alt - program tells to skip it
2) Then I press F1, program recognizes that Alt+F1 is pressed, so last key, which is F1, is passed, but Alt was skipped in the first place.
And result is that when I press Alt+F1, Excel gets instructions only for F1.
I'm not sure what to tell you. I can't play along because my version of Excel (2000) doesn't even have an Application.hWnd or .HInstance property. The fact that it works perfectly in VB (i.e., if Alt+anyKey except those I let thru), the project does not get the key. Therefore, even if I press Alt+F1+F2+F3, no key is passed to my project 'cause Alt is held down and none of those Fkeys are let thru.
Obviously it isn't working as advertised for you. I can only make guesses as to why it isn't working & first guess is VBA.
P.S. I was wrong regarding post 17; as it doesn't apply for the low-level keyboard hook.
Edited: Here is a quick sample of what got thru and what did not. Part of the output is from the form itself:
Code:Alt blocked ' passing Alt+someOtherKey
Alt blocked ' passing Alt+someOtherKey
Alt blocked ' passing Alt+someOtherKey
Alt blocked ' passing Alt+someOtherKey
Alt blocked ' passing Alt+someOtherKey
Alt blocked ' passing Alt+someOtherKey
Alt blocked ' passing Alt+someOtherKey
Alt blocked ' passing Alt+someOtherKey
Alt blocked ' passing Alt+someOtherKey
Alt+key allowed ' passed Alt+F1 & I had F1 allowed
>>> Rcvd KeyCode 112 ' F1 triggered in my form's KeyDown event
Alt+key allowed ' passed Alt+F4 & I had F4 allowed
>>> Rcvd KeyCode 115 ' F4 triggered in my form's KeyDown event
' form unloaded after that message
Ok, so it is not working for you correctly as well. F1 and F4 events in your form must not be triggered, what should be triggered is Alt+F1 and Alt+F4.
That is exactly the same behavior I have. I press Alt+F1 and only F1 is triggered.
No, it works perfectly & bad wording on the output. I just showed you that the Fkeys came thru. The ALT came through too else my form would not have closed when the Alt+F4 was pressed. It was just a simple output. Trust me, the Alt came thru too.
Edited: If you own VB, you can verify it yourself.
It is sad that you don't have Excel 2007.
I don't know if this will be any better, but it doesn't use the LowLevel hook type, it uses the old-fashioned standard keyboard hook. Note that this does require the ThreadID. I've extracted this from existing projects of mine before the lowlevel option existed.
Code:Option Explicit
Private Const WH_KEYBOARD As Long = 2
Private Const HC_ACTION = 0& 'wParam and lParam parameters
Private Const VK_TAB = &H9 'virtual key constants
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, _
ByVal lParam As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" _
Alias "RtlMoveMemory" _
(pDest As Any, _
pSource As Any, _
ByVal cb As Long)
Private Declare Function GetAsyncKeyState Lib "user32" _
(ByVal vKey As Long) As Integer
Private m_hDllKbdHook As Long 'private variable holding
'the handle to the hook procedure
Public Sub HookUnhook()
'set and obtain the handle to the keyboard hook
If m_hDllKbdHook Then
Call UnhookWindowsHookEx(m_hDllKbdHook)
m_hDllKbdHook = 0&
Else
m_hDllKbdHook = SetWindowsHookEx(WH_KEYBOARD, _
AddressOf LowLevelKeyboardProc, _
0&, App.ThreadID)
If m_hDllKbdHook = 0& Then MsgBox "Failed to install low-level keyboard hook - " & Err.LastDllError
End If
End Sub
Private Function LowLevelKeyboardProc(ByVal nCode As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
If nCode = HC_ACTION Then
If lParam < 0& Then 'wParam are vbKey... variables
Debug.Print "Keycode: "; wParam; Chr$(wParam), "key is up"
Else
If (lParam And &H40000000) Then
Debug.Print "Keycode: "; wParam; Chr$(wParam), "key is held down"
Else
Debug.Print "keycode: "; wParam; Chr$(wParam), "key is down first time"
End If
End If
' to test if extended key is pressed: (((lParam And &H1000000)) <> 0)
' to test if ALT is also pressed: (((lParam And &H20000000)) <> 0)
If ((lParam And &H20000000)) Then
Select Case wParam
Case vbKeyF1, vbKeyF4, VK_TAB
Debug.Print "Alt+Key allowed"
Case Else
Debug.Print "ALT+key blocked"
LowLevelKeyboardProc = 1
Exit Function
End Select
End If
End If 'nCode = HC_ACTION
LowLevelKeyboardProc = CallNextHookEx(m_hDllKbdHook, nCode, wParam, lParam)
End Function
When this code is executed I get something like infinite loop. LowLevelKeyboardProc is beeing called N+1 times.
Well it was a thought. I assume you supplied the ThreadID and changed the call the SetWindowsHookEx as shown in the newly posted code?
Because it works in VB and not Excel, I can only assume Excel is preprocessing somehow or that not passing to the next hook is interferring with Excel or something else. How MS Office wraps the VBA modules and what does and does not screw that relationship, I do not know.
I was trying a workaround – to use SendInput. As long as I can indentify when Alt+Key is pressed, I can send that combination. Unfortunately, that didn’t work either.
Is it a problem without a solution? I am almost convinced it is…
I have used GetCurrentThreadId. GetWindowThreadProcessId have not worked at all for me.