Shell_NotifyIcon PROTO dwMessage:DWORD ,pnid:DWORD
dwMessage
是发送到WINDOWS外壳的消息:
NIM_ADD
把小图标加到系统托盘区。
NIM_DELETE
从系统托盘中删除小图标。
NIM_MODIFY 修改小图标。
pnid
是指向NOTIFYICONDATA型结构体变量的指针。
如果您想要加入一个小图标就用NIM_ADD,删除时使用NIM_DELETE消息。
.386
.MODEL FLAT,STDCALL
option casemap:none
INCLUDE \MASM32\INCLUDE\WINDOWS.INC
INCLUDE \MASM32\INCLUDE\USER32.INC
INCLUDE \MASM32\INCLUDE\KERNEL32.INC
INCLUDE \MASM32\INCLUDE\SHELL32.INC
INCLUDELIB \MASM32\LIB\USER32.LIB
INCLUDELIB \MASM32\LIB\KERNEL32.LIB
INCLUDELIB \MASM32\LIB\SHELL32.LIB
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 DESTROYMENU,HPOPUPMENU
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 SETFOREGROUNDWINDOW,HWND
INVOKE TRACKPOPUPMENU,HPOPUPMENU,TPM_RIGHTALIGN,PT.X,PT.Y,NULL,HWND,NULL
INVOKE POSTMESSAGE,HWND,WM_NULL,0,0
.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
当主窗口创建时,将会创建一个弹出式菜单,并且加入两个菜单项。 AppendMenu的语法如下:
AppendMenu PROTO hMenu:DWORD, uFlags:DWORD, uIDNewItem:DWORD, lpNewItem:DWORD主窗口创建完成后,用户就可以开始测试了。这时按下最小化键。
hMenu 是将要加入菜单项的菜单的句柄。 uFlags 告诉WINDOWS要加入的菜单项是位图、字符串或自画的项目以及是可用、不可用或灰色显示等。您可以从WIN32 API 指南中得到全部的标志位的信息。在我们的例子中使用标志位MF_STRING,它表示我们加入的菜单项是字符串。 uIDNewItem 是菜单项的ID号。这是一个用户自定义的值,它用来唯一地代表菜单项。. lpNewItem 用来指定菜单项的内容,具体代表什么取决于uFlags中指定的标志。我们前面指定了MF_STRING标志,所以此处代表一个字符串
.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
这时我们来给NOTIFYICONDATA型结构体变量赋值。IDI_TRAY是在代码开始处定义的一个数值常量,您可以任意设定它的值。由于我们仅有一个图标,所以这一点并不重要,如果要同时加入几个系统图标的话,那么每个图标都要有一个唯一的ID号。由于我们指定了一个图标NIF_ICON,所以我们要在uFlags成员变量中指定所有的标志位,我们还指定了一个自定义的消息NIF_MESSAGE和帮助文本NIF_TIP。
WM_SHELLNOTIFY
被定义为WM_USER+5,只要是唯一的值,就无所谓是多少了,只要大于WM_USER。我们这里用的是WINDOWS登录时的图标,当然您可以使用任意您想要用的图标,您可以用LoadIcon函数从资源中装载,该函数返回一个图标的句柄。最后我们在szTip中放入当鼠标放在图标时显示的提示文本。为了达到“最小化然后只显示图标的效果”,我们在这时隐藏掉主窗口。
接下来,我们调用Shell_NotifyIcon函数并指定标志位NIM_ADD把图标加到系统托盘中去。
现在我们的主窗口隐藏了,图标显示在系统托盘中。如果您让鼠标从图标上滑过,将看到提示文本。如果您双击小图标,主窗口就会显示,图标将消失。
.ELSEIF UMSG==WM_SHELLNOTIFY
.IF WPARAM==IDI_TRAY
.IF LPARAM==WM_RBUTTONDOWN
INVOKE GETCURSORPOS,ADDR PT
INVOKE SETFOREGROUNDWINDOW,HWND
INVOKE TRACKPOPUPMENU,HPOPUPMENU,TPM_RIGHTALIGN,PT.X,PT.Y,NULL,HWND,NULL
INVOKE POSTMESSAGE,HWND,WM_NULL,0,0
.ELSEIF LPARAM==WM_LBUTTONDBLCLK
INVOKE SENDMESSAGE,HWND,WM_COMMAND,IDM_RESTORE,0
.ENDIF
.ENDIF
当在系统托盘中的图标发生鼠标事件时,您的窗口将接收到WM_SHELLNOTIFY消息,该消息是在uCallbackMessage成员变量中指定的。在接收到该消息时,wParam中包含了图标的ID号,lParam中包含了鼠标动作的原始数据。在上面的代码中,我们首先检测是否是我们感兴趣的消息。如果是的话,我们在看看是什么消息。因为我们只对右击和双击事件感兴趣,所以我们仅仅处理WM_RBUTTONDOWN和WM_LBUTTONDBLCLK消息。
如果是WM_RBUTTONDOWN,我们调用GetCursorPos来得到鼠标光标所在的当前屏幕位置。注意我指的是屏幕位置,即,其坐标是相对于整个的屏幕的。譬如,如果屏幕的解析读640*480,那么它的右下角的坐标是x==639
,y==479。如果您想要把屏幕位置转换成窗口的坐标,可以调用ScreenToClient函数
我们想要在当前的位置显示弹出式菜单,我们就调用TrackPopupMenu函数,该函数需要屏幕的坐标,由GetCursorPos函数返回的坐标就可以原封不动的拿过来用。
TrackPopupMenu的原型如下:
TRACKPOPUPMENU PROTO HMENU:DWORD, UFLAGS:DWORD, X:DWORD, Y:DWORD, NRESERVED:DWORD, HWND:DWORD, PRCRECT:DWORD
当用户双击图标时,我们给我们自己的窗口发送WM_COMMAND消息,并指定消息为IDM_RESTORE,这样可以达到和在弹出式菜单中选择“Restore”菜单项同样的效果。为了能够接收到双击消息,主窗口必须要有的CS_DBLCLKS 风格。
INVOKE SHELL_NOTIFYICON,NIM_DELETE,ADDR NOTE
MOV EAX,WPARAM
.IF AX==IDM_RESTORE
INVOKE SHOWWINDOW,HWND,SW_RESTORE
.ELSE
INVOKE DESTROYWINDOW,HWND
.ENDIF
当用户选择恢复主窗口时,我们调用Shell_NotifyIcon函数来删除掉系统托盘中的图标,这一次我们要指定NIM_DELETE消息。接下来我们把主窗口恢复到原始的状态。如果用户选择了Exit菜单项,我们不但把图标给删除掉,也从整个的应用程序中退出。