Скачай демонстрационный проект (26,1 кб)
Здесь мы рассмотрим 6 способов получения списка процессов в User Mode. Хотя способов гораздо больше, здесь будут рассмотрены только законные (рекомендованные) способы. Вот они: с помощью библиотек – Tool Help, Process Satus Helper, Performance Data Helper, из реестра, с помощью Native API, через сервер терминалов. Остальные можно отнести ко всяким хитростям и хакам, и в простой прикладной программе они вряд ли пригодятся. Если все-таки они тебе нужны, то читай статью MsRem’а «Обнаружение скрытых процессов».
Список процессов хранится в ядре системы в виде кольцевого двусвязного списка структур EPROCESS. Эти структуры разные в разных версиях Windows. И просто так ты этот список получить не сможешь. Для этого нужно опускаться в ring0 (например, писать драйвер). Из user-mode (ring3) информацию о процессах можно получить с помощью функции NtQuerySystemInformation (из ntdll.dll). Все остальные библиотеки, используемые для работы с процессами, потоками, модулями и т.п., так или иначе используют именно эту функцию.
Мы напишем функцию FillProcessesList, которая будет получать объект-список TStringList и заполнять его списком имен запущенных процессов и другой информацией. В случае удачного завершения функция будет возвращать True , при ошибке – False. Эту функцию мы напишем для каждого способа, а использовать ее будем в простом тестовом проекте. Работать будем в Delphi. Для начала создадим болванку проекта. Кинем на пустую форму Memo и 1 кнопку. Все имена компонентов оставим по умолчанию. В разделе uses добавим модуль Sample1, в котором и напишем функцию FillProcessesList. Работать наша прога будет до неприличия просто: при нажатии на кнопку в Memo будет выводиться список процессов. Код должен выглядеть примерно так:
unit
Unit1;
interface
uses
Windows,
Messages,
SysUtils,
Variants,
Classes,
Graphics,
Controls, Forms,
Dialogs, StdCtrls, ExtCtrls,
Sample1;
type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender:
TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure
TForm1.Button1Click(Sender: TObject);
var
slProcessesList:
TStringList;
// Сюда будем пихать список процессов
begin
slProcessesList :=
TStringList.Create; // Создаем
объект
if
FillProcessesList(slProcessesList) then //
Заполняем
список
процессов
Memo1.Lines.Assign(slProcessesList) //
и
передаем
его
в
TMemo
else
// или выводим ошибку.
ShowMessage('Ошибка.
Не могу получить список процессов.');
slProcessesList.Free; // Уничтожаем объект.
end;
end.
Чтобы исследовать каждый новый метод получения списка процессов, надо в разделе uses менять модуль Sample1 на Sample2, ...3 и т.д.
Кроме того, нам понадобится несколько вспомогательных функций. Дело в том, что некоторый гордые системные процессы не хотят открываться простой юзерской проге, мол мало прав. Поэтому наша прога будет нагло присваивать себе привилегии отладчика, функцией EnableDebugPrivileges и сдавать их функцией DisableDebugPrivileges.
Подробно код их разбирать не будем, т.к. это уже другая тема.
procedure EnableDebugPrivileges;
// Получить привилегии отладчика
var
hToken: THandle;
tp: TTokenPrivileges;
DebugNameValue: Int64;
ret: Cardinal;
begin
OpenProcessToken(GetCurrentProcess,TOKEN_ADJUST_PRIVILEGES or
TOKEN_QUERY,hToken); // Получаем
маркер
доступа
текущего
процесса
// Получаем значение отладочных привилегий
LookupPrivilegeValue(nil,'SeDebugPrivilege',DebugNameValue);
tp.PrivilegeCount := 1;
// Включаем отладочные привилегии
tp.Privileges[0].Luid :=
DebugNameValue;
tp.Privileges[0].Attributes :=
SE_PRIVILEGE_ENABLED;
// Применяем обновленные привилегии
AdjustTokenPrivileges(hToken,False,tp,sizeof(tp),nil,ret);
end;
procedure DisableDebugPrivileges;
// Отключить
привилегии
отладчика
var
hToken: THandle;
tp: TTokenPrivileges;
DebugNameValue: Int64;
ret: Cardinal;
begin
OpenProcessToken(GetCurrentProcess,TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY,hToken);
LookupPrivilegeValue(nil,'SeDebugPrivilege',DebugNameValue);
tp.PrivilegeCount := 1;
tp.Privileges[0].Luid :=
DebugNameValue;
tp.Privileges[0].Attributes := 0;
// Отключаем
отладочные
привилегии
AdjustTokenPrivileges(hToken,False,tp,sizeof(tp),nil,ret);
end;
Часто, когда API-функции исполняются неудачно, исключений не возникает. Чтобы все-таки определить причину неудачи надо вызывать GetLastError. Она возвращает код ошибки. А функция PrintError выводит более-менее вразумительное описание этой ошибки и избавит тебя от лазаний по справочникам.
procedure PrintError;
var
msgbuf: array [0..4095] of Char;
errcode: Cardinal;
begin
errcode
:= GetLastError;
// Получаем код последней ошибки
// Форматируем его и выводим на экран в
мессаджбоксе
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nil,errcode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
msgbuf,
sizeof(msgbuf), nil );
MessageBox(0,PChar('Код:'
+ IntToStr(errcode) + ' - ' + msgbuf),
'Ошибка!',MB_ICONERROR
+ MB_SETFOREGROUND);
end;
Эти функции объединены в модуль Addition.pas. Подключай его к проекту по мере надобности.
Ну вроде все. Остается только добавить, что все примеры я компилил в Delphi 7 и тестировал в Windows XP.
(с) tripsin aka Орехов Роман, 2006 г.