繁体   English   中英

Delphi:什么是Application.Handle?

[英]Delphi: What is Application.Handle?

什么是TApplication.Handle

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

德尔福帮助说:

TApplication.Handle

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

 property Handle: HWND; 

描述

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

如果我专注于“ 应用程序主窗体的窗口句柄 ”这个词,我认为它是指应用程序主窗体的窗口句柄 ,那么我可以比较:

  • “应用程序主窗体的窗口句柄”,用
  • Application MainForm的窗口句柄

但他们不一样:

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

什么是Application.Handle

  • 它从何而来?
  • Windows®窗口处理的是什么?
  • 如果它 ApplicationMainForm的Windows®窗口句柄,那么为什么它们不匹配?
  • 如果它不是 ApplicationMainForm的窗口句柄,那么它是什么?
  • 更重要的是:为什么它是每种形式的最终 父母 所有者?
  • 最重要的是:如果我试图让一个表格无 主义 (因此我可以出现在TaskBar上),或者尝试使用像IProgressDialog这样的东西,为什么一切都变得 混乱

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


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

在谈到通过使其所有者为null在任务栏上显示窗口的解决方案时,2000年的Peter Below说

这可能会导致从次要形式显示的模态形式出现一些问题。

如果用户在模态表单启动时从应用程序切换,然后返回到显示它的表单,则模式表单可能隐藏在表单下方。 可以通过确保模态形式是父级来处理这个问题[原文如此; 他的意思是拥有]到显示它的形式(使用params.WndParent如上)

但是,这是不可能的从标准对话框Dialogs单元和例外 ,这需要更多的努力让他们的工作权利(基本处理Application.OnActivate ,寻找通过父级应用模式表单GetLastActivePopup并把他们的顶通过SetWindowPos Z顺序)。

  • 为什么模态形式最终会落后于其他形式?
  • 什么机制通常会将模态形式带到前面,为什么它在这里不起作用?
  • 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另一篇文章中, Mike Edenfield说父窗口发送其他窗口的最小化,最大化和恢复消息

这将为您的表单添加任务栏按钮,但还有一些其他小细节需要处理。 最明显的是,您的表单仍然会收到最小化/最大化,并将其发送到父表单(应用程序的主要表单)。 为了避免这种情况,您可以通过添加以下行来为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 “拥有”? 这不意味着让所有者为null:

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

将删除Application和它的窗口处理干扰我的表单,Windows应该再次发送给我的mimimize / maximize / restore消息?


也许如果我们比较和对比现在一个“正常”的Windows应用程序做的事情,Borland最初设计Delphi应用程序来做事情 - 关于这个Application对象和它的主要循环。

  • Application对象解决了什么解决方案?
  • 使用更高版本的Delphi进行了哪些更改,以便不存在这些相同的问题?
  • Delphi的后续版本中的更改是否没有引入其他问题,因为初始应用程序设计试图如此难以解决?
  • 如果没有应用程序干扰它们,这些较新的应用程序如何运行?

很显然,Borland意识到了他们最初设计的缺陷。 他们的初始设计是什么,解决了什么问题,缺陷是什么,重新设计是什么,以及它如何解决问题?

应用程序窗口的原因有点肮脏的历史。 在开发Delphi 1时,我们知道我们想要为IDE使用“SDI”(遍布桌面的窗口)ui模型。 我们也知道Windows在该模型上吸引了(并且仍然存在)。 但是我们也注意到当时的Visual Basic使用了该模型,它似乎运行良好。 经过进一步检查,我们发现VB使用了一个特殊的“隐藏”停车窗口,用作所有其他可见窗口的“所有者”(Windows模糊了父级和所有者的概念,但区别类似于VCL) 。

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

这种安排还解决了通常多个顶级窗口完全独立的另一个问题。 通过使应用程序处理所有这些窗口的“所有者”,它们都会表现得很好。 例如,您可能已经注意到,当您选择任何应用程序窗口时, 所有应用程序窗口都会移到前面并保持相对于彼此的z顺序。 这也可以使应用程序最小化并恢复为功能分组。

这是使用此模型的结果。 我们可以手动完成所有这些工作以保持平稳,但设计理念是不重新发明Windows,而是尽可能地利用它。 这也是为什么一个TButton或TEDIT是一个真正的Windows“用户”按钮,编辑窗口类和风格,分别。

随着Windows的发展,“SDI”模式开始失宠。 事实上,Windows本身开始变得对这种应用程序“充满敌意”。 从Windows Vista开始并继续到7,用户shell似乎无法与使用停车窗口的应用程序一起使用。 因此,我们开始在VCL中改变现状以消除停车窗口并将其功能转移到主窗体中。 这提出了几个“鸡和鸡蛋”问题,我们需要在应用程序初始化中尽早提供停车窗口,以便其他窗口可以“附加”到它,但主窗体本身可能不会很快构建。 TApplication必须通过几个环节来实现这一点,并且有一些微妙的边缘情况引起了问题,但大多数问题已经解决了。 但是,对于您前进的任何应用程序,它将继续使用旧的停车窗口模型。

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

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

但是,并非所有窗口都必须将它作为父级,Windows只是倾向于更好地工作。 但是,使用Application.CreateForm创建的任何表单都将将其作为父项,并且它也将由Application对象拥有。 由于它们是拥有的,因此一旦应用程序被释放,它们将被释放。 这发生在Forms.DoneApplication的幕后

通过查看forms.pas(Delphi 2009)中的源代码,看起来他们在win32 gui应用程序中创建了一个“主”窗口,允许调用

  • TApplication.Minimize
  • TApplication.Restore
  • 等等

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

在这种情况下,即使您尚未创建主窗口, TApplication方法仍然可以工作。 不确定我是否掌握了所有目的,但我没有时间完成所有的TApplication代码。

根据你的问题:

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

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

  • 它是应用程序的主要形式 No 的窗口句柄

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

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

  • 最重要的是:为什么一切都变得混乱,如果我试图让一个表格无表情我认为因为隐藏的“主”表单正在获取它应该传递给它的子节点和/或主表单的系统消息,但是找不到无表格的形式。

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

最好的祝福,

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM