скачать рефераты
  RSS    

Меню

Быстрый поиск

скачать рефераты

скачать рефератыУчебное пособие: Подклассы окон

Учебное пособие: Подклассы окон

Подклассы окон

Сейчас нам надо рассмотреть один интересный прием - порождение подкласса окон. Часто бывает так, что возможностей, предоставляемых окном того или иного стандартного класса Вам не хватает, а создавать эквивалентный стандартному класс с небольшими отличиями слишком сложно. В этом случае было бы удобно научиться создавать дополнительные классы окон, основанные на уже известных классах.

Именно это и называется порождением подкласса окон. Основная идея заключается в использовании собственной функции обработки сообщений, которая выполняла бы требуемую обработку, отличную от стандартной. При этом в качестве процедуры обработки сообщений по умолчанию должна выступать процедура, определенная в уже существующем классе.

Для реализации этого метода нам надо сделать три вещи:

·   узнать адрес процедуры обработки сообщений заданного окна (или заданного класса).

·   научиться вызывать нужную процедуру вместо процедуры обработки сообщений по умолчанию.

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

Первую и третью задачи удобно решать с помощью функции

LONG  SetWindowLong( hWnd, GWL_WNDPROC, lpfnNewProc );

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

Теперь нам надо только организовать обращение к старой процедуре обработки сообщений вместо процедуры по умолчанию (DefWindowProc). Сделать это непосредственно мы не можем, так как при вызове оконной процедуры мы должны связать ее с приложением, зарегистрировавшем этот класс. Вместо этого нам надо воспользоваться функцией:

LONG  CallWindowProc( lpfnProc, hWnd, wMsg, wPar, lPar );

Итак, приведем небольшой пример:

static HANDLE hInstance;

static FARPROC lpfnNewProc;

static FARPROC lpfnOldProc;

LONG WINAPI ChildProc( HWND, UINT, UINT, LONG );

// функция обработки сообщений главного окна

LONG WINAPI _export WinProc(

HWND hWnd, UINT wMsg, UINT wPar, LONG lPar

) {

static HWND hChild;

switch ( wMsg )

hWnd, 0, hInstance, NULL

return DefWindowProc( hWnd, wMsg, wPar, lPar );

}

LONG WINAPI _export ChildProc(

HWND hWnd, UINT wMsg, UINT wPar, LONG lPar

) {

// специфичная обработка сообщений

// и вызов прежней функции, а не функции DefWindowProc

return CallWindowProc( lpfnOldProc, hWnd, wMsg, wPar, lPar );

}

Конечно, рассмотренный нами вариант не единственный. Так, например, мы можем заменять функцию обработки сообщений не окна, а класса. Тогда все вновь создаваемые окна этого класса будут применять нашу процедуру. Для этого мы должны использовать функцию

LONG  SetClassLong( hWnd, GCW_WNDPROC, lpfnNewProc );

Что неудобно, так это то, что мы должны сначала создать окно, а только затем заменять процедуру обработки сообщений. Мы можем поступить и иначе - сначала узнать адрес процедуры обработки сообщений, используя функцию

GetClassInfo( hInstance, lpszClassName, lpWndClass );

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


Связывание данных с окном

При работе с окнами очень часто возникает необходимость хранения данных, связанных с окном. Часто это приходится делать при работе с дочерними окнами, особенно при порождении подклассов окон, когда в таких связанных данных удобно сохранять адреса процедур обработки сообщений и пр. При этом не должно возникать вопросов с разделением данных между двумя окнами одного класса.

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

Для использования данных окна (или класса) мы должны, при регистрации класса окон указать размеры дополнительного пространства, выделяемого в струткуре окна (поле .cbWndExtra структуры WNDCLASS) и в структуре класса (поле .cbClsExra). При выделении пространства оно автоматически обнуляется. Подробнее об этом смотри лекцию 2.

