Секреты иконки в системном трее. Часть 1.

Автор: Рустик (http://delphi.hostmos.ru)

    Наверняка многие, кто начинает программировать на Delphi нередко задавались такими вопросами как:
1. А как можно поместить иконки приложения в системный трей Windows возле часов?
2. Как скрыть главную форму приложения при запуске и показывать ее по команде всплывающего меню иконки приложения в системной трее, и как затем скрыть ее обратно?

    Знакомо да? Что ж, сам я тоже когда-то был начинающим программистом, и потратил уйму времени и нервов за изучение и поиска решений для всех вышеизложенных вопросов. И именно сейчас я готов изложить Вам все свои знания по части работы с иконкой приложения в системной трее.

    Статью я решил разделить на две части. Что содержит каждая из них догадаться не трудно - вопросов, о решении которых я расскажу тоже две. 

Итак, это часть 1: "Добавление иконки в системный трей".

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

    Начать статью я решил не с описания всевозможных компонентов для данной сферы программинга (которых полным полно в интернете), а с описания азов - WinAPI функций, предназначенных для работы с system tray. Хотя, "функции", как то больно громко получилось. На самом деле она всего одна:

function
Shell_NotifyIcon ( dwMessage: Cardinal; lpData: PNotifyIconData ) : LongBool;

(я привожу Вам упрощенное представление функции сделанное лично мной, т.к. если взять описание функции из WinAPI SDK - понять его будет намного труднее)

подробнее о параметрах:
    dwMessage:
  Параметр, а точнее команда, которая указывает этой функции что именно она должна делать. Может принимать следующие значения констант:
 NIM_ADD - добавляет иконку в трей
 NIM_DELETE - удаляет иконку из системного трея
 NIM_MODIFY - меняет (обновляет) иконку в системном трее.
    lpData:
  Сей параметр есть запись, которая содержит всю информацию об добавляемой, удаляемой или изменяемой иконке. Данная запись имеет вид:

_NOTIFYICONDATAA = record
   cbSize: DWORD;
   Wnd: HWND;
   uID: UINT;
   uFlags: UINT;
   uCallbackMessage: UINT;
   hIcon: HICON;
   szTip: array [0..63] of AnsiChar;
end;
(указатель PNotifyIconData является указателем именно на эту запись)

Давайте рассмотрим, какие именно данные содержит данная запись:
     cbSize: Размер данной записи;
     Wnd: Handle того окна, которое будет получать сообщения от иконки;
     uID: Идентификатор иконки;
     uFlags: Здесь содержатся константы, означающие, какие данные верны в записи:
         NIF_ICON - hIcon содержит верную информацию.
         NIF_MESSAGE - uCallbackMessage содержит верную информацию.
         NIF_TIP -szTip содержит верную информацию.
        (Подробнее об этом я расскажу ниже)
     uCallbackMessage: Назначенный Вами идентификатор сообщения, которое будет получать приложение от иконки.
     hIcon: Собственно Handle иконки (TIcon.Handle). 
     szTip: Текст всплывающей подсказки (не более 64 символов).


Мда, наверняка ничего не понятно, да? ..особенно новичкам. Ничего! Далее я приведу код небольшой программы, написанной лично мной. Весь код программы имеет очень богатые комментарии. С их помощью понять смысл каждого шага Вам будет нетрудно:)

Вот скриншот этой небольшой программки:


Ниже привожу листинг файла main.pas (файл основной и единственной формы приложения):
Внимание!: в тексте программы прошу не путать два понятия: "иконка" - это то, что собственно находится в системно трее, и "иконка (изображение)" - это то, что содержит то само изображение иконочки.

unit main;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ShellApi, StdCtrls, ExtCtrls, Menus;

