Остановка выполнения макроса

Автор Kadet, 19 октября 2019, 10:13

0 Пользователи и 1 гость просматривают эту тему.

Kadet

Обыскал интернет не могу найти ответ.
В работе часто появляется следующая ситуация. Есть макросы, которые выполняются долго. Для объяснения обзовём Макро1 и Макро2, подразумевая при этом, что это один и тот же макрос, запущенный в разное время.
Так вот. Запускается Макро1 и начинает долго выполняться (заполнение таблиц). Оператор, не дождавшись окончания выполнения Макро1, заполняет фильтр и запускает этот же макрос (Макро2) повторно.
Получается следующая ситуация: Макро1 выполняется и во время этого запускается Макро2. Макро1 приостанавливается (засыпает) и начинает исполняться Макро2. После того, как Макро2 закончит исполнение (обычно второй запуск недолгий) Макро1 снова просыпается и продолжает исполняться.
В итоге вылезают некоторые нежелательные кракозябры и косяки.

Так вот, хочу найти способ при повторном запуске одного и того же макроса (Макро2) первый запуск этого макроса (Макро1) прекращал работу окончательно, а не приостанавливался, и потом не возобновлялся.

Должна быть какая-то команда. Пока не могу найти её.

Kadet

Клавиатурой это делается Ctrl+Shift+Q.
Если есть возможность остановки макросов клавиатурой, то должен быть соответствующий метод командой из макроса.

mikekaganski

#2
Цитата: Kadet от 19 октября 2019, 12:03
Клавиатурой это делается Ctrl+Shift+Q.
Если есть возможность остановки макросов клавиатурой, то должен быть соответствующий метод командой из макроса.

Естественно; эта комбинация клавиш (переназначаемая в Tools->Customize...) выполняет ".uno:BasicBreak". Но вот проблема: эта команда останавливает любой макрос; и если попытаться из макроса, который выполняется параллельно с другим, эту команду выполнить, остановятся оба. Ну и ещё вылезет диалог - он там вызывается без вариантов.

Можно попробовать по-другому (я не знаю подробностей Вашего макроса, и поэтому оно может быть неприменимо в Вашем случае): создать глобальную переменную-счётчик параллельно выполняемых макросов, и в начале функции делать инкремент её, а затем цикл ожидания, пока она станет равной 1; в теле в цикл вставить проверку, что она равна 1, и если больше - декремент и прерывание выполнения; в конце декремент. Оно может сработать - но только если принципиально невозможно получить три и больше параллельно выполняемых макроса...
С уважением,
Михаил Каганский

Kadet

Спасибо за наводку. Я поковыряюсь в ".uno:BasicBreak". Возможно, что-нибудь накопаю в этом направлении. Какие-нибудь варианты.
По большому счёту в моей программе при выполнении текущего макроса фоновое выполнение всех прочих не существенно. Лишь бы перехватчики не вырубались. При необходимости прочие макросы и потом можно и повторно запустить.
Выброс окна, конечно, вещь нежелательная.

Второй вариант, со счётчиком, мне кажется не очень удобным. Ведь счётчик, как мне кажется, может приостановить или вовсе остановить исполнение Макро2, а не Макро1. А в процессе работы именно Макро2 является более важным, чем Макро1. И выполнение Макро1 нужно останавливать, а не Макро2.
К тому же, если не останавливать Макро2 по счётчику, а приостанавливать (т.е. зациклить режим ожидания выполнения Макро1), то не факт, что Макро1 при этом продолжит работу, ведь он останавливается аппаратно (самой LO) и именно Макро1 ждёт выполнения Макро2, а потом сам начинает продолжать. Может получиться вечное ожидание друг-друга.

mikekaganski

Цитата: Kadet от 19 октября 2019, 10:13
Для объяснения обзовём Макро1 и Макро2, подразумевая при этом, что это один и тот же макрос, запущенный в разное время.

Итак, это один и тот же макрос.


Global MyMacroCounter As Long

Sub Macro
  On Error Resume Next
  ' Prevent running macros in parallel
  MyMacroCounter = MyMacroCounter + 1
  Do While MyMacroCounter > 1
    Wait 1
  Loop
  ' Now that we are alone, run actual code
  MacroImpl()
  ' Always decrement counter after exiting MacroImpl
  MyMacroCounter = MyMacroCounter - 1
End Sub

