日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

一種清除windows通知區(qū)域“僵尸”圖標的方案——問題分析

 quasiceo 2014-01-12
分類: windows開發(fā) 2013-12-09 01:01 1342人閱讀 評論(7) 收藏 舉報

目錄(?)[+]

通知區(qū)域名稱有趣的歷史

        假如說到windows通知區(qū)域,可能很多人還是不清楚它是什么。如果改稱Tray區(qū)域,可能有人就懂了。如果再白話點,叫它“托盤”或者“系統(tǒng)托盤”,可能會有更多的人猜到它是windows什么部位?,F(xiàn)在我們揭開它真實的面紗,以windows7系統(tǒng)為例,下圖就是它的通知區(qū)域。(轉載請指明出于breaksoftware的csdn博客)


        其實,我們叫通知區(qū)域為“托盤”或者“系統(tǒng)托盤”是錯誤的。這個錯誤并非來源于中文翻譯,而是來源于windows發(fā)展史上人們對其錯誤的認識。后來,這個命名也影響了中國一批程序員。我這兒要摘錄一個微軟老員工的回憶錄《The Old New Thing》(中文名《windows編程啟示錄》)一書中關于這個錯誤認識起源的一段,還是蠻有意思的。

        “后來,我們將通知圖標添加到任務欄中。”

        “我認為人們開始將通知區(qū)域叫作系統(tǒng)托盤是因為在Windows95中包含了一個systray.exe的程序,這個程序在通知區(qū)域中顯示了一些圖標,如音量控制,PCMCIA(在當時是叫這個名字)的狀態(tài)、電池的電量表等。如果你終止了systray.exe,那么這些通知圖標也將會消失。因此人們就認為,‘啊,systray程序一定是管理這些圖標的組件,我敢打賭這個組件的名字就叫作“系統(tǒng)托盤”’。于是這個誤解就形成了,而我們這十幾年來一直都在努力澄清這個誤解。”

        “更糟糕的是,其他的團隊(Shell之外的團隊)也錯誤地使用了這個詞,并且開始在他們自己的文檔和示例程序里面都使用了系統(tǒng)托盤這個詞,其中有一些地方甚至錯誤地聲稱系統(tǒng)托盤就是通知區(qū)域的正式名稱?!?/p>

        “有人可能會問,‘你為什么要關心這個名字的正誤?既然現(xiàn)在所有的人都叫這個名字,你也可以隨波逐流嘛?!?/p>

        “如果每個人都叫錯了你的名字,你會樂意嗎?”

        其實我覺得,如果微軟真的想徹底摒棄“系統(tǒng)托盤”這個名稱,最好是從現(xiàn)在做起,將通知區(qū)域的一些信息都修改成和Tray這個單詞無關。可是,我們使用Spy++查看Windows7任務欄的組成時就會發(fā)現(xiàn),Tray這個單詞無處不在??!



“僵尸圖標”

        說了這么多歷史故事,我們再回到我們這篇博文要講述的問題上。其實這個問題,依舊是個歷史問題。還好,我發(fā)現(xiàn)vista之后的系統(tǒng)上,微軟已經(jīng)意識并修復了這個設計缺陷。我們看下下面的場景

        

        很多使用Windows的人可能都遇到過這個問題:通知區(qū)域出現(xiàn)了N個相同的“僵尸”圖標。如果我們有意或者無意讓光標劃過這些圖標時,這些圖標會悄然消失。我們對這種現(xiàn)象,往往是疑惑一下就拋之腦后。然而,目前我在項目中就接到一個需求:把這些“僵尸”圖標自動消失。出于我們產(chǎn)品的設計,我們存在出現(xiàn)這么多“僵尸”圖標的場景,于是為了優(yōu)化用戶體驗,我需要找到一種方法去解決這種體驗問題。


通知區(qū)域圖標的正常生死過程

        首先要分析一下這個問題出現(xiàn)的原因。一般來說,一個程序在創(chuàng)建時,可能會在通知區(qū)域創(chuàng)建一個圖標。

