Violet Giraffe
Violet Giraffe

Reputation: 33579

Calling Windows shell menu (same as right-click in Explorer) for multiple files programmatically

I'm trying to display a shell context menu for a file (same as when I right-click that file in Explorer) programmatically. I've managed to do that for a single file / folder, my code is below. Now, how can I call a context menu for a list of files, as if I have selected a couple in Explorer and clicked them?

bool openShellContextMenuForObject(const std::wstring &path, int xPos, int yPos, void * parentWindow)
{
    assert (parentWindow);
    ITEMIDLIST * id = 0;
    std::wstring windowsPath = path;
    std::replace(windowsPath.begin(), windowsPath.end(), '/', '\\');
    HRESULT result = SHParseDisplayName(windowsPath.c_str(), 0, &id, 0, 0);
    if (!SUCCEEDED(result) || !id)
        return false;
    CItemIdListReleaser idReleaser (id);

    IShellFolder * ifolder = 0;

    LPCITEMIDLIST idChild = 0;
    result = SHBindToParent(id, IID_IShellFolder, (void**)&ifolder, &idChild);
    if (!SUCCEEDED(result) || !ifolder)
        return false;
    CComInterfaceReleaser ifolderReleaser (ifolder);

    IContextMenu * imenu = 0;
    result = ifolder->GetUIObjectOf((HWND)parentWindow, 1, (const ITEMIDLIST **)&idChild, IID_IContextMenu, 0, (void**)&imenu);
    if (!SUCCEEDED(result) || !ifolder)
        return false;
    CComInterfaceReleaser menuReleaser(imenu);

    HMENU hMenu = CreatePopupMenu();
    if (!hMenu)
        return false;
    if (SUCCEEDED(imenu->QueryContextMenu(hMenu, 0, 1, 0x7FFF, CMF_NORMAL)))
    {
        int iCmd = TrackPopupMenuEx(hMenu, TPM_RETURNCMD, xPos, yPos, (HWND)parentWindow, NULL);
        if (iCmd > 0)
        {
            CMINVOKECOMMANDINFOEX info = { 0 };
            info.cbSize = sizeof(info);
            info.fMask = CMIC_MASK_UNICODE;
            info.hwnd = (HWND)parentWindow;
            info.lpVerb  = MAKEINTRESOURCEA(iCmd - 1);
            info.lpVerbW = MAKEINTRESOURCEW(iCmd - 1);
            info.nShow = SW_SHOWNORMAL;
            imenu->InvokeCommand((LPCMINVOKECOMMANDINFO)&info);
        }
    }
    DestroyMenu(hMenu);

    return true;
}

Upvotes: 0

Views: 996

Answers (1)

Jonathan Potter
Jonathan Potter

Reputation: 37122

result = ifolder->GetUIObjectOf((HWND)parentWindow, 1, (const ITEMIDLIST **)&idChild, IID_IContextMenu, 0, (void**)&imenu);

The GetUIObjectOf function takes an array of PIDLs. The 1 in that function call indicates that your array only contains 1 item, but you can pass any number of child PIDLs using the same method. E.g.:

LPITEMIDLIST pidlArray[3] = { pidl1, pidl2, pidl3 };
result = ifolder->GetUIObjectOf((HWND)parentWindow, _countof(pidlArray), pidlArray, IID_IContextMenu, 0, (void**)&imenu);

(In the real world you would build your array dynamically). Note that the items all have to be children of the same parent folder.

Upvotes: 2

Related Questions