Поделиться через


«Эй, сценарист!»Исследование глубин WMI

Сценаристы Майкрософт

Cодержание

Пространства имен
Классы WMI
Свойства
Методы

Когда один из группы сценаристов был моложе, у него было два увлечения, от которых он получал огромное удовольствие. Первое — ферментированные напитки; второе — зимние туристические походы. Он подозревает, что между ними есть взаимосвязь. Его тогдашний приятель обожал предложения отправиться «в глубинку». Так вот, в этом месяце мы собираемся последовать совету старого друга. Мы намерены углубиться в дикую местность WMI (т.е. инструментария управления Windows).

К счастью, в этом случае нам не придется совершать никаких пеших прогулок, за исключением, может быть, похода к кофеварке и обратно. Вместо этого мы намерены с помощью сценариев углубиться в недра WMI.

В настоящее время «сценаристы» получили широкую известность как очень практичный народ. Мы стараемся предоставить решения реальных проблем, а не возвышенные рассуждения, оставляющие читателю в качестве самостоятельного упраженения некоторые мелкие детали — например, реальное осуществление какого-нибудь приема. Не стоит думать, что мы пытаемся примазаться к этой компании. Хотя данная статья не нацелена на решение конкретных задач системного администрирования, у нее есть некоторая практическая цель. Основная цель состоит в ознакомлении читателя с инфраструктурой WMI. Кроме этого, нам хотелось бы поделиться с вами некоторыми сценариями, удобными для данного исследования. Запускайте «Блокнот» — идем на погружение!

Пространства имен

Репозиторий WMI представляет собой базу данных и используется для хранения модели CIM (Common Information Model). Это объектно-ориентированная модель, что означает, что в нее входит набор описаний (классов WMI), представляющих то, чем может управлять WMI. Класс WMI с именем Win32_Process, например, представляет процессы. Классы WMI хранятся в разных разделах репозитория WMI. Раздел репозитория WMI известен под названием пространства имен. Если бы в дикой местности вы наткнулись на репозиторий WMI, то вероятно первое, на что вы обратили бы внимание, это на его деление на пространства имен высокого уровня. Сценарий показан на рис. 1.

Рис. 1 Отображение пространств имен

strComputer = "."
Call EnumNameSpaces("root")

