Перейти на главную страничку сайта (список статей, файлы для скачивания)

ФОРУМ (здесь можно обсудить эту статью, а также любые проблемы программирования на различных макроязыках и в скриптовых средах)

Рецепты Windows Scripting: мониторинг запуска процессов

Автор статьи - Александр Нагаев, г. Москва


Хочу продолжить тему о технологии WMI, рассмотрев подписку на события в асинхронном режиме в web-приложении, а также попутно показать динамическое изменение окна web-приложения.

При помощи объекта SWbemLocator в сценарии можно подключаться к удалённому или локальному компьютеру с помощью метода ConnectServer(). При использовании асинхронного режима можно выполнять обработку событий, получаемых от объекта SWbemSink, не заставляя сценарии ждать.

При подключении к локальному компьютеру соединение можно устанавливать только под текущей учётной записью. Такое ограничение сделано с точки зрения безопасности.

Это всё просто в сценариях, но в качестве примера сделаем web-приложение (hta). В приложении можно будет видеть появление новых процессов и, при желании, либо завершать их, либо не давать запускаться, пока работает наше приложение.

Само приложение вы можете скачать здесь (3 424 байт). Дальнейшее содержание статьи является описанием именно этого приложения.

Кроме всего прочего, будем использовать не только стандартные возможности HTML, но и возможности COM-объектов. В качестве такого COM будем использовать (для отображения процессов) виндовый элемент ListView, его свойства и методы.

Логика приложения заключается в том, чтобы минимальными усилиями сделать графический интерфейс и выполнять отслеживание новых процессов, которые будут видны в динамике в окне приложения. Для полноценного использования приложения нужно иметь возможность соединяться с испытуемыми системами не только под текущей учётной записью, но и под альтернативной. Это позволяет сделать метод ConnectServer("хост", "пространство_ имён", "имя_пользователя", "пароль", "", "", "", ""). Остальные четыре параметра для нашего случая не являются обязательными.

Итак, нужно создать web-приложение с пользовательским интерфейсом. Создадим обыкновенный HTML-документ по стандартному образцу, добавив в тег <HEAD> тег, объявляющий этот документ приложением:

<hta:application id="hta" applicationname="KillProc"
contextmenu="yes"
innerborder="no"
maximizebutton="yes"
minimizebutton="yes"
navigable="yes"
scroll="no"
scrollflat="yes"
selection="no"
showintaskbar="yes"
singleinstance="no"
sysmenu="yes"
version="1.0"
windowstate="normal"
>

Здесь же подгрузим в документ объект SWbemSink, присвоив ему идентификатор.

В этой статье я не буду рассматривать свойства web-приложения, но о них можно узнать в следующем месте: http://windowssdk.msdn.microsoft.com/en-us/library/ms536481.aspx. Или здесь (по-русски).

Далее определяем некоторые стили для красоты отображения (это сделано на мой вкус, кому-то может и не понравиться):

Body{
    background-color: buttonface;
    font-family: Arial Narrow;
    cursor: arrow;
}
Table{
    font-size:13px;
}
#ListView1{
    font-size: 11px;
}
#scroll{
    width: 250;
    overflow: hidden;
}

Некоторые стили будут в процессе работы программы определённым образом переопределяться, чтобы добиться эффекта динамического изменения документа.

Теперь немного о элементе ListView. Полный список свойств, методов и событий можно узнать либо в MSDN, либо используя, например, Visual Studio, или маленькую программку от мягких: Мicrosoft ActiveX Control Pad.

В процессе загрузки страницы (метод Window_OnLoad) добавим колонки к элементу ListView:

