Часто программисту приходится сталкиваться с задачей написания приложения, работающего в фоновом режиме и не нуждающегося в месте на
Панели задач. Если вы посмотрите на правый нижний угол рабочего стола windows, то наверняка найдете там приложения, для которых эта проблема решена: часы, переключатель раскладок клавиатуры, регулятор громкости и т. п. Ясно, что, как бы вы не увеличивали и не уменьшали формы своего приложения, попасть туда обычным путем не удастся. Способ для этого предоставляет
shell api.
Те картинки, которые находятся на
system tray — это действительно просто картинки, а не свернутые окна. Они управляются и располагаются панелью system tray. Она же берет на себя еще две функции: показ подсказки для каждого из значков и оповещение приложения, создавшего значок, обо всех перемещениях мыши над ним.
Весь
api system tray состоит из 1 (одной) функции:
Код:
function shell_notifyicon(dwmessage: dword;
ipdata: pnotifylcondata): bool; pnotifylcondata = tnotifylcondata; tnotifylcondata = record
cbsize: dword;
wnd: hwnd;
uid: uint;
uflags: uint;
ucallbackmessage: uint;
hlcon: hicon;
sztip: array [0..63] of ansichar;
end;
Параметр dwmessage определяет одну из операций: nim_add означает добавление значка в область, nim_delete — удаление, nim_modify — изменение.
Ход операции зависит от того, какие поля структуры tnotifyicondata будут заполнены.
Обязательным для заполнения является поле cbsize — там содержится размер структуры. Поле wnd должно содержать дескриптор окна, которое будет оповещаться о событиях, связанных со значком.
Идентификатор сообщения windows, которое вы хотите получать от системы о перемещениях мыши над значком, запишите в поле ucallbackmessage. Если вы хотите, чтобы при этих перемещениях над вашим значком показывалась подсказка, то задайте ее текст в поле sztip. В поле uid задается номер значка — каждое приложение может поместить на system tray сколько угодно значков. Дальнейшие операции вы будете производить, задавая этот номер. Дескриптор помещаемого значка должен быть задан в поле hicon. Здесь вы можете задать значок, связанный с вашим приложением, или загрузить свой — из ресурсов.
Примечание
Изменить главный значок приложения можно в диалоговом окне project/ options на странице application. Он будет доступен через свойство application.icon. Тут же можно отредактировать и строку для подсказки — свойство application.title.
Наконец, в поле uflags вы должны сообщить системе, что именно вы от нее хотите, или, другими словами, какие из полей hicon, ucallbackmessage и sztip вы на самом деле заполнили. В этом поле предусмотрена комбинация трех флагов: nif_icon, nif_message и nif_tip. Вы можете заполнить, скажем, поле sztip, но если вы при этом не установили флаг nif_tip, созданный вами значок не будет иметь строки с подсказкой.
Два приведенных ниже метода иллюстрируют сказанное. Первый из них создает значок на system tray, а второй — уничтожает его.
Код:
const wm_mytraynotify = wmjjser + 123;
procedure tforml.createtraylcon(n:integer);
var nidata : tnotifyicondata;
begin
with nidata do
begin
cbsize := sizeof{tnotifyicondata) ;
wnd := self.handle;
uid := n;
ufiags := nif_icon or nif_message or nifjtip;
ucallbackmessage := wm_mytraynotify;
hicon := application.icon.handle;
sztip := 'this is traylcon example';
end;
shell_notifyicon(nim_add, @nidata);
end;
procedure tforml.deletetraylcon(n:integer);
var nidata : tnotifylcondata; begin
with nidata do
begin
cbsize := sizeof(tnotifylcondata);
wnd := self.handle; uid := n; end;
shell_notifyicon(nim_delete, @nidata);
end;
Примечание
he забывайте уничтожать созданные вами значки на system tray. Это не делается автоматически даже при закрытии приложения. Значок будет удален только после перезагрузки системы.
Сообщение, задаваемое в поле ucallbackmessage, по сути дела является единственной ниточкой, связывающей вас со значком после его создания. Оно объединяет в себе несколько сообщений. Когда к вам пришло такое сообщение (в примере, рассмотренном выше, оно имеет идентификатор wm_mytraynotify), поля в переданной в обработчик структуре типа tmessage распределены так. Параметр wparam содержит номер значка (тот самый, что задавался в поле uid при его создании), а параметр lparam — идентификатор сообщения от мыши, вроде wm_mousemove, wm_lbuttondown и т. п. К сожалению, остальная информация из этих сообщений теряется.
Координаты мыши в момент события придется узнать, вызвав функцию api getcursorpos:
Код:
procedure tforml.wmicon(var msg: tmessage);
var p : tpoint; begin case msg.lparam of
wm_lbuttondown:
begin
getcursorpos(p);
setforegroundwindow(application.mainform.handle); popupmenul.popup(p.x, p.y);
end;
wm_lbuttonup :
end;
end;
Обратите внимание, что при показе всплывающего меню недостаточно просто вызвать метод popup. При этом нужно вынести главную форму приложения на передний план, в противном случае она не получит сообщений от меню.
Теперь решим еще две задачи. Во-первых, как сделать, чтобы приложение минимизировалось не на Панель задач (taskbar), а на system tray? И более того — как сразу запустить его в минимизированном виде, а показывать главную форму только по наступлении определенного события (приходу почты, наступлению определенного времени и т. п.).
Ответ на первый вопрос очевиден. Если минимизировать не только окно главной формы приложения (application.mainform.handle), но и окно приложения (application.handle), то приложение полностью исчезнет "с экранов радаров". В этот самый момент нужно создать значок на панели system tray. В его всплывающем меню должен быть пункт, при выборе которого оба окна восстанавливаются, а значок удаляется.
Чтобы приложение запустилось сразу в минимизированном виде и без главной формы, следует к вышесказанному добавить установку свойства application.showmainform в значение false. Здесь возникает одна сложность — если главная форма создавалась в невидимом состоянии, ее компоненты будут также созданы невидимыми. Поэтому при первом ее показе установим их свойство visible в значение true. Чтобы не повторять это дважды, установим флаг — глобальную переменную shownonce:
Код:
procedure tforml.hidemainform;
begin
appiication.showmainform := false;
showwindow(application.handle, sw_hide);
showwindow(application.mainform.handle, sw_hide);
end;
procedure tforml.restoremainform;
var i,j : integer;
begin
appiication.showmainform := true;
showwindow(application.handle, sw_restore); showwindow(application.mainform.handle, sw_restore);
if not shownonce then begin
for i := 0 to application.mainform.componentcount -1 do if application.mainform.components[i] is twincontrol then with application.mainform.components[i] as twincontrol do if visible then
begin
showwindow(handle, sw_showdefault);
for j := 0 to componentcount -1 do if components[j] is twincontrol then
showwindow((components[j] as twincontrol).handle, sw_showdefault);
end;
shownonce := true;
end;
end;
procedure tforml.wmsyscommand(var msg: tmessage);
begin inherited;
if (msg.wparam=sc_minimize) then
begin
hidemainform; createtraylcon(l) ;
end;
end;
procedure tforml.fileopenltemlclick(sender: tobject); begin
restoremainform;
deletetraylcon(l);
end;
Теперь у вас в руках полноценный набор средств для работы с панелью system tray.
В заключение необходимо добавить, что все описанное реализуется не в операционной системе, а в оболочке ОС — Проводнике (explorer). В принципе, и windows nt 4/2000, и windows 95/98 допускают замену оболочки ОС на другие, например dashboard или lightstep. Там функции панели system tray могут быть не реализованы или реализованы через другие api. Впрочем, случаи замены оболочки достаточно редки.