Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Hello, World!
  
 

Фильтр по датам

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  22:28[Войти] | [Зарегистрироваться]

Мечты вуайериста II

Дмитрий Богданов
дата публикации 23-08-2001 16:17

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

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

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

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

Итак…

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

Давайте попробуем для начала сделать чужое окно активным (мы уже это делали, когда спасали чужое окно в картинку).
Для этого можно использовать одну из следующих функций:
  • Функция 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ока (заканчивающаяся пустым символом).
  • Возвращаемое значение:Нет.
И текст, который иллюстрирует работу данной функции, например, находит окно Дельфы и меняет ее заголовок с «Delphi» на любой другой

Замена текста в заголовке окна.
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. Мигающий заголовок окна

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




Смотрите также материалы по темам:
[TStrings] [TStringList] [TFont] [Взаимодействие с 'чужими' процессами/приложениями] [Относительное расположение/скрытие/восстановление] [Работа с контролами чужого приложения] [WM_SYSCOMMAND] [WM_USER]

 Обсуждение материала [ 15-04-2005 06:03 ] 12 сообщений
  
Время на сайте: GMT минус 5 часов

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

Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

 
© При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
Все используемые на сайте торговые марки являются собственностью их производителей.

Яндекс цитирования