Добавление иконки в SystemTray средствами Visual Basic (исходники)

Источник: VBRussian
Кирилл Головин

Эта статья является самодостаточной, то есть в ней дана исчерпывающая информация по созданию иконки в SystemTray с помощью VB. Однако при этом она является компиляцией общедоступных источников, то есть заслуга автора состоит лишь в сборе этой информации в одном месте и пояснениях.

Основы создания иконки изложены в FAQ Льва Серебрякова. Используется пример на VB от Alexander Shherbakov. Описания функций и констант из книги Daniel Applemana и API.TXT. Вопросы связанные с редактором ресурсов не рассматриваются.

Единственная функция для работы с иконкой Shell_NotifyIcon. Ее описание на VB выглядит так:

Declare Function Shell_NotifyIcon Lib "shell32.dll" Alias "Shell_NotifyIconA" _
(ByVal dwMessage As dwMess, lpData As NOTIFYICONDATA) As Long

Возвращает ноль в случае ошибки

Тип dwMess описывается так:

Public Enum dwMess


NIM_ADD = &H0 ' Добавление иконки
NIM_DELETE = &H2 ' Удаление иконки
NIM_MODIFY = &H1 ' Изменение параметров иконки
End Enum

Переменная dwMessage должна иметь одно из этих значений.

Тип NOTIFYICONDATA имеет следующую структуру:

Type NOTIFYICONDATA

cbSize As Long ' Размер переменной типа NOTIFYICONDATA
hwnd As Long ' Указатель окна создающего иконку
uID As Long ' Указатель на иконку в пределах приложения
uFlags As uF ' Маска для следующих параметров
uCallbackMessage As CallMess ' Возвращаемое событие
hIcon As Long ' Указатель на изображение для иконки
szTip As String * 64 ' Всплывающий над иконкой текст
End Type

Где тип uF имеет вид:

Public Enum uF

NIF_MESSAGE = &H1 ' Значение имеет uCallbackMessage
NIF_ICON = &H2 ' Значение имеет hIcon 
NIF_TIP = &H4 ' Значение имеет szTip

End Enum 

Эти константы можно применять в любых сочетаниях, для определения какой из параметров имеет значение.
 
Тип CallMess:

Public Enum CallMess

WM_MOUSEMOVE = &H200 
WM_LBUTTONDOWN = &H201 
WM_LBUTTONUP = &H202
WM_LBUTTONDBLCLK = &H203
WM_RBUTTONDOWN = &H204
WM_RBUTTONUP = &H205
WM_RBUTTONDBLCLK = &H206


WM_MBUTTONDOWN = &H207
WM_MBUTTONUP = &H208
WM_MBUTTONDBLCLK = &H209
WM_SETFOCUS = &H7
WM_KEYDOWN = &H100
WM_KEYFIRST = &H100
WM_KEYLAST = &H108
WM_KEYUP = &H101

End Enum 

Эти константы обозначают, какое событие возвращается вызывающей форме. Буквально, все, что будет происходить с иконкой, будет вызывать у формы одно из перечисленных событий. Ясно, что самое частое событие самой иконки это MouseMove, но для формы оно будет выглядеть как событие заданное переменной uCallbackMessage. Как же узнать, что в действительности произошло с иконкой? Это можно узнать через переменные X и Y событий MouseMove, MouseDown и MouseUp вызывающей формы. При этом Y, если событие произошло с иконкой, а не формой, всегда будет равно нулю, а X несет информацию о событии с иконкой.
 
О параметре X следует сказать отдельно. Действительно, он передает информацию о событиях с иконкой, однако эти значения зависят от масштабного коэффициента системного шрифта, но не напрямую, а через параметр свойства TwipsPerPixelX объекта Screen. То есть для одной и той же системы, при разных величинах системного шрифта, значения будут разными. Начальными значениями событий являются следующие:

MouseMove ? 512
LeftButtonDown ? 513
LeftButtonUp - 514
LeftButtonDblClick - 515
RightButtonDown - 516
RightButtonUp ? 517
RightButtonDblClick - 518
 
Для того чтобы узнать действующие в данной системе значения их следует умножить на Screen.TwipsPerPixelX

Как же узнать, что событие произошло с иконкой, а не с формой? Просто, по значению Y, равному нулю. Но есть и другой способ, если используется двухкнопочная мышь то параметр Button в событиях MouseDown и MouseUp формы, будет принимать значения 1 и 2, и при uCallbackMessage равно WM_MBUTTONDOWN=&H207 или WM_MBUTTONUP = &H208 Button равен 4, если событие с иконкой. Само собой разумеется, что возвращаемые X значения следуют одно за другим, как и события (Down->Up->DbClick),поэтому невозможно на одну кнопку мыши назначить два события, к примеру, Click и DbClick. События не связанные с мышью не несут практически ни какой информации, и обычно не используются, следует так же отметить, что количество констант uCallbackMessage намного больше и здесь приведена лишь небольшая часть.

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

Следующий момент, который нужно осветить это получение hIcon (указателя на картинку). Предполагается, что иконка будет находится в исполняемом файле или в DLL с ресурсами, но ни в коем случае не валяется в виде ICO файла. Если иконка запакована в DLL, то нам понадобятся две функции:

Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal _ 
lpLibFileName As String) As Long