type
  TForm1 = class(TForm)
    GroupBox1: TGroupBox;
    TI_Event: TLabel;
    GroupBox2: TGroupBox;
    TI_DC: TLabel;
    GroupBox3: TGroupBox;
    Icon: TImage;
    IconFile: TEdit;
    Browse: TButton;
    Exit: TButton;
    About: TButton;
    OpenDialog1: TOpenDialog;
    GroupBox4: TGroupBox;
    ToolTip: TEdit;
    SetToolTip: TButton;
    AboutGroupBox: TGroupBox;
    Desk: TLabel;
    Author: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    email: TLabel;
    www: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure BrowseClick(Sender: TObject);
    procedure ExitClick(Sender: TObject);
    procedure SetToolTipClick(Sender: TObject);
    procedure wwwClick(Sender: TObject);
    procedure emailClick(Sender: TObject);
    procedure AboutClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
   procedure IconCallBackMessage( var Mess : TMessage ); message WM_USER + 100;
   //Здесь мы объявлем процедуру, которая будет выполнятся каждый раз, когда
   //на иконке будет происходит какое-либо событие (клик мышки и т.п.)
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var nid : TNotifyIconData;
begin
  //Добавляем иконку в трей при старте программы:
  with nid do  //Указываем параметры иконки, для чего используем структуру
               //TNotifyIconData.
  begin
    cbSize := SizeOf( TNotifyIconData ); //Размер все структуры
    Wnd := Form1.Handle; //Здесь мы указывает Handle нашей главной формы
                         //которая будет получать сообщения от иконки.
    uID := 1;            //Идентификатор иконки
    uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP; //Обозначаем то, что в
                                                  //параметры входят:
                                                  //Иконка, сообщение и текст
                                                  //подсказки (хинта).
    uCallbackMessage := WM_USER + 100;            //Здесь мы указываем, какое
                                                  //сообщение должна  высылать
                                                  //иконочка нашей главной форме,
                                                  //в тот момент, когда на ней
                                                  //(иконке)  происходят
                                                  //какие-либо события
    hIcon := Application.Icon.Handle;             //Указываем на Handle
                                                  //иконки (изображения)
                                                  //(в данной случае берем
                                                  //иконку основной формы
                                                  //приложения. Ниже Вы увидите
                                                  //как можно ее изменить)
    StrPCopy(szTip, ToolTip.Text);                //Указываем текст всплывающей
                                                  //посдказки, который берем из
                                                  //компонента ToolTip,
                                                  //расположенного на главной
                                                  //форме.
  end;
  Shell_NotifyIcon( NIM_ADD, @nid );
  //Собственно добавляем иконку в трей:)
  //Обратите внимание, что здесь мы исползуем константу
  //NIM_ADD (добавление иконки).
  IconFile.Text:=Application.ExeName;
  //Выводим на главной форме пусть к файлу, который содержит
  //иконку (изображение):)
  Icon.Picture.Icon:=Application.Icon;
  //Теперь выводим на главной форме изображение
  //иконки (изображения) в увеличенном виде
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var nid : TNotifyIconData;
begin
  with nid do
  begin
    cbSize := SizeOf( TNotifyIconData );
    Wnd := Form1.Handle;
    uID := 1;
    uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;
    uCallbackMessage := WM_USER + 100;
    hIcon := Application.Icon.Handle;
    StrPCopy(szTip, ToolTip.Text);
  end;
  Shell_NotifyIcon( NIM_DELETE, @nid );
  //Удаляем иконку из трея. Параметры мы вводим для того,
  //чтобы функция точно знала, какую именно иконку надо удалять.
  //Обратите внимание, что здесь мы исползуем константу
  //NIM_DELETE (удаление иконки).
end;

procedure TForm1.IconCallBackMessage( var Mess : TMessage );
var Mouse: TMouse;
begin
  case Mess.lParam of
    //Здесь Вы можете обрабатывать все события, происходящие на иконке:)
    //На главнй форме я специально расположил две метки, в которых,
    //при возникновении какого-либо события будет писаться что именно произошло:)
    //Но, теперь во второй части во время некоторых событий будут происходит
    //реальные процессы.
    WM_LBUTTONDBLCLK  : TI_DC.Caption   := 'Двойной щелчок левой кнопкой'       ;
    WM_LBUTTONDOWN    : TI_Event.Caption:= 'Нажатие левой кнопки мыши'          ;
    WM_LBUTTONUP      : TI_Event.Caption:= 'Отжатие левой кнопки мыши'          ;
    WM_MBUTTONDBLCLK  : TI_DC.Caption   := 'Двойной щелчок средней кнопкой мыши';
    WM_MBUTTONDOWN    : TI_Event.Caption:= 'Нажатие средней кнопки мыши'        ;
    WM_MBUTTONUP      : TI_Event.Caption:= 'Отжатие средней кнопки мыши'        ;
    WM_MOUSEMOVE      : TI_Event.Caption:= 'Перемещение мыши'                   ;
    WM_MOUSEWHEEL     : TI_Event.Caption:= 'Вращение колесика мыши'             ;
    WM_RBUTTONDBLCLK  : TI_DC.Caption   := 'Двойной щелчок правой кнопкой'      ;
    WM_RBUTTONDOWN    : TI_Event.Caption:= 'Нажатие правой кнопки мыши'         ;
    WM_RBUTTONUP      : TI_Event.Caption:= 'Отжатие правой кнопки мыши'         ;
  end;
