Перейти к содержимому

Doevents vba excel как использовать

  • автор:

Функция DoEvents

Уступает выполнение, чтобы операционная система могла обработать другие события.

Синтаксис

DoEvents( )

Замечания

Функция выполнения событий DoEvents возвращает целое число, представляющее количество открытых форм в автономных версиях Visual Basic, таких как профессиональный выпуск Visual Basic. DoEvents возвращает нуль во всех других приложениях.

DoEvents передает управление операционной системе. Управление возвращается, когда операционная система завершает обработку событий в очереди и все ключи в очереди SendKeys отправлены.

Функция DoEvents наиболее полезна для простых действий, таких как разрешение пользователю отменить процесс (например, поиск файла) после его запуска. Для длительных процессов передачу процессора лучше выполнять с помощью функции Timer или передачи задачи компоненту ActiveX EXE. В последнем случае задача может продолжаться полностью независимо от приложения, и операционная система выполняет многозадачность и нарезку времени.

Временно уступая процессор в процедуре события, всегда проверяйте, чтобы процедура не выполнялась повторно из другой части вашего кода, прежде чем будет возвращен первый вызов; это может привести к непредсказуемым результатам. Кроме того, не используйте DoEvents , если другие приложения могли взаимодействовать с процедурой непредвиденными способами во время предоставления управления.

Пример

В этом примере функция DoEvents используется для вызова передачи выполнения в операционной системе один раз на каждые 1000 итераций цикла. DoEvents возвращает количество открытых форм Visual Basic, но только в том случае, если ведущее приложение — Visual Basic.

' Create a variable to hold number of Visual Basic forms loaded ' and visible. Dim I, OpenForms For I = 1 To 150000 ' Start loop. If I Mod 1000 = 0 Then ' If loop has repeated 1000 times. OpenForms = DoEvents ' Yield to operating system. End If Next I ' Increment loop counter. 

См. также

Поддержка и обратная связь

Есть вопросы или отзывы, касающиеся Office VBA или этой статьи? Руководство по другим способам получения поддержки и отправки отзывов см. в статье Поддержка Office VBA и обратная связь.

Doevents vba excel как использовать

Функция DoEvents. Анализ ее влияния на быстродействие.
Организация прерывания цикла.
Цикл ожидания действий пользователя.

Возможно, в созданных тобой прекрасных проектах есть процедуры, выполняющие длительные по времени циклы: сортировки, считавание или запись данных, поиск файлов и т.п. И если во время их выполнения ты заметил, что не можешь подвинуть свою форму по экрану, а другие, открытые окна оставляют на ней белые области (форма не обновляется), то тебе в самый раз прочитать про функцию DoEvents. Если же ты ничего такого не замечал, то значит еще заметишь. Но использование нижеследующей функции необходимо.

Функция DoEvents.

Для того, чтобы твое приложение, имеющее длинные циклы, не мешало пользователю на время их выполнения заняться другой работой, временно передвинув или даже свернув твою красивую форму, в нашем могучем арсенале имеется невостребованная пока функция DoEvents, синтаксис которой приятно ласкает взгляд:
DoEvents()
и никаких тебе аргументов.

Эта функция, согласно мануалу, возвращает значение типа Integer, представляющее собой количество открытых форм, если фокус находится на форме Visual Basic и ноль, если фокус находится на окнах других приложений. Ну это кому что интересно. Главное это то, что эта функция передает управление операционной системе, то бишь Windows, для обработки других событий. И после обработки Windows’ом всех событий из очереди и передачи всех нажатий клавиш из очереди функции SendKeys возвращает управление обратно в Visual Basic.

Хотя в использовании этой функции нет особенных трудностей, учитывая специфику сайта, лучше один раз пощупать функцию в маленькой процедурке, чем искать ошибку в большом проекте.
Создадим Exe-проектик, положим на форму кнопку Command1 и текстбокс Text1и напишем маленькую процедурку:

Private Sub Command1_Click()
Dim x As Long
For x = 1 To 300000
Text1 = x
Next x
End Sub

Этот цикл по нашему разумению должен выводить в Text1 постоянно меняющееся значение переменной X от 1 до 300000. Запустим проект, нажмем кнопку Command1 и вместо созерцания мигающих циферек в текстбоксе мы видим. ничего мы не видим — форма наша не обновляется до окончания цикла. Да мы еще и не можем корректно приостановит выполнение цикла (я имею в виду , если он откомпилирован). Более того, попробовав передвинуть мышью форму мы убедимся, что это невозможно, а Менеджер задач (Task Manager) может выдать сообщение, что программа зависла и не отвечает (not responding). Вот так, ходим по минам, а подрываемся на говне.
Однако всего этого можно избежать, если ввести в цикл функцию DoEvents. Тогда наша процедурка может выглядеть так:

