{***********************************************************
 Обобщающие примеры работы с WinAPI. Пример №5
 Получение списка процессов, запущенных в системе, их
 модулей, окон и свойств этих окон

 Григорьев Антон, 2005
 Специально для Королевства Delphi
 http://www.delphikingdom.com
 ***********************************************************}
unit PIMain;

interface

// Модуль RichEdit подключается к программе только для того,
// чтобы можно было использовать константы стиля RICHEDIT,
// такие как ES_Sunken и ES_SelectionBar, определения
// которых нет в модуле Windows.
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Buttons, ComCtrls, TlHelp32, RichEdit;

type
  TProcessesInfoForm = class(TForm)
    ListProcesses: TListView;
    LabelProcesses: TLabel;
    BtnRefresh: TBitBtn;
    LabelModules: TLabel;
    ListModules: TListView;
    LabelWindows: TLabel;
    LabelParams: TLabel;
    TreeWindows: TTreeView;
    LabelProcessError: TLabel;
    LabelModuleError: TLabel;
    LabelParamsError: TLabel;
    ListParams: TListView;
    procedure FormResize(Sender: TObject);
    procedure BtnRefreshClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure TreeWindowsChange(Sender: TObject; Node: TTreeNode);
    procedure ListProcessesSelectItem(Sender: TObject; Item: TListItem; Selected: Boolean);
   private
    procedure ClearParamList;
    procedure ClearAll;
    procedure FillProcessList;
    procedure FillModuleList(PID:Cardinal);
    procedure FillWindowList(PID:Cardinal);
    procedure FillParamList(Wnd:HWnd);
  end;

var
  ProcessesInfoForm: TProcessesInfoForm;

implementation

{$R *.DFM}

// Размер буфера, резервируемого для имени класса при
// использовании функций GetClassName и GetReadWindowClass
const ClassNameLen=512;

// Данный метод изменяет размеры и положение элементов
// управления при изменении размеров окна таким образом,
// чтобы каждый из них занимал свою четверть окна.
// Ничего принципиально сложного здесь нет, нужно просто
// аккуратно посчитать координаты
procedure TProcessesInfoForm.FormResize(Sender: TObject);
 var XMed,YMed,Space,Space2,DeltaList:Integer;
  begin
   XMed:=ClientWidth div 2;
   YMed:=ClientHeight div 2;
   // Space содержит расстояние от края формы до элементов
   // управления, а также между элементами управления. Это
   // расстояние равно отступу списка процессов от левого
   // края формы - этот отступ остаётся неизменным при любых
   // изменениях размеров окна. При разрботке программы этот
   // отсутп установлен равным 8, но так как Scaled=True,
   // при запуске программы в системе с другим размером
   // шрифта он может измениться
   Space:=ListProcesses.Left;
   Space2:=Space div 2;
   DeltaList:=ListProcesses.Top-LabelProcesses.Top;
   BtnRefresh.Left:=XMed-Space2-BtnRefresh.Width;
   BtnRefresh.Top:=YMed-Space2-BtnRefresh.Height;
   ListProcesses.Width:=XMed-Space2-ListProcesses.Left;
   ListProcesses.Height:=BtnRefresh.Top-Space-ListProcesses.Top;
   LabelProcessError.BoundsRect:=ListProcesses.BoundsRect;
   LabelModules.Left:=XMed+Space2;
   ListModules.Left:=XMed+Space2;
   ListModules.Width:=ClientWidth-Space-ListModules.Left;
   ListModules.Height:=YMed-Space2-ListModules.Top;
   LabelModuleError.BoundsRect:=ListModules.BoundsRect;
   LabelWindows.Top:=YMed+Space2;
   TreeWindows.Top:=LabelWindows.Top+DeltaList;
   TreeWindows.Width:=ListProcesses.Width;
   TreeWindows.Height:=ClientHeight-Space-TreeWindows.Top;
   LabelParams.Left:=LabelModules.Left;
   LabelParams.Top:=LabelWindows.Top;
   ListParams.Left:=LabelParams.Left;
   ListParams.Top:=TreeWindows.Top;
   ListParams.Width:=ListModules.Width;
   ListParams.Height:=TreeWindows.Height;
   LabelParamsError.BoundsRect:=ListParams.BoundsRect
  end;

procedure TProcessesInfoForm.BtnRefreshClick(Sender: TObject);
 begin
  FillProcessList
 end;

procedure TProcessesInfoForm.FillProcessList;
 var SnapProc:THandle;
     ProcEntry:TProcessEntry32;
     Item:TListItem;
  begin
   CLearAll;
   // Создаём снимок, в котором сохраняем все процессы, а
   // затем в цикле получаем информацию о кажом из этих
   // процессов, перенося её в ListProcesses
   SnapProc:=CreateToolhelp32Snapshot(TH32CS_SnapProcess,0);
   if SnapProc<>Invalid_Handle_Value then
    try
     ProcEntry.dwSize:=SizeOf(TProcessEntry32);
     if Process32First(SnapProc,ProcEntry) then
      repeat
       Item:=ListProcesses.Items.Add;
       Item.Caption:=ProcEntry.szExeFile;
       Item.SubItems.Add(IntToStr(ProcEntry.th32ProcessID));
       Item.SubItems.Add(IntToStr(ProcEntry.th32ParentProcessID));
       Item.SubItems.Add(IntToStr(ProcEntry.cntThreads));
       // Сохраняем PID в поле Data соответствующего
       // элемента списка. Вообще, поле Data имеет тип
       // Pointer, а PID - это целое число, но т.к. оба этих
       // типа 32-битные, их можно приводить друг к другу
       Item.Data:=Pointer(ProcEntry.th32ProcessID)
      until not Process32Next(SnapProc,ProcEntry)
    finally
     CloseHandle(SnapProc)
    end
   else
    begin
     ListProcesses.Visible:=False;
     LabelProcessError.Caption:='Невозможно получить список процессов:'#13#10'Ошибка №'+IntToStr(GetLastError)
    end
  end;

procedure TProcessesInfoForm.ClearParamList;
 var I:Integer;
  begin
   for I:=0 to ListParams.Items.Count-1 do
    ListParams.Items[I].SubItems[0]:='';
   ListParams.Visible:=True
  end;

procedure TProcessesInfoForm.ClearAll;
 begin
  ListProcesses.Items.Clear;
  ListModules.Items.Clear;
  TreeWindows.Items.Clear;
  ClearParamList
 end;

procedure TProcessesInfoForm.FillModuleList(PID:Cardinal);
 var SnapMod:THandle;
     ModEntry:TModuleEntry32;
     Item:TListItem;
     ErrorCode:Integer;
  begin
   // PID=0 у процесса [System Process] (Task manager
   // называет его System Idle Process, или, в русском
   // варианте, "Бездействие системы"). Но функция
   // CreateToolhelp32Snapshot при PID=0 вернёт список
   // модулей текущего процесса. Чтобы при выборе
   // пользователем процесса [System Process] не отображался
   // список модулей данного примера, при PID=0 список не
   // заполняется.
   if PID=0 then
    Exit;
   // Делаем список модулей данного процесса и перебираем их
   // в цикле, аналогично тому, как до этого перебирали
   // процессы
   SnapMod:=CreateToolhelp32Snapshot(TH32CS_SnapModule,PID);
   if SnapMod<>Invalid_Handle_Value then
    try
     ModEntry.dwSize:=SizeOf(TModuleEntry32);
     if Module32First(SnapMod,ModEntry) then
      repeat
       Item:=ListModules.Items.Add;
       Item.Caption:=ModEntry.szModule;
       Item.SubItems.Add(IntToStr(ModEntry.modBaseSize));
       Item.SubItems.Add('$'+IntToHex(Integer(ModEntry.modBaseAddr),8));
       Item.SubItems.Add(ModEntry.szExePath)
      until not Module32Next(SnapMod,ModEntry);
     ListModules.Visible:=True
    finally
     CloseHandle(SnapMod)
    end
   else
    begin
     ListModules.Visible:=False;
     ErrorCode:=GetLastError;
     // Две наиболее вероятные ошибки анализируем отдельно.
     // Ошибка 5 возникает, например, при попытке получить
     // список модулей системной службы, ошибка 87 - при
     // попытке получить модули процесса, который уже
     // завершил работу.
     case ErrorCode of
      5:LabelModuleError.Caption:='Невозможно получить список модулей:'#13#10'Отказано в доступе';
      87:LabelModuleError.Caption:='Невозможно получить список модулей:'#13#10'Неверный параметр'#13#10'Скорее всего, процесс уже завершил работу'
     else
      LabelModuleError.Caption:='Невозможно получить список модулей:'#13#10'Ошибка №'+IntToStr(ErrorCode)
     end
    end
  end;

