Сведения о процедурах окна
Каждое окно является членом определенного класса окна. Класс окна определяет процедуру окна по умолчанию, которую использует отдельное окно для обработки сообщений. Все окна, принадлежащие одному классу, используют одну и ту же процедуру окна по умолчанию. Например, система определяет процедуру окна для класса поля со списком (CO МБ OBOX); все поля со списком и используют следующую процедуру окна.
Обычно приложение регистрирует по крайней мере один новый класс окна и связанную с ней процедуру окна. После регистрации класса приложение может создать множество окон этого класса, все из которых используют одну и ту же процедуру окна. Так как это означает, что несколько источников могут одновременно вызывать один и тот же фрагмент кода, необходимо быть осторожным при изменении общих ресурсов из процедуры окна. Дополнительные сведения см. в разделе "Классы окон".
Процедуры окна для диалоговых окон (называемые процедурами диалогового окна) имеют аналогичную структуру и функцию как обычные процедуры окна. Все точки, ссылающиеся на процедуры окна в этом разделе, также применяются к процедурам диалогового окна. Дополнительные сведения см. в диалоговом окне.
В этом разделе рассматриваются следующие разделы.
- Структура процедуры окна
- Процедура окна по умолчанию
- Подкласс процедуры окна
- Суперклассирование процедуры окна
Структура процедуры окна
Процедура окна — это функция, которая имеет четыре параметра и возвращает подписанное значение. Параметры состоят из дескриптора окна, идентификатора сообщения UINT и двух параметров сообщения, объявленных типами данных WPARAM и LPARAM. Дополнительные сведения см. в разделе WindowProc.
Параметры сообщения часто содержат сведения как в их словах с низким порядком, так и в высоком порядке. Существует несколько макросов, которые приложение может использовать для извлечения информации из параметров сообщения. Например, макрос LOWORD извлекает слово с низким порядком (биты от 0 до 15) из параметра сообщения. Другие макросы включают HIWORD, LOBYTE и макрос HIBYTE.
Интерпретация возвращаемого значения зависит от конкретного сообщения. Ознакомьтесь с описанием каждого сообщения, чтобы определить соответствующее возвращаемое значение.
Так как можно вызывать процедуру окна рекурсивно, важно свести к минимуму количество локальных переменных, которые он использует. При обработке отдельных сообщений приложение должно вызывать функции вне процедуры окна, чтобы избежать чрезмерного использования локальных переменных, что может привести к переполнению стека во время глубокого рекурсии.
Процедура окна по умолчанию
Функция процедуры окна по умолчанию DefWindowProc определяет определенное базовое поведение, совместно используемое всеми окнами. Процедура окна по умолчанию предоставляет минимальные функциональные возможности для окна. Процедура, определяемая приложением, должна передавать сообщения, которые не обрабатываются в функцию DefWindowProc для обработки по умолчанию.
Подкласс процедуры окна
При создании окна система выделяет блок памяти для хранения сведений, относящихся к окну, включая адрес процедуры окна, обрабатывающей сообщения для окна. Когда системе нужно передать сообщение в окно, он выполняет поиск сведений о окне для адреса процедуры окна и передает сообщение этой процедуре.
Подкласс — это метод, позволяющий приложению перехватывать и обрабатывать сообщения, отправленные или отправленные в определенное окно, прежде чем окно имеет возможность обработать их. Подклассив окно, приложение может расширить, изменить или отслеживать поведение окна. Приложение может подклассить окно, принадлежащее системным глобальным классам, например элементу управления редактирования или списку. Например, приложение может подклассить элемент управления редактирования, чтобы запретить элементу управления принимать определенные символы. Однако вы не можете подклассить окно или класс, принадлежащий другому приложению. Все подклассы должны выполняться в рамках одного процесса.
Приложение подклассирует окно, заменив адрес исходной процедуры окна адресом новой процедуры окна, называемой процедурой подкласса. После этого процедура подкласса получает все сообщения, отправленные или отправленные в окно.
Процедура подкласса может выполнить три действия при получении сообщения: оно может передать сообщение исходной процедуре окна, изменить сообщение и передать его в исходную процедуру окна или обработать сообщение и не передать его исходной процедуре окна. Если процедура подкласса обрабатывает сообщение, оно может сделать это до, после или до и после передачи сообщения в исходную процедуру окна.
Система предоставляет два типа подкласса: экземпляр и глобальный. В подклассах экземпляра приложение заменяет адрес процедуры окна одного экземпляра окна. Приложение должно использовать подкласс экземпляра для подкласса существующего окна. В глобальном подклассе приложение заменяет адрес процедуры окна в структуре WNDCLASSEX класса окна. Все последующие окна, созданные с помощью класса, имеют адрес процедуры подкласса, но существующие окна класса не затрагиваются.
Подкласс экземпляра
Подкласс приложения выполняет экземпляр окна с помощью функции SetWindowLongPtr. Приложение передает флаг GWL_WNDPROC , дескриптор в подкласс окна и адрес процедуры подкласса в SetWindowLongPtr. Процедура подкласса может находиться в исполняемом файле приложения или библиотеке DLL.
При передаче флага GWL_WNDPROC SetWindowLongPtr возвращает адрес исходной процедуры окна. Приложение должно сохранить этот адрес, используя его в последующих вызовах функции CallWindowProc , чтобы передать перехватанные сообщения исходной процедуре окна. Приложение также должно иметь исходный адрес процедуры окна, чтобы удалить подкласс из окна. Чтобы удалить подкласс, приложение снова вызывает SetWindowLongPtr , передав адрес исходной процедуры окна с флагом GWL_WNDPROC и дескриптором в окно.
Система владеет системными глобальными классами, а аспекты элементов управления могут измениться с одной версии системы на следующую. Если приложение должно подклассить окно, которое принадлежит системным глобальным классам, разработчик может потребоваться обновить приложение при выпуске новой версии системы.
Так как подкласс экземпляра происходит после создания окна, в окно нельзя добавить дополнительные байты. Приложения, которые подклассы окна должны использовать список свойств окна для хранения данных, необходимых для экземпляра подклассированного окна. Дополнительные сведения см. в разделе "Свойства окна".
Когда приложение подклассирует подклассное окно, оно должно удалить подклассы в обратном порядке, который они были выполнены. Если порядок удаления не отменен, может возникнуть неустранимая системная ошибка.
Глобальный подкласс
Для глобального подкласса класса окна приложение должно иметь дескриптор в окне класса. Приложению также требуется дескриптор для удаления подкласса. Чтобы получить дескриптор, приложение обычно создает скрытое окно класса для подкласса. После получения дескриптора приложение вызывает функцию SetClassLongPtr , указав дескриптор, флаг GCL_WNDPROC и адрес процедуры подкласса. SetClassLongPtr возвращает адрес исходной процедуры окна для класса.
Исходный адрес процедуры окна используется в глобальном подклассе таким же образом, как и в подклассах экземпляра. Процедура подкласса передает сообщения исходной процедуре окна путем вызова CallWindowProc. Приложение удаляет подкласс из класса окна, вызвав SetClassLongPtr еще раз, указав адрес исходной процедуры окна, флаг GCL_WNDPROC и дескриптор в окно подкласса класса. Приложение, которое глобально подклассирует класс элемента управления, должно удалить подкласс при завершении работы приложения; В противном случае может возникнуть неустранимая системная ошибка.
Глобальный подкласс имеет те же ограничения, что и подклассы экземпляров, а также некоторые дополнительные ограничения. Приложение не должно использовать дополнительные байты для класса или экземпляра окна, не зная точно, как используется исходная процедура окна. Если приложение должно связать данные с окном, оно должно использовать свойства окна.
Суперклассирование процедуры окна
Суперклассирование — это метод, позволяющий приложению создавать новый класс окна с основными функциями существующего класса, а также усовершенствования, предоставляемые приложением. Суперкласс основан на существующем классе окна, называемом базовым классом. Часто базовый класс — это системный глобальный класс окна, например элемент управления редактированием, но он может быть любым классом окна.
Суперкласс имеет собственную процедуру окна, называемую процедурой суперкласса. Процедура суперкласса может выполнить три действия при получении сообщения: оно может передать сообщение исходной процедуре окна, изменить сообщение и передать его в исходную процедуру окна или обработать сообщение и не передать его исходной процедуре окна. Если процедура суперкласса обрабатывает сообщение, оно может сделать это до, после или до и после того, как оно передает сообщение исходной процедуре окна.
В отличие от процедуры подкласса, процедура суперкласса может обрабатывать сообщения о создании окна (WM_NCCREATE, WM_CREATE и т. д.), но она также должна передавать их исходной процедуре окна базового класса, чтобы процедура окна базового класса могла выполнять свою процедуру инициализации.
Для суперкласса класса окна приложение сначала вызывает функцию GetClassInfoEx для получения сведений о базовом классе. GetClassInfoEx заполняет структуру WNDCLASSEX значениями из структуры WNDCLASSEX базового класса. Затем приложение копирует свой собственный дескриптор экземпляра в элемент hInstance структуры WNDCLASSEX и копирует имя суперкласса в член lpszClassName. Если базовый класс имеет меню, приложение должно предоставить новое меню с теми же идентификаторами меню и скопировать имя меню в член lpszMenuName . Если процедура суперкласса обрабатывает сообщение WM_COMMAND и не передает его в процедуру окна базового класса, меню не имеет соответствующих идентификаторов. GetClassInfoEx не возвращает элемент lpszMenuName, lpszClassName или hInstance структуры WNDCLASSEX.
Приложение также должно задать член lpfnWndProc структуры WNDCLASSEX. Функция GetClassInfoEx заполняет этот элемент адресом исходной процедуры окна для класса. Приложение должно сохранить этот адрес, передать сообщения в исходную процедуру окна, а затем скопировать адрес суперклассовой процедуры в член lpfnWndProc . При необходимости приложение может изменить любые другие члены структуры WNDCLASSEX . После заполнения структуры WNDCLASSEX приложение регистрирует суперкласс, передав адрес структуры функции RegisterClassEx. Затем суперкласс можно использовать для создания окон.
Так как суперклассинг регистрирует новый класс окна, приложение может добавить как в дополнительные байты класса, так и в дополнительные байты окна. Суперкласс не должен использовать исходные дополнительные байты для базового класса или окна по тем же причинам, что подкласс экземпляра или глобальный подкласс не должен использовать их. Кроме того, если приложение добавляет дополнительные байты для использования в класс или экземпляр окна, он должен ссылаться на дополнительные байты относительно количества дополнительных байтов, используемых исходным базовым классом. Так как количество байтов, используемых базовым классом, может отличаться от одной версии базового класса к следующей, начальная смещение для собственных дополнительных байтов суперкласса может также отличаться от одной версии базового класса к следующей.