Время прочтения: 4 мин.
- в каких случаях важно запускать макрос в «фоновом» режиме и почему необходимо реагировать на действия пользователя;
- пример реализации работы «фонового» приложения (проверено на версиях c 2007);
- актуализация программного кода в приведенном примере к версиям 2013-2016.
Зачем скрывать приложение с экрана?
Приложение с выполняющимся макросом имеет смысл переводить в «фоновый» режим, если предполагается, что пользователь продолжит работать за компьютером, при этом без необходимости контролировать работу макроса: скрытое с экрана приложение не вызовет раздражение пользователя «непрорисованной» экранной областью, не потребует ресурсов для такой прорисовки, не будет подвержено воздействию пользователя на формирующуюся книгу (например, активацией объектов книги, использованием буфера памяти приложения).
Как реализовать?
Объектная модель Excel позволяет работать с несколькими книгами (объектами Workbook) в одном приложении (в объекте Application). А значит, приложение, скрытое с экрана для выполнения макроса, может быть вызвано операционной системой для попытки открыть в нем указанный пользователем файл. В результате, попытка открытия файла либо будет безрезультатной, либо пользователю придется ожидать окончания выполнения скрытого макроса для возможности приступить к работе с файлом.
- Чтобы скрытое приложение могло реагировать должным образом на загрузку пользовательского файла, необходимо указать порядок действий в событиях приложения (события объекта Application) – реализую их в отдельном классе.
ExAppClass (class module)
'Переменная уровня приложения
Private WithEvents ExApp As Excel.Application
'Указывает объект для управления
Public Function SetExApp(ByVal NewExApp As Excel.Application) As Excel.Application
Set SetExApp = Nothing
If (NewExApp Is Nothing) Then
Exit Function
End If
If (Not ExApp Is Nothing) Then
Call ToNothingApp
End If
Set ExApp = NewExApp
Set SetExApp = ExApp
End Function
'Возвращает ссылку на управляемый объект
Public Function GetExApp() As Excel.Application
Set GetExApp = ExApp
End Function
'Заново открывает пользовательскую книгу в новом приложении
Private Sub ExApp_WorkbookOpen(ByVal Wb As Workbook)
Dim TransApp As Excel.Application
Dim TransWbName As String
'Запоминаю
TransWbName = Wb.FullName
Wb.Close False
Set Wb = Nothing
'Открываю заново
Set TransApp = New Excel.Application
TransApp.Visible = True
TransApp.Workbooks.Open TransWbName
Set TransApp = Nothing
End Sub
'Заново создает пустую книгу в отдельном приложении
Private Sub ExApp_NewWorkbook(ByVal Wb As Workbook)
'Реализация аналогична процедуре ExApp_WorkbookOpen()
End Sub
'Заново открывает пользовательскую книгу в новом приложении с режимом просмотра
'(реализация процедуры требуется при наличие данного события в версии Excel)
Private Sub ExApp_ProtectedViewWindowOpen(ByVal Pvw As ProtectedViewWindow)
Dim TransApp As Excel.Application
Dim TransWbName As String
'Запоминаю
TransWbName = Pvw.Workbook.FullName
Pvw.Workbook.Close False
Pvw.Close
Set Pvw = Nothing
'Открываю заново
Set TransApp = New Excel.Application
TransApp.Visible = True
TransApp.ProtectedViewWindows.Open TransWbName
Set TransApp = Nothing
End Sub
'Уничтожает ссылку на приложение
Public Sub ToNothingApp()
If (Not ExApp Is Nothing) Then
ExApp.Visible = True
Set ExApp = Nothing
End If
End Sub
'Уничтожает объект, управляющий приложением
Private Sub Class_Terminate()
Call ToNothingApp
End Sub
Стоит отметить, что в подобных задачах часто реализуются возможности для управления «своим» или «чужим» запущенным приложением Excel, определяется количество допустимых для обработки книг, список допустимых к открытию книг.
- Для скрытия приложения с экрана и запуска в нем требуемого функционала, добавляю программный модуль.
ExecModule (module)
'"Захватывает" приложение и запускает искомый макрос
Public Sub Exec()
Dim clsProgApp As New ExAppClass
With clsProgApp
'Начинаю управлять «своим» приложением
Call .SetExApp(NewExApp:=ThisWorkbook.Application)
'Скрываю приложение
.GetExApp.WindowState = xlMinimized
.GetExApp.Visible = False
'Запускаю макрос с искомым функционалом
Call ResultRun(.GetExApp)
'Отображаю приложение с искомым результатом
.GetExApp.Visible = True
.GetExApp.WindowState = xlNormal
'Перестаю управлять объектом Application
Call .ToNothingApp
End With
Set clsProgApp = Nothing
End Sub
Private Function ResultRun(ByVal ManagerExApp As Excel.Application) As Boolean
'Здесь реализуется какой-либо искомый функционал...
'Продолжительный по времени функционал
'должен периодически вызывать функцию DoEvents
'для реагирования процесса на действия пользователя
End Function
Что актуализировать?
Приложение Excel, начиная с версии 2013, позволяет пользователю работать с книгами в отдельных окнах (реализуя Single Document Interface). При этом, книги фактически, как и в прежних версиях, могут быть загружены в коллекцию одного приложения. Потому приведенный пример программного кода в целом остается работоспособным за исключением обнаруженных на практике «специфического поведения» приложения.
При попытке закрыть вызванный пользователем файл, происходит обращение к скрытому приложению, в котором он в данный момент загружен. Важно временно отобразить скрытое приложение, перед закрытием файла. Иначе в памяти остаются «висеть» ссылки на объекты закрываемого файла. Приведу пример исправления события.
ExAppClass (class module)
'(Для интерфейса SDI) заново открывает пользовательскую книгу в новом приложении
Private Sub ExApp_WorkbookOpen(ByVal Wb As Workbook)
Dim TransApp As Excel.Application
Dim TransWbName As String
Dim AppVersion As Integer
Dim SDIversion As Integer: SDIversion = 15
With ExApp
AppVersion = Fix(Replace(.Version, ".", ","))
If (AppVersion >= SDIversion) Then
'Временно отображаю скрытое приложение
.Visible = True
End If
'Запоминаю
TransWbName = Wb.FullName
Wb.Close False
Set Wb = Nothing
If (AppVersion >= SDIversion) Then
'Скрываю приложение
.Visible = False
End If
End With
'Открываю заново
Set TransApp = New Excel.Application
TransApp.Visible = True
TransApp.Workbooks.Open TransWbName
Set TransApp = Nothing
End Sub