AllAPI Network - The KPD-Team

 
Allapi Network
 API-Guide
 ApiViewer

 API List

 
API Resources
 Tips & Tricks
 VB Tutorials
 Error Lookup
 
Misc Stuff
 VB examples
 VB Tools
 VB Links
 Top Downloads
 
This Site
 Search Engine
 Contact Form
 

Donate to AllAPI.net

Subclassing

Sub-Classing has two definitions. In the object-oriented sense, a sub-class is derived from a base class to extend or change its functionality. In the Non-object oriented sense, Sub-classing is used to refer to a technique in which you intercept a window's default message queue. You cannot intercept every message that is sent to a window in VB, because you must have the window's handle in order to intercept the messages. You'll already have the window created before you have access to the HWND property; therefore you'll miss certain messages.

What Is It?

Windows is a message based OS. Every window has a message queue. The opperating system posts messages in this queue for the window to process. The messages range in type from the user moving the mouse, to the window being destroyed. All windows (underneath) are sitting in a countinuosly running loop. In this loop the window checks for messages, then processes them. Consider the following psudo code:

While (GetMessage(Message))
{
If Message = CloseWindow Then
If MessageBox("Are You Sure You Want To Quit") = Yes Then
DoSomeCleanUpWork
End If
End If
}

VB handles messages a little differently:

While (GetMessage(Message))
{
If Message = .... Then Call The VB's Event For the Message
}

But not all messages are processed on VB's low-level message loop. Some messages that are rarely used have been left out of VB becuase they slow the applications generated with VB. The Key Word here is Rarely. Some developers would still like to find out when one of these messages occur, So we must sub-class. From VB 1.0 to VB 4.0 there was no built in method for accessing the messages that were left out. VB 5.0 introduced a new token called AddressOf. The AddressOf operator gives us the specific memory location to the entry point of a function. When you create an application in VB, the application is loaded in to memory and executed. The AddressOf operator gives you the Address Of the entry point to the function in memory. This memory location is required by windows in order to invoke our function for the new message processing routine. The Following Information is not yet fully verified by Microsoft: It seems that the entry point to the function can be retrieved by using the VarPtr(FunctionName). This way VB keeps info in a structure (or type in VB) on every function in the application. One of these elements is the offset to the entry point of the function. This value can be retrieved (in long functions) with Long Address = VarPtr(Function) + 56 In VB 5, and Long Address = VarPtr(Function) + 64 in VB 6. (I haven't verified these offsets in vb 3 or 4, some one let me know?): End of Non-Confirmed Info. Windows takes this address and calls our function with it every time there is a new message to be processed. Windows requires our function to have a specified number and type of paramaters in our function so it can give us info on the new message. Here's how the function should look:

Public Function WindowProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long)

The paramaters in the function are given to us by windows. Here's what the paramaters mean:

hWnd: Handle of the window thats getting the new message
uMsg: The new message
wParam & lParam: additional info on the message

Different messages are handled in different ways, and there are literally thousands of messages that windows process. Hence this document is not going into detail of how to handle them all, but rather get you pointed in the right direction on how to process those messages. Messages are normally not removed from the queue by your sub-class routine, instead action is normally taken on them and then processed by the default handler. For example when we get a message that says the form is about to painted, we will call the default paint routine first, then add our graphics. If we add our graphic first, the default routine will just over-write our graphic. If we don't call the default routine, the window will become distorted. This shows the importance of the default window procedure or DefWindowProc as the API name.

How Do I Use It?

You'll need a few declerations in a code module:

Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long

Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

Public Const GWL_WNDPROC = (-4)

I've created some routines to simplify the interception process:

Dim PrevProc As Long

Public Sub HookForm(F As Form)
PrevProc = SetWindowLong(F.hWnd, GWL_WNDPROC, AddressOf WindowProc)
End Sub

Public Sub UnHookForm(F As Form)
SetWindowLong F.hWnd, GWL_WNDPROC, PrevProc
End Sub

Public Function WindowProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
WindowProc = CallWindowProc(PrevProc, hWnd, uMsg, wParam, lParam)
End Function

The PrevProc variable is declared on the Heap or in the General section of the code module. The routine HookForm needs a little attention. Here we call the SetWindowLong API. This API has several uses, on of gives us the ability to "re-route" the default windows procedure (GWL_WNDPROC) to a different procedure (AddressOf WindowProc) for a specified window (F.hWnd). This function returns us the address to the original function (PrevProc). We must save this address for use later. You must always return the default window procedure to VB before closing the form or your app will crash, so we'll save it for later. The UnHookForm routine is where we return the procedure back to VB's default for the form. This call will return us AddressOf WindowProc like to line in HookForm, but we dont need it. Our WindowProc function (for right now) is simply taking the messages and passing them on to VB's original message handler. At this point the only thing we've added to our application is wait time. The VB message handler is always going to be faster than your VB message handler. Any additional work is just going to slow our application. Messages can be sent to our form at thousands a second, so be very cautious when intercepting them. You should only sub-class when necissary, and keep it simple. Windows requires different return values for different messages, so make sure you set your function to the correct return value before exiting the function. In this example we've just set the value to whatever value VB has set for the message. In this example we are joing to sub-class a single form. So we need to create a form. Here's the code:

Private Sub Form_Load()
HookForm Me
End Sub

Private Sub Form_Unload(Cancel As Integer)
UnHookForm Me
End Sub

That's all there is to it (almost). Here we've successfully sub classed a window, but it doesn't do anything. Next we'll need to figure out which messages we want to catch. In this example we'll use the paint message or WM_PAINT. We're going to draw a circle on the form every time the window is painted. Here's the code:

Public Function WindowProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
WindowProc = CallWindowProc(PrevProc, hWnd, uMsg, wParam, lParam)
If uMsg = WM_PAINT Then Form1.Circle (100, 100), 100, vbRed
End Function

Make sure you compile this code and run the .EXE. VB will crash (under most circumstances) when you debug this code. Microsoft has a nice utility that simplifies debugging of sub-classed windows. You can download the dbgwproc.dll from their web site. Your application will also crash if you cause recursive events. For example If we substituted Form1.Circle (100, 100), 100, vbRed With Form1.Refresh, we would be causing another WM_PAINT message to be sent to our routine, thus recalling Form1.Refresh again. At that point our message queue would fill up with WM_PAINT messages and our app would crash. Microsoft has documented every message, it's purpose, it's paramaters and it's expected return value in the Platform SDK. Refer to it for specific info on the avilible messages.

Conclusion

Sub-classing windows extendes functionality to an application not directly exposed from VB.

Sub-classing can cause buggy applications if not used correctly.

Back to Tutorials - Main

 

 


Copyright © 1998-2007, The Mentalis.org Team - Privacy statement
Did you find a bug on this page? Tell us!
This site is located at http://allapi.mentalis.org/