Отменить длительный процесс в VB6.0 без DoEvents? - PullRequest
21 голосов
/ 01 октября 2008

Можно ли отменить длительный процесс в VB6.0 без использования DoEvents?

Например:

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" перед "если отменить, то ...", есть ли лучший способ? Это было некоторое время ...

Ответы [ 8 ]

29 голосов
/ 02 октября 2008

Нет, вы правильно поняли, вы определенно хотите, чтобы 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
8 голосов
/ 01 октября 2008

Нет, вы должны использовать DoEvents, иначе все события пользовательского интерфейса, клавиатуры и таймера будут оставаться в очереди в очереди.

Единственное, что вы можете сделать, это вызывать DoEvents один раз для каждых 1000 итераций или около того.

7 голосов
/ 01 октября 2008

Работает ли цикл for в потоке графического интерфейса? Если да, то вам понадобятся DoEvents. Возможно, вы захотите использовать отдельный поток, в этом случае DoEvents не потребуется. Вы можете сделать это в VB6 (не просто).

4 голосов
/ 13 октября 2009

Вот довольно стандартная схема для асинхронной фоновой обработки в VB6. (Например, он находится в книге Дэна Эпплмана и VB6 семплов Microsoft .) Вы создаете отдельный EXE-файл ActiveX для выполнения работы: таким образом, работа автоматически выполняется в другом потоке, в отдельном процесс (что означает, что вам не нужно беспокоиться о растоптанных переменных).

  • Объект VB6 ActiveX EXE должен предоставлять событие CheckQuitDoStuff (). Для этого используется логическое значение ByRef Quit.
  • Клиент вызывает StartDoStuff в объекте ActiveX EXE. Эта процедура запускает таймер в скрытой форме и немедленно возвращает . Это разблокирует вызывающий поток. Интервал таймера очень короткий, поэтому событие таймера срабатывает быстро.
  • Обработчик событий Timer отключает Timer, а затем вызывает метод DoStuff объекта ActiveX. Это начинает длительную обработку.
  • Периодически метод DoStuff вызывает событие CheckQuitDoStuff. Обработчик событий клиента проверяет специальный флаг и устанавливает Quit True, если необходимо прервать операцию. Затем DoStuff прерывает вычисление и возвращает рано, если Quit имеет значение True.

Эта схема означает, что клиент на самом деле не должен быть многопоточным, поскольку вызывающий поток не блокируется, пока происходит DoStuff. Сложная задача - убедиться, что DoStuff вызывает события через соответствующие промежутки времени - слишком долго, и вы не можете выйти, когда хотите: слишком коротко, и вы без необходимости замедляете DoStuff. Кроме того, когда DoStuff завершает работу, он должен выгружать скрытую форму.

Если DoStuff действительно удается выполнить все, что было сделано до того, как его прервать, вы можете вызвать другое событие, чтобы сообщить клиенту, что задание завершено.

4 голосов
/ 05 января 2009

РЕДАКТИРОВАТЬ это получается статья MSDN имеет недостатки и метод НЕ РАБОТАЕТ : (

Вот статья об использовании компонента .NET BackgroundWorker для запуска задачи в другом потоке из VB6.

4 голосов
/ 16 октября 2008

Это хорошо работает для меня, когда мне это нужно. Он проверяет, нажал ли пользователь клавишу выхода, чтобы выйти из цикла.

Обратите внимание, что у него действительно большой недостаток: он обнаружит, нажал ли пользователь клавишу escape на ЛЮБОМ приложении, а не только на вашем. Но это отличный трюк в разработке, когда вы хотите дать себе способ прервать длительный цикл или способ удерживать клавишу 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

4 голосов
/ 01 октября 2008

Разделите длительное задание на кванты. Такие задачи часто выполняются простым циклом, поэтому разбейте его на 10, 100, 1000 и т. Д. Используйте элемент управления Timer, и каждый раз, когда он срабатывает, выполняйте часть задания и сохраняйте его состояние по мере продвижения. Для начала настройте исходное состояние и включите таймер. По завершении отключите таймер и обработайте результаты.

Вы можете «настроить» это, изменив объем работы, выполняемой на квант. В обработчике событий Timer вы можете проверить «отмена» и остановить по мере необходимости. Вы можете сделать все аккуратно, связав рабочую нагрузку и таймер в UserControl с событием Completed.

4 голосов
/ 01 октября 2008

Вы можете запустить его в отдельном потоке, но в VB6 это королевская боль. DoEvents должен работать. Это хак, но то же самое относится и к VB6 (10-летний ветеран VB говорит здесь, так что не меняйте его).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...