程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 系統托盤編程完全指南(2)

系統托盤編程完全指南(2)

編輯:關於VC++

在本文的第一部分,我們討論並示范了如何在自己的程序中應用系統托盤圖標。通過使用自己創建的一個可重用的 C++ 類——CTrayIcon,我們可以輕松地實現托盤程序。不久以前我用這個類編寫了一個程序,開始運行很正常,但是有一次不知什麼原因Windows資源管理器死掉了,也就是說非正常關閉,重啟資源管理器後,發現托盤程序仍然在運行,但托盤圖標顯示不出來,在任務欄中看不到托盤圖標,只有重新啟動機器才能重新顯示出托盤圖標,讓人覺得心裡很不舒服,有沒有什麼辦法在這個時候不用重啟機器而讓Windows自動找回托盤圖標並把它添加到任務欄呢?,也就是讓它自動恢復托盤圖標程序的用戶界面,經過一番研究,終於有所收獲。

實際上,如果你用的操作系統是Windows 98或者你安裝了IE4.0的桌面。那麼不論什麼時候,只要IE4.0啟動了任務欄,那麼它就會向所有最頂層父窗口廣播一個注冊消息:TaskbarCreated。有了這個線索,你就可以重新創建圖標。如果你用MFC編程,那麼只要定義一個全程變量保存這個注冊消息並實現ON_REGISTERED_MESSAGE消息處理例程即可:

const UINT WM_TASKBARCREATED =
  ::RegisterWindowMessage(_T("TaskbarCreated"));
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
  ON_REGISTERED_MESSAGE(WM_TASKBARCREATED,
             OnTaskBarCreated)
END_MESSAGE_MAP(CMainFrame, CFrameWnd)
   The handler itself should reinstall whatever icons you need.
LRESULT CMainFrame::OnTaskBarCreated(WPARAM wp, LPARAM lp)
{
  VERIFY(InstallIcons());
  return 0;
}
BOOL CMainFrame::InstallIcons()
{
  NOTIFYICONDATA nid;
  //
  // 准備 nid 參數
  //
  return Shell_NotifyIcon(NIM_ADD, &nid);   

難道就這麼容易嗎?當然不是,你必須單獨實現InstallIcons函數,而不是直接從OnTaskBarCreated中調用Shell_NotifyIcon,因為一般來說,在應用程序啟動的時候你也會調用它。

當我搞清楚TaskbarCreated的奧秘之後,我便回到本文第一部分中創建的那個CTrayIcon類,對它進行了修改和完善,改動不是很大。這個類管理的是單個的托盤圖標(如果你有多個,那麼為每個圖標創建一個單獨的實例吧)。使用CTrayIcon時,你只要在CMainFrame中創建一個實例m_trayIcon並象下面這樣安裝它即可:

// 在CMainFrame::OnCreate中
m_trayIcon.SetNotificationWnd(this,
  WM_MY_TRAY_NOTIFICATION);
m_trayIcon.SetIcon(IDI_MYICON);   

WM_MY_TRAY_NOTIFICATION 是自己的私有消息,只在有事件發生時,托盤圖標才發送此消息,如用戶單擊圖標。對於托盤圖標來說,你不用對它做什麼處理,CTrayIcon全權負責。只是在構造CTrayIcon對象時,你給它一個資源ID就可以了。CTrayIcon用這個ID查找上下文菜單,如果找到的話,它便自動處理右鍵單擊托盤圖標事件,這時彈出上下文菜單。如果用戶雙擊圖標,CTrayIcon則執行第一個菜單項命令。所以你要做的全部工作只是創建一個菜單資源,將ID傳遞給構造函數,然後去驅動CTrayIcon就可以了。如果你想要一些非標准的例程,那麼只需處理WM_MY_TRAY_NOTIFICATION即可。有關細節請參考源代碼。

為了實現圖標自動重建特性,我使用了以前很多文章中都用到的一個類CSubclassWnd,這個類在我的編程生涯中幾乎無處不在。沒有了它,我幾乎寸步難行;在我的所有編程訣竅中,它是最有用的一個類。用這個類可以截獲Windows消息,並將它發送到另外一個窗口消息處理例程中處理,而不是發送到Windows自己默認的消息處理函數。CTrayIcon用它來截獲TaskbarCreated消息,所以你就不必為此編寫消息處理器——這個方法非常酷。CSubclassWnd通過安裝它自己的窗口過程,從而先於MFC一步對消息進行處理。

當CMainFrame調用CTrayIcon::SetNotificationWnd時,CTrayIcon調用GetTopLevelParent以獲取頂層父窗口,然後安裝CSubclassWnd窗口過程,這相當於一個鉤子,在消息傳到任何頂層窗口之前,CSubclassWnd的窗口過程先將它處理掉。這裡必須明白一點:Windows只將TaskbarCreated消息發送到頂層父窗口。它有點象WM_QUERYNEWPALETTE、WM_SYSCOLORCHANGE消息以及其它頂層窗口消息。現在當Windows發送TaskbarCreated時,控制將首先被傳遞到CTrayIcon::CTrayHook::WindowProc。

CTrayIcon::OnTaskBarCreate是CTrayIcon類中新加的一個虛擬函數,其默認實現是在系統托盤中重新安裝圖標。如果你想做一些其它的事情,可以派生一個新類並改寫此默認行為。實際的WindowProc代碼比我在本文中描述的要稍微復雜一些,因為它還要處理托盤通知以驅動默認的菜單處理例程,以前,當獲得WM_MY_TRAY_NOTIFICATION消息時,你必須自己調用OnTrayNotification。新的CTrayIcon實現請參考本文的源代碼。

順便提及一下,你想知道我是如何測試圖標自動重建特性的嗎?(如果是你,你用什麼辦法?),很容易。在Windows 98中,按下Ctrl+Alt+Del,彈出任務管理器的任務清單。選擇"資源管理器(Explorer)",然後選擇"結束任務(End Task)"。此時會顯示關機/重啟對話框,此時按下"取消"按鈕(不要按"關機"按鈕)。然後,等待幾秒鐘,你得到消息顯示"這個任務沒有響應",這時你要用"結束任務"來回復。接下來Windows的資源管理器便非正常終止!然後它又會自動起來,這時向所有頂層父窗口廣播TaskbarCreated消息。如果你的操作系統是Windows NT/2000/XP,並安裝了IE4.0,如法炮制。當任務欄死掉後,從"開始"菜單的"運行"對話框中敲入"explorer"啟動器源管理器。不管是用什麼操作系統,在殺掉任務欄之前,如果TrayTest程序處於運行狀態,那麼當資源管理器自己恢復狀態時,TrayTest程序的托盤圖標會自動創新安裝。不信的話,你用本文第一部分的例子程序和本文提供的范例程序試一下就知道了。你會發現本文的例子程序會處理TaskbarCreated消息,而另一個則完全不會。(待續)

本文配套源碼

  1. 上一頁:
  2. 下一頁:
欄目導航
Copyright © 程式師世界 All Rights Reserved