Prizes & Awards
My Profile
Active Members
TodayLast 7 Days
more...
|
Resources » Articles » General »
Subclassing
|
Subclassing Wizardry
This article teaches you subclassing with Visual Basic 6.0. Though .NET is the latest version, the concepts learnt in this article can easily be applied to .NET projects.
Introduction to subclassing
To begin with, subclassing is the the process of intercepting messages before they are passed to the actual window. This means that we can take action of our choice rather than the window whose is going to receive the message. Every window in WindowsOS has a function which receives messages for that window and takes action based upon different types of messages.Subclassing involves specifying a custom function which will receive all messages before they are sent to the original message handler.
In C++, whenever we create a window, we register its class. The structure used is WNDCLASSEX which is like following:
Public Type WndClassEx cbSize as long style as long lpfnWndProc as long . . . . End Type
Only lpfnWndproc parameter is important because it points to address of function which receives all messages for the specified window. Our purpose in Subclassing is to point this lpfnWndProc to our custom function so that we receive all the window notifications-here comes the role of SetWindowLong API function and AddressOf Operator in Visual Basic.
The SetWindowLong API function changes an attribute of the specified window. Among other possible things, it allows to modify the function which is going to receive notifications. We pass it the handle whose attribute we want to change, what attribute we want to change and the new attribute. In our case we are going to use GWL_WNDPROC which sets a new address for the window procedure and returns the AddressOf default message handler for the window which we later pass to CallWindowProc as described later.
The new Attribute must be AddressOf a function (in a BAS module because Visual Basic allows us to have function Address for only function in a BAS module) with following declaration which I explain later:
Public Function (ByVal hwnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
'Add custom functions here
End function
where hwnd is the handle of window for which we are receiving messages, uMsg is the message received presently, wParam and lParam are additional information associated with a message. A common beginners mistake is to write the above function as:
Public Function (hwnd As Long, uMsg As Long, wParam As Long, lParam As Long) As Long
'Add custom functions here
End function
Noticed the mistake? It simple! We have omitted the ByVal keyword due to which we are receiving the actual address of these parameters and trying to access any one of these result in GPF! Another mistake is to forget to specify the return type of function as Long which causes VB to crash. So watch out!
So since we are through with the concepts, we start to take a look at how to begin Subclassing. We start off with a simple example and then build on something useful.
But I need to clear one more concept before we begin off-we don't process all window messages we get. The messages we don't process must be passed back to Default message handler for the window otherwise unexpected results occur. So for this we use CallWindowProc API function which call the default message handle for the window and looks like this:
Public 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
where lpPrevFunction is the addressOf default message handler for the window which we got by SetWindowLong API function and the rest of the parameter correspond to the parameters we receive in out custom message handler..
Open a new Standard EXE project
1)Add two command buttons to the form. Name one as "cmdStartSubClass" and its caption to "Start Subclassing". Name the other as "cmdStopSubClass" and its caption to "Stop Subclassing".
2)Add a BAS Module and add the Following Code:
Option Explicit
'API Decalrations Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public 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) 'constant in SetWindowLong Public pFunction As Long 'pointer to default function
Public Function SubClassForm(frmHwnd As Long)
'Start Subclassing the Form pFunction = SetWindowLong(frmHwnd, GWL_WNDPROC, getFuncAddress(AddressOf WndProc)) End Function
Private Function getFuncAddress(pAddress As Long) As Long 'just pass whatever was received getFuncAddress = pAddress End Function
Private Function WndProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
'Just Print the uMsg,wParam and lParam Debug.Print "Message: " & uMsg & " wParam: " & wParam & " lParam: " & lParam
'Call the Default message handler WndProc = CallWindowProc(pFunction, hwnd, uMsg, wParam, lParam) End Function
Public Function StopSubclass(frmHwnd As Long)
'Stop Subclassing SetWindowLong frmHwnd, GWL_WNDPROC, pFunction End Function
3)Now add the following Code to Form:
Option Explicit
Private Sub cmdStartSubClass_Click()
'Start subclassing the Form SubClassForm Me.hwnd End Sub
Private Sub cmdStopSubClass_Click()
'Stop subclassing the form StopSubclass Me.hwnd End Sub
Thats It!!
Run the project and click the button with caption "Start Subclassing" and notice the Immediate window. You will see a list of all the messages you form is receiving and their wParam and lParam values.
Though it does not do anything usefult, but it was still useful to get started. I am not going to explain the above code as I have already done in preceding paragraphs and you can follow it with the help of Comments.
One interesting thing you might have skipped in this excitement is that you can subclass any window which exposes hWnd property-that is TextBoxes,PictureBoxes,TreeViews etc!!!
And moreover the Basic Procedure to start Subclassing each of them is same-so you will see this code in any application using SubClassing.
|
Responses
|
| Author: Nam Nguyen 30 Jun 2004 | Member Level: Bronze Points : 0 | It does not seem to work in VB.NET. The AddressOf operator can not convert to Long, because Long is not a delegate type.
| | Author: Paul Talbot 27 Aug 2004 | Member Level: Bronze Points : 0 | Nam,
To work around the AddressOf problem you have, you need to create a Delegate for the API Call, don't forget to overload the SetWindowLong object or you will not be able to do standard Long calls.
Delegate Function SetWindowLongProc( _ ByVal hwnd As Long, _ ByVal uMsg As Long, _ ByVal wParam As Long, _ ByVal lParam As Long) As Long
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" ( _ ByVal hwnd As Long, _ ByVal nIndex As Long, _ ByVal dwNewLong As SetWindowLongProc) As Long
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" ( _ ByVal hwnd As Long, _ ByVal nIndex As Long, _ ByVal dwNewLong As Long) As Long
Then change the function below to look like this
Public Function SubClassForm(frmHwnd As Long) 'Start Subclassing the Form pFunction = SetWindowLong(frmHwnd, GWL_WNDPROC, AddressOf WndProc) End Function
Hope this helps.
Paul
|
|