首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >PeekMessage是否足以查询消息队列中的鼠标按钮输入?

PeekMessage是否足以查询消息队列中的鼠标按钮输入?
EN

Stack Overflow用户
提问于 2017-07-13 21:07:13
回答 1查看 1.1K关注 0票数 1

我们的应用程序处理测量数据,这些数据必须从测量设备读取并存储在数据库中。

我们提供了一次批量读取和存储多组测量数据的选项。由于这是一个耗时的过程,我们显示一个带有进度条和按钮的模态对话框来取消操作。

只有在读取和存储了一组完整的测量数据之后,才能取消操作。

read & store循环如下:

代码语言:javascript
复制
ItemsToStore := GetSelectedTreeItems();
DlgProgress  := TProgressWithAbort.Create(Screen.ActiveForm);

try
  for i := 0 to Pred(ItemsToStore.Count) do
  begin
    if DlgProgress.Cancel then exit;

    DlgProgress.Description := ItemsToStore[i].Name;
    ReadAndStoreItem(ItemsToStore[i].Id);

    DlgProgress.Position := Succ(i) * 100 div ItemsToStore.Count;
  end;

finally
  DlgProgress.Free;
end;

进度对话框位置属性的设置程序调用一个名为CheckMouseButtonInput的过程,该过程目前编码如下:

代码语言:javascript
复制
procedure TProgressWithAbort.CheckMouseButtonInput;
var
  Msg: TMsg;

begin
  // if the left mouse button was pressed while the mouse was at the
  // Cancel button call the application's message loop to process the event
  if PeekMessage(Msg, btnCancel.Handle, WM_LBUTTONUP, WM_LBUTTONUP, PM_NOREMOVE) then
    Application.ProcessMessages;
end;

在应用程序的message循环中,调用以下按钮单击处理程序,它设置可通过属性Cancel访问的变量:

代码语言:javascript
复制
procedure TProgressWithAbort.btnCancelClick(Sender: TObject);
begin
  FCancel := true;
end;

一切都很好。但是,我不知道上面的CheckMouseButtonInput实现是否占用了太多的CPU时间。在GetQueueStatusMsgWaitForMultipleObjects之前调用PeekMessage (没有句柄,超时为0)更好吗?

EN

回答 1

Stack Overflow用户

发布于 2017-07-13 21:22:38

由于这是一个耗时的过程,我们显示一个带有进度条和按钮的模态对话框来取消操作。

然后,进程应该移动到一个单独的工作线程。不要在主UI线程中运行冗长的操作。它应该只处理UI,而不是其他任何东西。即使您想阻止主UI线程直到进程完成,您仍然应该让主线程处理消息正常,不要手动执行。

启动线程,显示对话框,如果按下Cancel按钮,则向线程发出终止信号,并在线程退出时关闭该对话框。线程可以在需要时向对话框发送UI更新,并在测量之间检查终止状态。不需要CheckMouseButtonInput()逻辑。

例如:

代码语言:javascript
复制
type
  TCancelEvent = procedure of object;

  TProgressWithAbort = class(TForm)
    btnCancel: TButton;
    procedure btnCancelClick(Sender: TObject);
  private
    FCancel: Boolean;
    FOnCancel: TCancelEvent;
  public
    property Cancel: Boolean read FCancel;
    property OnCancel: TCancelEvent read FOnCancel write FOnCancel;
  end;

procedure TProgressWithAbort.btnCancelClick(Sender: TObject);
begin
  FCancel := true;
  if Assigned(FOnCancel) then
    FOnCancel();
end;

代码语言:javascript
复制
procedure TMyForm.LengthyProcess;
var
  ItemsToStore: TListOfWhatever;
  StoreThread: TThread;
  DlgProgress: TProgressWithAbort;
begin
  ItemsToStore := GetSelectedTreeItems();

  DlgProgress  := TProgressWithAbort.Create(Self);
  try
    StoreThread := TThread.CreateAnonymousThread(
      procedure
      var
        i: Integer;
      begin
        try
          for i := 0 to Pred(ItemsToStore.Count) do
          begin
            if TThread.CheckTerminated then Exit;

            TThread.Queue(TThread.CurrentThread,
              procedure
              begin
                DlgProgress.Description := ItemsToStore[i].Name;
              end;
            );

            // make sure this function is thread-safe!
            ReadAndStoreItem(ItemsToStore[i].Id);

            TThread.Queue(TThread.CurrentThread,
              procedure
              begin
                DlgProgress.Position := Succ(i) * 100 div ItemsToStore.Count;
              end
            );
          end;
        finally
          DlgProgress.ModalResult := mrClose;
        end;
      end
    );
    try
      StoreThread.FreeOnTerminate := False;
      StoreThread.Start;    
      try
        DlgProgress.OnCancel := StoreThread.Terminate;
        DlgProgress.ShowModal;
      finally
        StoreThread.Terminate;
        StoreThread.WaitFor;
      end;
    finally
      StoreThread.Free;
    end;
  finally
    DlgProgress.Free;
  end;
end;

另一种选择是:

代码语言:javascript
复制
var
  ItemsToStore: TListOfWhatever;
  StoreThread: TThread;
  DlgProgress: TProgressWithAbort;
  ...

procedure TMyForm.StartLengthyProcess;
begin
  ItemsToStore := GetSelectedTreeItems();

  StoreThread := TThread.CreateAnonymousThread(
    procedure
    var
      i: Integer;
    begin
      for i := 0 to Pred(ItemsToStore.Count) do
      begin
        if TThread.CheckTerminated then Exit;

        TThread.Queue(TThread.CurrentThread,
          procedure
          begin
            DlgProgress.Description := ItemsToStore[i].Name;
          end;
        );

        // make sure this function is thread-safe!
        ReadAndStoreItem(ItemsToStore[i].Id);

        TThread.Queue(TThread.CurrentThread,
          procedure
          begin
            DlgProgress.Position := Succ(i) * 100 div ItemsToStore.Count;
          end
        );
      end;
    end
  );

  StoreThread.OnTerminate := LengthyProcessFinished;

  DlgProgress := TProgressWithAbort.Create(Self);
  DlgProgress.OnCancel := StoreThread.Terminate;
  DlgProgress.Show;
  // disable the rest of the UI as needed..

  StoreThread.Start;
end;

procedure TMyForm.LengthyProcessFinished(Sender: TObject);
begin
  StoreThread := nil;
  FreeAndNil(DlgProgress);
  // enable the rest of the UI as needed..
end;
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45090903

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档