Pengertian Windows API
Click here for larger image Figure 1: Use the API Viewer to get Windows API method signatures correct.
Stubbing the Keyboard Callback Method
Unfortunately callback methods must be in modules (not class files) in VB6. Thus we will need to place the callback method for SetWindowsHookEx in a module. The signature of the callback is a function that accepts three Long arguments and returns a Long. (You might have to do a little poking around to discover this information, but I will just provide an empty callback stub for you.)
Public Function KeyboardCallback(ByVal Code As Long, _ ByVal wParam As Long, ByVal lParam As Long) As Long End Function
The signature of the callback method looks suspiciously like a generic Windows message handler. Code helps us decide if the message is for us. The wParam argument contains the actual Windows message constant ad lParam plays the role of pointer to keyboard data. We will only test Code for a single constant value and the lParam is actually a pointer to the keyboard data. We will come back to this in a moment.
Hooking the Keyboard
Now that I have the callback method—albeit an empty method—I can hook the keyboard, which means that my method will be called very early in the message handling process. To hook the keyboard I defined a simple method named HookKeyboard that wraps the more complicated call to SetWindowsHookEx.
Private Const WH_KEYBOARD_LL = 13& Public Sub HookKeyboard() KeyboardHandle = SetWindowsHookEx( _ WH_KEYBOARD_LL, AddressOf KeyboardCallback, _ App.hInstance, 0&) Call CheckHooked End Sub
(All of the code so far is in a single module I named KeyboardHandler.bas.) The constant WH_KEYBOARD_LL defines the kind of hook we want to make: a low-level keyboard hook. There are several other kinds of hooks. For example, you can pass constants that indicate that you want to hook the mouse, a hook for debugging, and one that is suggested as being helpful for computer-based training. The second argument is the AddressOf our KeyboardCallback function. The third argument is the application Windows handle, and we can pass 0 for the thread ID. The value 0 indicates that the hook is associated with all threads on the desktop. By passing 0 we are hooking the keyboard and can effectively trap keys effecting all applications.
With the code we have so far the KeyboardCallback method will be called every time we press a key, whether our application has the focus or not. The next step is to write some code in the callback that evaluates the received keyboard messages.
Implementing the Keyboard Callback Method
Listing 1 contains the implementation I used for the KeyboardCallback function. The code is numbered for convenience, followed by a brief synopsis describing the behavior.
Listing 1: The implementation of my KeybaordCallback procedure.
1: Public Function KeyboardCallback(ByVal Code As Long, _
2: ByVal wParam As Long, ByVal lParam As Long) As Long
3:
4: Static Hookstruct As KBDLLHOOKSTRUCT
5:
6: If (Code = HC_ACTION) Then
7: ' Copy the keyboard data out of the lParam (which is a pointer)
8: Call CopyMemory(Hookstruct, ByVal lParam, Len(Hookstruct))
9:
10: If (IsHooked(Hookstruct)) Then
11: KeyboardCallback = 1
12: Exit Function
13: End If
14:
15: End If
16:
17: KeyboardCallback = CallNextHookEx(KeyboardHandle, _
18: Code, wParam, lParam)
19:
20: End Function
If we check the online help documentation it says that we can handle HC_ACTION, which is defined as constant with a value of 0. If Code is less than 0 then we need to immediately pass the message to CallNextHookEx. Hence, when Code equals HC_ACTION (0) I handle the data on lines 6 through 15; otherwise I pass the message to the next function in the callback chain. (Keep in mind that other programs may have keyboard hooks too.) Let’s examine the statement on line 17 and 18 first.
Line 17 returns the value of CallNextHookEx. Notice that I pass it the address of the callback I grabbed when I hooked the keyboard, the code, the wParam, and lParam arguments. wParam contains the constant for the Windows message, and the lParam contains a pointer to the keyboard data itself.
On line 6 if the Code is 0 then I want to handle the message. I could also check the wParam to determine if the message is a WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, or WM_SYSKEYUP. (These are Windows message constants that you can define using the API Viewer.) For our purposes we won’t check the wParam. If the Code is 0 then we want to convert the lParam to the type we know it is, a KBDLLHOOKSTRUCT, handle the keyboard input, and return 1 to indicate that the input was handled. I implemented a method IsHooked that afforded me a degree of separation between the murky API stuff and the keys I wanted to block. IsHooked is where you and I can decide what key combinations to manage manually.
Getting the Keyboard Data
Lines 4 and 8 of listing 1 introduced a type and an API method that we haven’t talked about yet. I couldn’t find KBDLLHOOKSTRUCT in my Windows API Viewer, but CopyMemory was there. (Both the type and the API method are shown in listing 2.)
Listing 2: The KBDLLHOOKSTRUCT type and the CopyMemory method.
Private Declare Sub CopyMemory Lib "kernel32" _ Alias "RtlMoveMemory" _ (pDest As Any, _ pSource As Any, _ ByVal cb As Long) Private Type KBDLLHOOKSTRUCT vkCode As Long scanCode As Long flags As Long time As Long dwExtraInfo As Long End Type
(I’m not sure how anyone without a lot of knowledge about the API was ever supposed to put this together.) As I mentioned lParam is really a pointer, which we don’t have in VB6. We use long integers to represent pointers, as a pointer is really just a number representing a location in memory. CopyMemory can copy information from one location in memory to a second location. Hence, we use CopyMemory to get the keyboard data from the address pointed to by lParam into our local static variable. (We are basically just copying the keyboard data here.)
The reason I wrote this code as I have shown you so far is that you could easily reuse this code in any context, over and over without revisiting the low-level API stuff. Simple reuse HookKeyboard, KeyboardCallback, and all you would have to do is re-implement IsHooked.
Chaining Hooked Callbacks
The signature for CallNextHookEx is provided for reference here. It is a good idea when you are hooking low-level operations to keep track of the thing that hooked it before you did. In this way your low-level hook will not foul up someone else’s low-level hook. HookKeyboard does that.
Private Declare Function CallNextHookEx Lib "user32" _ (ByVal hHook As Long, _ ByVal nCode As Long, _ ByVal wParam As Long, _ ByVal lParam As Long) As Long
I was unable to find CallNextHookEx in the API Viewer. (I have to admit that since .NET was released I haven’t been as diligent about updating my MSDN for Visual Studio 6 as I should be. I am sure you’re more diligent and won’t have any trouble finding these declarations in your API Viewer.)
IsHooked: Checking for Blocked Key Combinations
Tip: Event the low-level keyboard hook will not block some key combinations, for instance, Ctrl+Alt+Del. I believe Ctrl+Alt+Del generates an interrupt 0x19, which is not routed as a keyboard message. You probably have to implement an interrupt handler for interrupt 0x19 to catch Ctrl+Alt+Del. |
We now have the low-level API hooking code out of the way, and the data is in the somewhat more manageable KBDLLHOOKSTRUCT. Following my implementation all we have to is define IsHooked to return a Boolean that indicates if we want to handle any particular keyboard combination or not. Listing 3 demonstrates one implementation of IsHooked.
Listing 3: An implementation of my IsHooked method checks for Alt+Tab, Ctrl+Esc, and Alt+Esc.
' Implement this function to block as many key combinations as ' you'd like Public Function IsHooked(ByRef Hookstruct As KBDLLHOOKSTRUCT) _ As Boolean If (KeyboardHook Is Nothing) Then IsHooked = False Exit Function End If ' Checks for Ctrl + Esc If (Hookstruct.vkCode = VK_ESCAPE) And _ CBool(GetAsyncKeyState(VK_CONTROL) _ And &H8000) Then IsHooked = KeyboardHook.BlockControlEscape Call HookedState(IsHooked, "Ctrl + Esc blocked") Exit Function End If ' Checks for Alt + Tab If (Hookstruct.vkCode = VK_TAB) And _ CBool(Hookstruct.flags And _ LLKHF_ALTDOWN) Then IsHooked = KeyboardHook.BlockAltTab Call HookedState(IsHooked, "Alt + Tab blockd") Exit Function End If ' Checks for Alt + Esc If (Hookstruct.vkCode = VK_ESCAPE) And _ CBool(Hookstruct.flags And _ LLKHF_ALTDOWN) Then IsHooked = KeyboardHook.BlockAltEscape Call HookedState(IsHooked, "Alt + Escape blocked") Exit Function End If End Function
We have to pass user defined types ByRef. Even in IsHooked you can quickly determine that figuring out what keyboard combinations were being pressed is murky. Checking virtual keys, getting virtual key states asynchronously, and Anding flag values requires too much special knowledge. We only want to write this code one time.
I defined IsHooked to check for Ctrl+Esc, Alt+Tab, and Alt+Esc. If any of these combinations are pressed then I block them. More beneficially you will probably want to write the code to notify your application if any of these combinations are pressed and dynamically allow your application to block them or not based on some application state. (We will take a look at one way to do this in the last section “Genericizing Keyboard Hooks”.) Let’s finish our current discussion by implementing the code to unhook the keyboard.
Unhooking the Keyboard
When your application exits you want to release you hook. You need to call UnhookWindowsHookEx. To be symmetric I implemented a simpler method to wrap up the API method. Listing 4 contains the declaration for UnhookWindowsHookEx and the implementation of my wrapper method.
Listing 4: Unhooking the keyboard.
Public Declare Function UnhookWindowsHookEx Lib "user32" _ (ByVal hHook As Long) As Long Public Sub UnhookKeyboard() If (Hooked) Then Call UnhookWindowsHookEx(KeyboardHandle) End If End Sub
Before we wrap up and I show you the complete listing for the sample code I want to explain the meaning of the three statements in listing 2 that look something like this one: IsHooked = KeyboardHook.BlockAltEscape.
Genericizing Keyboard Hooks
I enjoy discovering nuts and bolts information that is highly specialized, but I have to make a distinction between fun and pragmatics. Figuring all of this stuff out (with some help from MSDN) was a lot of fun. However, as a matter of pragmatics it is too convoluted to ever repeat, especially when writing software for hire. As a result I created a very simple interface that uses named methods to represent the hooked values. Any class or Form that implements my interface can receive a notification when a specific keyboard combination is pressed. Using this strategy I will never have to figure out what the relationship between the flags, the vkCode, and the hexadecimal number &H8000 are again. This is fun only to the extent that I am a factoid junky, but there is a dichotomy between trying to be productive and figuring out these details.
Listing 5 contains the interface that I defined to wire up to the low-level keyboard code. Doing so vastly simplified a Form’s interaction with the API code.
Listing 5: The interface
' KeyboardHook.cls
Public Function BlockControlEscape() As Boolean
End Function
Public Function BlockAltEscape() As Boolean
End Function
Public Function BlockAltTab() As Boolean
End Function
Finally I defined a public variable KeyboardHook. If I assign an object that implements KeyboardHook to this public variable then the IsHooked method will call the appropriate KeyboardHook method, and that method—completely unaware of the low-level API code—can determine if it want to block that key combination or not. A Form that implements and wires into KeyboardHook is shown in the complete code listing, listing 6.
Listing 6: A single complete source code listing for a Form, class, and module that demonstrates the low-level keyboard hooks and an interface that simplifies the hook behavior.
' Form1.frm Option Explicit Implements KeyboardHook Private Sub Form_Load() Set KeyboardHandler.KeyboardHook = Me HookKeyboard End Sub Private Sub Form_Unload(Cancel As Integer) UnhookKeyboard End Sub Private Function KeyboardHook_BlockAltEscape() As Boolean End Function Private Function KeyboardHook_BlockAltTab() As Boolean End Function Private Function KeyboardHook_BlockControlEscape() As Boolean KeyboardHook_BlockControlEscape = True End Function ' Keyboardhandler.bas - Demonstrates low-level keyboard hooks ' Copyright (c) 2002. All Rights Reserved ' By Paul Kimmel. pkimmel@softconcepts.com 'http://msdn.microsoft.com/library/default.asp?url= '/library/en-us/winui/WinUI/WindowsUserInterface '/Windowing/Hooks/HookReference/HookFunctions/LowLevelKeyboardProc.asp Option Explicit Public Declare Function UnhookWindowsHookEx Lib "user32" _ (ByVal hHook As Long) 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 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 Declare Function CallNextHookEx Lib "user32" _ (ByVal hHook As Long, _ ByVal nCode As Long, _ ByVal wParam As Long, _ ByVal lParam As Long) As Long Private Type KBDLLHOOKSTRUCT vkCode As Long scanCode As Long flags As Long time As Long dwExtraInfo As Long End Type ' Low-Level Keyboard Constants Private Const HC_ACTION = 0 Private Const LLKHF_EXTENDED = &H1 Private Const LLKHF_INJECTED = &H10 Private Const LLKHF_ALTDOWN = &H20 Private Const LLKHF_UP = &H80 ' Virtual Keys Public Const VK_TAB = &H9 Public Const VK_CONTROL = &H11 Public Const VK_ESCAPE = &H1B Public Const VK_DELETE = &H2E Private Const WH_KEYBOARD_LL = 13& Public KeyboardHandle As Long Public KeyboardHook As KeyboardHook ' Implement this function to block as many key combinations as ' you'd like Public Function IsHooked(ByRef Hookstruct As KBDLLHOOKSTRUCT) _ As Boolean If (KeyboardHook Is Nothing) Then IsHooked = False Exit Function End If If (Hookstruct.vkCode = VK_ESCAPE) And _ CBool(GetAsyncKeyState(VK_CONTROL) _ And &H8000) Then IsHooked = KeyboardHook.BlockControlEscape Call HookedState(IsHooked, "Ctrl + Esc blocked") Exit Function End If If (Hookstruct.vkCode = VK_TAB) And _ CBool(Hookstruct.flags And _ LLKHF_ALTDOWN) Then IsHooked = KeyboardHook.BlockAltTab Call HookedState(IsHooked, "Alt + Tab blocked") Exit Function End If If (Hookstruct.vkCode = VK_ESCAPE) And _ CBool(Hookstruct.flags And _ LLKHF_ALTDOWN) Then IsHooked = KeyboardHook.BlockAltEscape Call HookedState(IsHooked, "Alt + Escape blocked") Exit Function End If End Function Private Sub HookedState(ByVal Hooked As Boolean, _ ByVal Text As String) If (Hooked) Then Debug.Print Text End Sub Public Function KeyboardCallback(ByVal Code As Long, _ ByVal wParam As Long, ByVal lParam As Long) As Long Static Hookstruct As KBDLLHOOKSTRUCT If (Code = HC_ACTION) Then ' Copy the keyboard data out of the lParam (which is a pointer) Call CopyMemory(Hookstruct, ByVal lParam, Len(Hookstruct)) If (IsHooked(Hookstruct)) Then KeyboardCallback = 1 Exit Function End If End If KeyboardCallback = CallNextHookEx(KeyboardHandle, _ Code, wParam, lParam) End Function Public Sub HookKeyboard() KeyboardHandle = SetWindowsHookEx( _ WH_KEYBOARD_LL, AddressOf KeyboardCallback, _ App.hInstance, 0&) Call CheckHooked End Sub Public Sub CheckHooked() If (Hooked) Then Debug.Print "Keyboard hooked" Else Debug.Print "Keyboard hook failed: " & Err.LastDllError End If End Sub Private Function Hooked() Hooked = KeyboardHandle <> 0 End Function Public Sub UnhookKeyboard() If (Hooked) Then Call UnhookWindowsHookEx(KeyboardHandle) End If End Sub ' KeyboardHook.cls Public Function BlockControlEscape() As Boolean End Function Public Function BlockAltEscape() As Boolean End Function Public Function BlockAltTab() As Boolean End Function
Summary
It is possible to do just about anything in Visual Basic 6. We just have to lean on the API some of the time, as we do when hooking the keyboard.
To acquire a low-level hook into the keyboard you need several Windows API methods, including: SetWindowsHookEx, UnHookWindowsHookEx, CopyMemory, CallNextHookEx, and GetAsyncKeyState. In addition you will need the KBDLLHOOKSTRUCT structure. The basic idea is to call SetWindowsHookEx passing a callback method that Windows will call when a low-level key operation occurs. If the Code passed to your callback equals 0 then Copy the keyboard data out of the parameter passed to your callback with the CopyMemory method into a local variable of type KBDLLHOOKSTRUCT; otherwise send all of the information to CallNextHookEx. You can examine the value of the KBDLLHOOKSTRUCT in part with the GetAsyncKeyState method, and when you are all finished make sure you call UnHookWindowsHookEx.
Sounds easy. Well, there is quite a bit of cryptic information involved, which suggests that all of this information should be wrapped up as part of a cleaner framework. This will tide you over for now.
About the Author
Paul Kimmel is a freelance writer for Developer.com and CodeGuru.com. Look for his recent book “Advanced C# Programming” from McGraw-Hill/Osborne on Amazon.com. Paul will be a keynote speaker in “Great Debate: .NET or .NOT?” at the Fall 2002 Comdex in Las Vegas, Nevada. Paul Kimmel is available to help design and build your .NET solutions and can be contacted at pkimmel@softconcepts.com.
# # #
I am having a problem with this code. I have copied code to my vb6 project but when i execute it an error occurs:
Invalid use of Addressof Operator
Please help me to resolve this issue….
Thanks
Y cual es la funci�n que me devuelve el string o el long de la tecla presionada???
//
- // World’s Fastest Object Database.
// Don’t Create Apps, Create Experiences! Intel AppUp Dev Challenge–New Categories, Bigger Prizes!
// IBM On-demand Webcast: Developing and Testing in the Cloud.
// Free Download: BlackBerry Enterprise Server Express–click here.
// New IBM developerWorks survey shows dominance of cloud computing & mobile app development–Learn more.
PHP SDK for Windows Azure
This open source project, announced in May, is an effort to bridge PHP developers to Windows Azure. PHPAzure is an open source project to provide software development kit for Windows Azure and Windows Azure Storage – Blobs, Tables, and Queues.
PHP SDK for Windows Azure
This open source project is an effort to bridge PHP developers to Windows Azure. PHPAzure provides software development kit for Windows Azure and Windows Azure Storage – Blobs, Tables, and Queues. >>
Windows Azure SDK
The Windows Azure SDK provides developers with the APIs, tools, and samples needed to develop apps on Windows Azure. Developers can use it to create applications that run it in a local development fabric even without a registered account.
Windows Azure SDK
The Windows Azure SDK provides developers with the APIs, tools, documentation and samples needed to develop Internet-scale apps on Windows Azure. Developers can use it to create applications that run it in a local development fabric even without a registered account. >>
Windows Azure Platform Training Kit
The Windows Azure Platform Training Kit includes a comprehensive set of technical content including hands-on labs, presentations, and demos that are designed to help you learn how to use the Windows Azure platform including: Windows Azure, SQL Azure and .NET Services.
Windows Azure Platform Training Kit
The Windows Azure Platform Training Kit includes hands-on labs, presentations, and demos that are designed to help you learn the Windows Azure platform. >>
Most Popular Stories
Most Commented On
Partners
- Partner With Us
// Colocation Los Angeles
// prepaid calling card
// Virtual Server
// Televisions
// VPS Hosting
// Host your Site
// Desktop Computers
// Data Center
// Colocation
// prepaid phone card
// Liability Insurance
// Phone Cards
// PDA Phones & Cases
// Site Hosting
On the Forums
Windows API (Application Programming Interface) merupakan sekumpulan fungsi-fungsi eksternal yang terdapat dalam file-file perpustakaan Windows (library Windows) atau file library lainnya yang dapat digunakan oleh program. Fungsi ini dapat menangani semua yang berhubungan dengan Windows, seperti pengaksesan disk, interface printer, grafik Windows, kotak dialog (buka file, simpan file, memilih font, memilih warna, dll) Windows shell, setting sistem operasi, penanganan file, mengakses sitem registry, memainkan musik, dan sebagainya. Fungsi inni menyediakan banyak fitur-fitur standar untuk semua program yang berbasis Windows.
Hampir semua fungsi Windows API terdapat dalam direktori sistem milik Windows dan paling banyak berekstensi .DLL yang digunakan oleh sistem operasi Windows. Selain itu fungsi ini juga memastikan secara konsisten penggunaan semua sumber yang terdapat dalam Windows. File-file itulah yang disebut dengan Windows API.
Karena fungsi Windows API merupakan fungsi eksternal, maka untuk menggunakan fungsi tersebut terlebih dahulu dideklarasikan dengan perintah Declare didalam kode program. Setelah pendeklarasian fungsi selesai dilakukan, selanjutnya anda bebas untuk menggunakan fungsi tersebut layaknya bahasa Visual Basic didalam program.