19. Windows Hooks

Page 322
  19. Windows Hooks  
   
  A hook is a technique for observing various portions of the Windows message stream. Windows defines more than a dozen different types of hooks, the locations of several of which are shown in Figure 19-1 (along with subclassing).  
   
  The principle behind hook is quite similar to that of subclassing. If a message passes through a portion of the Windows messaging system in which a hook is installed, the system will call a hook procedure which we must implement.  
 
  Global and Thread-Specific Hooks  
   
  A hook may be thread-specific, in which case it hooks messages (of the specified hook type) sent only to a particular thread, or it may be global (or system-wide), in which case it hooks all messages (of the specified hook type) throughout the system. Hooks of some types can be set for either individual threads or globally, whereas other hook types (such as journal hooks) must be global.  
   
  The difference between thread-specific hooks and global hooks is quite significant. In the first place, global hooks reduce performance on a system-wide basis and are usually used for debugging purposes.  
   
  More seriously, a global hook procedure will need to be called from any thread throughout the system that receives an appropriate message type. Hence, the hook procedure must be placed in a module that is accessible to all threads in the system that is, a DLL.  
   
  Indeed, whenever a thread in one process sets a hook on a thread in another process, the issue of cross-process communication must be addressed. To illustrate, imagine that Thread A in Process A wants to set a mouse hook on Thread B in Process B. Thread A must call the SetWindowsHookEx function (discussed soon) to  
Page 323
   
  0323-01.gif  
   
  Figure 19-1.
Windows hooks
 
   
  set the hook, passing this function the thread ID of Thread B. However, it is the hook procedure in Thread B that is called when a mouse message is sent to Thread B.  
   
  This raises two issues: how does Thread A get the hook procedure into the address space of Process B, and, once this is done, how does Thread A get the results of any processing done by the hook procedure in Process B?  
   
  The answer to the first question is that Windows takes care of this when Thread A calls SetWindowsHookEx. But for the system to do so, the hook procedure must be  
Page 324
   
  in a DLL, so that Windows can inject (map) that DLL into the address space of Process B. (We will discuss DLL injection in detail in Chapter 20, DLL Injection and Foreign Process Access.) Unfortunately, this takes global hooks out of the realm of Visual Basic, because we cannot write a traditional DLL in VB.  
   
  Thus, a global hook can potentially cause the injection of a DLL into every existing process space. Fortunately, if the DLL can be loaded at its default base address in each case, then it is not necessary to commit additional physical memory to a new virtual instance of the DLL.  
   
  Also, the official documentation is a little vague as to when these DLLs will be released. Clearly, a process will not call the API function FreeLibrary to unload a DLL that it is not aware is even loaded! However, Richter says in his book:  
  When a thread calls the UnhookWindowsHookEx function, the system cycles through its internal list of processes into which it had to inject the DLL and decrements the DLL's lock count. When this lock count reaches 0, the DLL is automatically unmapped from the process's address space.  
   
  Indeed, some experimentation seems to bear this out. Injected DLLs appear to disappear in a ghostly fashion when no longer needed. Nevertheless, global hooks should be used with circumspection.  
   
  Our plan now is to discuss the general principles of Windows hooks and then implement a thread-specific hook entirely within VB. Then, with the help of a simple DLL written in VC++, we will implement a global hook. In Chapter 20, we will discuss the process of DLL injection in more detail.  
 
  Setting the Hook  
   
  To set a Windows hook, we use the SetWindowsHookEx function:  
 
  HHOOK SetWindowsHookEx(
   int idHook,          // type of hook to install
   HOOKPROC lpfn,       // address of hook procedure
   HINSTANCE hMod,      // handle to application instance
   DWORD dwThreadId     // identity of thread to install hook for
);
 
   
  or, in VB:  
 
  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
 
   
  If successful, the SetWindowsHookEx function returns a handle to the newly created hook. The idHook parameter specifies the type of hook to install. We will  
