3

I'm trying to retrieve the shell context menu, the menu will not be displayed since I only need the HMENU to get the name and icons of menu items (including items in sub menus). There are plenty of articles talking about this online, and following them I have successfully get most of it working.

By supplying a PIDL of the selected item and an empty HMENU, my code can get the top level of the shell context menu without any problem.

// Callee need to DestroyMenu(hTopMenu);
HRESULT FillContextMenuFromPIDL(LPCITEMIDLIST pidl, HMENU hTopMenu)
{
    HRESULT ret = MAKE_HRESULT(SEVERITY_ERROR, 0, 0);

    IContextMenu *pcm;
    HRESULT hr;

    IShellFolder *psf;
    LPCITEMIDLIST pidlChild;
    if (SUCCEEDED(hr = SHBindToParent(pidl, IID_IShellFolder, (void**)&psf, &pidlChild))) {
        hr = psf->GetUIObjectOf(NULL, 1, &pidlChild, IID_IContextMenu, NULL, (void**)&pcm);
        // 0 was hWnd
        psf->Release();

        if (SUCCEEDED(hr)) {
            hr = pcm->QueryContextMenu(hTopMenu, 0, 1, 0x7FFF, 0x00020490);

            if (SUCCEEDED(hr)) {
                IContextMenu3* hContextMenu3 = NULL;
                IContextMenu2* hContextMenu2 = NULL;

                if (SUCCEEDED(pcm->QueryInterface(IID_IContextMenu3, (void**)&hContextMenu3))) {
                    LRESULT lres;

                    hContextMenu3->HandleMenuMsg2(WM_INITMENUPOPUP, (WPARAM)hTopMenu, 0, &lres);

                    for (int pos = 0; pos < GetMenuItemCount(hTopMenu); pos++) {
                        HMENU subMenu = GetSubMenu(hTopMenu, pos);
                        if (subMenu != NULL) {
                            LPARAM lparam = (LPARAM)(MAKELONG(pos, FALSE));
                            hContextMenu3->HandleMenuMsg2(WM_INITMENUPOPUP, (WPARAM)subMenu, lparam, &lres);
                        }
                    }
                    //hContextMenu3->Release();
                }

                if (SUCCEEDED( pcm->QueryInterface(IID_IContextMenu2, (void**)&hContextMenu2) )) {
                    for (int pos = 0; pos < GetMenuItemCount(hTopMenu); pos++) {
                        HMENU subMenu = GetSubMenu(hTopMenu, pos);
                        if (subMenu != NULL) {
                            LPARAM lparam = (LPARAM)(MAKELONG(pos, FALSE));
                            hContextMenu2->HandleMenuMsg(WM_INITMENUPOPUP, (WPARAM)subMenu, lparam);
                        }
                    }
                    //hContextMenu2->Release();
                }

                ret =  MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
            }

            //pcm->Release();
        }
    }

    return ret;
}

Previous programmers mentioned that in order to make 'Open with' and 'Send to' work, you have to forward WM_INITMENUPOPUP message to IContextMenu2:HandleMenuMsg and IContextMenu3:HandleMenuMsg2 in WndProc. Since I cannot display the menu, I send WM_INITMENUPOPUP to these two interfaces and let them add dynamic menu items. This works for 'Open With' and 'Give permission to', but not 'Send to'. GetMenuItemCount() returns 1 for 'Send to'.

Later I did an experiment, I hooked the WndProc of my desktop and let it drop all WM_INITMENUPOPUP so that the original WndProc and HandleMenuMsg could not see them. 'Open With' and 'Give permission to' stopped working as expected, but interestingly the 'Sent to' sub menu items were not affected! Also, GetMenuItemCount(HWND of 'Sent to') at the first WM_INITMENUPOPUP gives 1, but it gives the correct number at the WM_INITMENUPOPUP of 'Sent to', even the HandleMenuMsg didnt receive it!

Seems like these 'Send to' menu items are added after the top level menu is shown and before the sub menu becomes visible. Is there any workaround? Thanks in advance!

Rikka0_0
  • 61
  • 5

0 Answers0