Delphi-如何找出哪个模态对话框具有焦点并将其置于前端?
我有一个Delphi2006应用程序,它可以弹出一个模式对话框来响应错误条件。它似乎进入了这样一种状态:其中一个模态对话框处于打开状态,位于主窗体前面,但两个窗体都没有响应消息。点击其中一个会发出“砰”的一声。应用程序运行正常,用户界面正在更新主窗体,但您无法执行任何操作。我猜在主窗体下很可能有另一个模态对话框。不管是我的还是窗户上的,我都不知道 其他要点:Delphi-如何找出哪个模态对话框具有焦点并将其置于前端?,delphi,delphi-2006,Delphi,Delphi 2006,我有一个Delphi2006应用程序,它可以弹出一个模式对话框来响应错误条件。它似乎进入了这样一种状态:其中一个模态对话框处于打开状态,位于主窗体前面,但两个窗体都没有响应消息。点击其中一个会发出“砰”的一声。应用程序运行正常,用户界面正在更新主窗体,但您无法执行任何操作。我猜在主窗体下很可能有另一个模态对话框。不管是我的还是窗户上的,我都不知道 其他要点: 应用程序响应键盘快捷键OK。其中一个短消息优雅地关闭了应用程序,这就起作用了。从那以后,我一直无法再现这种情况 该应用程序有一个托盘图标
- 应用程序响应键盘快捷键OK。其中一个短消息优雅地关闭了应用程序,这就起作用了。从那以后,我一直无法再现这种情况
- 该应用程序有一个托盘图标。这是对鼠标右键单击的响应。如果我从这里最小化应用程序,主窗体将最小化并保持模式对话框显示,仍然没有焦点。如果我恢复主窗体,事情就会恢复原样,两个窗口都没有焦点。Alt选项卡也有类似的结果李>
- 平台是Windows7
- 在创建任何窗体之前,我调用DisableProcessWindowsGhosting
- 我使用打开模态对话框
ModalDialog.PopupParent := MainForm ; ModalDialog.ShowModal ;
- 如果其他模式对话框打开,我会推迟这些错误对话框:
if (Application.ModalLevel = 0) then {open modal dialog}
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;