Page 325
   
  discuss some hook types soon. If the hook is intended to be thread-specific, then the dwThreadId parameter is the thread ID of the target thread. If the hook is intended to be global, then dwThreadId should be set to NULL (0). The values of the other two parameters depend upon the scope of the hook. There are two possibilities.  
   
  If the hook is a local hook, that is, if it is a thread-specific hook for a thread in the process that is calling SetWindowsHookEx, then the hook procedure will also be in the calling process. Hence, it can be referred to by its address. In this case, lpfn is the address of the hook procedure and hMod is NULL (0).  
   
  On the other hand, if the thread is thread-specific for a foreign thread, or if the hook is global, then lpfn is the address of the hook procedure within the DLL that contains that procedure, and hMod is the instance handle of this DLL, that is, the base address of this DLL. To be absolutely clear, these values (lpfn and hMod) will, of course, be for the copy of the DLL that is loaded in the calling process. However, Windows will translate these values to the correct values for each process that is forced (by the hook) to load the DLL. Put another way, Windows will use the base address of the DLL and the address of the hook procedure in the calling process to determine the offset of the hook procedure within the DLL:  
 
  offset of hook proc = address of hook proc - base address of DLL  
   
  This offset can be used to find the hook procedure regardless of where the DLL is loaded into a given process space.  
   
  When an application no longer requires a hook, it should call UnhookWindowsHookEx:  
 
  BOOL UnhookWindowsHookEx(
   HHOOK hhk // handle to hook procedure to remove
);
 
 
  Hook Procedures  
   
  Hook procedures have the form:  
 
  LRESULT CALLBACK HookProc(
   int nCode,
   WPARAM wParam,
   LPARAM lParam
);
 
   
  where nCode is a hook code that depends upon the hook type. The other parameters also depend on the hook type, but generally contain information about the message. For instance, wParam may be the message number and lParam may be a pointer to further message information.  
Page 326
 
  Hook Types  
   
  Windows implements more than a dozen type of hooks (as shown in Figure 19-1). Note that some hooks are allowed to modify a message, whereas others can only examine the message. Here is a sampling:  
 
  WH_CALLWNDPROC
Windows calls a WH_CALLWNDPROC hook procedure before passing a message generated by a call to SendMessage to the receiving window procedure. Note that this hook is allowed to examine a hooked message, but not modify it.
 
 
  WH_CALLWNDPROCRET
Windows calls the WH_CALLWNDPROCRET hook procedure after the window procedure has processed a message generated by SendMessage. This hook works only within the process that generated the message. This type of hook also gives information about the results of the message-processing by the window procedure.
 
 
  WH_GETMESSAGE
Windows calls the WH_GETMESSAGE hook procedure when the GetMessage function has retrieved a message from a thread's message queue, but before passing the message to the destination window procedure. This type of hook is allowed to modify hooked messages.
 
 
  WH_JOURNALRECORD and WH_JOURNALPLAYBACK
Windows calls the WH_JOURNALRECORD hook procedure when a message is removed from the system message queue. Thus, it monitors hardware (mouse and keyboard) messages only. This hook is generally used, in conjunction with the WH_JOURNALPLAYBACK hook, to record and later play back user input. The WH_JOURNALRECORD hook is a global hook it cannot be used to hook an individual thread. A WH_JOURNALRECORD hook procedure must not modify hooked messages.
 
 
  WH_KEYBOARD
Windows calls the WH_KEYBOARD hook procedure when an application calls GetMessage (or PeekMessage) and the retrieved message is a keyboard message (WM_KEYUP or WM_KEYDOWN).
 
 
  WH_MOUSE
Windows calls the WH_MOUSE hook procedure when an application calls GetMessage (or PeekMessage) and the retrieved message is a mouse message.
 
 
  WH_SHELL
Windows calls the WH_SHELL hook procedure when the Windows shell application is about to be activated and when a top-level window is created or destroyed.
 
