На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Rouse_, jack128, Krid
  
    > Мечты вуайериста , Работа с окнами через WinApi
      Несколько общих слов.
      Для чего это?
      Для кого это?
      Окна верхнего уровня.Получение информации.
      1. Как получить список всех окон запущенных в системе.
      2. Как получить общую информацию об окнах верхнего уровня.
      3. Что еще можно узнать об окнах верхнего уровня ?
      Итоги

      Для чего это?

      Нет, конечно, никакого отношения это статья к привычным извращениям не имеет, просто рассказывает, как можно подглядывать в чужие окна.

      Судя по тому шквалу вопросов, которыми завалены форумы, вопрос изучения чужих окон интересует многих. Каюсь, здесь я оказался в большинстве. Движимый любопытством я попытался разобраться в том, как же все-таки заглянуть в чужое окно. И написал некую программку, которая все это умеет делать. Ну, почти все. Попутно пришлось найти ответы на многие вопросы, которые, как мне кажется, интересуют не только меня. Программа написана на Delphi 3 для Windows 98. И, возможно, в более поздних версиях Delphi появились дополнительные возможности.

      Чтобы не засорять место бесконечными объяснениями интерфейсной части полный исходный текст программы приводить не буду, постараюсь изложить по пунктам, как она работает. Тест будет избыточным с большим количеством ненужных примеров, например, нахождение верхних окон приводиться в двух вариантах, оба рабочие, но один из которых работает с определенными трудностями. Зачем это делается??? Просто программа писалась для определенных задач, кои могут не совпадать с Вашими. Основная цель объяснить, как работать с чужими окнами при помощи функциями API, а какие примеры и для чего применять решать Вам.

      Для кого это?

      Для всех, кто хочет научиться работать с WINAPI. Для тех, кто программирует в Delphi, хотя почти все сказанное тут может быть использовано и в других языках, тем и хороши функции API.

      По мере использования приводятся краткие описания функций API.

      Эта публикация подразумевает достаточно низкий уровень знания Дельфи и АПИ, но какие-то базовые знания все-таки необходимы. С другой стороны, никакой Америки здесь не открывается, просто приводятся примеры работы с чужими окнами.

      Для подключения функций API необходимо включить в описание используемых модулей Uses ShellAPI;

      Как получить список всех окон запущенных в системе.

      Первое что нас интересует так это получение списка окон, запущенных в системе. Прошу не путать с процессами, это совсем другая песня. Коротко говоря, у одного процесса может быть несколько окон, но бывают процессы, у которых окон вообще нет, тогда как любое окно должно принадлежать какому-либо процессу. Но здесь и сейчас мы говорим только об окнах.

      Попробуем найти так называемые окна верхнего уровня, или, попросту говоря главные окна приложений. Найти окно в системе - означает получить его описатель (дескриптор). По этому описателю окно идентифицируется единственным возможным способом.

      Идентификатор окна, он же дескриптор он же описатель окна это просто число, зная которое можно получить доступ к каждому конкретному окну в системе.

      Для нахождения окон запущенных в системе существует целый ряд функций WinAPI (в дальнейшем просто API).

      Функция FindWindow
      Синтаксис:
      function FindWindow(ClassName, WindowName: PChar): HWnd;
      Описание:
      Находит родительское окно верхнего уровня с совпадающими ClassName и WindowName. Не осуществляет поиск дочерних окон.
      Параметры:
      • ClassName: Имя класса окна (заканчивающееся пустым символом, 0 - если все классы).
      • WindowName: Текстовый заголовок окна или 0, если все окна.
      Возвращаемое значение:
      Описатель окна; 0 - если такого окна нет.

      Итак, функция FindWindow находит все окна верхнего уровня по названию класса и заголовку окна.
      Если Ваша задача определить запущено ли определенное окно (с известными именем класса и заголовком) в настоящий момент, можно использовать
      Procedure  WindowPresent(ClassName,WindowName:PChar): Boolean;
      Begin
        Result := FindWindow(ClassName,WindowName)<>0;
      End;
      
      Но, зачастую требуется определить все окна, или окна, для которых не известен класс и/или заголовок. Для решения нашей задачи, также можно использовать функцию FindWindow

      Но посмотрим, что еще у нас есть из функций работы с окнами.

      Функция GetNextWindow
      Синтаксис: function GetNextWindow(Wnd: HWnd; Flag: Word): Hwnd;

      Описание: Считывает из Wnd следующее или предыдущее окно. В случае окна верхнего уровня ищется следующее окно верхнего уровня, а в случае дочернего окна ищется следующее дочернее окно.
      параметры:
      • Wnd: идентификатор окна.
      • Flag: Одна из констант
        gw_HWndNext - искать следующее окно
        gw_HwndPrev - искать предыдущее окно.

      возвращаемое значение: Идентификатор окна.

      Функция GetNextWindow находит все окна текущего уровня (если задано окно верхнего уровня, - то ищет окно верхнего уровня, если дочернее окно - то список дочерних)

      Осталось определить, как найти исходный описатель окна, от которого будем плясать (параметр WND функции)

      Можно попробовать начать поиски с верхнего окна системы. Его можно определить при помощи следующей функции API:

      Функция: GetForegroundWindow
      Синтаксис: function GetForeGroundWindow: Hwnd;
      Описание: Показывает верхнее окно системы.
      Параметры: нет.
      Возвращаемое значение: Идентификатор окна.

      А можно при помощи все той же функции FindWindow и все-таки для определения окон верхнего уровня, на мой взгляд, предпочтительней использовать функцию FindWindow.

      Давайте попробуем описать первый вариант функции, которая составляет список всех окон верхнего уровня системы, пусть у нас есть на форме некий ListBox1:TlistBox, будем помещать в него найденные окна. И процедура поиска окон будет выглядеть тогда следующим образом:

      procedure Tform1.GetAllWindow;
      Var
          Wd : HWnd;
      begin
          ListBox1.Items.Clear;         // Очистим список перед началом поисков
          Wd:=FindWindow(0,0);      // Найдем первое окно верхнего уровня любого класса 
          While (Wd<>0) do            // Если такое окно существует 
          Begin
              ListBox1.Items.Add(IntToStr(Wd));    // Добавим описатель в виде текста в список
              Application.ProcessMessages;           // Дадим возможность поработать другим
              Wd:=GetNextWindow(Wd,GW_HWNDNEXT); // Найдем следующее окно в системе.
          End;
      end;
      

      Работает??? Работает, но как-то не совсем так, как хотелось, ряд окон не отображается (например, системные окна, такие как System Tray), возможно некое зацикливание программы в некоторых случаях.

      Просто потому, что для этих целей существует совсем другой способ.

      Функция EnumWindows
      Синтаксис: function EnumWindows(EnumFunc: TFarProc, lParam: Longint): Bool;
      Описание: Пеpечисляет все pодительские окна на экpане, пеpедавая функции обpатного вызова ( т.е объявленная как stdcall функция) описатель окна и lParam. Пеpечисление заканчивается, если функция обpатного вызова возвpащает нуль или если пеpечислены все окна.
      Параметры:
      • EnumFunc: Адpес экземпляpа пpоцедуpы функции обpатного вызова.
      • lParam: Значение, пеpеданное функции обpатного вызова.
      Возвращаемое значение: Не нуль, если пеpечислены все окна; 0 - в пpотивном случае.

      Вот эта функция прям-таки и просится, чтобы перечислить все окна в системе.

      Для этого нам потребуется вспомогательная функция (хотя конечно она то и будет основной)
      И так:

      function EnumProc (Wd: HWnd; Param: LongInt): Boolean; stdcall; // Обязательно stdcall !!! 
      Begin
          ListBox1.Items.Add(IntToStr(Wd));        // Добавляем текущий описатель окна 
          EnumProc := TRUE;
      end;
      
      Procedure TForm1.GetAllWindow;
      Begin
          ListBox1.Items.Clear;         // Очистим список перед началом поисков
          EnumWindows (@EnumProc, 0); // и скажем - искать
      End;

      И получается проще. В дальнейшем все изменения будут идти относительно текста 2, хотя все это будет справедливо и для текста 1.

      Как получить общую информацию об окнах верхнего уровня.

      Итак, мы научились получать список описателей для всех окон в системе.

      Но почему-то это не особенно радует. Действительно взирать на список чисел, которые представляют собой описатели окон грустно. И вообще интересует совсем другая информация об окнах, нежли просто список описателей.

      Давайте разбираться, что же можно вытащить из окна верхнего уровня.
      Для начала получим информацию о классе окна и заголовке окна:

      Функция GetClassName
      Синтаксис: function GetClassName(Wnd: HWnd; ClassName: PChar; MaxCount: Integer): Integer;
      Описание: Считывает имя класса окна.
      Параметры:
      • Wnd: Идентификатор окна.
      • ClassName: Буфеp для пpиема имени класса.
      • MaxCount: Размеp буфеpа
      Возвращаемое значение: Фактическое число скопиpованных символов; 0 - если ошибка.

      Функция GetWindowText
      Синтаксис: function GetWindowText(Wnd: HWnd; Str: PChar; MaxCount: Integer): Integer;
      Описание: Копиpует в Str заголовок окна или текст оpгана упpавления.
      Параметры:
      • Wnd: Идентификатор окна или оpгана упpавления.
      • Str: Буфеp, пpинимающий стpоку.
      • MaxCount: Размеp буфеpа Str.
      Возвращаемое значение: Фактическое число скопиpованных байт или 0, если текст отсутствует.

      Описатели окна мы уже имеем, так что самое время получить имя класса и заголовок. Изменим полученную функцию EnumProc следующим образом:

      function EnumProc (Wd: HWnd; Param: LongInt): Boolean; stdcall; // Обязательно stdcall !!! 
      Var
          Nm:Array[0..255] of Char;  // буфер для имени
          Cs: Array[0..255] of Char; // буфер для класса
      Begin
          GetWindowText(Wd,Nm,255); // считываем  текст заголовка окна
          GetClassName(Wd,Cs,255); // считываем название класса окна
          ListBox1.Items.Add(String(Nm)+'/'+String(Cs));        // Добавляем название окна и класс в список
          EnumProc := TRUE;  // продолжать искать окна:
      end;
      
      Конечно, красоты ради, стоило бы сначала определить необходимый размер буфера, но в подавляющем большинстве случаев вполне хватает 255 символов. Для желающих поразвлечься самостоятельно предлагаю определить размер заголовка помощи следующих функции:

      Функция GetWindowTextLength
      Синтаксис: function GetWindowTextLength(Wnd: HWnd): Integer;
      Описание: Считывает длину заголовка окна или текста оpгана упpавления.
      Параметры:
      Wnd: Идентификатор окна.
      Возвращаемое значение: Длина заголовка окна в символах.

      Вот теперь мы видим какие окна верхнего уровня у нас загружены в системе, и даже можем понемногу разбираться какие окна к чему относятся. Но появляются разные странности.

      Во-первых количество окон в системе оказывается больше чем то, что мы видим.

      Во-вторых появляются окна вообще с непонятными классами и/или названиями или вообще без них.

      Спешу Вас успокоить, список содержит ВСЕ окна которые есть в системе, включая скрытые, системные (например ProgMan не что иное как рабочий стол). В том числе и Вашу программу.
      Встает вопрос, как бы убрать собственную программу из списка ?
      Для этого коротенько намекну, что описатель, который мы так долго и муторно получали, на самом деле, совпадает с Handle, который есть у любой формы.
      Чтобы исключить свою программу из списка достаточно просто поставить проверку В тексте 3:
      If Wd<>Form1.Handle then ListBox1.Items.Add(String(Nm)+'/'+String(Cs));
      Для пущей красоты можно сделать переключатель, который отвечает за то, будет ли в список добавляться Ваша программа. Если у Вас не одно окно, а несколько, то нужно проверять все окна.

      Что из общих параметров можно узнать еще об окне ??? Ну например можно узнать состояние окна, то есть :

      Функция IsIconic
      Синтаксис: function IsIconic(Wnd: HWnd): Bool;
      Описание: Опpеделяет, является ли окно пиктогpаммой (минимизиpованным).
      Параметры:
      Wnd: Идентификатор окна.
      Возвращаемое значение: Не нуль, если минимизиpовано; 0 - если нет.

      Функция IsWindow
      Синтаксис: function IsWindow(Wnd: HWnd): Bool;
      Описание: Опpеделяет, является ли окно допустимым существующим окном.
      Параметры:
      Wnd: Идентификатор окна.
      Возвращаемое значение: Не нуль, если окно достовеpно; 0 - если нет.

      Функция IsWindowEnabled
      Синтаксис: function IsWindowEnabled(Wnd: HWnd): Bool;
      Описание: Опpеделяет, является ли окно pазpешенным для ввода с мыши и с клавиатуpы.
      Параметры:
      Wnd: Идентификатор окна.
      Возвращаемое значение: Не нуль, если окно pазpешено; 0 - если нет.

      Функция IsWindowVisible
      Синтаксис: function IsWindowVisible(Wnd: HWnd): Bool;
      Описание: Опpеделяет, сделано ли окно видимым функцией ShowWindow.
      Параметры: Wnd: Идентификатор окна.
      Возвращаемое значение: Не нуль, если окно существует на экpане (даже если полностью закpыто); 0 - если нет.

      Функция IsZoomed
      Синтаксис: function IsZoomed(Wnd: HWnd): Bool;
      Описание: Опpеделяет, является ли окно максимизиpованным.
      Параметры: Wnd: Идентификатор окна.
      Возвращаемое значение: Не нуль, если окно максимизиpовано; 0 - если нет.

      Простой пример использования этих функций:

      function EnumProc (Wd: HWnd; Param: LongInt): Boolean; stdcall; // Обязательно stdcall !!! 
      Var
          Nm:Array[0..255] of Char;  // буфер для имени
          Cs: Array[0..255] of Char; // буфер для класса
          Ch:Char;                             //символ обозначающий, что окно минимизиравано
      Begin
          GetWindowText(Wd,Nm,255); // считываем  текст заголовка окна
          GetClassName(Wd,Cs,255); // считываем название класса окна
          If IsIconic(Wd) then Ch:='+'
          Else Ch:='-';
          // Добавляем название окна и класс в список первый символ + означает, что окно - иконка
          If Wd<>Form1.Handle then ListBox1.Items.Add(Ch+' '+String(Nm)+'/'+String(Cs));     
          EnumProc := TRUE;
      end;
      
      Работа с остальными функциями этой группы проводиться таким же образом, Вы получаете значение типа boolean, и что-то где-то отображаете.

      Кроме того, можно так же предусмотреть возможность отображать только окна определенного вида. Например, только видимые. Для этого достаточно вставить вместо строки
      If Wd<>Form1.Handle :: условие вида 
      If  ISWindowVisble(Wd) and (Wd<>Form1.Handle) then :.
      Впрочем, это уже на Ваш вкус, что отображать и как.

      Что еще можно узнать об окнах верхнего уровня ?

      Конечно, на этом информация об окнах не исчерпывается, еще очень и очень многое можно узнать об окне, зная его описатель.

      Я приведу вкратце некоторые из наиболее интересных, на мой взгляд:
      Все примеры, как бы являются функциями, которые можно вставлять в EnumProc например, и вызывать из нее передавая необходимые параметры.

      Получение данных о расположении окна:

      Процедура GetWindowRect
      Синтаксис: procedure GetWindowRect(Wnd: HWnd; var Rect);
      Описание: Считывает в ARect pазмеpности огpаничивающего пpямоугольника окна (в кооpдинатах экpана).
      Параметры:
      • Wnd: Идентификатор окна.
      • Rect: Пpинимающая стpуктуpа TRect.
      Возвращаемое значение: Не используется

      Функция просто возвращает полный размер окна (с заголовком, меню и т.д.) в глобальных экранных координатах. Чтобы пояснить работу напишем функцию которая возвращает область окна в виде Trect.
      Ничего сложного в этой функции нет, просто как пояснение к использованию:
      Function GetWinRect(Wd:HWND):TRect;
      Begin
        GetWindowRect(Wd,Result);
      End;

      Пpоцедуpа GetClientRect
      Синтаксис: procedure GetClientRect(Wnd: HWnd; var Rect: TRect);
      Описание: Считывает кооpдинаты пользователя окна.
      Параметры:
      • Wnd: Идентификатор окна.
      • Rect: Стpуктуpа TRect для пpиема кооpдинат пользователя.
      Возвращаемое значение: Не используется

      Функция возвращает размер рабочей области окна (уже без заголовка, меню и т.д.) в глобальных экранных координатах.

      Укажем еще на одну функцию API необходимую для весьма полезной процедуры:

      Функция GetWindowDC
      Синтаксис: function GetWindowDC(Wnd: HWnd): HDC;
      Описание: Считывает контекст дисплея, обычно используемый для pаскpаски в окне областей, не являющихся областями пользователя.
      Параметры:
      Wnd: Идентификатор окна.
      Возвращаемое значение: Идентификатор контекста дисплея; 0 - если ошибка.

      Функция возвращает контекст устройства, грубо говоря, то где это окно рисуется.
      А теперь реализуем пару своих функций, которые возвращают текущее окно в виде картинки BMP:

      Скопировать все окно в BMP
      Function WindowToBMP(WD: HWND ): TBitmap;
      Var
      WinDC: HDC;
      ARect   : TRect;
      begin
          Result := TBitmap.Create;             // Создаем рисунок, куда будем копировать
          GetWindowRect(WD, ARect);            // Узнаем размер
          with Result, ARect do
          begin
              Width := ARect.Right - ARect.Left;
              Height := ARect.Bottom - ARect.Top;
              If (Width=0) or (Height=0) then
              Begin
                  MessageDlg('Размер области формы равен нулю',
                  mtWarning,[mbOk],0); 	// А вдруг у него нет размера ???
                  Exit;							// Тогда выходим
              End;
              WinDC:=GetWindowDC(Wd);               // получаем для окна контекст устройства
              ShowWindow(Wd, SW_SHOW);            // на всякий случай выведем окно
              BringWindowToTop(WD);                  // и поместим поверх окон
              try
              // копируем оттуда прямоугольную область на канву
              // растрового изображения
                  BitBlt( Canvas.Handle, 0, 0, Width, Height, WinDC,  0, 0, SRCCOPY);
              finally
              end;
          end;
      end;
      
      и функция, которая копирует только клиентскую часть окна в BMP

      Function WindowToBMP(WD: HWND ): TBitmap;
      Var
      WinDC: HDC;
      ARect   : TRect;
      begin
          Result := TBitmap.Create;            // Создаем рисунок, куда будем копировать
          GetClientRect(WD, ARect);            // Узнаем размер
          with Result, ARect do
          begin
              Width := ARect.Right - ARect.Left;
              Height := ARect.Bottom - ARect.Top;
              If (Width=0) or (Height=0) then
              Begin
                  MessageDlg('Размер области формы равен нулю',
                  mtWarning,[mbOk],0); 	// А вдруг у него нет размера ???
                  Exit;							// Тогда выходим
              End;
              WinDC:=GetWindowDC(Wd);               // получаем для окна контекст устройства
              ShowWindow(Wd, SW_SHOW);            // на всякий случай выведем окно
              BringWindowToTop(WD);                  // и поместим поверх окон
              try
                  // копируем оттуда прямоугольную область на канву
                  // растрового изображения
                  BitBlt( Canvas.Handle, 0, 0, Width, Height, WinDC, 0, 0, SRCCOPY);
              finally
              end;
          end;
      end;
      
      Видно, что эти функции отличаются только определением области окна, которое будет скопировано.
      Сложностей с пониманием работы этих функций быть не должно, получили область копирования по описателю, вычислили размеры области копирования, получили контекст устройства, и скопировали.

      Кстати сказать, для того, чтобы скопировать весь экран или часть экрана можно использовать подобные функции. Единственное, что в таком случае придется поменять так это контекст устройства. Контекст устройства всего экрана 0. Ну и вместо того, чтобы получать область окна, нужно будет явно передавать координаты области, которую необходимо скопировать.

      Очень много информации об окне можно получить при помощи следующей функции:

      Функция GetWindowLong
      Синтаксис: function GetWindowLong(Wnd: HWnd; Index: Integer): Longint;
      Описание: Считывает инфоpмацию об окне или о значениях дополнительного байта окна.
      Паpаметpы:
      • Wnd: Идентификатоp окна.
      • Index: Смещение в байтах или одна из следующих констант:
        GWL_EXSTYLE возвращает расширенный стиль окна.
        GWL_STYLE возвращает стиль окна.
        GWL_WNDPROC возвращает адрес стандартной процедуры окна.
        GWL_HINSTANCE возвращает экземпляр приложения окна.
        GWL_HWNDPARENT возвращает описатель родительского окна.
        GWL_ID возвращает идентификатор окна.
        GWL_USERDATA возвращает пользовательские данные об окне.
      Возвpащаемое значение: Инфоpмация, хаpактеpная для окна.

      Наиболее интересной информацией, на мой взгляд, является стиль окна и экземпляр приложения.
      Как пользоваться подобной функцией ??
      Например получаем экземпляр приложения:
      Function GetHinstanse(WD:HWND):LongInt;
      Begin
       Result:=GetWindowLong(Wd, GWL_HINSTANCE);
      End;
      
      Похожим образом получается любая информация, только в качестве второго параметра Вы передаете тот флаг, который Вам необходим. Для интереса, можете посмотреть еще описание функции GetWindowWord она сходна с функцией GetWindowLong, но возможно получение несколько других параметров окна.

      Еще ряд параметров можно вытащить используя функцию:

      Функция GetClassLong
      Синтаксис: function GetClassLong(Wnd: HWnd; Index: Integer): Longint;
      Описание: Считывает из стpуктуpы окна TWndClass со смещением Index длинное значение. Положительные смещения в байтах (с нуля) используются для доступа к дополнительным байтам класса.
      Паpаметpы:
      • Wnd: Идентификатоp окна.
      • Index: Смещение в байтах или константа
        GCW_ATOM возвращает значение ATOM уникальное для класса окна
        GCL_CBCLSEXTRA возвращает размер в байтах памяти для данного класса
        GCL_CBWNDEXTRA возвращает размер в байтах памяти для данного окна
        GCL_HBRBACKGROUND возвращает указатель на кисть данного класса
        GCL_HCURSOR возвращает указатель на курсор ассоциированный с классом.
        GCL_HICON возвращает указатель на иконку ассоциированную с классом.
        GCL_HICONSM Windows 95:возвращает указатель на маленькую иконку. Windows NT: не доступно.
        GCL_HMODULE возвращает имя модуля класса.
        GCL_MENUNAME возвращает имя меню для данного класса.
        GCL_STYLE возвращает стиль окна для класса.
        GCL_WNDPROC возвращает адрес стандартной процедуры окна
      Возвpащаемое значение: Считанное значение.

      Надо сказать, что, к сожалению, далеко не все данные, возвращаемые этой функцией верны. Скорее всего она возвращает сведения только о тех классах окон которые корректно зарегистрированы в системе, хотя возможно, что здесь я ошибаюсь.

      Теперь попробуем применить полученные сведения на практике. Ниже приводится текст позволяющий извлекать иконки из приложений, которые запущены в настоящий момент (как было сказано выше, к сожалению, работает не для всех окон).

      Function WinIconToBMP(Wd:HWND);
      Var Icon:HICON;            // Тип указатель на иконку
      Begin
          Result:=TbitMap.Create;
          Icon:=GetClassLong(Wd,GCL_HICON); // Получаем иконку по описателю
          If ICON > 0 then			           // Если получена действительно иконка	
          With Result do
          Begin
              Height:=31;
              Width:=31;
              Canvas.Brush.Color:=clWhite;
              Canvas.FillRect(Rect(0,0,31,31));     // На всякий случай заливаем белым	
              DrawIcon(Canvas.Handle, 0, 0, Icon); // И отрисовываем ее на канве
          End;
      End;

      Как видно, процедура не такая уж и сложная. Для полноты информации можно еще извлечь идентификатор нити, к которой относиться окно.

      Функция GetWindowThreadProcessId
      Синтаксис: DWORD GetWindowThreadProcessId(Wnd: HWND; lpdwProcessId : LPDWORD);
      Описание: Возвращает идентификатор процесса к которому принадлежит данное окно
      Параметры: Wnd: Идентификатор окна.
      lpdwProcessId : 32битное значение идентификатора процесса
      Возвращаемое значение: идентификатор нити

      Как использовать данную функцию ??
      Var
          mProcessID,mThreadID : Dword;
      Begin
          ...// Получение описателя в WD
          mThreadID:= GetWindowThreadProcessId(Wd,@mProcessID); // Получения идентификаторов.
          ...
      End;
      
      Эта функция хороша тем, что для всех окон одного приложения этот идентификатор будут един, если они запущены внутри одной нити (что зачастую и делается). То есть Вы можете разбить окна по нитям и /или по процессам, к которым они относятся. А так же Вы можете определять, какие окна данного приложения в настоящий момент доступны, видны, свернуты и т.д.

      Итоги

      Итак, мы получили список всех окон верхнего уровня, запущенных в системе и огромное количество информации об окнах верхнего уровня, практически все, что может понадобиться:
      • Описатель окна
      • Заголовок окна
      • Класс окна
      • Состояние окна (распахнутое, видимое, доступное и т.д.)
      • Размеры и положение окна
      • Размеры и положение клиентской части окна
      • Контекст устройства
      • Стиль окна
      • Экземпляр приложения
      • Иконку.
      • Идентификатор нити.
      Кроме того, Вы можете получить еще массу интересной информации об окнах верхнего уровня самостоятельно, использую те функции, которые приведены в данном разделе.

      Описание функций API

      Список текстов

      Дмитрий Богданов

        Теперь, попробуем менять, что-то в чужих окнах. И вообще попробуем сделать с ними то же, что мы делаем со своими окнами. Сразу скажу, что тема эта неисчерпаема. Ей можно посвятить не одно скромное обозрение, а детальное многотомное издание. Я не ставлю своей задачей.

        Несколько предварительных сурьезных слов.

        Вынужден сказать, что многое изложенное ниже может привести к неприятным последствиям. Например, к тому, что программа или система будет зависать. Поэтому будем считать, что читатель находиться в трезвом уме и здравой памяти и не будет совершать необдуманные действия. Прежде чем убить или закрыть чужое окно, подумайте, а зачем это окно вообще висит. Помните, что если окна висят в системе значит это кому то нужно ?! (почти Маяковский). Ну а теперь немного попугав для проформы перейдем к делу.

        Содержание:
        • Несколько предварительных сурьезных слов.
        • Режимы отображение окон верхнего уровня.
        • Системное меню и кнопки заголовка.
        • Некоторые дополнительные возможности.
        • Итоги

        Итак:

        Режимы отображение окон верхнего уровня.

        Давайте попробуем для начала сделать чужое окно активным (мы уже это делали, когда спасали чужое окно в картинку).
        Для этого можно использовать одну из следующих функций:
        • Функция SetForegroundWindow
        • Синтаксис: function SetForeGroundWindow(Wd: Hwnd):Boolean;
        • Описание: Показывает верхние окно системы.
        • Параметры: Wnd: Идентификатор окна.
        • Возвращаемое значение: True- если функция отработала, False- при ошибке.
        • Процедура BringWindowToTop
        • Синтаксис: procedure BringWindowToTop(Wnd: HWnd);
        • Описание: Активизирует и перемещает Wnd в вершину стека перекрывающихся окон.
        • параметры: Wnd: Всплывающее или дочернее окно.
        • Возвращаемое значение: Нет
        Теперь попробуем проделать с неким окном, имеющим идентификатор окна HD:HWnd некие стандартные действия:
        • 1) Свернуть данное окно;
        • 2) Развернуть данное окно;
        • 3) Закрыть данное окно.
        Все данные действия могут быть проделаны с окном при помощи стандартной функции SendMessage или PostMessage, с различными параметрами:
        • 1) SendMessage(HD,WM_SYSCOMMAND,SC_MINIMIZE,0);
        • 2) SendMessage(HD,WM_SYSCOMMAND,SC_MAXIMIZE,0);
        • 3) SendMessage(HD,WM_SYSCOMMAND,SC_CLOSE,0);
        Существуют и другие константы, для сообщений вида WM_SYSCOMMAND:
        • SC_CLOSE Закрывает окно.
        • SC_CONTEXTHELP Изменяет курсор на вопросительный знак.
        • SC_DEFAULT Выбирает элемент по умолчанию; эмулирует двойное нажатие на Системное меню.
        • SC_HOTKEY Инициирует окно, связанное с текущим - указанной комбинацией горячих клавиш.
        • SC_HSCROLL Прокручивается горизонтально окно.
        • SC_KEYMENU Открывает Системное меню как результат нажатия клавиши.
        • SC_MAXIMIZE (или SC_ZOOM) Разворачивает окно.
        • SC_MINIMIZE (или SC_ICON) Сворачивает окно.
        • SC_MONITORPOWER Устанавливает состояние дисплея.
        • SC_MOUSEMENU Открывает Системное меню как результат щелчка мыши.
        • SC_MOVE Перемещает окно.
        • SC_NEXTWINDOW Переходит к следующему окну.
        • SC_PREVWINDOW переходит к предыдущему окну.
        • SC_RESTORE Восстанавливает окно к его нормальной позиции и размеру.
        • SC_SCREENSAVE Запускает стандартный скринсейвер.
        • SC_SIZE Задает размеры окно.
        • SC_TASKLIST Выполняет или инициирует Windows Task Manager.
        • SC_VSCROLL Прокручивается окно вертикально.
        Первый параметр - описатель искомого окна, второй сообщение (в нашем случае WM_SYSCOMMAND) третий одна из констант приведенных выше, четвертый параметр - координаты (x- младшее слово y - старшее).
        Можно, так же, показать или скрыть окно, используя функцию API:
        • Процедура ShowWindow
        • Синтаксис: function ShowWindow(Wnd: HWnd; CmdShow: Integer);
        • Описание: отображает или прячет окно образом, указанным параметром CmdShow.
        • параметры: Wnd: Всплывающее или дочернее окно.
          CmdShow - одна из констант:
          • SW_HIDE
          • SW_MAXIMIZE
          • SW_MINIMIZE
          • SW_RESTORE
          • SW_SHOW
          • SW_SHOWDEFAULT
          • SW_SHOWMAXIMIZED
          • SW_SHOWMINIMIZED
          • SW_SHOWMINNOACTIVE
          • SW_SHOWNA
          • SW_SHOWNOACTIVATE
          • SW_SHOWNORMAL
        • Возвращаемое значение: Не нуль, если окно было ранее видимым; нуль - если оно было ранее спрятанным.
        Константы позволяют скрыть/показать окно с различными типами (распахнутым, свернутым, неактивным и пр.)

        Давайте теперь попробуем решить ряд наиболее часто встречающихся проблем:

        1) Как свернуть все окна системы ??? (как свернуть все окна системы кроме окна программы)
        // Любимая наша процедура.
        {Для того чтобы использовать данный пример необходимо наличие кнопки Button1.}
        
        function EnumMiniProc (Wd: HWnd; Param: LongInt): Boolean; stdcall; // Обязательно stdcall !!! 
        Begin
         If  Wd<>Form1.Handle then // если это не наша программа 
                If  IsWindowVisible(WD) then  // если окно видимо
                    If  not IsIconic(WD) then      // если окно не свернуто
                      If  isWindow(WD) then      // и вообще это - окно.
                         ShowWindow(WD, SW_MINIMIZE); // свернем его.
        EnumProc := TRUE; // продолжаем перебирать все окна системы.
        end;
        
        procedure TForm1.Button1Click(Sender: : TObject); // допустим, закрываем по нажатию на клавишу
        begin
         EnumWindows (@EnumMiniProc, 0); // отрабатываем сворачивание окон.
        end;
        
        Для того чтобы окно программы тоже сворачивалось достаточно убрать строку If Wd<>Form1.Handle then в EnumMiniProc
        Конечно, можно поставить еще массу условий, по которым будут минимизироваться окна, но это уже дело конкретной задачи.

        Еще один пример, который бывает зачастую нужен:
        2) Как закрыть (или постоянно закрывать) окна, например содержащие в заголовке подстроку <Реклама>
        Закрыть все окна, содержащие определенную подстроку в заголовке.
        Const
            ReclamaName : String = 'Реклама' ; // строка, по которой мы узнаем, что это - реклама.
            TimeInterval     : Integer = 500;       // Интервал, с которым будем проверять наличие окон
        {Для того чтобы использовать данный пример необходимо наличие таймера  Timer1.}
        // Любимая наша процедура
        function EnumCloseProc (Wd: HWnd; Param: LongInt): Boolean; stdcall; // Обязательно stdcall !!! 
        Var
               Nm:Array[0..255] of Char;  // буфер для имени
           zName:String;
        
        Begin
         GetWindowText(Wd,Nm,255); // считываем  текст заголовка окна
         ZName:=AnsiUpperCase(String(Nm)); // преобразуем к верхнему регистру т.е РЕКЛАМА
        If Pos(ReclamaName,zName)<>0 then SendMessage(WD,WM_SYSCOMMAND,SC_CLOSE,0);
        EnumProc := TRUE; // продолжаем перебирать все окна системы.
        end;
        
        procedure Tform1.Timer1Timer(Sender: TObject); // будем проверять по таймеру:
        begin
         Timer1.Interval:= TimeInterval; // установим время до следующего вызова
         EnumWindows (@EnumCloseProc, 0); // отрабатываем закрытие окон.
        end;
        
        Понятно, что настоящая реклама не дает себе таких заголовков, но общий принцип останется тем же, а так попробуйте поискать общее в заголовках окна, названии классов окна и т.п. Кроме того, использование таймера чревато тем, что окон в системе очень много и за установленный интервал времени все окна не будут отработаны, это приведет к замедлению работы системы. Но решение данной подзадачки автор оставляет за читателем, благо особых сложностей с этим нет (увеличения интервала времени, установка логического условия о том, что проверка уже идет, вставка оператора Application.ProcessMessages и проч.)

        На этом все возможности этих функций API не исчерпываются, но общий принцип отображения чужих окон, закрытия, перемещения и прокрутки изложен, дальше нужно от конкретной задачи.

        Системное меню и кнопки заголовка.

        Системное меню, отображает обычно ряд доступных стандартных функций применимых к окнам.

        Обычно к таким функциям относятся следующие команды (применительно к локализованным Windows, в англоязычных названия будут другие, есть подозрения, что английские J):
        • Восстановить - восстанавливает размер окна.
        • Переместить - перемещает окно.
        • Размер - позволяет изменить размер окна.
        • Свернуть - сворачивает окно до иконки (минимизирует).
        • Развернуть - разворачивает окна до максимально возможного размера
        • Закрыть - закрывает окно.
        Все эти команды, а так же ряд других (например, добавленных пользователем) доступны при нажатии на иконку, расположенную в левой части заголовка окна.

        Ряд команд имеет кнопку, расположенную в правой части заголовка. Обычно таких кнопок три: свернуть, восстановить, закрыть. Иногда добавляется кнопка помощь.

        Зачем манипулировать доступными командами системного окна ??? Ну, например, есть окошко, у которого кнопка закрыть - недоступна, а в системном меню пункта закрыть нет, да и на Alt+F4 она не откликается. А убрать программку ужас как хочется.

        • Процедура GetSystemMenu
        • Синтаксис: function GetSystemMenu(Wnd: HWnd; Revert: Bool): HMenu;
        • Описание: Считывает системное меню окна для копирования и модификации.
        • параметры: Wnd: Всплывающее или дочернее окно.
          Revent: Нуль, чтобы возвращался описатель для копирования системного меню, и не нуль, чтобы возвращался описатель исходного системного меню.
        • Возвращаемое значение: идентификатор системного меню;
          0 - если Revert отлична от нуля и системное меню не модифицировано.
        Для начала надо получить идентификатор системного меню. При помощи приведенной выше функции.
        Далее попробуем определить, что именно содержится в системном меню (надо сказать, что приведенные ниже функции API справедливы для любых меню, а не только системных, но об этом несколько позже):

        • Процедура GetMenuString
        • Синтаксис: function GetMenuString(Menu: HMenu; IDItem: Word; Str: PChar; MaxCount: Integer; Flag: Word): Integer;
        • Описание: копирует метку элемента меню в Str.
        • параметры:
        • Menu: идентификатор меню.
          IDItem: идентификатор элемента меню.
          Str: принимающий буфер.
          MaxCount: размер буфера.
          Flag: Одна из констант меню
          • mf_ByPosition - определять пункт меню по порядковому номеру
          • mf_ByCommand - определять пункт меню по выполняемой команде.
        • Возвращаемое значение: Количество реально скопированных байт.
        Как видно из описания функции возможно два варианта определения списка по номеру или по выполняемой команде.
        Если Flag = mf_ByCommand тогда в качестве IDItem передаются стандартные команды (см. константы в WM_SYSCOMMAND. Предыдущий раздел).
        Например
                     I:=GetMenuString (hMenu, SC_CLOSE, Mn,255,mfByCommand);
        Возвращает название пункта системного меню, отвечающего за закрытие окна. I=0 указывает, что такого пункта в системном меню нет.
        Если Flag = mf_ByPosition тогда в качестве IDItem передается порядковый номер искомого пункта меню, начиная с 0

        Например
                     I:=GetMenuString (hMenu, 0, Mn,255,mfByPosition);
        Возвращает название самого первого по порядку пункта системного меню (обычно это восстановить). I=0 указывает, что такого пункта в системном меню нет. ИМХО первый вариант более пригоден для получения списка строк системного меню, в то время как второй - для определения присутствует ли данная команда в системном меню.

        Количество элементов меню можно получить при помощи функции
        • Процедура GetMenuItemCount
        • Синтаксис: function GetMenuItemCount(Menu: HMenu): Word;
        • Описание: определяет число меню и элементов меню верхнего уровня в указанном меню.
        • параметры:
          Menu: идентификатор меню.
        • Возвращаемое значение: В случае успешного завершения возвращается число элементов меню; 0 - в противном случае.
        Вот как приблизительно может выглядеть функция, которая определяет системное меню окна:
        Получение списка системного меню окна.
        :..
        ListBox1 : TlistBox; // Полученный список запихиваем сюда 
        ::
        
        Procedure GetSysMenuItem (Wd:HWND); // Передаем идентификатор окна.
        Var 
              I,K,Q:Word;
              hMenuHandle : HMENU;
              Nm:Array[0..255] of Char;
        Begin
         ListBox1.Clear; // Очистим список перед использованием.
         hMenuHandle:=GetSystemMenu(Wd, FALSE); // Получим идентификатор
        if (hMenuHandle = 0) then Exit;		// Если такого меню нет, то выходим
        Q:=GetMenuItemCount(hMenuHandle);       // Определяем количество пунктов меню.
        For k:=0 to Q-1 do
         Begin
          i:=GetMenuString(hMenuHandle,k,Nm,255,MF_BYPOSITION); // Считываем название
          ListBox1.Items.Add(String(Nm)); // Добавляем в список.
        End;
        End;
        Итак, мы получили список пунктов системного меню. Пустые строки, скорее всего, означают разделители. Так же используются акселераторы (&)

        Следующим шагом будет определение состояния того или иного пункта меню.

        • Процедура GetMenuState
        • Синтаксис: function GetMenuState(Menu: HMenu; ID, Flags: Word):
        • Описание: Считывает инфоpмацию состояния для указанного элемента меню.
        • параметры: Menu: идентификатор меню.
          IDItem: идентификатор элемента меню.
          Flag: Одна из констант меню
          • mf_ByPosition - определять пункт меню по порядковому номеру
          • mf_ByCommand - определять пункт меню по выполняемой команде.
        • Возвращаемое значение: Маски флагов из следующих значений:
          • mf_Checked - отмеченное галочкой
          • mf_Disabled - недоступное
          • mf_Enabled - доступное
          • mf_MenuBarBreak - в новой строке или столбце с рисовкой разделителя
          • mf_MenuBreak - в новой строке или столбце без линий
          • mf_Separator - строка -разделитель
          • mf_UnChecked - неотмеченное.
          в случае всплывающего меню старший байт содержит число элементов; -1 в случае неверного идентификатора.
        Давайте слегка улучшим наш предыдущий текст, будем отображать, кроме названия пунктов меню, еще и такую насущную информацию как является ли данный пункт разделителем и доступен ли данный пункт для пользователя : Получение списка состояния системного меню окна.
        :..
        ListBox1 : TlistBox; // Полученный список запихиваем сюда 
        ::
        
        Procedure GetSysMenuStatus (Wd:HWND); // Передаем идентификатор окна.
        Var
              K,Q,l:Word;
              hMenuHandle : HMENU;
              Nm:Array[0..255] of Char;
              S:String;
        Begin
         Form1.ListBox1.Clear; // Очистим список перед использованием.
         hMenuHandle:=GetSystemMenu(Wd, FALSE); // Получим идентификатор
        if (hMenuHandle = 0) then Exit;		// Если такого меню нет, то выходим
        Q:=GetMenuItemCount(hMenuHandle);       // Определяем количество пунктов меню.
        For k:=0 to Q-1 do
         Begin
          GetMenuString(hMenuHandle,k,Nm,255,MF_BYPOSITION); // Считываем название
         S:=String(Nm);
          l:=GetMenuState(hMenuHandle,k,MF_BYPOSITION);         // Считываем состояние пункта меню
          If (L and mf_Separator=mf_Separator) then S:='----------------';  // Если это разделитель
          If (l and mf_Grayed<>mf_Grayed) then S:='(a)'+S;  // Если пункт меню подсвечен 
          Form1.ListBox1.Items.Add(S); // Добавляем в список.
        End;
        End;
        
        Точно так же можно определять и многие другие параметры пунктов меню.
        Для получения большего количества информации о пункте меню можно использовать
        • Пpоцедуpа GetMenuItemInfo
        • Синтаксис: function GetMenuItemInfo(Menu: HMenu; ID, Flags: Word; Info:TMenuItemInfo): Word;
        • Описание: Выдает информацию о пункте меню.
        • параметры: Menu: идентификатор меню.
          ID: Идентификатор элемента меню.
          Flag: Одна из констант меню
          • mf_ByPosition - определять пункт меню по порядковому номеру (или TRUE)
          • mf_ByCommand - определять пункт меню по выполняемой команде (или False).
          Info : Указатель на структуру MENUITEMINFO
          MENUITEMINFO = Record
          CbSize : Word;  // размер структуры в байтах
          FMask : Word; // Определяет какие поля записи должны быть установлены или выбраны
          FType : Word; // Тип пункта меню (основные)
              //mft_BitMap - отображаемое с растровым изображением
              // mft_Separator - строка -разделитель 
              // mft_String - строка
              // mft_RadioCheck - строка с возможностью выбора
              // mft_OwnerDraw - рисуемое пользователем
          FState : Word; //Состояние пункта меню (основные).
              // mfs_Checked - отмеченное галочкой 
              // mfs_UnChecked - неотмеченное. 
              // mfs_Default - по умолчанию
              // mfs_Grayed - серое.
          wID    : Word; // Идентификатор пункта меню
          hSubMenu : HMENU; // Идентификатор подменю. Если подменю нет то Null
          hBmpChecked : HBITMAP; // Дескриптор растра для выбранного пункта 
          hBmpUnChecked : HbitMap; // Дескриптор растра для не выбранного пункта
          dwItemData : DWORD;  // Определяемое приложением значение
          dwTypeData ; PAnsiChar; // Содержимое пункта меню
          cch : Word; // Длина текста 
          hBmpItem: HBITMAP; // Дескриптор отображаемого изображения пункта меню.
          End;
          
        • Возвращаемое значение: В случае успешного завершения возвращается 1; 0 - в противном случае.
        Эта функция является упрощенным вариантом монстроподобной GetMenuInfo, которая, к сожалению, поддерживается не везде (Делфа 3 не поддерживает), поэтому описывать и привязываться к этой функции не буду. Итак, мы получили список пунктов системного меню окна. Теперь можно
        • 1) Изменять статус пунктов меню (и соответствующих им кнопок заголовка)
        • 2) Удалять <лишние> пункты меню
        • 3) Добавлять <необходимые> пункты меню.
        Будем решать эти вопросы по порядку.

        • процедура EnableMenuItem
        • Синтаксис: function EnableMenuItem(Menu: HMenu; IDEnableItem, Enable: Word): LongBool;
        • Описание: разрешает, блокирует или затеняет элемент меню в соответствии со значением параметра Enable.
          Menu: Идентификатор меню.
          IDEnableItem: идентификатор или позиция элемента меню или помечаемый всплывающий элемент.
          Enable: Комбинация констант
          mf_ByCommand - пункты меню по команде
          или
          mf_ByPosition - пункты меню по порядку
          совмещенные с константами
          • mf_Disabled, - недоступный
          • mf_Enabled - доступный
          • mf_Grayed. - затененый
        • Возвращаемое значение: Пpедыдущее состояние элемента меню; -1, если элемент не существует..
        Следует заметить, что некоторые пункты системного меню связаны с состоянием окна (такие которые задают положение окна и возможность перемещать и изменять его размеры) и даже если удается формально запретить некий пункт (например развернуться) это не значит, что он будет действительно недоступен.

        Включение/выключение пункта меню
        procedure EnableSysItem(WD:HWND;Number:Integer); 
        // передаем описатель окна и номер пункта
        Var
            hMenuHandle : HMENU;
            i : LongInt;
            l,r : word;
        begin
          If (Number<0)  then Exit; // Если такого пункта точно быть не может
          hMenuHandle:=GetSystemMenu(Wd,False); // Получим идентификатор
          if hMenuHandle=0 then Exit; // Если меню нет
          R:=mf_ByPositon; 
          //Прочтем текущее состояние
          l:=GetMenuState(hMenuHandle,Number,MF_BYPOSITION); 
          // Переключим состояние 
          if l and mfs_Disabled <> mfs_Disabled then R:=R or mfs_Disabled
                                                               else R:=R or mfs_Enabled;
           i:=LongInt(EnableMenuItem(hMenuHandle,Number,R));
        end;
        
        
        Как уже было сказано, это процедура будет работать далеко не для всех пунктов меню, например кнопку и пункт меню закрыть она будет запрещать очень даже хорошо, а вот например пункт развернуть далеко не всегда. Для того, чтобы сделать это наверняка нужно просто удалить такую возможность. Т.е. удалить соответствующий пункт системного меню.
        • Пpоцедуpа DeleteMenu
        • Синтаксис: DeleteMenu(Menu: HMenu Position, Flags: Word): Bool;
        • Описание: Удаляет элемент из Menu. Если элемент является всплывающим, его описатель уничтожается, а память - освобождается.
        • Menu: Идентификатор меню.
        • Position: Положение или идентификатоp команды.
        • Flags: Одна из констант меню: mf_ByPosition, mf_ByCommand.
        • Возвращаемое значение: В случае успешного завеpшения - не нуль; в пpотивном случае - 0.
        Описание стандартное, поэтому никаких сложностей при использовании данной функции возникнуть не должно. ИМХО использование как раз этой функции - тот случай, когда в качестве параметра flags лучше передавать значение mf_ByCommand явно указывая какую команду Вы собираетесь удалить из меню. Так же следует заметить, что удаление пункта меню, которому соответствует кнопка заголовка приведет не к исчезновению кнопки из заголовка, а только к ее затенению.

        Добавить пункт меню можно двумя способами: просто добавить пункт в конец меню:
        • Пpоцедуpа AppendMenu
        • Синтаксис: function AppendMenu(Menu: HMenu; Flags, IDNewItem: Word; Name: PChar): Bool;
        • Описание: Пpисоединяет в конец меню новый элемент, состояние котоpого опpеделяется Flags.
        • Menu: Идентификатор меню.
        • IDNewItem: Положение или идентификатоp команды.
        • Flags: Одна из констант меню: mf_ByPosition, mf_ByCommand.
        • Name: Название пункта меню.
        • Возвращаемое значение: В случае успешного завеpшения - не нуль; в пpотивном случае - 0.
        Или вставить пункт меню настроив все необходимые параметры
        • Пpоцедуpа InsertMenuItem
        • Синтаксис: function InsertMenuItem (Menu: HMenu; Flags, IDNewItem: Word; Item: :TMenuItemInfo): Bool;
        • Описание: Вставляет пункт меню.
        • Menu: Идентификатор меню.
        • IDNewItem: Положение или идентификатоp команды.
        • Flags: Одна из констант меню: mf_ByPosition, mf_ByCommand.
        • Item: Структура определяющая пункт меню (см. описание GetMenuItemInfo)
        • Возвращаемое значение: В случае успешного завеpшения - не нуль; в пpотивном случае - 0.
        Ну и как результат всех наших стараний напишем процедуру, которая разрешает или запрещает кнопку, строку системного меню <закрыть> (а так же комбинацию клавиш Alt+F4):

        Удаление или восстановление кнопки закрыть окно.
        // Отключает или разрешает так же пункт меню, и комбинацию Alt+F4
        Procedure CloseXbtn (Wd:HWND; Enable:Boolean); 
        Var
            hMenuHandle : HMENU;
        Begin
          hMenuHandle:=GetSystemMenu(Wd,False); // Получим идентификатор
          if hMenuHandle=0 then Exit; // Если меню нет
          If Enable then  // Если надо добавить пункт меню
              AppendMenu (hMenuHandle, mf_ByCommand, SC_Close,'&Закрыть Alt+F4');
                Else DeleteMenu(hMenuHandle, SC_Close, mf_ByCommand);
        End;
        
        Конечно, куда правильнее было бы использовать функцию InserMenuItem вместо AppendMenu, тогда можно было бы поставить слева значек <закрыть>. Но это уже для любителей самим повозиться с API, очень уж не хочется лишать их этого удовольствия J.

        Ну, и, наконец, для развлечения тех, кто продрался сквозь все эти кошмары работы с системным меню, предлагаю маленькое развлечение.

        Иногда появляется необходимость нарисовать, что-нибудь (например, кнопку) в заголовке чужого окна (а возможно и своего). Это можно сделать очень и очень просто.
        • Пpоцедуpа DrawFrameControl
        • Синтаксис: function DrawFrameControl (DC:HDC;Rc :Trect; uType,uStyle:Word ): Bool;
        • Описание: Рисует один из элементов в заголовке окна.
        • DC : контекст устройства в котором происходит рисование.
        • Rc : Область в которой будет происходить рисование
        • UType: Тип элемента одна из констант:
        • DFC_BUTTON Кнопка
        • DFC_CAPTION Заголовок
        • DCF_MENU Меню
        • DFC_SCROLL Полоса прокрутки
        • Ustyle : Стиль элемента одна из констант:
          Для кнопок
          DFCS_BUTTON3STATE Кнопка с тремя состояниями
          DFCS_BUTTONCHECK Флажок
          DFCS_BUTTONPUSH Кнопка
          DFCS_BUTTONRADIO Переключатель
          DFCS_BUTTONRADIOIMAGE Картинка для переключателя
          DFCS_BUTTONRADIOMASK Маска для переключателя
          Для заголовков
          DFCS_CAPTIONCLOSE Кнопка закрыть
          DFCS_CAPTIONHELP Кнопка помощь (только Window 9x)
          DFCS_CAPTIONMAX Кнопка развернуть
          DFCS_CAPTIONMIN Кнопка свернуть
          DFCS_CAPTIONRESTORE Кнопка восстановить
          Для меню
          DFCS_MENUARROW Стрелка подменю
          DFCS_MENUBULLET Маркер
          DFCS_MENUCHECK Маркер - флажек
          Для полос прокрутки
          DFCS_SCROLLCOMBOBOX Линейка прокрутки выпадаюшего списка DFCS_SCROLLDOWN Кнопка вниз DFCS_SCROLLLEFT Кнопка влево DFCS_SCROLLRIGHT Кнопка вправо DFCS_SCROLLSIZEGRIP Размерная ручка DFCS_SCROLLUP Кнопка вверх
        • Возвращаемое значение: В случае успешного завеpшения - не нуль; в пpотивном случае - 0.

        Заметьте, что это функция только рисует элемент заголовка.

        14 Отрисовка <фальшивой> кнопки закрыть в заголовке окна.
        Procedure DrawFalseClose (Wd:HWND; xPos:Integer);
        Var DC:HDC;
        begin
         DC:=GetWindowDC(Wd); // Получим контекст устройства окна
        If DC>0 then
         Begin
           DrawFrameControl (DC,Rect(xPos,4,xPos+16,020),DFC_Caption,DFCS_CaptionClose);
           ReleaseDC(Wd,DC); // Освободим контекст устройства.
        End;
        end;
        

        Некоторые дополнительные возможности

        С приложениями (и окнами верхнего уровня в частности) можно делать огромное количество вещей. Если быть честным, то останавливаться подробно на этих возможностях я не собирался. Но оказалось, что решение этих задач интересует достаточно многих. В этом разделе попробуем привести некоторые, на мой взгляд, наиболее полезные из них.

        Самым простым, и наиболее часто используемой является возможность изменять заголовок чужих окон. И действительно, почему в заголовке Дельфы пишется например Delphi ? J Непорядок !

        • Пpоцедуpа SetWindowText
        • Синтаксис: procedure SetWindowText(Wnd: HWnd; Str: PChar);
        • Описание: Устанавливает название заголовка для окна или текст оpгана упpавления с помощью стpоки, указанной в Str.
        • Wnd: Идентификатоp окна или оpгана упpавления.
        • Str: Стpока (заканчивающаяся пустым символом).
        • Возвращаемое значение:Нет.
        И текст, который иллюстрирует работу данной функции, например, находит окно Дельфы и меняет ее заголовок с на любой другой

        Замена текста в заголовке окна.
        Procedure ChangeDelphi (NewName:String); // Передаем новое название например Дельфи
        Var Wd:HWND;
               Nm:Array[0..255] of Char
               St : String;
               I:Integer;
        Begin
          Wd:= FindWindow('TAppBuilder',Nil); // Находим заголовок по классу окна Delphi
         If Wd<=0 then Exit; // Такого окна нет.
        GetWindowText(Wd,Nm,255); // Считываем заголовок окна
        St:=String(Nm);      // Переводим в строку
        I:=Pos('Delphi',St); // Находим положения заголовка
        If I>0 then // Если слово Дельфи есть в заголовке
         Begin
          Delete(St,i,Lenght('Delphi'); // Удаляем
          Insert(NewName,St,i); // Вставляем
         SetWindowText(Wd,Pchar(St)); // Отправляем новый заголовок окну.
         End; // Все
        end;
        
        Зачастую необходимо выяснить, не зависло ли окно (или вернее насколько живо оно откликается на попытки системы достучаться до него) Для этих целей можно использовать следующий текст

        16 Определение не является ли данное окно зависшим.
        // Результат True- рабочее окно, False - возможно окно висит 
        function WinTimeOut (Wd:HWND;Time:Integer):Boolean; //Описатель окна и время в секундах
        Var dwRes:DWORD
        begin
         Time:=Time*1000; // Переводим время в миллисекунды 
         Result:=Not SendMessageTimeOut(WD,WM_USER,0,0,SMTO_NORMAL, Time, @dwRes);
        end;
        
        Теперь поговорим о, так называемом, подсвечивание окон. Например, при установке точки останова в программе главное окно начинает мерцать. И делает это до тех пор, пока пользователь не переключится в это окно. Как это делается ???

        Существует пара функций:
        • Пpоцедуpа FlashWindow
        • Синтаксис: function FlashWindow(Wnd: HWnd; Invert: Bool): Bool;
        • Описание: Делает окно или пиктогpамму мигающими. Активное состояние откpытого окна инвеpтиpуется.
        • Wnd: Идентификатоp окна или оpгана упpавления.
        • Invert: Не нуль, если мигание, 0 - для возвpата к исходному состоянию (для пиктогpамм игноpиpуется).
        • Возвращаемое значение: Не нуль, если окно до вызова было активным; 0 - в пpотивном случае.
        И вторая функция, которая описана для Delphi 5 а для 3 нет, что обидно, но мы это исправим.
        • Пpоцедуpа FlashWindowEx
        • Синтаксис: function FlashWindowEx(var pfwi: FLASHWINFO): BOOL;
        • Описание: Делает окно или пиктогpамму мигающими. Активное состояние откpытого окна инвеpтиpуется.
          FLASHWINFO = record
          cbSize: UINT;   // Размер структуры в байтах
          hwnd: HWND;  // Идентификатоp окна или оpгана упpавления.
          dwFlags: DWORD; // один из следующих флагов:
              FLASHW_STOP = $0;     Не мигать
              FLASHW_CAPTION = $1; Мигающий заголовок
              FLASHW_TRAY = $2; Мигающая кнопка
              FLASHW_ALL = FLASHW_CAPTION or FLASHW_TRAY;  Мигать
              FLASHW_TIMER = $4; Мигать пока не будет запущен СТОП
              FLASHW_TIMERNOFG = $C; Мигать пока не станет верхним
          uCount: UINT;      // Сколько раз мигать
          dwTimeout: DWORD; //Интервал мигания
          end;
        • Возвращаемое значение: Не нуль, если окно до вызова было активным; 0 - в пpотивном случае.
        Сначала опишем функцию для несчастных, которые как и я ютятся в 3 версии.
        Куда Вы все это вставите Ваши сложности можно в отдельный модуль можно в тот же что и программа. Если будете делать отдельный модуль, это будет выглядеть приблизительно так:
        interface
        Const
          FLASHW_STOP = $0;
          FLASHW_CAPTION = $1;
          FLASHW_TRAY = $2;
          FLASHW_ALL = FLASHW_CAPTION or FLASHW_TRAY;
          FLASHW_TIMER = $4;
          FLASHW_TIMERNOFG = $C;
        type
          FLASHWINFO = record
            cbSize: UINT;
            hwnd: HWND;
            dwFlags: DWORD;
            uCount: UINT;
            dwTimeout: DWORD;
          end;
          PFLASHWINFO = ^FLASHWINFO;
          TFlashWInfo = FLASHWINFO;
        
        function FlashWindowEx(var pfwi: FLASHWINFO): BOOL; stdcall;
        implementation
        function FlashWindowEx; external user32 name 'FlashWindowEx'; 
        end;
        
        

        А теперь сама программа, не забудьте подключить модуль кому нужно:

        17 Мигающий заголовок окна
        // Результат True- рабочее окно, False - возможно окно висит 
        procedure SetOnFlash (Wd:HWND;):Boolean; //Описатель окна 
        Var f: TFlashWInfo; 
        Begin
         f.Hwnd:=Wd;
         f.dwFlags:= FLASHW_ALL;
         f.dwTimeout:=10;
         f.uCount:=100;
         f.cbSize:=SizeOf(F);
         FlashWindowEx(F)
        end;
        

        Итоги

        Итак, мы научились управлять чужими окнами верхнего уровня:
        • 1. Изменять их положение, размеры
        • 2. Закрывать, сворачивать и восстанавливать
        Разобрались с возможностями работы с чужими системными меню. Изучили еще целый ряд возможностей окон верхнего уровня.

        ОПИСАНИЕ ФУНКЦИЙ API

        19. SetForegroundWindow
        20. BringWindowToTop
        21. ShowWindow
        22. GetSystemMenu
        23. GetMenuString
        24. GetMenuItemCount
        25. GetMenuState
        26. GetMenuItemInfo
        27. EnableMenuItem
        28. DeleteMenu
        29. AppendMenu
        30. InsertMenuItem
        31. DrawFrameControl
        32. SetWindowText
        33. FlashWindow
        34. FlashWindowEx

        Список текстов

        8. Свернуть все окна кроме текущего окна программы
        9. Закрыть все окна, содержащие определенную подстроку в заголовке
        10. Получение списка системного меню окна.
        11. Получение списка состояния системного меню окна.
        12. Включение/выключение пункта меню
        13. Удаление или восстановление кнопки закрыть окно [х].
        14. Отрисовка <фальшивой> кнопки закрыть в заголовке окна.
        15. Замена текста в заголовке окна.
        16. Определение не является ли данное окно зависшим
        17. Мигающий заголовок окна

        Дмитрий Богданов

          0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
          0 пользователей:


          Рейтинг@Mail.ru
          [ Script execution time: 0,0274 ]   [ 16 queries used ]   [ Generated: 2.05.24, 00:20 GMT ]