Sub EnumNameSpaces(strNameSpace)
    On Error Resume Next
    WScript.Echo strNameSpace
    Set objWMIService=GetObject _
        ("winmgmts:{impersonationLevel=impersonate}\\" & _ 
            strComputer & "\" & strNameSpace)

    Set colNameSpaces = objWMIService.InstancesOf("__NAMESPACE")

    For Each objNameSpace in colNameSpaces
        Call EnumNameSpaces(strNameSpace & "\" & objNameSpace.Name)
    Next
End Sub

отображает имена всех пространств имен в репозитории WMI на компьютере, указанном в strComputer (точка означает, что это локальный компьютер).

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

Все начинается с вызова EnumNameSpaces, получившей в качестве параметра корневой уровень. В результате мы получаем пространства имен из корневого пространства имен. Затем выполняем рекурсию. Обратите внимание, что, фактически, EnumNameSpaces вызывает сама себя, передавая каждое из подпространств имен, которые она определяет. Отхлебните побольше кофе и подумайте над этим. В результате будет обработано каждое пространство имен, и, если у него есть подпространства имен, все они будут отображены.

Обратите внимание, что в начале подпрограммы присутствует оператор On Error Resume Next. Это сделано на тот случай, если сценарий запускается в контексте безопасности, не имеющем доступа ко всем пространствам имен. В таком случае сценарий все равно будет работать, хотя несколько медленнее из-за ожидания тайм-аутов.

Конечно, чтобы выполнить это, можно было бы просто использовать wbemtest.exe (имеется на каждом компьютере, на котором установлен WMI) или Scriptomatic (go.microsoft.com/fwlink/?LinkId=125976). Но с исходным сценарием и с вашими выдающимися сценарными навыками вы сможете эти пространства имен отфильтровать или вывести в таблицу Excel, или провести сравнение пространств имен с двух компьютеров.

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

Классы WMI

Помните наше замечание относительно того, что в репозитории WMI хранится модель CIM (Common Information Model)? Так вот, эта модель хранится в пространстве имен CIMV2 (V2 означает версию 2). Если вы заглянете в пространство имен CIMV2, то увидите все классы WMI, из которых состоит эта модель. Это выполняется в следующем сценарии.

strComputer = "."
Set objWMIService=GetObject("winmgmts: _
    {impersonationLevel=impersonate}!\\" & _
    strComputer & "\root\cimv2")

For Each objclass in 
    objWMIService.SubclassesOf()
    Wscript.Echo objClass.Path_.Class
Next

Разберем, как именно работает этот сценарий. Вызов функции GetObject возвращает объект SWbemServices. GetObject является функцией VBScript, возвращающей ссылки на объекты COM, которые можно вносить в сценарий. В этом случае, поскольку мы передали ей строку «winmgmts:…», GetObject возвращает объект из библиотеки сценариев WMI Scripting Library. Отметим, что строка, передаваемая функции GetObject, содержит пространство имен, к которому мы подключаемся, в данном случае \root\cimv2. Поэтому объект SwbemServices, с которым нам требуется работать, привязан к конкретному указанному нами пространству имен. Для ознакомления с возможными действиями, которые допустимы в сценарии, обратитесь к документации по SWbemServices.

Вероятно, действие ExecQuery покажется вам знакомым. Это метод, позволяющий запускать на выполнение запрос WQL к пространству имен, к которому вы подключены. Но существует также множество других действий, которые можно предпринять, имея объект SwbemServices, связанный с пространством имен.

Нам требуется список всех классов WMI, входящих в пространство имен, и эту работу выполняет функция SubClassesOf. В документации сказано, что она возвращает SWbemObjectSet. Непохоже, чтобы вам захотелось встретиться с этим один на один в лесу! Обратите особое внимание на последние три буквы — это набор. А, как известно сценаристам WMI, по набору можно пройти с помощью цикла For Each.

Теперь не слишком удивительно, что каждый член набора SWbemObjectSet является объектом SWbemObject. Каждый из этих объектов SWbemObjects представляет один из классов WMI, входящих в пространство имен CIMV2. Загляните в документацию к SWbemObject, и вы найдете все сведения, которые можно было бы вывести об этих классах.

В нашем сценарии мы решили отображать только имя класса. Для этого мы обратились к свойству Path_. Как оказалось, свойство Path_ само по себе является объектом. Это SwbemObjectPath, который, будучи объектом, имеет множество своих собственных свойств. Мы используем свойство Class, являющееся именем этого класса.

Итак, у вас имеется не только сценарий, отображающий все классы WMI из пространства имен, но имеется также сценарий, который легко обновить для отображения разных других вещей, относящихся к этим классам. Например, классы WMI могут быть расширениями других классов WMI. Представьте, что у вас имеется модель для легкового автомобиля (Win32_Car), но на самом деле вам требуется управлять автомобилями с кузовом «универсал». В модели для легкового автомобля все применимо к автомобилю с кузовом «универсал».

Но вам потребуется несколько дополнительных вещей, например логическое значение, указывающее, имеются ли в нем модные деревянные панели. Вам не захочется заново создавать весь набор функций модели Win32_Car. Что вам потребуется, так это механизм для расширения класса Win32_Car с целью включения всех новых свойств. Именно такой механизм входит в WMI.

Чтобы понять, наследует ли класс WMI свойства от другого класса WMI, можно проверить свойство Derivation_ класса SwbemObject, связанного с интересующим классом WMI. Сценарий из рис. 2 отображает классы WMI из пространства имен CIMV2 наряду со списком классов, производными которых они являются.

Рис. 2. Происхождение классов CIMV2

strComputer = "."
Set objWMIService=GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _
    strComputer & "\root\cimv2")

For Each objclass in objWMIService.SubclassesOf()
    WScript.StdOut.Write objclass.Path_.Class
    arrDerivativeClasses = objClass.Derivation_ 
    For Each strDerivativeClass in arrDerivativeClasses 
       WScript.StdOut.Write " <- " & strDerivativeClass
    Next
    WScript.StdOut.Write vbNewLine
Next

    For Each objNameSpace in colNameSpaces
        Call EnumNameSpaces(strNameSpace & "\" & objNameSpace.Name)
    Next
End Sub

Сценарий начинается точно так же, как и предыдущий. В нем используется WScript.StdOut.Write вместо WScript.Echo, чтобы избежать автоматического добавления символа новой строки после отображенной строки. (Примечание. Чтобы StdOut.Write работал, сценарий необходимо запускать на выполнение с помощью Cscript.exe, а не Wscript.exe.)

Из документации к SwbemObject видно, что у него имеется свойство Derivation_. Это свойство представляет собой массив строк, содержащих имена классов, производным от которых является текущий класс. В нашем сценарии мы проходим по массиву с помощью цикла For Each и отображаем все классы, разделяя их стрелкой в коде ascii. После того, как вы привыкли начинать с объекта SwbemServices, возвращенного функцией GetObject, и просматриваете возможности, описанные в документации по библиотеке сценариев WMI Scripting Library, можно проверить свойства и методы на этих странно названных объектах, чтобы выяснить все, что возможно.

Понимание того, с которым из объектов библиотеки WMI Scripting Library вы работаете в каждой из точек сценария, позволяет перейти на следующие уровень создания сценариев WMI. Вместо того, чтобы всего лишь пользоваться нашими сценариями, вы поймете также, почему нам удалось вызвать ExecQuery или сослаться на свойство Properties_. И вы сможете продвинуться дальше.

Средство Scriptomatic работает не так, как вы ожидали? Неважно. Двигайтесь вперед и измените его, чтобы оно работало так, как вам требуется. Может быть другие купят у вас ваше творение. В таком случае просто отправьте по электронной почте на адрес scripter@microsoft.com подробные указания относительно того, как нам получить чеки с возраграждением.

Свойства

Каждый класс WMI моделирует что-то, чем вы можете управлять с помощью набора свойств и методов. Свойства являются характеристиками вещи. Процесс, например, имеет идентификатор и приоритет и использует определенный объем памяти. Эти свойства включены в класс WMI с именем Win32_Process.

После того, как определен класс, управляющий сущностью, посмотрите на доступные свойства, чтобы понять, входит ли в модель управления то, чем вы предполагаете управлять. В класс SWbemObject входит свойство с именем Properties_. Забавно, правда? Значением этого свойства является объект SwbemPropertySet, в который входит коллекция объектов SWbemProperty. Каждый из этих объектов SWbemProperty соответствует свойству из класса WMI, связанному с SWbemObject. Я знаю — из-за всех этих имен SWbem* все выглядит страшно запутанным, но все не так уж плохо. Взгляните на рис. 3.

fig03.gif

Рис. 3 SWbemObject предоставляет свойства класса WMI, к которому он привязан (щелкните изображение для его увеличения)

Помните, что классы, начинающиеся с SWbem*, являются членами библиотеки WMI Scripting Object. Это объекты, дающие возможность работать с WMI. Они не являются частью модели WMI того, чем вы управляете.

На рис. 3 SWbemObject представляет класс WMI, Win32_SomeClass, обладающий свойствами: Property_1, Property_2 и Property_3. Он предоставляет их посредством своего собственного свойства Properties_. Конечно, если он привязан к другому классу WMI, Win32_SomeOtherClass, тогда имя его свойства не изменяется. Это по-прежнему Properties_. Но свойства класса, к которому он привязан, скорее всего, будут отличаться.

По существу, SWbemObject перенимает свойства конкретного класса WMI, к которому он привязан, но это позволяет добраться до этих различающихся свойств с помощью того же механизма Properties_. Разумно? Сделайте еще один глоток кофе и поразмышляйте над схемой. Все станет понятным.

В сценарии на рис. 4 используются преимущества SWbemObject и его свойства Properties_ для извлечения и отображения всех свойств класса Win32_Service WMI. Начало сценария должно быть вам знакомо. Основное изменение заключается в том, что мы факторизовали пространство имен и класс WMI для облегчения их изменения. Например, можно просто изменить значение strClass на Win32_BIOS, чтобы увидеть свойства этого класса, а не свойства класса Win32_Service. В цикле For Each выполняется перебор коллекции SWbemPropertySet (objClass.Properties) и отображается параметр Name каждого свойства SWbemProperty.

Рис. 4. Получение свойств класса Win32_Service

strComputer = "."
strNameSpace = "root\cimv2"
strClass = "Win32_Service"

Set objClass = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _ 
    strComputer & "\" & strNameSpace & ":" & strClass)

WScript.Echo strClass & " Class Properties"
WScript.Echo "------------------------------"

For Each objClassProperty in objClass.Properties_
    WScript.Echo objClassProperty.Name
Next

Методы

Наконец, некоторые классы WMI идут дальше в моделировании свойств и характеристик управляемой сущности и включают в себя методы, предоставляющие доступ к поведениям или действиям, которые может предпринимать сущность — или которые могут предприниматься по отношению к ней.

Формат сценария, предназначенного для возврата всех методов класса WMI (приведен на рис. 5), является точным аналогом сценария, возвращающего свойства.

Ри. 5 Получение методов класса WMI

strComputer = "."
strNameSpace = "root\cimv2"
strClass = "Win32_Service"

Set objClass = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _ 
    strComputer & "\" & strNameSpace & ":" & strClass)

WScript.Echo strClass & " Class Methods"
WScript.Echo "---------------------------"

For Each objClassMethod in objClass.Methods_
    WScript.Echo objClassMethod.Name
Next

Отличие, естественно, заключается в том, что вместо свойства Properties_ используется свойство Methods_. Теперь интересно выяснить, удастся ли отобразить типы параметров, принимаемых этими методами? Как можно было бы отобразить только те классы WMI, у которых действительно есть методы? Это те вопросы, для получения ответа на которые вам следовало бы написать сценарии по окончании чтения данной статьи.

Надеемся, что мы предоставили полезный начальный обзор способов исследования глубин WMI. Вам необходимо проложить свой собственный путь через густой лес объектов SWbem*s. А сценарии предоставляют приятный и удобный механизм для продвижения вглубь. Двум друзьям, любителям зимних турпоходов, повезло куда меньше. Фактически, они никогда не забредали далеко вглубь. Оказалось, что посреди зимы не очень легко доставить достаточное количество необходимых ферментированных напитков вглубь дикой местности.

Сценаристы работают... ну хорошо, получают зарплату в корпорации Майкрософт. Когда они не играют в бейсбол, не тренируют бейсбольную команду, не смотрят бейсбол и не занимаются другими делами, они заведуют центром сценариев TechNet. Проверьте это по адресу www.scriptingguys.com.