Private Sub Command1_Click()
Dim x As Long
For x = 1 To 300000
Text1 = x
DoEvents
Next x
End Sub

Конечно мигание цифр не радует глаз. Кроме того, при передаче управления Windows выполнение программы Visual Basic (в нашем случае цикла) приостанавливается, но зато все наши проблемы мгновенно решились: и форму можно двигать и форма обновляется.
В связи с тем, что повторный вызов функции DoEvents в момент, когда функция еще не передала управление из операционной системы назад в Visual Basic может вызвать непредвиденные последствия, нежелательно вызывать процедуру DoEvents из других процедур программы. Видимо эти же соображения имеют место, когда предполагается, что нежелательно использовать функцию DoEvents в процедурах обработки событий Click. Но мне не удалось добиться никаких особенных фатальных результатов, кроме перезапуска цикла, даже беспрестанно кликая по кнопке Command1.

Основным недостатком этой функции является очень значительное уменьшение быстродействия в длинных по времени циклах (в коротких ее вообще нет смысла использовать). Самое простое решение этого вопроса — запуск DoEvents не при каждой итерации, а например, при каждом сотом (или тысячном) проходе цикла, в зависимости от его параметров. Решить это можно, например, вводя дополнительную переменную-счетчик (в нашем примере y). С каждым проходом цикла счетчик увеличивается на единицу, а при достижении им значения, например 1000, вызывает функцию DoEvents и обнуляется, чтобы при дальнейшей работе цикла снова увеличится до 1000. Тогда наша процедура будет выглядеть следующим образом:

Private Sub Command1_Click()
Dim x As Long
Dim y As Long
For x = 1 To 300000
Text1 = x
y = y + 1
If y = 1000 Then ‘проверяем счетчик итераций
‘и если он совпадает с заданым числом, запускаем DoEvents и обнуляем счетчик

DoEvents
y = 0
End If

Next x
End Sub

Если тебя уже раздражает такое «ламерское» (но на мой взгляд удобное и наглядное решение), можно отследить тысячный проход цикла по-другому, например деля значения счетчика цикла x на эту тысячу с остатком (Mod) при каждой итерации, тогда при остатке ноль можно считать, что значение х кратно нашей тысяче. Тогда наш код будет выглядеть так:

Private Sub Command1_Click()
Dim x As Long
Dim y As Long
For x = 1 To 300000
Text1 = x
y = y + 1
If x Mod 1000 = 0 Then DoEvents
‘получаем остаток от деления
‘и если он равен нулю, запускаем DoEvents

Next x
End Sub

А может быть снижение быстродействия не так уж и значительно и им можно пренебречь? Чтобы нам не принимать все на веру, мы сами проведем соответствующее сравнительное исследование зависимости быстродействия от частоты срабатывания функции DoEvents. Для этого нам на форме дополнительно понадобятся Timer1, и еще два текстбокса Text2 и Text3. В уже имеющийся Text1 мы будем вводить интервал цикла (по умолчанию он у нас будет 300000), в Text2 — на какой итерации (1-ой, т.е. каждой, 100-ой, 10000-й и т.д ) будет включаться функция DoEvents. В Text3 выведем результат измерений в миллисекундах.
Объявим три переменных:

Option Explicit
Dim Iter As Long ‘итерация срабатывания DoEvents
Dim RunTime As Integer ‘время работы цикла
Dim RunLong As Long ‘длина цикла

В процедуре Form_Load присвоим начальные значения цикла и итераций:

Private Sub Form_Load()
Text2 = 1
Text1 = 1000000
End Sub

Код процедуры Command1_Click Изменим следующим образом:

Private Sub Command1_Click()
Dim x As Long
Dim y As Long
Iter = Val(Text2) ‘Присваиваем переменным значения для цикла
RunLong = Val(Text1)
RunTime = 0
Text3 = «Цикл в работе»
Timer1.Interval = 1 ‘устанавливаем интервал таймера в 1 мск
Timer1.Enabled = True ‘запускаем таймер

For x = 1 To RunLong
‘обратите внимание на то, что вывод значений переменной цикла X не выводится в Text1,
‘так как это непомерные затраты времени и исказят наши значения.
‘Text1 = x
y = y + 1
If y = Iter Then
DoEvents
y = 0
End If
Next x
Timer1.Enabled = False ‘выключаем таймер
Text3 = RunTime ‘выводим значение счетчика таймера в текстбокс
End Sub