end;

procedure TForm1.BrowseClick(Sender: TObject);
var I   : TIcon;
    nid : TNotifyIconData;
begin
  //Здесь мы меням иконку приложения, для чего используем диалоговое окно
  //(для выбора файла, который содержит изображение иконки)
  //Итак, все потизоньку:
  if OpenDialog1.Execute then //Запуск диаголового окна
  begin
    I:=TIcon.Create; //Создаем объект I типа TIcon
    I.LoadFromFile(OpenDialog1.Filename); //Теперь загружаем в него изображение
                                          //из того файла, который был выбран в
                                          //окне диалогового окна
    //Далее делаем все тоже самое, что и раньше, создаем структуру типа
    //TNotifyIconData куда заносим все параметры иконки:)
    with nid do
    begin
      cbSize := SizeOf( TNotifyIconData );
      Wnd := Form1.Handle;
      uID := 1;
      uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;
      uCallbackMessage := WM_USER + 100;
      hIcon := I.Handle;
      StrPCopy(szTip, ToolTip.Text);
    end;
    Shell_NotifyIcon( NIM_MODIFY , @nid );
    //А вот здесь мы заносим изменения в иконку, которая находится в системном
    //трее обратите внимание, что здесь мы исползуем константу NIM_MODIFY
    //(изменение иконки).
    Icon.Picture.Icon:=I;
    IconFile.Text:=OpenDialog1.FileName;
    I.Free;
  end;
end;

procedure TForm1.ExitClick(Sender: TObject);
begin
  //Закрываем приложение. 
  Close;
end;

procedure TForm1.SetToolTipClick(Sender: TObject);
var nid : TNotifyIconData;
begin
  //Здесь мы назначаем всплывающую посказу:)
  //Здесь все тоже самое, что и в предыдущей процедуре.
  //Только иконка (изображение) берется из компонента Icon,
  //который назодится на главной форме:)
  //А текст всплывающей посказки берем из
  //компонента ToolTip (который расположен на главной форме)
  with nid do
  begin
      cbSize := SizeOf( TNotifyIconData );
      Wnd := Form1.Handle;
      uID := 1;
      uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;
      uCallbackMessage := WM_USER + 100;
      hIcon := Icon.Picture.Icon.Handle;
      StrPCopy(szTip, ToolTip.Text);
  end;
  Shell_NotifyIcon( NIM_MODIFY , @nid );
  //.. и снова меням иконку:)
end;

//Далее идут процедуры, относящие работе сведений о программе
//Тут нет ничего сложного :-)
procedure TForm1.wwwClick(Sender: TObject);
begin
  ShellExecute(Application.Handle,'open','http://delphi.hostmos.ru',nil,nil,0);
end;

procedure TForm1.emailClick(Sender: TObject);
begin
  ShellExecute(Application.Handle,'open','mailto:srustik@rambler.ru',nil,nil,0);
end;

procedure TForm1.AboutClick(Sender: TObject);
begin
  if Form1.Width=250 then
  begin
    Form1.Width:=430;
    About.Caption:='О Программе <<';
  end
  else begin
    Form1.Width:=250;
    About.Caption:='О Программе >>';
  end;
  AboutGroupBox.Visible:=not AboutGroupBox.Visible;
end;

end.
end.

Вот в принципе и все:) Надеюсь данная часть помогла Вам понять азы работы с системным треем:)
Исходные тексты примера для данной статьи Вы можете скачать здесь.



Опубликовал admin
10 Дек, Среда 2003г.



Программирование для чайников.