----目前有不少Win95應用程序在最小化後,代表該程序的小圖標就會隱藏在屏幕的右下角(即任務欄的右邊),如“金山詞霸”、“東方快車”等,而我們用VisualBasic編制的程序在最小化後,圖標卻總是出現在任務欄的中央,並且占據較大的空間。如何讓我們自己的程序也能將最小化圖標放在任務欄的右邊呢?我們可以通過WindowsAPI函數調用來實現該功能。
一、如何添加和隱藏圖標
----在Windows的動態連接庫Shell32.dll中有一個名為Shell_NotifyIconA的WindowsAPI函數,其功能是對任務欄的右下角圖標進行操作,包括添加、刪除和修改。Shell_NotifyIconA函數的VB聲明格式如下:
----DeclareFunctionShell_NotifyIconALib"SHELL32"(ByValdwMessageAsLong,lpDataAsNOTIFYICONDATA)AsIntegerShell_NotifyIconA有兩個參數DwMessage和lpData。DwMessage為操作圖標的方式,可能的取值和含義如下:
----0(向任務欄添加圖標)
----1(修改任務欄中的圖標)
----2(刪除任務欄中的圖標)
----lpData是自定義數據類型NOTIFYICONDATA的數據,該自定義數據結構由以下成員構成:
----1.cbSize:需填寫NOTIFYICONDATA數據結構的長度,在使用時可通過VB的標准函數Len來計算,如nid為NOTIFYICONDATA類型的變量,則cbSize為Len(nid)。
----2.hWnd:最小化窗體的句柄。
----3.uID:使用者為圖標所設定的ID,可自定。
----4.uFlags:用來設定後面三個成員(uCallbackMessage、hIcon、szTip)是否有效,等於1時為uCallbackMessage有效,將來使用者在圖標上按下鼠標鍵時,Windows會發出消息給窗體程序;等於2時為hIcon有效,表示要顯示圖標(如果不顯示圖標,用戶只是無法看見圖標,但仍然可在任務欄的圖標應該出現的位置上操作);等於4時為szTip有效,當用戶將鼠標指針放在圖標上時,要顯示提示信息,否則不顯示。通常將uFlags設定成7(即1+2+4),表示全部有效。
----5.uCallbackMessage:當用戶在圖標上按下鼠標按鍵時,Windows發給應用程序的消息編號。
----6.hIcon:圖標句柄。
----7.szTip:當用戶將鼠標指針放在圖標上時顯示的提示信息。
----在調用Shell_NotifyIconA函數之前,必須將NOTIFYICONDATA類型變量的以上各成員填寫正確的內容。
----該函數如果調用成功則返回1,否則返回0。
----了解了Shell_NotifyIconA函數的功能和用法後,我們可以很容易地將圖標添加在任務欄的右邊,或者從任務欄上將圖標刪除。
----如:假設我們事先聲明了一個NOTIFYICONDATA型的變量nid,且已經給它的所有成員賦予了合適的值,那麼,下面的兩條語句分別可完成添加和刪除圖標的任務:
Shell_NotifyIconA(0,nid) '添加圖標
Shell_NotifyIconA(2,nid)'刪除圖標
二、如何響應鼠標按鍵
----按照上面的方法,我們可以將最小化後的圖標放在任務欄的右邊,那麼當我們在該圖標上按下鼠標按鍵時會發生什麼事情呢?很遺憾,什麼也沒有發生,最小化的窗體不會象我們希望的那樣恢復正常顯示狀態。
----這時怎麼回事呢?很簡單,前面我們只是將圖標添加到了任務欄上,而並沒有對鼠標事件做任何的工作,為了對鼠標按鍵作出響應,我們還有一些事情要做。
----前面已經說過,如果將將NOTIFYICONDATA類型變量的成員uFlags設定為1,則當用戶在圖標上按下鼠標按鍵時,Windows會將編號為uCallbackMessage的消息發送到應用程序,因此,為了能響應鼠標事件,我們必須在程序中對該消息進行處理。
----需要注意的是,由於Windows自身的消息處理機制的限制,我們無法直接干預Windows的自身消息處理過程,所以只有采用一種類似於從前DOS環境下截獲中斷的“曲線救國“的方法來處理消息,過程是:
----在窗體最小化後,立即讀取該窗體原消息處理過程的地址並記錄在一個全局變量中,方法是調用WindowsAPI函數GetWindowLong,其聲明如下:
----DeclareFunctionGetWindowLongLib"user32"Alias"GetWindowLongA"(ByValhWndAsLong,ByValnIndexAsLong)AsLong其中hWnd為窗體句柄,nIndex為過程類型,這裡為-4(Windows默認過程)。
----(2)編寫自己的消息處理過程,注意參數的數量和類型必須按照下面的格式(過程名可隨意):
----SubWndProcForIcon(ByValhWndAsLong,ByValMsgAsLong,ByValwParamAsLong,ByVallParamAsLong)
----(3)將自己的消息處理過程設置為默認的消息處理程序,方法是調用WindowsAPI函數SetWindowLong,其聲明如下:
----DeclareFunctionSetWindowLongLib"user32"Alias"SetWindowLongA"(ByValhWndAsLong,ByValnIndexAsLong,ByValdwNewLongAsLong)AsLong
----其中hWnd與nIndex含義同上,dwNewLong為自定義過程的地址,可使用VB的求地址運算符AddressOf得到,如AddressOfWndProcForIcon可得到自定義過程WndProcForIcon的地址。
----(4)當鼠標事件發生後,自己的消息處理過程(現在已成為默認的消息處理過程)首先進行處理,一般是根據按鍵將窗體恢復或彈出一個PopUp菜單供用戶選擇。
----(5)讓Windows去執行原消息處理過程(該過程的地址由全局變量保存),方法是調用WindowsAPI函數CallWindowProc,其聲明如下:
----DeclareFunctionCallWindowProcLib"user32"Alias"CallWindowProcA"(ByVallpPrevWndFuncAsLong,ByValhWndAsLong,ByValMsgAsLong,ByValwParamAsLong,ByVallParamAsLong)AsLong
----其中lpPrevWndFunc為原消息處理程序的地址,其它參數與自定義消息處理過程中的參數相對應。
----細心的讀者一定會發現,如果在上述第3步完成後,沒有進行第5步,即:使用setWindowLong改變了原先消息處理程序的地址,卻沒有在程序結束前將地址設定回來,那程序將不會正常結束,往往會造成死機,因此,程序中一定要小心處理這個問題。
----另外,為了正確的處理消息,還有一個問題需要解決,就是如何識別消息是否是由圖標發出的?這一點可以這樣解決:利用一個全局變量記錄下NOTIFYICONDATA型變量的成員uCallbackMessage(消息號)的值,然後在消息處理程序中檢查Windows傳回的消息號(MsgNo)是否與其一致,如果一致,則說明消息的確是由圖標發出的,應該進行處理,否則即可忽略。如本文後面的程序實例就用全局變量IconMsg來實現這一功能(具體請參照程序代碼)。
三、程序實例
----下面,我們通過一個簡單的實例來實現上述方法:
----1、窗體布局
----新建一個工程IconTest,新建一個標准窗體frmIconTest,在窗體上放置三個命令按鈕,名稱(Name)分別為cmdCreateIcon、cmdNormalMin和cmdExit,標題(Caption)分別為“最小化到右下角”、“普通最小化”和“退出”。
----2、代碼
----添加一個模塊modIconTest,在其中編寫如下的代碼:
OptionExplicit
'定義常量
PublicConstGWL_WNDPROC=(-4)
PublicConstWM_LBUTTONDOWN=&H201
PublicConstNIM_ADD=0
PublicConstNIM_DELETE=2
PublicConstNIF_MESSAGE=1
PublicConstNIF_ICON=2
PublicConstNIF_TIP=4
'定義全局變量
PublicIconMsgAsLong'消息編號
PublicOldWinProcAsLong'原消息處理程序的地址
'定義自定義數據類型
PublicTypeNOTIFYICONDATA
cbSizeAsLong
hWndAsLong
uIDAsLong
uFlagsAsLong
uCallbackMessageAsLong
hIconAsLong
szTipAsString*64
EndType
'WindowsAPI函數聲明
DeclareFunctionCallWindowProcLib"user32"Alias
"CallWindowProcA"(ByVallpPrevWndFuncAsLong,
ByValhWndAsLong,ByValMsgAsLong,ByVal
wParamAsLong,ByVallParamAsLong)AsLong
DeclareFunctionGetWindowLongLib"user32"
Alias"GetWindowLongA"(ByValhWndAsLong,
ByValnIndexAsLong)AsLong
DeclareFunctionSetWindowLongLib"user32"Alias
"SetWindowLongA"(ByValhWndAsLong,ByValnIndex
AsLong,ByValdwNewLongAsLong)AsLong
DeclareFunctionShell_NotifyIconALib"SHELL32"(ByVal
dwMessageAsLong,lpDataAsNOTIFYICONDATA)AsInteger
'自定義消息處理程序
SubMyProc(ByValhWndAsLong,ByValMsgAsLong,
ByValwParamAsLong,ByVallParamAsLong)
'根據Msg判斷,消息是否是圖標發出的,
如果是,再進行處理
IfMsg=IconMsgThen
IflParam=WM_LBUTTONDOWNThen
frmIconTest.Show'如果在圖標上按
下了左鍵則顯示窗體
EndIf
EndIf
'執行全程變量OldWinProc記錄的原
消息處理程序的地址中的消息處理程序
CallWindowProcOldWinProc,hWnd,Msg,wParam,lParam
EndSub
在frmIconTest的窗體模塊中編寫如下代碼:
OptionExplicit
DimHadAddAsBoolean
DimnidAsNOTIFYICONDATA
PrivateSubcmdEnd_Click()
IfHadAddThen
'如果曾向右下角添加過小圖標
'則根據事先記錄的OldWinProc
恢復原來的消息處理程序,並刪除圖標
SetWindowLongfrmIconTest.hWnd,
GWL_WNDPROC,OldWinProc
CallShell_NotifyIconA(NIM_DELETE,nid)
'刪除圖標
EndIf
UnloadMe'卸載窗體
EndSub
PrivateSubcmdCreateIcon_Click()
IfNotHadAddThen
'如果沒有產生過圖標,則添加圖標到右下角
Withnid
'以下填寫nid變量的所有成員
.cbSize=Len(nid)'填寫自定義數據類型的長度
.hWnd=frmIconTest.hWnd'填寫窗體的句柄
.uID=9999'圖標的Id,可隨意
'允許顯示圖標、提示並產生消息
.uFlags=NIF_ICON NIF_TIP NIF_MESSAGE
.hIcon=frmIconTest.Icon.Handle'圖標句柄
.szTip="單擊鼠標左鍵恢復窗體!"'提示信息
.uCallbackMessage=2'圖標的消息號
EndWith
Shell_NotifyIconANIM_ADD,nid'添加圖標
IconMsg=nid.uCallbackMessage
'用全局變量記錄消息號,
'讀取frmIconTest正常的消息處理程序
的地址保存在全程變量OldWinProc中
OldWinProc=GetWindowLong(frmIconTest.hWnd,
GWL_WNDPROC)
'用自定義過程MyProc的地址
代替正常消息處理程序的地址
SetWindowLongfrmIconTest.hWnd,
GWL_WNDPROC,AddressOfMyProc
frmIconTest.Hide'隱藏窗體
HadAdd=True'置“已經添加圖標”標志
Else
frmIconTest.Hide'如果已經有小圖標了,
則直接隱藏窗體
EndIf
EndSub
PrivateSubcmdNormalMin_Click()
frmIconTest.WindowState=1'普通最小化窗體
EndSub
PrivateSubForm_Load()
HadAdd=False'將“是否添加了圖標的標志”置為False
'下面為窗體裝載圖標,讀者應當根據自己的VB目錄而定
frmIconTest.Icon=oadPicture("c:MyIconfiles10.ico")
EndSub
----按下F5,執行本程序,單擊“最小化到右下角”按鈕,窗體將消失,同時任務欄的右邊出現代表窗體的圖標(如圖2),在該圖標上單擊鼠標左鍵,窗體恢復正常。再單擊“普通最小化圖標”按鈕,你會看到窗體雖然也消失了,但最小化圖標在任務欄的中間,即普通位置(如圖3)。
----本程序用VB5編制,在Pwin95下調試正常。->