// Эта глобальная переменная используется для того, чтобы
// отфильтровать окна, не принадлежащие выбранному процессу.
// PID этого процесса сохраняется в переменной PIDNeeded,
// которая затем используется в функции EnumWindowsProc.
// Функция EnumWindowsProc имеет специальный параметр, через
// который можно передавать в неё 32-битные значения, но
// этот параметр уже занят - через него передаётся узел,
// соответствующий родительскому окну.
var PIDNeeded:Cardinal;

function EnumWindowsProc(Wnd:HWnd;ParentNode:TTreeNode):Bool;stdcall;
 var Text:string;
     TextLen:Integer;
     ClassName:array[0..ClassNameLen-1] of Char;
     Node:TTreeNode;
     NodeName:string;
     WndPID:Cardinal;
  begin
   Result:=True;
   // Функция EnumChildWindows позволяет возвращает список
   // не только прямых потомков окна, но и потомков его
   // потомков, поэтому необходимо отсеить все те окна,
   // которые не являются прямыми потомками данного
   if Assigned(ParentNode) and (THandle(ParentNode.Data)<>GetAncestor(Wnd,GA_Parent)) then
    Exit;
   // Затем отсеиваются окна, которые не принадлежат
   // выбранному процессу
   GetWindowThreadProcessID(Wnd,@WndPID);
   if WndPID=PIDNeeded then
    begin
     TextLen:=GetWindowTextLength(Wnd);
     SetLength(Text,TextLen);
     if TextLen>0 then
      GetWindowText(Wnd,PChar(Text),TextLen+1);
     if TextLen>100 then
      Text:=Copy(Text,1,100)+' ...';
     GetClassName(Wnd,ClassName,ClassNameLen);
     ClassName[ClassNameLen-1]:=#0;
     if Text='' then
      NodeName:='Без названия ('+ClassName+')'
     else
      NodeName:=Text+' ('+ClassName+')';
     NodeName:='$'+IntToHex(Wnd,8)+' '+NodeName;
     Node:=ProcessesInfoForm.TreeWindows.Items.AddChild(ParentNode,NodeName);
     Node.Data:=Pointer(Wnd);
     EnumChildWindows(Wnd,@EnumWindowsProc,LParam(Node))
    end
  end;

procedure TProcessesInfoForm.FillWindowList(PID:Cardinal);
 begin
  if PID=0 then
   Exit;
  PIDNeeded:=PID;
  EnumWindows(@EnumWindowsProc,0)
 end;

procedure TProcessesInfoForm.FormCreate(Sender: TObject);
 var I:Integer;
  begin
   ListParams.Items.Add.Caption:='Дескриптор';
   ListParams.Items.Add.Caption:='Название';
   ListParams.Items.Add.Caption:='Оконный класс';
   ListParams.Items.Add.Caption:='Реальный класс';
   ListParams.Items.Add.Caption:='Родительское окно';
   ListParams.Items.Add.Caption:='Окно-владелец';
   ListParams.Items.Add.Caption:='Координаты';
   ListParams.Items.Add.Caption:='Координаты клиентской части';
   ListParams.Items.Add.Caption:='Стиль';
   ListParams.Items.Add.Caption:='Расширенный стиль';
   ListParams.Items.Add.Caption:='Высота горизонтальной рамки';
   ListParams.Items.Add.Caption:='Ширина вертикальной рамки';
   ListParams.Items.Add.Caption:='Состояние';
   ListParams.Items.Add.Caption:='Разрешено';
   for I:=0 to ListParams.Items.Count-1 do
    ListParams.Items[I].SubItems.Add('');
   FillProcessList
  end;

