Антон Григорьев дата публикации 14-10-2005 03:47 Обобщающие примеры работы с WinAPI. Пример №5 — работа с процессами системы.
Более полный вариант этой статьи вошёл в книгу "О чём не пишут в книгах по Delphi"
Данный пример показывает, как можно получить список процессов, запущенных в системе, список модулей и окон, принадлежащих каждому из процессов, и информацию о каждом из окон.
Исторически сложилось, что существует два способа получить список процессов: с помощью функций Tool Help и с помощью функций PSAPI. Эти две группы функций использовались в разных линиях Windows: функции Tool Help появились в Windows 95, функции PSAPI - в Windows NT 4. Windows 2000/XP также поддерживают функции Tool Help, в то время как Windows 98/ME не поддерживают PSAPI. Поэтому мы будем использовать функции Tool Help - это даст нашему примеру возможность работать во всех версиях Windows, кроме NT 4 (впрочем, в Windows 95 пример тоже не будет работать, но по другой причине: из-за функций GetWindowInfo и RealGetWindowClass, отсутствующих в этой версии). Функции Tool Help объявлены в модуле TlHelp32.
Для получения списка процессов необходимо сделать снимок состояния системы с помощью функции CreateToolhelp32Snapshot. Эта функция создаёт специальный объект, который может хранить информацию о процессах, модулях, нитях и кучах, созданных в системе. Этот объект называется снимком потому, что информация, хранящаяся в нём, актуальна на момент вызова функции CreateToolhelp32Snapshot; дальнейшие изменения списка процессов, модулей и т.п. не приводят к изменению снимка. Доступ к снимку, как и к большинству объектов системы, осуществляется через его дескриптор.
Навигация по списку процессов, сохранённых в снимке, осуществляется с помощью функций Process32First и Process32Next. Они позволяют получить ряд параметров процесса, главный среди которых - идентификатор процесса (Process Identifier; PID). Это - уникальный идентификатор процесса, с помощью которого можно отличать один процесс от другого.
Примечание: не следует путать идентификатор процесса и дескриптор объекта процесса, который используется, например, в функции SetPriorityClass. Объект процесса - это специальный объект, связанный с процессом, но не тождественный ему. В частности, объект процесса может продолжать существовать уже после того, как сам процесс завершит работу - это позволяет, например, корректно синхронизироваться с уже завершённым процессом при помощи функции WaitForSingleObject. Через объект процесса можно управлять многими свойствами процесса. Поучить дескриптор объекта процесса по идентификатору процесса можно с помощью функции OpenProcess.
Для получения списка модулей данного процесса также используется снимок. Навигация по снимку модулей осуществляется с помощью функций Module32First и Module32Next.
Список окон, созданных процессом, получается с помощью функции EnumWindows. Эта функция позволяет получить список всех окон верхнего уровня (т.е. расположенных непосредственно на рабочем столе). Для каждого из этих окон с помощью функции GetWindowThreadProcessID определяется идентификатор процесса. Окна, не принадлежащие выбранному процессу, отсеиваются.
Для каждого из окон верхнего уровня, принадлежащих процессу, с помощью функции EnumChildWindows ищутся дочерние окна, а для каждого из найденных таким образом дочерних окон - его дочерние окна. Здесь следует учесть, что EnumChildWindows возвращает не только дочерние окна заданного окна, но и все окна, которыми владеют эти дочерние окна. Чтобы в дереве окон не было дублирования, при построении очередного уровня дерева окон отбрасываются все окна, непосредственным родителем которых не является данное окно.
Для получения названия окна существует два способа - через функцию GetWindowText и через сообщение WM_GetText. Функция GetWindowText безопасна при использовании с зависшими приложениями - она гарантирует, что вызвавшее её приложение не зависнет само, пытаясь получить ответ от зависшего приложения. Но эта функция не всегда может получить названия элементов управления, расположенных в окнах чужих приложений (точнее, в MSDN написано, что она вообще не может получать названия элементов управления в чужих окнах, но практика показывает, что нередко GetWindowText всё же делает это). При получении названия окна с помощью WM_GetText ограничений на работу с чужими элементами управления нет, но и гарантии, что из-за отсутствия ответа от чужого приложения программа не зависнет, тоже нет.
В данном примере используются оба способа получения текста. При построении дерева всех окон выбранного процесса используется функция GetWindowText, при получении параметров выбранного окна - сообщение WM_GetText. Чтобы программа не зависала, вместо SendMessage для отправки WM_GetText используется SendMessageTimeout.
Для каждого окна программа выводит имя оконного класса и реальный класс окна. В большинстве случаев эти два класса совпадают. Они различаются только для тех окон, чьи классы "унаследованы" от стандартных классов, таких как EDIT, COMBOBOX и т.п.
Вообще, наследования оконных классов в Windows нет. Но существует один нехитрый приём, который позволяет имитировать наследование. Оконная процедура обычно не обрабатывает все сообщения, а передаёт часть их в одну из стандартных оконных процедур (DefWindowProc, DefFrameProc и т.п.). Программа может узнать адрес оконной процедуры, назначенной стандартному классу, с помощью функции GetClassInfo, и использовать эту процедуру вместо стандартной оконной процедуры. Так как большая часть свойств окна определяется тем, как и какие сообщения оно обрабатывает, использование оконной процедуры другого класса позволяет почти полностью унаследовать свойства этого класса. В VCL для наследования оконных классов существует метод TWinControl.CreateSubClass. Функция RealGetWindowClass позволяет узнать имя класса-предка, если такой имеется.
У окна, если оно имеет стиль WS_Child, должно быть родительское окно. Если такого стиля нет, окно располагается непосредственно на рабочем столе. Кроме того, такое окно может (но не обязано) иметь владельца. Получить дескриптор родительского окна можно с помощью функции GetParent. Владельца - с помощью функции GetWindow с параметром GW_Owner.
Примечание: кроме функции GetParent существует также функция GetAncestor, которая также возвращает дескриптор родительского окна, если она вызвана с параметром GA_Parent. Разница между этими функциями заключается в том, что для окон верхнего уровня (т.е. расположенных непосредственно на рабочем столе) GetParent возвращает 0, а GetAncestor - дескриптор рабочего стола (этот дескриптор можно получить через функцию GetDesktopWindow).
Значительную часть кода программы составляет анализ того, какие флаги присутствуют в стиле окна. В этом анализе нет ничего сложного, но он громоздкий из-за большого количества флагов. Следует также учесть, что для стандартных классов одни и те же числовые значения могут иметь разный смысл. Так, например, константы ES_NoHideSel и BS_Left имеют одинаковые значения. Поэтому при расшифровке стиля следует также учитывать класс окна.
К материалу прилагаются файлы:
[Параметры процесса/приложения] [Стандартные элементы управления] [Работа с контролами чужого приложения] [Отправка и получение сообщений] [Классы и стили окон] [WM_GETTEXT ] [WM_GETTEXTLENGTH ]
Обсуждение материала [ 13-06-2008 13:58 ] 8 сообщений |