0

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

gouigoui
  • 48
  • 7
  • 1
    Not sure I fully understand. Jump Lists and Quick Access are not the same. You can browse what's pinned in Quick Access if you ask the Quick Access folder (CLSID_HomeFolder: 679f85cb-0220-4080-b29b-5540cc05aab6) the list of its children. – Simon Mourier Mar 10 '18 at 09:14
  • Thanks a lot Simon! This is the 'obvious' solution instead of reinventing the wheel. I also misunderstood the relation between jump lists and quick access. I should have written the quick access' jump list "f01b4d95cf55d32a.automaticDestinations-ms" and not the quick access' jump ListS. Still I don't understand why "unpin" menu item is not shown by default. – gouigoui Mar 10 '18 at 11:42
  • 1
    I agree this is weird. But it does that also on regular folders, so it looks like a Windows design bug... To unpin, just call the "unpinfromhome" verb on a given child item located under QuickAccess https://social.msdn.microsoft.com/Forums/en-US/155aba8d-2fa4-49fe-b5ef-1b114a19e5f2/how-to-programmatically-invoke-unpinfromhome-from-c?forum=windowssdk – Simon Mourier Mar 10 '18 at 11:49

1 Answers1

0

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();
    }
}
gouigoui
  • 48
  • 7