procedure TProcessesInfoForm.FillParamList(Wnd:HWnd);
 var Text,StyleList:string;
     TextLen:DWord;
     ClassName:array[0..ClassNameLen-1] of Char;
     Ancestor:HWnd;
     WindowInfo:TWindowInfo;
     LastError:Integer;

  // Процедуры AddStyle и CheckStyle упрощают рутинные
  // операции при формировании строки с именами констант
  // стиля
  procedure AddStyle(const StyleName:string);
   begin
    if StyleList='' then
     StyleList:=StyleName
    else
     StyleList:=StyleList+' or '+StyleName
   end;

  procedure CheckStyle(Styles,Style:Cardinal;const StyleName:string);
   begin
    if Style and Styles<>0 then
     AddStyle(StyleName)
   end;

   begin
    if IsWindow(Wnd) then
     begin
      ListParams.Items[0].SubItems[0]:='$'+IntToHex(Wnd,8);
      if SendMessageTimeout(Wnd,WM_GetTextLength,0,0,SMTO_Normal or SMTO_AbortIfHung,5000,TextLen)=0 then
       begin
        LastError:=GetLastError;
        if LastError=0 then
         Text:='Приложение не отвечает'
        else
         Text:='Ошибка при поучении длины заголовка: '+IntToStr(LastError)
       end
      else
       begin
        SetLength(Text,TextLen);
        if TextLen>0 then
         if SendMessageTimeout(Wnd,WM_GetText,TextLen+1,LParam(Text),SMTO_Normal or SMTO_AbortIfHung,5000,TextLen)=0 then
          begin
           LastError:=GetLastError;
           if LastError=0 then
            Text:='Приложение не отвечает'
           else
            Text:='Ошибка при поучении заголовка: '+IntToStr(LastError)
          end
       end;
      ListParams.Items[1].SubItems[0]:=Text;
      GetClassName(Wnd,ClassName,ClassNameLen);
      ClassName[ClassNameLen-1]:=#0;
      ListParams.Items[2].SubItems[0]:=ClassName;
      RealGetWindowClass(Wnd,ClassName,ClassNameLen);
      ClassName[ClassNameLen-1]:=#0;
      ListParams.Items[3].SubItems[0]:=ClassName;
      Ancestor:=GetParent(Wnd);
      if Ancestor=0 then
       ListParams.Items[4].SubItems[0]:='Рабочий стол'
      else
       ListParams.Items[4].SubItems[0]:='$'+IntToHex(Ancestor,8);
      ListParams.Items[5].SubItems[0]:='$'+IntToHex(GetWindow(Wnd,GW_Owner),8);
      WindowInfo.cbSize:=SizeOf(TWindowInfo);
      GetWindowInfo(Wnd,WindowInfo);
      ListParams.Items[6].SubItems[0]:='('+IntToStr(WindowInfo.rcWindow.Left)+','+IntToStr(WindowInfo.rcWindow.Top)+') - ('+IntToStr(WindowInfo.rcWindow.Right)+','+IntToStr(WindowInfo.rcWindow.Bottom)+')';
      ListParams.Items[7].SubItems[0]:='('+IntToStr(WindowInfo.rcClient.Left)+','+IntToStr(WindowInfo.rcClient.Top)+') - ('+IntToStr(WindowInfo.rcClient.Right)+','+IntToStr(WindowInfo.rcClient.Bottom)+')';
      // Проверка, присутствует ли соответствующий флаг в
      // стиле окна, и, если присутствует, добавление имени
      // флага в строку
      CheckStyle(WindowInfo.dwStyle,WS_Popup,'WS_Popup');
      CheckStyle(WindowInfo.dwStyle,WS_Child,'WS_Child');
      CheckStyle(WindowInfo.dwStyle,WS_Minimize,'WS_Minimize');
      CheckStyle(WindowInfo.dwStyle,WS_Visible,'WS_Visible');
      CheckStyle(WindowInfo.dwStyle,WS_Disabled,'WS_Disabled');
      CheckStyle(WindowInfo.dwStyle,WS_ClipSiblings,'WS_ClipSiblings');
      CheckStyle(WindowInfo.dwStyle,WS_ClipChildren,'WS_ClipChildren');
      // Для классов RichEdit и RichEdit_Class определён
      // флаг WS_SelectionBar, значения которого совпадают со
      // значением "общего" флага WS_Maximize, т.к. для этих
      // классов флаг WS_Maximize всё равно не имеет смысла.
      // Поэтому для этих классов проверка на WS_Maximize не
      // выполняется
      if (AnsiCompareText(ClassName,'RichEdit')<>0) and (AnsiCompareText(ClassName,'RichEdit_Class')<>0) then
       CheckStyle(WindowInfo.dwStyle,WS_Maximize,'WS_Maximize');
      // Константа WS_Caption является комбинацией констант
      // WS_Border и WS_DlgFrame, поэтому сначала проверяем,
      // пристуствуют ли оба эти флага в стиле, и, если
      // присутствуют, добавляем к строке 'WS_Caption', а
      // если нет, разбираемся с каждым из этих флагов
      // по отдельности.
      if WindowInfo.dwStyle and WS_Caption=WS_Caption then
       AddStyle('WS_Caption')
      else
       begin
        CheckStyle(WindowInfo.dwStyle,WS_Border,'WS_Border');
        CheckStyle(WindowInfo.dwStyle,WS_DlgFrame,'WS_DlgFrame')
       end;
      CheckStyle(WindowInfo.dwStyle,WS_VScroll,'WS_VScroll');
      CheckStyle(WindowInfo.dwStyle,WS_HScroll,'WS_HScroll');
      CheckStyle(WindowInfo.dwStyle,WS_SysMenu,'WS_SysMenu');
      CheckStyle(WindowInfo.dwStyle,WS_ThickFrame,'WS_ThickFrame');
      // Флаги WS_MinimizeBox и WS_Group, а также
      // WS_MаximizeBox и WS_TabStop имеют одинаковые
      // значения. Как интерпретируются эти константы,
      // зависит от того, установлен ли флаг WS_SysMenu.
      if WindowInfo.dwStyle and WS_SysMenu>0 then
       begin
        CheckStyle(WindowInfo.dwStyle,WS_MinimizeBox,'WS_MinimizeBox');
        CheckStyle(WindowInfo.dwStyle,WS_MaximizeBox,'WS_MaximizeBox')
       end
      else
       begin
        CheckStyle(WindowInfo.dwStyle,WS_Group,'WS_Group');
        CheckStyle(WindowInfo.dwStyle,WS_TabStop,'WS_TabStop')
       end;
      if AnsiCompareText(ClassName,'Button')=0 then
       begin
        // Первые четыре бита стиля кнопки кодируют её вид.
        // Соответствующие константы не являются
        // взаимоисключающими, поэтому их нужно проверять с
        // помощью маскирования соответствующих бит.
        case WindowInfo.dwStyle and $F of
         BS_PushButton:AddStyle('BS_PushButton');
         BS_DefPushButton:AddStyle('BS_DefPushButton');
         BS_CheckBox:AddStyle('BS_CheckBox');
         BS_AutoCheckBox:AddStyle('BS_AutoCheckBox');
         BS_RadioButton:AddStyle('BS_RadioButton');
         BS_AutoRadioButton:AddStyle('BS_AutoRadioButton');
         BS_3State:AddStyle('BS_3State');
         BS_Auto3State:AddStyle('BS_Auto3State');
         BS_GroupBox:AddStyle('BS_GroupBox');
         BS_UserButton:AddStyle('BS_UserButton');
         BS_OwnerDraw:AddStyle('BS_OwnerDraw')
        end;
        CheckStyle(WindowInfo.dwStyle,BS_LeftText,'BS_LeftText');
        CheckStyle(WindowInfo.dwStyle,BS_Icon,'BS_Icon');
        CheckStyle(WindowInfo.dwStyle,BS_Bitmap,'BS_Bitmap');
        if WindowInfo.dwStyle and BS_Center=BS_Center then
         AddStyle('BS_Center')
        else
         begin
          CheckStyle(WindowInfo.dwStyle,BS_Left,'BS_Left');
          CheckStyle(WindowInfo.dwStyle,BS_Right,'BS_Right')
         end;
        if WindowInfo.dwStyle and BS_VCenter=BS_VCenter then
         AddStyle('BS_VCenter')
        else
         begin
          CheckStyle(WindowInfo.dwStyle,BS_Top,'BS_Top');
          CheckStyle(WindowInfo.dwStyle,BS_Bottom,'BS_Bottom')
         end;
        CheckStyle(WindowInfo.dwStyle,BS_PushLike,'BS_PushLike');
        CheckStyle(WindowInfo.dwStyle,BS_Multiline,'BS_Multiline');
        CheckStyle(WindowInfo.dwStyle,BS_Notify,'BS_Notify');
        CheckStyle(WindowInfo.dwStyle,BS_Flat,'BS_Flat')
       end
      else if AnsiCompareText(ClassName,'ComboBox')=0 then
       begin
        case WindowInfo.dwStyle and 3 of
         CBS_Simple:AddStyle('CBS_Simple');
         CBS_DropDown:AddStyle('CBS_DropDown');
         CBS_DropDownList:AddStyle('CBS_DropDownList')
        end;
        CheckStyle(WindowInfo.dwStyle,CBS_OwnerDrawFixed,'CBS_OwnerDrawFixed');
        CheckStyle(WindowInfo.dwStyle,CBS_OwnerDrawVariable,'CBS_OwnerDrawVariable');
        CheckStyle(WindowInfo.dwStyle,CBS_AutoHScroll,'CBS_AutoHScroll');
        CheckStyle(WindowInfo.dwStyle,CBS_OEMConvert,'CBS_OEMConvert');
        CheckStyle(WindowInfo.dwStyle,CBS_Sort,'CBS_Sort');
        CheckStyle(WindowInfo.dwStyle,CBS_HasStrings,'CBS_HasStrings');
        CheckStyle(WindowInfo.dwStyle,CBS_NoIntegralHeight,'CBS_NoIntegralHeight');
        CheckStyle(WindowInfo.dwStyle,CBS_DisableNoScroll,'CBS_DisableNoScroll');
        CheckStyle(WindowInfo.dwStyle,CBS_UpperCase,'CBS_UpperCase');
        CheckStyle(WindowInfo.dwStyle,CBS_LowerCase,'CBS_LowerCase')
       end
      else if AnsiCompareText(ClassName,'Edit')=0 then
       begin
        CheckStyle(WindowInfo.dwStyle,ES_Center,'ES_Center');
        CheckStyle(WindowInfo.dwStyle,ES_Right,'ES_Right');
        CheckStyle(WindowInfo.dwStyle,ES_Multiline,'ES_Multiline');
        CheckStyle(WindowInfo.dwStyle,ES_Center,'ES_Center');
        CheckStyle(WindowInfo.dwStyle,ES_UpperCase,'ES_UpperCase');
        CheckStyle(WindowInfo.dwStyle,ES_LowerCase,'ES_LowerCase');
        CheckStyle(WindowInfo.dwStyle,ES_Password,'ES_Password');
        CheckStyle(WindowInfo.dwStyle,ES_AutoVScroll,'ES_AutoVScroll');
        CheckStyle(WindowInfo.dwStyle,ES_AutoHScroll,'ES_AutoHScroll');
        CheckStyle(WindowInfo.dwStyle,ES_NoHideSel,'ES_NoHideSel');
        CheckStyle(WindowInfo.dwStyle,ES_OEMConvert,'ES_OEMConvert');
        CheckStyle(WindowInfo.dwStyle,ES_ReadOnly,'ES_ReadOnly');
        CheckStyle(WindowInfo.dwStyle,ES_WantReturn,'ES_WantReturn');
        CheckStyle(WindowInfo.dwStyle,ES_Number,'ES_Number')
       end
      else if AnsiCompareText(ClassName,'Listbox')=0 then
       begin
        CheckStyle(WindowInfo.dwStyle,LBS_Notify,'LBS_Notify');
        CheckStyle(WindowInfo.dwStyle,LBS_Sort,'LBS_Sort');
        CheckStyle(WindowInfo.dwStyle,LBS_NoRedraw,'LBS_NoRedraw');
        CheckStyle(WindowInfo.dwStyle,LBS_MultipleSel,'LBS_MultipleSel');
        CheckStyle(WindowInfo.dwStyle,LBS_OwnerDrawFixed,'LBS_OwnerDrawFixed');
        CheckStyle(WindowInfo.dwStyle,LBS_OwnerDrawVariable,'LBS_OwnerDrawVariable');
        CheckStyle(WindowInfo.dwStyle,LBS_HasStrings,'LBS_HasStrings');
        CheckStyle(WindowInfo.dwStyle,LBS_UseTabStops,'LBS_UseTabStops');
        CheckStyle(WindowInfo.dwStyle,LBS_NoIntegralHeight,'LBS_NoIntegralHeight');
        CheckStyle(WindowInfo.dwStyle,LBS_MultiColumn,'LBS_MultiColumn');
        CheckStyle(WindowInfo.dwStyle,LBS_WantKeyboardInput,'LBS_WantKeyboardInput');
        CheckStyle(WindowInfo.dwStyle,LBS_ExtendedSel,'LBS_ExtendedSel');
        CheckStyle(WindowInfo.dwStyle,LBS_DisableNoScroll,'LBS_DisableNoScroll');
        CheckStyle(WindowInfo.dwStyle,LBS_NoData,'LBS_NoData');
        CheckStyle(WindowInfo.dwStyle,LBS_NoSel,'LBS_NoSel')
       end
      else if (AnsiCompareText(ClassName,'RichEdit')=0) or (AnsiCompareText(ClassName,'RichEdit_Class')=0) then
       begin
        CheckStyle(WindowInfo.dwStyle,ES_Center,'ES_Center');
        CheckStyle(WindowInfo.dwStyle,ES_Right,'ES_Right');
        CheckStyle(WindowInfo.dwStyle,ES_Multiline,'ES_Multiline');
        CheckStyle(WindowInfo.dwStyle,ES_Center,'ES_Center');
        CheckStyle(WindowInfo.dwStyle,ES_Password,'ES_Password');
        CheckStyle(WindowInfo.dwStyle,ES_AutoVScroll,'ES_AutoVScroll');
        CheckStyle(WindowInfo.dwStyle,ES_AutoHScroll,'ES_AutoHScroll');
        CheckStyle(WindowInfo.dwStyle,ES_NoHideSel,'ES_NoHideSel');
        CheckStyle(WindowInfo.dwStyle,ES_ReadOnly,'ES_ReadOnly');
        CheckStyle(WindowInfo.dwStyle,ES_WantReturn,'ES_WantReturn');
        CheckStyle(WindowInfo.dwStyle,ES_Number,'ES_Number');
        CheckStyle(WindowInfo.dwStyle,ES_DisableNoScroll,'ES_DisableNoScroll');
        CheckStyle(WindowInfo.dwStyle,ES_EX_NoCallOLEInit,'ES_EX_NoCallOleInit');
        CheckStyle(WindowInfo.dwStyle,ES_NoIME,'ES_NoIME');
        CheckStyle(WindowInfo.dwStyle,ES_SelfIME,'ES_SelfIME');
        CheckStyle(WindowInfo.dwStyle,ES_Sunken,'ES_Sunken');
        CheckStyle(WindowInfo.dwStyle,ES_Vertical,'ES_Vertical');
        CheckStyle(WindowInfo.dwStyle,ES_NoOLEDragDrop,'ES_NoOLEDragDrop');
        CheckStyle(WindowInfo.dwStyle,ES_SelectionBar,'ES_SelectionBar')
       end
      else if AnsiCompareText(ClassName,'ScrollBar')=0 then
       begin
        if (WindowInfo.dwStyle and SBS_SizeBox<>0) or (WindowInfo.dwStyle and SBS_SizeGrip<>0) then
         begin
          CheckStyle(WindowInfo.dwStyle,SBS_SizeBox,'SBS_SizeBox');
          CheckStyle(WindowInfo.dwStyle,SBS_SizeGrip,'SBS_SizeGrip');
          CheckStyle(WindowInfo.dwStyle,SBS_TopAlign,'SBS_SizeBoxTopLeftAlign');
          CheckStyle(WindowInfo.dwStyle,SBS_BottomAlign,'SBS_SizeBoxBottomRightAlign')
         end
        else
         if WindowInfo.dwStyle and SBS_Vert=0 then
          begin
           AddStyle('SBS_Horz');
           CheckStyle(WindowInfo.dwStyle,SBS_TopAlign,'SBS_TopAlign');
           CheckStyle(WindowInfo.dwStyle,SBS_BottomAlign,'SBS_BottomAlign')
          end
         else
          begin
           AddStyle('SBS_Vert');
           CheckStyle(WindowInfo.dwStyle,SBS_LeftAlign,'SBS_LeftAlign');
           CheckStyle(WindowInfo.dwStyle,SBS_RightAlign,'SBS_RighAlign')
          end
       end
      else if AnsiCompareText(ClassName,'Static')=0 then
       begin
        case WindowInfo.dwStyle and SS_TypeMask of
         SS_Left:AddStyle('SS_Left');
         SS_Center:AddStyle('SS_Center');
         SS_Right:AddStyle('SS_Right');
         SS_Icon:AddStyle('SS_Icon');
         SS_BlackRect:AddStyle('SS_BlackRect');
         SS_GrayRect:AddStyle('SS_GrayRect');
         SS_WhiteRect:AddStyle('SS_WhiteRect');
         SS_BlackFrame:AddStyle('SS_BlackFrame');
         SS_GrayFrame:AddStyle('SS_GrayFrame');
         SS_WhiteFrame:AddStyle('SS_WhiteFrame');
         SS_UserItem:AddStyle('SS_UserItem');
         SS_Simple:AddStyle('SS_Simple');
         SS_LeftNoWordWrap:AddStyle('SS_LeftNoWordWrap');
         SS_OwnerDraw:AddStyle('SS_OwnerDraw');
         SS_Bitmap:AddStyle('SS_Bitmap');
         SS_EnhMetaFile:AddStyle('SS_EnhMetaFile');
         SS_EtchedHorz:AddStyle('SS_EtchedHorz');
         SS_EtchedVert:AddStyle('SS_EtchedVert');
         SS_EtchedFrame:AddStyle('SS_EtchedFrame')
        end;
        CheckStyle(WindowInfo.dwStyle,SS_NoPrefix,'SS_NoPrefix');
        CheckStyle(WindowInfo.dwStyle,SS_Notify,'SS_Notify');
        CheckStyle(WindowInfo.dwStyle,SS_CenterImage,'SS_CenterImage');
        CheckStyle(WindowInfo.dwStyle,SS_RightJust,'SS_RightJust');
        CheckStyle(WindowInfo.dwStyle,SS_RealSizeImage,'SS_RealSizeImage');
        CheckStyle(WindowInfo.dwStyle,SS_Sunken,'SS_Sunken');
        if WindowInfo.dwStyle and SS_WordEllipsis=SS_WordEllipsis then
         AddStyle('SS_WordEllipsis')
        else
         begin
          CheckStyle(WindowInfo.dwStyle,SS_EndEllipsis,'SS_EndEllipsis');
          CheckStyle(WindowInfo.dwStyle,SS_PathEllipsis,'SS_PathEllipsis')
         end
       end;
      ListParams.Items[8].SubItems[0]:='$'+IntToHex(WindowInfo.dwStyle,8)+' ('+StyleList+')';
      StyleList:='';
      CheckStyle(WindowInfo.dwExStyle,WS_EX_DlgModalFrame,'WS_EX_DlgModalFrame');
      CheckStyle(WindowInfo.dwExStyle,WS_EX_NoParentNotify,'WS_EX_NoParentNotify');
      CheckStyle(WindowInfo.dwExStyle,WS_EX_Topmost,'WS_EX_Topmost');
      CheckStyle(WindowInfo.dwExStyle,WS_EX_AcceptFiles,'WS_EX_AcceptFiles');
      CheckStyle(WindowInfo.dwExStyle,WS_EX_Transparent,'WS_EX_Transparent');
      CheckStyle(WindowInfo.dwExStyle,WS_EX_MDIChild,'WS_EX_MDIChild');
      CheckStyle(WindowInfo.dwExStyle,WS_EX_ToolWindow,'WS_EX_ToolWindow');
      CheckStyle(WindowInfo.dwExStyle,WS_EX_WindowEdge,'WS_EX_WindowEdge');
      CheckStyle(WindowInfo.dwExStyle,WS_EX_ClientEdge,'WS_EX_ClientEdge');
      CheckStyle(WindowInfo.dwExStyle,WS_EX_ContextHelp,'WS_EX_ContextHelp');
      CheckStyle(WindowInfo.dwExStyle,WS_EX_Right,'WS_EX_Right');
      CheckStyle(WindowInfo.dwExStyle,WS_EX_RTLReading,'WS_EX_RTLReading');
      CheckStyle(WindowInfo.dwExStyle,WS_EX_LeftScrollbar,'WS_EX_LeftScrollbar');
      CheckStyle(WindowInfo.dwExStyle,WS_EX_ControlParent,'WS_EX_ControlParent');
      CheckStyle(WindowInfo.dwExStyle,WS_EX_StaticEdge,'WS_EX_StaticEdge');
      CheckStyle(WindowInfo.dwExStyle,WS_EX_AppWindow,'WS_EX_AppWindow');
      ListParams.Items[9].SubItems[0]:='$'+IntToHex(WindowInfo.dwExStyle,8)+' ('+StyleList+')';
      ListParams.Items[10].SubItems[0]:=IntToStr(WindowInfo.cxWindowBorders);
      ListParams.Items[11].SubItems[0]:=IntToStr(WindowInfo.cyWindowBorders);
      if not IsWindowVisible(Wnd) then
       ListParams.Items[12].SubItems[0]:='Невидимое'
      else if IsIconic(Wnd) then
       ListParams.Items[12].SubItems[0]:='Свёрнутое'
      else if IsZoomed(Wnd) then
       ListParams.Items[12].SubItems[0]:='Развёрнутое'
      else
       ListParams.Items[12].SubItems[0]:='Нормальное';
      if IsWindowEnabled(Wnd) then
       ListParams.Items[13].SubItems[0]:='Да'
      else
       ListParams.Items[13].SubItems[0]:='Нет'
     end
    else
     ListParams.Visible:=False
   end;

procedure TProcessesInfoForm.TreeWindowsChange(Sender:TObject;Node:TTreeNode);
 begin
  ClearParamList;
  if Assigned(Node) then
   FillParamList(HWnd(Node.Data))
 end;

procedure TProcessesInfoForm.ListProcessesSelectItem(Sender:TObject;Item:TListItem;Selected:Boolean);
 begin
  ListModules.Items.Clear;
  TreeWindows.Items.Clear;
  ClearParamList;
  if Selected then
   begin
    FillModuleList(Cardinal(ListProcesses.Selected.Data));
    FillWindowList(Cardinal(ListProcesses.Selected.Data))
   end
 end;

end.
