О сабклассинге

Сабклассинг - техника, позволяющая приложению перехватывать и обрабатывать сообщения (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

    Ну и напоследок - несколько предостережений:

· Пользоваться этим надо очень аккуратно.

· Не забывать возращать на место дефолтную функцию.

· Не задерживать обработку сообщений (они могут валится сотнями в секунду), что чревато переполнением стека.

 


Страница сайта http://silicontaiga.ru
Оригинал находится по адресу http://silicontaiga.ru/home.asp?artId=6476