有些应用在往任务栏(win7及以后)上固定图标后,单击图标运行实例时会生成一个新的图标,win7下似乎还能再次固定新的图标。

造成两个图标的原因在于应用使用了自定义的AppUserModelID
win7及以后的任务栏通过AppUserModelID来关联程序的图标,也就是说相同AppUserModelID的进程会在任务栏上共用一个图标。比如资源管理器,当你打开多个资源管理器窗口时,任务栏上始终只有一个对应的图标,鼠标移至图标上时就会显示出所有打开的窗口,也就是所有窗口被分为同一组了。应用程序可以根据实际需要来设置这个ID相同与否来达到控制应用在任务栏上的图标显示,特别是对于一些多进程、多显示窗口的应用用处会比较大。

在没有运行应用的时候进行固定,系统会自动分配一个内部ID,而如果应用使用了自定义的ID,那么运行时便会导致任务栏将两个图标区分开。这也是在运行过程中重新固定一次图标可以解决双图标问题的原因,因为这样ID就统一了;另一方面,如果一开始就没有指定过ID的话(使用系统自动分配的ID),也不会出现这个问题。
关于AppUserModelID的形式,一般是形如CompanyName.ProductName.SubProduct.VersionInformation,采用驼峰式大小写的字符串,SubProduct和VersionInformation可以用来提供更进一步的细分(比如不同子应用之间图标划分为不同组),但如果没有这个需要的话用其他形式的字符串也不会有问题。

设置自定义ID的方式有两种:

  1. 创建窗口后通过调用SHGetPropertyStoreForWindow来检索、设置AppUserModelID。在头文件propkey.h中有对AppUserModelID的定义
  2. //  Name:     System.AppUserModel.ID -- PKEY_AppUserModel_ID
    //  Type:     String -- VT_LPWSTR  (For variants: VT_BSTR)
    //  FormatID: {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}, 5
    DEFINE_PROPERTYKEY(PKEY_AppUserModel_ID, 0x9F4C2855, 0x9F79, 0x4B39, 0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3, 5);
    

    为此我们只要在创建窗口后进行如下设置(代码是MFC里App::InitInstance()中创建出主窗口之后):

        // 创建主 MDI 框架窗口
        CMainFrame* pMainFrame = new CMainFrame;
        if (!pMainFrame || !pMainFrame->LoadFrame(IDR_MAINFRAME))
        {
            delete pMainFrame;
            return FALSE;
        }
        m_pMainWnd = pMainFrame;
    
        // 包含头文件propkey.h
        IPropertyStore *ps;
        SHGetPropertyStoreForWindow(m_pMainWnd->m_hWnd, IID_IPropertyStore, (void**)&ps);
    
        PROPVARIANT pv = { 0 };
        pv.vt = VT_BSTR;                    // propkey.h中定义的AppUserModelID类型
        pv.pwszVal = L"MFCApplication1";    // 用户定义的AppUserModelID
        ps->SetValue(PKEY_AppUserModel_ID, pv);
    
  3. 通过调用SetCurrentProcessExplicitAppUserModelID进行设置。这种情况下AppUserModelID的设置必须在应用显示任何UI界面、任何对Jump Lists的操作和SHAllToRecentDocs被调用之前完成,一般在应用程序启动的时候就立刻设置,比如MFC就是在应用程序类的构造函数中设置的(所以MFC的应用默认都会有这种双图标的问题)。它的参数直接传入字符串即可。