Фокус MsgBox в Excel

Я рассчитываю много данных с помощью VBA в Excel и хочу показать MsgBox, когда это будет сделано. MsgBox фактически показывает время, затраченное на расчет.

Проблема заключается в том, что пользователь решает сделать что-то еще, пока происходит вычисление. Excel продолжает вычисления, и когда это будет сделано, MsgBox действительно отображается, но по какой-то причине Excel не передает фокус MsgBox. Значок Excel будет мигать на панели задач, и если мы щелкнем его, Excel развернется, но MsgBox находится за окном Excel, и мы НИКОГДА не сможем щелкнуть его. Так что единственный способ выбраться из этого - это убить excel.exe... не очень приятно. Alt+Pause также не работает, так как код будет остановлен только после текущей строки кода, которая заканчивается... при закрытии MsgBox.

Я пробовал функцию AppActivate("Microsoft Excel") раньше без какого-либо успеха (Как мне переместить фокус на почтовый ящик?). Имя приложения на самом деле длиннее, поскольку Excel 2010 добавляет имя документа к заголовку окна.

Любая идея, как я могу обойти эту досадную проблему?


person dan    schedule 04.12.2014    source источник
comment
Не то, чтобы это было решением, но поможет ли вам Alt-Tab?   -  person peege    schedule 04.12.2014
comment
Нет, я тоже пробовал. MsgBox не рассматривается Windows как окно (здесь W7), поэтому я могу видеть только Excel 2010 и фокусироваться на Excel 2010. Но в Excel фокус по-прежнему каким-то образом находится на MsgBox, который находится за окном Excel. Я мог бы попробовать нажать Enter на клавиатуре, это единственное, что я не пробовал.   -  person dan    schedule 04.12.2014
comment
Вы используете несколько мониторов? Кроме того, не могли бы вы опубликовать код?   -  person peege    schedule 04.12.2014
comment
У меня 2 монитора, да. Обычно Excel даже не сворачивается. Но MsgBox просто попадает под него, если я что-то делаю на другом экране (кажется, как только Excel теряет фокус).   -  person dan    schedule 04.12.2014
comment
У меня нет пользовательских форм. Я запускаю свои макросы с кнопок, которые я добавил на панели инструментов. Я использую встроенную функцию MsgBox для отображения моего сообщения в конце вычисления.   -  person dan    schedule 04.12.2014
comment
Нажатие Enter работает, даже если MsgBox находится в фоновом режиме (а MsgBox по умолчанию имеет только кнопку «ОК»), поэтому мне не нужно убивать весь процесс. Все еще раздражает, так как я планирую поделиться приложением.   -  person dan    schedule 04.12.2014
comment
Ознакомьтесь также с этой ссылкой, страницей ChipPearson о функциях ожидания.   -  person peege    schedule 04.12.2014
comment
Можете ли вы определить, какое поведение вызывает это? Сворачивает ли пользователь Excel, пока макрос выполняет свои вычисления и т. д.?   -  person David Zemens    schedule 04.12.2014
comment
Кроме того, необходимо ли окно сообщения? Вы можете использовать другой метод уведомления. Форма пользователя, уведомление по электронной почте, строка состояния и т. д.   -  person David Zemens    schedule 04.12.2014
comment
Пользовательская форма, вероятно, могла бы выполнить свою работу. VBA фактически генерирует ~ 60 документов PDF, сохраняя документ Excel в формате PDF и изменяя некоторые значения между каждым сохранением. Неважно, свернут ли Excel или нет, если я нажму Google Chrome на другом экране, MsgBox будет отображаться в фоновом режиме. Так что, как только он теряет фокус. Но я могу щелкнуть Excel и нажать Enter (зная, что MsgBox имеет фокус, но находится в фоновом режиме).   -  person dan    schedule 04.12.2014
comment
AppActivate Application.Caption работает лучше?   -  person Rory    schedule 05.12.2014
comment
@Rory Проверено, но все равно не работает.   -  person dan    schedule 08.12.2014


Ответы (5)


Это будет работать в Excel независимо от того, какое другое приложение имеет фокус:

Перед окном сообщения или любым предупреждением поместите следующий код:

AppActivate Application.Caption
DoEvents

Поверьте мне, это потрясающе!