Для доступа к элементам описаний класса и окна можно применять функции:

UINT   GetWindowWord( hWnd, nOffset );

LONG  GetWindowLong( hWnd, nOffset );

UINT   SetWindowWord( hWnd, nOffset, wNewValue );

LONG  SetWindowLong( hWnd, nOffset, dwNewValue );

UINT   GetClassWord( hWnd, nOffset );

LONG  GetClassLong( hWnd, nOffset );

UINT   SetClassWord( hWnd, nOffset, wNewValue );

LONG  SetClassLong( hWnd, nOffset, dwNewValue );

Параметр nOffset задает смещение требуемого слова (двойного слова) относительно начала дополнительных данных в байтах. Эти же функции могут применяться для доступа к некоторым полям самих структур, а не дополнительных данных, но при этом они имеют отрицательные значения.

Второй метод основан на применении специального списка свойств (property) окна. Этот список может динамически изменяться, но работа с ним медленее, чем с данными окна. Кроме того он размещается в локальной памяти модуля USER, поэтому ограничен размерами свободной памяти модуля USER.

Так как списки свойств размещаются не в нашем приложении, то перед уничтожением окна, мы должны удалить все внесенные нами свойства. Свойства состоят из имени (строка символов) и слова данных. Часто это слово рассматривается как хендл блока данных. Мы можем записывать читать, удалять и перебирать свойства, связанные с окном. Для этого предназначены следующие функции:

BOOL  SetProp( hWnd, lpszName, hData );

HANDLE  GetProp( hWnd, lpszName );

HANDLE  RemoveProp( hWnd, lpszName );

int         EnumProp( hWnd, lpfnEnumProc );

Хотя в документации данные, связанные с конктретным свойством, рассматриваются всегда как хендлы, но это не обязательно так. Конечно применение хендлов может несколько сократить размеры списка свойств за счет того, что одному элементу списка может соответствововать больше данных.

Так как Windows не знает, хендл какого блока данных (глобального или локального), объекта GDI или просто данные связан с конкретным элементом списка свойств, то при удалении записи эти данные не удаляются, а передаются Вам для их удаления.

Ресурсы приложения

На предыдущих лекциях мы довольно часто ссылались на такие ресурсы приложения, как курсоры и иконки, ранее мы познакомились более подробно с еще одной разновидностью ресурсов - битмапом. На примере битмапа мы рассмотрели основные правила применения ресурсов.

Сейчас несколько обобщим эти правила и рассмотрим несколько видов ресурсов более подробно. Для описания ресурсов мы должны сформировать файл описания ресурсов, имеющий расширение .RC. В этом файле перечисляются ресурсы, которые могут быть использованы приложением.

Описание ресурсов имеет следующий вид:

ResNameId     TypeNameId [load-opt] [mem-opt]     ResSource

Каждый ресурс должен иметь собственное уникальное имя или номер ResNameId и имя или номер типа ресурса TypeNameId.

Эти имена задаются либо текстом (имя), либо числом (номер), либо символическим именем (номер).

Примеры:

MYBITMAP  BITMAP my_bmp.bmp

100             ICON my_ico.ico

MYDATA       500           my_data.dat

#define IconID     101

IconID       ICON second.ico

(компилятор ресурсов может использовать директивы препроцессора C и включать заголовочные файлы .H, для задания символических имен).

Далее, при работе с ресурсами они будут загружаться из файла приложения в память. При этом для ресурса обычно выделяется блок глобальной памяти мы можем задать некоторые характеристики ресурса как блока памяти mem-opt и определять некоторые правила его загрузки load-opt.

load-opt, описывая правила загрузки, может быть:

PRELOAD     ресурс должен загружаться в памяти при запуске приложения

LOADONCALL  ресурс загружается только по требованию (используется по умолчанию)

mem-opt задает характеристики выделяемого блока и может быть:

FIXED ресурс должен размещаться в фиксированном блоке памяти

