ИВАН КОРОБКО
Решение задач инвентаризации в сети
Опытный системный администратор сети, построенной на основе Microsoft Windows 2000 и Windows XP, использует сценарии загрузки для решения различных задач. Одной из таких задач является инвентаризация аппаратного обеспечения рабочих станций. Решение данной задачи актуально в компаниях с большим количеством рабочих станций.
Обе части реализованы с помощью стандартных средств Microsoft Windows, что делает их надежными в работе, легко внедряемыми.
WMI. Основные понятия
Решение задачи накопления и сохранения информации об аппаратной конфигурации рабочей станции обеспечивается стандартными средствами Microsoft Windows. Одним из таких средств является Microsoft Windows Management Instrument (WMI).
WMI, разработанный в 1998 году, предоставляет разработчикам программного обеспечения и администраторам сети стандартизированные способы наблюдения и управления как локальными, так и удаленными ресурсами сети.
Теоретические сведения о внутреннем устройстве WMI
Исполняемым файлом, обеспечивающим функционирование WMI, является файл winmgmt.exe, находящийся в Microsoft Windows 200x каталоге c:WINNTsystem32wbem.
Механизм работы WMI следующий:
- На первом этапе происходит подключение к службе WMI локального или удаленного компьютера: при обращении приложения к WMI запросы приложения пересылаются диспетчеру объектов CIM (Common Information Model (CIM) Object Manager). CIMOM обеспечивает первоначальное создание объектов и однообразный способ доступа к управляемым объектам. Наиболее простым способом подключения к удаленному компьютеру является вызов функции языка VBScript GetObject(). Подключение к удаленному компьютеру происходит с помощью протокола winmgmts.
- На втором этапе происходит проверка хранилища объектов. Затем запрос передается поставщику объекта. Поставщик (provider) – это интерфейс между управляемым устройством и диспетчером CIMOM. Поставщик собирает информацию об устройствах и делает их доступными для диспетчера.
В состав WMI входит несколько поставщиков:
Таблица 1
Поставщик |
Файл |
Пространство имен |
Описание |
Active Directory Provider |
dsprov.dll |
RootDirectoryLdap |
Доступ к объектам AD через WMI. |
Event Log Provider |
Ntevt.dll |
RootCimv2 |
Управление протоколами событий Windows. Например, чтение, копирование, удаление, сжатие, переименование и т. д. |
Registry Provider |
Stdprov.dll |
RootDefault |
Чтение, запись, создание и удаление ключей и значений реестра. |
SNMP Provider |
Snmpincl.dll |
RootSnmp |
Доступа к SNMP. |
WDM Provider |
Wmiprov.dll |
RootWmi |
Доступ к информации о драйверах WDM-устройств. |
Win32 Provider |
cimwin32.dll |
RootCimv2 |
Доступ к информации и компьютере, дисках, периферийных устройствах, файлах, папках, файловой системе, сетевых компонентах, операционной системе, принтерах, сервисах и т.д. |
Windows Installer Provider |
Msiprov.dll |
RootCimv2 |
Доступ к информации о установленном ПО на рабочей станции. |
- На третьем этапе, после окончания обработки запроса, поставщик пересылает результаты исходному сценарию или приложению.
Способы доступа к объектам WMI
На практике для получения доступа к объектам WMI пользуются одним из следующих шаблонов:
Шаблон 1. Получение доступа к WMI-объектам
Вариант1
strComputer=””
strNameSpace=””
strClass=””
Set objElements = GetObject( "winmgmts:{ImpersonationLevel=Impersonate}!//" & strComputer & ”/ ” & strNameSpace & ”: ” & strClass)
For each Element in objElements
Temp=Element.Value
Next
Вариант2
strComputer=””
strNameSpace=””
strClass=””
Set objWMIService = GetObject( "winmgmts:{ImpersonationLevel=Impersonate}!//" & strComputer & ”/ ” & strNameSpace)
Set colItems = objWMIService.InstancesOf(strClass )
For Each objItem in colItems
Temp=Element.Value
Next
Вариант3
strComputer=””
strNameSpace=””
strClass=””
Set objWMIService = GetObject("winmgmts:{ImpersonationLevel=Impersonate }!// " & strComputer & ”/ ” & strNameSpace)
Set colItems = objWMIService.ExecQuery(“SELECT поле_1, поле_2, …, поле_n FROM” & strClass )
For Each objItem in colItems
Temp=Element.Value
Next
В приведенных примерах переменная strComputer содержит имя удаленного компьютера. Если компьютер локальный, то вместо имени указывается символ «.»: strComputer=”.”; Переменная strNameSpace содержит пространство имен (см. таблицу 1). strClass – переменная, содержащая название класса. Например, для Win32 Provider, одним из классов является Win32_BIOS, соответственно, переменная strNameSpace принимает значение RootCimv2. Инстркуция {ImpersonationLevel=Impersonate}! заставляет выполнить сценарий с привилегиями пользователя, вызывающего сценарий, а не с привилегиями пользователя, который в настоящий момент работает на рабочей станции. Таким образом, осуществляется подстановка, которая полезна при выполнении сценариев удаленного доступа на таких ОС, как Microsoft Windows 200x. Инструкция {ImpersonationLevel=Impersonate}! в Microsoft Windows 200x является выражением подстановки по умолчанию, поэтому для семейства Windows 200x ее можно опустить.
Варианты 1 и 2 шаблона 1 родственны и отличаются лишь формой записи. В варианте 3 обращение к классу строится с помощью SQL-запроса, что позволяет экономить трафик.
В общем случае запрос SQL выглядит следующим образом:
SELECT поле_1, поле_2, …, поле_n FROM strClass
В поле SELECT указываются поля, по которым идет выборка. Поля перечисляются через запятую, «пробелы» после запятой обязательны. Если выборка должна идти по всем полям, то вместо названий полей указывается символ «*». В поле FROM указывается один из ранее перечисленных поставщиков:
SELECT * FROM strClass
В зависимости от способа доступа к хранилищу размер пересылаемых данных разный (см. таблицу 2).
Таблица 2
Метод |
Трафик (Кбайт) |
objSWbemServices.InstanceOf(“Win32_Service”) |
157 |
objSWbemServices.ExecQuery(“Select * From Win32_Service”) |
156 |
objSWbemServices.ExecQuery(“Select field1 From Win32_Service”) |
86 |
Вывод: видно, что выгодно пользоваться шаблоном 3. Несмотря на то, что код получится несколько громоздким, уменьшится межсетевой трафик, сократится время работы сценария.
Практика использования WMI
Данная статья посвящена решению задачи инвентаризации. Как видно из таблицы 1, для решения этой задачи необходимо использовать Win32 Provider, которому соответствует пространство имен RootCimv2. Win32 Provider соответствует массив, состоящий из двух частей: идентификатора и названия ресурса, которые разделены знаком подчеркивания. Идентификатором всегда является «WIN32». С объектной моделью WMI можно ознакомиться с помощью утилиты WMI Object Browser (см. рис. 1), входящей в пакет WMI Tools. Набор утилит WMI Tools можно найти на сайте компании Microsoft: http://msdn.microsoft.com/developer/sdk/wmisdk/default.asp.
Рисунок 1. Объектная модель WMI
Таким образом, вариант 3 шаблона 1 примет следующий вид:
Шаблон 1. Обобщенный вариант 3
strComputer=””
strNameSpace=” Root\Cimv2”
strClass=”Win32_Value”
Set objWMIService = GetObject( " winmgmts: // " & strComputer & ”/ ” & strNameSpace)
Set colItems = objWMIService.ExecQuery(“SELECT поле_1, поле_2, …, поле_n FROM” & strClass )
For Each objItem in colItems
Temp=Element.Value
Next
Приведем пример получения информации о материнской плате на основе обобщенного варианта 3 рабочей станции ComputerName (VBScript). Массив данных, в котором содержится информация о материнской плате, называется WIN32_BIOS:
Пример 1. Получение информации о материнской плате рабочей станции
strComputer="."
strNameSpace="Root\Cimv2"
strClass="Win32_BIOS"
Set objWMIService = GetObject("winmgmts://"&strComputer&"/"& strNameSpace)
Set colItems = objWMIService.ExecQuery("SELECT SMBIOSBIOSVersion FROM " & strClass )
For Each objItem in colItems
MsgBox objItem.SMBIOSBIOSVersion
Next
Необходимо отметить, что переменная objItem.SMBIOS-BIOSVersion строковая (см. рис. 1). Если переменная является числом, то ее необходимо преобразовать в строковую переменную с помощью функции Сstr(). Если переменная представляет собой массив, то необходимо прочитать элементы массива и преобразовать их в строки, если элементы массива не являются строковыми переменными.
Результатом выполнения данной программы будет сообщение вида:
Рисунок 2. Информация о модели материнской платы
Порядок работы сценария загрузки
Сценарий загрузки, написанный на VBScript, работает в соответствии со следующим алгоритмом. На первой стадии работы, для идентификации рабочей станции системным администратором, сценарием загрузки определяются следующие параметры: имя рабочей станции; домен, к которому принадлежит рабочая станция; версия операционной системы, установленной на рабочей станции, учетная запись пользователя; текущая дата. На втором этапе определяются аппаратные характеристики рабочей станции с помощью WMI. На третьем этапе формируется файл отчета в формате XML. Данные, хранящиеся в XML, могут быть экспортированы для последующей обработки в одно из следующих приложений: Microsoft Excel, Microsoft Access, Microsoft SQL или обработаны любым другим приложением, предназначенным для этой цели. Об одном из таких альтернативных приложений и пойдет речь во второй части. Стоит лишь добавить, что оно также будет создано с помощью стандартных средств, предоставляемых компанией Microsoft – ASP и HTML.
Запись файла отчета на диск сервера является заключительным этапом, однако формирование файла отчета идет на протяжении всего времени выполнения сценария загрузки. Поэтому начать описание работы сценария загрузки необходимо с процесса формирования файла отчета.
Формирование файла отчета
Файл отчета представляет собой текстовый файл в кодировке Unicode с данными, записанными в формате XML. Extensible Markup Language (XML) – это язык разметки документов, созданный Microsoft, структурированного хранения информации. Полную спецификацию XML см. на сайте: http://www.w3c.org/xml.
Файл отчета формируется в течение всего времени выполнения сценария загрузки методом накопления. Определяя требуемые для отчета параметры по ходу выполнения сценария загрузки, происходит пополнение переменной. Для этого используют выражение вида: temp=temp+data, где выражение data строится по шаблону <раздел> значение . Названия разделов чувствительны к регистру. В подтверждение приведем пример, в котором в раздел «Date» XML-файла помещают текущую дату и точное время запуска сценария загрузки:
temp = temp + ”” + Date() + ”; ” + Time() + ””
В конце сценария загрузки открывается текстовый файл, в него записывается содержимое переменной temp:
Пример 2. Формирование файла отчета
Set fso = CreateObject("Scripting.FileSystemObject")
Set f = fso.OpenTextFile(FileName, 2, True)
f.Write "temp"
f.Close
Переменная FileName, фигурирующая в данном примере, состоит из двух частей – пути к папке на сервере, в которой будут храниться файлы отчетов со всех рабочих станций; имени файла. Имя файла должно совпадать с именем рабочей станции. В папку, предназначенную для хранения файлов отчетов, пользователи группы Everyone должны иметь возможность записи. Второе требование к этой папке – ее скрытость от всех пользователей. Исходя из ранее сказанного, отметим, что переменная FileName имеет следующий вид: serverfolder$PC_name.xml.
Определение параметров рабочей станции
Определяя те или иные параметры рабочей станции, многократно повторяется один и тот же программный код, претерпевающий небольшие изменения. Для сокращения программного кода рекомендуется названия WMI-классов, к которым происходит обращение, объединить в массив. Шаблон будет выглядеть следующим образом:
Шаблон 2. Обеспечение универсального доступа к нескольким классам WMI
dim strClass
strClass=Array(”Win32_Value1”, ”Win32_Value2”,… ”Win32_Value?”)
Set objWMIService = GetObject( " winmgmts: //./ Root\Cimv2 ")
for a=0 to ?
Set colItems = objWMIService.ExecQuery(“SELECT * FROM” & strClass(a) )
For Each objItem in colItems
Select case a
Case 0
b=b+1
temp=temp+"<Parametr1" + cstr(b) +">"
temp=temp+"<Parametr2" + cstr(objItem.Value1) +">"
temp=temp+"<Parametr3" + cstr(objItem.Value2) +">"
……………………………………………………
temp=temp+"<Parametrµ" + cstr(objItem.Valueµ) +">"
temp=temp+"</Parametr1" + cstr(b) +">"
Case1
c=c+1
temp=temp+"<Parametr1" + cstr(c) +">"
temp=temp+"<Parametr2" + cstr(objItem.Value1) +">"
temp=temp+"<Parametr3" + cstr(objItem.Value2) +">"
……………………………………………………
temp=temp+"<Parametr?" + cstr(objItem.Value?) +">"
temp=temp+"</Parametr1" + cstr(c) +">"
………………………………………………………………………
Case ?
y=y+1
temp=temp+"<Parametr1" + cstr(y) +">"
temp=temp+"<Parametr2" + cstr(objItem.Value1) +">"
temp=temp+"<Parametr3" + cstr(objItem.Value2) +">"
……………………………………………………
temp=temp+"<Parametr?" + cstr(objItem.Value?) +">"
temp=temp+"</Parametr1" + cstr(y) +">"
End Select
Next
Next
Set FileName=”\\server\folder”
Set fso = CreateObject("Scripting.FileSystemObject")
Set f = fso.OpenTextFile(FileName, 2, True)
f.Write temp
f.Close
Поскольку поля в разных классах разные, то использование варианта 3 шаблона 1 становится невозможным – необходимо использовать вариант 1 или 2. В шаблоне 2 строка:
Set colItems = objWMIService.ExecQuery(“SELECT * FROM” & strClass(a))
заменяется на строку:
Set colItems = objWMIService.InstancesOf(strClass(a))
Приведем конкретный пример, в котором определим некоторые данные о рабочей станции. Названия параметров и соответствующие им названия WMI-классов приведены таблице:
Таблица 3
Класс |
Параметры |
Win32_ComputerSystem |
Название рабочей станции; домен, к которому принадлежит рабочая станция; пользователь, который выполнил вход в локальную сеть. |
VBScript
|
Точная дата и время регистрации пользователя в сети. |
Win32_BIOS |
Фирма производитель BIOS и модель материнской платы. |
Win32_CDROMDrive |
Модель; описание; буква, соответствующая устройству. |
Пример 3 Создание XML-файла
dim strClass
strClass=Array("Win32_ComputerSystem", "Win32_BIOS", "Win32_CDROMDrive")
Set objWMIService = GetObject("winmgmts://./Root\Cimv2")
for a=0 to 2
Set colItems = objWMIService.InstancesOf(strClass(a))
For Each objItem in colItems
select case a
case 0
FileName="\\server\folder\" & cstr(objItem.Name) & ".xml"
temp=temp + "<Computer><PCName>" + objItem.Name + "</PCName>" + chr(10)
temp=temp + " <Domain> " + objItem.Domain + " </Domain>" + chr(10)
temp=temp + "<User> " + objItem.Username + " </User>" + chr(10)
temp = temp + "<Date>" + cstr(Date()) + "; " + cstr(Time()) + "</Date> </Computer>" + chr(10)
case 1
temp=temp + "<MB> <Version> " + objItem.Version + " </Version>" + chr(10)
temp=temp + "<Mbinfo> " + objItem.SMBIOSBIOSVersion + " </Mbinfo></MB>" + chr(10)
case 2
b=b+1
temp=temp + "<CD-ROM" + cstr(b) +"> <Model>" + objItem.Caption + " </Model>" + chr(10)
temp=temp + "<Description>" + objItem.Description + " </Description>" + chr(10)
temp=temp + "<Letter>" + objItem.Drive + " </Letter> </CD-ROM" + cstr(b) +">" + chr(10)
end select
Next
Next
Set fso = CreateObject("Scripting.FileSystemObject")
Set f = fso.OpenTextFile(FileName, 2, True)
f.Write "<inf>" & temp & "</inf>"
f.Close
Результатом данного сценария является файл в формате XML. Формат файла совпадает с названием рабочей станции (см. рис. 3).
Рисунок 3. Пример XML-файла
Полная версия сценария загрузки приведена в Приложении 1.
Обработка полученных данных
Инструмент, позволяющий обработать данные о рабочих станциях, собранные в XML-файлы, представляет собой сайт, построенный на основе ASP. Выбор ASP обусловлен его способностью работать с OLE-объектами. Инструмент представляет сайт, точкой входа в который является страница на языке HTML. Сайт создан на основе окон (Frameset): файл default.htm делит окно браузера на два столбца. Текст файла default.htm (HTML) см. в Приложении 2.
С помощью файла xml_list.asp осуществляется чтение XML-файлов в заданном каталоге, составляется список рабочих станций или пользователей в домене. Выбор списка осуществляется в HTML-файле. Вторая задача, решаемая этим файлом, – осуществление поиска по указанному значению. Поиск является сквозным, т.е. поиск происходит по всем полям всех файлов. Результатом поиска является таблица, левый столбец которой содержит в себе название рабочей станции, правый – значение параметра в контексте (см. рис. 5).
Второй ASP-файл – analyse.asp, предназначен для отображения информации по интересующей системного администратора рабочей станции.
Принцип работы файла xml_list.asp
Чтение данных из XML-файла
Процесс чтения значений параметров XML-файла, как и других структурированных файлов, состоит из нескольких этапов:
- получение списка файлов, которые необходимо обработать;
- чтение полей и занесение их в массивы;
- упорядочивание массивов по одному из полей;
- отображение полученной информации.
Рассмотрим каждый из этих этапов подробнее.
Шаг 1. Получение списка файлов в указанном каталоге
Получение списка файлов осуществляется с помощью Windows Hosting Script (WSH). WSH был предложен в 1998 году Microsoft в качестве инструмента разработки и выполнения сценариев для операционной системы Microsoft Windows. Бесспорным достоинством WSH является тот факт, что он входит в комплект поставки Microsoft Windows и поддерживает два встроенных в Microsoft Windows языка программирования – Microsoft Visual Basic Script Edition (VBScript) и Miscrosoft Java Script Edition(Jscript).
В приведенном ниже примере (VBScript), осуществляется чтение имен всех файлов, находящихся в каталоге, указанном в переменной Path, и вывод этой информации на экран:
Пример 4. Чтение названий файлов, содержащихся в каталоге
Path="\\Server\Folder"
Set a=Wscript.CreateObject("Wscript.Shell")
Set fso=CreateObject("Scripting.FileSystemObject")
Set oFolder=fso.GetFolder(a.ExpandEnvironmentStrings(Path))
Set oFiles=oFolder.Files
For each oFile in oFiles
temp=temp+oFile.Name & chr(10)
next
MsgBox temp
Шаг 2. Чтение полей и занесение их в массивы
Чтение полей XML-файла осуществляется с помощью объекта Microsoft.XMLDOM. XML-файл представляет собой структурированную базу данных. Для чтения полей XML-файла необходимо четко представлять принцип построения карты XML-файла. Рассмотрим карту, представленную на рисунке 3: она имеет 4 раздела – Computer, MB, CD-ROM1 и CD-ROM2. Видно, что в ширину XML-файл имеет четыре ячейки. Количество ячеек можно определить с помощью функции .childNodes.Lenght-1. Каждая из ячеек имеет глубину – второе измерение (см. рис. 3). Глубина одной ячейки может быть отлична от другой. В приведенном примере глубина каждой из ячеек различна. Например, глубина ячейки Computer равна четырем – PCName, Domain, User, Data. Глубина ячейки определяется функцией функции .childNodes(i). childNodes.Lenght-1, где i – номер ячейки. Название ячейки осуществляется с помощью функции .childNodes(i).child-Nodes(j).NodeName, где i – номер ячейки, j – ее глубина. Вызывая функция .childNodes(2).childNodes(0).NodeName для рис. 3, в результате получим «Model». Необходимо отметить, что нумерация и ширины поля XML и глубина ячеек начинаются с нуля. Чтение значения параметра осуществляется с помощью функции .childNodes(i).childNodes(j).Text, где i и j имеют ту же смысловую нагрузку, что и ранее.
Необходимо отметить одну особенность: при чтении значения уровня, ниже которого есть уровень, который также содержит значения, происходит логическое сложение данных нижнего уровня и присовокупление их к верхнему. Рассмотрим этот процесс на примере карты на рис. 3. Результатом чтения .childNodes(3).Text будет выражение «SONY DVD-ROM DDU1211 CD-ROM Drive F:». Именно эта способность сквозного просмотра ячейки до дна будет использована при поиске по заданному значению. Приведем пример, который иллюстрирует эту особенность:
Пример 5. Сквозное чтение ячейки XML-файла
<%@ Language=VBScript%>
<% path="xml\10000PC.xml"
set xmlVisitor=server.CreateObject("Microsoft.XMLDOM")
XMLFile=xmlVisitor.load(server.MapPath(path))
With xmlVisitor.documentElement
response.write .childNodes(3).text
End With
set xmlVisitor=Nothing %>
Зная основные принципы чтения XML-файла, приведем пример, в котором сначала происходит чтение списка из названий файлов, находящихся в каталоге; затем чтение двух полей в каждом файле – название рабочей станции и имени пользователя с последующим занесением в массив каждого из параметров. Необходимо отметить, что в приведенном примере переопределяется размер массивов, в которые в конце примера будут заноситься названия рабочих станций и имен пользователей. Переопределение размеров массивов осуществляется с помощью функции redim preserve array_name(count), где array_name – название массива, а count – новый размер массива. Полный листинг файла xml_list.asp см. в Приложении 2.
Пример 6. Формирование массивов из названий рабочих станций и учетных записей пользователей
<%@ Language=VBScript%>
<% path="\\server\folder"
Set a=CreateObject("Wscript.Shell")
Set fso=CreateObject("Scripting.FileSystemObject")
Set oFolder=fso.GetFolder(a.ExpandEnvironmentStrings(path))
Set oFiles=oFolder.Files
set xmlVisitor=server.CreateObject("Microsoft.XMLDOM")
dim array_pcname()
dim array_username()
redim preserve array_pcname(oFiles.count)
redim preserve array_username(oFiles.count)
i=0
For each oFile in oFiles
XMLFile=xmlVisitor.load(server.MapPath(path & oFile.Name))
With xmlVisitor.documentElement
UN=.childNodes(0).childNodes(2).text
array_filename(i)=oFile.Name
array_pcname(i)=.childNodes(0).childNodes(0).text
array_username(i)=right(UN,len(UN)-instr(UN,"\"))
i=i+1
End With
Next
set xmlVisitor=Nothing %>
Шаг 3. Упорядочивание массивов по одному из полей
Упорядочивание массива является классической задачей, поэтому нет необходимости углубляться в то, как это делается. В данном случае применен пузырьковый метод упорядочивания массивов. Следует отметить, что, читая XML-файлы, формируются два массива. Задача состоит в том, чтобы упорядочить один из массивов, при этом не нарушить соответствия элементов второго массива первому массив. Для достижения этой цели используется третий массив – array_sort(i), элементам которого присваиваются в зависимости от поставленного условия элементы первого или второго массивов.
Пример 7. Упорядочивание взаимосвязанных массивов
<%@ Language=VBScript%>
<% path="\\server\folder"
…………………………………
'Верхняя граница массивов определяется с помощью функции
'Ubound(). Условие сортировки массива осуществляется с
'помощью StrComp().
dim array_sort ()
redim preserve array_sort(oFiles.count)
size_array=ubound(array_sort)
…………………………………
on error resume next
for j=0 to size_array
for i=0 to size_array
if strcomp(array_sort(i),array_sort(i+1),0)=1 then
temp=array_sort(i)
array_sort(i)=array_sort(i+1)
array_sort(i+1)=temp
temp=array_pcname(i)
array_pcname(i)=array_pcname(i+1)
array_pcname(i+1)=temp
temp=array_pcname(i)
array_pcname(i)=array_pcname(i+1)
array_pcname(i+1)=temp
end if
next
next %>
Шаг 4. Отображение полученной информации
После упорядочивания массивов обработанная информация готова к отображению в браузере. В зависимости от значения параметра sort в HTML-файле, выводится либо список пользователей, либо список рабочих станций домена. Оба списка упорядочены по алфавиту. Рассмотрим механизм передачи данных из одной страницы в другую. Данные могут передаваться двумя способами: прямым и косвенным.
Прямой метод:
В исходной странице создается форма, которая содержит поля для ввода информации и кнопку для отправки информации и загрузки страницы, принимающей данные (HTML):
Пример 8. Прямой метод формирования запросов
<FORM ACTION="printer_adsi.asp" TARGET="main" METHOD="get">
<INPUT TYPE="submit" VALUE="Поиск"></INPUT>
<INPUT NAME="Search_Text"></INPUT>
</FORM>
В разделе FORM присутствуют следующие параметры: ACTION, TARGET, METHOD. В параметре ACTION указывается файл, в который будут передаваться данные после нажатия на кнопку.
Подразделом, который обязательно присутствует в разделе FORM, является INPUT. INPUT имеет следующие параметры: TYPE, NAME, VALUE. Параметр TYPE определяет вид приемника информации: кнопка (TYPE=«submit» или «reset»); окно для ввода текста (TYPE=«text»), значение по умолчанию; кнопка выбора одного параметра из группы (TYPE=«radio»). VALUE – название поля, отображаемое в объекте. NAME – имя, которое участвует в формировании запроса.
Запрос, который формируется с помощью метода GET, в общем виде выглядит следующим образом:
http://имя_страница.asp(htm)?П1=З1&П2=З2&...Пn=Зn
В приведенной строке присутствуют следующие обозначения: П – параметр, З – значение.
В используемом примере данная строка будет выглядеть следующим образом:
http://printer_adsi.asp?Search_Text=value1
если в строке было введено «value1».
Рассмотрим «страницу-приемник»: для получения переданных принимающей странице данных, необходимо прочитать запрос и присвоить переданные значения переменным. Данная операция, опираясь на приведенный пример, осуществляется следующим образом:
Set search_value= Request.QueryString(«Search_Text»)
таким образом, параметр search_value=«value1».
Косвенный метод:
Косвенный метод используется для передачи данных из одной ASP-страницы в другую. Он основан на том, что при нажатии пользователем на картинку или текст, являющийся ссылкой, сразу формируется запрос, который передает данные.
Поскольку очередью принтера невозможно управлять, если рассматривать принтер как сетевое устройство, то использование протокола LDAP становится невозможным. Для доступа к очереди принтера, как ранее отмечалось, используется протокол WINNT. Для использования протокола необходимо передавать 2 параметра: сетевое имя принтера (Share Name) и название окна (Frame), в котором необходимо вывести данную страницу. Опираясь на приведенные ранее примеры, данные будут передаваться на страницу View_Printer.asp, запрос будет иметь следующий вид (ASP):
Пример 9. Косвенный метод формирования запросов
<% <A HREF=""View_Printer.asp?Printer_to=" & printer_name & "" "target=""var"" > <IMG SRC =images\pr1.jpg BORDER=0> </A> %>
В данном проекте прямой метод используется для осуществления поиска в файлах, косвенный – для определения вида вывода информации (по названиям рабочих станций или именам пользователей).
В приведенном примере определяется значение параметра sort. Если параметр sort=1, то перед сортировкой массива происходит присвоение элементов массива array_pcname(i) массиву array_sort(i). В том случае, если sort=2, происходит присвоение элементов массива array_username(i).
Вывод информации осуществляется с помощью конструкции Response.Write «Value». Информация является гиперссылкой на второй фрейм. Название фрейма, в котором будет загружаться HTML-страница, указывается в параметре target. Название фрейма задается в HTML-странице, которая является родительским объектом для обоих ASP-страниц (см. Приложение 2, файл default.htm). В гиперссылке также формируется запрос, содержащий параметр, значением которого является UNC-путь, включающий имя XML-файла, который необходимо прочитать.
Пример 10. Формирование запросов в зависимости от значения параметра sort
<%@ Language=VBScript%>
<% path="\\server\folder"
…………………………………
set sort_var = Request.QueryString("sort")
select case sort_var
case 1
for i=0 to size_array
array_sort(i) = array_pcname(i)
next
case 2
for i=0 to size_array
array_sort(i) = array_username(i)
next
end select
…………………………………
for i=0 to size_array-1
select case sort_var
case 1
response.write "<a href=analyse.asp?FN=" & fname & array_filename(i+1) & "&Sort=" & sort_var & " \
target=""analyse"" title=""Имя пользователя - " & array_username(i) &""" a>" & array_pcname(i) &"</a><BR>"
case 2
response.write "<a href=analyse.asp?FN=" & fname & array_filename(i+1) & "&Sort=" & sort_var & " \
target=""analyse"" title=""Рабочая станция - " & array_pcname(i) &""" a>" & array_username(i) &"</a><BR>"
end select
next
…………………………………%>
Поиск данных в XML-файле
Вторая задача, решаемая файлом xml_list.asp – осуществление поиска по указанному значению. Поиск происходит по всем полям всех файлов. Результатом поиска является таблица, левый столбец которой содержит в себе название рабочей станции, правый – значение параметра в контексте. Рекурсивная передача данных в файле xml_list.asp осуществляется с помощью прямого метода, который был рассмотрен в предыдущем разделе (см. пример 8). Методика поиска основана на использовании особенности XML-файла – сквозного просмотра ячейки до дна. Более подробную информацию и наглядный пример использования этой особенности можно найти в предыдущем разделе (см. пример 5). Приведем пример, демонстрирующий реализацию процесса поиска. Параметр search_var содержит в себе искомое словосочетание:
Пример 11. Процедура поиска в XML-файлах
<%@ Language=VBScript%>
<%…………………………………
XMLFile=xmlVisitor.load(server.MapPath(fname & oFile.Name))
For each oFile in oFiles
With xmlVisitor.documentElement
For i=0 to .childNodes.length-1
if instr(Lcase(.childNodes(i).text),LCase(search_var)) then
Response.Write " <TR> <TD width=100><H6><a href=analyse.asp?FN=" & fname & ofile.name & " \
target=""analyse"">" & left(oFile.Name,len(oFile.Name)-4)& " </a> </TD><H6> \
<TD><H6>" & .childNodes(i).text & "</TD><H6></TR>"
end if
Next
End With
Next
Response.Write "</Table>"
…………………………………%>
Рисунок 4. Результат поиска
Принцип работы файла analyse.asp
Стилизация оформления страниц
Обе ASP-страницы используют таблицу стилей и работают в кодировке WIN-1251. Стили описываются в подключаемом файле style.css (см. Приложение 2). Файл стилей указывается в разделе LINK, необходимая кодировка – в разделе META (HTML):
Пример 12. Установка кодировки и чтение таблицы стилей
<HEAD>
<LINK HREF="style.css" TYPE=text/css REL=stylesheet>
<META HTTP-EQUIV ="Content-Type" CONTENT="text/html; CHARSET=windows-1251">
</HEAD>
Параметр CHARSET отвечает за выбор кодировки, в которой будет отображаться документ. После задания таблицы стилей и подключения этой таблицы в ASP или HTML-файле, можно приступить к использованию данной таблицы. Опираясь на приведенный пример таблицы стилей, в ASP- или HTML-файле открывается и закрывается соответствующий раздел – тэг, например: <H4> «Hellow» </H4>. В приведенном примере начертание слова Hellow будет выдержано в стиле <H4>, т.е. будет написано шрифтом Arial 11-го размера.
Формирование страницы отчета
Процесс формирования страницы отчета состоит из двух этапов:
- чтение переданного параметра из файла xml_list.asp, содержащего UNC-путь и название XML-файла;
- чтение параметров из XML-файла и формирование отчета; вывод результата на экран. Чтение параметра осуществляется с помощью функции Request.QueryString(), которая была ранее многократно описана. Затем указанный в запросе XML-файл открывается для чтения. Чтение файла осуществляется по описанной ранее методике.
Рисунок 5. Статистика по рабочей станции
Внедрение продукта
Готовый продукт, как отмечалось в самом начале статьи, состоит из двух частей – сценария загрузки, который обеспечивает сбор информации, и сайта на ASP, с помощью которого осуществляется анализ собранной информации о рабочих станциях домена.
Внедрение сценария загрузки
Внедрение сценария загрузки состоит из нескольких этапов:
- В свойствах каждого пользователя необходимо прописать в разделе Logon Script сценарий загрузки (см. рис. 6). Поскольку сценарий должен быть гибким, то рекомендуется создать файл Script.bat и в нем прописывать скрипты. Предлагаемое модульное решение в случае сбоя позволит быстро справиться с возникшей проблемой, при этом лишив пользователя минимального количества сервисов, которые предлагаются набором сценариев загрузки.
Рисунок 6. Сценарий загрузки
Естественно, что прописывать сценарий загрузки для каждого пользователя вручную – занятие неблагодарное, поэтому предлагается скрипт, который позволит автоматизировать данную задачу. Скрипт будет прописан всем пользователям домена, кроме тех, которые входят в группу. Название файла присваивается Logon Script (см. рис. 6). Приведенный скрипт перебирает учетные записи всех пользователей домена и проверяет членство пользователя в группе. Если пользователь не входит в группу, то в свойствах этого пользователя прописывается сценарий загрузки. Необходимо отметить, что сценарий загрузки самостоятельно определяет название домена, в котором находится пользователь, запускающий скрипт; скрипт должен быть запущен с правами администратора, т.к. изменения в Active Directory могут быть внесены только системным администратором; для увеличения скорости отработки скрипта рекомендуется запускать сценарий на контроллере домена; группа, созданная для пользователей, которым сценарий загрузки не нужен, может быть создана в любом месте Active Directory; группа должна быть глобальной; название группы и скрипта, который прописывается в свойствах пользователя, могут быть изменены в ходе выполнения скрипта. Текст скрипта см. в Приложении 1 – файл auto_script.vsb.
- Файл Script.bat (пример файла см. в Приложении 1) и VBS-файл должны находиться в каталоге Netlogon контроллера домена. Физически эта папка находится в каталоге с установленной операционной системой в подкаталоге SYSVOL\Domain\Script\.
- При загрузке рабочей станции выполняется сценарий загрузки. На время выполнения сценария загрузки необходимо скрыть CMD-панель, в которой выполняется скрипт, и приостановить загрузку рабочего стола до окончания работы всего скрипта. Необходимы результат достигается использованием групповых политик.
В разделе групповой политики «User Configuration» вам необходимо, соответственно, включить «Run legacy logon script synhronously» (Запускать сценарий загрузки синхронно) и «Run legasy script hidden» (Запускать сценарий скрыто).
Рисунок 7. Групповые политики
Установка сайта и особенности конфигурирования IIS
Для работы ASP-страницы, созданный сайт необходимо опубликовать с помощью IIS на любом компьютере домена. Желательно, чтобы этим компьютером был компьютер, который функционирует круглосуточно. Таким компьютером является сервер. Теоретически просматривать результаты могут все пользователи сети, однако это нерационально. Необходимо назначить следующие права доступа на папку, в которой опубликован сайт: группе «Domain Admins» необходимо предоставить уровень доступа «Full Control» на папку с сайтом; остальные группы и пользователи должны быть удалены (см. рис. 8). В настройке IIS для данного сайта в его свойствах необходимо, как показано на рис. 9, войти во вкладку «Authentication Methods», нажав на кнопку «Edit». Затем выключить анонимный доступ (Anonnymos access), убрав галку напротив соответствующей записи. В разделе «Authenticated access» необходимо установить галку только напротив «Basic authentication».
Желательно включить «Integrated Windows authentication» для того, чтобы системному администратору, который вошел в сеть под своей учетной записью, не предлагалось вводить пароль. Однако этот механизм доступа к сайту невозможен по неизвестным причинам. Рекомендуется не ставить эту галку.
Рисунок 8
Рисунок 9
Замечание
Компания Microsoft утверждает, что корректно работать с WMI можно только пользователю с административными привилегиями. Практика показывает, что более 90% параметров корректно определяются при работе с WMI без наличия административных привилегий и привилегий опытного пользователя. Замечено, что для пользователя, не обладающего привилегиями, возникают проблемы при определении биоса материнской платы и характеристик жесткого диска. Возникшие проблемы решаются чтением соответствующих ветвей реестра с помощью VBScript или WSH. Пользователи, имеющие привилегии опытного пользователя на локальной машине, не испытывают никаких неудобств.
Приложение 1
Sysinfo.VBS
dim strClass
strClass=Array("Win32_ComputerSystem", "Win32_BIOS", "Win32_Processor", "Win32_PhysicalMemory", \
"Win32_SoundDevice", "Win32_VideoConfiguration", "Win32_NetworkAdapter", "Win32_USBController", \
"Win32_CDROMDrive", "Win32_DiskDrive", "Win32_LogicalDisk", "Win32_DiskPartition")
Set objWMIService = GetObject("winmgmts://./Root\Cimv2")
for a=0 to 11
Set colItems = objWMIService.InstancesOf(strClass(a))
For Each objItem in colItems
select case a
case 0
FileName=objItem.Name & "_Приложение1.xml"
temp=temp + "<Computer><PCName>" + objItem.Name + "</PCName>" + chr(10)
temp=temp + " <Domain> " + objItem.Domain + " </Domain>" + chr(10)
temp=temp + "<User> " + objItem.Username + " </User>" + chr(10)
temp = temp + "<Date>" + cstr(Date()) + "; " + cstr(Time()) + "</Date> </Computer>" + chr(10)
case 1
temp=temp + "<MB> <Version> " + objItem.Version + " </Version>" + chr(10)
temp=temp + "<Mbinfo> " + objItem.SMBIOSBIOSVersion + " </Mbinfo></MB>" + chr(10)
+
case 2
temp=temp + "<CPU> <Name> " + objItem.name + " </Name>" + chr(10) ' CPU name
temp=temp + "<Description> " + objItem.Description + " </Description>" + chr(10) ' Description
temp=temp + "<Socket> " + objItem.SocketDesignation + " </Socket></CPU>" + chr(10) ' Socket type
case 3
if b=0 then
temp=temp +"<Memory>"
end if
b=b+1
temp=temp+"<Device" + cstr(b) +">"
temp=temp+" <Slot> " + objItem.DeviceLocator + " </Slot> "
temp=temp+" <Capacity> " + objItem.Capacity + "</Capacity> "
temp=temp+"</Device" + cstr(b) +">"
case 4
if c=0 then
temp=temp +"</Memory>" & chr(10) &"<Audio>"
end if
c=c+1
temp=temp+"<Device" + cstr(c) + "> "
temp=temp+"<Manufacturer> " + objItem.Manufacturer + " </Manufacturer> "
temp=temp+"<Name> " + objItem.Name + " </Name> "
temp=temp+"</Device" + cstr(c) + "> "
case 5
if d=0 then
temp=temp +"</Audio>" & chr(10) &"<Video>"
end if
d=d+1
temp=temp+"<Device" + cstr(d) + "><Type> " + objItem.AdapterType+ " </Type> "
temp=temp+"<Memory> " + cstr(objItem.AdapterRAM) + "</Memory> "
temp=temp+ "<Resolution>"+ cstr(objItem.HorizontalResolution)+ "x"+cstr(objItem.VerticalResolution)+ \
"x"+cstr(objItem.BitsPerPixel)+"@"+ cstr(objItem.RefreshRate)+"Hz</Resolution>"
temp=temp+"</Device" + cstr(d) + "> "
case 6
if e=0 then
temp=temp +"</Video>" & chr(10) &"<NetCard>"
end if
e=e+1
if objItem.MacAddress<>"" then
q=q+1
temp=temp+"<Device" + cstr(q) + ">
<Manufacturer>"+objItem.Manufacturer+"</Manufacturer>"
temp=temp+"<Name>"+objItem.Name+"</Name>"
temp=temp+"<MAC>"+objItem.MacAddress+"</MAC> </Device" + cstr(q) + "> "
end if
case 7
if g=0 then
temp=temp +"</NetCard>" & chr(10) & "<USB>"
end if
if objItem.Name<>"" then
g=g+1
temp=temp + "<NameUSB" + cstr(g) +">" + objItem.Name + "</NameUSB" + cstr(g) +"> " + chr(10) ' USB Controller
end if
case 8
if h=0 then
temp=temp +"</USB>" & chr(10) & "<CD-ROM>"
end if
if objItem.Caption<>"" then
h=h+1
temp=temp + "<Device" + cstr(h) +"> <Model>" + objItem.Caption + " </Model>" + chr(10) ' Model
temp=temp + "<Description>" + objItem.Description + " </Description>" + chr(10) ' Description
temp=temp + "<Letter>" + objItem.Drive + " </Letter> </Device" + cstr(h) +"> " + chr(10) ' Letter
end if
case 9
if i=0 then
temp=temp + "</CD-ROM>" & chr(10) & " <HDD>"
end if
i=i+1
temp=temp + "<Device" +cstr(i) +"> <Model>" + objItem.Caption + " </Model>" + chr(10) ' Model
temp=temp + "<Description>" + objItem.Description + " </Description>" + chr(10) ' Description
temp=temp + "<Size>" + cstr(objItem.Size) + " </Size>" + chr(10) ' Size of HDD
temp=temp + "<Number>" + cstr(objItem.Index) + " </Number>" + chr(10) ' Number of HDD
temp=temp + "<Partitions>" + cstr(objItem.Partitions) + " </Partitions> </Device" + cstr(i) +">" + \
chr(10) ' Number of partitions
case 10
if k=0 then
temp=temp + "</HDD>" & chr(10) & " <Media>"
end if
k=k+1
temp=temp +"<Device" +cstr(k) +"> <Letter>" + objItem.Name + " </Letter>" + chr(10) ' Letter
temp=temp + "<Description>" + cstr(objItem.Description) + " </Description>" + chr(10) ' Description
if objItem.FileSystem<>"" then
temp=temp + "<FullSize>" + objItem.Size + " </FullSize>" + chr(10) ' File System
temp=temp + "<FreeSize>" + objItem.FreeSpace + " </FreeSize>" + chr(10) ' File System
end if
temp=temp +"</Device" + cstr(k) +"> "
case 11
if l=0 then
temp=temp + "</Media> " & chr(10) & "<Partition>"
end if
l=l+1
temp=temp + "<Device" + cstr(l) +"> <Name>" + objItem.Name + " </Name>" + chr(10) ' Model
temp=temp + "<Size>" + cstr(objItem.Size) + " </Size> </Device" + cstr(l) +">" + chr(10) ' Number of partitions
end select
Next
Next
temp=temp + "</Partition>"
Set fso = CreateObject("Scripting.FileSystemObject")
Set f = fso.OpenTextFile(FileName, 2, True)
f.Write "<inf>" & temp & "</inf>"
f.Close
msgbox "end"
Script.BAT
@ECHO OFF
if c:\%os%==c:\ goto win9x
if not c:\%os%==c:\ goto winnt
:winnt
@echo Подождите! Выполняется настойка Вашего компьютера.
start /wait sysinfo.vbs
goto end
:win9x
@echo windows 9x
%0\..\Script. sysinfo.vbs
goto end
:end
@echo End Of Batch File
AutoScript.VBS
Set objNameSpace = GetObject("WinNT:")
For Each Domain in objNameSpace
strDomain Domain.Name
Next
Set Users_Domain = getobject("WinNT://" & strDomain)
Users_Domain.Filter = Array("user")
strGroup="Skip"
strGroup = InputBox("Group:", "Skip users", "Skip")
strScript="Script.bat"
strScript=InputBox("Script:","Script Name", " Script.bat ")
For each user in users_domain
i=0
a=user.name
Set user_group = GetObject("WinNT://" & strDomain & "/" & strGroup)
For each user_group in user_group.Members
b=user_group.name
if b=a then
i=1
end if
next
if i=0 then
User.LoginScript=strScript
User.setinfo
End if
Next
Приложение 2
Default.HTM
<HTML>
<HEAD>
<!-- Левый столбец составляет 30% от ширины всего окна.
Левое окно имеет идентификатор «main», второе «var». -->
<TITLE>Список доступных сетевых принтеров </TITLE>
<META HTTP-EQUIV ="Content-Type" CONTENT="text/html; CHARSET=windows-1251">
</HEAD>
<!-- Если параметр sort=1, осуществляется сортировка по
названию рабочих станций, если sort=2 – сортировка идет
по имени пользователя». -->
<FRAMESET cols="30%,*" FRAMEBORDER="5" BORDER="yes" FRAMESPACING="5" >
<FRAME SRC="xml_list.asp?sort=1&Search_Text=""" NAME="xml" SCROLLING="yes" МАRGINHEIGHT=1 >
<FRAME SRC ="about:blank" NAME="var">
</FRAMESET >
</HTML>
Stylet.CSS
H4 {font-size:11; font-family:Arial;}
H5 {font-size:10; font-family:Arial;}
H6 {font-size:9; font-family:Arial;}
Xml_List.ASP
<%@ Language=VBScript CODEPAGE=1251%>
<HEAD>
<LINK href="style.css" type=text/css rel=stylesheet>
<meta http-equiv="Content-Type" content="text/html" charset=windows-1251>
<meta http-equiv="refresh" content=10 >
</HEAD>
<BODY>
<H4><b><center>"Информация о рабочих
станциях"</center></H4> <br> <h6>
<FORM ACTION="xml_list.asp" target="xml" METHOD="get">
<INPUT TYPE="submit" VALUE="Поиск"></INPUT>
<INPUT NAME="Search_Text"></INPUT>
</FORM> </h6>
<%set search_var = Request.QueryString("Search_Text")
set sort_var = Request.QueryString("sort")
path="\\server\folder"
Set a=CreateObject("Wscript.Shell")
Set fso=CreateObject("Scripting.FileSystemObject")
Set oFolder=fso.GetFolder(a.ExpandEnvironmentStrings(path))
Set oFiles=oFolder.Files
set xmlVisitor=server.CreateObject("Microsoft.XMLDOM")
dim array_pcname()
dim array_username()
dim array_sort()
dim array_filename()
redim preserve array_pcname(oFiles.count)
redim preserve array_username(oFiles.count)
redim preserve array_sort(oFiles.count)
redim preserve array_filename(oFiles.count)
size_array=ubound(array_sort)
i=0
For each oFile in oFiles
XMLFile=xmlVisitor.load(server.MapPath(path & oFile.Name))
With xmlVisitor.documentElement
UN=.childNodes(0).childNodes(2).text
array_filename(i)=oFile.Name
array_pcname(i)=.childNodes(0).childNodes(0).text
array_username(i)=right(UN, len(UN)-instr(UN,"\"))
i=i+1
End With
next
if search_var="" then
select case sort_var
case 1
for i=0 to size_array
array_sort(i) = array_pcname(i)
next
case 2
for i=0 to size_array
array_sort(i) = array_username(i)
next
end select
on error resume next
for j=0 to size_array
for i=0 to size_array
if strcomp(array_sort(i),array_sort(i+1),0)=1 then
temp=array_sort(i)
array_sort(i)=array_sort(i+1)
array_sort(i+1)=temp
temp=array_pcname(i)
array_pcname(i)=array_pcname(i+1)
array_pcname(i+1)=temp
temp=array_pcname(i)
array_pcname(i)=array_pcname(i+1)
array_pcname(i+1)=temp
temp=array_filename(i)
array_filename(i)=array_filename(i+1)
array_filename(i+1)=temp
end if
next
next
for i=0 to size_array-1
select case sort_var
case 1
response.write "<a href=analyse.asp?FN=" & fname & array_filename(i+1) & "&Sort=" & \
sort_var & " target=""analyse"" title=""Имя пользователя - " & \
array_username(i) &""" a>" & array_pcname(i) &"</a><BR>"
case 2
response.write "<a href=analyse.asp?FN=" & fname & array_filename(i+1) & "&Sort=" & \
sort_var & " target=""analyse"" title=""Рабочая станция - " & \
array_pcname(i) &""" a>" & array_username(i) &"</a><BR>"
end select
next
else
Response.Write "<table width=""100%"", Cellpacing=""0"">"
Response.Write "<TR><TD width=150><H4> Результаты поиска по </H4></TD><TD> <H6>" & search_var & "</H6></TD></TR>"
Response.Write "<table width=""100%"", Cellpacing=""0"">"
For each oFile in oFiles
XMLFile=xmlVisitor.load(server.MapPath(path & oFile.Name))
With xmlVisitor.documentElement
for i=0 to .childNodes.length-1
if instr(Lcase(.childNodes(i).text),LCase(search_var)) then
Response.Write " <TR> <TD width=50><H6><a href=analyse.asp?FN=" & fname & \
ofile.name & " target=""analyse"">" & left(oFile.Name,len(oFile.Name)-4)& " \
</a> </TD><H6> <TD><H6>" & .childNodes(i).text & "</TD><H6></TR>"
end if
next
End With
next
Response.Write "</Table>"
end if
set xmlVisitor=Nothing %>
Analyse.ASP
<%@ Language=VBScript CODEPAGE=1251%>
<HEAD>
<LINK href="style.css" type=text/css rel=stylesheet>
<meta http-equiv="Content-Type" content="text/html" charset=windows-1251>
</HEAD>
<%set path=Request.QueryString("fn")
set xmlVisitor=server.CreateObject("Microsoft.XMLDOM")
XMLFile=xmlVisitor.load(server.MapPath(path))
With xmlVisitor.documentElement
Response.Write "<H4> Рабочая станция</H4> <table width=""100%"", Cellpacing=""0"">"
Response.Write " <TR> <TD width=300><H6> Рабочая станция </TD><H6> <TD width=300><H6>" & \
.childNodes(0).childNodes(0).text & "</TD><H6></TR>"
Response.Write " <TR> <TD width=300><H6> Пользователь </TD><H6> <TD width=300><H6>" & \
.childNodes(0).childNodes(2).text & "</TD><H6></TR>"
Response.Write " <TR> <TD width=300><H6> Домен </TD><H6> <TD width=300><H6>" & \
.childNodes(0).childNodes(1).text & "</TD><H6></TR>"
Response.Write " <TR> <TD width=300><H6> Дата регистрации в сети</TD><H6> <TD width=300><H6>" & \
.childNodes(0).childNodes(3).text & "</TD><H6></TR>"
Response.Write "</Table>"
Response.Write "<H4> Материнская плата, процессор, память</H4> <table width=""100%"", Cellpacing=""0"">"
Response.Write " <TR> <TD width=300><H6> Материнская плата </TD><H6> <TD width=300><H6>" & \
.childNodes(1).childNodes(1).text & "</TD><H6></TR>"
Response.Write " <TR> <TD width=300><H6> Модель BIOS </TD><H6> <TD width=300><H6>" & \
.childNodes(1).childNodes(0).text & "</TD><H6></TR>"
Response.Write " <TR> <TD width=300><H6> Процессор </TD><H6> <TD width=300><H6>" & \
.childNodes(2).childNodes(0).text & "</TD><H6></TR>"
Response.Write " <TR> <TD width=300><H6> Поколение </TD><H6> <TD width=300><H6>" & \
.childNodes(2).childNodes(1).text & "</TD><H6></TR>"
Response.Write " <TR> <TD width=300><H6> Разъем процессора </TD><H6> <TD width=300><H6>" & \
.childNodes(2).childNodes(2).text & "</TD><H6></TR>"
Response.Write " <TR> <TD width=300><H6> Слот и тип RAM </TD><H6> <TD width=300><H6>" & \
.childNodes(3).childNodes(0).childNodes(0).text & "</TD><H6></TR>"
Response.Write " <TR> <TD width=300><H6> Размер, Мб </TD><H6> <TD width=300><H6>" & \
.childNodes(3).childNodes(0).childNodes(1).text/(1000*1000) & "</TD><H6></TR>"
Response.Write "</Table>"
Response.Write "<H4> Жесткие диски</H4> <table width=""100%"", Cellpacing=""0"">"
q=9
for i=0 to .childNodes(q).childNodes.length-1
Response.Write " <TR> <TD width=300><H6> <i>" & .childNodes(q).childNodes(i).childNodes(1).text & \
i+1 & "</i></TD><H6><TD width=300><H6>" & .childNodes(q).childNodes(i).childNodes(0).text & \
"</TD><H6></TR>"
Response.Write " <TR> <TD width=300><H6> Размер, Гб </TD><H6> <TD width=300><H6>" & \
round(.childNodes(q).childNodes(i).childNodes(2). text/(1000*1000*1000),1) & "</TD><H6></TR>"
Response.Write " <TR> <TD width=300><H6> Логических разделов</TD><H6> <TD width=300><H6>" & \
.childNodes(q).childNodes(i).childNodes(4).text & "</TD><H6></TR>"
next
Response.Write "</Table>"
Response.Write "<H4> CD-ROM</H4> <table width=""100%"", Cellpacing=""0"">"
q=8
for i=0 to .childNodes(q).childNodes.length-1
Response.Write " <TR> <TD width=300><H6>" & .childNodes(q).childNodes(i).childNodes(1).text & \
i+1 & "</TD><H6><TD width=300><H6>" & .childNodes(q).childNodes(i).childNodes(0).text & \
"</TD><H6></TR>"
next
Response.Write "</Table>"
Response.Write "<H4> Звуковая плата</H4> <table width=""100%"", Cellpacing=""0"">"
q=4
for i=0 to .childNodes(q).childNodes.length-1
Response.Write " <TR> <TD width=300><H6> <i> Адаптер" & i+1 & "</i> </TD><H6> <TD width=300><H6></TD><H6></TR>"
Response.Write " <TR> <TD width=300><H6> Производитель </TD><H6> <TD width=300><H6>" & \
.childNodes(q).childNodes(i).childNodes(0).text & "</TD><H6></TR>"
Response.Write " <TR> <TD width=300><H6> Модель </TD><H6> <TD width=300><H6>" & \
.childNodes(q).childNodes(i).childNodes(1).text & "</TD><H6></TR>"
next
Response.Write "</Table>"
Response.Write "<H4> Графический адаптер</H4> <table width=""100%"", Cellpacing=""0"">"
q=5
for i=0 to .childNodes(q).childNodes.length-1
Response.Write " <TR> <TD width=300><H6><i> Адаптер </i>" & i+1 & "</TD><H6> <TD width=300><H6></TD><H6></TR>"
Response.Write " <TR> <TD width=300><H6> Модель </TD><H6> <TD width=300><H6>" & \
.childNodes(q).childNodes(i).childNodes(0).text & "</TD><H6></TR>"
Response.Write " <TR> <TD width=300><H6> Память, Мб </TD><H6> <TD width=300><H6>" & \
.childNodes(q).childNodes(i).childNodes(1). text/(1000*1000) & "</TD><H6></TR>"
Response.Write " <TR> <TD width=300><H6> Характеристики экрана </TD><H6> <TD width=300><H6>" & \
.childNodes(q).childNodes(i).childNodes(2).text & "</TD><H6></TR>"
next
Response.Write "</Table>"
Response.Write "<H4> Сетевой адаптер</H4> <table width=""100%"", Cellpacing=""0"">"
q=6
for i=0 to .childNodes(q).childNodes.length-1
Response.Write " <TR> <TD width=300><H6> <i> Адаптер </i>" & i+1 & "</TD><H6> <TD width=300><H6></TD><H6></TR>"
Response.Write " <TR> <TD width=300><H6> Производитель </TD><H6> <TD width=300><H6>" & \
.childNodes(q).childNodes(i).childNodes(0).text & "</TD><H6></TR>"
Response.Write " <TR> <TD width=300><H6> Модель </TD><H6> <TD width=300><H6>" & \
.childNodes(q).childNodes(i).childNodes(1).text & "</TD><H6></TR>"
Response.Write " <TR> <TD width=300><H6> МАС-адрес </TD><H6> <TD width=300><H6>" & \
.childNodes(q).childNodes(i).childNodes(2).text & "</TD><H6></TR>"
next
Response.Write "</Table>"
Response.Write "<H4> USB </H4> <table width=""100%"", Cellpacing=""0"">"
q=7
for i=0 to .childNodes(q).childNodes.length-1
Response.Write " <TR> <TD width=300><H6> Порт " & i+1 & " </TD><H6> <TD width=300><H6>" & \
.childNodes(q).childNodes(i).childNodes(0).text & "</TD><H6></TR>"
next
Response.Write "</Table>"
End With
set xmlVisitor=Nothing%>