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"sMainForm тогда почему они не совпадают?
  • Если этоне дескриптор окна Application"sMainForm, тогда что это?
  • Что еще более важно: почему это основнойродительский владелец каждой формы?
  • И самое главное: почему все идет наперекосяк, если я пытаюсь, чтобы форма быланепареной, не принадлежащей ей (чтобы она могла отображаться на панели задач), или пытаюсь использовать что-то вроде 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 и это окно Handle от вмешательства в мою форму, и Windows должна снова отправить мне мои сообщения об уменьшении / увеличении / восстановлении?


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

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

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

3 ответа

Решение

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

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

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

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

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

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

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

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

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

  • TApplication.Minimize
  • TApplication.Restore
  • так далее

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

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

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

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

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

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

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

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

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

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

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

дон

Другие вопросы по тегам