Article ID: 179398
Article Last Modified on 3/11/2005
Option Explicit
'mWndProcOrg holds the original address of the
'Window Procedure for this window. This is used to
'route messages to the original procedure after you
'process them.
Private mWndProcOrg As Long
'Handle (hWnd) of the subclassed window.
Private mHWndSubClassed As Long
'Constant for Windows Message used in sample.
Private Const WM_MOUSEACTIVATE = &H21
Private Sub SubClass()
'-------------------------------------------------------------
'Initiates the subclassing of this UserControl's window (hwnd).
'Records the original WinProc of the window in mWndProcOrg.
'Places a pointer to the object in the window's UserData area.
'-------------------------------------------------------------
'Exit if the window is already subclassed.
If mWndProcOrg Then Exit Sub
'Redirect the window's messages from this control's default
'Window Procedure to the SubWndProc function in your .BAS
'module and record the address of the previous Window
'Procedure for this window in mWndProcOrg.
mWndProcOrg = SetWindowLong(hWnd, GWL_WNDPROC, _
AddressOf SubWndProc)
'Record your window handle in case SetWindowLong gave you a
'new one. You will need this handle so that you can unsubclass.
mHWndSubClassed = hWnd
'Store a pointer to this object in the UserData section of
'this window that will be used later to get the pointer to
'the control based on the handle (hwnd) of the window getting
'the message.
Call SetWindowLong(hWnd, GWL_USERDATA, ObjPtr(Me))
End Sub
Private Sub UnSubClass()
'-----------------------------------------------------------
'Unsubclasses this UserControl's window (hwnd), setting the
'address of the Windows Procedure back to the address it was
'at before it was subclassed.
'-----------------------------------------------------------
'Ensures that you don't try to unsubclass the window when
'it is not subclassed.
If mWndProcOrg = 0 Then Exit Sub
'Reset the window's function back to the original address.
SetWindowLong mHWndSubClassed, GWL_WNDPROC, mWndProcOrg
'0 Indicates that you are no longer subclassed.
mWndProcOrg = 0
End Sub
Friend Function WindowProc(ByVal hWnd As Long, _
ByVal uMsg As Long, ByVal wParam As Long, _
ByVal lParam As Long) As Long
'--------------------------------------------------------------
'Process the window's messages that are sent to your UserControl.
'The WindowProc function is declared as a "Friend" function so
'that the .BAS module can call the function but the function
'cannot be seen from outside the UserControl project.
'--------------------------------------------------------------
'Start Demo Code: Changes the color of the UserControl each
'time the control is clicked in design-time from red to blue
'or from blue to red.
If uMsg = WM_MOUSEACTIVATE Then
If UserControl.BackColor = vbRed Then
UserControl.BackColor = vbBlue
Else
UserControl.BackColor = vbRed
End If
End If
'End Demo Code.
'Forwards the window's messages that came in to the original
'Window Procedure that handles the messages and returns
'the result back to the SubWndProc function.
WindowProc = CallWindowProc(mWndProcOrg, hWnd, _
uMsg, wParam, ByVal lParam)
End Function
Private Sub UserControl_Initialize()
'Occurs the first time a UserControl is placed on a container.
UserControl.BackColor = vbRed
SubClass
End Sub
Private Sub UserControl_Terminate()
UnSubClass
End Sub
Option Explicit
'API Declarations used for subclassing.
Public Declare Sub CopyMemory _
Lib "kernel32" Alias "RtlMoveMemory" _
(pDest As Any, _
pSrc As Any, _
ByVal ByteLen As Long)
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 GetWindowLong _
Lib "user32" Alias "GetWindowLongA" _
(ByVal hWnd As Long, _
ByVal nIndex 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
'Constants for GetWindowLong() and SetWindowLong() APIs.
Public Const GWL_WNDPROC = (-4)
Public Const GWL_USERDATA = (-21)
'Used to hold a reference to the control to call its procedure.
'NOTE: "UserControl1" is the UserControl.Name Property at
' design-time of the .CTL file.
' ('As Object' or 'As Control' does not work)
Dim ctlShadowControl As UserControl1
'Used as a pointer to the UserData section of a window.
Dim ptrObject As Long
'The address of this function is used for subclassing.
'Messages will be sent here and then forwarded to the
'UserControl's WindowProc function. The HWND determines
'to which control the message is sent.
Public Function SubWndProc( _
ByVal hWnd As Long, _
ByVal Msg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
On Error Resume Next
'Get pointer to the control's VTable from the
'window's UserData section. The VTable is an internal
'structure that contains pointers to the methods and
'properties of the control.
ptrObject = GetWindowLong(hWnd, GWL_USERDATA)
'Copy the memory that points to the VTable of our original
'control to the shadow copy of the control you use to
'call the original control's WindowProc Function.
'This way, when you call the method of the shadow control,
'you are actually calling the original controls' method.
CopyMemory ctlShadowControl, ptrObject, 4
'Call the WindowProc function in the instance of the UserControl.
SubWndProc = ctlShadowControl.WindowProc(hWnd, Msg, _
wParam, lParam)
'Destroy the Shadow Control Copy
CopyMemory ctlShadowControl, 0&, 4
Set ctlShadowControl = Nothing
End Function
NOTE: If your UserControl is not named UserControl1, you need to change
the "Dim ctlControl As UserControl1" line of code to indicate the
correct name of your UserControl as specified by its Name property.Keywords: kbhowto KB179398