Сейчас нам надо рассмотреть один интересный прием - порождение подкласса окон. Часто бывает так, что возможностей, предоставляемых окном того или иного стандартного класса Вам не хватает, а создавать эквивалентный стандартному класс с небольшими отличиями слишком сложно. В этом случае было бы удобно научиться создавать дополнительные классы окон, основанные на уже известных классах.
Именно это и называется порождением подкласса окон. Основная идея заключается в использовании собственной функции обработки сообщений, которая выполняла бы требуемую обработку, отличную от стандартной. При этом в качестве процедуры обработки сообщений по умолчанию должна выступать процедура, определенная в уже существующем классе.
Для реализации этого метода нам надо сделать три вещи:
· узнать адрес процедуры обработки сообщений заданного окна (или заданного класса).
· научиться вызывать нужную процедуру вместо процедуры обработки сообщений по умолчанию.
· сделать так, что бы сообщения обрабатывала написанная нами процедура, а не определенная в классе.
Первую и третью задачи удобно решать с помощью функции
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 ) {
case WM_CREATE:
lpfnNewProc= MakeProcInstance( (FARPROC)ChildProc, hInstance );
hChild= CreateWindow(
“BUTTON”, “Btn A”,
BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE,
10,10, 50,50,
hWnd, 0, hInstance, NULL
);
// заменяем процедуру обработки сообщений дочернего окна
lpfnOldProc= (FARPROC)SetWindowLong(
hChild,GWL_WNDPROC,(LONG)lpfnNewProc
);
break;
case WM_DESTROY:
DestroyWindow( hChild );
FreeProcInstance( lpfnNewProc );
break;
...
}
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
... мере поддерживаются как встроенными средствами визуального программирования (оконным редактором, редактором меню), так и развитыми средствами языка программирования. 1.2 DOS и Windows: разные подходы к программированию Графический интерфейс - не самое главное нововведение в Windows, по сравнению c DOS. Гораздо более важным является то, что Windows - мультизадачная операционная среда, ...
... в Win32 позволила реализовать так называемые многопотоковые приложения (multithread application). При этом выделяют два новых понятия — процесс (proccess) и поток (thread). Процессы в Win32 API примерно эквивалентны приложениям в Windows API. Для каждого процесса выделяются определенные системные ресурсы — адресное пространство, приоритеты и права доступа к разделяемым ресурсам и прочее, но не ...
... проекта. В этом случае редактор кода вызывается кнопкой View Code (Просмотр кода) панели инструментов окна Проводника. 2.3 Характеристика программы Данная программа написана на языке Visual Basic 6.0 и представляет собой 1 приложением, предназначенных выполнять все функции, которые требуются заданию. В конечный продукт входит 1 откомпилированное приложения, размер которого составляет ...
... ведущим классом может быть авиация, для строительства – машиностроение. При выборе дополнительных прототипов рекомендуется использовать словари технических функций, МКИ (международную классификацию изобретений). Патентные описания за последние 5–10 лет (как по рассматриваемому, так и функционально близким классам технического объекта), каталоги выставок и т.д. Иногда при выборе прототипа ...
0 комментариев