Страницы: 1
RSS
Как остановить таймер, Таймер не желает останавливаться
 
Уважаемые форумчане, добрый день.
Очень требуется помощь знатоков.
В прикреплённом файле есть кнопка, на которую выведена подсказка с помощью библиотеки user32. При клике ЛКМ по этой кнопке срабатывает макрос, который проверяет количество открытых файлов эксель, если открыт только один файл, тогда макрос закрывает приложение, иначе закрывает только текущий файл.
Но так получается что процесс таймера не прибивается и в итоге закрываются все открытые файлы.
Если запустить этот макрос принудительно (т.е. не с помощью клика по оранжевой кнопке), то всё работает отлично, а вот с кнопки всё печально.

Но помощь нужна не в том, как обойти процесс использования библиотеки user32, а в том, как остановить процесс таймера.
 
AndreiSMT, пример использования в хелпе смотрели? Насколько помню там описано.
Если конечно угадал что за таймер, на мобиле не вижу...
 
Hugo, добрый день Игорь. К сожалению я в этом не разбираюсь :(
 
Написал этот код Jaafar Tribak, но он уже долгое время не появляется на этом форуме.
 
Я думал речь об Application.OnTime, про этот таймер ничего не скажу.
У меня тоже глючит, даже если через кнопку выполнять сразу Sub DataSave()
 
Hugo, Игорь, вот здесь парень написал про остановку таймера через библиотеку user32, но как проверить этот метод на моей кнопке я совершенно не соображаю.
Может глянете, если будет время.
 
Код и не рабочий.Гиперссылка макрос не запускает,она только открывает код.
В чем смысл таймера.Сделал, чтобы работало.На всякий случай сделал задержку 3 сек, хотя она не нужна
 
doober, спасибо вам большое, но давайте всё же вернёмся к моему образцу. Почему вы говорите, что кнопка с гиперссылкой не запускает код? У меня всё запускает!
Просто иногда получается так, что в момент выполнения такого кода открыты другие файлы эксель и эта система с user32 закрывает мне все открытые файлы.
Изначально вот этот код для кнопок с подсказками через гиперссылку предложил Tim Williams.
Код
Sub Tester()
    'set up some buttons
    With ActiveSheet
        AddMacroAndPopUp .Shapes("Rectangle 1"), "Test1", "popup 1"
        AddMacroAndPopUp .Shapes("Rectangle 2"), "Test2", "popup 2"
    End With
End Sub

'utility sub to configure a shape with a link and some pop-up text
Sub AddMacroAndPopUp(shp As Shape, macroName, txt As String)
    Dim ws As Worksheet
    shp.Parent.Hyperlinks.Add Anchor:=shp, Address:="#" & macroName & "()", ScreenTip:=txt
End Sub

'Example functions called from hyperlinks
'**************************************************
Function Test1()
    Debug.Print "Test1"
    SaveData      'do something here
    Set Test1 = Selection  '<< must return a "destination" for the link,
                           '      in this case the clicked shape
End Function

Sub SaveData()
    Application.ScreenUpdating = False
    Application.DisplayAlerts = False
    On Error Resume Next
    With ThisWorkbook
        .Worksheets("Sheet1").Copy
        ActiveWorkbook.ActiveSheet.Name = "SMT"
        ActiveWorkbook.Worksheets(1).DrawingObjects.Delete
        With ActiveWorkbook.VBProject.VBComponents("Sheet1")
          .CodeModule.DeleteLines 1, .CodeModule.CountOfLines
        End With
        ActiveWorkbook.SaveAs Filename:=ThisWorkbook.Path & "\Data_1C.xlsb", FileFormat:=xlExcel12, CreateBackup:=False
    End With
End Sub

'called from hyperlink
Function Test2()
    Debug.Print "Test2"
    MacroTest2      'do something here
    Set Test2 = Selection
End Function

Sub MacroTest2()
MsgBox "Test2, popup 2"
End Sub
Пример прикрепил.
Но этот код успевает запустить только какие-нибудь короткие действия, в данном примере MsgBox "Test2, popup 2" запускает, а SaveData нет.
Я обратился к Jaafar Tribak за помощью и он мне предложил вариант с использованием библиотеки user32. Я очень обрадовался, когда увидел что всё работает, и всё запилил на этой основе. Но спустя какое-то время мне понадобилось добавить проверку открытых файлов excel и вот тут я и заметил эту проблемку.

В общем есть веские причины, по которым меня стандартная подсказка совершенно не устраивает. А подсказка через гиперссылку устраивает полностью.

Если у вас есть идеи как доработать макрос от Tim Williams, или как прибить таймер от Jaafar Tribak - я был бы вам очень признателен.
Может можно, какими нибудь стандартными средствами отследить выполнение таких коротких действий как:
Debug.Print "Test1"
или
Set Test1 = Selection
или может можно как-то отследить MsgBox, но только чтобы само окно MsgBox не выскакивало.
Если одно из таких действий сработало, то запустить другое действие. В таком случае можно было бы обойтись без библиотеки user32.
Изменено: AndreiSMT - 20.05.2026 07:47:32
 
AndreiSMT - Добрый день, Андрей.
Запуск макроса гиперссылкой работает, но он не документирован.
Дело в том, что при этом никакие манипуляции с новыми объектами невозможны, например, такой код не сработает:
Код
Range("A1").Copy Range("N1")
А этот сработает:
Код
Range("O1").Value = Range("B1").Value
так как здесь объекты не создаются и не удаляются, а лишь меняется свойство Value.
Jaafar использовал т.н. CallBack (обратный вызов) с помощью однократного запуска таймера.
То есть, дал нормально завершиться коду, вызванному гиперссылкой, и сразу же, уже в нормальном, а не ограниченном режиме, таймером вызвал следующий код SaveData. Таймер там однократный и в циклах не используется, его останавливать не требуется.
Проблемы, очевидно, где-то в Вашем добавленном/измененном коде.
Если речь об этом из сообщения #1:
Код
Sub DataSave()
    ThisWorkbook.Saved = True
    If Workbooks.Count > 1 Then
        ThisWorkbook.Close False
    Else
        Application.Quit
    End If
End Sub
то здесь ThisWorkbook.Saved = True не позволит сохранить несохраненные данные, код просто закроет книгу без сохранения, что некорректно и не соответствует data save & exit на кнопке. Если проблема с тем, что после загрузки и попытки закрытия есть предупреждение, что не сохранены изменения, то это отдельный вопрос, который решается не так.
А Workbooks.Count > 1 не учитывает возможное наличие скрытой книги макросов, которая дает +1.
Корректнее считать количество только нескрытых книг.
Например, так:
Код
Sub DataSave()
    Dim i As Long, Wb As Workbook
    For Each Wb In Application.Workbooks
      If Wb.Windows(1).Visible Then
        i = i + 1
        If i > 1 Then Exit For
      End If
    Next
    If i > 1 Then
        ThisWorkbook.Close
    Else
        Application.Quit
    End If
End Sub
Изменено: ZVI - 22.05.2026 04:10:22
 
ZVI, добрый день. Жаль не знаю вашего имени. А моё имя вам подсказал мой ник :)
Спасибо вам большое, что откликнулись. Я учёл все неточности, о которых вы написали и прикрепил исправленный образец.
Когда создавал образец, не думая копировал имена функций. Прошу прощения, что ввел в заблуждение именем функции.
Дело в том, что в файле, который выполняет все функции, функция DataSave делает копию книги и сохраняет её, а в текущей книге мне как раз сохранять ничего и не нужно.

