Немного другой взгляд на DoEvents состоит в том, что он сбрасывает события в очереди событий. Если ваша подпрограмма или функция запускает событие, этот обработчик событий становится подпрограммой, которая должна быть запущена, как только ваша подпрограмма / функция будет завершена. DoEvents говорит, что теперь нужно запускать эту подпрограмму обработчика событий, а не ждать конца своей подпрограммы.
Хотя я согласен с Джонатоном в духе отказа от использования DoEvents, я бы умерил его высказывание, сказав, что рекомендую использовать его, только если вы точно знаете, почему, и знаете все последствия изменения порядка очереди событий таким образом. Чаще всего DoEvents указывается, когда вы хотите каким-либо образом обновить экран из контекста подпрограммы, прежде чем подпрограмма будет завершена.
Примером этого является использование элемента управления ProgressBar. Предположим, вы перебираете несколько тысяч записей и хотите предоставить пользователю обратную связь о том, как далеко вы продвинулись, обновив индикатор выполнения. Вы можете прерывать цикл через каждые сто записей и изменять значение в элементе управления ProgressBar. Однако (если вы не сделаете что-то с этим), вы не увидите изменения на экране до тех пор, пока не будет запущен обработчик события изменения индикатора выполнения, и этот обработчик не запустится, пока ваша подпрограмма не будет выполнена. Он просто будет помещен в очередь событий. Чтобы принудительно запустить событие изменения, приостанавливая работу вашего сабвуфера, нужно вызвать DoEvents. Это удалит все существующие события из очереди - в этом случае событие изменения вашего индикатора выполнения - и обновит элемент управления индикатором на экране.
Теперь "нехватка стека" в основном означает, что вы попали в бесконечный цикл вызовов функций. Самый простой способ вызвать это так:
Public sub MySub()
MySub
End Sub
А затем позвоните MySub откуда-то. Вы получите ошибку пространства вне стека. Если вы посмотрите на стек вызовов, вы увидите очень длинную линию вызовов на MySub.
Хорошо известный пример из реальной жизни мог бы появиться в более старых версиях VB:
Public Sub TextBoxArray_LostFocus(index as Integer)
If TextBoxArray(index) = "" Then
TextBoxArray(index).SetFocus
MsgBox "Please enter a value"
End If
End Sub
В этой ситуации предполагается наличие двух членов массива элемента управления TextBox с именем TextBoxArray. Теперь, если пользователь начинает с первого (индекс 0) и переходит ко второму (индекс 1), событие LostFocus индекса 0 сработает. Однако VB также внутренне установил бы фокус на поле индекса 1. Затем код установит фокус обратно на индекс 0, запустив событие индекса 1 LostFocus! Вы попали в петлю. Они исправили это в VB5 или 6, ожидая установки фокуса, пока не завершилось выполнение события LostFocus.