Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Capture Key Strokes - Disable PrintScreen

Status
Not open for further replies.

ELDeveloper

Programmer
Oct 3, 2002
3
0
0
US
Saw a bunch of questions about this including my own and came up with a solution. I found most of this from user strongm and added to it to trap system keys also. I only expanded on it enough to disable screen prints, including using the ALT key. It seems stable, but when running your app in the IDE, don't press the stop button or VB will die the next time you start it since the Hooks were left in place.

Just call ToggleHook(True) whenever you want it to start and ToggleHook(False) when you want it to stop. Thanks again strongm!

Enjoy =)

Option Explicit

Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Public 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
Public Declare Function CallNextHookEx Lib "user32" (ByVal hHook As Long, ByVal nCode As Long, ByVal wParam As Long, lParam As Any) As Long
Public Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As Long) As Long

Public Const HC_ACTION = 0
Public Const WM_KEYDOWN = 256
Public Const SYS_KEY = 260
Public Const WH_KEYBOARD_LL = 13

Public Type WM_COMMAND
wNotifyCode As Long 'notification code
wID As Long 'item, control, or accelerator identifier
hwndCtl As Long 'handle of control
End Type

Public Type KBDLLHOOKSTRUCT
vkCode As Long ' virtual keycode
scanCode As Long ' hardware scancode
flags As Long ' Read MSDN documentation for further info on this member
time As Long
dwExtraInfo As Long
End Type

Public prevLowLevelKybd As Long