MOVEABLE  ресурс размещается в перемещаемом блоке памяти (используется по умолчанию)

DISCARDABLE  перемещаемый ресурс может быть удален из памяти (практически любой перемещаемый ресурс может быть удален, так как его содержимое не меняется)

Наконец, нам требуются данные этого ресурса ResSource. Некоторые виды ресурсов должны быть размещены в отдельном файле, в этом случае в качестве ResSource используется имя файла, другие виды ресурсов требуют непосредственного описания непосредственно в файле описания ресурсов, в этом случае ResSource может быть:

BEGIN

данные ресурса

END

или

{

данные ресурса

}

Иногда допускается либо использования файлов, либо непосредственное описание ресурсов. Некоторый “разнобой” может быть связан с применением компиляторов (и редакторов) ресурсов разных фирм - так как многие из них используют расширенные возможности.

Так, например, редактор ресурсов Borland WorkShop может описывать практически все ресуры непосредственно в файле описания ресурсов, включая их в виде дампа ресурса, а стандартные компилятор ресурсов Microsoft RC не допускает этого, например, для курсоров или битмапов. Компилятор ресурсов Symantec позволяет применять кавычки при задании имен ресурса или типа, что позволяет составлять имена из нескольких слов, что невозможно для Borland и Microsoft и т.д.

Windows предусматривает несколько стандартных типов ресурсов, а Вы можете легко описать ресурсы собственного типа, указав собственный тип (или номер, больший 255 - номера типов от 0 до 255 зарезервированы Windows). При задании данных собственного ресурса Вы можете указать имя файла, содержащего этот ресурс - тогда этот файл включается в ресурс как он есть, либо описав в виде текста непосредственно в файле описания ресурсов:

MyResource MyType

BEGIN

“This is a 0-terminated string\0”, 1, 2, 3, 4, 5,

100, 0x1000

END

В качестве типа ресурса можно указать стандартный тип RCDATA, который соответствует включаемым в файл описания ресурсов данным пользователя в этом-же формате. Если Вы хотите получить доступ к Вашим ресурсам, то надо воспользоваться парой функций:

HRSRC     FindResource( hInstance, lpszName, lpszType );

HGLOBAL     LoadResource( hInstance, hrSrc );

Первая функция позволяет получить промежуточный хендл описания ресурса, а вторая - загрузить ресурс память и получить хендл глобального блока памяти, содержащего ресурс. Если Вы декларировали ресурс как LOADONCALL, то физическое размещение ресурса в памяти произойдет не при вызове функции LoadResource, а при непосредственном обращении к ресурсу.

Если для задания имен ресурса или типа вы использовали текст, то параметры lpszName и lpszType являются указателями на соответствующие строки; если же используются номера, то вы можете их указывать двумя способами - передав строку, начинающуюся на #, например, “#123”, либо разместив в младшем слове адреса нужный номер, а старшем 0. Последний механизм реализуется с помощью макроса:

LPSTR MAKEINTRESOURCE( nId );

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

LPVOID    LockResource( hGlobResource );

и после доступа к данным разрешить его перемещение:

BOOL  UnlockResource( hGlobResource );

(Это не отдельная процедура, а обычный GlobalUnlock). После использования ресурса его можно удалить с помощью процедуры:

BOOL  FreeResource( hGlobResource );

Когда Вы применяете ресурсы какого-либо типа, предусмотренного Windows, то приходится применять несколько другие способы доступа к данным, связанные с необходимостью специальной обработки таких ресурсов. Можно выделить следующие основные типы ресурсов:

ACCELERATORS   – таблица акселераторов клавиатуры; для загрузки применяется функция

HACCEL  LoadAccelerators( hInstance, lpszAccName );

BITMAP              – битмап, включенный в приложение для загрузки применяется функция

HBITMAP      LoadBitmap( hInstance, lpszBitmapName );

