模态窗口的“最小化”行为的正确处理

8
我有以下问题:我们正在构建一个相当大的应用程序(win32,Delphi 6 Enterprise)。在应用程序的几个部分中,通常包含主窗口选择的详细信息的模态窗口被使用。
我们修改了WM_SYSCOMMAND消息的处理方式,以便如果窗口是模态的,则会向应用程序的主窗口发送SW_SHOWMINNOACTIVE消息。这将导致整个应用程序最小化,而不仅仅是模态窗体。
然而,在特定情况下会出现问题:如果调用窗口设置为全屏,则在恢复时,模态窗口将出现在(禁用的)最大化主窗口下方(似乎在Windows 7上发生)。
我的问题有两个:
首先,当应用程序不再恢复时,我似乎没有收到任何syscommand消息,因此我无法引入代码来恢复Z-Order,因为我不知道该放置在哪里。 其次,我认为,如果整个应用程序都被最小化,那么在任务栏中单击应用程序的按钮应该以相同的状态恢复它,而不是在其下方出现模态窗口。是否有修复的方法?
编辑:我们进行了一些额外的测试,发现我们实际上可以在主窗体的WM_ACTIVATE处理程序中检测到问题。在那个阶段,我们也可以识别模态窗口。然而,我找不到将其恢复到Z-Order顶部的方法。
编辑2:这是将模态窗体最小化时最小化应用程序的代码:
procedure TfmGITForm.WMSysCommand(var Message: TWMSysCommand);
begin
  if (fsModal in FormState) or
      not Application.MainForm.Visible then
  begin
    case Message.CmdType of
      SC_MINIMIZE:
      begin
        ShowWindow(Application.Handle, SW_SHOWMINNOACTIVE);
      end;
      SC_RESTORE:
      begin
        ShowWindow(Application.Handle, SW_SHOWNORMAL);
        inherited;
      end;
    else
      inherited;
    end;    // case
  end
  else
    inherited;
end;

所有的表单都来源于那个表单。

嗯,我记得在 Delphi 6 中有类似的问题,通过引入“PopupParent”属性及其相关属性,我认为在更近期的 Delphi 版本中已经改进了。其他人肯定会更熟悉具体细节。 - David Heffernan
你能详细说明一下发送 SW_SHOWMINNOACTIVE 的作用吗?这样做是为了让模态窗体最小化时整个应用程序都被最小化吗?如果是的话,建议尝试调用 Application.Minimize - David Heffernan
另一个问题:模态窗口的所有者是哪个窗口?我的意思是窗口所有者,而不是 VCL 所有者。 - David Heffernan
1
我最近犯了同样的错误,正确的写法是case Message.CmdType and $FFF0 of。请参考WM_SYSCOMMAND中的注释。 - Sertac Akyuz
@David Heffernan: 我几乎可以确定这是隐藏的TApplication窗口。 标题是全局TApplication实例的标题,类名为TApplication,并且由getWindow(handle,GW_OWNER)返回的句柄与Application.Handle返回的句柄相同。 - Stephane
显示剩余6条评论
2个回答

3

覆盖对话框的CreateParams函数并将Params.WndParent设置为全屏窗口(或者如果您正确拥有,则为Owner.Handle)。 默认值为Application.Handle,这将导致此类问题。在后来的Delphi版本中引入的PopupParent属性完全相同。


1
这与Windows中引入的窗口幽灵化有关,我认为是在XP中引入的。我在这些操作系统上的D5应用程序中遇到了相同的问题。当时Peter Below提供了以下解决方法,现在仍然对我有用:
procedure DisableProcessWindowsGhosting;
type
  TDisableProcessWindowsGhostingProc = procedure; stdcall;
const
  sUser32 = 'User32.dll';
var
  ModH: HMODULE;
  _DisableProcessWindowsGhosting: TDisableProcessWindowsGhostingProc;
begin
  ModH := GetModuleHandle(sUser32);
  if ModH <> 0 then begin
    @_DisableProcessWindowsGhosting := nil;
    @_DisableProcessWindowsGhosting := GetProcAddress(ModH,
        'DisableProcessWindowsGhosting');
    if Assigned(_DisableProcessWindowsGhosting) then begin
      _DisableProcessWindowsGhosting;
    end;
  end;
end;

我在应用程序主窗体的OnCreate处理程序的开头调用它。


1
不确定你从哪里得到了鬼影效应。一般来说,禁用鬼影效应并不理想 - 保持推送你的队列! - David Heffernan
@David:一般来说不是一个好主意。然而,鬼窗口在不同的时间被创建。当应用程序无响应(我想你是在暗示通过排队来解决这个问题)时,只是其中之一。最小化应用程序窗口(你知道那个隐藏的窗口)是/曾经是另一个问题。D5、D6和可能的D7应用程序都会受到影响,在这种情况下,告诉Windows不要为此进程创建鬼窗口似乎对我来说是可以接受的,因为另一种选择——一个模态对话框隐藏在你的主窗体后面——更糟糕... - Marjan Venema
谢谢你的想法。我尝试了一下,不幸的是,它没有起作用 :( - Stephane
@Stephane,很抱歉。不过我不确定我能提供更多帮助。恐怕这对我来说也是一段“古老的历史”。 - Marjan Venema
抱歉,当我说“它不起作用”时,我的意思是它不能解决模态窗体在从最小化状态恢复应用程序时位于最大化窗体下面的问题。代码执行正确且完整。 - Stephane
@Marjan:没问题,谢谢你的尝试,我很感激 :) - Stephane

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接