WMI - практика применения в DelphiDelphi , ОС и Железо , WindowsWMI - практика применения в Delphi
Оформил: DeeCo Автор: Алексей Павлов Содержание: 1. Предисловие.Приветствую всех любителей Delphi! В этой статье я хочу поведать вам об одной из замечательных, с моей точки зрения, технологии, разработанной Microsoft для облегчения нашей жизни. Теперь любой программист, используя любой современный язык программирования (не исключая и скрип языков!) может с лёгкостью узнать о своём компьютере практически всё. Теперь программисты всех "вероисповеданий" могут определить, какое оборудование установлено на их компьютере, узнать информацию о материнской плате и процессоре, параметры БИОСа, какие процессы запущены в данный момент, какова температура процессора и скорость вращения кулера, какие настройки графической системы, какие.… Одним словом, все о чём вы так долго мечтали, стало доступно благодаря WMI. Звучит заманчиво, не так ли? ;)Естественно, что WMI - это не только набор параметров. А что это такое - читайте ниже. 2. Введение.Для того, чтобы не лишиться большей части потенциальных читателей на начальном этапе, скажу сразу, что применение Windows Management Instrumentation (WMI) я буду рассматривать в основном с практической точки зрения (т.е. теории в этой статье будет немного). Сразу хочу оговориться, что т.к. я собираюсь осветить вопрос практического использования технологии WMI в Delphi и намериваюсь достигнуть этой цели достаточно быстро, то я не стану разжёвывать все, с чем нам придётся сталкиваться по ходу повествования и ограничусь минимумом теории. Так что материал изначально ориентирован на тех читателей, кто уверено чувствует себя в области применения интерфейсов, технологии COM и кто умеет работать с литературой.Так же, я не стану излагать всю теорию, непосредственно связанную с технологией WMI, т.к. во-первых, это достаточно утомительно, а во-вторых, это уже сделано в лучшем виде, и заниматься перепечатыванием не имеет смысла. Итак, дальнейшее прочтение статьи и использование прилагаемых к ней примеров возможно и без понимания самой сути WMI, но, согласитесь, смысла в этом чуть. Так что очень рекомендую прочитать следующее:
Итак, начали. 3. Теория. Windows Management Instrumentation (WMI) - технология, входящая в состав ядра Windows 2000 и предоставляющая доступ с помощью интерфейсов к объектам системы. Представлю несколько упрощённую архитектуру WMI в том виде, в котором она нас будет интересовать в нашем конкретном случае. Быстро пробежимся по всем её компонентам.
На этом я закончу теоретическое вступление и перейду к практической части. 4. Практика.Подготовка. Итак, начнём с того, что нам необходимо сделать перед тем, как непосредственно начать использовать мощь технологии WMI в своих программах:
Порядок действий.
5.1. Получение данных о центральном процессоре. Этот первый пример я старался сделать как можно нагляднее и разберу я его достаточно подробно. В остальных же 3-х примерах я не буду повторяться в комментариях, а постараюсь продемонстрировать некоторые приёмы, которые, возможно, помогут вам в дальнейшем в ваших собственных программах.Создаём новый проект и добавляем к нему компоненту TSWbemLocator, которая должна появиться в палитре компонент после импортирования указанных выше библиотек типов. Далее я буду давать комментарии непосредственно в коде программы. unit Unit1; interface uses …, WbemScripting_TLB, OleServer, ActiveX; type TForm1 = class(TForm) … SWbemLocator1: TSWbemLocator; …; private { Private declarations } procedure ShowProp(SProp: SWBemProperty); public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var Service: ISWbemServices; ObjectSet: ISWbemObjectSet; SObject: ISWbemObject; PropSet: ISWbemPropertySet; SProp: ISWbemProperty; PropEnum, Enum: IEnumVariant; TempObj: OleVariant; Value: Cardinal; StrValue: string; begin // см. Примечание 1 Service := SWbemLocator1.ConnectServer('.', 'root\CIMV2', '', '', '', '', 0, nil); // см. Примечание 2 SObject := Service.Get('Win32_Processor', wbemFlagUseAmendedQualifiers, nil); // см. Примечание 3 ObjectSet := SObject.Instances_(0, nil); { Далее нам нужно из коллекции ObjectSet получить экземпляр объекта, соответствующий классу Win32_Processor. Делается это с помощью метода Item объекта ObjectSet. В качестве первого параметра этого метода указывается путь к объекту, экземпляр которого вы желаете извлечь из коллекции. Данный метод возвращает объект типа SWbemObject. Но нам не известно, как выглядит этот путь. Использовать дополнительный класс SwbemObjectPath тоже нет никакого желания. Так что делаю "финт ушами": } // SObject:= ObjectSet.Item('???', 0); Enum := (ObjectSet._NewEnum) as IEnumVariant; Enum.Next(1, TempObj, Value); SObject := IUnknown(TempObj) as SWBemObject; { Полагаю, что данный приём понятен читателю и в комментариях не нуждается. Вот практически и всё - осталось прочитать интересующие нас свойства. Сколько было слов и как всё просто оказалось в действительности :) Перебираем свойства объекта SObject: } while (PropEnum.Next(1, TempObj, Value) = S_OK) do begin SProp := IUnknown(TempObj) as SWBemProperty; StrValue := ''; ListBox1.AddItem(SProp.Name, nil); ShowProp(SProp); end; end; procedure TForm1.ShowProp(SProp: SWBemProperty); begin if (SProp.Get_Value < > null) then begin with SProp do begin if Name = 'Name' then Label2.Caption := Get_Value else if Name = 'Manufacturer' then …. end; { with } end; { if } end; end.Пояснения к коду:
Вот что у меня получилось: 5.2. Получение данных о запущенных процессах. Данный пример будет отличаться от предыдущего только тем, что я использую нехитрый приём и покажу, как вывести все свойства объекта некого класса, не зная самих имён этих свойств. Действуем так же, как и в первом примере:var Form1: TForm1; ListItem: TListItem; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var Service: ISWbemServices; ObjectSet: ISWbemObjectSet; SObject: ISWbemObject; PropSet: ISWbemPropertySet; SProp: ISWbemProperty; PropEnum, Enum: IEnumVariant; TempObj: OleVariant; Value: Cardinal; Column: TListColumn; begin ListView1.Items.BeginUpdate; ListView1.Items.Clear; Service := SWbemLocator1.ConnectServer('.', 'root\CIMV2', '', '', '', '', 0, nil); SObject := Service.Get('Win32_Process', wbemFlagUseAmendedQualifiers, nil); ObjectSet := SObject.Instances_(0, nil); Enum := (ObjectSet._NewEnum) as IEnumVariant; { На данном этапе начинаются некоторые незначительные отличия от первого примера. В предыдущем примере, мы знали, что у нас был единственный экземпляр класса Win32_Processor, характеризующий центральный процессор. В данном примере мы имеем столько экземпляров, сколько запущенных процессов, поэтому их все необходимо перебрать и получить их свойства: } // в этом цикле перебираю все имеющиеся экземпляры while (Enum.Next(1, TempObj, Value) = S_OK) do begin SObject := IUnknown(TempObj) as SWBemObject; PropSet := SObject.Properties_; PropEnum := (PropSet._NewEnum) as IEnumVariant; ListItem := ListView1.Items.Add; // перебираю свойства while (PropEnum.Next(1, TempObj, Value) = S_OK) do begin SProp := IUnknown(TempObj) as SWBemProperty; if ListView1.Items.Count = 1 then begin Column := ListView1.Columns.Add; Column.Width := 100; Column.Caption := SProp.Name; end; ShowProp(SProp); end; end; { while } ListView1.Items.EndUpdate; end; // В процедуре ShowProp происходит определение типа свойства // и соответствующие приведение типа. procedure TForm1.ShowProp(SProp: ISWbemProperty); var StrValue: string; Count: Cardinal; begin StrValue := ''; if VarIsNull(SProp.Get_Value) then StrValue := '<empty>' else case SProp.CIMType of //******************************************************************// wbemCimtypeUint8, wbemCimtypeSint8, wbemCimtypeUint16, wbemCimtypeSint16, wbemCimtypeUint32, wbemCimtypeSint32, wbemCimtypeSint64: if VarIsArray(SProp.Get_Value) then begin if VarArrayHighBound(SProp.Get_Value, 1)> 0 then for Count := 1 to VarArrayHighBound(SProp.Get_Value, 1) do StrValue := StrValue + ' ' + IntToStr(SProp.Get_Value[Count]); end else StrValue := IntToStr(SProp.Get_Value); //******************************************************************// wbemCimtypeReal32, wbemCimtypeReal64: StrValue := FloatToStr(SProp.Get_Value); //******************************************************************// … //******************************************************************// else MessageBox(0, PChar('Unknown type'), PChar(Form1.Caption), MB_OK); end; {case} if ListItem.Caption = '' then ListItem.Caption := StrValue else ListItem.SubItems.Add(StrValue); end; end.Исходный код и exe-файл данного примера вы сможете найти в прилагаемом к статье архиве в каталогах \source\ GetProcessData и \Exe-files соответственно. А выглядит это так: 5.3. Запуск приложений и выключение компьютера. В данном примере я продемонстрирую, как использовать методы, предоставляемые провайдерами.var Form1: TForm1; ListItem: TListItem; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var Service: ISWbemServices; ObjectSet: ISWbemObjectSet; SObject: ISWbemObject; PropSet: ISWbemPropertySet; SProp: ISWbemProperty; PropEnum, Enum: IEnumVariant; TempObj: OleVariant; Value: Cardinal; Column: TListColumn; begin ListView1.Items.BeginUpdate; ListView1.Items.Clear; Service := SWbemLocator1.ConnectServer('.', 'root\CIMV2', '', '', '', '', 0, nil); SObject := Service.Get('Win32_Process', wbemFlagUseAmendedQualifiers, nil); ObjectSet := SObject.Instances_(0, nil); Enum := (ObjectSet._NewEnum) as IEnumVariant; { На данном этапе начинаются некоторые незначительные отличия от первого примера. В предыдущем примере, мы знали, что у нас был единственный экземпляр класса Win32_Processor, характеризующий центральный процессор. В данном примере мы имеем столько экземпляров, сколько запущенных процессов, поэтому их все необходимо перебрать и получить их свойства: } // в этом цикле перебираю все имеющиеся экземпляры while (Enum.Next(1, TempObj, Value) = S_OK) do begin SObject := IUnknown(TempObj) as SWBemObject; PropSet := SObject.Properties_; PropEnum := (PropSet._NewEnum) as IEnumVariant; ListItem := ListView1.Items.Add; // перебираю свойства while (PropEnum.Next(1, TempObj, Value) = S_OK) do begin SProp := IUnknown(TempObj) as SWBemProperty; if ListView1.Items.Count = 1 then begin Column := ListView1.Columns.Add; Column.Width := 100; Column.Caption := SProp.Name; end; ShowProp(SProp); end; end; { while } ListView1.Items.EndUpdate; end; // В процедуре ShowProp происходит определение типа свойства // и соответствующие приведение типа. procedure TForm1.ShowProp(SProp: ISWbemProperty); var StrValue: string; Count: Cardinal; begin StrValue := ''; if VarIsNull(SProp.Get_Value) then StrValue := '<empty>' else case SProp.CIMType of //******************************************************************// wbemCimtypeUint8, wbemCimtypeSint8, wbemCimtypeUint16, wbemCimtypeSint16, wbemCimtypeUint32, wbemCimtypeSint32, wbemCimtypeSint64: if VarIsArray(SProp.Get_Value) then begin if VarArrayHighBound(SProp.Get_Value, 1)> 0 then for Count := 1 to VarArrayHighBound(SProp.Get_Value, 1) do StrValue := StrValue + ' ' + IntToStr(SProp.Get_Value[Count]); end else StrValue := IntToStr(SProp.Get_Value); //******************************************************************// wbemCimtypeReal32, wbemCimtypeReal64: StrValue := FloatToStr(SProp.Get_Value); //******************************************************************// … //******************************************************************// else MessageBox(0, PChar('Unknown type'), PChar(Form1.Caption), MB_OK); end; {case} if ListItem.Caption = '' then ListItem.Caption := StrValue else ListItem.SubItems.Add(StrValue); end; end.Пояснения к коду:
5.4. Получения значений c температурных сенсоров и с установленных вентиляторов. Данный пример не содержит каких-либо новых решений или приёмов, кроме проверки на существование провайдера (вернее возможности работать с ним), осуществляющего связь между требуемым компонентом системы и программой. У меня (Chaintech 7VJL (Apogee) VIA KT333 / Athlon XP 1600+ / Windows 2000 Professional SP3) не удаётся получить свойства некоторых классов, например, Win32_Fan и Win32_TemperatureProbe. Выражается это в том, что не удаётся получить экземпляр ни одного из этих классов. Дело в том, что WMI не может получить доступ к WMI провайдеру. Но, т.к. данные классы имеются в хранилище CIM классов, то получить описание данных классов удаётся:Service := SWbemLocator1.ConnectServer('.', 'root\CIMV2', '', '', '', '', 0, nil); SObject := Service.Get('Win32_Fan', wbemFlagUseAmendedQualifiers, nil);Но при выполнении: ObjectSet := SObject.Instances_(0, nil);Метод Instances_ не возвращает требуемой коллекции экземпляров и функция Enum.Next(1, TempObj, Value) вернёт значение S_FALSE, а при попытке выполнить PropSet := SObject.Properties_; как я сделал в первом примере, вы получите отказ в доступе (Access Violation), причина понятна…. Я проверял работу данного примера и на материнской плате Gigabyte VIA KT266 с аналогичным процессором и операционной системой - результат тот же. Думаю, не нужно говорить о том, что обе материнских платы имеют соответствующие сенсоры для диагностики температурного режима и контроля вращения вентиляторов.Между тем, имеется информация о том, что данные параметры удаётся без проблем получить на материнских платах с чипсетами (chipset) фирмы Intel. Ничего по этому поводу сказать не могу - у меня в момент написания данной статьи не было возможности протестировать данный пример на компьютерах на базе чипсетов от Intel. Исходный код и exe-файл данного примера вы сможете найти в прилагаемом к статье архиве в каталогах \source\ FanAndTemperature и \Exe-files соответственно. 6. Послесловие.На этом я закончу рассказ о применении технологии WMI. Надеюсь, что теперь вы прониклись мыслью о том, что WMI крайне удобная и относительно несложная в реализации технология, которая поможет решить массу ваших проблем :) Microsoft продолжает активно развивать WMI, и в скором времени мы получим мощнейший инструмент, области применения которого очень и очень обширны. Нам, людям занимающимся программированием, как никому другому приходится год от года, месяц от месяца пополнять свой "боевой" арсенал всё новыми и новыми знаниями, приёмами, инструментами и методологиями. На мой взгляд, технология WMI может занять достойное место в вашем личном арсенале знаний.P.S. Если вам что-то не понятно в примерах или самой теории, то прежде чем отягощать кого-либо своими расспросами, откройте Platform SDK Documentation, затратьте немного своего драгоценного времени и поищите самостоятельно ответы на свои вопросы. Как показывает практика многих поколений - решение, найденное самостоятельно, стоит нескольких, полученных от кого-то. Но если всё же у вас ничего не выйдет - пишите мне, будем разбираться вместе :) Статья WMI - практика применения в Delphi раздела ОС и Железо Windows может быть полезна для разработчиков на Delphi и FreePascal. Комментарии и вопросыМатериалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.
|
||||
©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007 |