|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Visual Basic Subclassing Routines WM_HOTKEY: System-Wide Keyboard Trapping |
||
Posted: | Friday July 16, 1999 | |
Updated: | Monday December 26, 2011 | |
Applies to: | VB5, VB6 | |
Developed with: | VB6, Windows NT4 | |
OS restrictions: | None | |
Author: | VBnet - Randy Birch | |
Prerequisites |
VB5 or VB6. |
|
As
VB development expands from its origins as a small-utilities tool into its now-mature role as the tool for large scale production
applications, developers rightfully ask for more intrinsic functionality formerly only available in lower languages.
One of these requests has been for a method to define and trap specific system-wide keystrokes. Here we demo how to perform this feat using the RegisterHotKey API. RegisterHotKey is explained in the MSDN with the following Remarks:
As the above implies, RegisterHotKey is not limited to providing just system-wide keypress data. A registered hotkey can also used simply to perform a method solely within the application not available via menu accelerators. In this context however, it would be prudent for the developer to unhook the key when the application looses focus to another outside application, and restore it when focus returns. This can be easily achieved by implementing the code detailed in the page Subclassing Form Messages: WM_ACTIVATEAPP: Detect Application Activation State. The basic code in this article was provided to me without indication of the author. If you know who should get credit, please let me know via the Comments option. |
Class Code |
Add the following code to a Class module (CGlobalAtom).: |
|
Option Explicit '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Copyright ©1996-2011 VBnet/Randy Birch, All Rights Reserved. ' Some pages may also contain other copyrights by the author. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Distribution: You can freely use this code in your own ' applications, but you may not reproduce ' or publish this code on any web site, ' online service, or distribute as source ' on any media without express permission. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Private m_AtomID As Long Private Sub Class_Initialize() m_AtomID = GlobalAddAtom(CStr(Now)) End Sub Private Sub Class_Terminate() Call GlobalDeleteAtom(m_AtomID) End Sub Public Property Get Value() As Long Value = m_AtomID End Property |
BAS Module Code |
Place the following code into the general declarations area of a bas module: |
|
Option Explicit '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Copyright ©1996-2011 VBnet/Randy Birch, All Rights Reserved. ' Some pages may also contain other copyrights by the author. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Distribution: You can freely use this code in your own ' applications, but you may not reproduce ' or publish this code on any web site, ' online service, or distribute as source ' on any media without express permission. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' 'used by the RegisterHotKey method Public Enum RegisterHotKeyModifiers MOD_ALT = &H1 MOD_CONTROL = &H2 MOD_SHIFT = &H4 End Enum Public Declare Function RegisterHotKey Lib "user32" _ (ByVal hWnd As Long, _ ByVal id As Long, _ ByVal fsModifiers As RegisterHotKeyModifiers, _ ByVal vk As KeyCodeConstants) As Long Public Declare Function UnregisterHotKey Lib "user32" _ (ByVal hWnd As Long, _ ByVal id As Long) As Long Public Declare Function GlobalAddAtom Lib "kernel32" _ Alias "GlobalAddAtomA" _ (ByVal lpString As String) As Long Public Declare Function GlobalDeleteAtom Lib "kernel32" _ (ByVal nAtom As Long) As Long Private Declare Function CallWindowProc Lib "user32" _ Alias "CallWindowProcA" _ (ByVal lpPrevWndFunc As Long, _ ByVal hWnd As Long, ByVal Msg As Long, _ ByVal wParam As Long, ByVal lParam As Long) As Long Private Declare Function SetWindowLong Lib "user32" _ Alias "SetWindowLongA" _ (ByVal hWnd As Long, ByVal nIndex As Long, _ ByVal dwNewLong As Long) As Long Public Const GWL_WNDPROC As Long = (-4) Public Const WM_HOTKEY As Long= &H312 Public lpPrevWndProc As Long 'used by the PrintScreen method Public Declare Function BitBlt Lib "gdi32" _ (ByVal hDCDest As Long, ByVal XDest As Long, _ ByVal YDest As Long, ByVal nWidth As Long, _ ByVal nHeight As Long, ByVal hDCSrc As Long, _ ByVal XSrc As Long, ByVal YSrc As Long, _ ByVal dwRop As Long) As Long Public Declare Function GetDesktopWindow Lib "user32" () As Long Public Declare Function GetWindowDC Lib "user32" _ (ByVal hWnd As Long) As Long Public Declare Function ReleaseDC Lib "user32" _ (ByVal hWnd As Long, _ ByVal hdc As Long) As Long Public Sub Hook(ByVal gHW As Long) 'Establish a hook lpPrevWndProc = SetWindowLong(gHW, GWL_WNDPROC, AddressOf WindowProc) End Sub Public Sub Unhook(ByVal gHW As Long) 'Reset the message handler Call SetWindowLong(gHW, GWL_WNDPROC, lpPrevWndProc) End Sub Function WindowProc(ByVal hWnd As Long, ByVal uMsg As Long, _ ByVal wParam As Long, ByVal lParam As Long) As Long Select Case hWnd Case frmMain.hWnd If uMsg = WM_HOTKEY Then 'add code to process the Hotkey frmMain.Caption = "Hotkey Pressed PrintScreen End If Case Else End Select 'Pass message to the original window message handler WindowProc = CallWindowProc(lpPrevWndProc, hWnd, uMsg, wParam, lParam) End Function Private Sub PrintScreen() Dim hWndDesk As Long Dim hDCDesk As Long Dim LeftDesk As Long Dim TopDesk As Long Dim WidthDesk As Long Dim HeightDesk As Long 'define the screen coordinates (upper 'corner (0,0) and lower corner (Width, Height) LeftDesk = 0 TopDesk = 0 WidthDesk = Screen.Width \ Screen.TwipsPerPixelX HeightDesk = Screen.Height \ Screen.TwipsPerPixelY 'get the desktop handle and display context hWndDesk = GetDesktopWindow() hDCDesk = GetWindowDC(hWndDesk) 'copy the desktop to the picture box Call BitBlt(frmMain.Picture1.hdc, 0, 0, _ WidthDesk, HeightDesk, hDCDesk, _ LeftDesk, TopDesk, vbSrcCopy) Call ReleaseDC(hWndDesk, hDCDesk) End Sub |
Form Code |
To a form, add a three-button control array (Command1(0) - Command1(2)) and a picture box (Picture1). Add the following code: |
|
Option Explicit Private GlobalAtom As CGlobalAtom Private IsTrapping As Boolean 'command button index consts Private Const nTrap = 0 Private Const nStop = 1 Private Const nClear = 2 Private Sub Form_Load() UpdateInfo End Sub Private Sub Form_Unload(Cancel As Integer) If IsTrapping Then Command1(nStop).Value = True End If End Sub Private Sub UpdateInfo() Caption = IIf(IsTrapping, "Trapping Print Screen", "Trapping Off") Command1(0).Enabled = IsTrapping = False Command1(1).Enabled = IsTrapping = True End Sub Private Sub Command1_Click(index As Integer) Select Case index Case nTrap 'begin trapping Set GlobalAtom = New CGlobalAtom 'Call procedure to begin capturing messages Call Hook(Me.hWnd) 'Trap Print Screen (vbKeySnapshot). 'API returns 1 if successful, so 'evaluating to 1 renders true. IsTrapping = RegisterHotKey(Me.hWnd, GlobalAtom.Value, 0, vbKeySnapshot) = 1 Case nStop 'end trapping 'API returns 1 if successful, so 'evaluating to 0 renders False. IsTrapping = UnregisterHotKey(Me.hWnd, GlobalAtom.Value) = 0 Call Unhook(Me.hWnd) Case nClear 'clear the pix and clipboard Clipboard.Clear Picture1.Cls End Select UpdateInfo End Sub |
Comments |
Remember this is a subclassed project, so be sure to save
before running, and always "Start with Full Compile" to catch any errors.
Once running, pressing the Trap button will register the vbKeySnapshot key with your application. Pressing the Print Screen key will fill the picture box with the screen shot. The clear button clears both the clipboard and the pixbox. Other PrintScreen API methods can be found on the VBnet Bitmap pages. Feel free to change the print screen vbKeySnapshot to any other key constant - ie vbKeyF6 in the Command1_Click event. |
|
|
|
|||||
|
|||||
|
|||||
Copyright ©1996-2011 VBnet and Randy Birch. All Rights Reserved. |