Getting Started
Introduction
Sample Programs
IDEs
History
Advice
Mini-Tutorial
Tutorials
Code Snippets

Resources
Web Sites
More Tutorials
Forums
Vendors/Tools
Books
Magazines
Newsletters
NewsGroups
User Groups
Talk Shows
Blogs

Controls
Overview
Button
Check3State
Checkbox
ComboBox
Frame
Graphic
Image
ImageX
ImgButton
ImgButtonX
Label
Line
ListBox
ListView
Option
Progress Bar
Scrollbar
StatusBar
TAB
TextBox
Toolbar
TreeView

GBIC >> PowerBASIC >> Tutorials >> Threads

PowerBASIC Information Center Tutorials
These tutorials were written to help you get a quick, but thorough, understanding of PowerBASIC - the scope of the language as well as it's specific capabilities.

Introduction       Projects       Language           Messages       Functions           Advanced
  • Overview
  • Examples
  • IDE
  • Compilation
  • Distribution
  • Project Files
  • DDT Windows
  • Controls
  • Menus
  • Dialogs  
  • Help Files
  • Resources  
  • Templates  
  • Project Shell  
  • Syntax
  • Operators
  • Data Types
  • Variables
  • Scope
  • Declarations  
  • Procedures
  • Flow Control
  • Windows
  • Messages
  • Callbacks
  • Mouse
  • Keyboard
  • Dialogs
  • Controls
  • Subclassing
  • Arithmetic
  • Trig  
  • Strings
  • Arrays
  • Date/Time
  • Printing
  • Files
  • Folders
  • Keyboard
  • Mouse
  • Sound
  • System
  • Error Traps
  • Debugging
  • Objects
  • Graphics
  • Databases
  • API
  • DLLs
  • ASM
  • Threads
  • SubClassing
    Each window class (windows, dialogs, and common controls) has a default message handling function called the default window procedure. Subclassing is a way of intercepting the messages that would normally go to the default message handler.

    The purpose of intercepting those messages is to allow the programmer to customize the response of the window to the messages - customizing both the visual and functional aspects of the window.

    Subclassing works by temporarily changing the address of the default window class procedure to the address of a function within the PowerBASIC application (not a Callback function).

    If any messages are received that the programmer does not wish to handle, the message can then be forwarded to the original default window procedure for handling.

    Note that the need to subclass does not apply when a custom window class is created, since part of the creation is to write a custom window procedure. It is only in the predefined windows classes, such as dialogs and common controls, that the programmer does not have access to the default window procedure.

    Step-by-Step
    There are two API involved in subclassing, SetWindowLong and CallWindowProc. Additionally, one PowerBASIC function (CodePtr) is needed to provide the address of the substitute PowerBASIC application function. Here is some sample code for each of the steps, followed by examples of how to put all of the steps together into a complete PowerBASIC subclassing application.

    • SetWindowLong - changes default procedure
      Not only does it change the window procedure, it also returns the address of the old window procedure. Here's the code:

          OldProc& = SetWindowLong(hWnd&, %GWL_WNDPROC, NewProc&)
          

      OldProc& and NewProc& are the addresses of the old and new window procedures, respectively. See the next paragraph on how to get the new address (NewProc&) to use in the code.

    • CodePtr - returns address of any PowerBASIC function
      CodePtr is a PowerBASIC function which will return the address of any function within the application. Simply use it to get the NewProc& address that was used in the SetWindowLong example above. Here's the code.

          NewProc& = CodePtr(MyFunction)
          

    • CallWindowProc - send message to default window procedure
      The new window procedure has three options - do nothing, respond, or send the message to the original window procedure.

      Typically, a programmer selectively responds to incoming messages. Then, if a message is received for which a response is not made, the message is forwarded on to the original window procedure for taking the normal default action. Here's the code.

          CallWindowProc(OldProc&, hWnd, Msg, wParam, lParam)
          

    The New Window Procedure
    The new window procedure declaration must exactly match that of the default window procedure. Fortunately, all window procedures have exactly the same declaration.

    Here's the declaration code for a new window procedure.

        Function NewProc(ByVal hWnd As Long, ByVal Msg As Long, _
                        ByVal wParam As Long, ByVal lParam As Long) As Long
        End Function
    

    There's a lot of flexibility as to what goes inside the new window procedure, but in general the approach is to watch for a particular incoming message, take some action, then pass/not pass the message to the original window procedure for additional handling.

    Here's sample code of what might be placed in the new window procedure, in this case to be able to respond to a %WM_LButtonUp message.

        Function NewProc(ByVal hWnd As Long, ByVal Msg As Long, _
                        ByVal wParam As Long, ByVal lParam As Long) As Long
           Select Case Msg
              Case %WM_LButtonUp   'any mouse message might be here
                 'respond to left button up 
              Case Else
                 CallWindowProc(OldProc&, hWnd, Msg, wParam, lParam)
           End Select
        End Function
    

    The previous example assumes that the response to %WM_LButtonUp does not require any action to be taken by the default window procedure (OldProc&). In many cases, the action a programmer takes is in addition to default processing. In that case the code might look like this next example, which the incoming message is always sent to the original window procedure.

        Function NewProc(ByVal hWnd As Long, ByVal Msg As Long, _
                        ByVal wParam As Long, ByVal lParam As Long) As Long
           Select Case Msg
              Case %WM_LButtonUp   'any mouse message might be here
                 'respond to left button up 
                 'but additional response is needed
           End Select
           CallWindowProc(OldProc&, hWnd, Msg, wParam, lParam)
        End Function
    

    Finally, the CallWindowProc could be selectively used, such as in this example where two message types are intercepted - one which requires not further response, and one which does.

        Function NewProc(ByVal hWnd As Long, ByVal Msg As Long, _
                        ByVal wParam As Long, ByVal lParam As Long) As Long
           Select Case Msg
              Case %WM_LButtonDown
                 'respond to left button down - do not forward to OldProc&
              Case %WM_LButtonUp
                 'respond to left button up - then forward to OldProc&
                 CallWindowProc(OldProc&, hWnd, Msg, wParam, lParam)
              Case Else
                 CallWindowProc(OldProc&, hWnd, Msg, wParam, lParam)
           End Select
        End Function
    

    Complete Example
    Here's a simple application showing how a label might be subclassed to respond to a left mouse down message (%WM_LButtonDown).

    Normally, that message is sent directly to the default label window procedure and is not available to a PowerBASIC application. But with subclassing, the message can be intercepted and action taken. In this example, the message response is all that is required, so the message is not forwarded on to the default window procedure.

        #Compile Exe
        #Dim All
        #Include "win32api.inc"
    
        Global hDlg As Dword, OrigProc&, hLabel As Dword
    
        Function PBMain()
            Dialog New Pixels, 0, "Subclassing",300,300,150,100, _
                                      %WS_OverlappedWindow To hDlg
            Control Add Label, hDlg, 100, "Subclassed Label", _
                             10,20,120,20, %WS_Border Or %SS_Notify
            Control Add Label, hDlg, 102, "Not Subclassed Label", _
                      10,50,120,20, %WS_Border Or %SS_Notify Call LabelProc
            SubClassLabels
            Dialog Show Modal hdlg
        End Function
    
        Sub SubClassLabels
            Local NewProc&
            Control Handle hDlg, 100 To hLabel
            NewProc& = CodePtr(NewLabelProc)
            OrigProc& = SetWindowLong(hLabel, %GWL_WNDPROC, NewProc&)
        End Sub
    
        CallBack Function LabelProc() As Long
            If Cb.Msg = %WM_Command And Cb.CtlMsg = %WM_LButtonDown Then
               Control Set Text hDlg, 102, "Left Button Down"
            End If
        End Function
    
        Function NewLabelProc(ByVal hWnd As Long, ByVal Msg As Long, _
                   ByVal wParam As Long, ByVal lParam As Long) As Long
            Control Set Text hDlg, 100, ""
            Select Case Msg
                Case %WM_LButtonDown
                    Control Set Text hDlg, 100, "Left Button Down"
                Case Else
                    CallWindowProc(OrigProc&, hWnd, Msg, wParam, lParam)
            End Select
        End Function  
    

    By clicking on both labels in the example, you'll see that the subclassed label can respond to a %WM_LButtonDown, whereas the non-subclassed label cannot.

    Restoring the Default Window Procedure
    At some point in the program it may be desirable to allow the default window procedure to handle all future messages. To do this, simply use the SetWindowLong API again, this time using the saved address of the original default window process. Here's the code.

        SetWindowLong(hWnd, %GWL_WNDPROC, OrigProc&)
    

    Many programmers always use this code when their program ends, to specifically end subclassing. However, it is not a requirement since the window that is being subclassed will end along with the application. Not resetting the default window procedure will not an application error on shutdown.

    If you have any suggestions or corrections, please let me know.