一般初始化圖標

        創(chuàng)建圖標之前,我們需要初始化一個圖標

  1. NOTIFYICONDATA m_NotifyIcon;  
  2. ……  
  3. m_NotifyIcon.cbSize = sizeof(m_NotifyIcon);  
  4. m_NotifyIcon.uFlags = NIF_ICON | NIF_TIP;  
  5. m_NotifyIcon.uVersion = NOTIFYICON_VERSION; // xp  
  6. m_NotifyIcon.hWnd = m_hWnd;  
  7. m_NotifyIcon.hIcon = m_hIcon;  
  8. std::wstring wstrInfo = L"中A英1文"// 故意取一個晦澀的名字  
  9. wmemcpy_s(m_NotifyIcon.szTip, ARRAYSIZE(m_NotifyIcon.szTip), wstrInfo.c_str(), wstrInfo.length()+1 );  

       這個地方需要注意的是下面幾個參數(shù):

  1. uFlags。我們只是設置了NIF_ICON和NIF_TIP,因為我們需要讓我們的通知區(qū)域圖標變得與眾不同,故通過指定這兩個標志分別告知系統(tǒng):我們要設定圖標和Tip文字。這個屬性我們會在處理Windows7系統(tǒng)上“僵尸”圖標的時候再次提起。
  2. hWnd。因為我們圖標要相應用戶的點擊,并將相應消息傳遞給我們主窗口,所以我們此時要綁定主窗口句柄。這個屬性我們會在未來介紹一個特定場景時再次提到。
  3. szTip。我們故意給我們這個圖標取了一個晦澀的Tip,這樣我們在之后查找“僵尸圖標”時將有據(jù)可憑。

圖標添加到通知區(qū)域

        圖標初始化后,我們要將圖標增加到通知區(qū)域
  1. Shell_NotifyIcon(NIM_ADD, &m_NotifyIcon);  

        這個圖標是可以表明“這個進程還活著”;而且在無界面展現(xiàn)時,讓用戶方便喚起界面或者執(zhí)行相應的功能。比如QQ的通知區(qū)域圖標,它的存在表明QQ進程還是存在的。我們可以左鍵雙擊之,可以讓主界面展現(xiàn)出來;還可以右擊之,可以出現(xiàn)很多快捷功能鍵


圖標從通知區(qū)域剔除        

        相應的,如果進程退出,應該通知系統(tǒng)通知區(qū)域:要將我設置的通知區(qū)域圖標刪除,因為我馬上要退出了。

  1. Shell_NotifyIcon(NIM_DELETE, &m_NotifyIcon);  

        如果一切都如此按照規(guī)律的“正常生死”,也就沒有之前提出的問題??墒?,出于策略考慮以及一些異常情況,進程的意外死亡還是不可避免的。這樣,如果出現(xiàn)連續(xù)的意外死亡場景,系統(tǒng)通知區(qū)域就會殘留很多“僵尸”圖標。為了大戰(zhàn)這些“僵尸”,我們需要找到這些“僵尸”的家,然后對“僵尸”各個擊破。于是,我們要看下各系統(tǒng)下通知區(qū)域的樹狀結構圖。