Page 327
 
  Hook Chains  
   
  It is important to understand that more than one hook of a particular type may exist in the system at any given time. Indeed, several applications may have installed, say, WH_MOUSE hooks. For this reason, Windows maintains a separate hook chain for each type of hook. A hook chain is just a list of pointers to the hook procedures for that hook type.  
   
  When a message occurs that is associated with a particular type of hook, Windows passes that message to the first (most recently created) hook procedure in the hook chain for that hook type. It is up to the hook procedure to determine what to do with the message. However, as noted earlier, some hooks are allowed to alter or delete messages, whereas others are not.  
   
  If a hook procedure wants to (or must) pass the message along the hook chain, it calls the function CallNextHookEx:  
 
  LRESULT CallNextHookEx(
   HHOOK hhk,       // handle to current hook (returned by SetWindowsHookEx)
   int nCode,       // hook code passed to hook procedure
   WPARAM wParam,   // value passed to hook procedure
   LPARAM lParam    // value passed to hook procedure
);
 
   
  As you can see, this basically just passes on the message information to the next hook procedure in the hook chain.  
 
  Example: A Local Hook  
   
  Implementing a local hook is not hard. Figure 19-2 shows the main window of our example utility rpiLocalHook.  
   
  0327-01.gif  
   
  Figure 19-2.
A local mouse hook example
 
Page 328
   
  The entire code behind the form is:  
 
  Option Explicit

Sub EnableHook()
   ghHook = SetWindowsHookEx(WH_MOUSE, AddressOf MouseProc, 0&, App.ThreadID)
End Sub

Sub DisableHook()
   UnhookWindowsHookEx ghHook
End Sub

Private Sub cmdDisable_Click()
   DisableHook
   ghHook = 0
   lblIsHooked = "Not Hooked"
   lblIsHooked.ForeColor = &HO
End Sub

Private Sub cmdEnable_Click()
   EnableHook
   lblIsHooked = "Hooked"
   lblIsHooked.ForeColor = &HFF
End Sub

Private Sub cmdExit_Click()
   Unload Me
End Sub

Private Sub Form_Unload(Cancel As Integer)
   If ghHook <> 0 Then DisableHook
End Sub
 
   
  The action here is in the EnableHook procedure, wherein lies the call to SetWindowsHookEx:  
 
  ghHook = SetWindowsHookEx(WH_MOUSE, AddressOf MouseProc, 0& App.ThreadID)  
   
  The supporting standard module contains the mouse hook procedure. Example 19-1 is the entire code for this module.  
   
  Example 19-1. SetWindowsHookEx  
   
  Option Explicit

Public ghHook As Long
Dim mhs As MOUSEHOOKSTRUCT
Dim sText As String

Public Const WH_MOUSE = 7

Type POINTAPI
     x As Long
     y As Long
End Type
 
Page 329
   
  Example 19-1. SetWindowsHookEx (continued)  
   
  Type MOUSEHOOKSTRUCT
     pt As POINTAPI
     hwnd As Long
     wHitTestCode As Long
     dwExtraInfo As Long
End Type

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

Declare Function CallNextHookEx Lib  user32  ( _
   ByVal ghHook As Long, _
   ByVal ncode As Long, _
   ByVal wParam As Integer, _
   ByVal lParam As Long _
) As Long

Declare Function UnhookWindowsHookEx Lib  user32  ( _
   ByVal ghHook As Long _
) As Long

Declare Sub CopyMemory Lib  kernel32  Alias  RtlMoveMemory  ( _
   Destination As Any, _
   Source As Any, _
   ByVal Length As Long)

-----
Public Function MouseProc (ByVal ncode As Long, ByVal wParam As Long, _
   ByVal lParam As Long) As Long

 Documentation says to do this
If ncode < 0 Then
     Forward message and get out
   MouseProc = CallNextHookEx(ghHook, ncode, wParam, lParam)
   Exit Function
End If

 Get MOUSEHOOKSTRUCT pointed to by lParam
CopyMemory mhs.pt.x, ByVal lParam, LenB(mhs)

 Fill text box with message data
