Отправляет email-рассылки с помощью сервиса Sendsay

Программирование на Visual С++

  Все выпуски  

Служба Рассылок Городского Кота


Служба Рассылок Городского Кота
       


   Программирование на Visual C++

    выпуск No.11 от 22/07/2000

Добрый день всем!

В ответ на публикацию вопроса Дмитрия о System Tray в предыдущем выпуске помимо прямых ответов пришло еще несколько просьб рассказать о том, как в системный tray вообще помещать иконки. Я, видимо, был излишне оптимистичен, когда посчитал, что это все знают ;) Так что я решил поведать уважаемым читателям об этом в данном выпуске, в рубрике "WINAPI", в расчете на то, что эта информация будет полезна многим. Получается, сегодняшний выпуск целиком посвящен system tray ;)

/ / / / WINAPI / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /

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

Начнем с начала - нужно поместить иконку в tray. Сами вы это вряд ли сделаете - да это и не нужно. За вас это сделает Windows, вам нужно только сообщить операционной системе о своем намерении. Для этого служит функция Shell_NotifyIcon( ), которая позволяет создавать, изменять и удалять такие иконки.

Первый аргумент этой функции  это код операции, которую вам нужно осуществить. Он имеет три возможных значения - NIM_ADD, NIM_DELETE и NIM_MODIFY. В пояснениях, по-моему, не нуждается. Второй параметр - указатель на структуру NOTIFYICONDATA. Вот как эта структура выглядит:

typedef struct _NOTIFYICONDATA {
      DWORD cbSize; // размер, обязательно указывать
      HWND hWnd;    // HWND для посылки уведомлений
      UINT uID;     // идентификатор иконки в tray, не имеет отношения к ресурсам
      UINT uFlags;  // см. ниже
      UINT uCallbackMessage; // посылается вашей функции окна
      HICON hIcon;    // дескриптор иконки
      CHAR szTip[64]; // строка с подсказкой
} NOTIFYICONDATA;

// uFlags
#define NIF_MESSAGE 0x1 // uCallbackMessage содержит информацию
#define NIF_ICON 0x2    // hIcon содержит информацию
#define NIF_TIP 0x4     // szTip содержит информацию

В принципе, назначение каждого члена этой структуры довольно прозрачно. Замечу только, что uID - это не идентификатор ресурса иконки, как можно было бы подумать, а вами определенный идентификатор для tray icon вашего приложения. Иконка, которую выводит в tray приложение, может меняться в процессе работы, но этот идентификатор остается постоянным.
Также вам нужно в uCallbackMessage записать сообщение, которое вы хотите чтобы система вам посылала в качестве уведомления о событиях, происходящих с вашей иконкой. Для этого в программе определите какое-нибудь user-defined сообщение,  например так: #define WM_TRAYNOTIFY (WM_APP+100)
WM_APP используется как раз для того, чтобы именно в таком виде и определять нужные вам сообщения.

Теперь, предположим у вас подготовлена иконка для tray:  IDI_MYTRAYICON. Нам нужно ее вывести в tray. Вот что мы делаем:

// уведомляющее сообщение
#define WM_TRAYNOTIFY (WM_APP+100)

// идентификатор иконки
#define ID_TRAYICON 1000
           
...
CString sNotifyTip =
"Название вашей программы или другая подсказка";

NOTIFYICONDATA nid;


memset (&nid, 0, sizeof(nid) );
// обнулять структуру перед использованием - хорошая привычка

nid.cbSize = sizeof(nid);
nid.hWnd = hWnd;
nid.uID = ID_TRAYICON;
nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
nid.uCallbackMessage = WM_TRAYNOTIFY;
nid.hIcon = ::LoadIcon (hInstance, MAKEINTRESOURCE(IDR_MAINFRAME) );
lstrcpyn (nid.szTip, sNotifyTip, sizeof(nid.szTip) );

Shell_NotifyIcon ( NIM_ADD, &nid );

Этот код вставьте в функцию инициализации, причем окно  вашего приложения уже должно быть создано, hWnd и hInstance должны быть определены. hWnd вы получаете при создании окна, а hInstance вам передают прямо в WinMain. Если у вас MFC-приложение, поставьте вместо них соответственно
AfxGetMainWnd()->m_hWnd и AfxGetApp()->m_hInstance.

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

NOTIFYICONDATA nid;

memset (&nid, 0, sizeof(nid) ); 

