* При перепечатке материалов ссылка на www.SeoLiga.ru обязательна!
Сабклассинг
19 февраля 2009
Сабклассинг (subclassing) - контроль сообщений окон путем модификации (взятия под контроль) оконной процедуры последних. Сабклассинг подразумевает использование изменённой оконной процедуры до оригинальной, что позволяет нам создать сколь угодно заготовок оконных процедур. Хотя на практике обычно используется только одна.
Оконная процедура
Оконная процедура (window procedure) - специальная функция любого окна, имеющего дескриптор, которая принимает и обрабатывает все поступающие окну сообщения (от других программ или от Windows). Оконная процедура является косвенно вызываемой (callback) пользовательской (user-defined) функцией. Соответственно, реакцию на сообщения задаёт программист.
Оконная процедура - самое существенное из всего того, что принадлежит окну, поэтому сабклассинг является очень мощной технологией, необходимой для полноценной работы с Windows API. Важно уметь правильно обрабатывать сообщения, чтобы использовать сабклассинг.
Оконная процедура обычно назначается при создании окна, когда заполняется структура класса последнего TWndClass(Ex).
Оконная процедура имеет такой прототип: код Pascal/Delphi
Function XWindowProc(HWnd: THandle; Msg: Cardinal; WParam, LParam: Integer): Integer; Stdcall;
Где X - любой префикс (можно и опустить), по которому можно идентифицировать нужную оконную процедуру (например, Edit или New).
Рассмотрим, какие параметры передаются при вызове оконной процедуры. В параметреHWnd передаётся дескриптор окна, классу которого принадлежит оконная процедура. В параметре Msg передаётся идентификатор поступившего сообщения. В параметрах WParamи LParam передаётся дополнительная информация, которая зависит от типа посланного сообщения.
Возвращаемый функцией результат должен определить программист.
Рекомендуется обрабатывать сообщения через оператор Case: код Pascal/Delphi
Case Msg Of WM_DESTROY: End;
Для игнорирования сообщения необходимо осуществить выход из блока Case: код Pascal/Delphi
Case Msg Of WM_CLOSE: Exit; End;
Этот способ также применяется для того, чтобы функция DefWindowProc не обрабатывала сообщение.
Для вызова оконной процедуры по её адресу используется функция CallWindowProc. По параметрам она аналогична любой оконной процедуре, но помимо этого она имеет еще один параметр, определяющий адрес требуемой оконной процедуры для вызова (параметр первый). код Pascal/Delphi ... { Тип первого параметра представляет собой простой указатель } TFarProc = Pointer; TFNWndProc = TFarProc; ... Function CallWindowProc(lpPrevWndFunc: TFNWndProc; HWnd: HWND; Msg: Cardinal; WParam: Integer; LParam: Integer): Integer; Stdcall;
Функция CallWindowProc позволяет нам, по сути, менять поведение окна, ведь мы можем сабклассировать его множество раз с сохранением адресов оконных процедур, а потом вызывать нужные оконные процедуры по надобности. Но на практике чаще всего эта функция используется для вызова одной оригинальной оконной процедуры окна, которая была до его сабклассирования.
После детального рассмотрения основ сабклассинга непосредственно перейдём к его реализации в Delphi.
Примечание: суперклассинг, как один из видов сабклассинга, будет описан далее отдельно!
Примечание: сабклассинг для окон, принадлежащих чужим процессам, в данной статье не рассматривается! В частности, для начинающих программистов он достаточно сложен.
Основная функция сабклассирования окна: SetWindowLong. Вообще, эта функция предназначена для изменения определённого аттрибута окна (функция может изменять аттрибут как самого окна, так и аттрибут его класса). Рассмотрим её параметры.
Объявление функции: код Pascal/Delphi
Function SetWindowLong(HWnd: HWND; nIndex: Integer; dwNewLong: LongInt): LongInt; Stdcall;
Параметр HWnd определяет окно, с которым будет производиться работа. Параметр nIndex определяет индекс аттрибута, который мы хотим изменить. Пока нас будут интересовать значения GWL_WNDPROC и GWL_USERDATA. Первый индекс определяет, что изменения затронут оконную процедуру окна, второй - то, что будет изменена специальная внутренняя четырёхбайтовая переменная, которой обладает каждое окно. В ней удобно хранить адрес старой оконной процедуры при сабклассинге.
Рассмотрим, как по шагам засабклассировать окно. :) Создаём заготовку новой оконной процедуры; Помещаем в переменную GWL_USERDATA адрес старой оконной процедуры; Изменяем адрес оконной процедуры на новый. Последние два действия можно объединить в одно, так как функция SetWindowLongвозвращает предыдущее значение изменённого параметра.
Далее я публикую примеры кода, в которых будут рассмотрены способы сабклассирования окон как средствами VCL, так и средствами WinAPI. Все примеры кода хорошо комментированы.