本課中,我們將學習如何把小圖標放到系統(tǒng)托盤中去以及如何創(chuàng)建和使用彈出式菜單。
理論:
系統(tǒng)托盤是指任務條中的一個方形區(qū)域,在該區(qū)域中可以放入一些小圖標,通常您可以在此處看到系統(tǒng)提供的新時間。您自己當然也可以把快捷小圖標放到此處。下面是這么做的步驟:
設置NOTIFYICONDATA型的結(jié)構(gòu)體變量的成員變量的值:
cbSize 該結(jié)構(gòu)體的大小。
hwnd 窗口的句柄。當鼠標滑過該小圖標時,該窗口將接收到相關的消息。
uID 小圖標的ID號。您可以取任意值,只是當您的應用程序有不止一個小圖標時,您要能夠區(qū)分出到底是那一個小圖標接收到了鼠標的消息,也即ID號必須。
uFlags 指定該結(jié)構(gòu)體變量的那些成員變量有效。
NIF_ICON 有效。
NIF_MESSAGE 有效。
NIF_TIP 有效。
uCallbackMessage 自定義的消息。當鼠標對小圖標動作時,WINDOWS外殼將把該消息發(fā)送到您的應用程序。該消息的值您可以自己定義。
hIcon 放入系統(tǒng)托盤中的圖標的句柄。
szTip 64字節(jié)的緩沖區(qū),它用來放入提示字符串,當鼠標停留在小圖標上時,就會顯示該字符串。
調(diào)用Shell_NotifyIcon函數(shù)。該函數(shù)在shell32.inc中定義,其原型如下:
Shell_NotifyIcon PROTO dwMessage:DWORD ,pnid:DWORD
dwMessage 是發(fā)送到WINDOWS外殼的消息:
NIM_ADD 把小圖標加到系統(tǒng)托盤區(qū)。
NIM_DELETE 從系統(tǒng)托盤中刪除小圖標。
NIM_MODIFY 修改小圖標。
pnid 是指向NOTIFYICONDATA型結(jié)構(gòu)體變量的指針。
如果您想要加入一個小圖標就用NIM_ADD,刪除時使用NIM_DELETE消息。
基本上的消息就是這些。但是大多數(shù)的情況下,您不會僅僅滿足把一個小圖標放到那里。您還必須要對鼠標事件作出適當?shù)姆磻?。您可以在 NOTIFYICONDATA型的結(jié)構(gòu)體變量的成員變量uCallbackMessage 中設置您要處理的消息,然后WINDOWS外殼將在發(fā)生這些事件時通知您的應用程序。隨著消息傳送的參數(shù)wParam和lParam的值如下:
wParam 小圖標的ID號。它和您在NOTIFYICONDATA型結(jié)構(gòu)體變量中的成員變量uID中設置的值一樣。
lParam 低字包含鼠標消息。譬如,用戶在小圖標上按下了右鍵時,lParam中將包含WM_RBUTTONDOWN消息。
大多數(shù)的系統(tǒng)托盤中的小圖標,在用戶用鼠標右擊時都會彈出一個菜單以方便用戶選擇。我們可先創(chuàng)建菜單,然后調(diào)用TrackPopupMenu函數(shù)來顯示它。步驟如下:
調(diào)用CreatePopupMenu函數(shù)來創(chuàng)建菜單。該函數(shù)創(chuàng)建一個空的菜單。如果成功,將在eax中返回該菜單的句柄。
調(diào)用AppendMenu, InsertMenu 或 InsertMenuItem來向菜單中加入菜單項。
當您想在當前鼠標位置顯示該菜單時,調(diào)用GetCursorPosition函數(shù)來得到鼠標當前的屏幕位置,然后調(diào)用TrackPopupMenu來顯示菜單。當用戶從彈出式菜單中選擇了一個菜單項時,WINDOWS將發(fā)送WM_COMMAND消息給您應用程序的消息處理過程,這和通常的菜單選擇是一樣的。.
注意:當您使用系統(tǒng)托盤中的小圖標時有兩件比較討厭的事:
該菜單可能不會像通常那樣馬上消失掉。這是因為從彈出式接收消息的窗口必須是前景窗口。調(diào)用SetForegroundWindow函數(shù)就可以糾正該錯誤;
在調(diào)用了SetForegroundWindow函數(shù)后,您會發(fā)現(xiàn)第該彈出式菜單會正常彈出而且工作的很好。但是隨后,該菜單只是一彈出就立即消失。根據(jù)MSDN,這么做是故意的。為了使得彈出菜單保持住,必須要求下一個切換到的是程序的主窗口。您可以通過郵寄任何消息給該程序的窗口來強行進行任務切換。注意要使用PostMessage而不是SendMessage。
例子:
.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
分析:
該程序?qū)@示一個簡單的窗口。當您按下小化按鈕時,該窗口將隱藏,然后放一個小圖標到系統(tǒng)托盤中。當您雙擊小圖標時,應用程序?qū)⒒謴妥约?,并把小圖標從系統(tǒng)托盤中刪除。當您右擊小圖標時,會顯示一個彈出式菜單。您可以在菜單中選擇是恢復窗口還是退出應用程序。
.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
當主窗口創(chuàng)建時,將會創(chuàng)建一個彈出式菜單,并且加入兩個菜單項。 AppendMenu的語法如下:
AppendMenu PROTO hMenu:DWORD, uFlags:DWORD, uIDNewItem:DWORD, lpNewItem:DWORD
hMenu 是將要加入菜單項的菜單的句柄。
uFlags 告訴WINDOWS要加入的菜單項是位圖、字符串或自畫的項目以及是可用、不可用或灰色顯示等。您可以從WIN32 API 指南中得到全部的標志位的信息。在我們的例子中使用標志位MF_STRING,它表示我們加入的菜單項是字符串。
uIDNewItem 是菜單項的ID號。這是一個用戶自定義的值,它用來地代表菜單項。.
lpNewItem 用來指定菜單項的內(nèi)容,具體代表什么取決于uFlags中指定的標志。我們前面指定了MF_STRING標志,所以此處代表一個字符串
主窗口創(chuàng)建完成后,用戶就可以開始測試了。這時按下小化鍵。
當一個窗口被小化時將接收到WM_SIZE消息,其中wParam參數(shù)中的值為SIZE_MINIMIZED。
.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型結(jié)構(gòu)體變量賦值。IDI_TRAY是在代碼開始處定義的一個數(shù)值常量,您可以任意設定它的值。由于我們僅有一個圖標,所以這一點并不重要,如果要同時加入幾個系統(tǒng)圖標的話,那么每個圖標都要有一個的ID號。由于我們指定了一個圖標NIF_ICON,所以我們要在uFlags成員變量中指定所有的標志位,我們還指定了一個自定義的消息NIF_MESSAGE和幫助文本NIF_TIP。 WM_SHELLNOTIFY 被定義為WM_USER+5,只要是的值,就無所謂是多少了,只要大于WM_USER。我們這里用的是WINDOWS登錄時的圖標,當然您可以使用任意您想要用的圖標,您可以用LoadIcon函數(shù)從資源中裝載,該函數(shù)返回一個圖標的句柄。后我們在szTip中放入當鼠標放在圖標時顯示的提示文本。為了達到“小化然后只顯示圖標的效果”,我們在這時隱藏掉主窗口。
接下來,我們調(diào)用Shell_NotifyIcon函數(shù)并指定標志位NIM_ADD把圖標加到系統(tǒng)托盤中去。
現(xiàn)在我們的主窗口隱藏了,圖標顯示在系統(tǒng)托盤中。如果您讓鼠標從圖標上滑過,將看到提示文本。如果您雙擊小圖標,主窗口就會顯示,圖標將消失。
.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
當在系統(tǒng)托盤中的圖標發(fā)生鼠標事件時,您的窗口將接收到WM_SHELLNOTIFY消息,該消息是在uCallbackMessage成員變量中指定的。在接收到該消息時,wParam中包含了圖標的ID號,lParam中包含了鼠標動作的原始數(shù)據(jù)。在上面的代碼中,我們首先檢測是否是我們感興趣的消息。如果是的話,我們在看看是什么消息。因為我們只對右擊和雙擊事件感興趣,所以我們僅僅處理WM_RBUTTONDOWN和WM_LBUTTONDBLCLK消息。
如果是WM_RBUTTONDOWN,我們調(diào)用GetCursorPos來得到鼠標光標所在的當前屏幕位置。注意我指的是屏幕位置,即,其坐標是相對于整個的屏幕的。譬如,如果屏幕的解析讀640*480,那么它的右下角的坐標是x==639 ,y==479。如果您想要把屏幕位置轉(zhuǎn)換成窗口的坐標,可以調(diào)用ScreenToClient函數(shù)
我們想要在當前的位置顯示彈出式菜單,我們就調(diào)用TrackPopupMenu函數(shù),該函數(shù)需要屏幕的坐標,由GetCursorPos函數(shù)返回的坐標就可以原封不動的拿過來用。
TrackPopupMenu的原型如下:
TrackPopupMenu PROTO hMenu:DWORD, uFlags:DWORD, x:DWORD, y:DWORD, nReserved:DWORD, hWnd:DWORD, prcRect:DWORD
hMenu 是彈出式菜單的句柄。
uFlags 功能的選擇。像在哪里放置(相對于隨后將指定的坐標)菜單,那一個鼠標按鈕用來跟蹤彈出式菜單。在我們的例子中,我們用 TPM_RIGHTALIGN標志位來指定彈出式菜單放在坐標的左邊。
x 和 y 指定放置菜單的屏幕坐標。
nReserved 必須為NULL。
hWnd 是將要接收消息的窗口的句柄。
prcRect 指定一個矩形區(qū)域。如果在該矩形區(qū)域外面按下鼠標的話,菜單將消失。一般我們把該值設為NULL,這樣當用戶只要在菜單外面按下鼠標,菜單立即消失。
當用戶雙擊圖標時,我們給我們自己的窗口發(fā)送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
當用戶選擇恢復主窗口時,我們調(diào)用Shell_NotifyIcon函數(shù)來刪除掉系統(tǒng)托盤中的圖標,這我們要指定NIM_DELETE消息。接下來我們把主窗口恢復到原始的狀態(tài)。如果用戶選擇了Exit菜單項,我們不但把圖標給刪除掉,也從整個的應用程序中退出。
理論:
系統(tǒng)托盤是指任務條中的一個方形區(qū)域,在該區(qū)域中可以放入一些小圖標,通常您可以在此處看到系統(tǒng)提供的新時間。您自己當然也可以把快捷小圖標放到此處。下面是這么做的步驟:
設置NOTIFYICONDATA型的結(jié)構(gòu)體變量的成員變量的值:
cbSize 該結(jié)構(gòu)體的大小。
hwnd 窗口的句柄。當鼠標滑過該小圖標時,該窗口將接收到相關的消息。
uID 小圖標的ID號。您可以取任意值,只是當您的應用程序有不止一個小圖標時,您要能夠區(qū)分出到底是那一個小圖標接收到了鼠標的消息,也即ID號必須。
uFlags 指定該結(jié)構(gòu)體變量的那些成員變量有效。
NIF_ICON 有效。
NIF_MESSAGE 有效。
NIF_TIP 有效。
uCallbackMessage 自定義的消息。當鼠標對小圖標動作時,WINDOWS外殼將把該消息發(fā)送到您的應用程序。該消息的值您可以自己定義。
hIcon 放入系統(tǒng)托盤中的圖標的句柄。
szTip 64字節(jié)的緩沖區(qū),它用來放入提示字符串,當鼠標停留在小圖標上時,就會顯示該字符串。
調(diào)用Shell_NotifyIcon函數(shù)。該函數(shù)在shell32.inc中定義,其原型如下:
Shell_NotifyIcon PROTO dwMessage:DWORD ,pnid:DWORD
dwMessage 是發(fā)送到WINDOWS外殼的消息:
NIM_ADD 把小圖標加到系統(tǒng)托盤區(qū)。
NIM_DELETE 從系統(tǒng)托盤中刪除小圖標。
NIM_MODIFY 修改小圖標。
pnid 是指向NOTIFYICONDATA型結(jié)構(gòu)體變量的指針。
如果您想要加入一個小圖標就用NIM_ADD,刪除時使用NIM_DELETE消息。
基本上的消息就是這些。但是大多數(shù)的情況下,您不會僅僅滿足把一個小圖標放到那里。您還必須要對鼠標事件作出適當?shù)姆磻?。您可以在 NOTIFYICONDATA型的結(jié)構(gòu)體變量的成員變量uCallbackMessage 中設置您要處理的消息,然后WINDOWS外殼將在發(fā)生這些事件時通知您的應用程序。隨著消息傳送的參數(shù)wParam和lParam的值如下:
wParam 小圖標的ID號。它和您在NOTIFYICONDATA型結(jié)構(gòu)體變量中的成員變量uID中設置的值一樣。
lParam 低字包含鼠標消息。譬如,用戶在小圖標上按下了右鍵時,lParam中將包含WM_RBUTTONDOWN消息。
大多數(shù)的系統(tǒng)托盤中的小圖標,在用戶用鼠標右擊時都會彈出一個菜單以方便用戶選擇。我們可先創(chuàng)建菜單,然后調(diào)用TrackPopupMenu函數(shù)來顯示它。步驟如下:
調(diào)用CreatePopupMenu函數(shù)來創(chuàng)建菜單。該函數(shù)創(chuàng)建一個空的菜單。如果成功,將在eax中返回該菜單的句柄。
調(diào)用AppendMenu, InsertMenu 或 InsertMenuItem來向菜單中加入菜單項。
當您想在當前鼠標位置顯示該菜單時,調(diào)用GetCursorPosition函數(shù)來得到鼠標當前的屏幕位置,然后調(diào)用TrackPopupMenu來顯示菜單。當用戶從彈出式菜單中選擇了一個菜單項時,WINDOWS將發(fā)送WM_COMMAND消息給您應用程序的消息處理過程,這和通常的菜單選擇是一樣的。.
注意:當您使用系統(tǒng)托盤中的小圖標時有兩件比較討厭的事:
該菜單可能不會像通常那樣馬上消失掉。這是因為從彈出式接收消息的窗口必須是前景窗口。調(diào)用SetForegroundWindow函數(shù)就可以糾正該錯誤;
在調(diào)用了SetForegroundWindow函數(shù)后,您會發(fā)現(xiàn)第該彈出式菜單會正常彈出而且工作的很好。但是隨后,該菜單只是一彈出就立即消失。根據(jù)MSDN,這么做是故意的。為了使得彈出菜單保持住,必須要求下一個切換到的是程序的主窗口。您可以通過郵寄任何消息給該程序的窗口來強行進行任務切換。注意要使用PostMessage而不是SendMessage。
例子:
.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
分析:
該程序?qū)@示一個簡單的窗口。當您按下小化按鈕時,該窗口將隱藏,然后放一個小圖標到系統(tǒng)托盤中。當您雙擊小圖標時,應用程序?qū)⒒謴妥约?,并把小圖標從系統(tǒng)托盤中刪除。當您右擊小圖標時,會顯示一個彈出式菜單。您可以在菜單中選擇是恢復窗口還是退出應用程序。
.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
當主窗口創(chuàng)建時,將會創(chuàng)建一個彈出式菜單,并且加入兩個菜單項。 AppendMenu的語法如下:
AppendMenu PROTO hMenu:DWORD, uFlags:DWORD, uIDNewItem:DWORD, lpNewItem:DWORD
hMenu 是將要加入菜單項的菜單的句柄。
uFlags 告訴WINDOWS要加入的菜單項是位圖、字符串或自畫的項目以及是可用、不可用或灰色顯示等。您可以從WIN32 API 指南中得到全部的標志位的信息。在我們的例子中使用標志位MF_STRING,它表示我們加入的菜單項是字符串。
uIDNewItem 是菜單項的ID號。這是一個用戶自定義的值,它用來地代表菜單項。.
lpNewItem 用來指定菜單項的內(nèi)容,具體代表什么取決于uFlags中指定的標志。我們前面指定了MF_STRING標志,所以此處代表一個字符串
主窗口創(chuàng)建完成后,用戶就可以開始測試了。這時按下小化鍵。
當一個窗口被小化時將接收到WM_SIZE消息,其中wParam參數(shù)中的值為SIZE_MINIMIZED。
.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型結(jié)構(gòu)體變量賦值。IDI_TRAY是在代碼開始處定義的一個數(shù)值常量,您可以任意設定它的值。由于我們僅有一個圖標,所以這一點并不重要,如果要同時加入幾個系統(tǒng)圖標的話,那么每個圖標都要有一個的ID號。由于我們指定了一個圖標NIF_ICON,所以我們要在uFlags成員變量中指定所有的標志位,我們還指定了一個自定義的消息NIF_MESSAGE和幫助文本NIF_TIP。 WM_SHELLNOTIFY 被定義為WM_USER+5,只要是的值,就無所謂是多少了,只要大于WM_USER。我們這里用的是WINDOWS登錄時的圖標,當然您可以使用任意您想要用的圖標,您可以用LoadIcon函數(shù)從資源中裝載,該函數(shù)返回一個圖標的句柄。后我們在szTip中放入當鼠標放在圖標時顯示的提示文本。為了達到“小化然后只顯示圖標的效果”,我們在這時隱藏掉主窗口。
接下來,我們調(diào)用Shell_NotifyIcon函數(shù)并指定標志位NIM_ADD把圖標加到系統(tǒng)托盤中去。
現(xiàn)在我們的主窗口隱藏了,圖標顯示在系統(tǒng)托盤中。如果您讓鼠標從圖標上滑過,將看到提示文本。如果您雙擊小圖標,主窗口就會顯示,圖標將消失。
.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
當在系統(tǒng)托盤中的圖標發(fā)生鼠標事件時,您的窗口將接收到WM_SHELLNOTIFY消息,該消息是在uCallbackMessage成員變量中指定的。在接收到該消息時,wParam中包含了圖標的ID號,lParam中包含了鼠標動作的原始數(shù)據(jù)。在上面的代碼中,我們首先檢測是否是我們感興趣的消息。如果是的話,我們在看看是什么消息。因為我們只對右擊和雙擊事件感興趣,所以我們僅僅處理WM_RBUTTONDOWN和WM_LBUTTONDBLCLK消息。
如果是WM_RBUTTONDOWN,我們調(diào)用GetCursorPos來得到鼠標光標所在的當前屏幕位置。注意我指的是屏幕位置,即,其坐標是相對于整個的屏幕的。譬如,如果屏幕的解析讀640*480,那么它的右下角的坐標是x==639 ,y==479。如果您想要把屏幕位置轉(zhuǎn)換成窗口的坐標,可以調(diào)用ScreenToClient函數(shù)
我們想要在當前的位置顯示彈出式菜單,我們就調(diào)用TrackPopupMenu函數(shù),該函數(shù)需要屏幕的坐標,由GetCursorPos函數(shù)返回的坐標就可以原封不動的拿過來用。
TrackPopupMenu的原型如下:
TrackPopupMenu PROTO hMenu:DWORD, uFlags:DWORD, x:DWORD, y:DWORD, nReserved:DWORD, hWnd:DWORD, prcRect:DWORD
hMenu 是彈出式菜單的句柄。
uFlags 功能的選擇。像在哪里放置(相對于隨后將指定的坐標)菜單,那一個鼠標按鈕用來跟蹤彈出式菜單。在我們的例子中,我們用 TPM_RIGHTALIGN標志位來指定彈出式菜單放在坐標的左邊。
x 和 y 指定放置菜單的屏幕坐標。
nReserved 必須為NULL。
hWnd 是將要接收消息的窗口的句柄。
prcRect 指定一個矩形區(qū)域。如果在該矩形區(qū)域外面按下鼠標的話,菜單將消失。一般我們把該值設為NULL,這樣當用戶只要在菜單外面按下鼠標,菜單立即消失。
當用戶雙擊圖標時,我們給我們自己的窗口發(fā)送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
當用戶選擇恢復主窗口時,我們調(diào)用Shell_NotifyIcon函數(shù)來刪除掉系統(tǒng)托盤中的圖標,這我們要指定NIM_DELETE消息。接下來我們把主窗口恢復到原始的狀態(tài)。如果用戶選擇了Exit菜單項,我們不但把圖標給刪除掉,也從整個的應用程序中退出。