nid.cbSize = sizeof(nid);
nid.hWnd = hWnd;
nid.uID = ID_TRAYICON;

Shell_NotifyIcon( NIM_DELETE, &nid ); 

(в структуре nid достаточно теперь определить только cbSize, hWnd и  uID).

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

Добавьте в программу обработчик события WM_TRAYNOTIFY. В этом сообщении wParam - это ID иконки, а lParam - код сообщения от мыши, например WM_RBUTTONDOWN. Если у вас не MFC-приложение, просто добавьте один case в функцию окна.  Если же вы имеете дело с MFC, то сделайте следующее: в класс главного окна(диалога) добавьте функцию  afxmsg void OnTrayNotify ( WPARAM wParam, LPARAM lParam );


В карту сообщений класса добавьте следующую строку: ON_MESSAGE ( WM_TRAYNOTIFY, OnTrayNotify )
Таким образом обрабатываются пользовательские сообщения. Эта строка свяжет наше сообщение WM_TRAYNOTIFY с функцией его обработки OnTrayNotify( ). 

В этой функции проверяйте значение lParam и делайте то, что вам нужно, например, выводите меню. Как именно это делать - уже совсем другая история...

void CMyDlg::OnTrayNotify(WPARAM wParam, LPARAM lParam)
{
  if (lParam==WM_LBUTTONDOWN)
  {
    ::SetForegroundWindow(m_hWnd); // активизируем наше приложение
    AfxMessageBox("Была нажата левая кнопка");
  }
  else
  if (lParam==WM_RBUTTONDOWN)
  {
    ::SetForegroundWindow(m_hWnd);
    AfxMessageBox("Была нажата правая кнопка");
  }
}

/ / / / ВОПРОС-ОТВЕТ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /

Q. Не подскажете как в tray выводить текст, как например сделаны часы в windows?

- Dmitriy

A1. Copy from ListSOFT от 18.07.2000

"...Если хочешь, чтобы рядом с системными часами располагалась надпись, например, твое имя, то в HKEY_CURRENT_USER\ Control panel\ International\
в первые два параметра запиши его (не более 8 символов), а в третий запиши "HH:mm:ss tt". Кстати, если изменить формат времени таким способом, то строка, записанная в первые два параметра будет фигурировать во всех программах, запрашивающих время, например, в Outlook Express в графе Отправлено и Получено."

- Grigori Zagarski

Я попробовал так сделать - не получилось. У меня в этом разделе вообще всего один параметр  - "Locale". Что-то автор напутал... Может, путь указан неправильно? Хоть результат и отрицательный, я решил все же на всякий случай опубликовать этот ответ - может, тут действительно дело во мне (я проверял в Windows 98SE), ведь на ListSOFT действительно была такая публикация. А может, кто и подскажет, в чем дело.

A2. Я предлагаю набирать текст из иконок, которые должны создаваться динамически.
Подобный подход я видел в нескольких программах, где tray-иконки используются для индикации уровня занятости CPU и т.п.
Пример вывода текста в tray приаттачен. Сам вывод делается в классе CShellNotifyText. Тестовая программка организует в tray'е что-то типа таймера.
Может быть это не самый простой вариант, но пусть кто-нибудь предложит лучше :))
Хочу высказать свое положительное мнение о рассылке. Единственное, что смущает в свете последних известий от Microsoft: Кому будут нужны знания по MFC, когда все начнут программировать на Си-диез (C#)?

- Сергей Цивин

Пример я посмотрел, он работает. Но, к сожалению, у такого подхода есть один очень существенный недостаток: если TaskBar в высоту имеет больше одной полосы , никто не гарантирует, что у вас не произойдет переноса на самом  неподходящем для этого символе. Я сам смоделировал такую ситуацию, это было сделать легко и выглядело совершенно неприемлемо. Если кто-нибудь знает вдруг, как эту дилемму разрешить - пишите.
А насчет C# - вынужден повториться, он не позиционируется как конкурент VC и MFC. Microsoft полагает, что это Java-киллер. Так, в следующую версии VisualStudio известный продукт Visual J++, скорее всего, не войдет, а вместо него будет сами догадайтесь что...

/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / 

Успехов!

©Алекс Jenter    mailto:jenter@mail.ru
Красноярск, 2000.

[ Предыдущие выпуски | Статистика рассылки ]

    
     


http://subscribe.ru/
E-mail: ask@subscribe.ru

В избранное