众所周知,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(5); // 等消息框建立 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: