Remkomplekty.ru

IT Новости из мира ПК
0 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Doevents vba excel

Функция DoEvents Visual Basic

Обычно программист ожидает, что 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 реагировать на события в момент вычислений. Небольшое дополнительное время при этом с лихвой окупается.

VBA DoEvents and when to use it

The VBA Tutorials Blog

Introduction to VBA DoEvents

VBA DoEvents yields execution of your macro, so your computer processor will be able to simultaneously run other tasks and recognize other events. The VBA DoEvents function also enables interruption of code execution so it’s easier to stop a running macro.

Only few things in life are more frustrating than trying the halt the execution of a program and not being able to because Excel has become unresponsive. I’m sure you’ve been there. We’ve all made a mistake in a code which causes an infinite loop or something Your program will, in theory, never terminate and Excel will remain unresponsive until you shut down your Excel application! So frustrating.

In the first part of this tutorial, we’ll show you how to circumvent this and other similar problems by using the VBA DoEvents function. However, as the saying goes, all good things come with a price. The DoEvents function in VBA is no exception since it will cause the execution time of your program to increase.

This is logical, since, by yielding execution to other events and processes, Excel temporarily halts the code execution while the operating system handles these other events or processes.

The second part of this introduction is dedicated to exploring how the DoEvents function affects performance. We’ll present some performance test results with important lessons attached to them and then we’ll show you how to balance the yielding of code execution with performance considerations.

Finally, we’ll discuss the generic applications of the DoEvents function in Excel and when not to use it in your VBA macros.

With this in mind, let’s present a very basic and nifty example of VBA DoEvents usage!

VBA DoEvents Example

Insert the code below into a standard code module:

Make powerful macros with our free VBA Developer Kit

There’s a lot to unpack here. To save time and become really good at VBA, make sure you get our free VBA Developer Kit below. It’s full of tips and pre-built macros to make writing VBA easier.

In this simple procedure we use VBA to repeatedly update the value of the Application.Statusbar in the lower left corner of the Excel window inside a loop. In practical situations, if a program containing a loop takes a long time to complete it’s really helpful to track its progress that way.


VBA Excel Application.StatusBar

The procedure starts by storing the current value of the .StatusBar in a Variant type variable. This is because you update the .StatusBar with a string, but its default value is the boolean value False . This may sound strange, but otherwise it would be impossible update the the .StatusBar with the string “False” without setting it to its default value, “Ready”!

We also turn the screen updating off by setting .ScreenUpdating to False , just like we did in our Application.ScreenUpdating VBA tutorial. As will become evident in the next section, this is crucial in terms of performance!

Next, the procedure loops from 1 to 5000 and updates the .StatusBar at every iteration. The DoEvents function in the loop enables you to interrupt the code execution. To do this, simply press Esc or Ctrl + Break. Note, however, that not all computer keyboards come with a Break button!

We’ve actually written a good tutorial explaining how to stop an Excel macro. By including VBA DoEvents, it’s easier for the system to not got bogged down so it should be better able to recognize when you’re trying to press the Esc or Ctrl + Break to end your running macro.


Interrupting code execution with VBA DoEvents

When the loop completes, the .StatusBar is set to its original value stored in the AppStatus variable and we also set the .ScreenUpdating property back to True .

Okay, we’ve shown you how to include the DoEvents VBA function in your macro and how it can make it easier for you to cancel your macro. Next, let’s examine the price you pay for this increased user friendliness and control stemming from the use of the DoEvents function.

VBA DoEvents Performance Impact

To test the influence of the DoEvents function on macro performance, we modified the ShowStatusWithDoEvents procedure to loop from 1 to 10 and we wrote a small timing procedure which calls it 10,000 times. Next, we timed the execution of the four different variants of ShowStatusWithDoEvents procedure shown in table 1 below.

Table 1. DoEvents test results ranked by average execution time.

It should come as no surprise that the fastest execution time (25.34s) is obtained by removing DoEvents and setting .ScreenUpdating to False in the ShowStatusWithDoEvents procedure.

The second row in the table shows that calling the DoEvents function at every iteration increases the execution time by a factor of 3.6 (91.16s / 25.34s). In other words, the performance overhead incurred by using DoEvents that way is quite substantial!

Even more conspicuous is the steep increase in execution time we observe from row 2 to row 3 (1,663.66s). This is caused by not setting the .ScreenUpdating property to False in the procedure. This result suggests that forgetting to do so may increase the execution time of your program by a factor of more than 18! The main takeaway from this is that, unless you have specific reasons not to do so, you should always set the .ScreenUpdating to False while executing your programs.

The bottom feeder in our tests is row 4 (2,658.62s), where the .ScreenUpdating property was set to True and DoEvents was removed. It is quite surprising that including DoEvents at every iteration in the loop, cf. row 3, decreases the execution time of the procedure compared to when DoEvents is removed from the procedure!

The Microsoft Developer Network documentation on the Application.ScreenUpdating property states: “Turn screen updating off to speed up your macro code. You won’t be able to see what the macro is doing, but it will run faster. “ However, it’s not entirely accurate to say that you can’t always see what the macro is doing while it’s being executed when the screen updating is turned off. When you update the status bar, you can in fact see what your macro is doing, even if you haven’t included the DoEvents function in your code. That is, unless your screen freezes up. This suggests that .ScreenUpdating only updates the worksheet area (i.e. the cells), rather than the entire Excel window, including the status bar. This may in turn explain the surprising result in row 4 of the table. If the repeated requests from the loop to update the screen and the status bar are conflicting with one another, this might indeed cause the procedure to run slower without the DoEvents function!

Читать еще:  Изменение формул в excel

Of course this is all just a theory. I’d love to hear if you all get similar results in your tests!

The obvious solution to decreased performance caused by the DoEvents function is to only call it intermittently in the loop. This can easily be achieved by using the VBA Mod operator like this:

The line of code above tells Excel only to call DoEvents at every tenth iteration. This decreases the total execution time of the macro tested in row 2 of the table from 91.16s to only 29.67s and you can still interrupt the code execution. A no-brainer!

Finally, let’s discuss the generic applications of the VBA DoEvents function and, just as importantly, when not to use it!

Application ideas using VBA DoEvents

Broadly speaking, there are two main reasons for using the DoEvents function:

  • Debugging purposes.
    • Let say you’re developing a procedure which loops through thousands of rows while performing expensive operations. Unless you include DoEvents in your loop, it means you simply must wait until all the operations specified in your code have been carried out, even if your code isn’t working as expected. This is particularly annoying if you mistakenly wrote an infinite loop because you forgot to specify a termination clause.
  • Improved user experience.
    • The users of your Excel VBA programs may find it annoying to watch a blank screen while your procedure is executing. For psychological reasons, you may want use something like a status bar or VBA progress bar to track the progress of your procedure while it’s running. Visual tools like these are nice distractions for an end user.

The DoEvents function should not always be used in your procedures, though. For most “lightweight” procedures with short execution time, this function is entirely unnecessary. Moreover, if performance is key and your code has been thoroughly tested, you may want to leave DoEvents out of your code, even if the execution time is substantial. In that case, the benefits described above may be outweighed by the price of using it.

The choice is yours!

Comments

If you haven’t already done so, please subscribe to my free wellsrPRO VBA Training Program. What are you waiting for? You’ll love the great VBA content I send your way!

Share this article on Twitter and Facebook, then leave a comment below and let’s have a discussion.

Oh, and if you have a question, post it in our VBA Q&A community.

The best free VBA training on the web
I see people struggling with Excel every day and I want to help. That’s why I’m giving away my 90-days to Master VBA eCourse and my entire personal macro library for free.

Over 2 million people use our VBA tutorials each year to help automate their work. Are you ready to reclaim your time, too? Grab our VBA Cheat Sheets and you’ll be writing macros like a professional. With over 180 tips and 135 macro examples, they include everything you need to know to become a great VBA programmer.

This article was written by Michael H. Sorensen, a contributing writer for The VBA Tutorials Blog.

Doevents vba excel

Возможно, в созданных тобой прекрасных проектах есть процедуры, выполняющие длительные по времени циклы: сортировки, считавание или запись данных, поиск файлов и т.п. И если во время их выполнения ты заметил, что не можешь подвинуть свою форму по экрану, а другие, открытые окна оставляют на ней белые области (форма не обновляется), то тебе в самый раз прочитать про функцию 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 снижает быстродействие просто в разы. Но тем не менее, использовать надо, но очень разумно.

Читать еще:  Excel vba активировать лист

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

Dim Flag As Boolean

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

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

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

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

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

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

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

VBA DoEvents, problèmes et solutions

Qu’est-ce que DoEvents ?

Quand utilise-t-on DoEvents ?

Cas des Entrées/Sorties (I/O ou E/S) :

Cas d’une boucle d’attente :

DoEvents, dans ce cas, permet à votre système de traiter d’autres informations, d’autres messages en attente. C’est le cas, par exemple, si vous devez attendre le rafraichissement d’une page Internet.

Cas d’un objet généré par appel au système d’exploitation :

Lorsque votre code génère un objet géré par un appel à votre système d’exploitation, il faut que cet objet soit totalement disponible pour pouvoir s’exécuter correctement. L’exemple sur le site de Microsoft démarre l’application Word grâce à la commande Shell. Si Word n’est pas encore ouvert, tout effort visant à établir un lien vers elle arrêtera le code. En utilisant DoEvents, votre procédure permet de s’assurer qu’une opération, telles que Shell, est complètement exécutée avant l’instruction de macro suivante est traitée.

Cas de l’appel d’une application qui a besoin d’une autre :

Cas du rafraichissement de l’affichage :

Lorsque votre code affiche, dans une Form, par exemple, des informations les unes après les autres, si vous ne souhaitez pas voir le résultat qu’en fin de macro, il vous faudra y intégrer des DoEvents.
Voyez ce test avec un UserForm en VBA :

Faites ce test avec et sans les 2 DoEvents.

Problèmes liés à l’utilisation de DoEvents

Imbrications de DoEvents

Exécution d’une procédure événementielle

Reprise du contrôle

Quantité de traitements

Illustrations / Exemples (à ne pas reproduire dans vos codes !) :

Double lancement d’une procédure événementielle

L’exemple du clic sur un bouton de commande est le plus fréquent. Que se passe t’il lorsque l’utilisateur clic deux fois (par impatience) sur un bouton dont le code contient un DoEvents ?

Dan Tohatan :
Imaginez ceci : Vous avez un bouton sur votre formulaire qui, une fois cliqué, fait un traitement complexe. Pendant ce traitement complexe, il appelle également de façon intermittente DoEvents afin de garder l’interface utilisateur de l’application «réactive» — pas la meilleure méthode, mais nous parlons d’un programmeur médiocre ici. Quoi qu’il en soit, l’utilisateur voit l’application toujours en action mais sans aucune indication qu’il y a un processus en cours. Ainsi, l’utilisateur clique à nouveau sur ce bouton pendant que le traitement se passe! A cause du DoEvents, La touche répond à l’événement et commence un autre thread de traitement. Donc, comme je l’ai dit plus tôt, DoEvents « fout en l’air » le flux de l’application trop facilement.

Constatations : Une fois que vous aurez cliqué deux fois sur votre bouton, vous pourrez fermer la Form grâce à la petite croix de fermeture. Cependant, votre code sera toujours en train de tourner. Vous ne pouvez pas repasser en mode création, à moins de ne « breaker » votre macro en frappant sur les touches Ctrl+Pause.

DoEvents en boucle et surchauffe du processeur

Comme nous l’avons dit précédemment : utiliser DoEvents en boucle ne peut que solliciter abusivement le processeur et en provoquer la surchauffe. Il ne s’agit d’augmentation de mémoire utilisée, mais de sollicitation abusive.

ucfoutu :
Insérez une TextBox (TextBox1) et un bouton de commande (Commandbutton1) et ce code :

Mettez en route le gestionnaire de tâches, onglet performances et, parallèlement lancez en cliquant sur le bouton de commande. Observez ce qui se passe alors (dans la petite «fenêtre» la plus à droite relative à l’utilisation de l’UC). L’outil constate à ce moment-là une augmentation (croissante ensuite) de la température. Sur ma vieille machine de test (utilisée exprès car moins bien réfrigérée) on entend même le ventilateur de refroidissement du processeur commencer à s’affoler.
Faites un break (CTRL + PAUSE) de la macro, tout se «calme».
Il ne faut donc pas utiliser DoEvents en boucle sur une si longue durée. Va bene sur des petites «attentes», mais pas sur de longues périodes.

DoEvents a tendance à ralentir le système

En effet, il augmente la quantité de traitement que le système doit effectuer pour chaque message.

ucfoutu :
Sur un userform :
— deux boutons (un pour lancer et l’autre pour arrêter)
— un label

Constatation : Il faut être patient pour 300 000 itérations. Vous pouvez réduire ce nombre si la patience n’est pas votre qualité principale. Le temps d’exécution entre les deux méthodes utilisés est saisissant.

Pourquoi avoir conservé cette fonction ?

Question: Pour qu’elle raison avoir maintenu DoEvents dans le .NET framework? Pourquoi ne pas l’avoir limité à une association dans l’espace de noms Microsoft.VisualBasic? Qu’elle est l’utilité de DoEvents lorsqu’il existe un support approprié pour les applications Multi-Thread?

Réponse: Jason (Microsoft): DoEvents est un vestige de VB 5.x, mais il est toujours utile dans le monde de .NET Framework. Si vous avez une boucle qui fonctionne pendant une longue période, il est souvent plus facile d’appeler DoEvents que de remanier votre boucle pour utiliser un véritable multitâche (threading) .NET.

Q: Pourquoi DoEvents est-il dans l’espace de noms BCL?

R: Glenn (Microsoft): Le multitâche est difficile, et devrait être évité s’il y a un moyen plus simple. Si tout ce que vous avez besoin dans votre thread d’interface utilisateur c’est du rendement, DoEvents est parfait.

Q: DoEvents est-il le diable?

A: Glenn (Microsoft) : La souplesse dans votre thread d’interface utilisateur est une pratique légitime dans la programmation Windows. Cela a toujours été. DoEvents rend les choses faciles, parce que les situations dans lesquelles vous avez besoin de l’utiliser sont simples.

Méthodes de substitution :

Application.OnTime

Vous pouvez avoir besoin d’exécuter du code périodiquement et automatiquement. Pour cela, vous pouvez faire appel à la méthode OnTime de l’application afin d’exécuter automatiquement une procédure. Cette méthode permet de planifier l’exécution d’une action spécifique lorsqu’un délai donné s’est écoulé ou de planifier l’exécution d’une action à une heure définie. Elle met donc en attente l’exécution d’une action. Contrairement à d’autres méthodes (Application.Wait. ), avec Application.Ontime, l’utilisateur ne perd jamais la main.
L’idée ici est de créer un appel, à intervalle régulier, de notre fonction, à partir d’elle-même. En s’auto-appelant toutes les x secondes, elle laisse le temps, à l’utilisateur, de réaliser des actions.
Chip Pearson :
http://www.cpearson.com/excel/ontime.aspx
Comme paramètres, la méthode OnTime prend l’heure précise à laquelle elle doit exécuter la procédure et le nom de la procédure à exécuter. Si vous devez annuler un OnTime, vous devez fournir l’heure exacte à laquelle l’événement a été calendrier avoir lieu. Il n’y a pas moyen de dire à Excel d’annuler le prochain événement OnTime ou d’annuler tous les événements OnTime attente. Par conséquent, vous avez besoin de stocker l’heure à laquelle la procédure est à exécuter dans une variable publique et utiliser la valeur de cette variable dans les appels à OnTime.

Exemple 1
Regardez ce que fait cet exemple, sans aucun DoEvents :
Dans un module :

Ajoutez deux boutons de commandes : CommandButton1 et CommandButton2.
Leurs codes respectifs seront :

Читать еще:  Формат excel 2020

Toutes les deux secondes, la procédure mon_timer est appelée par OnTime. L’utilisateur peut continuer de travailler entre ces appels.

Exemple 2
Dans cet exemple, il s’agit d’alimenter une boucle (pour une fausse progress_barre par exemple) tout en maintenant le bouton appuyé. La boucle stoppe lorsque l’utilisateur « relâche » le bouton.
Code du bouton de commande :

Function GetInputState — Un DoEvents maîtrisé

Sans DoEvent — Timer Event

Cette méthode de substitution utilise les API SetTimer et KillTimer pour créer une procédure événementielle. Celle-ci va donc «lancer» à intervalles réguliers une fonction, que vous désignerez dans le code.
Cet exemple est basé sur celui disponible sur le site de microsoft à cette adresse : http://support.microsoft.com/kb/180736/fr
Il va s’agir d’alimenter progressivement une progressBar (ici un label dont la propriété Width sera incrémentée de 1 en 1) pendant que l’utilisateur saisit des données dans un TextBox.
Dans cet exemple, l’intervalle d’exécution est de 200 millisecondes (0,2 sec) et la fonction a pour nom : TimerProc

Dans un UserForm (UserForm1), placez : un CommandButton (CommandButton1), un Label (Label1), un TextBox (TextBox1) et un second bouton (CommandButton2).
Le code :

Nota : Si le code exécuté par le Timer change une valeur de cellule, et que vous êtes en mode d’édition dans Excel, pour, par exemple, la saisie des données dans une cellule, Excel va probablement planter complètement et vous perdrez tout travail non enregistré. Utilisez Windows Timer avec prudence.

Conseils :

  • Si vous démarrez un Timer, assurez-vous de l’arrêter. A la différence avec une boucle sans fin, vous ne vous apercevrez pas nécessairement du non-arrêt de votre Timer et ainsi consommerez de la mémoire inutilement. En cas de souci, fermez Excel.
  • N’utilisez qu’un Timer à la fois. Ne débutez jamais un nouveau Timer sans avoir préalablement arrêté le premier.
  • Une procédure utilisant ce type d’API n’est pas nécessairement portable d’une machine à l’autre. A tester par conséquent sur toutes les machines devant utiliser votre code.

Conclusion :

DoEvents devrait n’être utilisé que pour des choses simples comme permettre à l’utilisateur d’annuler un processus après qu’il ait commencé ou le rafraichissement d’un UserForm. Pour les opérations nécessitant une exécution plus longue, l’opérateur pourra plus aisément rendre la main au processeur s’il a recours à un contrôle Timer ou à un composant EXE ActiveX. Dans ce cas, la tâche s’effectue de manière complètement indépendante, hors de votre application, le système d’exploitation gérant à la fois le multitâche et le partage du temps.

Un très grand merci à ucfoutu. Rendons à César.

Sources et saines lectures :

Ce tutoriel est un complément de : celui-ci. Vous pourrez y trouver un exemple d’utilisation.

Конференция VBStreets

Весь вкус программирования!

  • Список форумовVisual BasicVisual Basic 1–6
  • Изменить размер шрифта
  • FAQ
  • Вход

Вопрос про doevents. Как побороть подвисание формы.

Вопрос про doevents. Как побороть подвисание формы.

2vv » 17.03.2008 (Пн) 19:37

ANDLL » 17.03.2008 (Пн) 20:05

Не, она делает нечто совершенно другое. Просто обрабатывает накопленные клики, клавиши и прочее.
Система сама себе передает управление, когда ей захочется.

Вообще решение проблемы — просто понаставить в узких местах функции DoEvents.

Wildwhiteash » 17.03.2008 (Пн) 22:34

покажу кусок кода с камментами
Код: Выделить всё ‘******************************
‘этот кусок для понимания не принципиален
‘время его выполнения где то от 0,05сек до 3 сек
Private Sub mnuWriteToDisk_Click()
ReDim BuferS(0)
Call WriteModule(SelectedItemMenuMod) ‘ SelectedItemMenuMod — переменная с номером эл-та списка
‘пишет модуль служебной информации на блины винта
ReDim BuferS(0)
End Sub
‘******************************

‘******************************
‘а вот эта фиговина пишет туду все отмеченные
‘если не делать DoEvents
‘то пока всё не отработает, то формы приложения висят
‘и ессно по кнопке СТОП ничего не происходит
Private Sub mnuWriteAllToDisk_Click()

Dim XX As Byte
Dim NextMod As Label

ProgressBarMod.Min = 0
ProgressBarMod.Max = 100

bStopMOD = False
For XX = 1 To (lw1.ListItems.Count)
lw1.ListItems.Item(XX).Selected = True
SelectedItemMenuMod = XX
If lw1.ListItems.Item(XX).Checked = False Then GoTo NextMod

ReDim BuferS(0)
Call WriteModule(SelectedItemMenuMod)
ReDim BuferS(0)

DoEvents ‘ ВОТ ОНО.
‘перед проверкой кнопки СТОП
‘можно тыкать несколько штук в процедурине
‘там где хотелось бы что то развесить (антоним «завесить»)

If bStopMOD = True Then
ProgressBarMod.Value = 0
ProgressBarMod.DrawProgressBar
Exit Sub
End If

NextMod: ProgressBarMod.Value = Int((XX * 100) (lw1.ListItems.Count))
ProgressBarMod.DrawProgressBar
lw1.Refresh

Next XX
ProgressBarMod.Value = 0
ProgressBarMod.DrawProgressBar
End Sub
‘******************************

2vv » 17.03.2008 (Пн) 23:18

Wildwhiteash В приведенном примере Doevents стоит после процедуры. Это я к тому , что до DoEvents еще очередь должна дойти, а дойдет она после того как выполниться процедура «Call WriteModule(SelectedItemMenuMod)» .
Привожу код , который я использую:
Код: Выделить всё strComputer = «.»
Set objWMIService = GetObject(_
«winmgmts:\» & strComputer & «rootcimv2»)
Set colPings = objWMIService.ExecQuery _
(«Select * From Win32_PingStatus where Address = ‘192.168.1.1’»)

For Each objStatus in colPings
If IsNull(objStatus.StatusCode) _
or objStatus.StatusCode<>0 Then
WScript.Echo «Computer did not respond.»
Else
Wscript.Echo «Computer responded.»
End If
Next

Так вот, у меня , пока не отработает IsNull(objStatus.StatusCode) программа дальше не выполняется, а ждать приходится несколько секунд, на которые программка «вешается». Добавление DoEvents строкой выше или ниже проблему не решает, потому как ждемс пока отработает вышеприведенная функция.
Если я не прав, поправьте.

alibek » 18.03.2008 (Вт) 9:28

alibek » 18.03.2008 (Вт) 9:29

2vv » 18.03.2008 (Вт) 12:36

alibek » 18.03.2008 (Вт) 16:02

2vv » 18.03.2008 (Вт) 19:24

2vv » 18.03.2008 (Вт) 19:26

Viper » 19.03.2008 (Ср) 7:56

Antonariy » 19.03.2008 (Ср) 10:26

2vv » 19.03.2008 (Ср) 11:39

Antonariy Не могли бы пояснить как это сделать.

Что еще пробовал:
Посмотрел пример многопоточности с вышеприведенной ссылки. Попробовал свой код запустить в другом потоке — неудачно. Похоже что в фоновом потоке нельзя обращаться к WMI.

Решил поискать готовый контрол для работы с icmp . Оказалось что есть и такие. Например здесь OstroSoft.com. С этим контролом еще проще чем с WMI. Но все-таки форма «подвисает» когда пингуешь выключенные компы, но в целом стало значительно лучше. Остановлюсь на этом варианте. Хотя на досуге попробую организовать пингование в фоновом потоке.

Antonariy » 19.03.2008 (Ср) 14:00

2vv » 19.03.2008 (Ср) 17:50

alibek » 19.03.2008 (Ср) 17:59

Основное тут не в этом.
Основное — в первом предложении, тип проекта — Active EXE.
Это единственный легальный способ сделать в VB многопоточную программу.

Ты пишешь объект, у которого будет метод Ping и событие PingComplete. Из своей основной программы ты создаешь этот объект, вызываешь метод Ping, а тот вызовет событие PingComplete по завершению работы. При этом этот метод будет работать в отдельном потоке и не будет приостанавливать твою программу.

Antonariy » 19.03.2008 (Ср) 19:10

если сам пинг выполняется не в методе. Это важно.

В общем, вот заготовка.

2vv » 19.03.2008 (Ср) 19:41

2vv » 19.03.2008 (Ср) 19:57

Хакер » 19.03.2008 (Ср) 20:48

