18. Windows Subclassing

Page 317
  18. Windows Subclassing  
   
  In the next few chapters, we want to discuss some more advanced topics, including subclassing windows, installing Windows hooks, and accessing memory in foreign processes through DLL injection. We begin in this chapter with subclassing.  
 
  Subclassing a Window or Window Class  
   
  We have already seen that the API function SetWindowLong can be used to change a window's style. It can also be used to change the window procedure for any window. The declaration is:  
 
  LONG SetWindowLong(
   HWND hWnd,       // handle of any window
   int nIndex,      // index of value to set
   LONG dwNewLong   // new value
);
 
   
  In VB, this is:  
 
  Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" ( _
   ByVal hWnd As Long, _
   ByVal nIndex As Long, _
   ByVal dwNewLong As Long _
) As Long
 
   
  We can change the window procedure for a window by setting the nIndex parameter to one of the following values:  
 
  GWL_WNDPROC
Sets a new address for the window procedure
 
 
  DWL_DLGPROC (for dialog boxes)
Sets a new address for the dialog box procedure (Every dialog box has a dialog procedure, rather than a window procedure. However, the two are similar.)
 
Page 318
   
  Note that the return value of SetWindowLong is the address of (i.e., handle to) the previous window or dialog procedure. This is important.  
   
  Using this function, we can replace the window procedure for a particular window by a procedure of our own making. However, we have no way of knowing which messages were processed by the original window procedure. This makes it vital to be able to call the original procedure after our procedure is done doing its mischief.  
   
  Fortunately, the CallWindowProc function is made to order for this purpose:  
 
  LRESULT CallWindowProc(
   WNDPROC lpPrevWndFunc,       // pointer to previous procedure
   HWND hWnd,                   // handle to window of this class
   UINT Msg,                    // message
   WPARAM wParam,               // first message parameter
   LPARAM lParam                // second message parameter
);
 
   
  This function passes a message to the window procedure indicated by the lpPrevWindowProc argument. This parameter should be set to the return value of SetWindowLong. thereby passing the message to the original window procedure for the class associated with the window.  
   
  The process of replacing a window procedure by another procedure and passing unprocessed messages on to the original procedure is called subclassing the window. It is important to emphasize that the new window procedure must call the original procedure to process all messages that are not processed by the new procedure.  
   
  Let us emphasize that SetWindowLong affects only the window whose handle is placed in the hWnd parameter. To subclass an entire class, we can use SetClassLong:  
 
  DWORD SetClassLong(
  HWND hWnd,        // handle of window of the class
  int nIndex,       // index of value to change
  LONG dwNewLong    // new value
);
 
   
  and set nIndex to GCL_WNDPROC. In this case, all windows of the specified class in the calling process only that are created after the call to SetClassLong are subclassed.  
   
  Superclassing  
   
  Incidentally, there is a related concept called superclassing. This refers to the process of creating a new class that processes some messages and then calls the window procedure of another class. The new class is called a superclass of the original class. This may sound the same as subclassing, but the difference is that in subclassing, we do not create a new class, but just modify an existing window or class.  
Page 319
 
  Example: Subclassing the VB Checkbox Class  
   
  Subclassing under Visual Basic is not difficult, but it must be done carefully since any misstep will almost surely lead to the well-known General Protection Fault.  
   
  To illustrate the subclassing process, the following example subclasses a VB checkbox control. As you know, clicking with the mouse on a VB checkbox normally cycles the value of the checkbox between the checked (value 1) and unchecked (value 0) states only it does not put the checkbox in the grayed state (value 2). However, we can easily subclass the VB checkbox control so that clicking on the mouse will cycle the value from checked to grayed to unchecked.  
   
  Figure 18-1 shows the main window of the rpiSubClass example (whose source code is on the CD).  
   
  0319-01.gif  
   
  Figure 18-1.
Subclassing example
 
   
  Clicking the Subclass Checkbox button runs the following procedure:  
 
  Sub SubClass()

' Subclass check box
hPrevWndProc = SetWindowLong(Check1.hwnd, GWL_WNDPROC, AddressOf WindowProc)

' For subclassing the entire class
"hPrevWndProc = SetClassLong(Check1.hwnd, GCL_WNDPROC, AddressOf WindowProc)

If hPrevWndProc <> 0 Then
   bIsSubclassed = True
 
 

Page 320
 
     lblStatus = "Subclassed"
End If

End Sub
 
   
  Clicking the Remove Subclassing button executes the Remove procedure:  
 
  Sub Remove()

Dim lret As Long

' Remove Subclass if appropriate
If bIsSubclassed Then

   lret = SetWindowLong(Check1.hwnd, GWL_WNDPROC, hPrevWndProc)

   ' For Unsubclassing the entire class
   ' 'lret = SetClassLong(Check1.hwnd, GCL_WNDPROC, hPrevWndProc)

   bIsSubclassed = False
   lblStatus = "Not Subclassed"
End If

End Sub
 
   
  The window procedure is:  
 
  Public Function WindowProc(ByVal hwnd As Long, ByVal iMsg As Long, _
   ByVal wParam As Long, ByVal lParam As Long) As Long

Select Case iMsg
   ' Process left mouse button and exit
   Case WM_LBUTTONDOWN

      ' Search through controls collection
      ' to see if this handle belongs to a checkbox
      For Each ctl In frmSubClass.Controls
         If TypeName(ctl) = "CheckBox" Then
            If ctl.hwnd = hwnd Then
               ' Change the value
               If ctl.Value <= 1 Then
        ctl.Value = ctl.Value + 1
               Else
                  ctl.Value = 0
               End If
               ' No default processing
               Exit Function
            End If
         End If
      Next
End Select

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

End Function
 
Page 321
   
  This window procedure processes the WM_LBUTTONDOWN message by checking to see if the control whose handle is hwnd is actually a checkbox. We must be very careful here, because some controls (such as the label control) do not have handles and will cause an exception when we try to reference the hWnd property.  
   
  Try running the program and clicking on Check1 a few times. Then click on the Subclass Checkbox button and click Check1 a few more times. Then click on the Remove Subclassing button.  
   
  Note that since we are using SetWindowLong, the subclassing applies only to the checkbox Check1, because this is the control whose handle is placed in the hWnd parameter. There is no effect on Check2.  
   
  Now comment out the two references to SetWindowLong and uncomment the references to SetClassLong. If you run the program and try clicking on the two checkboxes, you will notice that there is no effect on either one. This is because these controls existed prior to the call to SetClassLong. However, the subclassing will work on the checkbox created by hitting the Create New Checkbox button!  
   
  In closing, allow me to suggest that both caution and restraint should be exercised when subclassing entire classes.  


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