sText =  MsgID:   & wParam
sText = sText & vbCrLf &  For window:   & Hex$ (mhs.hwnd)
sText = sText & vbCrLf &  X:   & mhs.pt.x &  Y:   & mhs.pt.y
sText = sText & vbCrLf &  Hit test:   & GetConstant(mhs.wHitTestCode)
frmMain.txtMsg.Text = sText

 Forward to next hook
 
Page 330
   
  Example 19-1. SetWindowsHookEx (continued)  
   
  MouseProc = CallNextHookEx(ghHook, ncode, wParam, lParam)

End Function

-----
Function GetConstant (vValue As Variant) As String

 Function returns name of constant for given value.

Dim sName As String

Select Case vValue

   Case 18
      sName =  HTBORDER
   Case 15
     sName =  HTBOTTOM
   Case 16
      sName =  HTBOTTOMLEFT
   Case 17
      sName =  HTBOTTOMRIGHT
   Case 2
      sName =  HTCAPTION
   Case 1
      sName =  HTCLIENT
   Case -2
      sName =  HTEFROR
   Case 4
      sName =  HTGROWBOX
   Case 6
      sName =  HTHSCROLL
   Case 10
      sName =  HTLEFT
   Case 9
      sName =  HTMAXBUTTON
   Case 5
      sName =  HTMENU
   Case 8
      sName =  HTMINBUTTON
   Case 0
      sName =  HTNOWHERE
   Case 11
      sName =  HTRIGHT
   Case 3
      sName =  HTSYSMENU
   Case 12
      sName =  HTTOP
   Case 13
      sName =  HTTOPLEFT
   Case 14
      sName =  HTTOPRIGHT
   Case -1
 
Page 331
   
  Example 19-1. SetWindowsHookEx (continued)  
   
        sName =  HTTRANSPARENT
   Case 7
      sName =  HTVSCROLL

End Select

GetConstant = sName
End Function
 
   
  Note that lParam points to a MOUSEHOOKSTRUCT structure variable that contains message data. The dwHitTestCode data is interesting, since it describes where the mouse pointer is when the message is generated: on a window border, on a scrollbar, on a menu, and so on. For more on these values, see the MSDN documentation for the message WM_NCHITTEST.  
 
  Example: A Global Hook  
   
  As discussed earlier, to set a global hook, we need to make the hook procedure accessible to all threads, which means placing the hook procedure in a DLL. Since VB cannot produce traditional DLLs, we need to use another programming environment, such as VC++.  
   
  The rpiGlobalHook application consists of two parts. The global hook is actually implemented in a pair of modules a form module called frmRpiHook and a standard module called basRpiHook. The form frmRpiHook is shown in Figure 19-3. This form is intended to be loaded but remain hidden. Thus, it functions somewhat like a class module, but also provides a window (the form itself) that can be subclassed.  
   
  0331-01.gif  
   
  Figure 19-3.
The global hook form
 
   
  These two modules can be added to any VB project in order to implement a global mouse hook. The rpiGlobalHook application also contains this installing project, in the form of the frmMain form shown in Figure 19-4 and the basMain standard module.  
   
  Remember that our goal here is to demonstrate the technique of creating a global hook, not to produce a commercial-quality product. Such a product could be created as an ActiveX control. Also, we have not included any error checking in this simple example.  
Page 332
   
  0332-01.gif  
   
  Figure 19-4.
A global mouse hook example
 
   
  Figure 19-5 shows the entire project.  
   
  0332-02.gif  
   
  Figure 19-5.
The global hook project
 
   
  Here are the steps shown in Figure 19-5:  
   
  1. When the user clicks the Enable Hook button, the installing project loads the form frmRpiHook shown in Figure 19-3.  
   
  2. The Load event of frmRpiHook subclasses itself by calling SetWindowLong and passing it the address of the window procedure in basRpiHook.  
   
  3. The installing program then calls the SetGlobalHook procedure, which is implemented in the frmRpiHook form.  
   
  4. This function calls the DLL function rpiSetGlobalMouseHook,  
   
  passing it the handle of the form (its own handle) and the message IDs of the  
   
  messages to process. Note that you must put this DLL in your Windows System  
   
  directory or change the Lib clause in the appropriate declarations  
   
  in the source code before this program will work.  