В файле образце создал ещё одну (зелёную) фигуру прямоугольника и назначил ей макрос, но подсказку через функцию гиперссылки на зеленую фигуру не вешал.
Прошу вас скачать прикреплённый файл и протестировать.
Для теста запустите какой-нибудь файл или несколько файлов и так же запустите мой файл, и нажмите на зеленую фигуру. Вы увидите что всё сработает, как положено, закроется только мой файл.
А если всё повторить, но нажать на оранжевую фигуру, которая запускает этот же макрос через гиперссылку, то увы закрываются все файлы, и судя по всему в аварийном режиме.

Кстати вот здесь автор темы пишет:
Код
'Этопришлось вынести в отдельную процедуру,  
'т.к. таймер не желал "убиваться" в таймерной процедуре tmrPrc  
Sub tmrKill()  
KillTimer &H0, tmrID  
End Sub
Вот я и сделал такой вывод, что таймер не прибивается. Может я и не прав, но какая-то проблема тут очевидно существует.
На мой взгляд таймер не дружит именно с ThisWorkbook.Close.
Изменено: AndreiSMT - 22.05.2026 14:43:29
 
Цитата
написал:
скачать прикреплённый файл и протестировать
Используйте такой код для безопасного закрытия книги/Excel из кода с API-таймером:
Код
Private Sub OnTimeExitXX()
    KillTimer Application.hwnd, 0
    Application.OnTime Now, "xExit"
