2017-07-13 9 views
0

当社のアプリケーションは、測定デバイスから読み込まれなければならず、データベースに格納されている測定データを扱います。PeekMessageはメッセージキューにマウスボタンの入力を照会するのに十分ですか?

一度に多くの測定データセットを一括読み取りするオプションを提供します(&)。これは時間がかかるプロセスなので、モーダルダイアログボックスには進行状況バーと操作をキャンセルするボタンが表示されます。

測定データの完全なセットが読み込まれて保存された後に操作をキャンセルすることができます。アプリケーションのより

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; 

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; 

進捗ダイアログのPositionプロパティのセッターは、現在、以下のようにコーディングされているCheckMouseButtonInputという名前のプロシージャを呼び出します。

ザ・は次のように&ストア・ループがある読んでメッセージループでは、プロパティを介してアクセス可能な変数を設定する次のボタンクリックハンドラが呼び出されます。

procedure TProgressWithAbort.btnCancelClick(Sender: TObject); 
begin 
    FCancel := true; 
end; 

すべて正常です。しかし、上記のCheckMouseButtonInputの実装が多すぎるCPU時間を消費するのではないかと思います。 PeekMessageの前にGetQueueStatusまたはMsgWaitForMultipleObjects(ハンドルなしでタイムアウト0)に電話する方が良いでしょうか?

+0

移動。 – Victoria

+0

'MsgWaitForMultipleObjects'を同期オブジェクトなしで呼び出すと、' WaitMessage'を呼び出すのと同じ効果があります。 'WaitMessage'に続いて' PeekMessage'を呼び出すことは 'GetMessage'を呼び出すのと同じ効果を持ちます。これが解決策であっても、それを実装するのに最も複雑な方法です。 – IInspectable

+0

メインのUIスレッドを読み込みおよび保存中にブロックしたい場合は、キャンセルボタンのみが応答する必要があります。したがって、ワーカースレッドは必要ありません。現在の解決策は、特定のメッセージのメッセージキューをスキャンするポーリング戦略です。私は、GetQueueStatusまたはMsgWaitForMultipleObjectsがPeekMessageと同じ原則に従っているかどうか、あるいは単純にいくつかのフラグや何か(それほどリソースを消費しない)をテストするだけで作業をするかどうかを知りたいと思っています。 – DinkumOil

答えて

1

これは時間がかかるプロセスであるため、モーダルダイアログボックスに進捗バーと操作をキャンセルするボタンが表示されます。

次に、プロセスを別のワーカースレッドに移動する必要があります。メインのUIスレッドで長い操作を実行しないでください。それはUIだけを処理する必要があります。プロセスが終了するまでメインのUIスレッドをブロックしたい場合でも、メインスレッドがメッセージを正常に処理させるようにし、手動では行わないようにしてください。

スレッドを開始し、ダイアログを表示し、キャンセルボタンが押されたら終了するようにスレッドに通知し、スレッドが終了したときにダイアログを閉じます。スレッドは、必要に応じてダイアログにUI更新を送信し、測定の間に終了ステータスを確認できます。いいえCheckMouseButtonInput()ロジックが必要です。例えば

:あるいは

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; 

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; 

:ワーカースレッドに動作

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; 
+0

あなたのソリューションを使用できるとは思えませんが、あなたの努力(あなたの投稿を投票しようとしましたが、評判が足りません。私が上に掲示したリード&ストアループは本当に起こるものの非常に単純化されたバージョンです。また、プロセス中に、ツリーとそのデータモデルが絶えず更新されます。読み書きロジックをワーカースレッドに移すと、より多くのコードを移動する必要があります。私たちはこのアプリケーションを8年近く働いており、およそ200万行のコードのソフトウェアとなっています。大きな変化は実現するのが難しいです。 – DinkumOil

+0

この場合、手動メッセージ処理に頼る必要があります。 'Application.ProcessMessages()'はキュー内のすべてのメッセージを処理するので、 'WM_LBUTTONUP'だけでフィルタリングすることはあまり役に立ちません。フラグを 'QS_ALLINPUT'に設定して' MsgWaitForMultipleObjects() 'または' GetQueueStatus() 'を使用します。あるいは、ユーザーの入力とペイントだけを許可するには、 'QS_INPUT | QS_PAINT'フラグを設定し、 'PM_QS_INPUT |と' PeekMessage(PM_REMOVE) 'ループを使用してください。各メッセージの 'TranslateMessage()'と 'DispatchMessage()'の後に( 'Application.ProcessMessage()'がプライベートである) 'PM_QS_PAINT'フラグがあります。 –

+0

また、フォーカス可能なボタンは、キーボード(例:スペースキー)でも_clickedできることを忘れないでください。 – Victoria

関連する問題