Shell_NotifyIcon PROTO dwMessage:DWORD ,pnid:DWORD
dwMessage
is the type of message to send to the shell.
NIM_ADD Adds an icon to the status
area.
NIM_DELETE Deletes an icon from the
status area.
NIM_MODIFY Modifies an icon in the
status area.
pnid
is the pointer to a NOTIFYICONDATA structure
filled with proper values
If you want to add an icon
to the tray, use NIM_ADD message, if you want to remove the icon, use NIM_DELETE.
; Equates:
[IDI_TRAY 0 IDM_RESTORE 1000 IDM_EXIT 1010]
[WM_SHELLNOTIFY
0405] ; WM_USER+5
____________________________________________________________________________________________
; Data:
[ClassName:
'TrayIconWinClass' 0
AppName:
'TrayIcon Demo' 0
RestoreString:
'&Restore' 0
ExitString:
'E&xit Program' 0]
[hPopupMenu: 0 WindowHandle: 0]
[WindowClass:
style:
&CS_HREDRAW+&CS_VREDRAW+&CS_DBLCLKS
lpfnWndProc:
MainWindowProc cbClsExtra: 0 cbWndExtra: 0
hInstance:
0 hIcon: 0 hCursor: 0 hbrBackground: &COLOR_APPWORKSPACE
lpszMenuName:
0 lpszClassName: ClassName]
[FirstMessage:
0 #7]
____________________________________________________________________________________________
____________________________________________________________________________________________
Main:
call 'Kernel32.GetModuleHandleA' 0 | mov D§hInstance eax
call 'User32.LoadIconA' 0 &IDI_APPLICATION | mov D§hIcon
eax
call 'User32.LoadCursorA' 0 &IDC_ARROW | mov D§hCursor eax
call 'User32.RegisterClassA' WindowClass
call 'User32.CreateWindowExA' &WS_EX_CLIENTEDGE ClassName AppName,
&WS_OVERLAPPEDWINDOW+&WS_VISIBLE,
&CW_USEDEFAULT &CW_USEDEFAULT 350 200,
0 D§hInstance 0
mov D§WindowHandle eax, D§NID_hwnd eax
call 'User32.ShowWindow' D§WindowHandle &SW_SHOW
call 'User32.UpdateWindow' D§WindowHandle
add D§NID_cbSize 64
L1:
call 'User32.GetMessageA' FirstMessage 0 0 0 | cmp eax 0 | je L9>
call 'User32.TranslateMessage' FirstMessage
call 'User32.DispatchMessageA' FirstMessage
jmp L1<
L9: call 'Kernel32.ExitProcess' 0
_____________________________________________________________________________________
[POINT: Point_X: 0 Point_Y: 0]
[RECT: Rect_Left: 0 Rect_Top: 0 Rect_Right: 0 Rect_Bottom: 0]
[NOTIFYICONDATA:
NID_cbSize:
len
NID_hwnd:
0
NID_uID:
IDI_TRAY
NID_uFlags:
&NIF_ICON+&NIF_MESSAGE+&NIF_TIP
NID_uCallbackMessage:
WM_SHELLNOTIFY
NID_hIcon:
0] [NID_szTip: B§ 0 #64]
____________________________________________________________________________________________
____________________________________________________________________________________________
Proc
MainWindowProc:
Arguments @Adressee, @Message, @wParam, @lParam
pushad
.If D@Message = &WM_CREATE
call 'USER32.CreatePopupMenu'
mov D§hPopupMenu eax
call 'USER32.AppendMenuA' D§hPopupMenu &MF_STRING IDM_RESTORE
RestoreString
call 'USER32.AppendMenuA' D§hPopupMenu &MF_STRING IDM_EXIT
ExitString
.Else_If D@Message = &WM_DESTROY
call 'USER32.DestroyMenu' D§hPopupMenu
call 'USER32.PostQuitMessage' &NULL
.Else_If D@Message = &WM_SIZE
..If D@wParam = &SIZE_MINIMIZED
move D§NID_hwnd D@Adressee
call 'User32.LoadIconA' &NULL &IDI_WINLOGO
mov D§NID_hIcon eax, esi AppName, edi NID_szTip
While B§esi a 0
movsb
End_While
call 'User32.ShowWindow' D@Adressee &SW_HIDE
call 'Shell32.Shell_NotifyIconA' &NIM_ADD NOTIFYICONDATA
..End_If
.Else_If D@Message = &WM_COMMAND
..If D@lParam = 0
call 'Shell32.Shell_NotifyIconA' &NIM_DELETE NOTIFYICONDATA
mov eax D@wParam
If ax = IDM_RESTORE
call 'User32.ShowWindow' D@Adressee &SW_RESTORE
Else
call 'User32.DestroyWindow' D@Adressee
End_If
..End_If
.Else_If D@Message = WM_SHELLNOTIFY
..If D@wParam = IDI_TRAY
If D@lParam = &WM_RBUTTONDOWN
call 'User32.GetCursorPos' POINT
call 'User32.SetForegroundWindow' D@Adressee
call 'User32.TrackPopupMenu' D§hPopupMenu &TPM_RIGHTALIGN+&TPM_RIGHTBUTTON,
D§Point_X D§Point_Y &NULL D@Adressee &NULL
call 'User32.PostMessageA' D@Adressee &NULL 0 0
Else_If D@lParam = &WM_LBUTTONDBLCLK
call 'User32.SendMessageA' D@Adressee &WM_COMMAND IDM_RESTORE 0
End_If
..End_If
.Else
popad
call 'User32.DefWindowProcA' D@Adressee D@Message D@Wparam D@Lparam
Exit
.End_If
popad | mov eax &FALSE
EndP
.If D@Message = &WM_CREATE
call 'USER32.CreatePopupMenu'
mov D§hPopupMenu eax
call 'USER32.AppendMenuA' D§hPopupMenu &MF_STRING IDM_RESTORE
RestoreString
call 'USER32.AppendMenuA' D§hPopupMenu &MF_STRING IDM_EXIT
ExitString
When
the main window is created, it creates a popup menu and append two menu
items. AppendMenu has the following syntax:
AppendMenu PROTO hMenu:DWORD, uFlags:DWORD, uIDNewItem:DWORD, lpNewItem:DWORDAfter the popup menu is created, the main window waits patiently for the user to press minimize button.
- hMenu is the handle of the menu you want to append the item to
- uFlags tells Windows about the menu item to be appended to the menu whether it is a bitmap or a string or an owner-draw item, enabled, grayed or disable etc. You can get the complete list from win32 api reference. In our example, we use MF_STRING which means the menu item is a string.
- uIDNewItem is the ID of the menu item. This is a user-defined value that is used to represent the menu item.
- lpNewItem specifies the content of the menu item, depending on what you specify in uFlags member. Since we specify MF_STRING in uFlags member, lpNewItem must contain the pointer to the string to be displayed in the popup menu.
.Else_If D@Message = &WM_SIZE
..If D@wParam = &SIZE_MINIMIZED
move D§NID_hwnd D@Adressee
call 'User32.LoadIconA' &NULL &IDI_WINLOGO
mov D§NID_hIcon eax, esi AppName, edi NID_szTip
While B§esi a 0
movsb
End_While
call 'User32.ShowWindow' D@Adressee &SW_HIDE
call 'Shell32.Shell_NotifyIconA' &NIM_ADD NOTIFYICONDATA
..End_If
We
use this opportunity to fill NOTIFYICONDATA structure. IDI_TRAY is just
a constant defined at the beginning of the source code. You can set it
to any value you like. It's not important because you have only one tray
icon. But if you will put several icons into the system tray, you need
unique IDs for each tray icon. We specify all flags in uFlags member because
we specify an icon (NIF_ICON), we specify a custom message (NIF_MESSAGE)
and we specify the tooltip text (NIF_TIP). WM_SHELLNOTIFY is just a custom
message defined as WM_USER+5. The actual value is not important so long
as it's unique. I use the winlogo icon as the tray icon here but you can
use any icon in your program. Just load it from the resource with LoadIcon
and put the returned handle in hIcon member. Lastly, we fill the szTip
with the text we want the shell to display when the mouse is over the icon.
We
hide the main window to give the illusion of "minimizing-to-tray-icon"
appearance.
Next
we call Shell_NotifyIcon with NIM_ADD message to add the icon to
the system tray.
Now
our main window is hidden and the icon is in the system tray. If you move
the mouse over it, you will see a tooltip that displays the text we put
into szTip member. Next, if you double-click at the icon, the main window
will reappear and the tray icon is gone.
.Else_If D@Message = WM_SHELLNOTIFY
..If D@wParam = IDI_TRAY
If D@lParam = &WM_RBUTTONDOWN
call 'User32.GetCursorPos' POINT
call 'User32.SetForegroundWindow' D@Adressee
call 'User32.TrackPopupMenu' D§hPopupMenu &TPM_RIGHTALIGN+&TPM_RIGHTBUTTON,
D§Point_X D§Point_Y &NULL D@Adressee &NULL
call 'User32.PostMessageA' D@Adressee &NULL 0 0
Else_If D@lParam = &WM_LBUTTONDBLCLK
call 'User32.SendMessageA' D@Adressee &WM_COMMAND IDM_RESTORE 0
End_If
..End_If
When
a mouse event occurs over the tray icon, your window receives WM_SHELLNOTIFY
message which is the custom message you specified in uCallbackMessage member.
Recall that on receiving this message, wParam contains the tray icon's
ID and lParam contains the actual mouse message. In the code above, we
check first if this message comes from the tray icon we are interested
in. If it does, we check the actual mouse message. Since we are only interested
in right mouse click and double-left-click, we process only WM_RBUTTONDOWN
and WM_LBUTTONDBLCLK messages.
If
the mouse message is WM_RBUTTONDOWN, we call GetCursorPos to obtain the
current screen coordinate of the mouse cursor. When the function returns,
the POINT structure is filled with the screen coordinate of the mouse cursor.
By screen coordinate, I mean the coordinate of the entire screen without
regarding to any window boundary. For example, if the screen resolution
is 640*480, the right-lower corner of the screen is x==639 and y==479.
If you want to convert the screen coordinate to window coordinate, use
ScreenToClient function.
However,
for our purpose, we want to display the popup menu at the current mouse
cursor position with TrackPopupMenu call and it requires screen coordinates,
we can use the coordinates filled by GetCursorPos directly.
TrackPopupMenu
has the following syntax:
call 'Shell32.Shell_NotifyIconA' &NIM_DELETE NOTIFYICONDATA
mov eax D@wParam
If ax = IDM_RESTORE
call 'User32.ShowWindow' D@Adressee &SW_RESTORE
Else
call 'User32.DestroyWindow' D@Adressee
End_If
When
the user selects Restore menu item, we remove the tray icon by calling
Shell_NotifyIcon again, this time we specify NIM_DELETE as the message.
Next, we restore the main window to its original state. If the user selects
Exit menu item, we also remove the icon from the tray and destroy the main
window by calling DestroyWindow.