众所周知,Delphi 的 MessageBox 消息框是封装的 Win32 函数。其函数原型为:

MessageBox(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UINT uType);

  其中各参数想必大家都很熟悉,在此不再赘述。主要谈谈 HWND。按官方描述:此参数代表消息框拥有的窗口。如果为NULL,则消息框没有拥有窗口。按理说消息框就应该显示在 HWND 窗口中,但直至 Windows 11,无论 HWND 如何,消息框都稳占桌面中央。对于应用程序而言,我们希望这个消息框能显示在应用程序的主窗口正中,甚至派生窗口的正中。也就是说真正显示在 HWND 窗口正中。

  这个问题,网上已有人给出过解决方案。但有的只给出解决思路,有的虽然给出了解决方案,但用了Delphi最新版本才有的功能(如TThread.ForceQueue)。因此,我用最基本的Delphi方法给出一个实用的解决方案。作为实用的方案,考虑到了 MessageBox 不能移出屏幕边界、HNWD 窗口不存在即显示在桌面中央等实际情况。

  解决的原理很简单,就是想法将 MessageBox 移动到指定的位置。但 Windows 系统的 MessageBox 是一个模态对话框,也就是说直至用户响应才执行下一条程序指令,移动窗口的指令不可能在同一线程,必须开启另一个线程。这个线程在 MessageBox 弹出前建立且运行,等待 MessageBox 弹出后通过查找其窗口标题对应的窗口句柄(我的实验是不能通过顶层窗口或活动窗口查找 MessageBox),将其移动到指定位置后结束线程。这样就实现了将 MessageBox 显示在 HWND 窗口中央。具体程序如下,这段程序可放在 implementation 后的任何地方,只要在调用它的程序前就行:

  • implementation

  • {$R *.dfm}

  • type
  •  TMyThread = class(TThread)
  •  protected
  •    procedure Execute; override;
  •  end;

  • var
  •  hMain: HWND;
  •  MsgTitle: string;

  • procedure TMyThread.Execute;
  • var
  •  mR, pR, sR: TRect;
  •  X, Y: Integer;
  •  hMsg: HWND;
  • begin
  •  FreeOnTerminate := True; { 这可以让线程执行完毕后随即释放 }
  •  sleep(10); // 等消息框建立
  •  hMsg := FindWindow(nil, PChar(MsgTitle));

  •  GetWindowRect(hMsg, mR); // 取消息框窗口矩形位置大小
  •  GetWindowRect(hMain, pR); // 取 HWND 窗口矩形位置大小
  •  GetWindowRect(GetDesktopWindow, sR); // 取屏幕桌面矩形位置大小

  •  X := pR.Left + (pR.Width - mR.Width) div 2;
  •  Y := pR.Top + (pR.Height - mR.Height) div 2;
  •  if X < 0 then
  •    X := 0;
  •  if X > sR.Width - mR.Width then
  •    X := sR.Width - mR.Width;
  •  if Y < 0 then
  •    Y := 0;
  •  if Y > sR.Height - mR.Height then
  •    Y := sR.Height - mR.Height;

  •  SetWindowPos(hMsg, HWND_TOP, X, Y, 0, 0, SWP_NOSIZE or SWP_SHOWWINDOW or
  •    SWP_NOOWNERZORDER);
  • end;

  • // 主函数。在HWND中央显示消息框
  • function MsgBox(const HWND: HWND; const msg, title: string;
  •  const mbType: WORD): WORD;
  • begin
  •  hMain := HWND;
  •  if not IsWindow(hMain) then
  •    hMain := GetDesktopWindow;
  •  if title <> '' then
  •    MsgTitle := title
  •  else
  •    MsgTitle := #32;
  •  TMyThread.Create(False);
  •  result := MessageBox(HWND, PChar(msg), PChar(MsgTitle), mbType + MB_TOPMOST);
  • end;

   新的消息函数名为 MsgBox ,调用它与 MessageBox 完全相同。例如:

  • procedure TForm3.Button1Click(Sender: TObject);
  • begin
  • if  MsgBox(handle, '消息内容', '消息标题', MB_YESNO) = mrYes then
  • close;
  • end;

   运行后,MessageBox 显示在 Form3 窗口的中央而不是桌面中央(要显示桌面在中央,HWND指定为0即可)。按“是(Y)”将关闭 Form3:

Delphi MessageBox消息框应用窗口居中实用解决方案_Delphi