* При перепечатке материалов ссылка на www.SeoLiga.ru обязательна! RSS



Суперклассинг
19 февраля 2009

Сабклассинг особенно удобен, когда дело касается изменения одного окна, класс которого не совпадает с другими окнами, подлежащими сабклассированию. А что, если нам нужно засабклассировать сотню Edit'ов? Сабклассинг здесь будет громоздким. Решением этой проблемы является суперклассинг.
Суперклассинг (superclassing) - создание и регистрация нового класса окна в системе. После чего этот класс окна готов к использованию.

VCL-суперклассинг мы рассматривать не будем. Думаю, Вам понятно, что реализация суперклассинга на VCL - это создание компонентов. При создании оконного компонента в Delphi вы неявно создаёте подобие суперкласса. После этого вы можете использовать хоть сотню таких компонентов (например, создать из них массив). Заметьте, что такой компонент будет, как правило не стандартным, например, кнопка TBitBtn. Чтобы Вам было понятней, почему это суперкласс, можете посмотреть имя класса окна компонента через любой сканер окон (я использовал InqSoft Window Scanner) - это имя будет совпадать с тем именем, которое обозначает имя компонента в Delphi (например, TBitBtn или TLabeledEdit). Из этого мы можем сделать вывод, что суперклассинг прекрасно прижился в Delphi и широко там используется.

У каждого потомка класса TWinControl в Delphi есть метод CreateParams. Можете воспользоваться им, чтобы изменить название класса окна.

Гораздо более интересен суперклассинг на WinAPI. Необходимо уметь его использовать.

Рассмотрим, как по шагам создать суперкласс. :)
Вызываем функцию GetClassInfoEx, чтобы получить информацию о классе окна, который мы будем далее модернизировать. Эта функция заполнит переданную ей запись TWndClassEx параметрами класса;
Изменяем всё, что нам нужно в полученной записи. Нужно задать свое имя класса, размер структуры, а также дескриптор HInstance, также нас будет интересовать оконная процедура - мы также изменим её у класса;
Регистрируем новый класс при помощи функции RegisterClassEx;
По окончании работы программы освобождаем класс функцией UnregisterClass.
Далее новый класс можно использовать. В примерах я буду делать простые изменения в классах окон.

Давайте рассмотрим функции для суперклассинга более подробно.

Суперклассинг начинается с функции GetClassInfoEx.

Объявление функции:
код Pascal/Delphi

Function GetClassInfoEx(Instance: Cardinal; Classname: PChar;
Var WndClass: TWndClassEx): LongBool; Stdcall;

Первый параметр функции - дескриптор приложения, которое создало класс. Если же Вы желаете модифицировать предопределённые класс окон Windows (например, классы 'Button', 'Edit', 'ListBox' и т. п.), то передайте ноль в параметре.

Следующий параметр - собственно название интересующего Вас класса. Сюда можно передать атом (см. ниже)

В последнем параметре передается структура типа TWndClassEx, в которую в случае успешного вызова функции будет помещена информация о классе.

Когда информация о классе получена, можно изменить его (что обязательно, сказано выше).

После подготовки класса окна Вы регистрируете его в Windows с помощью функции RegisterClassEx.
код Pascal/Delphi

Function RegisterClassEx(Const WndClass: TWndClassEx): Word; Stdcall;

Функция возвращает атом, который по сути есть числовое уникальное значение. Это будет идентификатор класса окна в системе.

По завершению работы приложения желательно уничтожить класс. В противном случае - "утечка памяти".
Для этого существует функция UnregisterClass:
код Pascal/Delphi

Function UnregisterClass(lpClassName: PChar; hInstance: Cardinal): LongBool; Stdcall;

Эта функция уничтожает класс окна из Windows, освобождая память, ранее под него выделенную.

Первый параметр функции - имя класса для деинсталляции. Обратите внимание, что эта функция сможет уничтожить только класс, который был зарегистрирован приложением, чей дескриптор передан во втором параметре. Глобальные предопределённые классы (см. ниже) Windows (например, класс Edit) не могут быть уничтожены. В первом параметре также разрешается передавать атом-идентификатор класса.

Для полного ознакомления с суперклассингом следует обобщить знания о самом классе окна.

Класс окна
Вообще, класс окна - объемная тема. Мы рассмотрим её самые главные особенности.

Класс окна (window class) - набор свойств, который используются как шаблон для создания окон. Класс окна всегда можно расширить, изменить. Давайте подробнее разберем аттрибуты класса.

