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.
WM_SHELLNOTIFY
equ WM_USER+5
IDI_TRAY
equ 0
IDM_RESTORE
equ 1000
IDM_EXIT
equ 1010
WinMain
PROTO :DWORD,:DWORD,:DWORD,:DWORD
.data
ClassName
db "TrayIconWinClass",0
AppName
db "TrayIcon Demo",0
RestoreString
db "&Restore",0
ExitString
db "E&xit Program",0
.data?
hInstance
dd ?
note
NOTIFYICONDATA <>
hPopupMenu
dd ?
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke WinMain, hInstance,NULL,NULL, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain
proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW or CS_DBLCLKS
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_APPWORKSPACE
mov wc.lpszMenuName,NULL
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPED+WS_CAPTION+WS_SYSMENU+WS_MINIMIZEBOX+WS_MAXIMIZEBOX+WS_VISIBLE,CW_USEDEFAULT,\
CW_USEDEFAULT,350,200,NULL,NULL,\
hInst,NULL
mov hwnd,eax
.while TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.endw
mov eax,msg.wParam
ret
WinMain
endp
WndProc
proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL pt:POINT
.if uMsg==WM_CREATE
invoke CreatePopupMenu
mov hPopupMenu,eax
invoke AppendMenu,hPopupMenu,MF_STRING,IDM_RESTORE,addr RestoreString
invoke AppendMenu,hPopupMenu,MF_STRING,IDM_EXIT,addr ExitString
.elseif uMsg==WM_DESTROY
invoke PostQuitMessage,NULL
.elseif uMsg==WM_SIZE
.if wParam==SIZE_MINIMIZED
mov note.cbSize,sizeof NOTIFYICONDATA
push hWnd
pop note.hwnd
mov note.uID,IDI_TRAY
mov note.uFlags,NIF_ICON+NIF_MESSAGE+NIF_TIP
mov note.uCallbackMessage,WM_SHELLNOTIFY
invoke LoadIcon,NULL,IDI_WINLOGO
mov note.hIcon,eax
invoke lstrcpy,addr note.szTip,addr AppName
invoke ShowWindow,hWnd,SW_HIDE
invoke Shell_NotifyIcon,NIM_ADD,addr note
.endif
.elseif uMsg==WM_COMMAND
.if lParam==0
invoke Shell_NotifyIcon,NIM_DELETE,addr note
mov eax,wParam
.if ax==IDM_RESTORE
invoke ShowWindow,hWnd,SW_RESTORE
.else
invoke DestroyWindow,hWnd
.endif
.endif
.elseif uMsg==WM_SHELLNOTIFY
.if wParam==IDI_TRAY
.if lParam==WM_RBUTTONDOWN
invoke GetCursorPos,addr pt
invoke TrackPopupMenu,hPopupMenu,TPM_RIGHTALIGN,pt.x,pt.y,NULL,hWnd,NULL
.elseif lParam==WM_LBUTTONDBLCLK
invoke SendMessage,hWnd,WM_COMMAND,IDM_RESTORE,0
.endif
.endif
.else
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.endif
xor eax,eax
ret
WndProc
endp
end
start
.if uMsg==WM_CREATE
invoke CreatePopupMenu
mov hPopupMenu,eax
invoke AppendMenu,hPopupMenu,MF_STRING,IDM_RESTORE,addr RestoreString
invoke AppendMenu,hPopupMenu,MF_STRING,IDM_EXIT,addr 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.
.elseif uMsg==WM_SIZE
.if wParam==SIZE_MINIMIZED
mov note.cbSize,sizeof NOTIFYICONDATA
push hWnd
pop note.hwnd
mov note.uID,IDI_TRAY
mov note.uFlags,NIF_ICON+NIF_MESSAGE+NIF_TIP
mov note.uCallbackMessage,WM_SHELLNOTIFY
invoke LoadIcon,NULL,IDI_WINLOGO
mov note.hIcon,eax
invoke lstrcpy,addr note.szTip,addr AppName
invoke ShowWindow,hWnd,SW_HIDE
invoke Shell_NotifyIcon,NIM_ADD,addr note
.endif
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.
.elseif uMsg==WM_SHELLNOTIFY
.if wParam==IDI_TRAY
.if lParam==WM_RBUTTONDOWN
invoke GetCursorPos,addr pt
invoke TrackPopupMenu,hPopupMenu,TPM_RIGHTALIGN,pt.x,pt.y,NULL,hWnd,NULL
.elseif lParam==WM_LBUTTONDBLCLK
invoke SendMessage,hWnd,WM_COMMAND,IDM_RESTORE,0
.endif
.endif
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:
invoke Shell_NotifyIcon,NIM_DELETE,addr note
mov eax,wParam
.if ax==IDM_RESTORE
invoke ShowWindow,hWnd,SW_RESTORE
.else
invoke DestroyWindow,hWnd
.endif
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.