XP、Win7下通知區(qū)域的結構

        先使用SPY++看下XP下任務欄即通知區(qū)域的結構

  1. #32769 (桌面)  
  2.   - Shell_TrayWnd  
  3.     - Button  
  4.     - TrayNotifyWnd  
  5.       - TrayClockWClass  
  6.       - SysPager  
  7.           - ToolbarWindow32(我們關心的,其直接顯示在桌面上)  
  8.       - Button  
  9.         - CiceroUIWndFrame  
  10.         - MSTaskSwWClass  
  11.           - ToolbarWindow32  
  12.     - ReBarWindow32  

        SysPager下類名為ToolbarWindow32的控件就是系統(tǒng)通知區(qū)域。非常慶幸,XP下只有這么一個通知區(qū)域,而且這個通知區(qū)域一直是可見的(Win7下有個不可見的通知區(qū)域)。

        再看下Win7的通知區(qū)域結構

  1. #32769 (桌面)  
  2.   - Shell_TrayWnd  
  3.     - TrayNotifyWnd  
  4.       - TrayClockWClass  
  5.       - TrayShowDesktopButtonWClass  
  6.       - SysPager  
  7.         - ToolbarWindow32(我們關心的,其直接顯示在桌面上)  
  8.       - ToolbarWindow32(其隱藏在桌面上,通過SendTimeout發(fā)送TB_BUTTONCOUNT不能獲取其個數(shù))  
  9.       - Button  
  10.     - ReBarWindow32  
  11.       - CiceroUIWndFrame  
  12.       - MSTaskSwWClass  
  13.         - MSTaskListWClass  
        Win7的通知區(qū)域相對于XP有點復雜,其中我們一直可見的通知區(qū)域的樹狀結構和XP上是一致的。但是Win7上多出了一個隱藏的通知區(qū)域,它和SysPager同級


        針對XP和Win7上都可見的通知區(qū)域,我們可以通過如下代碼找到相應區(qū)域去清理

  1. VOID CKillRunProcessDlg::VisitNotificationArea()  
  2. {  
  3.     HWND hwndChildAfter = NULL;  
  4.     DWORD dwMaxLoopCount = MAXLOOPCOUNT;  
  5.     do {  
  6.         // 保守性編程,防止死循環(huán)  
  7.         dwMaxLoopCount--;  
  8.         HWND hTrayWnd = NULL;  
  9.         hTrayWnd = ::FindWindowEx( NULL, hwndChildAfter, L"Shell_TrayWnd", NULL);  
  10.         if ( NULL == hTrayWnd ) {  
  11.             break;  
  12.         }  
  13.   
  14.         // 找到了窗口類為 Shell_TrayWnd的窗口,  
  15.         // 但是不保證找到的就是Notification所在區(qū)域的,  
  16.         // 所以記錄下當前找到的,之后繼續(xù)找  
  17.         hwndChildAfter = hTrayWnd;  
  18.   
  19.         HWND hTrayNotifyWnd = ::FindWindowEx(hTrayWnd, NULL, L"TrayNotifyWnd", NULL );  
  20.         if ( NULL == hTrayNotifyWnd ) {  
  21.             // 繼續(xù)找符合條件的Shell_TrayWnd類,然后再在其下找類為TrayNotifyWnd的子窗口  
  22.             continue;  
  23.         }  
  24.   
  25.         // 這個窗口只能在Win7系統(tǒng)中可以找到  
  26.         HWND hToolBar32Ex = ::FindWindowEx( hTrayNotifyWnd, NULL, L"ToolbarWindow32", NULL );  
  27.           
  28.         // 在win7 xp下都可以找到該樹結構  
  29.         HWND hSysPager = ::FindWindowEx( hTrayNotifyWnd, NULL, L"SysPager", NULL );  
  30.         HWND hToolBar32Showed = NULL;  
  31.         if ( NULL != hSysPager ) {    
  32.             hToolBar32Showed = ::FindWindowEx( hSysPager, NULL, L"ToolbarWindow32", NULL );  
  33.         }  
  34.         else {  
  35.             // 找不到該樹結構  
  36.             // 則繼續(xù)找符合條件的Shell_TrayWnd類,然后再在其下找類為TrayNotifyWnd的子窗口  
  37.             continue;;  
  38.         }  
  39.   
  40.         if ( m_bVistaLater ) {  
  41.             if ( NULL == hToolBar32Showed || NULL == hToolBar32Ex ) {  
  42.                 // 都要有,否則不是合法的  
  43.                 continue;  
  44.             }  
  45.         }  
  46.         else {  
  47.             if ( NULL == hToolBar32Showed ) {  
  48.                 continue;  
  49.             }  
  50.         }  
  51.   
  52.         if ( FALSE == IsExplorerProcess(hToolBar32Showed) ) {  
  53.             if ( ERROR_NOT_FOUND != ::GetLastError() ) {  
  54.                 // 不是Explorer進程,則繼續(xù)尋找  
  55.                 continue;  
  56.             }  
  57.         }  
  58.   
  59.         if ( NULL != hToolBar32Showed ) {  
  60.             // 清理通知區(qū)域  
  61.             CleareIcons(hToolBar32Showed);  
  62.         }  
  63.   
  64.     } while ( dwMaxLoopCount > 0 );  
  65. }  

        鑒于XP的通知區(qū)域的結構簡單性,我決定先從XP系統(tǒng)入手。其實XP上的解決方案是多種的,也是非常有意思的。詳細的分析過程可以參看下篇博文《一種清除windows通知區(qū)域“僵尸”圖標的方案——XP系統(tǒng)解決方案》。

更多 2

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多