当拖动窗口或单击下拉菜单时,VCL TTimer 停止

发布于 2024-10-19 12:01:20 字数 1983 浏览 6 评论 0原文

我启用了 TTimer 并且应该运行非- 永远停止,直到用户停止为止。然而,事实并非如此。在 OnTimer 事件中,它以毫秒为单位一遍又一遍地处理窗口消息。

例如,这是我的代码片段。

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。如果我错了请纠正我。

I have TTimer enabled and is supposed to run non-stop forever until the user stop it. However, it doesn't work that way. Within OnTimerevent, it processes window messages over and over again in milliseconds.

For instance, here is a snippet of my code.

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;

What happens is this. While the TTimer is enabled and running, you drag any windows of the application or click on pull down menu the TTimer event completely stops working, even though I have taken precautionary steps in other part of the code to prevent this from happening. However, it doesn't seem to be helping.

The only way to restart the OnTimer event is to stop and restart the Timer by the user through TButton event.

The same code or program works fine under Windows XP compiled with Delphi 7. Currently, I am using Windows 7 and Delphi 2010 to rebuild my system.

I will try to give you more information. What I am working on is a copyrighted software.

There is a user defined procedure called HandleMsg. It actually processes the serial port messages. HandleMsg is set to the Application event onMessage;

Application.onMessage:=HandleMsg();

PostMessage is associated with onMessage event of the application.

Whenever PostMessage is called, it fires onMessage event which is set to HandleMsg().

Here more of my code:

 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() responds to PostMessage. Correct me if I am wrong.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

呆橘 2024-10-26 12:01:20

在这两种情况下(开始调整窗口大小/移动窗口或打开菜单),从 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 本身已发布

In both cases (starting to size/move window or opening a menu) the last message dispatched from TApplication.ProcessMessage is WM_NCLBUTTONDOWN (or a WM_NCRBUTTONDOWN if caption and system menu exists and clicked on the caption.. or a WM_RBUTTONUP if opening a context menu, etc..). Common to all is they are starting a modal message loop.

For instance the below is from WM_ENTERSIZEMOVE documentation:

The WM_ENTERSIZEMOVE message is sent
one time to a window after it enters
the moving or sizing modal loop.
[....] The operation is complete when
DefWindowProc returns.

After a modal message loop is started the HandleMessage call in TApplication.Run will not return until DefWindowProc for the relevant window returns (in the WM_NCLBUTTONDOWN case for instance, the dispatched message will cause a WM_SYSCOMMAND to be sent to the window which will start the modal message loop and will not return until moving/sizing is complete). So you won't be able to use an OnMessage handler of the application in this period which is called in TApplication.ProcessMessage.

Your solution is simple. Instead of using the OnMessage handler, handle the message with a message handler of your form:

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;

Or, put your code in the OnTimer event, since WM_TIMER is itself posted.

得不到的就毁灭 2024-10-26 12:01:20

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

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

This is to be expected. The Windows timer is based on a message queue flag, and it is the lowest priority event. When you start resizing a window, the timer flag in the main application event queue stops being processed, and the timer will stop. Nothing you can do about that. To be clear, ordinary messages will still work, but the timer stops. This is very easy to test.

Now, to resolve this, you can either send yourself WM_USER messages, which should get through, or the better solution is to use a thread to do the critical operation and use the timer to update UI from information that the thread updates. Then when the user resizes the window, you get a UI pause, but the operations are continuing.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文