comshortcutjump-listshell-namespace-extension

Quick Access and Namespace Extensions: Unpin object


When I right click on an lnk file - link on a virtual folder in a namespace extension -, then the functions CreateViewObject and/or GetUiObjectOf of my IShellFolder/IShellFolder2 implementations are called. These functions are called with the IID_IContextMenu as parameter (riid). Also when QueryContextMenu is called the flag CMF_VERBSONLY (0x00000002) is set. It means that 1) I know that a menu should be shown and 2) that because of the flag CMF_VERBSONLY that this menu was queried by a .lnk file and that (source Microsoft):

0x00000002. The shortcut menu is that of a shortcut file (normally, a .lnk file). Shortcut menu handlers should ignore this value.

Most of the time I don't add menu items when this flag is present. When right-clicking on an .lnk file then windows will return a standard menu for .lnk files and the opportunity will be offered to delete this file. I had the same opportunity with the favorites folder on Windows 7. Since Windows 10 and "introduction" of quick access it is not possible anymore. No "Unpin" menu item will be shown by default.

Since it's very difficult from the namespace extension, assuming IID_IContextMenu and CMF_VERBSONLY to know if the object is pinned in quick access and also how to unpin it - I probably would have to open the automatic jump lists folder and then check all the jump list files against my object displayname - , I was wondering if there is an easier way to handle this (at the end jump list are a concatenation of lnk files).

Thanks for your help


Solution

  • Thanks to Simon Mourier's hint, here a possible way to check if a folder (of any type) is pinned in quick access or not...

    extern bool __cdecl IsInQuickAccess(LPWSTR folderParsingName)
    {
        IShellFolder* desktopFolder;
        HRESULT hr = SHGetDesktopFolder(&desktopFolder);
        bool isInQuickAccess = false;
    
        if (SUCCEEDED(hr))
        {
            LPITEMIDLIST quickAccessIdList;
            hr = desktopFolder->ParseDisplayName(NULL, NULL, _T("shell:::{679f85cb-0220-4080-b29b-5540cc05aab6}"), NULL, &quickAccessIdList, NULL);
    
            if (SUCCEEDED(hr))
            {
                IShellFolder* quickAccessFolder;
                hr = desktopFolder->BindToObject(quickAccessIdList, NULL, IID_PPV_ARGS(&quickAccessFolder));
    
                if (SUCCEEDED(hr))
                {
                    IEnumIDList* currentChildren = NULL;
                    hr = quickAccessFolder->EnumObjects(NULL, SHCONTF_FOLDERS, &currentChildren);
    
                    if (SUCCEEDED(hr))
                    {
                        CString wPathToFolder = CharLower(folderParsingName);
                        LPITEMIDLIST childPidl = NULL;
    
                        while (!isInQuickAccess && currentChildren->Next(1, &childPidl, NULL) == S_OK)
                        {
                            STRRET childDisplayName;
                            hr = quickAccessFolder->GetDisplayNameOf(childPidl, SHGDN_FORPARSING, &childDisplayName);
    
                            if (SUCCEEDED(hr))
                            {
                                LPWSTR childDisplayNameString;
                                hr = StrRetToStr(&childDisplayName, NULL, &childDisplayNameString);
    
                                if (SUCCEEDED(hr))
                                {
                                    LPWSTR childDisplayNameStringToLower = CharLower(childDisplayNameString);
    
                                    if (wPathToFolder.Compare(childDisplayNameStringToLower) == 0)
                                        isInQuickAccess = true;
    
                                    CoTaskMemFree(childDisplayNameString);
                                }
                            }
                        }
    
                        CoTaskMemFree(childPidl);
                        currentChildren->Release();
                    }
                    quickAccessFolder->Release();
                }
                CoTaskMemFree(quickAccessIdList);
            }
            desktopFolder->Release();
        }
        return isInQuickAccess;
    }
    

    and here unpin from home (with check if folder with given display name is in quick access).

    extern void __cdecl UnpinFromHome(LPWSTR folderParsingName)
    {
        IShellFolder* desktopFolder;
        HRESULT hr = SHGetDesktopFolder(&desktopFolder);
    
        if (SUCCEEDED(hr))
        {
            LPITEMIDLIST quickAccessIdList;
            hr = desktopFolder->ParseDisplayName(NULL, NULL, _T("shell:::{679f85cb-0220-4080-b29b-5540cc05aab6}"), NULL, &quickAccessIdList, NULL);
    
            if (SUCCEEDED(hr))
            {
                IShellFolder* quickAccessFolder;
                hr = desktopFolder->BindToObject(quickAccessIdList, NULL, IID_PPV_ARGS(&quickAccessFolder));
    
                if (SUCCEEDED(hr))
                {
                    IEnumIDList* currentChildren = NULL;
                    hr = quickAccessFolder->EnumObjects(NULL, SHCONTF_FOLDERS, &currentChildren);
    
                    if (SUCCEEDED(hr))
                    {
                        CString wPathToFolder = CharLower(folderParsingName);
                        LPITEMIDLIST childPidl = NULL;
                        bool isInQuickAccess = false;
    
                        while (!isInQuickAccess && currentChildren->Next(1, &childPidl, NULL) == S_OK)
                        {
                            STRRET childDisplayName;
                            hr = quickAccessFolder->GetDisplayNameOf(childPidl, SHGDN_FORPARSING, &childDisplayName);
    
                            if (SUCCEEDED(hr))
                            {
                                LPWSTR childDisplayNameString;
                                hr = StrRetToStr(&childDisplayName, NULL, &childDisplayNameString);
    
                                if (SUCCEEDED(hr))
                                {
                                    LPWSTR childDisplayNameStringToLower = CharLower(childDisplayNameString);
    
                                    if (wPathToFolder.Compare(childDisplayNameStringToLower) == 0)
                                    {
                                        IContextMenu* childContextMenu;
                                        LPCITEMIDLIST childCPidl = childPidl;
                                        hr = quickAccessFolder->GetUIObjectOf(NULL, 1, &childCPidl, IID_IContextMenu, NULL, (void**)&childContextMenu);
    
                                        if (SUCCEEDED(hr))
                                        {
                                            HMENU hmenu = CreatePopupMenu();
                                            if (hmenu)
                                            {
                                                hr = childContextMenu->QueryContextMenu(hmenu, 0, 1, 0x7FFF, CMF_NORMAL);
                                                if (SUCCEEDED(hr))
                                                {
                                                    CMINVOKECOMMANDINFO info = { 0 };
                                                    info.cbSize = sizeof(info);
                                                    info.hwnd = NULL;
                                                    info.lpVerb = "unpinfromhome";
                                                    info.nShow = 1;
                                                    info.fMask = CMIC_MASK_ASYNCOK;
                                                    childContextMenu->InvokeCommand(&info);
                                                }
                                                DestroyMenu(hmenu);
                                            }
    
                                        }
                                        isInQuickAccess = true;
                                    }
                                    CoTaskMemFree(childDisplayNameString);
                                }
                            }
                        }
                        CoTaskMemFree(childPidl);
                        currentChildren->Release();
                    }
                    quickAccessFolder->Release();
                }
                CoTaskMemFree(quickAccessIdList);
            }
            desktopFolder->Release();
        }
    }