person DeerSpotter    schedule 16.11.2015
comment
Мне нужно будет попробовать. Я поставил это непосредственно перед отображением MsgBox? Проблема 1 год назад (!) заключалась в том, что MsgBox отображался в фоновом режиме, а пользователь этого не замечал. Так как MsgBox в Excel/Access не создает задачу в Windows, не было никакого способа получить фокус на MsgBox, единственным обходным путем было уничтожение приложения или нажатие Enter после фокуса приложения (хотя MsgBox все еще позади, он почему-то есть фокус через основное приложение (Excel)). - person dan; 26.11.2015
comment
это не работает, если Excel находится на том же мониторе, что и самое последнее активированное приложение. Я посмотрю на это больше ... - person Doug Coats; 21.01.2016
comment
@DougCoats, можете ли вы объяснить больше? это единственное, что помогло мне добавить фокус на msgbox - person DeerSpotter; 05.02.2016
comment
Хорошо, чтобы объяснить больше: у меня есть Excel, открытый на мониторе справа, и интернет-страница, которая открывается прямо перед окном сообщения, находится на том же мониторе. Когда это происходит, окно сообщения не устанавливает фокус. Если у меня есть excel на другом мониторе, он работает. - person Doug Coats; 05.02.2016
comment
@DougCoats, я думаю, что то, что у меня есть выше, наиболее близко к любому решению, которое я когда-либо видел. Я поиграюсь с этим еще и посмотрю, что я могу придумать. Если у кого-то, кто читает это, есть решение, пожалуйста, выскажитесь. - person DeerSpotter; 14.04.2016
comment
У меня это работает, даже если я открываю третье приложение/окно на том же мониторе перед AppActivate+msgbox. - person 6diegodiego9; 08.11.2019
comment
Не уверен, что это полезно, но я бы также добавил атрибут vbMsgBoxSetForeground в msgbox: msgbox foobar, vbMsgBoxSetForeground - person 6diegodiego9; 08.11.2019
comment
@ 6diegodiego9 6diegodiego9 foobar несовместим, я пробовал, и это не всегда работает. - person DeerSpotter; 11.11.2019

Я провел небольшое тестирование и нашел для вас потенциальную работу.

Я установил эту простую процедуру, чтобы проверить вашу ситуацию:

Sub test()
    If Application.Wait(Now + TimeValue("0:00:10")) Then
        MsgBox "Time expired"
    End If
End Sub

Я запускаю это, затем сворачиваю все окна, и когда таймер истекает, ничего не происходит. Если я переключаюсь на Excel, я вижу окно сообщения, но ничего другого.

Итак, я попробовал это:

Sub test()
    If Application.Wait(Now + TimeValue("0:00:10")) Then
        ThisWorkbook.Activate
        MsgBox "Time expired"
    End If
End Sub

На этот раз, когда я запускаю процедуру, затем сворачиваю все окна, вместо того, чтобы ничего не видеть, открывается всплывающее окно сообщения (но не окно Excel).

Я думаю, что если добавить ThisWorkbook.Activate прямо перед кодом MsgBox, то же самое произойдет и в вашем файле.

Это не совсем то, что вам нужно, но, надеюсь, лучше, чем то, где вы находитесь.

person guitarthrower    schedule 04.12.2014
comment
Я попробовал ThisWorkbook.Activate, но, к сожалению, это не помогает. Я не уверен, что именно не так, но я попробовал с другим компьютером, и у меня возникла та же проблема. Но все наши компьютеры в нашей компании практически идентичны как по аппаратному, так и по программному обеспечению. Я, вероятно, выберу совершенно альтернативное решение, например, открытие папки вместо MsgBox. - person dan; 04.12.2014

* к сожалению, я не прочитал остальную часть вашего вопроса, я использовал AppActivate и работал, как ожидалось, и я использую MS Office Professional Plus 2010.

Я не мог заставить работать метод Guitarthrower, но смог заставить его работать в качестве примера. Вместо ThisWorkbook.Activate попробуйте AppActivate ("Microsoft excel"), затем MsgBox

Sub test()
    If Application.Wait(Now + TimeValue("0:00:10")) Then
        AppActivate ("Microsoft excel")
        MsgBox "Time expired"
    End If
End Sub
person mrbungle    schedule 05.12.2014
comment
Тоже не работает, использую ли я AppActive или ThisWorkbook.Activate. Application.Wait, похоже, не помогает. Обратите внимание, что когда я нажимаю на Excel, я вижу, что элементы в книге постоянно мигают, ожидая ответа на MsgBox (который скрыт в фоновом режиме). Нажатие Enter работает не всегда, но обычно с комбинацией Ctrl+Break, Esc и Enter. - person dan; 08.12.2014
comment
Это странно, я запускаю это, и я могу свернуть Excel, а затем появляется только окно msgbox. Не знаю :( - person mrbungle; 08.12.2014
comment
Вероятно, это как-то связано с тем, как Excel и Windows настроены на наших рабочих компьютерах. Хотя это не совсем решает проблему, я выберу обходной путь, основанный на Forms. - person dan; 08.12.2014
comment
@dnLL: это AppActivate, а не AppActive - person 6diegodiego9; 08.11.2019

Похоже, процесс макроса делает приложение невосприимчивым. Не уверен, поможет ли это, но рассматривали ли вы возможность добавления DoEvents или Sleep (вызов API) в свой длительный процесс, чтобы вернуть управление ОС? Sleep — это вызов API, поэтому вам нужно объявить его в модуле, чтобы использовать его. DoEvents предотвращает блокировку приложения, но использует больше ЦП, поэтому, если оно находится в цикле, я буду обращаться к нему время от времени (30% или меньше итераций). Если это не цикл, и вы знаете, где находятся узкие места в вашем длительном процессе, вы можете вызывать DoEvents после каждого длительного процесса.

#If VBA7 And Win64 Then
' 64 bit Excel
Public Declare PtrSafe Sub Sleep Lib "kernel32" ( _
    ByVal dwMilliseconds As LongLong)
#Else
' 32 bit Excel
Public Declare Sub Sleep Lib "kernel32" ( _
    ByVal dwMilliseconds As Long)
#End If

Исходный код Sleep API

Затем в вашем процессе

Sub SomeLongProcessWithDoEventsExample()
   For i = 1 to 100000
       'Some lengthy code
       If i Mod 333 = 0 Then
          DoEvents
       End If
   Next i
End Sub

Sub SomeLongProcessWithSleepExample()
   For i = 1 to 100000
       'Some lengthy code
       If i Mod 333 = 0 Then
          Sleep 1 * 1000 'Millseconds
       End If
   Next i      
End Sub

Я бы предложил установить Application.ScreenUpdating = False, а затем снова включить его после завершения процесса, но это может усугубить ситуацию.

Обновить

Просто прочитайте комментарии, которые были введены при наборе моего ответа. Другим вариантом вместо окна сообщения может быть открытие окна папки, в которой файлы сохраняются после того, как все файлы были созданы (замените Environ$("APPDATA") на место сохранения):

Shell "explorer.exe" & " " & Environ$("APPDATA"), vbMaximizedFocus

ИЛИ откройте один из PDF-файлов:

Shell Environ$("COMSPEC") & " /c Start C:\SomeFile.pdf", vbMaximizedFocus

Другой вариант

Я не мог поместить это в комментарии, потому что было слишком много кода, но вместо этого сделайте вызов API к MessageBox, но не устанавливайте владельца окна сообщения (hWnd), установите его на &H0 или &O0. vbSystemModal должен вывести его на первое место. Я не знаю, позволит ли это вам выбрать окно приложения Excel после того, как пользователь нажмет «ОК»:

MessageBox &O0, "My Message", "My Caption", vbOKOnly + vbSystemModal


#If VBA7 And Win64 Then
Public Declare PtrSafe Function MessageBox _
    Lib "User32" Alias "MessageBoxA" _
       (ByVal hWnd As LongLong, _
        ByVal lpText As String, _
        ByVal lpCaption As String, _
        ByVal wType As LongLong) _
    As Long

#Else
Public Declare Function MessageBox _
    Lib "User32" Alias "MessageBoxA" _
       (ByVal hWnd As Long, _
        ByVal lpText As String, _
        ByVal lpCaption As String, _
        ByVal wType As Long) _
    As Long

#End If
person Charles Byrne    schedule 04.12.2014
comment
Если больше ничего не работает, открытие папки, безусловно, является хорошим способом обойти проблему. Спасибо, я рассмотрю ваше первое решение и другие решения в понедельник. - person dan; 04.12.2014
comment
Ни одно из решений не работает, поэтому я сейчас пытаюсь обойти это и не использую MsgBox. Открытие папки решает проблему, и это работает. Спасибо за это. Единственная проблема заключается в том, что даже с vbMaximizedFocus фокус не переходит в папку, поэтому мне нужно заглянуть в панель задач, чтобы узнать, что она действительно там. Я попробую с формами, чтобы увидеть, лучше ли это. - person dan; 08.12.2014
comment
Пользовательская форма фокусируется на Excel (оранжевый мигает на панели задач) и работает хорошо, хотя диаграммы Excel мигают в фоновом режиме. Я поиграю с опцией ScreenUpdating, чтобы увидеть, имеет ли это значение, но, вероятно, это обходной путь, которого я буду придерживаться. - person dan; 08.12.2014
comment
Похоже, ты рядом. Обычно я снова включаю обновление экрана на метке обработчика ошибок (весь код переходит на метку обработчика ошибок, и я проверяю код ошибки ‹› 0 и т. д.), или вы можете поместить его до и в обработчик ошибок, если вы выйдете раньше метка обработчика ошибок. - person Charles Byrne; 08.12.2014
comment
Да, он больше не мерцает, когда ScreeUpdating имеет значение false перед отображением формы. Мне нужно, чтобы он был включен при создании моих PDF-файлов, потому что я использую события Worksheet_Change() для редактирования положения меток диаграммы, когда они перекрываются (это происходит даже с xlLabelPositionBestFit), так что это немного усложняет. Так или иначе. Работает с формой. Это по-прежнему не устраняет первоначальную проблему MsgBox, работающего в фоновом режиме, но я думаю, что это как-то связано с действительно конкретной настройкой либо в Excel, либо в Windows на наших рабочих компьютерах. - person dan; 08.12.2014
comment
Вы можете попробовать вызвать API MessageBoxA в User32 и установить hWnd = &0. Кажется, диалог ставится сверху. У меня недостаточно места в этом комментарии, чтобы предоставить пример кода, поэтому я добавлю к своему ответу еще один вариант. Извините, это будет выглядеть так, будто я добавил туда все варианты (не в моих намерениях). - person Charles Byrne; 08.12.2014
comment
Определенно интересный подход, хотя это все еще обходной путь. Я также видел, что проблема возникает при нажатии кнопки, отображающей MsgBox (вы уверены, что хотите продолжить и т. д.). Это происходит в 100% случаев, когда приложение не имеет фокуса, но это может произойти и в противном случае (возможно, в 5% случаев), даже если оно действительно имеет фокус. Возможно, Windows тоже ведет себя странно (и убирает фокус, хотя этого не должно быть). - person dan; 08.12.2014

Я пробовал больше всего других ответов:

  • ThisWorkbook.Activate
  • AppActivate()
  • Application.Wait()
  • Sleep библиотека

По какой-то причине ничего из вышеперечисленного не работает. Я считаю, что в нашей компьютерной среде существуют какие-то действительно специфические бизнес-настройки, которые могут создавать проблему. Как уже упоминалось, все вышеперечисленные решения, вероятно, должны работать на любой новой отформатированной установке Windows 7 с любой установленной версией Office 2010, даже в настройке с двумя мониторами. Так что +1 ко всем этим ответам. Кроме того, я заметил, что проблема возникает только в некоторых конкретных книгах. Действительно странное поведение, это может быть просто ошибка Office 2010, связанная с чем-то, что я делаю в своей книге (будь то VBA или просто Excel).

При этом мое реальное решение (которое на самом деле не является решением исходной проблемы) не использует MsgBox(). Существует несколько обходных путей, и я обнаружил, что Forms — лучший для меня. Поэтому вместо того, чтобы тратить больше времени на эту проблему, я придумал действительно простой следующий код, чтобы заменить мой оригинальный MsgBox():

Application.ScreenUpdating = False
frmMsgBox.Show
Application.ScreenUpdating = True

Я могу поместить любой текст в метку в frmMsgBox, а поскольку это Excel, я могу просто передавать параметры, используя скрытые ячейки.

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

person dan    schedule 08.12.2014