C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
Обработка сообщений Windows средствами Builder'а, и несколько вопросов по окнам... :)
BreakPointMAN
Отправлено: 16.03.2006, 23:53


Станционный диспетчер

Группа: Участник
Сообщений: 86



Всем доброго здравия желаю! smile.gif
Народ, есть ли среди форумчан кто-то, кто хорошо разбирается в том, как устроена VCL и во внутренностях Windows?


(* 1 *) Может ли кто-нибудь простым четким русским языком объяснить, что есть "Application->Handle" и что, скажем, "Form1->Handle"?

Создал обычное VCL-приложение, две формы, вот что показывает Microsoft Spy++:


QUOTE

1)
Window Handle: 0005051C
Window Caption: Form1
Class Name: TForm1
Owner Window Handle: 000C0514

2)
Window Handle: 00060534
Window Caption: Form2
Class Name: TForm2
Owner Window Handle: 000C0514

3)
Window Handle: 000C0514
Window Caption: Project1
Class Name: TApplication
Owner: None



При этом:
Application->Handle==0x000C0514
Application->MainForm->Handle==0x0005051C
Form1->Handle==0x0005051C
Form2->Handle==0x00060534


Значит ли это, что форма (даже главная) не является главным окном приложения, что дескриптор главного окна приложения — Appication->Handle, и это окно является владельцем всех форм?


(* 2 *) В каких случаях следует создавать карту сообщений (макросами BEGIN_MESSAGE_MAP, MESSAGE_HANDLER, END_MESSAGE_MAP), написав соответствующие обработчики, а в каких — воспользоваться событиями TApplicationEvents::OnMessage, TApplication::OnMessage? Что есть "WndProc"? Какие сообщения кем могут отлавливаться? А, точнее, какие сообщения кому посылаются, собственно?

Справку читал, но все равно остается много вопросов... Заранее спасибо всем, кто откликнется и ответит хотя бы на часть моих вопросов... smile.gif
Grigoriy
Отправлено: 17.03.2006, 02:08


Мастер участка

Группа: Участник
Сообщений: 381



Application->Handle — это свойство объекта Application, которое хранит значение четырехбайтного маркера(дескриптора) исполняемого модуля (в данном случае приложения). Этот дескриптор необходим для Windows. По его значению ОС находит адрес начала исполняемого кода этого модуля в виртуальной памяти. И самой программе необходимо "знать" свой дескриптор потому что ей нужны сервисы ОС.

Form1->Handle — это свойство объекта класса TForm, которое хранит четырехбайтный дескриптор окна.
По значению этого дескриптора ОС находит адрес блока памяти в виртуальной памяти где находятся ресурсы окна, для того чтобы эти ресурсы обрабатывать. В этих ресурсах в часности хранятся данные об окне. Там же хранится адрес точки входа в оконную подпрограмму (оконную функцию, обрабатывающую сообщения).
Программе необходим дескриптор окна по многим причинам.
Главные из них
1. Для окон одного и того же класса имеется общая оконная функция, которая должна определить какому именно окну принадлежит сообщение. Это она определяет по первому параметру передаваемому оконной функции — дескриптору окна.
Например все кнопки приложения Word — это окна одного класса и имеют общую оконную функцию, но оконная функция по параметру дескриптора окна-кнопки однозначно определяет что нужно выполнять над текстовым документом.
2. Для рисования. Для того чтобы нарисовать что либо в окне программа получает от ОС дескриптор графического устройства, способного рисовать в определенном окне не затрагивая другие окна. ОС в свою очередь может дать этот дескриптор устройства исходя из дескриптора окна, так как без дескриптора окна ОС просто не сможет найти у себя данных про это окно и таким образом не сможет оформить это графическое устройство.
QUOTE

Значит ли это, что форма (даже главная) не является главным окном приложения, что дескриптор главного окна приложения — Appication->Handle, и это окно является владельцем всех форм?


Вас запутали подходы программирования в средах высокого уровня.
Appication->Handle это дескриптор самого модуля приложения, а не его главного окна. Приложение может вообще не создавать окон. Например есть так называемые службы ОС, которые постоянно "сидят" в памяти и "ждут", пока их код понадобится. И вы даже не подозреваете что кроме тех программ, которые вы запустили ещё есть много системных программ не имеющих окон. И кроме того, некоторые из них жизненно важные для операционной системы и их остановка требует незамедлительной перезагрузки компьютера.

QUOTE

В каких случаях следует создавать карту сообщений (макросами BEGIN_MESSAGE_MAP, MESSAGE_HANDLER, END_MESSAGE_MAP), написав соответствующие обработчики, а в каких — воспользоваться событиями TApplicationEvents::OnMessage, TApplication::OnMessage?


В каких Вам удобно.