Первый аттрибут - имя класса. Оно позволяет отличать одни классы от других. Классы с одинаковыми именами считаются идентичными. После создания окна по классу это окно может подвергнуться сабклассингу. Сабклассинг не изменяет класс окна. Не делайте имена классов длиннее 64 символов.

Второй аттрибут - это адрес оконной процедуры для окна. Об оконной процедуре подробно рассказано выше.

Третий аттрибут - дескриптор приложения (или DLL), которое зарегистрировало класс.

Четвёртый - курсор окна при создании.

Пятый - дескриптор большой иконки для окна.

Шестой - тоже дескриптор иконки, но маленькой. Этого аттрибута нет у структуры типа TWndClass (поняли, в чем отличие TWndClass от TWndClassEx?).

Седьмой - дескриптор кисти, которой будет зарисована клиентская область окна.

Восьмой - дескриптор меню, которое присваивается окну при создании.

Девятый - стили класса (см. ниже)

Десятый - дополнительная память, выделяемая классу (тип Integer).

Одиннадцатый - дополнительная память (Integer), выделяемая под каждое окно класса.

Напоследок рассмотрим стили класса. Стили класса - это комбинация значений, которые определяют поведение класса.
Вот они:

CS_BYTEALIGNCLIENT - выстраивает клиентскую часть окна на границу байта, что позволяет достичь большей производительности при отрисовке;

CS_BYTEALIGNWINDOW - то же, что и CS_BYTEALIGNCLIENT, только увеличивает производительность при перемещении окна;

CS_CLASSDC - создает контекст устройства, который разделяется между всеми наследниками этого класса;

CS_DBLCLKS - разрешает обработку сообщений при двойном щелчке мыши;

CS_GLOBALCLASS - разрешает создание окон с независимыми идентификаторами приложений, если этот флаг не указан, то значение HInstance при создании окна должно быть таким же как и при регистрации класса RegisterClass(Ex).

CS_HREDRAW - перерисовывает окно при его перемещении по горизонтали (и при изменении горизонтальных размеров);

CS_VREDRAW - перерисовывает окно при его перемещении по вертикали (и при изменении вертикальных размеров);

CS_NOCLOSE - убирает команду "Закрыть" из системного меню окна;

CS_OWNDC - создает уникальный контекст устройства для каждого вновь создаваемого окна.

На суперклассинг я публикую один пример, в котором на главном окне будет создано 10 "измененных" Edit'ов. Каждый такой Edit при наведении на него мышки уничтожит себя сам.
код Pascal/Delphi

Program SampleProject04;

{$R *.res}
{$R WinXP.res}

Uses
Windows, Messages;

Procedure InitCommonControls; Stdcall; External 'comctl32.dll';

Var
{ Главное окно }
HWnd: THandle;
{ Массив Edit'ов }
Edits: Array[0..9] Of THandle;
{ Сюда будет помещено значение оригинальной оконной процедуры класса Edit }
OldProc: Pointer;

{ Устанавливает для окна AWindow шрифт для контролов по умолчанию }
Procedure SetDefFont(AWindow: THandle);
Begin
SendMessage(AWindow, WM_SETFONT, GetStockObject(DEFAULT_GUI_FONT), 1);
End;

{ Модифицированная оконная процедура каждого поля ввода }
Function EditWinProc(HWnd: THandle; Msg: Cardinal;
WParam, LParam: Integer): Cardinal; Stdcall;
Begin
Case Msg Of
{ Запрещаем показ контекстного меню }
WM_LBUTTONDOWN: DestroyWindow(HWnd);
End;
{ Вызов оригинальной оконной процедуры }
Result := CallWindowProc(OldProc,
HWnd, Msg, WParam, LParam);
End;

{ Оконная процедура главного окна }
Function MainWinProc(HWnd: THandle; Msg: Cardinal;
WParam, LParam: Integer): Cardinal; Stdcall;
Var
TmpEdit: TWndClassEx;
I: Integer;
Begin
Case Msg Of
{ Здесь будет произведено создание дочерних окон }
WM_CREATE:
Begin
{ Начало суперклассинга }
If Not GetClassInfoEx(0, 'Edit', TmpEdit) Then Halt;
{ Запоминаем оконную процедуры для правильной работы окна }
OldProc := TmpEdit.lpfnWndProc;
{ Модификация класса }
TmpEdit.cbSize := SizeOf(TWndClassEx);
TmpEdit.lpfnWndProc := @EditWinProc;
TmpEdit.lpszClassName := 'Sample04EditWindowClass';
TmpEdit.hInstance := GetModuleHandle(NIL);
{ Регистрация класса }
If RegisterClassEx(TmpEdit) = 0 Then Halt;
{ Подготовка массива }
FillChar(Edits, SizeOf(Edits), 0);
For I := Low(Edits) To High(Edits) Do
Begin
Edits[I] := CreateWindowEx(WS_EX_CLIENTEDGE,
'Sample04EditWindowClass', 'Sample',
WS_CHILD Or WS_VISIBLE Or ES_LEFT,
8, 28, 300, 21, HWnd, 0, HInstance, NIL);
SetDefFont(Edits[I]);
End;
End;

WM_KEYDOWN:
{ Закрытие окна по нажатию Enter'а }
If WParam = VK_RETURN Then PostQuitMessage(0);

WM_DESTROY:
Begin
{ Уничтожение классов}
UnregisterClass('Sample04EditWindowClass', HInstance);
{ Выход для освобождения памяти }
PostQuitMessage(0);
End;
End;
{ Обработка всех остальных сообщений по умолчанию }
Result := DefWindowProc(HWnd, Msg, WParam, LParam);
End;

Procedure WinMain;
Var
Msg: TMsg;
{ Оконный класс }
WndClassEx: TWndClassEx;
Begin
{ Подготовка структуры класса окна }
ZeroMemory(@WndClassEx, SizeOf(WndClassEx));

{************* Заполнение структуры нужными значениями ******************* }

{ Размер структуры }
WndClassEx.cbSize := SizeOf(TWndClassEx);
{ Имя класса окна }
WndClassEx.lpszClassName := 'SuperclassSampleWnd';
{ Стиль класса, не окна }
WndClassEx.style := CS_VREDRAW Or CS_HREDRAW;
{ Дескриптор программы (для доступа к сегменту данных) }
WndClassEx.hInstance := HInstance;
{ Адрес оконной процедуры }
WndClassEx.lpfnWndProc := @MainWinProc;
{ Иконки }
WndClassEx.hIcon := LoadIcon(HInstance, MakeIntResource('MAINICON'));
WndClassEx.hIconSm := LoadIcon(HInstance, MakeIntResource('MAINICON'));
{ Курсор }
WndClassEx.hCursor := LoadCursor(0, IDC_ARROW);
{ Кисть для заполнения фона }
WndClassEx.hbrBackground := COLOR_BTNFACE + 1;
{ Меню }
WndClassEx.lpszMenuName := NIL;

{ Регистрация оконного класса в Windows }
If RegisterClassEx(WndClassEx) = 0 Then
MessageBox(0, 'Невозможно зарегистрировать класс окна',
'Ошибка', MB_OK Or MB_ICONHAND)
Else
Begin
{ Создание окна по зарегистрированному классу }
HWnd := CreateWindowEx(0, WndClassEx.lpszClassName,
'Superclassing Sample by Rrader', WS_OVERLAPPEDWINDOW And Not WS_BORDER
And Not WS_MAXIMIZEBOX And Not WS_SIZEBOX,
Integer(CW_USEDEFAULT), Integer(CW_USEDEFAULT), 320, 116, 0, 0,
HInstance, NIL);

If HWnd = 0 Then
MessageBox (0, 'Окно не создалось!',
'Ошибка', MB_OK Or MB_ICONHAND)
Else
Begin
{ Показ окна }
ShowWindow(HWnd, SW_SHOWNORMAL);
{ Обновление окна }
UpdateWindow(HWnd);

{ Цикл обработки сообщений }
While GetMessage(Msg, 0, 0, 0) Do
Begin
TranslateMessage(Msg);
DispatchMessage(Msg);
End;
{ Выход по прерыванию цикла }
Halt(Msg.WParam);
End;
End;
End;

Begin
InitCommonControls;
{ Создание окна }
WinMain;
End.


Теги: Borland Delphi

Статьи по теме:

LockTable
Первая программа
Выключение секций
Электронная доска объявлений
BeforeAutoCreate
OpenIndexFile
Data Marts
Насчет QuickReport
Гипертекстовые и мультимедийные информационные технологии
Ключевые слова языка Delphi
UnlockTable
Свойство ChildBand
Объединение сетей в интерсеть
Плавающая запятая в BASM
Свойство PaperWidth
| Borland Delphi | ext |
 


Пн Вт Ср Чт Пт Сб Вс
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30


     



Rambler's Top100

Данный сайт или домен продается ICQ: 403-353-727

© 2009 Seoliga.ru | Borland Delphi | Суперклассинг. Регион сайта: Москва и Санкт-Петербург