マウスボタンを離すとスクロールバーがスクロールし続ける

現象

スクロールバーは、マウスの左ボタンを離した後でも連続的にスクロールします。 スクロールバーの種類はこの問題には無関係であり、スクロールバーがウィンドウの一部であるか、スクロールバーコントロールであるかに関係なく、同じ問題が発生します。

原因

通常、この問題は、スクロールバーの通知メッセージの1つを受け取るときにスクロールのために実行されたアクションの結果として、メッセージ取得ループが実行されるときに発生します。スクロールするときに、Windows で内部メッセージ取得ループが開始されます。 このメッセージループのタスクでは、スクロールを追跡し、適切なスクロールバーの通知メッセージ、WM_HSCROLL、WM_VSCROLL を送信します。 WM_LBUTTONUP の受信後、スクロールは終了します。 スクロール中に別のメッセージループが開始された場合は、そのメッセージループによって WM_LBUTTONUP が取得されます。また、アプリケーションにはスクロールバーの内部メッセージ取得ループへのアクセス権がないため、WM_LBUTTONUP を正しくディスパッチすることはできません。 そのため、WM_LBUTTONUP は内部メッセージ取得者によって受信されることはありません。スクロールは終了しません。スクロールしているアプリケーションは、この問題を引き起こすために、明示的にメッセージを取得する必要はありません。 次の関数のいずれかを呼び出すか、スクロール中にメッセージ取得ループを含むメッセージを処理すると、WM_LBUTTONUP が失われる可能性があります。 以下の関数は、このカテゴリに分類されます。

DialogBox() DialogBoxIndirect() DialogBoxIndirectParam() DialogBoxParam() GetMessage() MessageBox() PeekMessage()

解決方法

スクロール中は、スクロールバーの内部のメッセージ以外のメッセージ取得ループによって、WM_LBUTTONUP メッセージをキューから取得することはできません。この問題を解決するには、次のようなアプリケーションが考えられます。

  • アプリケーションは、バックグラウンド処理を実装するためのメッセージ取得ループを実装します。たとえば、時間のかかる塗料を実行している間、バックグラウンド処理を実装します。

  • アプリケーションは、別のアプリケーションや DLL との通信を実装するためのメッセージ取得ループを実装します。 たとえば、スクロールするために、アプリケーションは別の場所からデータを受け取る必要があります。

可能な回避策

次の2つの回避策が考えられます。 最初の回避策は、多くのアプリケーションや Windows によって使用されます。ただし、まれな状況では、1つ目の回避策は実現できない場合があります。 この場合は、2つ目の回避策を使用することができます。 ただし、可能であれば、スクロール時にメッセージの取得を完全に実装しないようにしてください。

  • タイマーメッセージベースの処理を使います。 複雑な処理を小さいタスクに分割し、各タスクの開始と終了を把握して、タイマーメッセージに基づいて各タスクを実行します。 処理のすべてのコンポーネントが完了したら、タイマーを強制終了します。 この回避策の例については、以下を参照してください。

  • メッセージ取得ループを実装しますが、WM_LBUTTONUP が取得されていないことを確認します。 これは、フィルターを使用することで実現できます。 この回避策のいくつかの例については、以下を参照してください。

回避策1の例

アプリケーションには複雑な描画手順があります。 ScrollWindow () をスクロールすると、描画メッセージが生成されます。 描画中にバックグラウンド処理が行われます。

  1. WM_PAINT メッセージが表示されたら、次の操作を行います。

    1. BeginPaint () を呼び出します。

    2. 無効化された rect を、手順2で使用するグローバル rect 変数 (grcPaint など) にコピーします。 グローバルな rect は、前に取得した rect (grcPaint) と新しい無効化された rect (.ps Paint) の和集合となります。 このコードは、次のようになります。

               RECT grcPaint;    // Should be initialized before getting the                           // first paint message.            :            :         UnionRect(&grcPaint, &ps.rcPaint,&grcPaint);
    3. ValidateRect () を rcPaint とともに呼び出します。

    4. EndPaint () に通話を発信します。

    5. タイマーを設定します。

    この方法では、無効な領域がなく、タイマーが設定されて WM_TIMER メッセージを生成する WM_PAINT メッセージは生成されません。

  2. WM_TIMER メッセージを受信したら、大域 rect 変数を確認します。空でない場合は、セクションを撮り、ペイントします。 次に、塗装された領域が含まれなくなるようにグローバル rect 変数を調整します。

  3. グローバル rect 変数が空になったら、タイマーを強制終了します。

回避策2の例

アプリケーションは、DDE または他のアプリケーションからの他のメカニズムによってデータを取得する必要があります。これは、ウィンドウに表示されます。 スクロールするために、アプリケーションは、サーバーアプリケーションからデータを要求してから取得する必要があります。Peekmessage 呼び出す () を設定して情報を取得するには、3種類のフィルターを使用できます。 フィルターを設定するには、Peekmessage 呼び出す () の uFilterFirst パラメーターと Ufilterfirst パラメーターを使用します。 uFilterFirst は、チェックする範囲の最初のメッセージを指定し、Ufilterfirst はチェックする範囲内の最後のメッセージを指定します。

  1. 必要なデータを取得するために、関連メッセージのみを確認して取得します。

  2. キューのフォームを削除することなく WM_LBUTTONUP を確認します。キュー内にある場合は、中断します。 それ以外の場合は、すべてのメッセージを取得してディスパッチします。

  3. WM_LBUTTONUP 未満で WM_LBUTTONUP よりも大きいメッセージをすべて取得しますが、WM_LBUTTONUP は取得しません。

詳細情報

問題の再現手順

WM_LBUTTONUP メッセージが失われる前のイベントのシーケンスを次に示します。

  1. マウスを使用してスクロールバーをクリックします。

  2. 手順1で WM_NCLBUTTONDOWN メッセージが生成されます。

  3. 手順2では、Windows の内部メッセージループが開始されます。 このメッセージループは、スクロールバーに関連するメッセージを探します。 このメッセージループの目的は、適切な WM_HSCROLL または WM_VSCROLL メッセージを生成することです。 メッセージループとスクロールは WM_LBUTTONUP が受信されると終了します。

  4. WM_HSCROLL または WM_VSCROLL メッセージを受信するときに、アプリケーションは、メッセージ取得ループに直接、またはメッセージを取得するための関数を呼び出します。

  5. WM_LBUTTONUP は、手順4で説明したメッセージループによってキューから削除されます。 WM_LBUTTONUP はディスパッチされます。

  6. 手順5の WM_LBUTTONUP メッセージは、他の場所で送信されます。また、手順3で説明した内部メッセージ取得ループでは、受信しません。 手順3のメッセージループは、スクロールを停止する WM_LBUTTONUP を探しています。 受信されないため、スクロールバーはスクロールを続けます。

ヘルプを表示

スキルを磨く
トレーニングの探索
新機能を最初に入手
Microsoft Insider に参加する

この情報は役に立ちましたか?

フィードバックをお送りいただきありがとうございます!

フィードバックをお寄せいただき、ありがとうございます。Office サポートの担当者におつなぎいたします。

×