【问题标题】:Delphi application main form moving behind other windows on modal closeDelphi 应用程序主窗体在模式关闭时移动到其他窗口后面
【发布时间】:2016-03-17 15:55:08
【问题描述】:

我开始遇到关闭模式表单时主表单消失在其他应用程序窗口后面的问题,我希望有人以前会遇到(并解决!)这个问题,或者有关于在哪里定位断点进行调试的建议问题。

我的问题最初始于经典的“害羞对话框”问题,模态对话框出现在间歇性出现的主窗体下。为了尝试对此进行排序,我将所有模态表单的 popupmode 更改为 pmAuto 并添加了 Application.ModalPopupMode := pmAuto;Application.MainFormOnTaskBar := true; 到我的应用程序dpr。

现在我在关闭模式弹出窗口时让主窗体消失在其他窗口后面。我怀疑该行为主要是在模式表单打开第二个窗口时引起的(MessageDlg 和直接Form.create(Application); Form.show; 都有问题),尽管显示/免费代码没有明显问题(ShowModal表单创建所有者 = nil,无模式,所有者 = 应用程序)。在这两种情况下,表单都会在关闭第一个原始模式表单时消失,但在不触发新表单/对话框出现的情况下操作模式表单似乎可以按预期工作。

在主窗体的后台还有其他令人讨厌的事情发生,刷新计时器激活了后台线程,但通常在看到它不起作用的时间内这并没有触发。除此之外,我们还通过第三方 DLL 触发对远程服务器的调用(该应用程序实际上是一个客户端 GUI)。

令人讨厌的是,我无法获得一个小程序来模拟该行为,并且在 IDE 中运行使查看该行为变得困难,因为 IDE 本身包含许多混淆 Z 顺序的窗口。

编辑 - 在下面写下我的答案后,我似乎收到了一个发送到应用程序的停用事件(我可以通过 Application.OnDeactivate 捕获它) - 它似乎类似于WPF App loses completely focus on window close Delphi 没有激活方法c# 解决方案有,但我会玩一些 Windows 消息传递,看看我是否能到达任何地方

【问题讨论】:

  • 你需要做一些调试。您说 IDE 使事情变得混乱。这是正常的。但并非所有调试都应在 IDE 中完成。事实上,对于这种调试,IDE 通常是无用的。使用跟踪调试。记录。您需要记录的是每个顶级窗口的所有者。我不是指TComponent 属性Owner。意思是 Win32 窗口所有者。通过调用GetWindow 传递GW_OWNER 找出它是什么。使用一些记录工具记录它。 OutputDebugString 就足够了。然后你会看到所有权结构被打破了。那么你需要找出原因。
  • 你会想要熟悉这个:msdn.microsoft.com/en-us/library/windows/desktop/ms632599.aspx,特别是这个一个拥有的窗口在 z 顺序中总是在它的所有者之上。 主窗体的窗口通常应位于所有权链的顶端。您的主窗体窗口显然不是。您需要找出原因。
  • 您的症状是您的非模态窗口没有及时启用。我不是说是这样,但要检查是一回事。无法激活禁用的窗口,因此如果在模式对话框关闭时未启用调用表单,则窗口管理器会自行选择另一个窗口。

标签: delphi delphi-xe2


【解决方案1】:

按照 David 在 cmets 中的建议,我创建了一个在启动时创建的小日志表单,其中包含备忘录、计时器和以下 OnTimer 事件:

procedure TForm1.Timer1Timer(Sender: TObject);
  function logtomemo(aHandle: HWND): TWinControl;
  var
    form: TWinControl;
  begin
    form := findControl(ahandle);
    if form <> nil then
      memo1.Lines.Add(format('handle %d - form %s', [ahandle, form.Name]));
    result := form;
  end;
var
  handle: HWND;
  form: TWinControl;

begin
  memo1.Clear;
  handle := application.ActiveFormHandle;
  repeat
    form := logtomemo(handle);
    handle := GetWindow(handle, GW_OWNER);
  until (handle = application.MainFormHandle) or (form = nil);
  logtomemo(handle);
end;

四处点击我注意到,当我在我的应用程序外部单击时,我们的启动表单作为列表中唯一的表单出现。 (从历史上看,我们的初始屏幕仅在 Application.Run 之后才被释放,因为它们曾经出于某种原因在其上保留一些其他引用 - 在我的时间之前并且不再需要了)。

在 Application.Run 似乎解决了问题之前更改了要销毁的启动画面的生命周期 - 我从来没有想过会是一百万年后的原因。

需要最终确认,一旦我摆脱了这个小调试表单,它就不会再次出现,但希望现在已经解决了困扰我几天的问题 - 谢谢!

编辑

正如我在我的编辑和对该问题的 cmets 中所指出的,上述调试不起作用,因为新表单的存在“解决了”问题。更改代码以将输出发送到事件日志或文本文件而不需要表单也没有显示任何内容 - Z 顺序中的所有表单都保留在原位。

最后,通过将以下代码附加到 Application.OnModalEnd,我能够解决症状而不是原因

if Application.ModalLevel = 0 then
  Windows.SetActiveWindow(Application.MainFormHandle);

这会在最后一个模式对话框关闭后成功地将活动窗口设置回主窗体。

如果用户期望一个不是主要表单的非模态表单重新获得焦点,这可能会产生一些副作用,但是我们的应用程序架构并没有真正遵循这种结构,并且使用 Application.MainFormOnTaskbar,主要表单不会隐藏其他表单(只要它们不是无父级的)

【讨论】:

  • 日志记录是一个被低估的调试工具。很高兴它在这里被证明如此具有启发性。
  • @DavidHeffernan 说得太早了 :-( - 我的调试表单的存在解决了这个问题,但是当我再次删除它时它又出现了。看来我的应用程序正在收到一条发送给它的停用消息,但很难从哪里找到
  • 您需要以非侵入方式正确登录。我已经告诉过你了。
  • @DavidHeffernan 在找到表格后直接做了。我的所有权列表没有任何问题,但我收到一条 DEACTIVATE 消息被发送到我的应用程序 - 虽然不知道如何捕捉发送它的内容,但我已将类似的 C# 问题与问题联系起来
猜你喜欢
  • 2019-01-05
  • 2023-01-24
  • 2015-09-25
  • 2012-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-17
相关资源
最近更新 更多