Последние записи
- Удаление папки с файлами
- Распечатка файла
- Преобразовать массив байт в вещественное число (single)
- TChromium (CEF3), сохранение изображений
- Как в Delphi XE обнулить таймер?
- Изменить цвет шрифта TextBox на форме
- Ресайз PNG без потери прозрачности
- Вывод на печать графического файла
- Взаимодействие через командную строку
- Перенести программу из Delphi в Lazarus
Интенсив по Python: Работа с API и фреймворками 24-26 ИЮНЯ 2022. Знаете Python, но хотите расширить свои навыки?
Slurm подготовили для вас особенный продукт! Оставить заявку по ссылке - https://slurm.club/3MeqNEk
Online-курс Java с оплатой после трудоустройства. Каждый выпускник получает предложение о работе
И зарплату на 30% выше ожидаемой, подробнее на сайте академии, ссылка - ttps://clck.ru/fCrQw
23rd
Фев
WMI. Wладение Mагической Iнформацией. Часть 2
Здравствуйте, уважаемые читатели! Помните меня? А статью про WMI, где я на двух языках показывал как просто получить власть над Windows посредством… Ктулху? Не-е-е. Читаем дальше? Однозначно, сегодня мы продолжим наши изыскания…
Продолжение. Начало смотрите в предыдущем номере журнала…
Виталий Белик
by Stilet www.programmersforum.ru
Ну, да ладно. Мы же пойдем ровным путем, дающим краткое, но четкое представление о том, как будет работать система. Заранее раскрывая карты, скажу, что с Делфийском коде обращение к записям и полям таблицы будет похоже на обращение к ячейкам массива.
Двумерного разумеется. На С++
TWMIRecord *r=0;
// Инициируем итератор для списка
list<twmirecord>::iterator k;
// Пройдемся циклом по списку пока не дойдем
// до указанной по номеру записи
for(k=RecList.begin();(k!=RecList.end())&&(i>0);k++,i—);
// Если записть такая нойдена, в том смысле
// что индекс запрошенной записи
// не вылезает за пределы списка
// то вернем объект из списка
if(k!=RecList.end()&&i>=0){
r=&*k;
}
return r;
};Syhi-подсветка кода
Здесь единственный бок – я не знаю, как проверить выход за пределы списка указанного номера запрошенной записи, и так же не знаю другого способа получить по номеру из списка без прохода циклом. Поэтому я решил просто написать проход циклом по списку, пока не конец, или пока нужный номер записи в списке не достигнут. В принципе это работает, так что пусть так и остается.
Ладушки. Пора приступать к описанию второго класса, класса отвечающего за обработку записи TWMIRecord (на Делфи):
private
// Список полей и их значений
FFields:TStringList;
// Процедура получения данных из полей записи
Procedure Enum(Obj:OleVariant);
Constructor Create;
Destructor Free;
function GetItem(v: Variant): String;
public
Path:String;
// Функция возвращающая число полей
Function HighRecordIndex:Integer;
// Функция, возвращающая имя i-того поля
Function FieldName(i:integer):String;
// Свойство получающее значение определенного поля
Property Item[v:Variant]:String read GetItem; default;
end;Syhi-подсветка кода
Здесь особое внимание должно быть уделено процедуре Enum(), которая, приняв объект-запись от энумератора записей, прокатится по ее полям, выделив ее значения в свой список, и свойство Item, объявленное по умолчанию. Оно может принимать как строку – при этом получить значение по имени поля, так и число, чтоб получить значение из поля по определенному номеру. Это дает серьезную мобильность, можно в цикле пройтись по полям, а можно просто получить значение по имени поля (на С++):
// Имя поля
wstring Name;
// Значение поля, переведенное в строку
wstring Value;
};
class TWMIRecord
{
private:
// Свойство, принимающее значение поля
VARIANT Prop;
// Обьект-записи полученный от провайдера
IWbemClassObject *Obj;
wstring FString;
// Список полей и их значений
list<sfield> FFields;
// Количество полей
int CountList;
public:
TWMIRecord(IWbemClassObject *AObj);
~TWMIRecord(void);
// Метод, получающий поле по имени
sField Field(wstring AName);
// Метод получающий поле по номеру
sField Field(int iName);
// Функция, получающая верхний индекс в списке полей
int high();
};Syhi-подсветка кода
Кто-то скажет: «А зачем Field(wstring AName) возвращает структуру, достаточно ведь вернуть значение?». Верно, но вдруг захочется пополнить структуру еще какой-нибудь характеристикой поля, например, типом, так что пусть этот метод возвращает всю структуру – ничего пагубного в этом нет. Ок. Реализуем этот класс (на Делфи):
constructor TWMIRecord.Create;
begin
// Создается класс списка полей и их значения
FFields:=TStringList.Create;
// Здесь я не предполагал хранить ничегокроме имя и значения поля
// так что TString’a вполне хватит
end;
// Метод проходя по полям
procedure TWMIRecord.Enum(Obj: OleVariant);
var PropEnum:IEnumVariant; i:Cardinal; s:string; d:double;
begin
// Приготовим список для внесения в него данных
FFields.Clear;
// Инициализируем энумератор
PropEnum:=IEnumVariant(IUnknown(Obj.Properties_._NewEnum)); //Поля
// и начнем по нему лазить, пока он выбирает записи
while (PropEnum.Next(1, Obj, i) = S_OK) do begin
try
// Здесь я прикрутил распознавание типа даты
// если поле содержит дату, то привести ее в
// понятный человеку вид
if obj.CIMType=$00000065 then begin
s:=Obj.Value;
s:=copy(s,7,2)+‘.’+copy(s,5,2)+‘.’+copy(s,1,4);
end else
// если же это не дата, то пусть сама программа приводит
// значение к строке. в противном случае в строку
// должно писаться значение [NULL], если такое поле пусто
if VarIsNull(Obj.Value) then s:=WMIValueNull else s:=Obj.Value;
FFields.Values[Obj.Name]:=s;
except
end;
end;
end;
function TWMIRecord.FieldName(i: integer): String;
begin Result:=»;
// если номер попадает в список полей, вернем имя поля
// по его номеру
if (i>=0)and(i<FFields.Count) then
Result:=FFields.Names[i];
end;
destructor TWMIRecord.Free;
begin
// Освободим список, уберем мусор
FFields.Free;FFields:=nil;
end;
function TWMIRecord.GetItem(v: Variant): String;
begin
Result:=»;
// Если мы хотим получить значение по номеру поля
// нужно проверить, не выходит ли указанный индекс
// за пределы списка полей
if VarIsOrdinal(v)and(v>=0)and(v<FFields.Count) then
// И если не выходит — вернуть это поле
Result:=FFields.Values[FFields.Names[v]];
// если же мы хотим получить значение поля по имени
if VarIsStr(v) then
// мы просто передаем имя, и если
// поле с таким именен есть возвращается его значение
Result:=FFields.Values[v];
// иначе вернется пустая строка
end;
function TWMIRecord.HighRecordIndex: Integer;
begin
// Последний индекс в списке полей
Result:=FFields.Count-1;
end;Syhi-подсветка кода
Что тут добавить еще? Энумератором получаем поля и их значения, раскладывая в «массив». И даем возможность выбирать из этого массива в самой программе, любым способом, по индексу или по имени. И еще константа const WMIValueNull='[NULL]’. На С++:
{
Obj=AObj;
HRESULT hres;
BSTR pstrName;
VARIANT pVal;
CIMTYPE pvtType;
sField field;
// Начинаем перечисление
hres=Obj->BeginEnumeration(0);
CountList=0;
// Если это возможно конечно, проходимся циклом
// пока не нарвемся на ошибку, или пока энумератор не
// выберет все данные
while(!FAILED(hres)&&(hres!=WBEM_S_NO_MORE_DATA)){
// Получим очередное "следующее" поле
hres=Obj->Next(0,&pstrName,&pVal,&pvtType,0);
// Если оно удачно получено
if(!FAILED(hres)&&(hres!=WBEM_S_NO_MORE_DATA)){
field.Name.clear();
// Запишем его имя
field.Name=wstring(_bstr_t(pstrName,false));
// Если его значение не пусто
if(pVal.vt!=VT_NULL){
field.Value.clear();
// получим его, преобразовав в строку в зависимости
// от типа
switch(pvtType){
case wbemCimtypeBoolean:
field.Value=(pVal.boolVal)?L"TRUE":L"FALSE";
break;
case wbemCimtypeString:
field.Value=wstring(_bstr_t(pVal.bstrVal,false));
break;
case wbemCimtypeSint32:
int i=pVal.intVal;
char c[30]="";
itoa(i,c,10);
for(int i=0;i<10&&c[i]!=0;i++){field.Value+=c[i];}
break;
}
}
// и внеся новое поле в наш список
FFields.push_back(field);
// увеличим счетчик количества полей
CountList++;
}
}
}
int TWMIRecord::high()
{
// Вернем верхний индекс списка полей
return CountList-1;
}
TWMIRecord::~TWMIRecord(void)
{
// Освободим список
FFields.clear();
}
sField TWMIRecord::Field(wstring AName){
FString=L"";
sField res={L"",L""};
// Приготовим итератор для прохода по списку
list<sfield>::iterator i=FFields.begin();
// пока не конец списка
for(;i!=FFields.end();i++){
// получим очередной элемент
sField ws=*i;
// проверим не совпадает ли имя поля
// полученного элемента с указанным нами
if(ws.Name==AName){
// Если совпадает — выйдем из цикла
res=*i;
break;
}
}
return res;
};
sField TWMIRecord::Field(int iName){
FString=L"";
sField res;
// Приготовим итератор для прохода по списку
list<sfield>::iterator i;
// пока не конец списка или не достигнут указанный индекс поля
for(i=FFields.begin();(i!=FFields.end())&&(iName>=0);i++,iName—);
// Если список полей весь пройден а индекс поля еще не достигнут
// вернем пустые строки.
if(i!=FFields.end() && iName>=0){ res.Name=L"";res.Value=L"";}
// Иначе вернем данные из списка
else {res=*i;}
return res;
};Syhi-подсветка кода
Опять-таки, итерации по списку – в Делфи, дяди из Борланда дали возможность обращаться к элементам списка как к массиву, как сделано в STL я не знаю, поэтому банально – прошелся циклом по list’y.
Так… Вроде ничего не забыли описать? Если нет, то пора попробовать эту махину в действии.
Starting Line
Начнем, пожалуй, с Делфи. Создадим проект с формой, на него кинем StringGrid, и в обработчике создания формы OnCreate напишем такой код (на Делфи):
var w:TWMI;i,j:integer; wr:TWMIRecord;
begin
// Создадим обьект WMI
w:=TWMI.Create(nil);
// Выкатим ему запрос
w.SQL:= ‘SELECT caption, CommandLine FROM Win32_Process’;
with StringGrid1 do begin
// Развернем Грид на нужно е количество записей
RowCount:=w.HighObject+1;
FixedCols:=0;
// В цикле пройдясь по записям
for i:=0 to w.HighObject do begin
wr:=w[i];
if ColCount<(wr.HighRecordIndex+1) then ColCount:=(wr.HighRecordIndex+1);
// впишем значения полей в таблицу
for j:=0 to wr.HighRecordIndex do begin
Cells[j,i]:=wr[j];
end;
end;
end;
w.Free;
end;Syhi-подсветка кода
Перед этим не забудем создать в проекте Unit (назовем его Unit2), и вложить в него код классов, не забыв в нем указать необходимые модули для работы механизма в описанный классах uses Classes,contnrs,variants,ActiveX,Comobj; Не забыв указать в разделе uses модуля формы этот самый Unit2.
Теперь код обработчика на форме связан с модулем, где описаны классы.
Если все проделано правильно после жмака по F9 на экран выкатится форма со списком процессов (см. рисунок 1):
Рис. 1. Работа программы написанной на Делфи
Ну вот. Запрос SELECT caption, CommandLine FROM Win32_Process получил набор с заголовками запущенных в целевой системе процессов, и путями к файлам этих процессов. Администратору сразу видно, что запущено у пользователя, не нужно идти к нему, или использовать платные средства удаленного администрирования. Увы, не все они могут показать такую информацию, а иногда это важно для понимания состояния системы.
А теперь тоже самое но на С++. Лукаво не мудрствуя, сделаем это в консоли, это попроще будет:
#include "TWMI.h"
#include <locale>
#include <iostream>
int _tmain(int argc, _TCHAR* argv[])
{
// Включим русский язык для консоли
setlocale(LC_ALL,"russian");
// Создадим класс WMI
TWMI *wmi=new TWMI();
// Скормим ему запрос
if(wmi->SetQuery("SELECT caption FROM Win32_Process")){
// Если запрос удачно обработал
printf("Получили данные\n");
int i=0;
// В цикле пройдемся по записям,
for(TWMIRecord *r=wmi->Item(i);r;i++,r=wmi->Item(i)){
// И выведем значение поля
wcout<<r->Field(L"Caption").Value<<‘\n‘;
}
} else {printf("Неудача");}
// после чего уберем мусор
delete wmi;
getchar();
return 0;
}Syhi-подсветка кода
Здесь тоже самое, разве что я изменил запрос. Попросил только заголовки процессов. В консоли красиво не выведешь. Здесь же применен метод получения поля по его имени, но с таким же успехом его можно заменить на:
sField f=r->Field(i);
wcout<<f.Name<<‘=’<<f.Value<<‘\n‘;
} wcout<<‘\n‘;Syhi-подсветка кода
Где в цикле будет вестись проход по всем полям по их номеру. Запустим и посмотрим, что же получилось (см. рисунки 2, 3):
Рис. 2. Получение по имени поля
Рис. 3. Получение нескольких полей
Запрос на рисунке 2 показывает, что все хорошо прошло. Объект получил от провайдера WMI информацию, и программа вывела ее на экран. На втором же рисунке показана возможность показа всех полей, одна запись тут разделена пустой строкой. Вообще я в запросе указал два поля CommandLine, но провайдер предоставляет кроме этого еще несколько, судя по всему стандартных полей, характеризующих тип самого запроса. Например, свойство __PROPERTY_COUNT=2 говорит о том, что мы запросили два поля, между прочим, им можно пользоваться, чтоб узнать количество полей. А __CLASS=Win32_Process говорит о том, какое «представление» использовалось. Мы хотели получить список процессов – вот и получили, соответственно и класс Win32_Process. Ну и, конечно же, в конце данные о тех самых наших полях, которые мы запрашивали, Caption и CommandLine. Все работает замечательно.
Теперь можно самостоятельно написать свою «тулзу» для удаленного администрирования. Учитывая, что классов в WMI много, можно много чего узнать о компьютерах в сети. Например, можно узнать, что за пользователи описаны в компьютере, выкатив запрос SELECT * FROM Win32_Account (см. рисунок 4):
Рис. 4. Информация о пользователях на целевой машине
Или предположим звонит юзер:
-у меня материнка с ума сошла, дайте дрова!
-какая у вас материнка?
-пластиковая, из магазина…
Да-да. Это не анекдот. Юзеры, иногда в силу своей компьютерной неграмотности, такое откалывают, что Задорнов «плякаль». А, запросив SELECT * FROM Win32_BaseBoard у виндоуса того пользователя можно увидеть, что у него (см. рисунок 5):
Рис. 5. Параметры материнской платы
Оказывается Асус. О! И даже серийник есть. Ну, теперь просто запросить на сайте производителя дровишки для P5GZ-MX и удаленно RAdmin’ом например проинсталлировать. Кто-то скажет: «Так, а чего самим РАдмином или типа него не посмотреть денить в свойствах?» Где? Все знают, где информация такая лежит? Ану-ка, партизаны: «шнель шпрехен… ». Я лично не знаю, где можно посмотреть такие подробности. А тут раз – и все как на ладони. А тем паче, своя Наташка… ))))
Post Scriptum
Ну вот. Собственно, на этом можно поставить точку. А можно и троеточие, ибо WMI помимо получения информации позволяет управлять компьютером. Опять-таки теми же запросами. «Тушить процессы» или выключать компьютер удаленно.
Например, если вызвать метод Terminate класса, полученного по запросу на Win32_Process, то можно потушить все процессы из запроса. Порывшись в MSDN, можно даже найти пример [2]. Если ссылку еще не завалили, посмотрите, как это делается: Запросом получается набор объектов, у которых вызывается метод Terminate, и они тушатся.
В общем, все это очень обнадеживает, и, если системный администратор владеет этим каратэ – цены ему нет. Домен свой он будет держать атланту подобно. Так что рекомендую вляпаться в эти микрософтовские катакомбы – не пожалеете.
The Чтиво
- Ресурс вики http://ru.wikipedia.org/wiki/WMI
- MSDN http://msdn.microsoft.com/en-us/library/aa393907%28VS.85%29.aspx
Статья из девятого выпуска журнала «ПРОграммист».
Обсудить на форуме — WMI. Wладение Mагической Iнформацией. Часть 2
23rd
WMI. Wладение Mагической Iнформацией. часть 1
Чего тут писать-то? Про то, как вы думаете, что нужно хорошему системному администратору? «Монога» пива? Ну, это тоже не плохо бы. Но, что еще? Чтобы пользователи никогда не звонили? Ну, а если среди них симпатичные девчонки, к которым приятно нагрянуть для «осмотра» их компушек? Нет, все это отмазки. Хороший администратор должен иметь в своем арсенале как можно больше инструментов управления подчиненной ему сети. Конечно, такие инструменты существуют и давно, кстати, но большинство из них требуют наличие установленного клиента на целевой машине, а это не всегда кошерно. Можно, но вдруг пользователь запротестует: «…нечего мне на винду ставить хлам». К тому же, не все из этих инструментов бесплатные. Что же делать в таких случаях, когда и нужно и никак? Ответ прост – написать инструмент самому, но на своих условиях игры.
Виталий Белик
by Stilet www.programmersforum.ru
Как бы не ругали Виндоус – это все таки могучая операционная система, и если уметь владеть ей в полной мере, она станет надежнее плохо настроенного (из-за незнания) Линукса, к примеру. Я веду к тому, что в винде уже встроены механизмы удаленного администрирования. Например, это «Удаленный рабочий стол». Не Радмин, но уже неплохо – стандартный. А мы сегодня поговорим о другом механизме. О механизме, позволяющем пройтись по операционке аки по таблицам базы данных – это WMI…
Мы начинаем КВН
Итак, предлагаю начать с того момента, где упоминается эта самая технология. Что это, почему это, что она может? Заглянем в Википедию [1]. Это инструментарий управления Виндоусом. Уже обнадеживает. Три эти слова подразумевают возможность контроля за происходящим в операционной системе, это как раз то, что нужно хорошему администратору.
Читаем дальше: «WMI* представляет собой набор классов, содержащих свойства, доступ к которым открывает информацию о компьютере, операционной системе, их параметрах и установках». Особенную важность уделили динамическому изменению этой информации, поэтому доступ к ней происходит посредством запросов. Получается, что Виндовс выступает в роли системы управления базой данных, откуда мы эту информацию и запрашиваем. Ну, а раз это база данных, согласитесь, было бы просто замечательно, если-бы работа с ней велась на языке, привычном для многих (если не всех) СУБД – SQL.
Читаем дальше. Опа! Угадали. То ли мы верно догадались, то ли Микрософт просчитал заранее удобство… В любом случае, для получения информации используется язык запросов WQL, являющийся одной из разновидностью SQL. Вот это уже радует. Зная язык SQL, трудно будет запутаться при запросах WMI. Это облегчает задачу, еще и потому что запрос сам по себе является строкой, и может быть введен самим пользователем программы непосредственно во время работы самой программы.
Отличненько. По логике в SQL описывается тот или иной доступ к таблицам и объектам базы данных, но в случае с СУБД программисту известно о том, к каким объектам в БД у него есть доступ, который ставит админ базы данных, либо же сам программист знает, какими финтами обладает его база данных. Как же быть в случае с WMI? Кто скажет: какова структура таблиц? Ой, пардон, речь-то шла о классах… какова структура классов WMI и вообще что они из себя представляют?
Вот эту информацию можно почерпнуть в MSDN [2]. Кстати, счастливые обладатели Visual Studio с MSDN-ом, могут набрать в хелпе в поиске WMI, и получить ту же информацию. Ну, предположим, у нас нет такого хелпа (хотя я лично для себя запасся им), и нам придется напрягать интернет. Ничего. Не так сложно выйдя по ссылке [2], попасть на одну из ветвей, описывающих все это благо, с названием «WMI Service Management Classes», а уж на ней можно выйти на WMI Classes, где собственно описаны все классы, которые можно получить запросом**.
Ну, да ладно. Билл им судья. Я на всякий случай приведу ссылку, по которой точнее можно выйти на список классов [3]. Не знаю, что с ней случится через год-два, но на момент написания статьи она была актуальна.
Давайте посмотрим, что за информацию нам предлагает Микрософт…430 классов.… Не кисло. Такое впечатление, что о системе, о ее состоянии на момент запроса можно узнать все-все. Это хорошо. Такая информация для администратора иногда оказывается очень ценной.
Ладно. Что может эта загогулина мы теперь знаем. А как получить эти самые классы? То есть объекты, или что там у них… интерфейсы?
Вот тут немного придется мыслить неравномерно. Думаю, многим привычно, что указатели на объекты возвращают конструкторы класса. Однако, в случае с WMI это не совсем так. Здесь бал правит особый провайдер (все-таки получение информации происходит путем взаимодействия с операционкой через язык запросом, а стало быть, должен быть «черный ящичек», обрабатывающий эти запросы) – WBEM. Механизм этот представляет СОМ сервер, который можно использовать в своих программах, предварительно заполучив его инстанцию. Это делается стандартными методами загрузки СОМ сервера. Например, в Делфи это функция CreateOleObject, которая вызывает CoCreateInstance из библиотеки ole32.dll, и возвращает указатель на объект-провайдер, от которого, скормив ему WQL запрос, можно получить указатели на WMI классы.
Его механизм открывает набор-энумератор. Таким образом, проходя в цикле по динамическому списку, получаем указатели на классы, которые и содержат информацию по запросу. Вообще, если проводить аналогию с базами данных очень легко представить класс WMI как плоскую таблицу, где имена свойств – это имена полей, а их содержимое… нет, не записи, а запись. Один экземпляр класса – одна запись. Таким образом, получается два цикла: один перечисляет список объектов, запрошенного класса, а второй перечисляет поля объекта с информацией.
Что еще нужно знать о WMI? То, что эта технология позволяет не только получать информацию о системе, но и вызывать команды управления системой. То есть классы, полученные энумерацией, имеют методы, вызов которых приведет к выполнению на целевой машине неких, подлежащих ему действий. Не думайте, что я описался, назвав локальный компьютер целевым. WMI позволяет проводить все эти операции с любым хостом Windows в сети, лишь бы имелись административные права на подключение к целевой машине.
Итак, пора написать программку, которая будет нашим телескопом в локальной сети.
Show must go on…
Ради интереса, предлагаю небольшой холливар. Да, да… Вот такой вот я нехороший провокатор. Предлагаю написать программу на двух языках – Делфи и Си. В качестве компиляторов я взял Delphi 6 (на мой взгляд, самая лучшая версия Делфи) и Visual Studio 2010. Не потому что VS2008 или VS6 хуже, а просто у меня нет их под рукой.
Для начала давайте определимся со стратегией. Чтобы получить набор данных от WMI нам нужно следующее:
- запустить СОМ сервер, провайдер WMI. А точнее WbemLocator – это имя провайдера;
- приконнектить его к целевой машине;
- заставить его выполнить запрос;
- пройтись энумератором по коллекции записей;
- пройтись энумератором по коллекции полей каждой записи.
Мы, кстати, реализуем механизмы получения значения поля, как по его названию, так и по его номеру. Для этого предлагаю выделить два класса. Первый назовем TWMI. Он будет главным. Его задача принять запрос, подключится к компьютеру, выполнить запрос, и, перебрав его записи, создать список объектов типа TWMIRecord. Этот второй класс будет отвечать за получение данных из полей переданной ему записи (записи передаются в виде объекта) либо по имени, либо по номеру поля. Опять таки и тут без энумерации не обойтись.
Ладушки. Начнем. Сначала опишем главный класс. Листинг 1 и 2 показывает, как он может выглядеть в Делфи и Си:
Листинг 1 (Делфи)
private
// Список объектов-записей
FRecords:TObjectList;
FSQL: String;
FRoot: String;
FHost: String;
FSQL2: String;
FLogin: String;
FPassword: String;
procedure SetHost(const Value: String);
procedure SetRoot(const Value: String);
function GetItem(i: Variant): TWMIRecord;
procedure SetSQL2(const Value: String);
procedure SetLogin(const Value: String);
procedure SetPassword(const Value: String);
Public
//**************************************
// Функция для циклов. Выдает номер последней записи
Function HighObject:Integer;
// Функция поиска записи по имени поля
Function Find(AFieldName,AValue:String):TWMIRecord;
//**************************************
// Свойство, получающее по номеру запись из набора
Property Item[i:Variant]:TWMIRecord read GetItem; default;
Constructor Create(AOwner:TComponent);
Destructor Free;
published
// Свойства Логина и пароля. Чтоб подключится к компьютеру и
// получить информацию нужны привилегии администратора
Property Login:String read FLogin write SetLogin;
Property Password:String read FPassword write SetPassword;
// Имя машины к которой подключаемся
Property Host:String read FHost write SetHost;
// Путь к таблице с данными
Property Root:String read FRoot write SetRoot;
// Свойство, принимающее строку запроса
// и инициализирующее его выполнение
Property SQL:String read FSQL2 write SetSQL2;
end;
//******************************************Syhi-подсветка кода
Листинг 2 (С++)
{
private:
// Обьект-провайдер, через который можно будет приконнектится
// к компьютеру.
IWbemLocator *loc;
// обьект, которому будем скармливать запрос, и получать
// записи
IWbemServices *serv;
// поле, хранящее наш запрос
string FQuery;
// энумератор записей
IEnumWbemClassObject* enum_Record;
// список, который будет хранить объекты-записи
list<TWMIRecord> RecList;
// функция, активирующая запуск провайдера
bool RunLocatorInstance();
// функция, активирующая подключение к хосту
bool ConnectToWBEM();
// процедура, создающая новыйй обьект-запись
// в ходе энумерации
void CreaRecord(IWbemClassObject *O);
public:
TWMI(void);
~TWMI(void);
// Функция, открывающая набор данных
// по переданному ей запросу
bool SetQuery(string wql);
// Функция, получающая запись по ее номеру
TWMIRecord* Item(int i);
};Syhi-подсветка кода
На всякий случай хочу сказать, что для Сишного кода нужны инструкции:
Листинг 3
#pragma comment(lib, "wbemuuid.lib")
#include <comdef.h>
#include <WbemIdl.h>
#include "TWMIRecord.h"
#include <string>
#include <list>
using namespace std;Syhi-подсветка кода
Здесь единственный хедер, который стоит описать – TWMIRecord.h. Он описывает наш класс, отвечающий за получение и хранение полей записи, переданной ему. Все остальное – стандартный набор. Я не буду их описывать, ибо их описание вполне можно найти на MSDN или в хелпе.
Теперь можно описать конструкторы и деструкторы главных классов. На Делфи:
begin
inherited;
FRecords:=TObjectList.Create;
FHost:=‘.’;
FRoot:=‘root\cimv2’;
end;
destructor TWMI.Free;
begin
FRecords.Free;FRecords:=nil;
end;Syhi-подсветка кода
На Си:
{
loc=NULL; serv=NULL;
CoInitialize(0);
}
TWMI::~TWMI(void)
{
CoUninitialize();
RecList.clear();
Здесь мало интересного. Инициализируются главные свойства, и в случае с Си инициализируется (в конструкторе) и сбрасывается (в деструкторе) СОМ библиотека. В случае с Делфи инициализация будет не здесь, а в коде обработки запроса.
Для Делфи:
На всякий случай, чтоб не было недомолвок, скажу, что обработчики свойств Login, Password, Host, Root стандартные для любого проекта, я не буду приводить их реализацию, ибо там всего лишь внутреннему полю класса, отвечающему за значение этих свойств, описывается методика присвоения нового значения. Если что станьте курсором на эти свойства и нажмите CTRL+SHIFT+C – будут созданы стандартные тела обработчиков – именно их я и имею в виду.
Самое главное для этого класса – метод обработки запроса. Именно здесь сконцентрирована вся магия работы с WMI. Давайте посмотрим, как она выглядит (на Делфи):
var
// Переменная провайдер. подключается к хосту,
// Через нее будем получать объект для обработки
// запросов
objSWbemLocator,
// Переменная объекта обработки запросов
// Ей будем скармливать запрос и ее же
// энумератором будем получать записи
objWMIService,
// Сервисные переменные. Нужны для
// получения энумераторов
Records,o1,o2
:OleVariant;
i:Cardinal;
// Интерфейс, который будет являться посредником
// между провайдером и объектом запросов
id:IDispatch;
// Энумератор для прохода по записям
Enum:IEnumVariant;
// Переменная класса, который будет обрабатывать
// переданную ему запись
r:TWMIRecord;
begin
FSQL2 := Value;
// инициализируем СОМ модель
CoInitialize(0);
// Получаем объект провайдера WMI, Локатор, если говорить
// по микрософтовски
objSWbemLocator:=CreateOleObject(‘WbemScripting.SWbemLocator’);
// Если он успешно получен, то можно далее с ним работать
if not VarIsClear(objSWbemLocator) then begin
// Присоединяемся к хосту, получив интерфейс для
// отработки запросов
objWMIService:=objSWbemLocator.ConnectServer(Host,Root,FLogin,FPassword,»,»,0,id);
// Если присоединение прошло успешно то можно
// скармливать запрос
if not VarIsClear(objWMIService) then begin
// Скормим запрос WQL нашему провайдеру
Records:=objWMIService.ExecQuery(FSQL2,‘WQL’,0,id);
// Он вернет объект записей. Ну если конечно вернет
if not VarIsClear(Records) then begin
//**************************************
// В случае когда все удачно, инициализируем
// энумератор для прохода по записям
Enum:=IEnumVariant(IUnknown(Records._NewEnum)); //Список Записей
// Приготовим список для наполнения объектами,
// обрабатывающими переданные им записи
FRecords.Clear;
// и пройдемся энумератором, пока он
// не достигнет конца коллекции записей
// или точнее пока очередная запись выбрана успешно
while (Enum.Next(1, o1, i) = S_OK) do begin
// Создадим объект — запись
r:=TWMIRecord.Create;
// внеся его в список
FRecords.Add(r);
// И заставим его пройтись по полям,
// переданной ему записи
r.Enum(o1);
end;
//**************************************
// после чего приберем мусор. Набор мы уже получили
// так что можно отключаться
Records:=Unassigned;
end;
objWMIService:=Unassigned;
end;
objSWbemLocator:=Unassigned;
end;
// и освободить ресурсы СОМ машины.
CoUninitialize;
end;Syhi-подсветка кода
Добавлю, что у класса TWMIRecord предусмотрен метод Enum, которому передается объект записи. Он инициализирует проход по полям, получая из них значения. Теперь в С++:
Листинг 7
// Переменная для результатов СОМ механизмов
HRESULT hres;
// результат выполнения запроса.
// он будет подаваться на выход метода, дабы
// программист знал, отработал ли метод успешно
// или не отработал
bool res=false;
// Если Локатор запустился нормально
if(RunLocatorInstance()){
// И присоединился к хосту
if(ConnectToWBEM()){
// Можно скармливать провайдеру
// запрос, не забыв преобразовать типы 🙂
hres=serv->ExecQuery(bstr_t("WQL"),
bstr_t(wql.c_str()),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&enum_Record);
// Если запрос скормлен, и провайдер отработал его правильно
// можно идти далее. Предидущей функцией был
// получен энумератор записей, который позволит
// сделать проход по ним
if(!FAILED(hres)){
// Обьявим переменную — запись. В нее на итерации
// будет подаваться очередная запись
IWbemClassObject *Obj;
// и переменную, в которую будет подаваться сколько // записей
// выбрано. На итерациях это значение будет равно 1,
// ведь мы будет проходитьпо каждой записи 🙂
ULONG uReturn = 0;
hres=S_OK;
// приготовим список, хранящий объекты записей
RecList.clear();
// и прокатимся по энумератору
while(enum_Record){
// получим очередную запись
hres=enum_Record->Next(
//указав что мы будем ждать до последнего, пока на сервере выбирается запись
WBEM_INFINITE,
// проходя по одной записи за итерацию,
1,
// передавая ее в переменную вышеописанную нами для этого
&Obj,
// и получая ответ "сколько записей пройдено", короче 1
&uReturn
);
// Если же всетки мы достигли конца набора, вернется 0 пройденных
// записей. Придется прервать цикл
if( uReturn==0) break;
// А пока записи прибывают будем составлять на них
// опись, протокол, сдал-принял… отпечатки пальцев 😀
CreaRecord(Obj);
}
// по прошествии цикла будем считать что функция успешно отработала
// соответственно вернем позитивный ответ
res=true;
}
// и освободим нашего провайдера и его сервис
serv->Release();
}
loc->Release();
}
return res;
};Syhi-подсветка кода
Здесь применены методы, описанные выше:
HRESULT hres;
bool rt=false;
hres=CoCreateInstance(CLSID_WbemLocator,0,CLSCTX_INPROC_SERVER,
IID_IWbemLocator,(LPVOID *)&loc);
if(!FAILED(hres)){
hres = CoInitializeSecurity(
NULL,
-1, // COM authentication
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
);
rt=(!FAILED(hres))?true:false;
}
return rt;
}
bool TWMI::ConnectToWBEM(){
HRESULT hres;
BSTR bUser=NULL,bPassword=NULL;
BSTR bHost=L"ROOT\\CIMV2";
if(Host!=""){bHost=L"\\\\"+_bstr_t(Host.c_str())+L"\\ROOT\\CIMV2";}
hres=loc->ConnectServer(
bHost, // Хост с веткой WMI
// User name. NULL = current user
(Login!="")?_bstr_t(Login.c_str()):bUser,
// User password. NULL = current (Password!="")?_bstr_t(Password.c_str()):bPassword,
0, // Locale. NULL indicates current
NULL, // Security flags.
0, // Authority (e.g. Kerberos)
0, // Context object
&serv // pointer to IWbemServices proxy
);
return (!FAILED(hres))?true:false;
};Syhi-подсветка кода
В принципе, здесь пояснять особо нечего. Создается инстанция провайдера, говоря по-русски, запускается для нашей программы механизм WMI (аналог CreateOleObject(‘WbemScripting.SWbemLocator’); примененной мной в Делфи варианте) и происходит подключение к хосту, учитывая логин и пароль пользователя.
Если логин и пароль отсутствуют, берется аккаунт текущего сеанса (Если уж по правде то текущий контекст безопасности, т.е. с чьими правами запущена программа. Будем считать, что запуск программы будет от текущего сеанса. Мы ведь рассчитываем, что это будет запускать администратор всея сети). Если отсутствует указание хоста – подключение идет к нашей машинке.
Почему я выделил в Сишном классе эти функции подключения отдельно? Захотелось так. Мне показалось, что слишком много в Си писанины, и я не хотел загромождать метод обработки запроса лишним кодом, это бы выглядело некрасиво. Казалось бы сложным. На Делфи эти функции как-то срослись в две строчки, к сожалению, в Си мне не удалось так же аккуратно написать их, может потому, что я плохо знаю Си, или потому что в Делфи действительно удобнее описывать работу с СОМ интерфейсами… Да и работа со строками в Делфи попрозрачнее будет… Впрочем, это не важно. Ну и сюда же следует приписать реализацию процедуры, создания класса для обработки записи:
// создаем новый объект, скормив ему
// переданную строку
TWMIRecord *r=new TWMIRecord(O);
// Помещаем его в список строк
RecList.push_back(*r);
};Syhi-подсветка кода
Энумерация по полям записи будет инициирована конструктором. Опять таки, в Си я описал создание класса в отдельную процедуру не по каким-то особым причинам, а потому что просто так захотелось. Запросто эти две строчки можно поместить в энумерацию SetQuery.
Так, набор получили. Прокатились по нему. Теперь нужен механизм, проходя по списку объектов TWMIRecord. Надо же как-то получать выбранные данные. Посмотрим, как это можно сделать (на Делфи):
var ii:integer;
begin Result:=nil;
if VarIsOrdinal(i) then
// Если переданный номер записи не
// вылезает за рамки списка
// получим ее
if (i>=0)and(i<FRecords.Count) then
Result:=TWMIRecord(FRecords[i]);
end;Syhi-подсветка кода
Эта функция повешена на свойство:
Хорошо зная Делфи, могу заверить, что возможность объявлять свойство для класса по умолчанию очень полезна. Достаточно в выражениях указать имя переменной объекта, если какое-то из его свойств установлено по умолчанию, оно вызовется, так как будто программист руками его вызвал в коде.
Например, многим известен Дельфийский Listbox. У него есть свойство Items[номер строки]. Вызывают его так ListBox.Items[такой-то] – Это дает строку по указанному номеру. Но далеко не все знают, что при написании такого на самом деле Делфи воспринимает это указание как ListBox.Items.Strings[такая-то], потому что Strings описана как свойство по умолчанию для поля Items, да еще и с указанием индексации. Согласитесь удобнее не писать Items.Strings раз среда позволяет это. Код становится короче и красивее. Кстати некоторые нерадивые авторы методичек на этом деле спекулируют. В одних методичках пишут длинную форму обращения, в других краткую. Учеников это часто путает, появляется вопросы «В каких случаях писать длинную форму инструкции в каких короткую». Ответ то на самом деле прост – эти две формы равнозначны. Кто как хочет, пусть так и пишет. Линейкой по рукам нужно проехаться этим борзописцам-преподавателям. Сразу видно они сами мало понимают в программировании. Это большая беда нашей системы обучения, но, увы… Законы Подлости диктуют свою игру.
Продолжение следует…
The Чтиво
- Ресурс вики http://ru.wikipedia.org/wiki/WMI
- MSDN http://social.msdn.microsoft.com/Search/ru-RU?query=WMI&ac=8
- MSDN. Win32 Classes http://msdn.microsoftcom/en-us/library/aa394084%28v=VS.85%29.aspx
- Марк Русинович. Внутри Windows Management Interface. – Magazine/RE, 05, 2000
Статья из восьмого выпуска журнала «ПРОграммист».
Обсудить на форуме — WMI. Wладение Mагической Iнформацией. часть 1
10th
Фев
Забанить IP на vBulletin
Зашел на свой форум и увидел 57 гостей, смотрю IP они начинаются на 66.249. (и там разные). Я понимаю не так просто они туда зашли…
Как забанить эту подсеть(или как привильно)?
Облако меток
css реестр ассемблер timer SaveToFile ShellExecute программы массив советы word MySQL SQL ListView pos random компоненты дата LoadFromFile form база данных сеть html php RichEdit indy строки Win Api tstringlist Image мысли макросы Edit ListBox office C/C++ memo графика StringGrid canvas поиск файл Pascal форма Файлы интернет Microsoft Office Excel excel winapi журнал ПРОграммист DelphiКупить рекламу на сайте за 1000 руб
пишите сюда - alarforum@yandex.ru
Да и по любым другим вопросам пишите на почту
пеллетные котлы
Пеллетный котел Emtas
Наши форумы по программированию:
- Форум Web программирование (веб)
- Delphi форумы
- Форумы C (Си)
- Форум .NET Frameworks (точка нет фреймворки)
- Форум Java (джава)
- Форум низкоуровневое программирование
- Форум VBA (вба)
- Форум OpenGL
- Форум DirectX
- Форум CAD проектирование
- Форум по операционным системам
- Форум Software (Софт)
- Форум Hardware (Компьютерное железо)