Delphi: что такое Application.Handle?

Что такое TApplication.Handle?

  • От куда это?
  • Почему он существует?
  • И самое главное: почему все формы имеют его как дескриптор родительского окна?

Справка Delphi говорит:

TApplication.Handle

Предоставляет доступ к дескриптору окна основной формы (окна) приложения.

property Handle: HWND;

Описание

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

Если я сфокусируюсь на словах «дескриптор окна основной формы приложения», и я пойму, что это означает дескриптор окна основной формы приложения, тогда я могу сравнить:

  • "дескриптор окна основной формы приложения", с
  • оконный дескриптор MainForm Application

но они не то же самое:

Application.MainForm.Handle: 11473728
Application.Handle: 11079574

Так что такое Application.Handle?

  • От куда это?
  • Что это за дескриптор окна Windows?
  • Если это дескриптор окна Windows для Application MainForm, то почему они не совпадают?
  • Если это не дескриптор окна MainForm Application, тогда что это?
  • Что еще более важно: почему он является конечным владельцем каждой формы parent?
  • И самое главное: почему все идет наперекосяк, если я пытаюсь сделать форму unparented unowned (чтобы она могла отображаться на панели задач) или пытаюсь использовать что-то вроде IProgressDialog ?

На самом деле я спрашиваю: каково обоснование дизайна, благодаря которому существует Application.Handle? Если я могу понять почему, то как должно стать очевидным.


Обновление. Разберитесь с помощью игры из двадцати вопросов:

Говоря о решении создания окна на панели задач, сделав его владельцем null, Питер Белов в 2000 году сказал:

Это может вызвать некоторые проблемы с модальными формами, отображаемыми из вторичных форм.

Если пользователь переключается с приложения, пока модальная форма активна, а затем возвращается к форме, которая показывала его, модальная форма может скрываться под формой. С этим можно справиться, убедившись, что модальная форма является родительской [sic; он имел в виду принадлежал] в форму, которая его показывала (используя params.WndParent, как указано выше)

Но это невозможно со стандартными диалоговыми окнами из модуля Dialogs и исключений, которым требуется больше усилий, чтобы заставить их работать правильно (в основном обработка Application.OnActivate, поиск модальных форм, связанных с приложением через GetLastActivePopup и вывод их на вершину Z-порядка через SetWindowPos).

  • Почему модальная форма застревает позади других форм?
  • Какой механизм обычно выводит модальную форму на передний план и почему здесь она не работает?
  • Windows отвечает за отображение окон в стопке. Что пошло не так, что Windows не показывает правильные окна?

Он также рассказал об использовании нового расширенного стиля Windows, который заставляет окно появляться на панели задач (когда обычные правила вывода окна из-под контроля недостаточны, непрактичны или нежелательны), добавляя WS_EX_APPWINDOW расширенный стиль:

procedure TForm2.CreateParams(var Params: TCreateParams); 
begin 
   inherited CreateParams( params ); 

   Params.ExStyle := Params.ExStyle or WS_EX_APPWINDOW; 
end; 

Но затем он предупреждает:

Если вы нажмете кнопку вторичной формы на панели задач, когда другое приложение активно, все формы приложений все равно будут выведены на передний план. Если вы не хотите, есть вариант

