1

Тема: VBA: Непонятки с перехватом событий Word (WithEvents)

Здравствуйте! Не могу разобраться с такими непонятками. Допустим, мне надо перехватывать клики в Word. Пусть есть форма "myForm1" с двумя кнопками:

"Включить перехват" -- запускает процедуру "Word_Events_Register" (её код см. ниже)
"Выключить перехват" -- запускает процедуру "Word_Events_UnRegister" (её код см. ниже)

Есть модуль класса "clsWordEvents" с таким кодом

' Этот объект (класс) нужен для перехвата событий Word.
' В данном случаю он использ-ся для перехвата события
' WindowSelectionChange (это когда кликаешь курсором в
' разных местах)
'
Option Explicit

Public WithEvents AppWord As Word.Application

Private Sub AppWord_WindowSelectionChange(ByVal Sel As Selection)

    Dim FontName As String
    Dim SymbCode As Integer
    Dim SymbCodeS As String

    FontName = Sel.Range.Font.Name

    If FontName <> "Verdana" Then
       ' шрифт не Verdana -- выходим
       Exit Sub
    End If
    ' Шрифт Verdana -- продолжаем

    ' выделяем смивол, по которому (перед которым) кликнули курсором
    '
    If Sel.Type = wdSelectionIP Then
       Sel.MoveRight Unit:=wdCharacter, Count:=1, Extend:=wdExtend
    End If

End Sub

есть модуль "Module1" с кодом:

Option Explicit

Private obWordEvents As clsWordEvents
'
' Объект класса clsWordEvents, после своего создания он будет
' реагир на события Word (тут на событ WindowSelectionChange)
'
' NOTES:
'
' 1. чтобы это работало, в VBA-проекте должен быть класс
'    clsWordEvents -- проверь!
'
' 2. создание obj obWordEvents -- в sub Word_Events_Register,
'    уничтожен -- в sub Word_Events_UnRegister (после уничтож
'    Word не будет реагир на событие WindowSelectionChange --
'    это сделано для удобства, чтобы отключ. перехват событий
'    когда это не нужно и мешает. Запуск этих SUB -- вручную
'    из окна макросов Word.
'

Sub Word_Events_Register()

    ' Создает объект (обработчик событий), перехватывающий события
    ' Word (в данном случае это будет событ WindowSelectionChange)

    If Not obWordEvents Is Nothing Then
       ' ветка, когда объект-обработчик событий Word уже
       ' существует, so его создавать не надо -- выходим.
       '
       ' MsgBox "Обработчик событий уже был создан" & Chr(13) & _
       ' "и не требует повторной регистрации" & Chr(13) & _
       ' "(просто выходим)."
       Exit Sub
    End If

    Set obWordEvents = New clsWordEvents
    Set obWordEvents.AppWord = Word.Application

End Sub

Sub Word_Events_UnRegister()

    ' Уничтожает объект (если он ранее был создан запуском
    ' Word_Events_Register), перехватывающий события Word.
    ' Я сделал это просто для удобства, чтобы при необходимости
    ' отключать перехват событий.

    If obWordEvents Is Nothing Then
       ' ветка, когда объект-обработчик событий Word не
       ' существует, so его уничтожать не надо -- выход.
       '
       ' MsgBox "Обработчик событий не существует," & Chr(13) & _
       ' "поэтому его уничтожение не нужно." & Chr(13) & _
       ' "(просто выходим)."
       Exit Sub
    End If
    Set obWordEvents = Nothing

End Sub

Есть модуль "FormsView" с кодом:

Option Explicit

' В этом модуле Е макросы для отображения или скрытия
' разного рода диалоговых окон (форм, сообщений итп.)

Sub myForm1_Show()
    myForm1.Show ' отображ. форму myForm1
End Sub

Теперь делаем следующее. Выводим форму "myForm1" (запуском в окне макросов процедуры  "myForm1_Show"). Жмём кнопку "Включить перехват" (сработает процедура "Word_Events_Register"). Кликаем по тексту в Word и после клика курсор превращается в выделение — примерно так: "сл|ово" --> "сл[о]во" ("|" означает курсор, а "[]" выделение).

Жмем кнопку "Выключить перехват" (сработает процедура "Word_Events_UnRegister") и теперь при кликах выделения не будет: "сл|ово". — Всё так как мне и надо, это понятно и нормально.

Допустим теперь, что на форме есть третья кнопка "Action" (название условное), которая запускает такой код:

Private Sub btnAction_Click()

    Word_Events_UnRegister ' выключили перехват событий
    Selection.Move Unit:=wdCharacter, Count:=1 ' сдвинули курсор на 1 символ вправо
    Word_Events_Register ' включили перехват обратно

End Sub

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

