Delphi:什么是Application.Handle?

什么是TApplication.Handle

  • 它从何而来?
  • 它为什么存在?
  • 而最重要的是:为什么所有的表单都将它作为父窗口来处理呢?

delphi的帮助说:

TApplication.Handle

提供对应用程序主窗体(窗口)的窗口句柄的访问。

 property Handle: HWND; 

描述

调用需要父窗口句柄的Windows API函数时使用句柄。 例如,显示其自己的顶级popup窗口的DLL需要父窗口在应用程序中显示其窗口。 使用Handle属性可以使这些窗口成为应用程序的一部分,这样它们就可以最小化,恢复,启用和禁用应用程序。

如果我把注意力集中在“ 应用程序主窗体的窗口句柄 ”这个词上,我把它理解为应用程序主窗体的窗口句柄 ,那么我可以比较一下:

  • “应用程序主窗体的窗口句柄”
  • ApplicationMainForm的窗口句柄

但他们不一样:

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

那么Application.Handle是什么?

  • 它从何而来?
  • 什么是Windows®窗口句柄?
  • 如果它 Application MainForm的Windows®窗口句柄,那为什么它们不匹配呢?
  • 如果它不是 Application MainForm的窗口句柄,那么它是什么?
  • 更重要的是:为什么它是每种forms的最终父母?
  • 而最重要的是:为什么一切都变得不合时宜,如果我试图让一个窗体被取消pipe理(所以我可以出现在TaskBar上),或者尝试使用类似IProgressDialog的东西?

真的,我问的是:使Application.Handle存在的devise原理是什么? 如果我能理解为什么,那么应该如何变得明显。


通过二十个问题的游戏更新理解:

在谈到通过使其所有者为null来使窗口出现在任务栏上的解决scheme时, Peter Below在2000年说 :

这可能会导致一些问题与二级表格显示的模式forms。

