скрыть

скрыть

  Форум  

Delphi FAQ - Часто задаваемые вопросы

| Базы данных | Графика и Игры | Интернет и Сети | Компоненты и Классы | Мультимедиа |
| ОС и Железо | Программа и Интерфейс | Рабочий стол | Синтаксис | Технологии | Файловая система |



Google  
 

Shell своими руками - System Tray



Автор: Богдан Минич

Иконку в трей помещают с помощью Shell_NotifyIconW. Интересено посмотреть на этот процесс с другой точки зрения.

Цитата с сайта delphi.mastak.ru:

Shell_NotifyIconW просто ищет окно с классом "Shell_TrayWnd" и посылает в него сообщение WM_COPYDATA. в качестве данных выступает простая структура TNIDMessage. возвращаясь к топику: если создать свое окно с классом "Shell_TrayWnd" и обрабатывать входящие сообщения WM_COPYDATA, то можно написать полный аналог system tray! ... (с) paul_shmakov

...чем и займемся.

В первую очередь немаловажное замечание: сообщение посылается только одному окну, то есть наше приложение должно грузится первым. Разные там explorer'ы и другие подобные будут мешать.

Шаг первый:

Создаем окно "Shell_TrayWnd"


procedure TForm1.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.WinClassName := 'Shell_TrayWnd';
end;

Все. Окно класса "Shell_TrayWnd" имеем.

Шаг второй:

Ловим WM_COPYDATA


procedure TFORM1.WMCOPYDATA(var Msg: Tmessage);
var
  pcd: PCopyDataStruct;
  NID: PNotifyIconData;
begin
  pcd := PCOPYDATASTRUCT(msg.lParam);
  if pcd^.dwData = 1 then
  begin
    NID := pointer(integer(pcd.lpData) + 8);
    case integer(pointer(integer(pcd.lpData) + 4)^) of
      NIM_ADD: Msg.Result := NewTrayIcon(NID); // добавить иконку
      NIM_DELETE: Msg.Result := DeleteTrayIcon(NID); // удалить иконку
      NIM_MODIFY: Msg.Result := ModifyTrayIcon(NID);
        // изменить иконку (или подсказку)
    end;
    exit;
  end;
end;

Обратите внимание на Msg.Result. Желательно чтобы NewTrayIcon, DeleteTrayIcon, ModifyTrayIcon возвращали Integer(True) или Integer(False) в зависимости от помещения/удаления иконки.
Некоторые приложения не проверяют этот результат, но если начнут проверять - то причины "глючного" поведения иконки того же AVP Monitor можно искать долго и безуспешно.

Шаг третий:

Поймали, и че с ним теперь делать?

А мы имеем очень интересную структуру -
  • NID.cbSize - размер записи, в принципе не интересен;
  • NID.Wnd - хендл окна (владельца иконки);
  • NID.uID - идентификатор иконки (если их в приложении несколько), для данной задачи нужен для отсылки обратного сообщения;
  • NID.uFlags - определяет, какие поля используются в сообщении. Параметр может быть любой комбинацией из флагов (0 - uCallbackMessage, 2 - hIcon, 4 - czTip);
  • NID.uCallbackMessage - номер сообщения, которое посылается окну, определяемому полем NID.Wnd (владельцу). lParam отсылаемого сообщения дожен равняться NID.uID, а wParam сообщение от мыши.
    Пример:
    PostMessage(NID.Wnd, NID.uCallBack, NID.uID, MOUSE_EVENT) где MOUSE_EVENT может принимать значения WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK и подобные для других кнопок мыши.
  • NID.hIcon - хендл иконки, которую собственно и предполагается отображать;
  • NID.szTip - строка, оканчивающаяся нулевым символом, содержит подсказку, которая должна выводится при наведении курсора на иконку.

В случаях ошибки нужно информировать приложения про необходимость поместить иконки обратно. Для этого послужат такие действия:


procedure TForm1.FormCreate(Sender: TObject);
var
  WM_TASKBARCREATED: UINT;
begin
  WM_TASKBARCREATED := RegisterWindowMessage('TaskbarCreated');
  PostMessage(HWND_BROADCAST, WM_TASKBARCREATED, 0, 0);
end;

Не все приложения реагируют на такое сообщение.

Скачать демо проект DemoTray.zip (9.8K)

И несколько слов о демо проекте.

ВНИМАНИЕ!!! Следуйте данным инструкциям только в том случае, если Вы ясно понимаете смысл действий!!!

Повторюсь: Shell_NotifyIconW сообщение посылает только одному окну. Поэтому чтобы увидеть результаты работы демопроекта, загружать его надо без или вместо explorer'а.

Первый вариант (для Win9x): Пример: файл %windir%\system.ini изменить следующим образом:

Найти строчку:


shell=explorer.exe

Заменить на (предполагается что демопроект находится в C:\Demotray\ ) :


;shell=explorer.exe
 shell=c:\demotray\demotray.exe

Перегрузите Windows
Для возврата explorer'a раскомментируйте первую строчку, закомментируйте или удалите вторую.

Второй вариант: Лично я использую Far для выгрузки Explorer.exe

  • 1) Загружаем IDE Delphi и демопроект
  • 2) Загружаем Far, F11->Process list->
    выбираем EXPLORER.EXE->F8->OK
  • 3) Отлаживаем проект
  • 4) Для появления Explorer'a просто запустите его.

Используйте эти инструкции на свой страх и риск. Прочитайте их дважды. Внимательно изучите исходники. Трижды.

Инструкции по закрытию EXPLORER.EXE действительны для Win9x.

Если у Вас NT - разберитесь сами. Если не сможете разобратся - то за такие проекты Вам браться рановато.

Гревные ругательства "А у меня после ... ничего не работает!" не принимаются.

Благодарности:

  • Paul Shmakov - реверсинг Shell_NotifyIcon, моральная поддержка.
  • Стив Тейксейра & Ксавье Пачеко - литература.
  • Особая благодарность обоим использованым алфавитам.





Copyright © 2004-2016 "Delphi Sources". Delphi World FAQ




Группа ВКонтакте   Ссылка на Twitter   Группа на Facebook