End Sub

Владимир
 
ZVI, Владимир, большое вам спасибо!
 
Цитата
AndreiSMT написал: ZVI , Владимир, большое вам спасибо!
Андрей, рад, что проблема решена, удачи Вам!  
Изменено: ZVI - 22.05.2026 16:13:08
 
Владимир, спасибо и от меня!  :)  
Владимир
 
Цитата
sokol92 написал: Владимир, спасибо и от меня!  
Владимир, приветствую! 🤝
Изменено: ZVI - 25.05.2026 21:37:58
 
ZVI, Владимир, подскажите пожалуйста, можно ли как-то победить вот это?:

"Sheet1.xExit" работает, но такой вариант не устраивает по некоторым причинам.
Хотелось бы таким вариантом обойтись:
"Sheets("Sheet1").xExit"
 
Цитата
AndreiSMT написал: "Sheets("Sheet1").xExit"
Не очень понятно, что именно не устраивает и что требуется.
Отладчик нашел синтаксическую ошибку в текстовом выражении, т.к. внутри текста кавычки должны дублироваться.
Но исправление этой ошибки даст другую, потому что OnTime так не используется.
Лист имеет обычное (пользовательское) имя (.Name), которое отображается внизу в ярлычке листа, которое пользователь может поменять правым кликом на ярлычке с выбором "Переименовать".
Лист также имеет кодовое имя (.CodeName), которое не меняется при переименовании, указанном выше, и используется в кодовых ссылках, аналогично "Sheet1.xExit", где Sheet1 - это именно кодовое имя. При этом код будет работать при переименовании (ярлычка) листа, что и повышает стабильность кода.
Пользовательское и кодовое имя не обязательно должны совпадать.
Чтобы не путаться, полезно в VBA в окне ObjectBrowser изменить кодовое имя листа, например, на Sh1:

Если же Вам всё-таки зачем-то нужно использовать в ссылке пользовательское имя, то можно так:
Код
Application.OnTime Now, Sheets("Sheet1").CodeName & ".xExit"
Изменено: ZVI - 27.05.2026 02:52:57
 
ZVI, Володя, вы гений! Спасибо вам большое!
CodeName меня очень выручает. В моем случае имена листов, которые могут переименовываться так, как вы написали выше, уже не будут переименовываться, но последовательность их может меняться. И когда я добавляю какой-то лист и располагаю его не в конце, а где-то между существующими листами, то точно так же делаю и с CodeName, чтобы последовательность листов в окне VBA была такая же, как и в книге. Мне просто так легче ориентироваться:

И все обращения к функциям в макросах моей рабочей книги выглядят именно так: Sheets("Maket").xExit, т. е. Sheets("имя_листа").имя_функции.
Я знал, что можно обращаться к функции через CodeName вот так: Лист02.xExit, но этот вариант меня не устраивает, т. к. я иногда меняю CodeName. В таком случае мне пришлось бы бегать по всем листам и модулям, искать старое имя CodeName, чтобы сменить его на новое.
Я и не догадывался, что можно вот так прописать: Sheets("имя_листа").CodeName & ".имя функции"
Поэтому Sheets("Maket").CodeName & ".xExit" меня полностью устраивает.
Еще раз большое вам спасибо!

Уважаемая администрация форума, прошу прощения, что сам отвлёкся и других участников отвлёк от темы.
Изменено: AndreiSMT - 27.05.2026 07:06:47
 
Андрей, понятно тогда зачем.
Используйте, как Вам удобнее, конечно.
Удачи!
Страницы: 1
Читают тему
Наверх