如果用户在模态表单启动时切换到应用程序,然后返回到显示该模式的表单,模式表单可能会隐藏在表单下方。 有可能通过确保模式forms被用于显示它的forms来处理这个问题(使用上面的`params.WndParent“)

但是这对于Dialogs单元和exception的标准对话是不可能的,它们需要更多的努力来使它们正常工作(基本上处理Application.OnActivate ,通过GetLastActivePopup寻找Application.OnActivate的模式forms,并将它们带到Z-order通过SetWindowPos )。

  • 为什么模式表单最终会被卡在其他表单之后呢?
  • 什么机制通常将模态forms带到前面,为什么它在这里不起作用?
  • 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; 

但是他提醒说:

如果您在另一个应用程序处于活动状态时单击辅助表单任务栏button,则仍会将所有应用程序表单放在前面。 如果你不希望有select

当表单的所有者仍然是Application.Handle时,谁将所有表单带到最前面。 应用程序正在做这个? 为什么这样做? 而不是这样做, 应该这样做吗? 这样做的缺点是什么? 我发现这样做的弊端(系统菜单不起作用,任务栏button缩略图不准确,Windows®shell不能最小化窗口。


在处理Application另一篇文章中, Mike Edenfield说,父窗口发送其他窗口的最小化,最大化和还原消息 :

这将为您的表单添加任务栏button,但还有一些其他小细节需要处理。 最明显的是,你的表单仍然收到最小化/最大化发送到父窗体(应用程序的主要forms)。 为了避免这种情况,可以通过添加一行来安装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; 

请注意,此处理程序以您希望独立于应用程序的其余部分的方式运行,以避免传递最小化消息。 您可以为SC_MAXIMIZE,SC_RESTORE等添加类似的代码

它是如何最小化/最大化/还原我的Windows®窗口消息不会去我的窗口? 这是因为发送给窗口的消息是由Windows®发送到窗口的所有者? 在这种情况下,Delphi应用程序中的所有表单都由Application程序“拥有”? 这不意味着使所有者为空:

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

将删除Application ,它的窗口处理干扰我的forms,Windows应该再次给我的最小化/最大化/还原消息?


也许如果我们现在比较一下“正常”的Windows应用程序,那么Borland最初是如何deviseDelphi应用程序来完成这件事的呢?就这个Application对象而言,它是主循环。

  • Application对象解决什么问题?
  • delphi后来的版本做了什么改变,使这些相同的问题不存在?
  • Delphi的后续版本中的变化是否引入了其他问题,即初始应用程序devise如何努力解决?
  • 这些新的应用程序如何在没有应用程序干扰的情况下仍能正常工作?

Borland显然意识到了他们最初devise中的缺陷。 他们最初的devise是什么,它解决了什么问题,这个缺陷是什么,重新devise了什么,以及它是如何解决问题的?

应用程序窗口的原因有一点肮脏的历史。 在开发Delphi 1的时候,我们知道我们想要使用IDE的“SDI”(分布在整个桌面上的窗口)ui模型。 我们也知道Windows在这个模型中吸引(而且仍然)。 但是我们也注意到当时的Visual Basic使用了这个模型,而且看起来效果很好。 经过进一步的检查,我们发现VB使用了一个特殊的“隐藏”的停车窗口作为“所有者”(Windows时常模糊父母和拥有者的概念,但区别与VCL类似) 。

这就是我们如何解决包含主菜单的窗口很less被聚焦的“问题”,所以处理文件菜单的Alt-F根本不起作用。 通过使用这个中央停车窗口作为中介,我们可以更容易地跟踪和路由消息到适当的窗口。

这种安排也解决了另一个问题,通常多个顶层窗口是完全独立的。 通过使应用程序处理所有这些窗口的“所有者”,它们都将performance得一致。 例如,您可能已经注意到,当您select任何应用程序窗口时, 所有的应用程序窗口将移动到前面,并相对于彼此保留它们的z顺序。 这也将使应用程序最小化和恢复作为一个function分组。

这是使用这个模型的结果。 我们可以手动完成所有这些工作,以保持直线,但devise理念是不重新发明Windows,而是在可能的地方利用它。 这也是为什么TButton或TEdit分别是Windows“用户”button和编辑窗口类和样式。

随着Windows的发展,这种“SDI”模式开始失宠。 事实上,Windows本身就开始对这种应用风格产生“敌意”。 从Windows Vista开始,继续到7,用户shell似乎不适用于使用停车窗口的应用程序。 所以,我们着手在VCL中洗牌,以消除停车窗口,并将其function移到主窗体中。 这提出了几个“鸡和鸡蛋”的问题,我们需要在应用程序初始化时足够早地提供停车窗口,以便其他窗口可以“附加”到它,但是主窗体本身可能不会很快被构build。 应用程序必须通过几个环节才能实现这个目标,并且有一些微妙的边缘案例已经引起了问题,但是大部分的问题已经被解决了。 但是,对于任何您前进的应用程序,它将继续使用较旧的停车窗模型。

所有VCL应用程序都有一个名为Application的“隐藏”顶级窗口。 这是在应用程序启动时自动创build的。 除此之外,它是VCL的主要窗口消息处理程序 – 因此是Application.ProcessMessages。

隐藏应用程序顶层窗口确实会导致一些奇怪的事情,特别是在任务栏中显示的不完整的系统菜单以及Vista中不正确的缩略图窗口。 delphi的更高版本纠正这一点。

但是,并不是所有的窗口都必须作为父窗口,如果是Windows,它往往会更好地工作。 但是,使用Application.CreateForm创build的任何表单都将其作为父级,并且也将由Application对象拥有。 由于他们拥有,一旦申请被释放,他们将被释放。 这发生在Forms.DoneApplication的幕后

从forms.pas(Delphi 2009)中查看源代码,似乎他们在win32 gui应用程序中创build了一个“主”窗口,以允许调用

  • TApplication.Minimize
  • TApplication.Restore
  • 等等

看来传递给Application.Handle消息会根据情况转发给MainForm (如果存在)。 这将允许应用程序响应最小化等,如果主窗口尚未创build。 通过修改项目源,你可以创build一个没有主窗口的delphi应用程序。

在这种情况下,即使您没有创build主窗口, TApplication方法仍然可以正常工作。 不知道我是否抓住了所有的目的,但是我没有时间去浏览所有的TApplication代码。

根据你的问题:

  • 它从何而来? 它是在TApplication.Create创build的一个窗口的句柄

  • 什么窗口处理? 每个gui delphi应用都需要作为TApplication抽象的一部分的假窗口

  • 它是应用程序主窗体的窗口句柄

  • 如果它不是应用程序的mainform的句柄那么它是什么? 往上看

  • 更重要的是:为什么它是每种forms的最终父母? 假设你是正确的,它的最终父母,我认为这是因为它可以很容易地find你的应用程序中的所有forms(枚举这个“主”forms的孩子)。

  • 最重要的是:为什么一切都变得不合时宜,如果我试图让一个表单被忽略,我认为是因为隐藏的“主”表单正在获取应该传递给它的子系统和/或mainform的系统消息,但是找不到未经授权的forms。

无论如何,这是我的承担。 您可以通过在forms.pas查看TApplication声明和代码来了解更多forms.pas 。 从我看到的底线是一个方便的抽象。

最好的祝福,