2vv » 19.03.2008 (Ср) 22:04

2vv » 19.03.2008 (Ср) 22:30

2vv » 20.03.2008 (Чт) 1:30

Хакер » 20.03.2008 (Чт) 2:21

2vv
«Параметр не факультативный» означает в первую очередь, что надо избавиться от руссифицированного VB6.

Во-втторую, что параметр, который не Optional, почему-то тобой не задан.

2vv » 20.03.2008 (Чт) 14:48

На работе стоит английская версия. Так ней даже класс не может открыть. Пишет «Automation error» на строке:
Set lPing = New PingExe.PingClass

Дома такой ошибки нет. Ссылку на класс в проекте прописал.
Может в VB какие-то настройки есть хитрые ?

2vv » 20.03.2008 (Чт) 15:30

Проблема которая описана постом выше решена. Проект был на рабочем столе. Английский VB6 не умеет работать с русскими папками.

Возвращаясь к теме, как задать параметр который не Optional.
Я объявляю класс:
Dim WithEvents lPing as PingExe.PingClass

в классе есть событие PingComplete, которое получает значение по прохождении процедуры Ping (это метод класса). Так вот mPing_PingComplete — «Argument no optional» . Я не понимаю в чем загвоздка.

Antonariy » 20.03.2008 (Чт) 15:39

Умеет. Проблема была не в этом.

На строчке mPing_PingComplete не может возникать такая ошибка. Она может возникнуть если событие описано с параметрами, а генеришь его без параметров, типа этого:
Код: Выделить всё Public Event PingComplete(Success as Boolean, Timeout as Single)

2vv » 20.03.2008 (Чт) 15:45

Что бы было проще, покажу что написал
Это процедура главной формы опрашивает ячейки таблицы и присваивает статус:
Код: Выделить всё Private Sub stping()
Dim i
For i = 1 To 15 Step 1
IP_ADR = CStr(GrdTable.TextMatrix(i, 1))
If Len(IP_ADR) <> 0 Then
lPing.Ping IP_ADR
If lPing_PingComplete <> 0 Then
Avaible = «OFFLINE»
GrdTable.Row = i
GrdTable.Col = 3
GrdTable.CellBackColor = vbRed
Else
Avaible = «ONLINE»
GrdTable.Row = i
GrdTable.Col = 3
GrdTable.CellBackColor = vbGreen
End If
GrdTable.TextMatrix(i, 3) = Avaible
‘WinsockInit
‘GrdTable.TextMatrix(i, 2) = HostByAddress(IP_ADR)
Else
End If
Next
End Sub

Это модуль класса:
Код: Выделить всё Option Explicit
Dim WithEvents Timer As Timer
Dim Form As Form1
Dim IpAdr As String
Dim objPing
Dim objStatus
Dim Ret As Long
Public Event PingComplete(Status As Long)

Public Sub Ping(IP_ADR As String)
IpAdr = IP_ADR
Set objPing = GetObject(«winmgmts:«).ExecQuery(«select * from Win32_PingStatus where address = ‘» & IpAdr & «‘»)
For Each objStatus In objPing
If IsNull(objStatus.StatusCode) Or objStatus.StatusCode <> 0 Then
Ret = 1
Else
Ret = 0
End If
Next
Timer.Enabled = True
End Sub
Private Sub Class_Initialize()
Set Form = New Form1
Load Form
Set Timer = Form.Timer1
End Sub
Private Sub Class_Terminate()
Set Timer = Nothing
Unload Form
Set Form = Nothing
End Sub
Private Sub Timer_Timer()
Timer.Enabled = False
RaiseEvent PingComplete(Ret)
End Sub

Вобщем lPing_PingComplete ничего не отдает. Правда я еще не до конца разобрался как работать с событиями.

alibek » 20.03.2008 (Чт) 15:54

2vv , ты написал ерунду, не разобравшись, что тебе посоветовали.
lPing_PingComplete — это не переменная.
Твой код должен быть таким: lPing.Ping IP_ADR
Все.
Остальное пишется в событии lPing_PingComplete.

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

2vv » 20.03.2008 (Чт) 22:02

Ссылка на основную публикацию
Adblock
detector