‘В процедуре таймера мы просто имеем счетчик, который увеличивается
‘ на единицу при каждом включении, т. е. каждую миллисекунду.

Private Sub Timer1_Timer()
RunTime = RunTime + 1
End Sub

Скажу сразу, что данный эксперимент не ставит собой целью получить точные данные, т. к. это невозможно хотя бы из-за того, что процессор в Windows’е постоянно выполняет определенную работу в фоновом режиме, и хотя мы этого не видим, но отнимает время у нашей программы. Для получения корректных результатов промежуток времени работы цикла должен быть большой, с тем, чтобы погрешность, скажем 5 мск была бы не критичной.
Попробуй запустить тест. При этом, конечно, не надо двигать форму по экрану, чтобы не занимать дополнительного времени. Мы увидим, что при включении функции DoEvents каждую 10-ю итерацию ускоряет цикл в 7-8 раз, каждую 100-ю — в 18-20 раз. Т.е. функция DoEvents снижает быстродействие просто в разы. Но тем не менее, использовать надо, но очень разумно.

Используя эту функцию мы можем позволить пользователю прервать продолжительный процесс. Для этого достаточно ввести булеву переменную для флага, например Flag , которая и будет сигнализировать о том, что процесс необходимо прервать.
Добавим на форму кнопку Command2, которой и будем прерывать выполнение цикла. В секцию (General) добавим объявление флага

Dim Flag As Boolean

В процедуре Command1_Click, прямо в самом начале установим начальное для цикла значения флага:

Flag = False

а в самом цикле поставим условие на выход

If Flag = True Then Exit Sub

Теперь, для выхода из цикла надо просто изменить значение флага False на True. Это мы и сделаем в процедуре Command2_Click:

Private Sub Command2_Click()
Flag = True
Text3 = «Цикл прерван»
End Sub

Вот и готово. Теперь наша кнопка Command2 прерывает цикл. Скачать исходник примера можно вверху страницы.

С помощью функции DoEvents можно также организовать цикл ожидания, прерываемый пользователем. В качестве примера сделаем программку-шутку, которая иммитирует предупреждение пользователю о начале форматирования жесткого диска. Для этого в новом Exe-проекте на форме нам понадобится Label1, Command1 и Timer1.
Объявим пару переменных:

Option Explicit
Dim RunTime As Integer ‘время в секундах
Dim Flag As Boolean ‘флаг, сигнализирующей о прерывании цикла

Для процедуры, собственно, цикла ожидания я выбрал событие формы _Activate, так как оно возникает, когда форма уже загружена и инициализирована и элементы на ней видимы.

Private Sub Form_Activate()
RunTime = 10 ‘устанавливаем начальные значения секунд
Timer1.Interval = 1000 ‘интервал срабатывания таймера 1 сек
Timer1.Enabled = True ‘ включаем таймер
‘организуем цикл, работающий до тех пор, пока переменная RunTime
‘не стане равной нулю (изменяется таймером)
Do Until RunTime = 0
DoEvents ‘постоянно передаем управление Windows
If Flag Then ‘проверяется, нажата ли кнопка Command1 для
‘прерывания цикла (Flag=True) или нет (Flag=False)
Label1 = «Молодец, успел, но все равно форматирование начинается!»
Exit Sub ‘выход из процедуры, если кнопка Command1 нажималась
End If ‘конец условия
Loop ‘конец цикла
Timer1.Enabled = False ‘выключение таймера
Label1 = «Не успел, не успел, надеюсь на винчестере ничего ценного нет?»
End Sub

В процедуре таймера при каждом срабатывании (ежесекундно) уменьшаем значение RunTime на единицу

Private Sub Timer1_Timer()
RunTime = RunTime — 1
‘и выводим соответствующее сообщение в лейбл
Label1 = «Форматирование диска начнется через » & RunTime & » сек. » _
& » Успеешь нажать кнопку?»
End Sub

Процедура Command1_Click() управляет флагом и выключает таймер

Private Sub Command1_Click()
Timer1.Enabled = False
Flag = True
End Sub

Скачать исходник этого примера можно отсюда.

Функция DoEvents

Примечание: Функция, метод, объект или свойство, описанные в данном разделе, отключаются, если служба обработки выражений Microsoft Jet выполняется в режиме песочницы, который не позволяет рассчитывать потенциально небезопасные выражения. Для получения дополнительных сведений выполните в справке поиск по словам «режим песочницы».

Функция передает управление операционной системе для обработки других событий.

DoEvents ( )

Функция DoEvents возвращает целое число, представляющее количество открытых форм в автономных версиях Microsoft Visual Basic, таких как Visual Basic Professional Edition. Функция DoEvents возвращает нуль для всех других приложений.

