Перейти на главную страничку сайта (список статей, файлы для скачивания)
Автор статьи - Александр Нагаев, г. Москва
Хочу продолжить тему о технологии 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 При любом использовании материалов сайта обязательна ссылка на него как на источник информации, а также сохранение целостности и авторства материалов.