【问题标题】:VCL TTimer stops when a window dragged or pull down menu clicked拖动窗口或单击下拉菜单时,VCL TTimer 停止
【发布时间】:2011-03-01 14:21:00
【问题描述】:

我启用了TTimer,并且应该永远不间断地运行,直到用户停止它。但是,它不是那样工作的。在OnTimerevent 中,它以毫秒为单位一遍又一遍地处理窗口消息。

例如,这是我的代码的 sn-p。

procedure TDXCommDlg.Timer2Timer(Sender: TObject);
begin
  inherited;
  if Scanning then
  begin
    Timer1.Enabled := false;
    Timer2.Enabled := false;
     while not PostMessage(Handle,WM_USER + 10,1234,5678) do;
     Timer1.Enabled := true;
  end;
end;

发生的事情是这样的。当 TTimer 启用并运行时,您拖动应用程序的任何窗口或单击下拉菜单,TTimer 事件完全停止工作,即使我在代码的其他部分采取了预防措施来防止这种情况发生。但是,它似乎没有帮助。

重启OnTimer事件的唯一方法是用户通过TButton事件停止并重启Timer。

相同的代码或程序在使用 Delphi 7 编译的 Windows XP 下运行良好。目前,我正在使用 Windows 7 和 Delphi 2010 来重建我的系统。

我会尽力为您提供更多信息。我正在开发的是一个受版权保护的软件。

有一个名为 HandleMsg 的用户定义过程。它实际上处理串行端口消息。 HandleMsg设置为Application事件onMessage;

Application.onMessage:=HandleMsg();

PostMessage 与应用程序的 onMessage 事件相关联。

每当调用 PostMessage 时,它​​都会触发设置为 HandleMsg() 的 onMessage 事件。

这里有更多我的代码:

 procedure TDXCommDlg.HandleMsg(var
 Msg: TMsg; var Handled: Boolean);
 begin
      Handled := false;
      case Msg.message of
      WM_USER + 10:
              begin
                   if (Msg.wParam = 1111) and (Msg.lParam = 2222) then
                   begin
                     SendLanMessage;
                     Handled := true;
                   end
                   else if (Msg.wParam = 1234) and (Msg.lParam = 5678) then
                   begin
                        SendMessage;
                        Handled := true;
                   end
                   else
                   begin
                     if (Msg.wParam = 4321) then
                     begin
                       MainFrm.CloseWindow(TViewFrm(Msg.lParam).WinCap);
                     end;
                   end;
              end;
      end; { case } end;

HandleMsg() 响应 PostMessage。如果我错了,请纠正我。

【问题讨论】:

  • 这段代码应该做什么? while not PostMessage(Handle,WM_USER + 10,1234,5678) do; 为什么你认为你需要它?
  • 我在表单中添加了计时器、标签和菜单。我实现了一个计时器事件处理程序,它增加了一个计数器并将其显示在标签中。当我的菜单被下拉和表单拖动时,标签不断变化。我不知道你的问题是什么。您需要使其更清晰,但添加足够的代码以便我们能够理解和重现您的问题。
  • 此代码以毫秒为单位反复处理串行通信。它通过 DB9 串口与外部硬件通信。串行消息在作为窗口消息发送出去时被处理。因此,应用程序本身不需要直接处理它们并使应用程序的其他部分超载或更糟的是减慢速度。
  • 那个 PostMessage 循环是一个非常糟糕的主意。如果 PostMessage 失败,你有什么合理的理由期望立即再次尝试它会成功?如果失败,请按照文档告诉您的操作并调用 GetLastError 以找出问题所在。 (但如果您报告的问题即使在该行不存在的情况下仍然存在,那么请将其从问题中删除,因为它无关紧要。)
  • 也不能复制。但请注意“WM_TIMER 消息是低优先级消息。GetMessage 和 PeekMessage 函数仅在线程的消息队列中没有其他高优先级消息时才发布此消息。”来自msdn跨度>

标签: delphi events delphi-2010


【解决方案1】:

在这两种情况下(开始调整窗口大小/移动窗口或打开菜单)从TApplication.ProcessMessage 发送的最后一条消息是WM_NCLBUTTONDOWN(或者如果标题和系统菜单存在并单击标题,则为WM_NCRBUTTONDOWN.. 或WM_RBUTTONUP 如果打开上下文菜单等..)。所有人的共同点是他们正在启动一个模态消息循环。

例如以下来自WM_ENTERSIZEMOVE 文档:

发送 WM_ENTERSIZEMOVE 消息 进入窗口后一次 移动或调整模态循环。 [....] 操作完成时 DefWindowProc 返回。

在模式消息循环开始后,TApplication.Run 中的 HandleMessage 调用将不会返回,直到相关窗口返回 DefWindowProc(例如,在 WM_NCLBUTTONDOWN 情况下,发送的消息将导致 WM_SYSCOMMAND发送到将启动模态消息循环的窗口,并且在移动/调整大小完成之前不会返回)。因此,您将无法在此期间使用应用程序的 OnMessage 处理程序,该处理程序在 TApplication.ProcessMessage 中调用。

您的解决方案很简单。不要使用OnMessage 处理程序,而是使用表单的消息处理程序处理消息:

const
  WMUSER_10 = WM_USER + 10;

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    procedure Timer1Timer(Sender: TObject);
  private
    procedure WmUser10(var Msg: TMsg); message WMUSER_10;
  public
  end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  PostMessage(Handle, WMUSER_10, 1234, 5678);
end;

procedure TForm1.WmUser10(var Msg: TMsg);
begin
  //
end;


或者,将您的代码放在OnTimer 事件中,因为WM_TIMER 本身已发布

【讨论】:

  • 效果很好。我确实意识到我可以将代码放在 onTimer 中,但我想我很懒。现在只有我知道它在我的软件崩溃之前运行了多长时间。
  • 很高兴您的问题已得到解决(目前 ;))。并不是说它会影响分辨率,但我编辑了一些答案,试图澄清发生了什么。
【解决方案2】:

这是意料之中的。 Windows 计时器基于消息队列标志,它是最低优先级事件。当您开始调整窗口大小时,主应用程序事件队列中的计时器标志将停止处理,并且计时器将停止。对此你无能为力。需要明确的是,普通消息仍然可以工作,但计时器会停止。这很容易测试。

现在,要解决此问题,您可以向自己发送 WM_USER 消息,该消息应该可以通过,或者更好的解决方案是使用线程执行关键操作并使用计时器根据线程更新的信息更新 UI。然后,当用户调整窗口大小时,您会出现 UI 暂停,但操作仍在继续。

【讨论】:

  • 好的。但对我来说仍然不清楚。我需要一个实际的例子
  • -1 不正确。消息队列在调整大小时被抽出。下拉菜单和移动窗口时,它也会抽水。
  • 这不是真的——消息队列在调整窗口大小时继续被处理——因此所有的 WM_SIZE 消息。
  • 如果调整大小触发了高优先级消息,例如在触发下一个鼠标触发事件之前未完成的复杂自定义绘制事件,这实际上可能发生。然而,问题的根源不是调整大小本身,而是它触发了什么,以及它如何使消息队列饱和。
  • 嗯,我已经澄清了没有被处理的是定时器标志,普通消息仍然是可行的。这在我的第一个解决方案中有所暗示,但不是明确的。
猜你喜欢
  • 1970-01-01
  • 2012-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-16
相关资源
最近更新 更多