Начнем с закуски - уведомления на e-mail.
Напишем код класса по имени MailNotification. Для работы с электронной почтой используем набор библиотек CDO(Collaboration Data Objects).
'для примера используем сервер mail.ru
Const Login = "mymyail@mail.ru"
Const PW = "mypassword"
Const SMTPSrv = "smtp.mail.ru"
Set objMail = New MailNotification
'можно изменить порт, аутентификацию, таймаут подключения,
'кодировку и использовать SSL
'With objMail
' .Port = 465 'gmail
' .UseSSL = True
'End With
'можно добавить несколько вложений - вместо Nothing - какие-нибудь логи например
'arrLogs = Array("c:\temp\1.log","c:\temp\2.log")
If objMail.Send(SMTPSrv, Login, PW, Login, "Заголовок", "Содержание", Nothing) Then
MsgBox "Сообщение отправлено", vbInformation
Else
MsgBox "Не удалось отправить сообщение", vbCritical
End If
Set objMail = Nothing
Class MailNotification
Private m_Msg, m_Conf
Private m_SMTPPort, m_SMTPAuth, m_SMTPUseSSL, m_SMTPTimeout, m_Charset
Private Sub Class_Initialize()
Set m_Msg = CreateObject("CDO.Message")
Set m_Conf = CreateObject("CDO.Configuration")
'значения по умолчанию
m_SMTPPort = 25 'порт
m_SMTPAuth = 1 'базовая аутентификация
m_SMTPUseSSL = False 'не использовать SSL
m_SMTPTimeout = 60 'таймаут подключения
m_Charset = "windows-1251" 'кодировка
End Sub
Private Sub Class_Terminate()
Set m_Msg = Nothing
Set m_Conf = Nothing
End Sub
Public Property Let Port(i)
m_SMTPPort = i
End Property
Public Property Let Auth(i)
m_SMTPAuth = i
End Property
Public Property Let UseSSL(b)
m_SMTPUseSSL = b
End Property
Public Property Let Timeout(i)
m_SMTPTimeout = i
End Property
Public Property Let Charset(s)
m_Charset = s
End Property
Public Function Send(sSMTPSrv, sLogin, sPW, sTo, sSubject, sBody, arrAttachment)
On Error Resume Next
With m_Conf.Fields
'значение 1, которое используется по умолчанию – использовать каталог Pickup
.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
'1 - базовая аутентификация, 0 – без аутентификации (анонимно), 2 – аутентификация NTLM
.Item("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = m_SMTPAuth
.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = sSMTPSrv
.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = m_SMTPPort
.Item("http://schemas.microsoft.com/cdo/configuration/sendusername") = sLogin
.Item("http://schemas.microsoft.com/cdo/configuration/sendpassword") = sPW
'использовать ssl
.Item("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = m_SMTPUseSSL
'таймаут
.Item("http://schemas.microsoft.com/cdo/configuration/smtpconnectiontimeout") = m_SMTPTimeout
.Update
End With
With m_Msg
.Configuration = m_Conf
.From = sLogin
.To = sTo
.Subject = sSubject
.TextBody = sBody
.Bodypart.Charset = m_Charset ' выставляем кодировку
If IsArray(arrAttachment) Then
For i = 0 To UBound(arrAttachment)
.AddAttachment arrAttachment(i)
Next
End If
.Send
End With
If Err.Number = 0 Then Send = True
End Function
End Class
Таким образом мы можем не только отправить сообщение о наступлении события, например падения целевого сервиса, но и прикрепить к сообщению какие-нибудь логи, содержащие более детальную информацию.Приступим к приготовлению основного блюда - SMS.
Писать код для работы с каждым sms-оператором - не айс :). Кроме того все sms-операторы для отправки sms через их сервисы используют капчу, что вполне объяснимо.
"Что же делать?", - спросите Вы меня. Ну-ка, спросите меня :).
Предлагаю использовать сервис Google Календарь, который позволяет создавать события и получать уведомления об их наступлении в виде сообщений электронной почты или SMS, для чего необходимо добавить номер мобильного телефона в настройки календаря.
На текущий момент поддерживаются следующие sms-операторы: Билайн, Мегафон, МТС и Скай Линк.
Авторизацию и общение с календарем реализуем с помошью объекта XMLHttpRequest.
Напишем код класса по имени SMSNotification и используем его единственный метод SendMessage по назначению.
Const Login = "mymail@gmail.com"
Const PW = "mypassword"
Set objSMS = New SMSNotification
If objSMS.SendMessage(Login, PW, "Заголовок", "Содержание", "Место") Then
MsgBox "Сообщение отправлено", vbInformation
Else
MsgBox "Не удалось отправить сообщение", vbCritical
End If
Set objSMS = Nothing
Class SMSNotification
Public Function SendMessage(sLogin, sPW, sTitle, sContent, sWhere)
'проверяем службу времени
If Not W32TimeCheck Then Exit Function
'синхронизируем время
If Not TimeSinc Then Exit Function
Dim XMLHttp 'XMLHttpRequest
Dim sAuthTokens
Set XMLHttp = CreateObject("Microsoft.XMLHTTP")
With XMLHttp
'авторизуемся
.Open "POST", "https://www.google.com/accounts/ClientLogin", False
.SetRequestHeader "Content-Type", "application/x-www-form-urlencoded"
.Send "Email=" & sLogin & "&Passwd=" & sPW & "&service=cl&source=da440dil-GSMS-1.0"
'получаем строку аутентификации
sAuthTokens = Right(.responseText, Len(.responseText)-InStr(.responseText, "Auth=")-4)
If .Status <> 200 Then Exit Function 'ошибка
'работаем с календарем
.Open "POST", "http://www.google.com/calendar/feeds/default/private/full", False
.SetRequestHeader "Content-Type", "application/atom+xml"
.SetRequestHeader "X-If-No-Redirect", "True"
.SetRequestHeader "Authorization", "GoogleLogin auth=" & sAuthTokens
.Send "<?xml version='1.0' ?><entry xmlns='http://www.w3.org/2005/Atom' " & _
"xmlns:gd='http://schemas.google.com/g/2005'>" & _
"<title type='text'>" & sTitle & "</title>" & _
"<content type='text'>" & sContent & "</content>" & _
"<gd:eventStatus value='http://schemas.google.com/g/2005#event.confirmed'>" & _
"</gd:eventStatus>" & _
"<gd:where valueString='" & sWhere & "'></gd:where>" & _
"<gd:when startTime='" & MakeDateTime() & _
"' endTime='" & MakeDateTime() & "'>" & _
"<gd:reminder minutes='1' method='sms' /></gd:when></entry>"
If .Status <> 201 Then Exit Function 'ошибка
End With
Set XMLHttp = Nothing
SendMessage = True
End Function
'собираем время в необходимом формате
Private Function MakeDateTime()
MakeDateTime = Year(Date()) & "-" & Right(0 & Month(Date()),2) & "-" & _
Right(0 & Day(Date),2) & "T" & Right(0 & DateAdd("s",60,Time()),8)
End Function
'запуск службы времени
Private Function W32TimeCheck()
Set objShellApp = CreateObject("Shell.Application")
With objShellApp
If Not .IsServiceRunning("W32Time") Then
If .ServiceStart("W32Time", True) = 0 Then Exit Function
End If
'вместо WScript.Sleep, т.к. вероятно Shell.Application выполняет ServiceStart асинхронно
W32TimeCheck = .IsServiceRunning("W32Time")
End With
Set objShellApp = Nothing
End Function
'синхронизация времени с временем интернета
Private Function TimeSinc()
Set wshShell = CreateObject("WScript.Shell")
With wshShell
If .Run("w32tm /config /syncfromflags:manual /manualpeerlist:" & Chr(34) & _
"time.windows.com time.nist.gov, time-nw.nist.gov, time-a.nist.gov, time-a.nist.gov" & Chr(34) _
,0,True) <> 0 Then Exit Function
If .Run("w32tm /config /update",0,True) <> 0 Then Exit Function
If .Run("w32Tm /resync /rediscover",0,True) <> 0 Then Exit Function
End With
Set wshShell = Nothing
TimeSinc = True
End Function
End Class
С помощью календаря Google в случае необходимости можно даже организовать sms-рассылку, но здесь не об этом :).Переходим к десерту - постоянной подписке на события WMI.
В предыдущей статье я изложил свой взгляд на временную подписку WMI, способы преодоления ее недостатков, и в заключении намекнул о существовании более "неубиваемых" способов мониторинга событий, под которыми подразумевал именно постоянную подписку.
В рамках настоящей статьи мы не сможем осветить эту тему подробно, поэтому сразу перейдем к стандартному потребителю событий CommandLineEventConsumer.
Для использования нашего потребителя необходимо скомпилировать файл wbemcons.mof, проживаюший по адресу %SystemRoot%\system32\Wbem\, в пространство имен root\cimv2 репозитория WMI. В Windows XP достаточно выполнить команду "mofcomp -N:root\cimv2 %SystemRoot%\system32\Wbem\wbemcons.mof", однако в Windows 7 такой способ не прокатит (возможно в Висте тоже - не пробовал). Все дело в строке "#pragma namespace autorecover" файла wbemcons.mof (в самом начале, первая после коммента), которая запрещает компиляцию в пространство имен, отличное от пространства имен по умолчанию. Если верить MSDN, единственный способ преодолеть это ограничение - редактировать mof-файл.
Не будем париться с редактированием файла в коде преодолевая параноидальную систему безопасности Windows 7 путем изменения DACL файла, смены владельца с TrustedInstaller на админский аккаунт и т.п. Просто скопируем файл wbemcons.mof в каталог скрипта, удалим эту злополучную строку руками и скомпилируем mof-файл из нового места проживания.
With CreateObject("Scripting.FileSystemObject")
If CreateObject("WScript.Shell").Run("mofcomp -N:root\cimv2 " & Chr(34) & _
.BuildPath(.GetParentFolderName(WScript.ScriptFullName),"WBEMCons.mof") & Chr(34) _
,0,True) = 0 Then
MsgBox "Файл scrcons.mof скомпилирован", vbInformation
Else
MsgBox "Не удалось скопмилировать файл scrcons.mof", vbCritical
End If
End With
Далее напишем код создания модального окна, которое будем отображать каждый раз в момент отлова события.
Разумеется можно использовать написанные выше скрипты, но модальное окно по-моему нагляднее.
MsgBox "Вы запустили блокнот",vbInformation,"Achtung!"Назовем его Achtung.vbs.
Затем создадим подписку на событие запуска стандартного блокнота Notepad.
With CreateObject("Scripting.FileSystemObject")
If CreateSubscription("MyFilter","MyConsumer","wscript.exe " & _
.BuildPath(.GetParentFolderName(WScript.ScriptFullName),"Achtung.vbs")) Then
MsgBox "Создание подписки на событие завершено", vbInformation
Else
MsgBox "Не удалось создать подписку на событие", vbCritical
End If
End With
'функция создания интерактивной подписки на событие запуска блокнота
Function CreateSubscription(sFilterName,sConsumerName,sScriptFilePath)
On Error Resume Next
Set objWMI = GetObject("winmgmts:\\.\Root\CIMV2")
'создание фильтра события
With objWMI.Get("__EventFilter").SpawnInstance_()
.Name = sFilterName
.QueryLanguage = "WQL"
.Query = "SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process' " & _
"AND TargetInstance.Name = 'notepad.exe'"
Set objFilterPath = .Put_()
End With
'создание потребителя события
With objWMI.Get("CommandLineEventConsumer").SpawnInstance_()
.Name = sConsumerName
.CommandLineTemplate = sScriptFilePath
.RunInteractively = True
Set objConsumerPath = .Put_()
End With
'связка фильтра и потребителя
With objWMI.Get("__FilterToConsumerBinding").SpawnInstance_()
.Filter = objFilterPath
.Consumer = objConsumerPath
.Put_()
End With
Set objWMI = Nothing
If Err.Number = 0 Then
CreateSubscription = True
End If
End Function
Выполняем скрипт. Запускаем блокнот... Кушать подано... :).Теперь каждый раз при запуске блокнота мы будем видеть наше модальное окно.
"Что же делать?", - продублирую я цитату из одного популярного мультфильма по примеру одного известного режиссера :).
Пишем скрипт удаления постоянной подписки.
If DeleteSubscription("MyFilter","MyConsumer") Then
MsgBox "Удаление подписки на событие завершено", vbInformation
Else
MsgBox "Не удалось удалить подписку на событие", vbCritical
End If
'функция удаления подписки на событие
Function DeleteSubscription(sFilterName,sConsumerName)
On Error Resume Next
Set objWMI = GetObject("winmgmts:\\.\Root\CIMV2")
'удаляем фильтр
Set colFilters = objWMI.ExecQuery("SELECT * FROM __EventFilter WHERE Name='" & sFilterName & "'")
If colFilters.Count Then
For Each objFilter In colFilters
objFilter.Delete_
Next
End If
Set colFilters = Nothing
'удаляем потребителя
Set colConsumers = objWMI.ExecQuery("SELECT * FROM CommandLineEventConsumer WHERE Name='" & sConsumerName & "'")
If colConsumers.Count Then
For Each objConsumer In colConsumers
objConsumer.Delete_
Next
End If
Set colConsumers = Nothing
Set objWMI = Nothing
If Err.Number = 0 Then
DeleteSubscription = True
End If
End Function
Запускаем... Избавились.В заключение еще пару слов о постоянной подписке.
С моей точки зрения ее преимущество в том, что она не создает никаких собственных процессов, служб и пр., таким образом оставляя гораздо более меньший по сравнению с временной подпиской "угол атаки". Правда у использованного в статье потребителя событий CommandLineEventConsumer есть один небольшой, на мой взгляд, недостаток - файл, прописанный в свойстве CommandLineTemplate можно удалить.
Но существуют иные стандартные потребители событий, обладающие своими преимуществами и недостатками, которые вы сможете использовать, если покопаете тему постоянной подписки на события WMI поглубже. Более того, углубившись в подписку на события WMI основательно, вы сможете написать код своего собственного потребителя событий. Но это уже совсем другая история...

Комментариев нет:
Отправить комментарий
Комментарий будет опубликован после модерации