Sub MacroImpl
  ' ... some slow code
  ' A check somewhere in a loop
  If MyMacroCounter > 1 Then Exit Sub
  ' ... more slow code
End Sub

С уважением,
Михаил Каганский

mikekaganski

Цитата: Kadet от 19 октября 2019, 12:49ведь он останавливается аппаратно (самой LO) и именно Макро1 ждёт выполнения Макро2, а потом сам начинает продолжать. Может получиться вечное ожидание друг-друга

Ага, я об этом не подумал. Почему-то я решил, что оно работает в параллельных потоках.

Тогда надо сделать примерно то же самое, только без декремента:


Global MyMacroCounter As Long

Sub Macro
  Dim InitialMacroCount As Long
  InitialMacroCount = MyMacroCounter ' Remember initial value
  ' ... some slow code
  ' A check somewhere in a loop: if MyMacroCounter has incremented,
  ' then in the meanwhile, another macro instance was successfully run
  If MyMacroCounter > InitialMacroCount Then Exit Sub
  ' ... more slow code
  ' Increment counter after success
  MyMacroCounter = MyMacroCounter + 1
End Sub
С уважением,
Михаил Каганский

Kadet

mikekaganski, ну я так и понял. Единственное, вы предлагаете проверять счётчик в промежуточной (предзапускной) процедуре Macro. Тогда, конечно, приостановки первой запущенной процедуры не происходит.
Но, изменять счётчик (MyMacroCounter = MyMacroCounter - 1) нужно и в конце самой процедуры MacroImpl, а не только в предзапускной, иначе мы попадаем в вечный цикл.

Однако, спасибо, но опять таки, запуск Макро2 будет отложен до завершения работы Макро1. А мне нужно наоборот: при запуске Макро2 вообще прекратить выполнение Макро1.

Kadet

mikekaganski, ну, в принципе в длинном макросе идёт много вложенных циклов, поэтому-то он и длинный. В принципе проверку If MyMacroCounter > InitialMacroCount Then Exit Sub можно поставить в начале главного цикла, чтобы при каждой итерации проводил проверку и при MyMacroCounter>1 выбрасывал через Exit Sub. Тогда да. Вылетает именно Макро1, а Макро2, после уменьшения счётчика, запустится.

Спасибо за идею.

rami

Цитата: Kadet от 19 октября 2019, 12:49Выброс окна, конечно, вещь нежелательная.
Есть ещё .uno: BasicStop он останавливает без окна.

Kadet


Kadet

#10
Спасибо за помощь. Вопрос решил ".uno:BasicStop"

Dim dispatcher
Dim document
document   = ThisComponent.CurrentController.Frame
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
' dispatcher.executeDispatch(document, ".uno:BasicBreak", "", 0, Array())
dispatcher.executeDispatch(document, ".uno:BasicStop", "", 0, Array())

Поставил этот код в самый конец процедуры формирования таблиц. Останавливает все макросы. Перехватчики не выключаются.
В общем - всё устраивает.

Kadet

#11
Хотел таки воспользоваться идеей mikekaganski со счётчиком в другом месте. Там макросы исполняются последовательно - второй ждёт первого. Но не получилось.
Там два перехватчика отслеживают изменения значений двух ячеек Calc и в итоге вызывают один и тот же макрос. А ячейки играют роль фильтров. Так вот, в отличие от запуска макросов, тут второй Листенер не начинает работу, пока не закончит работу первый, а первый не заканчивается пока не закончит работу вызванный из него макрос. Именно его и желательно периодически останавливать. В общем из-за этого ожидания счётчик просто не срабатывае - всегда в работе только одна версия макроса.

Однако, идея mikekaganski с проверкой внутри цикла макроса оказалась полезной. Внедрил в цикл макроса проверку первичных значений этих ячеек, и если они изменяются, то Exit Sub. И начинает работать второй Листенер.

Спасибо за помощь!

Kadet

И ещё один непростой, для меня, вопрос. Может тоже подскажите как это решить.
Во вложенных документах calc по клику мыши (по событию "mouseReleased" - отжатие) выполняется первичное форматирование листа. Так вот иногда после подобного клика кнопка как бы не сбрасывается, а происходит как бы "удержание кнопки мыши", хотя этого на самом деле нет. Просто мышкой водишь, а за ней голубое окошко тянется.
Как можно макросом сбросить это фальшивое "удержание", выделение области.