В программе действует цикл обработки сообщений.
Простой цикл обработки сообщения действует по
принципу следующему.
Сначала в сегмент стека оперативной памяти записываются следующие аргументы :
1) четырехбайтное значение наивысшего значения аргумента типа сообщения, которое может быть получено, или нуль если фильтрация не нужна.
2) четырехбайтное значение наинизшего значения аргумента типа сообщения, которое может быть получено, или нуль если фильтрация не нужна.
3) Дескриптор окна для фильтрации сообщений только определенного окна или нуль если не нужна фильтрация по дескрипторам окон
4) Указатель LPMSG на структуру MSG с сообщением, куда затем ОС запишет очередное сообщение.
Затем вызывается API-функция GetMessageA которая получает очередное сообщение от ОС.
Если очередь сообщений для вашего приложения содержит хотя бы одно сообщение, то ОС заполняет структуру MSG.
Если нет сообщений в очереди, то программа простаивает.
Если было послано сообщение WM_QUIT, то функция GetMessageA возвращает в регистре eax процессора нулевое значение, что означает завершение выполнения приложения и инструкция процессора, которая сравнивает значение регистра eax с нулем в результате работы установит флаг(бит) ZF в регистре флагов в единицу, после чего выполнится инструкция условного перехода, которая выведет процессор на метку вне цикла обработки сообщений и программа завершит свою работу.
Я не слишком детально объясняю ?
Если сообщение не WM_QUIT, то функция возвращает ненулевое значение eax.
Затем в стек записывается снова указатель на структуру MSG
и вызывается API-функция
TranslateMessage которая транслирует ввод с клавиатуры.
Она предназначена для обнаружения события от клавиатуры.
Затем в стек записывается снова указатель на структуру MSG.
И в конце вызывается API-функция DispatchMessageA которая отправляет сообщение обратно в операционную систему, чтобы та отправила это сообщение в оконную функцию нужного класса.
Возникает вопрос. А зачем программа опять отправляет сообщение в ОС ? Ответ на этот вопрос — чтобы не было монополии прикладных программ. Дело в том, что ОС сама решает когда её подать сообщение в оконную функцию приложения. И в зависимости от нагрузки процессора и других ресурсов компьютера приложением ОС выбирает моменты времени для каждого приложения, чтобы отправить сообщение. То есть сообщение может быть доставлено в оконную функцию намного позже, чем оно появится в очереди этого приложения.
Если бы этот принцип нарушался, то могло бы получиться так, что какому-то приложению очень часто попадали бы сообщения и оно их долго обрабатывало, нагружало процессор, и программа владела бы ресурсами компьютера монопольно и не давала бы другим программам заниматься обработкой своих сообщений.
Но вероятнее всего, в приложениях создаваемых средами БОРЛАНДА цикл обработки сообщений устроен сложнее...
Интуитивно можно себе представить вызов подпрограммы TApplication::OnMessage в цикле обработки сообщений.
BreakPointMAN
Отправлено: 17.03.2006, 03:05


Станционный диспетчер

Группа: Участник
Сообщений: 86



Grigoriy, большое спасибо за столь подробные разъяснения и за то, что уделили столько времени на ответ! smile.gif

Тем не менее, насчет Application->Handle ситуация, по крайней мере, для меня, остается непонятной. Я все же склоняюсь к тому, что это дескриптор главного окна приложения, а не дескриптор исполняемого модуля. Смущают несколько вещей:

1) есть WinAPI-функция GetWindow. Если первым её параметром указать дескриптор окна, а вторым — GW_OWNER, то функция вернет дескриптор окна-владельца либо NULL, если такого не существует. Так вот, если передать в эту функцию дескриптор любой формы (например, Form1->Handle) и указать в качестве второго параметра GW_OWNER, то функция вернет значение, равное Application->Handle;

2) есть WinAPI-функция SetWindowPos, которая изменяет размеры, позицию, положения окна в Z-последовательности и т.д., если ей передать дескриптор соответствующего окна. Если Application->Handle — не дескриптор окна, а дескриптор исполняемого модуля, то почему при вызове:

CODE
SetWindowPos((HWND)Application->Handle,(HWND)NULL,0,0,100,100,(UINT)HWND_TOP);


в левом верхнем углу экрана появляется окошко размером 100x100? Исполняемый модуль ведь не должен вести себя как окно?

3) то, что написано в справке:

QUOTE

TApplication::Handle

Provides access to the window handle of the main form (window) of the application.

__property HWND Handle = {read=FHandle, write=SetHandle, nodefault};

Description

Use Handle when calling Windows API functions that require a parent window handle. For example, a DLL that displays its own top-level pop-up windows needs a parent window to display its windows in the application. Using the Handle property makes such windows part of the application, so that they are minimized, restored, enabled, and disabled with the application.

Note: When writing a DLL that uses VCL forms, assign the window handle of the host EXE’s main window to the DLL’s Application.HandleApplication->Handle property. This makes the DLL’s form part of the host application. Never assign to the Handle property in an EXE.


и так далее... так что создается впечатление, что главное окно VCL-приложения — это невидимое окно, дескриптор которого Application->Handle.

Отредактировано BreakPointMAN — 17/03/2006, 03:15
Grigoriy
Отправлено: 17.03.2006, 03:47


Мастер участка

Группа: Участник
Сообщений: 381



QUOTE
так что создается впечатление, что главное окно VCL-приложения — это невидимое окно, дескриптор которого Application->Handle.


Ну что, в этом я признаю свою ошибку.

И чтобы окончательно в этом убедиться — вот вам код...
CODE

void __fastcall TForm1::Button1Click(TObject *Sender)
{
HANDLE hd1;
hd1=Application->Handle;
ShowWindow(hd1,SW_SHOWMAXIMIZED);
}


Испытайте его и поделитесь впечатлениями smile.gif

Отредактировано Grigoriy — 17/03/2006, 03:47
AVC
Отправлено: 17.03.2006, 09:51


Ветеран

Группа: Модератор
Сообщений: 1583



QUOTE

... так что создается впечатление, что главное окно VCL-приложения — это невидимое окно, дескриптор которого Application->Handle.

Так и есть, и воспринимайть это нужное как данное. smile.gif

Вернуться в Вопросы программирования в C++Builder