Page 333
   
  5. The DLL function rpiSetGloablMouseHook sets up the global mouse hook by calling SetWindowsHookEx, thus setting up the hook's mouse procedure in the DLL.  
   
  6. When a mouse message is generated, the system injects the DLL into the target process space, and the message is passed to the mouse procedure in the target process. If the message ID matches the lMsgID argument, the mouse procedure sends a WM_COPYDATA message to the window procedure of the subclassed form frmRpiHook.  
   
  7. The window procedure calls the function rpiMouseHookProc, which is defined by the user in the installing project.  
   
  Note that steps 1 5 (and step 7) take place in the installing application's process, and Step 6 takes place in the process that is the target of the mouse message, but the WM_COPYDATA message is marshalled from the target process to the installing process.  
   
  Now let us look at some of the actual code.  
   
  frmRpiHook  
   
  The code behind the form frmRpiHook is:  
 
  Option Explicit

Private mhHook As Long
Private mhPrevWndProc As Long

' -----
Public Property Get hHook() As Long
   hHook = mhHook
End Property

' -----
Public Property Get hPrevWndProc() As Long
   hPrevWndProc = mhPrevWndProc
End Property

' -----
Public Function SetGlobalHook (ByVal lMsgID As Long) As Long
' Call DLL to set hHook property
' Returns handle to hook (0 indicates error)
mhHook = rpiSetGlobalMouseHook(Me.hwnd, lMsgID)
SetGlobalHook = mhHook
End Function

' -----
Public Function FreeGlobalHook() As Boolean
   FreeGlobalHook = (rpiFreeGlobalMouseHook <> 0)
End Function
 
 

Page 334
 
  ' -----
Private Sub Form_Load()
' Subclass the form
mhPrevWndProc = SetWindowLong(Me.hwnd, GWL_WNDPROC, AddressOf WindowProc)
End Sub

' -----
Private Sub Form_Unload(Cancel As Integer)

' Remove subclassing
SetWindowLong Me.hwnd, GWL_WNDPROC, mhPrevWndProc

' When unloading form, check for a valid hook and free it
If hHook <> 0 Then FreeGlobalHook
Set frmRpiHook = Nothing

End Sub
 
   
  The form has two properties:  
 
  hHook
The handle of the hook (if set). Read-only.
 
 
  hPrevWndProc
Handle to form's original window procedure. Read-only.
 
   
  And two methods:  
 
  SetGlobalHook
Calls DLL to set hook for messages.
 
 
  FreeGlobalHook
Calls DLL to free hook.
 
   
  The SetGlobalHook method is:  
 
  Public Function SetGlobalHook (ByVal lMsgID As Long) As Long
' Call DLL to set hHook property
' Returns handle to hook (0 indicates error)
mhHook = rpiSetGlobalMouseHook(Me.hwnd, lMsgID)
SetGlobalHook = mhHook
End Function
 
   
  This method calls the rpiSetGlobalMouseHook function in our DLL, passing it the handle of the subclassed form (this form) and the message IDs of the messages that we want to process. Similarly, the FreeGlobalHook method calls the rpiFreeGlobalMouseHook DLL function. We will take a look at these DLL functions soon.  
   
  basRpiHook  
   
  The accompanying standard module basRpiHook contains the required declarations, along with the window procedure for the subclassed form. It is necessary to put the window procedure in a standard module because the VB AddressOf  
Page 335
   
  operator requires its argument to be in a standard module. If it weren't for this, we could place the entire project in the form module. What a shame.  
   
  Here is the code for the window procedure:  
 
  Public Function WindowProc(ByVal hwnd As Long, ByVal iMsg As Long, _
   ByVal wParam As Long, ByVal lParam As Long) As Long