Public Function LowLevelKeyboardProc(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
' LowLevelKeyboardProc is only supported in NT 4.0 SP3 and later

On Error GoTo ErrorHandler

Dim HookStruct As KBDLLHOOKSTRUCT
Dim CommandStruct As WM_COMMAND

' Is it a keyboard message?
If wParam = WM_KEYDOWN Then 'a non-system key was pressed
CopyMemory HookStruct, ByVal lParam, Len(HookStruct)
Select Case HookStruct.vkCode
Case 44, 106
Wait (1) 'must wait since printscreen is obviously slow - use whatever sleep function you have
Clipboard.Clear
Debug.Print &quot;Clearing Clipboard - <SHIFT | CNTRL>+PrintScreen&quot;
Debug.Print Clipboard.GetData(2) 'should be a 0 to represent no BMPs
End Select
ElseIf wParam = SYS_KEY Then 'a system key was pressed
CopyMemory CommandStruct, ByVal lParam, Len(CommandStruct)
Select Case CommandStruct.wID
Case 84
Wait (1) 'must wait since printscreen is obviously slow - use whatever sleep function you have
Clipboard.Clear
Debug.Print &quot;Clearing Clipboard - ALT+PrintScreen&quot;
Debug.Print Clipboard.GetData(2) 'should be a 0 to represent no BMPs
End Select
Else 'WM_SYSCOMMAND
End If

' OK, pass everything on for default processing
LowLevelKeyboardProc = CallNextHookEx(0, nCode, wParam, ByVal lParam)
Exit Function

ErrorHandler:
If Err.Number = 521 Then 'don't fight for control of the clipboard, just wait it out
Wait (1)
Err.Clear
Resume
End If
End Function

Public Sub ToggleHook(WantHook As Boolean)
If WantHook = True Then
prevLowLevelKybd = SetWindowsHookEx(WH_KEYBOARD_LL, AddressOf LowLevelKeyboardProc, App.hInstance, 0)
Else
UnhookWindowsHookEx prevLowLevelKybd
prevLowLevelKybd = 0
End If
End Sub
 
Hi,

No offense to your code, but I have a few remarks.

1. The code IS stable, but the IDE isn't ;-)
2. This will only work in Windows NT based Operating Systems, because the low level hooks are only implemented there.
3. You are using a few wait statements (which are nothing but reliable for these purposes by the way) in your Hook Procedure, which means it can REALLY slow down windows, in fact if it takes to long, you can't even finish processing the message, because the low level keyboard hook has a timeout value in the registry and if you don't process the message within that time it passes the message on to the next application
4. You use &quot;WM_COMMAND&quot; for a Type Structure, while it should be a Constant normally, might give some naming errors
5. With this hook you don't block the Print Screen button, but you clean up the mess it did, what if you already had something on the clipboard you didn't want to loose?
6. I'd rather use the conventional Constants Naming such as WM_SYSKEYDOWN instead of SYS_KEY
7. If you simply delete the keypress from the message queue you don't need to trap the error and you don't need to use the wait functions either
8. Why do u use at all the WM_COMMAND type instead of using the KBDLLHOOKSTRUCT type there as well?
9. You use the variable name &quot;prevLowLevelKybd&quot; for the handle to the hook, it seems you are mixing subclassing and hooking up
10. You forget to pass the handle to the hook to the CallNextHook function
11. I'd rather only check the queue on HC_ACTION
12. I don't see why you should check on the combinations with ALT, CTRL and SHIFT, just capture all PrtSc keystrokes

I have rewritten your code in a much nicer and cleaner and faster way:

[tt]
Option Explicit

Private Declare Function SetWindowsHookEx Lib &quot;user32&quot; Alias &quot;SetWindowsHookExA&quot; (ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long
Private Declare Function UnhookWindowsHookEx Lib &quot;user32&quot; (ByVal hHook As Long) As Long
Private Declare Function CallNextHookEx Lib &quot;user32&quot; (ByVal hHook As Long, ByVal nCode As Long, ByVal wParam As Long, lParam As Any) As Long

Private Declare Sub CopyMemory Lib &quot;kernel32&quot; Alias &quot;RtlMoveMemory&quot; (Destination As Any, Source As Any, ByVal Length As Long)

Private Const WH_KEYBOARD_LL = &HD

Private Const WM_KEYDOWN = &H100
Private Const WM_SYSKEYDOWN = &H104

Private Const HC_ACTION = 0

Private Const VK_PRINTSCREEN = &H2C

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

Private hHook As Long

Public Function SetKeyboardHook() As Long

If hHook = 0 Then
hHook = SetWindowsHookEx(WH_KEYBOARD_LL, AddressOf LowLevelKeyboardProc, App.hInstance, 0)
SetKeyboardHook = hHook
End If

End Function

Public Sub UnsetKeyboardHook()

Call UnhookWindowsHookEx(hHook)
hHook = 0

End Sub

Private Function LowLevelKeyboardProc(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

Dim lpllkHookStruct As KBDLLHOOKSTRUCT

If nCode = HC_ACTION Then
Call CopyMemory(lpllkHookStruct, ByVal lParam, Len(lpllkHookStruct))

If lpllkHookStruct.vkCode = VK_PRINTSCREEN Then
LowLevelKeyboardProc = True
Else
LowLevelKeyboardProc = CallNextHookEx(hHook, nCode, wParam, lParam)
End If
Else
LowLevelKeyboardProc = CallNextHookEx(hHook, nCode, wParam, lParam)
End If

End Function
[/tt]

I didn't mean to flame you, but I just wanted to show you how hooking should be done, especially system wide hooks, since if you do it wrong, you don't only slow down your program, but you slow down Windows.
 
Thanks for the input, this was my first attempt at using Hooks and most of it was taken from another post. I was having problems with capturing the ALT+PrintScreen and that was my work around with the WM_COMMAND. The wait function I am using is a very good one, that allows the user to keep inputting data, receive messages and what-not. I use the wait since the only time that it should be used is when the print screener is trying to paste immediately after pressing the printscreen button. I don't have any experience with message queues but will look into them thoroughly when I get a chance. Where do you get the &quot;&H&quot; codes at? I couldn't find them to save my life.

I will try your code to see if it captures the ALT commands also.
 
Just got done testing it, works great LuckyLuke, thanks. I would still like to know where the '&H' codes come from...
 
I take the Constant values directly from the windows headers. If you have Visual C++ installed or Platform SDK, you can find a map named &quot;Includes&quot; in which all windows headers are to be found. You can find ALL API functions and Constants there. You could also use the API Text Viewer provided with Visual Basic, but it lacks a lot of constants and declarations. Ofcourse, you could also convert your own values to hex values :)
 
2. More accurately, NT-based OS post NT4 SP3
9. My fault. He's just extracted the name I used in my original example
10. Again, my fault. But done for a (vague) reason. Typically the address you would pass would normally be in a shared data section, which you can't achieve in a VB program. So I wanted to play safe by passing a null pointer.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top