Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi-如何找出哪个模态对话框具有焦点并将其置于前端?_Delphi_Delphi 2006 - Fatal编程技术网

Delphi-如何找出哪个模态对话框具有焦点并将其置于前端?

Delphi-如何找出哪个模态对话框具有焦点并将其置于前端?,delphi,delphi-2006,Delphi,Delphi 2006,我有一个Delphi2006应用程序,它可以弹出一个模式对话框来响应错误条件。它似乎进入了这样一种状态:其中一个模态对话框处于打开状态,位于主窗体前面,但两个窗体都没有响应消息。点击其中一个会发出“砰”的一声。应用程序运行正常,用户界面正在更新主窗体,但您无法执行任何操作。我猜在主窗体下很可能有另一个模态对话框。不管是我的还是窗户上的,我都不知道 其他要点: 应用程序响应键盘快捷键OK。其中一个短消息优雅地关闭了应用程序,这就起作用了。从那以后,我一直无法再现这种情况 该应用程序有一个托盘图标

我有一个Delphi2006应用程序,它可以弹出一个模式对话框来响应错误条件。它似乎进入了这样一种状态:其中一个模态对话框处于打开状态,位于主窗体前面,但两个窗体都没有响应消息。点击其中一个会发出“砰”的一声。应用程序运行正常,用户界面正在更新主窗体,但您无法执行任何操作。我猜在主窗体下很可能有另一个模态对话框。不管是我的还是窗户上的,我都不知道

其他要点:

  • 应用程序响应键盘快捷键OK。其中一个短消息优雅地关闭了应用程序,这就起作用了。从那以后,我一直无法再现这种情况
  • 该应用程序有一个托盘图标。这是对鼠标右键单击的响应。如果我从这里最小化应用程序,主窗体将最小化并保持模式对话框显示,仍然没有焦点。如果我恢复主窗体,事情就会恢复原样,两个窗口都没有焦点。Alt选项卡也有类似的结果
  • 平台是Windows7
  • 在创建任何窗体之前,我调用DisableProcessWindowsGhosting
  • 我使用打开模态对话框

    ModalDialog.PopupParent := MainForm ;
    ModalDialog.ShowModal ;
    
  • 如果其他模式对话框打开,我会推迟这些错误对话框:

    if (Application.ModalLevel = 0) then
        {open modal dialog}
    
我的问题分为两部分:

有没有一种方法可以通过编程找出哪个窗口有焦点?然后,我可以针对这种情况采取一些措施,或者是最后一种手段,我可以让他们提供一个快捷键,将其带到最前面,或者采取一些规避措施(取决于对话框),例如将ModalResult设置为mrCancel

这种情况怎么会出现呢?通常,当我在主窗体后面看到一个模式对话框时(我可以通过打开模式对话框,从任务栏图标最小化应用程序,然后再次恢复应用程序-应用程序主窗体在对话框前面恢复,对话框仍然保持焦点),我可以通过单击任务栏图标将其再次带到前面,或者用
Esc
键将其关闭,但在这种情况下不起作用

**更新**

Misha的修复与TSaveDialog等非delphi对话无关。通过添加
Application.ModalPopupMode:=pmAuto,我也可以让它们正常工作就在调用
Execute
之前

“让它工作”是指保存对话框在以下顺序之后位于前面:

  • 打开保存对话框
  • 从托盘图标最小化应用程序
  • 从托盘图标还原应用程序
而它位于主窗体后面,没有
ModalPopupMode:=pmAuto

因此,我希望这些变化将有助于解决这个(尚未产生的)问题

GetForegroundWindow()是您正在寻找的函数,如果您知道模式窗口的标题或句柄,那么它很简单

HWND getforegroughindow()

检索前景窗口的句柄(用于 用户当前正在工作)。系统将分配稍高的值 创建前景窗口的线程的优先级高于它的优先级 到其他线程


如果具有焦点的表单响应消息(Form1)的时间太长,以致Windows认为Form1没有响应,Form1随后显示一个模式表单(Form2),则在显示Form2且应用程序再次处理消息后,Form1将被带到前面,从而可能“覆盖”Form2