' Window procedure for subclassed form.

If iMsg = WM_COPYDATA Then
   ' lParam has pointer to COPYDATASTRUCT structure.
   ' Get it out immediately

   ' First copy COPYDATASTRUCT
   CopyMemory cds.dwData, ByVal lParam, LenB(cds)

   ' Then copy MOUSEHOOKSTRUCT
   CopyMemory mhs.pt.x, ByVal cds.lpData, LenB(mhs)

   ' Call user-implemented mouse hook procedure
   rpiMouseHookProc mhs.hwnd, cds.dwData, mhs.pt.x, _
      mhs.pt.y, mhs.wHitTestCode, mhs.dwExtraInfo

   ' Do not pass message this on to form
   Exit Function

End If

' Call original window procedure
WindowProc = CallWindowProc(frmRpiHook.hPrevWndProc, _
   hwnd, iMsg, wParam, lParam)

End Function
 
   
  When the global mouse hook is set, all mouse messages are observed by our DLL. For each message, the DLL sends a WM_COPYDATA message to the window procedure WindowProc, which processes the incoming message, calls the user-implemented rpiMouseHookProc (in the installing project), and kills this message. All other messages are passed on to the default window procedure for the form.  
   
  The data in the parameters of WindowProc is as follows:  
 
  hwnd
The handle of the subclassed form. This is not of much use to us.
 
 
  iMsg
The message number, which is always WM_COPYDATA
 
 
  wParam
The handle of the sending window, which is always 0 because there is no sending window (the DLL does the sending).
 
Page 336
 
  lParam
A pointer to a variable of type COPYDATASTRUCT, marshalled from the target process by the call to SendMessage using WM_COPYDATA
 
   
  The COPYDATASTRUCT is:  
 
  Type COPYDATASTRUCT
   dwData As Long   ' message ID of the hooked mouse message
   cbData As Long   ' count of bytes pointed to by lpData--equal to 20
   lpData As Long   ' pointer to variable of type MOUSEHOOKSTRUCT
End Type
 
   
  The MOUSEHOOKSTRUCT structure is:  
 
  Type MOUSEHOOKSTRUCT
   pt As POINTAPI           ' POINT structure with mouse coordinates
   hwnd As Long             ' handle of the window receiving the mouse message
   wHitTestCode As Long     ' describes location of mouse
   dwExtraInfo As Long     ' extra info with message, normally 0
End Type
 
   
  Finally, the DLL expects the user-implemented mouse hook procedure to have the form:  
 
  Public Sub rpiMouseHookProc(ByVal hwnd As Long, _
   ByVal iMsg As Long, _
   ByVal x As Long, _
   ByVal y As Long, _
   ByVal wHitTestCode As Long, _
   ByVal dwExtraInfo As Long)
 
   
  rpiGlobalHook.dll  
   
  Now for the DLL, which is written in VC++. Fortunately, it is not complicated. The source code is also on the accompanying CD.  
   
  First, here is the rpiSetGlobalHook procedure, which essentially just calls SetWindowHookEx. It also saves the handle to the hook, the handle to the subclassed form, and the message IDs to process in shared variables; that is, variables that are shared by all processes that load the DLL.  
 
  int WINAPI rpiSetGlobalMouseHook(int hSubclassedForm, int iMsgID)
{
   // Sets a mouse hook and returns its handle, or 0 for failure.
   // Input is handle to subclassed form and message ID's to hook.
   // Sets the shared variable ghSubclassedForm containing handle
   // of subclassed form, the msgID and the ghHook variable.

   // If hook already set, do not set it again
   if (!ghHook == 0)
      return 0;

   ghHook = SetWindowsHookEx(
      WH_MOUSE,
 
 

Page 337
 
        (HOOKPROC)MouseProc,
      hDLLInst,
      0);

      // Save subclassed form handle
      ghSubclassedForm = (HANDLE)hSubclassedForm;

      // Save the message ID(s)
      lMsgID = iMsgID;

      return (int)ghHook;
}
 
   
  The rpiFreeGlobalMouseHook is even simpler:  
 
  int WINAPI rpiFreeGlobalMouseHook()
{
   HRESULT hr;

   // Free hook hh. Return 0 on failure.
   hr = UnhookWindowsHookEx(ghHook);
   ghHook = 0;

   return hr;
}
 
   
  Finally, we come to the mouse procedure for the hook. As mentioned earlier, this procedure gathers up the mouse message data and sends it off to the subclassed form in the VB project using a WM_COPYDATA message, which is automatically marshalled by Windows. It then calls the next hook procedure in the hook chain.  
 
  LRESULT WINAPI MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
   // If nCode<0 then do not process the message
   if (nCode < 0)
        return CallNextHookEx(ghHook, nCode, wParam, lParam);

   // Check for message ID filter
   if ( (lMsgID == 0)    (lMsgID & wParam) ) //   is logical OR, & is bitwise AND
{
      // Set up data to copy in message
      copydata.dwData = wParam;      //message ID here
      copydata.lpData = (MOUSEHOOKSTRUCT*) lParam;
      copydata.cbData = sizeof(MOUSEHOOKSTRUCT);

      // Send message
      if (!ghSubclassedForm == 0) {
      SendMessage(ghSubclassedForm, WM_COPYDATA,
         NULL, (LPARAM) &(copydata.dwData) );
      }
   }

   return CallNextHookEx(ghHook, nCode, wParam,lParam);
}
 
