О сабклассинге
Сабклассинг - техника, позволяющая приложению перехватывать и обрабатывать сообщения (messages), посылаемые или поставляемые в очередь (posted) определенному окну перед тем, как само окно будет иметь возможность их обработать.
В windows практически все, что мы видим - окна (формы, контролы и т.д.). Каждое окно имеет свою "оконную процедуру" (window proc). В обязанности оконной процедуры входит обработка "сообщений", пришедших этому окну. Передача сообщений - это способ, которым windows производит манипуляции со всеми окнами. Hапример, когда юзер пытается закрыть окно, система посылает этому окну сообщение WM_CLOSE и оконная процедура должна должным образом обработать это сообщение.
У каждого созданного в системе окна зарегистрированного класса ("editbox", "button" и т.д.) есть "оконная процедура по умолчанию" (default window proc), которая и отвечает за обработку сообщений, приходящих этому окну. Если мы напишем свою оконную процедуру для какого-то окна и заменим ей процедуру по умолчанию, то значит мы произвели subclass этого окна. Это и есть сабклассинг.
Сабклассинг производится вызовом следующих api функций:
GetWindowLong - для выяснения адреса default window proc (в принципе не обязательна)
SetWindowLong - для указания адреса новой window proc (эта функция возвращает указатель на предыдущую функцию окна)
CallWindowProc - для последующего вызова default window proc, если надо (см. также DefWindowProc).
Сама оконная процедура должна иметь определенный вид:
Function WindowProc(ByVal hWnd As Long, ByVal uMsg As _ Long, ByVal wParam As Long, ByVal lParam As Long) As _ Long
где: hWnd - хендл (указатель) окна uMsg - сообщение wParam - дополнительная информация сообщения (значение зависит от конкретного типа сообщения) lParam - дополнительная информация сообщения (значение зависит от конкретного типа сообщения)
Ну и пример. В модуль:
Private 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
Private Declare Function SetWindowLong Lib "user32" Alias _ "SetWindowLongA" (ByVal hwnd As Long, _ ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Const GWL_WNDPROC = -4 Private Const WM_MOVE = &H3
Private lpPrevWndProc As Long ' тут будет указатель на default proc
Public Sub start_subclass(hwnd As Long) 'указываем новую оконную процедуру 'SetWindowLong при этом возвращает указатель 'на процедуру по умолчанию для указанного окна 'Этим самым мы и начинаем субклассинг lpPrevWndProc = SetWindowLong(hwnd, GWL_WNDPROC, _ AddressOf WindowProc) End Sub
Public Sub stop_subclass(hwnd As Long) 'возвращаем окну его "родную" процедуру SetWindowLong hwnd, GWL_WNDPROC, lpPrevWndProc End Sub
Function WindowProc(ByVal hw As Long, ByVal uMsg As _ Long, ByVal wParam As Long, ByVal lParam As Long) As _ Long
'"обрабатываем" сообщение WM_MOVE Select Case uMsg Case WM_MOVE Debug.Print "WM_MOVE" End Select
'поскольку мы не написали "полноценной" оконной 'процедуры, вызовем "родную" для обработки всех 'сообщений. Этого можно и не делать, но тогда надо 'обрабатывать все сообщения и возвращать 'соответствующие значения. WindowProc = CallWindowProc(lpPrevWndProc, hw, _ uMsg, wParam, lParam) End Function
Теперь в форму:
Private Sub Form_Load() start_subclass Me.hwnd End Sub
Private Sub Form_Unload(Cancel As Integer) stop_subclass Me.hwnd End Sub
Ну и напоследок - несколько предостережений:
· Пользоваться этим надо очень аккуратно.
· Не забывать возращать на место дефолтную функцию.
· Не задерживать обработку сообщений (они могут валится сотнями в секунду), что чревато переполнением стека.
|