将其放入Application.OnIdle事件将实现以下功能:

  if Assigned(Screen.ActiveForm) then
  begin
    if (fsModal in Screen.ActiveForm.FormState) and
       (Application.DialogHandle <= 0)) then 
    begin
      Screen.ActiveForm.BringToFront;
    end;
  end;
如果已分配(Screen.ActiveForm),则
开始
if(Screen.ActiveForm.FormState中的fsModal)和

(Application.DialogHandle可以通过以下方式查询最后一个激活的弹出窗口(VCL或非VCL):

这是从
TApplication.BringToFront
复制的

将此窗口移到前面可以通过以下方式完成:


请注意,
Application.BringToFront
可能会完全起作用,但我曾经体验过它不能正常工作,这是一种我无法重现的情况。

我使用了米莎的解决方案,并进一步(使用NGLN的代码)解决了罗斯麦克看到的问题(处理非VCL对话框)

以下代码正在计时器中运行:

type
  TCustomFormAccess = class(TCustomForm);


if Assigned(Screen.ActiveCustomForm) then
begin
  if ((fsModal in Screen.ActiveCustomForm.FormState) and
      (Application.DialogHandle <= 0)) then
  begin
    TopWindow := GetLastActivePopup(Application.Handle);
    TopWindowForm := nil;
    for i := 0 to Screen.CustomFormCount - 1 do
    begin
      CustomFormAccess := TCustomFormAccess(Screen.CustomForms[i]);
      if CustomFormAccess.WindowHandle = TopWindow then TopWindowForm := CustomFormAccess;
    end;
    if Assigned(TopWindowForm) and (Screen.ActiveCustomForm.Handle <> TopWindow) then
    begin
      Screen.ActiveCustomForm.BringToFront;
    end;
  end;
end;
类型
TCustomFormAccess=类(TCustomForm);
如果已分配(Screen.ActiveCustomForm),则
开始
if((Screen.ActiveCustomForm.FormState中的fsModal)和

(Application.DialogHandle找出哪个窗口有焦点并采取补救措施是没有帮助的。您需要从源头上解决问题。这意味着了解窗口的所有权(即POUPPARENT)。谢谢@David。找出哪个窗口有焦点肯定会有帮助,因为它可能会告诉我如何重现问题,而这是我目前无法做到的。一旦你发现问题,你应该能够使用Spy++之类的工具来理解所有权关系。我会尝试从外部调试此问题,至少在你找到之前一个复制。你通常可以通过在你的调试构建中添加{$ifdef DEBUG}Sleep(5000);{endif}来复制它-需要一些实验来找到最“破坏性”的地方。这个z顺序地狱的神奇触发因素是windows认为您的应用程序没有响应。谢谢@Misha。这似乎确实起到了作用,除非有一个非VCL对话框打开,例如加载文件对话框,所以我们还没有完全打开。我使用了一个计时器,因为OnIdle事件没有触发(例如)如果您打开了上下文菜单,您需要检查
分配(Screen.ActiveCustomForm)
。我正在托盘中运行我的应用程序,其中
Application.ShowMainForm:=False
,如果我在没有显示表单时调用
GetTopWindow
,它会抛出AV。
SetForegroundWindow(GetTopWindow);
type
  TCustomFormAccess = class(TCustomForm);


if Assigned(Screen.ActiveCustomForm) then
begin
  if ((fsModal in Screen.ActiveCustomForm.FormState) and
      (Application.DialogHandle <= 0)) then
  begin
    TopWindow := GetLastActivePopup(Application.Handle);
    TopWindowForm := nil;
    for i := 0 to Screen.CustomFormCount - 1 do
    begin
      CustomFormAccess := TCustomFormAccess(Screen.CustomForms[i]);
      if CustomFormAccess.WindowHandle = TopWindow then TopWindowForm := CustomFormAccess;
    end;
    if Assigned(TopWindowForm) and (Screen.ActiveCustomForm.Handle <> TopWindow) then
    begin
      Screen.ActiveCustomForm.BringToFront;
    end;
  end;
end;