DoEvents передает управление операционной системе. Управление возвращается по завершении обработки очереди событий операционной системой и отправки всех значений из очереди SendKeys.

Функция DoEvents наиболее удобна при выполнении простых задач, таких как предоставление пользователю возможности прервать запущенный процесс (например, поиск файла). При обработке продолжительных процессов рекомендуется для передачи управления использовать событие «Таймер» или делегирование задачи компоненту ActiveX EXE. В последнем случае выполнение задачи может продолжаться совершенно независимо от приложения, а многозадачность и управление временем процессора осуществляется операционной системой.

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

Пример

Примечание: В примерах ниже показано, как использовать эту функцию в модуле Visual Basic для приложений (VBA). Чтобы получить дополнительные сведения о работе с VBA, выберите Справочник разработчика в раскрывающемся списке рядом с полем Поиск и введите одно или несколько слов в поле поиска.

В этом примере функция DoEvents используется для передачи управления операционной системе через каждую тысячу итераций цикла. Функция DoEvents возвращает количество открытых форм Visual Basic при условии, что ведущим приложением является Visual Basic.

‘ Create a variable to hold number of
‘ Visual Basic forms loaded and visible.
Dim I, OpenForms
For I = 1 To 150000 ‘ Start loop.
If I Mod 1000 = 0 Then ‘ If loop has repeated
‘ 1000 times.
OpenForms = DoEvents ‘ Yield to operating system.
End If
Next I ‘ Increment loop counter.

Функция DoEvents.

Обычно программист ожидает, что Windows (и Visual Basic) постоянно отслеживает все события и реагирует на них. С другой стороны, много времени уходит на различного рода вычисления или сортировку данных. Однако при этом не всегда желательно запрещать приложению Visual Basic реагировать на события. Если создается процедура, в которой много времени будет занято различного рода вычислениями, необходим механизм отслеживания состояния операционной системы и реагирования на различного рода события.

Функция, выполняющая такого рода задачи, носит название DoEvents. В каком бы месте программы ни стоял данный оператор, он сигнализирует Visual Basic о том, что управление передано операционной системе для обработки всех событий. (Windows сохраняет последовательность наступления событий в очереди, а также нажатий клавиш в очереди SendKeys.) DoEvents нельзя использовать в процедуре обработки событий, которая вызывается несколько раз. Например, процедура обработки событий Click может быть вызвана еще раз щелчком мыши. Если забыть про это, можно легко организовать в программе бесконечный цикл.

Цикл, работающий только в случае отсутствия каких-либо событий, носит наименование цикла ожидания. Такие циклы создаются внутри специализированной процедуры Sub, носящей имя Main, и подключающейся к любому программному модулю. В каждом проекте существует только одна процедура Main. Ее формат следующий:

Sub Main() Do While DoEvents() 'Код, работающий в процессе цикла ожидания Loop End Sub

Далее необходимо сделать так, чтобы модуль с Main загружался первым. Для этого нужно выбрать в меню пункт Project|Project Properties и затем страницу General. На этой странице выбрать и открыть список StartUp Object и выбрать Sub Main вместо формы. После того как процедура Main указана загрузочной. Visual Basic не будет автоматически загружать какую-нибудь форму. Для этого теперь необходимо писать программный код, используя, сервированные слова Load и Show.

Ниже приведен простой пример цикла ожидания. Необходимо создать новый проект и добавить к нему программный модуль. Далее в нем объявляется глобальная переменная Counter типа long integer:

Public Counter As Long

Теперь добавим несколько строк кода к процедуре Main:

Sub Main() Fonnl. Show Do While DoEventsf) Counter = Counter + 1 loop End Sub

В завершение установим Sub Main, как загрузочный модуль, и добавим процедуру Form_Click:

Sub Form_Click() Print Counter End Sub

После запуска такой программы счетчик будет увеличиваться каждый раз после щелчка внутри формы. Причина состоит в том, что в течение периода ожидания (когда пользователь не щелкает мышью) Visual Basic переходит к процедуре Main и добавляет 1 к счетчику. Поскольку Counter является глобальной переменной, ее значение сохраняется между вызовами процедуры.

Функция DoEvents на самом деле возвращает число форм, загруженных приложением в текущий момент. Цикл ожидания останавливается, когда выгружены все формы. (Или Visual Basic встречает оператор End.)

Другое общее использование функции DoEvents внутри функции состоит в запуске больших по времени вычислений. Установка цикла ожидания позволяет Visual Basic реагировать на события в момент вычислений. Небольшое дополнительное время при этом с лихвой окупается.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *