You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

模态窗体OnCreate中调用SetWindowPlacement导致冻结的问题求助

模态窗体SetWindowPlacement冻结问题解决方案

问题根源

  1. FormCreate直接调用的问题:在模态窗体的FormCreate事件中调用SetWindowPlacement时,VCL的窗体初始化流程尚未完成,直接调用系统API会打乱内部消息处理逻辑,导致后续窗体消息循环异常,最终引发冻结。
  2. OnShow事件的限制:OnShow事件中VCL禁止修改窗体可见性相关操作,而SetWindowPlacement会间接触发窗体位置/状态变更,因此触发EInvalidOperation异常。

解决方案

方案一:自定义消息延迟执行恢复操作

通过PostMessage发送自定义消息,让位置恢复操作在窗体初始化完成、消息循环正常运转后再执行,避免干扰初始化流程。

代码修改步骤:

  1. 在Form2的interface部分定义自定义消息:
const
  WM_RESTORE_POSITION = WM_USER + 1;
  1. 在Form2的private段声明消息处理函数:
procedure WMRestorePosition(var Msg: TMessage); message WM_RESTORE_POSITION;
  1. 修改FormCreate事件,改为发送延迟消息:
procedure TForm2.FormCreate(Sender: TObject);
begin
  PostMessage(Self.Handle, WM_RESTORE_POSITION, 0, 0);
end;
  1. 实现消息处理函数执行位置恢复:
procedure TForm2.WMRestorePosition(var Msg: TMessage);
begin
  RestoreFormPosition(Self, 100, 200, 301, 463);
end;
  1. 修正RestoreFormPosition的RECT赋值错误(原代码直接给Height/Width赋值是错误的,RECT的Bottom/Right需要是Top+Height、Left+Width):
procedure TForm2.RestoreFormPosition(WhichForm: TForm; const pTop, pLeft, pHeight, pWidth: Integer);
var
  recWndPlmt : TWindowPlacement;
begin
  WhichForm.Position := poDesigned;

  recWndPlmt.Length := SizeOf(TWindowPlacement);
  // 先获取完整的窗口Placement结构,确保所有字段默认值正确
  GetWindowPlacement(WhichForm.Handle, @recWndPlmt);

  recWndPlmt.rcNormalPosition.Top      := pTop;
  recWndPlmt.rcNormalPosition.Left     := pLeft;
  recWndPlmt.rcNormalPosition.Bottom   := pTop + pHeight;
  recWndPlmt.rcNormalPosition.Right    := pLeft + pWidth;
  recWndPlmt.showCMD := SW_SHOWNORMAL;

  SetWindowPlacement(WhichForm.Handle, @recWndPlmt);
end;

方案二:利用Application.Idle事件一次性执行

通过Application.OnIdle事件,在应用程序首次空闲时执行位置恢复操作,完成后立即注销事件避免重复执行。

代码示例:

procedure TForm2.FormCreate(Sender: TObject);
begin
  Application.OnIdle := FormIdle;
end;

procedure TForm2.FormIdle(Sender: TObject; var Done: Boolean);
begin
  // 标记任务完成,避免重复触发
  Done := True;
  // 注销事件
  Application.OnIdle := nil;
  // 执行位置恢复
  RestoreFormPosition(Self, 100, 200, 301, 463);
end;

(同样需要使用修正后的RestoreFormPosition函数)

效果说明

两种方案都能避开模态窗体初始化的敏感阶段,确保SetWindowPlacement在安全时机执行,既解决冻结问题,又符合多显示器场景的适配需求(SetWindowPlacement会自动处理显示器缺失的情况,避免窗体显示在屏幕外)。

内容的提问来源于stack exchange,提问作者Hugo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 02:00:17