CURSOR             – ресурс, представляющий курсор мыши для загрузки применяется функция

HCURSOR     LoadCursor( hInstance, lpszCursorName );

DIALOG              – диалог с ресурсами типа DIALOG и с самими диалогами мы разберемся позже.

FONT                   – включение шрифтового ресурса о применении шрифтов мы говорили ранее. Включать ресурс этого типа в Ваше приложение следует специфическим способом, существенно отличающимся от остальных ресурсов.

ICON                    – иконка для загрузки применяется функция

HICON      LoadIcon( hInstance, lpszIconName );

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

HMENU    LoadMenu( hInstance, lpszMenuName );

STRINGTABLE       – таблица строк ресурс этого типа вообще не загружается целиком. Он представляет собой таблицу строк, имеющих идентификаторы, и обеспечивает доступ к конкретной строке по ее идентификатору. Реально ресурсу этого типа соответствует не блок данных в памяти, а, может быть, несколько - для каждых 16 строк (по порядку номеров) выделяется отдельный блок. Приложение может содержать только один ресурс этого типа, поэтому при описании таблицы строк в файле описания ресурсов ее имя не указывается - указывается только тип этого ресурса. Для доступа к строкам применяется функция

int         LoadString( hInstance, idString, lpszBuff, nmaxCount );

Мы достаточно близко познакомились с ресурсами типа BITMAP и FONT; практически можно считать что мы знакомы и с ресурсами типа CURSOR и ICON, так как они описываются так‑же, как и BITMAP. Сейчас нам надо лучше разобраться с тремя новыми типами – ACCELERARTORS, MENU и DIALOG.


Акселераторы

Акселераторы представляют собой простейшее средство для связывания определенных комбинаций клавиш с конкретными действиями. Можно считать, что акселераторы представляют собой таблицу, в которой записываются нажимаемые клавиши и генерируемые ими сообщения.

Точнее, акселераторы генерируют только сообщение WM_COMMAND, указывая более подробную информацию в параметрах этого сообщения. Параметр wPar сообщения содержит идентификатор, назначенный клавише, а параметр lPar всегда равен 0x00010000L.

Для создания таблицы акселераторов Вы должны поместить в файле описания ресурсов соответствующий ресурс:

AccName ACCELERATORS [load-opt] [mem-opt]

BEGIN

key, id [, type] [, options]

...

END

параметры могут быть следующими:

key  определяет назначаемую клавишу

id    посылаемый код извещения

type тип клавиши ASCII, VIRTKEY или опущен

options указывает состояние специальных клавиш и некоторые действия: NOINVERT, ALT, SHIFT, CONTROL или опущен.

подробнее рассмотрим назначение акселераторов на примере:

“A”,    100                          // послать извещение 100 при нажатии А

65,      100, ASCII              // то же самое, ASCII код 65 соответствует А

“^A”, 101                          // послать 101 при нажатии Ctrl-A

“A”,    101, CONTROL        // то же самое

VK_SPACE,           102,    VIRTKEY       // послать 102 при нажатии Space

VK_SPACE,           103,    VIRTKEY, SHIFT     // послать 103 при нажатии Shift-Space

Несколько слов следует сказать о применении акселераторов в приложении. Сам факт загрузки акселератора в память еще не обозначает его применения. Для того, что бы сообщения от клавиатуры начали обрабатываться акселератором надо в главном цикле обработки сообщений включить специальлные средства для их трансляции.

int   TranslateAccelerator( hWnd, hAccel, lpMSG );

Параметр hWnd указывает окно, которое будет получать извещения, hAccel задает хендл таблицы акселераторов, lpMSG - адрес структуры MSG, содержащей сообщение.

Страницы: 1, 2


Новости

Быстрый поиск

Группа вКонтакте: новости

Пока нет

Новости в Twitter и Facebook

  скачать рефераты              скачать рефераты

Новости

скачать рефераты

© 2010.