22

DoEvents を使用せずに VB6.0 で実行時間の長いプロセスをキャンセルすることは可能ですか?

例えば:

for i = 1 to someVeryHighNumber
    ' Do some work here '
    ...

    if cancel then
        exit for
    end if
next

Sub btnCancel_Click()
    cancel = true
End Sub

「キャンセルした場合...」の前に「DoEvents」が必要だと思いますが、もっと良い方法はありますか?お久しぶりです…

4

8 に答える 8

30

いいえ、あなたはそれを正しく理解しました。間違いなく DoEvents がループに必要です。

をメイン ループに入れDoEventsて処理が非常に遅くなることがわかった場合は、Windows API 関数GetQueueStatus(DoEvents よりもはるかに高速) を呼び出して、DoEvents を呼び出す必要があるかどうかをすばやく判断してみてください。GetQueueStatus処理するイベントがあるかどうかを示します。

' at the top:
Declare Function GetQueueStatus Lib "user32" (ByVal qsFlags As Long) As Long

' then call this instead of DoEvents:
Sub DoEventsIfNecessary()
    If GetQueueStatus(255) <> 0 Then DoEvents
End Sub
于 2008-10-02T04:35:45.663 に答える
8

いいえ、DoEvents を使用する必要があります。そうしないと、すべての UI、キーボード、およびタイマー イベントがキューで待機したままになります。

あなたができる唯一のことは、1000回の反復ごとに1回DoEventsを呼び出すことです。

于 2008-09-30T23:13:19.223 に答える
7

GUI スレッドで「for」ループが実行されていますか? そうであれば、DoEvents が必要です。別のスレッドを使用することもできます。その場合、DoEvents は必要ありません。これはVB6で実行できます(単純ではありません)。

于 2008-09-30T23:09:51.463 に答える
4

長時間実行されるタスクを量子に分割します。このようなタスクは多くの場合、単純なループによって駆動されるため、10、100、1000 などの繰り返しにスライスします。Timer コントロールを使用して、起動するたびにタスクの一部を実行し、その状態を保存します。開始するには、初期状態を設定し、タイマーを有効にします。完了したら、タイマーを無効にして結果を処理します。

量子ごとに実行される作業量を変更することで、これを「調整」できます。タイマー イベント ハンドラーでは、「キャンセル」を確認し、必要に応じて早期に停止できます。ワークロードとタイマーを、Completed イベントを使用して UserControl にバンドルすることで、すべてをすっきりさせることができます。

于 2008-10-01T03:39:14.233 に答える
4

これは、必要なときにうまく機能します。ユーザーがエスケープ キーを押してループを終了したかどうかを確認します。

非常に大きな欠点があることに注意してください。ユーザーが任意のアプリケーションでエスケープ キーを押したかどうかを検出します。しかし、長時間実行されるループを中断する方法や、Shift キーを押したままにしてコードを少しバイパスする方法を自分自身に与えたい場合、これは開発における素晴らしいトリックです。

Option Explicit

Private Declare Function GetAsyncKeyState Lib "user32" (ByVal nVirtKey As Long) As Integer

Private Sub Command1_Click()
    Do
        Label1.Caption = Now()
        Label1.Refresh
        If WasKeyPressed(vbKeyEscape) Then Exit Do
    Loop

    Label1.Caption = "Exited loop successfully"

End Sub

Function WasKeyPressed(ByVal plVirtualKey As Long) As Boolean
    If (GetAsyncKeyState(plVirtualKey) And &H8000) Then WasKeyPressed = True
End Function

GetAsyncKeyState のドキュメントは次のとおりです。

http://msdn.microsoft.com/en-us/library/ms646301(VS.85).aspx

于 2008-10-15T22:36:53.127 に答える
4

これは、VB6 での非同期バックグラウンド処理のかなり標準的なスキームです。(たとえば、Dan Appleman のと Microsoft の VB6サンプルにあります。)作業を行うために別の ActiveX EXE を作成します。そうすれば、作業は別のスレッドの別のプロセスで自動的に行われます (つまり、別のプロセスについて心配する必要はありません。踏みにじられている変数)。

  • VB6 ActiveX EXE オブジェクトは、イベント CheckQuitDoStuff() を公開する必要があります。これは、Quit と呼ばれる ByRef Boolean を取ります。
  • クライアントは、ActiveX EXE オブジェクトで StartDoStuff を呼び出します。このルーチンは非表示のフォームでタイマーを開始し、すぐに を返します。これにより、呼び出しスレッドのブロックが解除されます。タイマー間隔は非常に短いため、タイマー イベントはすぐに発生します。
  • Timer イベント ハンドラは、Timer を無効にしてから、ActiveX オブジェクトの DoStuff メソッドにコールバックします。これにより、時間のかかる処理が開始されます。
  • DoStuff メソッドは定期的に CheckQuitDoStuff イベントを発生させます。クライアントのイベント ハンドラーは特別なフラグをチェックし、中止する必要がある場合は Quit True を設定します。次に、Quit が True の場合、DoStuff は計算を中止し、早期に戻ります。

このスキームは、「DoStuff」が発生している間は呼び出しスレッドがブロックされないため、クライアントが実際にマルチスレッドである必要がないことを意味します。トリッキーな部分は、DoStuff が適切な間隔でイベントを発生させることを確認することです。長すぎると、終了したいときに終了できません。短すぎると、DoStuff が不必要に遅くなります。また、DoStuff が終了するときに、非表示のフォームをアンロードする必要があります。

DoStuff が実際に中止される前にすべての作業を完了することができた場合は、別のイベントを発生させて、ジョブが終了したことをクライアントに伝えることができます。

于 2009-10-13T17:01:33.243 に答える
4

別のスレッドで開始することもできますが、VB6 では非常に面倒です。DoEvents が機能するはずです。これはハックですが、VB6 も同様です (10 年の VB のベテランがここで話しているので、私をダウンモッドしないでください)。

于 2008-09-30T23:14:32.450 に答える
4

編集すると、MSDNの記事に欠陥があり、テクニックが機能しないことが判明しました:(

.NET BackgroundWorkerコンポーネントを使用して、VB6 内から別のスレッドでタスクを実行する方法に関する記事を次に示します。

于 2009-01-05T17:55:20.223 に答える