0

How do I enumerate the shell context menu item using Windows API? I need to do it in pure C.

user877329
  • 6,717
  • 8
  • 46
  • 88

3 Answers3

1

You need to do exactly what Explorer does: for the item in question (file, folder, other shell namespace location) you identify the list of shell extensions. These are COM classes and they implement IContextMenu interface. For every shell extension applicable, you can provide your own menu and request that such extension (handler) populates the menu with its additional items (IContextMenu::QueryContextMenu).

Later on you are free to decide when, where and if you are going to pop this menu up. Should there a need in handle one of those additional handler's commands, you are responsible to fowrward the command to the handler via IContextMenu::InvokeCommand. If you for some reason prefer to automate certain command and invoke it without popping up the menu, you can do it as well.

Links you might be interested in:

Community
  • 1
  • 1
Roman R.
  • 68,205
  • 6
  • 94
  • 158
  • From where do i get the declaration of the SHELLFOLDER structure? – user877329 Jul 05 '12 at 18:03
  • Do you mean `IShellFolder`? From API calls, e.g. [`SHGetDesktopFolder`](http://msdn.microsoft.com/en-us/library/windows/desktop/bb762175%28v=vs.85%29.aspx). – Roman R. Jul 05 '12 at 18:39
  • Is there a function that creates an IShellFolder object out of a directory path in the Win32 namespace i.e C:\Users\Foo – user877329 Jul 05 '12 at 19:03
  • Check these code snippets out: http://forums.codeguru.com/archive/index.php/t-105564.html – Roman R. Jul 05 '12 at 19:07
1

It is a little unclear to me if you want to show a menu or just enumerate the items, this does the latter (Excluding submenus)

HRESULT GetContextMenuForFSItem(PCTSTR path,IContextMenu**ppCM)
{
    PIDLIST_ABSOLUTE pidlAbs;
    HRESULT hr = SHParseDisplayName(path,0,&pidlAbs,0,0);
    if (!hr)
    {
        IShellFolder*pSF;
        PCUITEMID_CHILD pidlLast;
        hr = SHBindToParent(pidlAbs,&IID_IShellFolder,(void**)&pSF,&pidlLast);
        if (!hr)
        {
            hr = pSF->lpVtbl->GetUIObjectOf(pSF,0,1,&pidlLast,&IID_IContextMenu,0,(void**)ppCM);
            pSF->lpVtbl->Release(pSF);
        }
        ILFree(pidlAbs);
    }
    return hr;
}

int main()
{
    CoInitialize(0);
    WCHAR buf[MAX_PATH];
    GetWindowsDirectory(buf,MAX_PATH); /* Arbitrary filesystem item */
    IContextMenu*pCM;
    HRESULT hr = GetContextMenuForFSItem(buf,&pCM);
    if (!hr) 
    {
        HMENU hMenu = CreatePopupMenu();
        hr = pCM->lpVtbl->QueryContextMenu(pCM,hMenu,0,1,0x7fff,0);
        if (hr > 0)
        {
            UINT c=GetMenuItemCount(hMenu), i=0;
            for (; i<c; ++i)
            {
                GetMenuString(hMenu,i,buf,MAX_PATH,MF_BYPOSITION);
                if (GetMenuState(hMenu,i,MF_BYPOSITION)&MF_SEPARATOR) lstrcpy(buf,_T("--separator--"));
                printf("%.2u=%ws\n",i,buf);
                /*
                Call IContextMenu::GetCommandString  to get the verb 
                or IContextMenu::InvokeCommand to execute
                */
            }
        }
        pCM->lpVtbl->Release(pCM);
        DestroyMenu(hMenu);
    }
    CoUninitialize();
    return 0;
}

On my system this prints:

00=&Open
01=--separator--
02=S&hare with
03=Restore previous &versions
04=&Include in library
05=--separator--
06=Se&nd to
07=--separator--
08=Cu&t
09=&Copy
10=--separator--
11=Create &shortcut
12=&Delete
13=--separator--
14=P&roperties

Using COM in C is not fun, switch to C++ if you can...

Anders
  • 97,548
  • 12
  • 110
  • 164
0

Raymond Chen wrote an eleven part series explaining how to do this. It's really not straightforward.

arx
  • 16,686
  • 2
  • 44
  • 61