"сл|ово" --> "сл[о]во"
-- а должно быть (по идее) так: --
"сл|ово"

То есть такое впечатление, что движение курсора (Selection.Move) почему-то перехватывается, хотя перехват вроде бы выключен. Может кто-нибудь объяснить, почему так происходит?

2

Re: VBA: Непонятки с перехватом событий Word (WithEvents)

Проверяйте, вызывается ли Word_Events_UnRegister в этом случае.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

3

Re: VBA: Непонятки с перехватом событий Word (WithEvents)

Перед тем, как задавать вопрос тут на форуме, я всё проверил. Всё там вызывается: если в моём коде выше убрать последнюю строку (Word_Events_Register), то будет как и должно быть: "слово" -- кликаем (допустим) перед первым "о" -- получаем: "сл|ово".

Сначала (для отработки) я сделал на форме 2 вспомогательне кнопки (писал о них выше), одна включает перехват, другая выключает. В моём коде есть места, где мне надо отключить перехват, что-то сделать и потом включить его снова. Пока я делал это кнопками вручную всё было нормально, но когда стал делать программно (как и должно быть, ведь я же не буду потом в готовой программе руками тыкать кнопки включения/отключения перехвата) вылез вот такой странный глюк. Хотя может это и не глюк, а я просто что-то не понимаю в этом дурацком VBA …

4

Re: VBA: Непонятки с перехватом событий Word (WithEvents)

Если нужно временно отключать выделение программно, то вместо отписки/подписки сделайте глобальный флаг, подавляющий действие в AppWord_WindowSelectionChange.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

5 (изменено: EgorS, 2026-01-31 23:22:52)

Re: VBA: Непонятки с перехватом событий Word (WithEvents)

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

Короче, добиться того, что мне надо, никак не получается (отключить обработчик, что-то сделать, включить снова).

Если есть желание и/или возможность поковыряться — могу выложить файл Word с примером.

6

Re: VBA: Непонятки с перехватом событий Word (WithEvents)

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

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

7

Re: VBA: Непонятки с перехватом событий Word (WithEvents)

У меня тоже была мысль, что может быть там какая-то нестыковка по времени. Я пробовал добавлять паузу (ещё до того как спросил тут на форуме): отключаем обработчик -> двигаем курсор -> пауза (функция Timer в VBA) 10 секунд -> включаем обработчик -- та же самая погань: как только пауза истекает происходит реакция на сдвиг курсора.

Но если делать все вручную (я писал уже про это, просто повторю для наглядности) отдельными кнопками: включили обработчик (кнопкой No1 на форме) -> подвигали курсор (руками с клавы стрелками или отдельную кнопку можно сделать) -- перехват есть -> выключили/или уничтожили обработчик (кнопкой No2 на форме) -> подвигали курсор -- всё нормально: нет реакции на движение. А вот программно почему-то так не работает...

8 (изменено: 3wedsmncjklvjvd73734, 2026-02-01 17:09:25)

Re: VBA: Непонятки с перехватом событий Word (WithEvents)

teadrinker пишет:

Попробуйте добавить небольшую паузу


В своё время, я так ловил проблемы в прогах на медленных машинах. Там очень хорошо было видно как выполняется прога, потому что машины очень медленные... Из-за памяти. И поэтому что бы прога работала компы лезли в виртуальную память. И вот поэтому прекрасно было видно как и что там творилось в проге.

EgorS пишет:

(функция Timer в VBA) 10 секунд

Не не так надо.
Там в VB и в VBA есть

Doevents

Код останавливается и ждёт.. Обычно это делают что бы не было подвисания в циклах, и что бы юзер смог остановить цикл. В VBS такого нет, и пришлось писать собственную ожидалку с системным таймером. Хотя есть в VBS и sleep, но бывает он не срабатывает полностью как надо.

9 (изменено: 3wedsmncjklvjvd73734, 2026-02-01 17:21:14)

Re: VBA: Непонятки с перехватом событий Word (WithEvents)

EgorS пишет:

Private Sub btnAction_Click()

    Word_Events_UnRegister ' выключили перехват событий
    Selection.Move Unit:=wdCharacter, Count:=1 ' сдвинули курсор на 1 символ вправо
    Word_Events_Register ' включили перехват обратно

End Sub


Private Sub btnAction_Click()

    Word_Events_UnRegister ' выключили перехват событий
Do_100
    Selection.Move Unit:=wdCharacter, Count:=1 ' сдвинули курсор на 1 символ вправо
Do_2000
    Word_Events_Register ' включили перехват обратно

End Sub

Function Do_(byval H)
Dim x as integer
for x=0 to H
Doevents
next 
end function

xxxx://learn.microsoft.com/ru-ru/office … s-function