Возвращающая hInstance библиотеки с именем lpLibFileName. Достаточно указать только имя файла с расширением, без пути. Возвращает ноль в случае ошибки

Declare Function LoadIconA Lib "user32" (ByVal hInstance As Long, ByVal _
lpIconName As String) As Long

Возвращающая hIcon для иконки указанной параметром lpIconName в библиотеке. Этот параметр может быть String или Long, в зависимости от данного вами наименования в Res файле, соответственно надо изменить декларацию. Можно передать и число как строку, для этого перед числом ставится знак #, а все это берется в кавычки. Следует заметить, что использование срокового параметра не желательно из за значительно большего размера занимаемой памяти и соответственно, большего времени на передачу параметра. Функция возвращает ноль в случае ошибки

Понадобится так же функция:

Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long

Выгружающая библиотеку из памяти. Параметр hLibModule это hInstanse, возвращаемое LoadLibrary. Возвращает ноль в случае ошибки. Обязательно надо не забыть выгрузить из памяти библиотеку, для освобождения памяти. Выгрузку можно произвести сразу же после добавления иконки в SystemTray.

Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" _ 
(ByVal lpModuleName As String) As Long

Возвращающей hInstanse нашего приложения. В качестве lpModuleName передается имя EXE файла с расширением. Следует быть внимательным, так как имя процесса в TaskMenager не всегда соответствует имени процесса для Windows. Я использую для определения имени DLLView, можно воспользоваться, встроенным в VB System Information. Функция возвращает действительное значение только при работе скомпилированного приложения, а в режиме отладки возвращает ноль, ведь реального процесса при отладке не существует. Свойство hInstanse объекта App всегда возвращает действительное значение, однако при отладке из за отсутствия процесса LoadIcon возвращает 0, и создается "пустая" иконка, тем не менее годная для отладки (реагирующая на все события).

Полученное hInstanse передаем LoadIconA, в качестве lpIconName указываем номер или имя иконки в Res файле, как и в случае с DLL. Выгружать в этом случае ничего не надо.

Создание иконки можно проиллюстрировать следующим примером. Считается, что иконка с номером 101 находится в файле Project1.exe. Понятно, что пока мы его не скомпилировали, ее там нет (да и самого файла нет). Форма приложения называется Form1.

Dim NID As NOTIFYICONDATA 

Sub AddIcon() 

Dim IDLib As Long ' Указатель на библиотеку
Dim IDIcon As Long ' Указатель на иконку 
Const IDMyIcon = 101 ' Идентификатор иконки внутри приложения
Dim AddResult As Long ' Результат добавления иконки

IDLib = GetModuleHandle("Project1.exe") ' Получаем hInstanse
IDIcon = LoadIcon(IDLib, "#101") ' Получаем hIcon 

' Заполняем структуру NID типа NOTIFYICONDATA

NID.cbSize = Len(NID) ' Размер структуры
NID.hwnd = Form1.hWnd ' Указатель на форму
NID.uID = IDMyIcon ' Идентификатор иконки
NID.uFlags = NIF_MESSAGE + NIF_ICON + NIF_TIP 'Указываем, что действующими являются поля 
'uCallBackMessage, hIcon и szTip.
NID.uCallbackMessage = WM_LBUTTONDOWN ' Указываем, что событием возвращаемым в форму 
'является MouseDown с параметром Button = 2
NID.hIcon = IDIcon ' Указатель на иконку в файле
NID.szTip = Left$("MyIcon", 63) & Chr(0) ' Передаем всплывающую фразу "MyIcon", при этом обрезаем 
'ее до 63 символов и добавляем 64-й символ с кодом ноль
AddResult = Shell_NotifyIcon(NIM_ADD, NID) ' Вызываем функцию, через параметр dwMessage указываем, 
'что следует добавить иконку, и передаем заполненный NID

End Sub

Удаление созданной иконки можно сделать так:

Sub DeleteIcon()

Dim DeletResult As Long
DeleteResult = Shell_NotifyIcon(NIM_DELETE, NID) ' Вызываем функцию, через dwMessage указываем, 
'что следует удалить иконку, при этом, раз переменная NID описана на уровне модуля, не следует 
'заполнять ее заново 

End Sub 

Размер структуры достаточно указывать один раз, так как за время жизни переменной он измениться не может, и в данном виде составляет 88 байт. Даже при изменении всплывающей строки ее длина (строки) не будет больше 64 байт.

Для модификации иконки надо вызвать Shell_NotifyIcon с параметром dwMessage равным NIM_MODIFY и NID с внесенными изменениями, при этом параметр uFlags будет указывать, какие из параметров изменены.

В форме Form1 для обработки, к примеру, DbClick левой кнопкой мыши по иконке можно применить следующий код:

Private Sub Form_MouseDown(Button As Integer, Shift As Integer _
X As Single, Y As Single) ' Событие MouseDown происходит не потому, 
                          'что пользователь нажал на кнопку мыши над иконкой, а из-за того, 
                          'что параметр uCallbackMessage имеет значение WM_LBUTTONDOWN 

If Y = 0 Then ' Y = 0 если событие с иконкой 
   Select Case X
      Case 515*Screen.TwipsPerPixelX ' Значение X при LeftDblClick 
                                     ' Код, выполняемый в случае LeftDblClick 
   End Select
End If

End Sub

Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=10388