admin 管理员组

文章数量: 887007

Windows7下“僵尸”图标的解决方案

        从《一种清除windows通知区域“僵尸”图标的方案——问题分析》(以后简称《问题分析》)一文中分析的通知区域结构可以看出,Windows7的通知区域比XP通知区域多出了一个“临时”系统通知区域(转载请指明出于breaksoftware的csdn博客)


        虽然我们可以在“控制面板\所有控制面板项\通知区域图标”下选择“始终在任务栏上显示所有图标和通知(A)”,来让Windows7的通知区域和XP上一致,但是我们无法让用户这么去做,因为我们给不出这样做的必要。

        这个隐藏的通知区域是让我非常头疼的,《一种清除windows通知区域“僵尸”图标的方案——XP系统解决方案》一文中的所有方案都不适用于它。

        在各种方法走不通的情况下,我们不妨换一种思路:保证只有一个图标。如果进程意外退出,那么在下次启动时,再次注册和新增通知区域图标时,我们先删掉原来的图标然后再新增图标,而不是简单的新增一个图标。或许有人会问,为什么XP下不这么做呢?因为XP下无法删除其他进程创建的图标。

       XP下密集出现的“僵尸”图标问题,应该是引起了微软的注意。所以他们在Vista之后的系统中,给通知区域图标新增了一个GUID信息。于是,我们就可以简单的指定GUID便可以保证了图标的唯一性!

    m_NotifyIcon.cbSize = sizeof(m_NotifyIcon);
    m_NotifyIcon.uFlags = NIF_ICON | NIF_TIP;
    if ( bWin7OrLater ) {
        GUID PathGUID = {0x23977b55, 0x10e0, 0x4041, {0xb8, 0x62, 0xb1, 0x95, 0x41, 0x96, 0x36, 0x69}};

        m_NotifyIcon.guidItem = PathGUID;
        m_NotifyIcon.uVersion = NOTIFYICON_VERSION_4;
        m_NotifyIcon.uFlags |= NIF_GUID;
    }
    m_NotifyIcon.hWnd = m_hWnd;
    m_NotifyIcon.hIcon = m_hIcon;

        看似这个问题就这么解决了,但是问题绝对不是这么简单的。我们发现,如果我们将程序A拷贝到其他目录下叫作A‘。如果A'创建了图标,并且A'进程被杀死。那么A进程可能出现无法创建和无法删除图标的情况,这样就严重影响了正常执行流程。

        其实这个问题也比较好解决:去掉hWnd参数的指定。但是这个方案将导致一个缺陷:通知区域图标不能将消息传递给主界面!

        一个比较好的解决方法,就是让不同目录下进程新增的图标的GUID不同。于是我们可以计算当前进程路径的MD5,方法是

……
    if ( bWin7OrLater ) {
        WCHAR wcharPath[MAX_PATH] = {0};
        DWORD dwSize = GetModuleFileName(NULL, wcharPath, ARRAYSIZE(wcharPath) );

        unsigned char md5[16] = {0};
        GetMD5((const unsigned char*)wcharPath, dwSize * sizeof(WCHAR), md5, sizeof(md5));

        GUID PathGUID;// = {0x23977b55, 0x10e0, 0x4041, {0xb8, 0x62, 0xb1, 0x95, 0x41, 0x96, 0x36, 0x69}};
        memcpy_s(&PathGUID, sizeof(GUID), md5, sizeof(md5));
……
    }
        创建图标的方法是

VOID CNotificationIconDlg::ShowTrayIcon()
{
    if ( FALSE == Shell_NotifyIcon(NIM_ADD, &m_NotifyIcon) ) {

        if ( ERROR_NO_TOKEN == ::GetLastError()) {
            for ( int i = 0; i < 64; i++) {
                if ( FALSE == Shell_NotifyIcon(NIM_DELETE, &m_NotifyIcon) ) {
                    if ( ERROR_TIMEOUT == ::GetLastError() ) {
                        break;
                    }
                }
            }
            
            if ( FALSE == Shell_NotifyIcon(NIM_ADD, &m_NotifyIcon)) {
                 if ( ERROR_NO_TOKEN == ::GetLastError()) {
                     m_NotifyIcon.uFlags ^= NIF_GUID;
                 }
                 if ( FALSE == Shell_NotifyIcon(NIM_ADD, &m_NotifyIcon) ) {
                 }
            }
            
        }
    }
}
        至此,Windows7下的图标问题也就解决了。这儿再记录一份使用advapi.dll中相关方法计算MD5的代码

#include "stdafx.h"
#include "WinMd5.h"

BOOL GetMD5(const unsigned char * buf, unsigned int unbufferlenth, 
    unsigned char* retMd5, unsigned int unMd5BufferSize)
{
    BOOL bSuc = FALSE;
    HINSTANCE hDLL = NULL;
    do {
        MD5_CTX ctx;

        if ( unMd5BufferSize > sizeof(ctx.digest) ){
            break;
        }

        HMODULE hDLL = LoadLibrary(L"advapi32.dll");
        if ( NULL == hDLL ) {
            break;
        }

        PMD5Init MD5Init = (PMD5Init)GetProcAddress(hDLL, "MD5Init");
        PMD5Update MD5Update = (PMD5Update)GetProcAddress(hDLL, "MD5Update");
        PMD5Final MD5Final = (PMD5Final)GetProcAddress(hDLL, "MD5Final");

        if ( !MD5Init || !MD5Update || !MD5Final ) {
            break;
        }

        MD5Init(&ctx);
        MD5Update(&ctx, buf, unbufferlenth);
        MD5Final(&ctx);

        memcpy_s(retMd5, unMd5BufferSize, ctx.digest, sizeof(ctx.digest)); 
        bSuc = TRUE;
    } while (0);

    if ( NULL != hDLL ) {
        FreeLibrary(hDLL);
        hDLL = NULL;
    }
    return bSuc;

}

        最后附上工程地址

本文标签: 僵尸 图标 解决方案 区域 通知