Home > Code Library > SmartSubclass Library

Last Update 29 Oct 2001

 


Search:



  

Options:

   

   

   

   



SmartSubclass Library

About this page

This article describes how to subclass controls using library 'SmartSubclass' and gives an example on how to change a Progressbar gauge. 

Downloads

SmartSubclass.dll Binary
SmartSubclass.dll Source
SmartSubclass.dll Example

What is Subclass?

Every window has a function that handles its messages. This function is called a windowproc. If we were coding in C++ we would be able to modify this windowproc but in VB we have to rely on the implementation of each control to get access to its messages (events). The problem comes when we find that not all messages are passed from the windowproc to VB or, even worse, when we discover that there isn't a way to modify such messages.

The solution to this problem is called subclassing: we can set our own VB function to act as a windowproc and trap all messages before these are processed. There are very good articles on the net that describe how to subclass a window from VB and you can read them if you need more information. I would suggest "Subclassing without the crashes" from Steve McMahon, "Intercepting Windows Messages in Visual Basic" from Deborah L. Cooper or "HOWTO: Subclass a UserControl" from MSDN.

I will now focus on how to use my library SmartSubClass.dll so you can subclass your controls without having to worry about the implementation.


Installation

The steps you need to follow to install the library are:

    1. Download SmartSubClass.dll
    2. Use RegSvr32.exe to register the class
    3. Open your VB project
    4. Click on Project>References
    5. Click on "VBSmart SubClass" or click on 'Browse' and find SmartSubClass.dll
    6. You're ready now to use the library

How to use the library

Once you have added a reference to the library you have a new class available: SmartSubClass. You can define variables of this class and use them to control your windows. Every instance of the class can take control of as many windows as you want. It is up to you to whether have one variable per window or a single variable for more than one window.

The class has a single function SubClassHwnd() and a single event NewMessage(). You will use the function to both subclass and unsubclass your windows and you will use the event to detect when a new message for your subclassed window is available.

I will use the following example to explain in more detail how to use the library: you all know that VB, through its library mscomctl.ocx (comctl32.ocx in VB5) provides a control named Progressbar. Microsoft describes this control saying "(...) shows the progress of a lengthy operation by filling a rectangle with chunks from left to right".


Figure 1- Example of VB Progressbar

In fact it provides two ways of displaying the progress status: ccScrollingStandard and ccScrollingSmooth, available through its property 'Scrolling'. We are going to subclass two Progressbars and make them show its progress using a gradient style.


Figure 2 - Progressbars after subclassing

Open a new VB project, add a reference to 'VBSmart SmartSubClass' as described above, add two progressbars and name them Progressbar1 and Progressbar2.

The first thing you will need to do is to declare a SmartSubClass variable.

    Dim WithEvents oSniff As SmartSubClass

Make sure you use the keyword 'WithEvents' when you declare the variable. Now, if you open the code window and you click on 'oSniff' you will see there's an event for this variable. This is where you will trap the progressbar messages.



Figure 3 - The event NewMessage is available to be used

Now we have to create our variable and tell it to subclass both progressbars. We could do that on the Form_Load event. Let's try something like:

Private Sub Form_Load()
    Set oSniff = New SmartSubClass
    
    oSniff.SubClassHwnd ProgressBar1.hWnd,
True
    oSniff.SubClassHwnd ProgressBar2.hWnd,
True
End Sub


As you can see the function SubClassHwnd has two parameters: a window handle and a boolean flag. The window handle is the identifier of the main window of a control and it is normal accesible through its property Hwnd. Most controls have this property, although there are exceptions like the Label, the Shape or the Line. The second parameter is a flag that indicates whether to subclass or unsubclass the window. It is good practice to restore the original windowproc when our application unloads. If we don't, it shouldn't be a problem because the class does it when its 'Finalize' event is fired but... it is good practice!

So that's what we're going to do next: make sure that both progressbars are unsubclassed before our form unloads. We need to type the following code:

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
    oSniff.SubClassHwnd ProgressBar1.hWnd,
False
    oSniff.SubClassHwnd ProgressBar2.hWnd,
False
End Sub


At this stage we are ready to receive all Windows messages sent to both progressbars through the event oSniff_NewMessage(). To change the behaviour of the progressbar and have the gradient effect we will have to listen to the message WM_PAINT. This is the message that Windows sends to a progressbar when its client area needs repainting.

You will see that the event NewMessage has 5 parameters:
    ByVal hWnd As Long
    ByRef uMsg As Long
    ByRef wParam As Long
    ByRef lParam As Long
    ByRef Cancel As Boolean

The first 4 parameters are typical of a Windows message: 'hWnd' is the window handler of the target window, 'uMsg' is the message identifier, 'wParam' and 'lParam' will vary depending on the message and you will have to access Microsoft's documentation (or any other source of information) to get their possible values and description. Finally, 'Cancel' is a boolean flag that will allow you to decide whether you want Windows to handle your message after you trap it or not. 'Cancel' comes with a default value of False. You will normally set 'Cancel' to True only for messages that you modify by adding code to the function. Another interesting thing of NewMessage is the fact that all message parameters are passed by reference. You can change their values before sending them back to Windows.

In order to modify the WM_PAINT message you will have to declare a constant for it. You can normally find this constant using the 'API Text Viewer' that comes with VB. Not all APIs are included in this tool but most of them are. Another interesting source of information is AllAPI Network where you will find not only an updated version of the API library but a better tool to copy and paste to VB.

Add this line to your VB project:

    Private Const WM_PAINT = &HF

The final step is to add code to the NewMessage to handle the WM_PAINT, something like:

Private Sub oSniff_NewMessage( _
    ByVal hWnd As Long, _
    ByVal uMsg As Long, _
    wParam As Long, _
    lParam As Long, _
    Cancel As Boolean)
    
    Select Case uMsg
        Case WM_PAINT
        
            If hWnd = ProgressBar1.hWnd Then
                
pFillWithGradient1 hWnd
            Else
                
pFillWithGradient2 hWnd
            End If
            
            
Cancel = True
    End Select
    
End Sub


As you can see, we only have to select the message and call our own painting function depending on the window handle (the same variable handles two different windows and we want different progressbars to have different gradients). It is also important to set the flag Cancel to True because otherwise the original windowproc would continue and would paint the progressbar - we would think that nothing is working!

If you want the code for pFillWithGradient1 and pFillWithGradient2 you can download the test project from the top of this page...

Finally I would like to explain why SmartSubClass.dll has been implemented as an external DLL rather than a simple class module. Subclassing in VB has only one problem: if you press the 'End' button from your VB toolbar and your class (or any subclassing code that you implement) belongs to your project, your VB session wil crash! This problem has been reported by Microsoft and they give a solution which I think is only partial. They provide the 'Debug Object for AddressOf' that can be downloaded from Visual Basic's download center. The solution is a DLL (Dbgwproc.dll) that allows VB projects to know whether they are running in debugging mode (IDE) or not. The solution works fine but they've missed one scenario: if your application executes the 'End' statement the program will still crash. This is because the 'End' statement stops code execution abruptly, without invoking the Unload, QueryUnload, or Terminate event, or any other Visual Basic code. Unfortunatelly, many programmers use the 'End' statement to close their applications...

So what's the solution then? well, the solution is to have your class in a separate DLL. This allows you to use the library from VB and press the 'End' button without getting one of those ugly GPF windows. You will also be able to use the 'End' statement in your applications. You have access to the source code of the SmartSubClass class so you could include it as a class module in your project but... you do so at your own risk!

 
Copyright © 2001, Andrés Pons (andres@vbsmart.com). All rights reserved.