Page 338
   
  The Installing Application  
   
  The code for the installing process's form, that is, the code behind the form in Figure 19-3, is:  
 
  Option Explicit

Dim hMouseHook As Long
Const WM_LBUTTONDOWN = &H201
Const WM_RBUTTONDOWN = &H204

' -----
Private Sub cmdEnable_Click()

' Load hook form but no need to show
Load frmRpiHook

' Set hook

" frmRpiHook.SetGlobalHook WM_LBUTTONDOWN + WM_RBUTTONDOWN
' For all mouse messages
frmRpiHook.SetGlobalHook 0

' Save handle to hook for later freeing
hMouseHook = frmRpiHook.hHook

lblIsHooked = "Hooked"
lblIsHooked.ForeColor = &HFF

End Sub

' -----
Private Sub cmdDisable_Click()
   frmRpiHook.FreeGlobalHook
   hMouseHook = 0
   lblIsHooked = "Not Hooked"
   lblIsHooked.ForeColor = &H0
End Sub

' -----
Private Sub cmdExit_Click()
   If hMouseHook <> 0 Then frmRpiHook.FreeGlobalHook
   Unload Me
End Sub

' -----
Private Sub Form_Unload(Cancel As Integer)
   ' This will remove subclassing and free hook
   Unload frmRpiHook
End Sub
 
   
  Finally, the user code for processing the mouse messages is in a standard module for the installing application. This procedure processes the mouse information in the same manner as that of the local hook example:  
Page 339
 
  Public Sub rpiMouseHookProc(ByVal hwnd As Long, ByVal iMsg As Long, _
   ByVal x As Long, ByVal y As Long, ByVal wHitTestCode As Long, _
   ByVal dwExtraInfo As Long)

Dim sText As String

' Fill text box with message data
sText = "MsgID: " & iMsg
sText = sText & vbCrLf & "For window: " & Hex$ (hwnd)
sText = sText & vbCrLf & "X: " & X & "Y: " & y
sText = sText & vbCrLf & "Hit test: " & GetConstant(wHitTestCode)
frmMain.txtMsg.Text = sText

frmMain.txtMsg = sText

End Sub
 
   
  Unlike the local hook example, we can now move the mouse over any window in the system and hook the generated mouse messages.


WIN32 API Programming with Visual Basic
Win32 API Programming with Visual Basic
ISBN: 1565926315
EAN: 2147483647
Year: 1999
Pages: 31
Authors: Steven Roman

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net