Sub Window_Onload
    ListView1.font = "arial"        'Тип текста
    ListView1.View = 3              'Тип отображения
    ListView1.GridLines = True      'Видимость сетки
    ListView1.ColumnHeaders.Add , , "Имя процесса", 100 'Колонки
    ListView1.ColumnHeaders.Add , , "PID", 50
    ListView1.ColumnHeaders.Add , , "Время создания", 100
    ListView1.ColumnHeaders.Add , , "Приоритет", 70
    ListView1.ColumnHeaders.Add , , "Путь к испоняемому файлу", 250
    ListView1.ColumnHeaders.Add , , "Владелец", 140
    self.Focus()                    'Передача фокуса окну приложения
    self.MoveTo 65,30               'Перемещение левого вехнего угла в x, y
    self.ResizeTo 755,550 'Размер по ширине и высоте
End Sub

Все остальные свойства ListView определяются в теге <Object> этого элемента.

Сам документ определён в теге <Body> - обыкновенный документ HTML разметки.

Кнопкам же нужно назначить события, такие как onclick, и определить процедуры.

Здесь используется процедура Connect("имя_компьютера", "логин", "пароль"):

<input type="button" ... onclick="vbscript:
call Connect(document.all.host.value, document.all.login.value, document.all.passwd.value)">

Процедура же выглядит следующим образом:

Sub Connect(ht, lg, pswd)
    On Error Resume Next 'Игнорируем ошибки
    Set wshNet = CreateObject("WScript.Network") 'Объект wshNetwork
    Set locator = CreateObject ("WbemScripting.SWbemLocator") 'Ссылка на SWbemLocator
    If isLogin.checked Then 'Проверка чекбокса
        Set service = locator.ConnectServer(ht, "root\CIMV2", lg, pswd) 'Используем лог/пар.
        If Err.Number = 0 Then
            Call Query(ht, wshNet.ComputerName) 'При отсутствии ошибок вызываем процедуру Query
        Else
            alert Err.Description               'При ошибке – сообщение о ней
        End If
    Else
        Set service = locator.ConnectServer(ht) 'Используем текущую учётн. запись
        If Err.Number = 0 Then
            Call Query(ht, wshNet.ComputerName)
        Else
            alert Err.Description
        End If
    End If
    Set wshNet = Nothing
End Sub

Процедура запроса на языке WQL – создадим временный фильтр событий для класса Win32_Process, опрос проводится каждую секунду:

Sub Query(remoteHost, localHost)
    myQuery = "SELECT * FROM InstanceCreationEvent  WITHIN 1 WHERE TargetInstance ISA 'Win32_Process'"
    service.ExecNotificationQueryAsync mysink, myQuery 'Асинхронный запрос, где
    If remoteHost = "" Then                            'mysink – идентификатор объекта SWbemSink
        strmess = localHost
    Else
        strmess = remoteHost
    End If
    message.innerHTML = "Подключено к: " & strmess
End Sub

Обработка событий объекта SWbemSink приведена далее. При отключении:

<script language="vbscript" FOR="mysink"
EVENT="OnCompleted(iHResult, objWbemErrorObject, objWbemAsyncContext)">
    document.all.host.value = ""
    document.all.Kill.value = ""
    Call ClearGrid()
    alert("Мониторинг отключен")
    message.innerHTML = "Отключено"
</script>

При запуске нового процесса:

<script language="vbscript" FOR="mysink" EVENT="OnObjectReady(obj, objAsyncContext)">
    timeCreated = obj.TargetInstance.CreationDate
    TimeCreated = Mid(timeCreated, 1, 4) & "." & Mid(timeCreated, 5, 2) & "." & _
        Mid(timeCreated, 7, 2) & "  " & Mid(timeCreated, 9, 2) & ":" & _
        Mid(timeCreated, 11, 2) & ":" & Mid(timeCreated, 13, 2)
    colProp = obj.TargetInstance.GetOwner(User,Domain)
    Call AddItem(obj.TargetInstance.Name, obj.TargetInstance.ProcessID, timeCreated, _
        obj.TargetInstance.Priority, obj.TargetInstance.ExecutablePath, Domain & "\" & User)
    If obj.TargetInstance.Name = ProcTemp Then
        obj.TargetInstance.Terminate()
    End If
</script>

Здесь используем свойства класса Win32_Process, такие, как:

И метод:

Далее нужно определить различные вспомогательные процедуры для управления и отображения в окне приложения. Вот процедура AddItem:

Sub AddItem(ProcName, ProcID, ProcTime, ProcPriority, ProcPath, ProcOwner)
    Set itmx = ListView1.ListItems.Add(, , ProcName) 'Элемент в первый столбец
    itmX.SubItems(1) = ProcID                        'В коллекцию 
    itmX.SubItems(2) = ProcTime
    itmX.SubItems(3) = ProcPriority
    If IsNull(ProcPath) Then
        itmX.SubItems(4) = ""
    Else
        itmX.SubItems(4) = ProcPath
    End If
    itmX.SubItems(5) = ProcOwner
End Sub

Здесь добавляются строки в элемент ListView при старте процесса на наблюдаемой системе.

Далее обработаем щелчок на ListView:

Sub ListView1_Click 'Обработка щелчка на строках ListView
    On Error Resume Next
    If ListView1.SelectedItem.Text <> "" Then
        document.all.Kill.value = ListView1.SelectedItem.Text 'Добавляем имя процесса
    End If 'В поле с id=Kill
End Sub

Также нужно иметь возможность сортировать столбцы при нажатии на их заголовок, а это достигается обработкой события ListView1_ColumnClick(ColumnHeader). Здесь представлен простой способ сортировки, используя свойства Sorted, SortKey и SortOrder:

Sub ListView1_ColumnClick(ColumnHeader)
    With ListView1
        .Sorted = False
        .SortKey = ColumnHeader.Index - 1
        If .SortOrder = 0 Then
            .SortOrder = 1
        Else
            .SortOrder = 0
        End If
        .Sorted = True
    End With
End Sub

Теперь для завершения процесса, при нажатии на кнопку "Завершить процесс" вызывается процедура KillOne():

Sub KillOne()
    On Error Resume Next
    If document.all.Kill.value <> "" Then
        Set colProcessList = service.ExecQuery("Select * from Win32_Process Where Name = '" & _
            document.all.Kill.value & "'")
        For Each objProcess in colProcessList 'Коллекция процесса
            ret = objProcess.Terminate()      'Завершение процесса
            If ret <> 0 Then
                alert("Невозможно завершить процесс!")
            End If
        Next
        Set colProcessList = Nothing
        document.all.Kill.value = ""
        ListView1.ListItems.Remove(ListView1.SelectedItem.Index) 'Удаление строки из элемента ListView
    Else
        alert("Не выбран процесс!")
    End If
End Sub

Теперь сделаем так, чтобы процесс завершался сразу же, как только он запускается, пока будет работать мониторинг. Для этого определим процедуру KillPermanent():

Sub KillPermanent()
    If document.all.Kill.value <> "" Then
        ProcTemp = document.all.Kill.value 'Сохраняем имя процесса в глобальную переменную
    Else                            
        alert("Не выбран процесс!")
    End If
End Sub

Можно создать динамический массив и добавлять не один, а несколько процессов, таким образом ограничивая запуск нескольких процессов. Просто при возникновении события OnObjectReady нужно будет пробегаться по массиву и сравнивать имена процессов с содержимым массива.

Для отмены асинхронных операций, ассоциированных с объектом SWbemSink, используем его метод cancel:

Sub wbemsinkCancel()
    mysink.cancel() 'mysink – идентификатор объекта
End Sub

Процедура ClearGrid() использует метод Clear() коллекции ListItems для очистки сетки:

Sub ClearGrid()
    ListView1.ListItems.Clear()
End Sub

Завершающий этап – это освобождение объектов при закрытии окна:

Sub Window_OnUnload
    mysink.cancel()
    Set service = Nothing
    Set locator = Nothing
End Sub

Перейти на главную страничку сайта (список статей, файлы для скачивания)

© 2007 http://www.script-coding.com При любом использовании материалов сайта обязательна ссылка на него как на источник информации, а также сохранение целостности и авторства материалов.