Difference between revisions of "Modularised subclassing"

From HashVB
Jump to: navigation, search
m (Added VB6 header)
Line 1: Line 1:
{{VB6}}
 
In an extension of this method, it is possible to 'modularise' subclassing and have your subclassing handled inside a form or class module.
 
  
To do this you will need an extra class module, called ISubclass:
 
 
  Public Function WndProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long, bHandled As Boolean, RetVal As Boolean) As Long
 
 
 
  End Function
 
 
This class looks useless, and, on its own, is.  However, what you have created with this class is an interface that can be 'implemented' (a form of inheritance supported by VB6) into other class modules, and forms.  You could omit this and opt for a simpler, but less flexible, solution, but if you are familiar with inheritance then the benefits of using this particular method will be apparent.  If you are not familiar with implementation inheritance then there are plenty of tutorials out there - http://www.google.co.uk/search?hl=en&safe=off&q=vb6+implementation+inheritance&spell=1
 
 
Now for the module coding:
 
The module will work much in the same way as the previous example, except for the fact that you are not passed a pointer or a reference to your module function, which is the Window Procedure for whatever window you are subclassing.  So how do you get access to your object?  You use ObjPtr and SetProp as follows:
 
 
First things first, we will have to write a function to subclass a window (note: API declarations are not shown here).
 
 
  Public Sub SubclassWnd(ByVal hwnd As Long, oHandler As ISubclass)
 
      Dim pOldWndProc As Long
 
     
 
      pOldWndProc = SetWindowLong(hwnd, GWL_WNDPROC, AddressOf fnWndProc)
 
      SetProp hwnd, "pOldWndProc", pOldWndProc
 
      SetProp hwnd, "pHandler", ObjPtr(oHandler)
 
  End Sub
 
 
What's going on here?  The sub is passed a reference to an instance of ISubclass (which is really a reference to your class/form that is type-compatible with ISubclass because it implements it).  Once the window is subclassed in the normal way, we use SetProp to give the window a property telling us what the old window proc address is, and one which is a pointer to our ISubclass instance.  As we are passed the hwnd of the window in the Window Procedure, we can then use the properties of the window to retrieve the ISubclass object and call methods on it.
 
 
Before we proceed with the Window Procedure, let's just make a quick sub to un-subclass the window:
 
 
  Public Sub UnSubclassWnd(ByVal hwnd As Long)
 
      Dim pOldWndProc As Long
 
     
 
      pOldWndProc = GetProp(hwnd, "pOldWndProc")
 
      SetWindowLong hwnd, GWL_WNDPROC, pOldWndProc
 
      RemoveProp hwnd, "pOldWndProc"
 
      RemoveProp hwnd, "pHandler"
 
  End Sub
 
 
As you can see, the use of GetProp is demonstrated here in order to get the old Window Proc address and set it back to that.  RemoveProp is also used to clean up a bit.
 
 
On with the Window Proc:
 
 
  Private Function fnWndProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
 
      Dim pOldWndProc As Long
 
      Dim pHandler As Long
 
      Dim oHandler As ISubclass
 
      Dim bHandled As Boolean
 
      Dim bRet As Boolean
 
      Dim RetVal As Long
 
     
 
      pOldWndProc = GetProp(hwnd, "pOldWndProc")
 
      pHandler = GetProp(hwnd, "pHandler")
 
     
 
      If pHandler <> 0 Then
 
          CopyMemory oHandler, pHandler, 4&
 
          RetVal = oHandler.WndProc(hwnd, uMsg, wParam, lParam, bHandled, bRet)
 
          ZeroMemory oHandler, 4&
 
      End If
 
     
 
      If Not bHandled Then
 
          If pOldWndProc <> 0 Then
 
              If Not bRet Then
 
                  fnWndProc = CallWindowProc(pOldWndProc, hwnd, uMsg, wParam, lParam)
 
              Else
 
                  Call CallWindowProc(pOldWndProc, hwnd, uMsg, wParam, lParam)
 
                  fnWndProc = RetVal
 
              End If
 
          Else
 
              If Not bRet Then
 
                  fnWndProc = DefWindowProc(hwnd, uMsg, wParam, lParam)
 
              Else
 
                  Call DefWindowProc(hwnd, uMsg, wParam, lParam)
 
                  fnWndProc = RetVal
 
              End If
 
          End If
 
      Else
 
          If Not bRet Then
 
              fnWndProc = 0
 
          Else
 
              fnWndProc = RetVal
 
          End If
 
      End If
 
  End Function
 
 
While this may look a little daunting at first, the only complex thing that is going on here really is the usage of GetProp and CopyMemory to get an ISubclass instance.  This works exactly the same as getting the old Window Proc address as we did in the unsubclassing procedure, and exactly the same as getting an object reference from a pointer in the previous example.
 
 
All of the rest of the function is simply to handle return values and the 'bHandled' variable that is passed to ISubclass.WndProc.  If ISubclass.WndProc returns with bHandled true then CallWindowProc and DefWindowProc are not called, otherwise one or the other is called depending on whether or not GetProp returned to us a proper address for the old Window Proc.  In all cases, if bRet is true when ISubclass.WndProc returns then the return value of ISubclass.WndProc is returned with fnWndProc.  Otherwise the return of CallWindowProc/DefWindowProc or 0 is returned.
 

Revision as of 16:00, 31 December 2005