Кто выносит все формы на передний план, когда владельцем формы еще Application.Handle. Приложение делает это? Почему он это делает? Вместо того, чтобы делать это, разве он не должен этого делать? В чем обратная сторона того, не этого делать; Я вижу обратную сторону выполнения этого (системное меню не работает должным образом, эскизы кнопок на панели задач неточны, оболочка Windows не может свернуть окна.


В другом сообщении, посвященном Application, Майк Эденфилд говорит, что родительское окно отправляет другим окнам их минимизацию , развернуть и восстановить сообщения:

Это добавит кнопку панели задач для вашей формы, но есть еще несколько мелких деталей, которые нужно обработать. Наиболее очевидно, что ваша форма по-прежнему получает минимизацию / максимизацию, которые отправляются в родительскую форму (основная форма приложения). Чтобы этого избежать, вы можете установить обработчик сообщений для WM_SYSCOMMAND, добавив такую ​​строку, как:

procedure WMSysCommand(var Msg: TMessage); WM_SYSCOMMAND; 

procedure TParentForm.WMSysCommand(var Msg: TMessage); 
begin 
   if Msg.wParam = SC_MINIMIZE then 
   begin 
      // Send child windows message, don't 
      // send to windows with a taskbar button. 
   end; 
end; 

Обратите внимание, что этот обработчик имеет форму PARENT того, который вы хотите вести независимо от> остальной части приложения, чтобы избежать передачи сообщения сворачивания. Вы можете добавить аналогичный> код для SC_MAXIMIZE, SC_RESTORE и т. Д.

Как получается, что сообщения сворачивания / разворачивания / восстановления для моих окон Windows не попадают в мое окно? Это потому, что сообщения, предназначенные для окна, отправляются Windows владельцу окна? И в этом случае все формы в приложении Delphi «принадлежат» Application? Разве это не означает, что значение владельца становится равным нулю:

procedure TForm2.CreateParams(var Params: TCreateParams);
begin
   inherited;
   Params.WndParent := 0; //NULL
end;

удалит Application и его оконный дескриптор от вмешательства в мою форму, и Windows должна снова отправить мне мои сообщения о минимизации / максимизации / восстановлении?


Возможно, если бы мы сравнили и сопоставили теперь «нормальное» приложение Windows, которое делает что-то, с тем, как Borland изначально разрабатывал приложения Delphi, чтобы делать что-то - в отношении этого Application объекта и его основного цикла.

  • какое решение было решено Application объектом?
  • Какие изменения были внесены в более поздние версии Delphi, чтобы этих проблем не было?
  • Разве изменение в более поздних версиях Delphi не привело к возникновению других проблем, которые так старался решить исходный дизайн приложения?
  • Как эти новые приложения могут работать без вмешательства приложения?

Очевидно, Borland осознала этот недостаток в своем первоначальном дизайне. Каков был их первоначальный дизайн, какую проблему он решал, в чем был недостаток, в чем был изменен дизайн и как он решает проблему?


person Ian Boyd    schedule 05.02.2010    source источник
comment
Думаю, вам будет интересно узнать об этих двух уловках: yoy.be/item.asp?i89 yoy.be/item.asp?i87   -  person Stijn Sanders    schedule 05.02.2010
comment
@Stinh Sanders: я видел их, они не решают проблемы. Кроме того, никогда, никогда, никогда не передавайте GetDesktopWindow в качестве владельца окна, как предлагается в тех и других сообщениях по этой теме. Это раньше приводило к зависанию Windows. Это была такая проблема, что Microsoft исправила CreateWindow, поэтому любой, кто передает GetDesktopWindow в качестве владельца, заменяется на использование NULL. И если бы я мог редактировать этот пост на yoy.com, я бы это сделал.   -  person Ian Boyd    schedule 05.02.2010
comment
В Токио Application.Handle равен нулю!   -  person Z80    schedule 20.08.2018
comment
«Владелец» формы не зависит от «родителя» формы (но может быть тем же самым). Владелец имеет отношение к тому, как Delphi связывает объекты на основе TComponent друг с другом, поэтому они автоматически освобождаются, когда освобождается «Владелец» (см. Create(AOwner: TComponent). «Родитель» (или «WndParent») имеет отношение к визуальным родительским / дочерним отношениям визуальных элементов управления. Итак, почему каждая форма имеет Application в качестве владельца, потому что Application.CreateForm(TMyForm, MyForm) создает форму, используя себя в качестве владельца. А о родительском дескрипторе Application.Handle см. TCustomForm.CreateParams.   -  person R. Hoek    schedule 03.06.2019


Ответы (3)


Причина появления окна приложения имеет неприятную историю. При разработке Delphi 1 мы знали, что хотим использовать модель пользовательского интерфейса «SDI» (окна разбросаны по всему рабочему столу) для IDE. Мы также знали, что Windows отстойная (и до сих пор не справляется) с этой моделью. Однако мы также заметили, что Visual Basic в то время использовал эту модель, и, похоже, она работала хорошо. При дальнейшем исследовании мы обнаружили, что VB использовал специальное «скрытое» парковочное окно, которое использовалось как «владелец» (Windows иногда размывает понятие родителя и владельца, но различие аналогично VCL) для всех других видимых окон. .

Вот как мы решили «проблему», когда окна, содержащие главное меню, редко когда-либо фокусировались, поэтому обработка Alt-F для меню «Файл» просто не работала. Используя это центральное парковочное окно в качестве посредника, мы могли бы легче отслеживать и перенаправлять сообщения в соответствующие окна.

Эта компоновка также решила другую проблему, когда обычно несколько окон верхнего уровня были полностью независимыми. Если заставить приложение обрабатывать «владельца» всех этих окон, все они будут вести себя согласованно. Например, вы могли заметить, что когда вы выбираете любое из окон приложения, все окна приложения перемещаются вперед и сохраняют свой z-порядок относительно друг друга. Это также заставит приложение свернуть и восстановить как функциональную группу.

Это следствие использования этой модели. Мы могли проделать всю эту работу вручную, чтобы все было в порядке, но философия дизайна заключалась в том, чтобы не изобретать Windows заново, а использовать ее там, где это возможно. Вот почему TButton или TEdit действительно являются классом и стилем окна BUTTON и EDIT в Windows, соответственно.

По мере развития Windows эта модель "SDI" начала терять популярность. Фактически, сама Windows стала «враждебно относиться» к такому стилю приложений. Начиная с Windows Vista и заканчивая 7, оболочка пользователя, похоже, плохо работает с приложением, использующим окно парковки. Итак, мы решили перетасовать вещи в VCL, чтобы убрать окно парковки и перенести его функцию в основную форму. Это представило несколько проблем типа «курица и яйцо», когда нам нужно, чтобы окно парковки было доступно достаточно рано при инициализации приложения, чтобы другие окна могли «присоединиться» к нему, но сама основная форма может быть построена недостаточно скоро. TApplication пришлось преодолеть несколько препятствий, чтобы заставить это работать, и было несколько незначительных крайних случаев, которые вызывали проблемы, но большинство проблем были решены. Однако для любого приложения, которое вы продвигаете вперед, оно будет по-прежнему использовать старую модель окна парковки.

person Allen Bauer    schedule 05.02.2010
comment
+1 за то, что признал, что это было грязно. И хаки Windows XP, добавленные к уровню управления окнами Microsoft, стали частью смертельного звонка этой системы, поскольку начали появляться ужасные ошибки Z-Order, когда вы запускали приложения Delphi 7 на XP. - person Warren P; 10.02.2010
comment
Ваше объяснение не на 100% ясное. Я так понимаю, что в итоге (новые версии Delphi) скрытая форма больше не создается. Думаю, именно поэтому Application.Handle теперь равен нулю. Правильно? - person Z80; 21.08.2018

Все приложения VCL имеют «скрытое» окно верхнего уровня под названием «Приложение». Он создается автоматически при запуске приложения. Помимо прочего, это основной обработчик сообщений Windows для VCL, отсюда и Application.ProcessMessages.

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

Однако не во всех окнах он должен быть родительским, просто Windows, как правило, работает лучше, если это так. Однако любая форма, созданная с помощью Application.CreateForm, будет иметь ее как родительскую, и она также будет принадлежать объекту Application. Поскольку они принадлежат, они будут освобождены после освобождения Приложения. Это происходит за кулисами в Forms.DoneApplication

person Gerry Coll    schedule 05.02.2010
comment
Для форм верхнего уровня вашего приложения свойство Parent не установлено в окне приложения! Для объекта Application устанавливается только владелец. Чтобы уточнить: Application.ProcessMessages обрабатывает сообщения для ВСЕХ окон в основном потоке (все окна VCL), это фактически шаг в обычном цикле обработки сообщений, который есть во всех приложениях Windows GUI. - person Ritsaert Hornstra; 05.02.2010
comment
@Ritsaert Hornstra: Какой дескриптор у форм верхнего уровня моего приложения является их родительским? Также обратите внимание, что любые формы, которые я создаю, действительно имеют Application.Handle в качестве родителя. - person Ian Boyd; 05.02.2010
comment
Вероятно, поэтому saphua.com/minime/minime.aspx не работает правильно с моими приложениями delphi (7). +1 за эту крупицу информации. - person cmw; 05.02.2010
comment
@Ian Boid: Окна верхнего уровня не имеют Родителя - у них есть Владелец, сбивчиво названный Родителем (я имею в виду поле WndParent в аргументе TForm.CreateParams, а не Владелец VCL или Родительский контроль). Вы, вероятно, используете старую версию Delphi, если ваши формы верхнего уровня имеют Application.Handle как владелец (в смысле термина «владелец» Windows). - person kludg; 05.02.2010
comment
@Serge: Зависит от вашего определения старого; Delphi 5. Кто является владельцем окон без родителя в более поздних версиях Delphi? - person Ian Boyd; 05.02.2010
comment
@Ian: Я использую Delphi 2009 и вижу, что владельцем формы по умолчанию является Application.MainForm.Handle. Когда я показываю форму и нажимаю на основную форму, она остается над основной формой (в z-порядке). Я считаю, что раньше все было иначе. - person kludg; 05.02.2010
comment
@Serg: Здесь мы должны прояснить ситуацию. Что вы имеете в виду под формой по умолчанию. Я знаю, что первая форма, которая создается при вызове Application.CreateForm в файле проекта, становится MainForm в том, что касается объекта приложения, и эта форма назначается Application.MainForm. Что вы имеете в виду, когда произносите форму по умолчанию? - person Ian Boyd; 05.02.2010
comment
Я не имел в виду форму по умолчанию, я имел в виду владельца формы по умолчанию, то есть не измененный переопределенным методом CreateParam. Я считаю, что с предыдущими версиями Delphi (сейчас не могу проверить) я мог свободно изменять z-порядок основной формы и других форм, теперь я не могу этого сделать - основная форма всегда остается позади. - person kludg; 05.02.2010
comment
@Ian - D5 11 лет - у меня была версия в 1999 году (исправлены некоторые проблемы Y2k с D3) - person Gerry Coll; 06.02.2010

Глядя на исходный код в forms.pas (Delphi 2009), кажется, что они создают «главное» окно в приложениях с графическим интерфейсом Win32, чтобы разрешить вызовы

  • TApplication.Minimize
  • TApplication.Restore
  • и т.д

Похоже, что сообщения, переданные Application.Handle, пересылаются соответствующим образом MainForm, если он существует. Это позволит приложению реагировать на сворачивание и т. Д., Если главное окно не было создано. Изменив источник проекта, вы можете создать приложение delphi без главного окна.

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

По вашим вопросам:

  • Откуда он? Это дескриптор окна, созданного в TApplication.Create

  • Что это за дескриптор окна? поддельное окно, которое требуется каждому приложению gui delphi как часть абстракции TApplication.

  • Это дескриптор окна главной формы приложения Нет

  • Если это не дескриптор основной формы приложения, то что это? См. выше

  • что еще более важно: почему это конечный родитель для каждой формы? если вы правы, что это конечный родительский элемент, я предполагаю, что это так, потому что он позволяет легко найти все формы в вашем приложении. (перечисляя потомков этой «основной» формы).

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

Во всяком случае, это мое мнение. Вы, вероятно, сможете узнать больше, посмотрев объявление и код TApplication в forms.pas. Суть в том, что я вижу, это удобная абстракция.

С наилучшими пожеланиями,

Дон

person Don Dickinson    schedule 05.02.2010
comment
В Delphi 2007 VCL изменен по умолчанию на отсутствие скрытого окна, но вы также можете выбрать старый способ, если это поможет. Скрытое окно препятствовало правильной работе предварительного просмотра Windows 7. - person mj2008; 05.02.2010
comment
@ mj2008: у вас есть ссылка на дополнительную информацию по этому поводу? В настоящее время я обновляю проект с C ++ Builder 2006 - ›C ++ Builder 2009, и мне кажется, что мое приложение-› Handle ptr отображается как NULL. Так ли обстоит дело в Builder 2009? И если да, то будет ли использование MainForm- ›Handle хорошей заменой? - person Rob Thomas; 07.06.2010
comment
@Rob Элемент управления Delphi для этого - Application.MainFormOnTaskbar, но я не знаю, подходит ли он для вас. Обычно в обновленном приложении это не меняется. - person mj2008; 09.06.2010