c++windowscomwindows-shellshell-namespace-extension

Paste and Paste shortcut Greyed-Out In Context Menu Of Background of Default IShellView


I have a namespace extension that uses both SHCreateShellFolderView and SHCreateDefaultContextMenu. I also use IShellFolderViewCB and IContextMenuCB to create default objects. Most of the expected functionality is working. For example: I can double-click on "folder" items to browse into them, and double-click on "non-folder" items to get an app to launch against the supplied path.

Both of my IShellFolder "folder" and "non-folder" objects have:

dwAttributes |= SFGAO_FILESYSTEM;
dwAttributes |= SFGAO_HASPROPSHEET;
dwAttributes |= SFGAO_CANCOPY;
dwAttributes |= SFGAO_CANMOVE;
dwAttributes |= SFGAO_CANLINK;
dwAttributes |= SFGAO_CANRENAME;
dwAttributes |= SFGAO_CANDELETE;

Each of my IShellFolder "folder" (directory) objects additionally have:

dwAttributes |= SFGAO_STORAGEANCESTOR;
dwAttributes |= SFGAO_STORAGE;
dwAttributes |= SFGAO_FILESYSANCESTOR;
dwAttributes |= SFGAO_FOLDER;
dwAttributes |= SFGAO_BROWSABLE;
dwAttributes |= SFGAO_HASSUBFOLDER;
dwAttributes |= SFGAO_DROPTARGET;

In both cases, I do the proper:

*pdwAttributes &= dwAttributes;

When I right-click on one of the "folder" objects listed in CDefView, to bring-up the context menu for that folder, I can clearly see Paste enabled (dark, not greyed).

When I click in the blank background area of CDefView, to bring-up the context menu for the folder that corresponds to the blank space (not any of the listed items), that is, the folder of the tree control that created the view, Paste and Paste shortcut are present, but are always greyed-out, no matter what is on the Clipboard.

I tried the following to get Paste and Paste shortcut to stop being greyed:

  1. Tweak the SFGAO_* flags.
  2. Disable the invocation ofIContextMenuCB::Callback.
  3. Disable the invocation ofIShellFolderViewCB::MessageSFVCB.
  4. Tweak the associations for theIContextmenu generated in IShellFolder::CreateViewObject.

The code for generating the associations in IShellFolder::CreateViewObject is currently like this after tweaking:

else if (riid == IID_IContextMenu)
{
    ASSOCIATIONELEMENT rgClasses[5];

    unsigned short int i = 0;

    rgClasses[i++] = { ASSOCCLASS_PROGID_STR, 0, TEXT("Directory\\Background") };
    rgClasses[i++] = { ASSOCCLASS_PROGID_STR, 0, TEXT("Folder") };
    rgClasses[i++] = { ASSOCCLASS_PROGID_STR, 0, TEXT("Directory") };       
    rgClasses[i++] = { ASSOCCLASS_PROGID_STR, 0, TEXT("AllFilesystemObjects") };

    IQueryAssociations *pIQueryAssociationInfo = 0;

    hr = AssocCreateForClasses(&rgClasses[0], 1, IID_PPV_ARGS(&pIQueryAssociationInfo));

    if (SUCCEEDED(hr))
    {
        DEFCONTEXTMENU dcm = { hwndOwner, 0, this->pidl_absolute, this, 0, NULL, pIQueryAssociationInfo, 0, NULL };

        hr = SHCreateDefaultContextMenu(&dcm, riid, ppvOut);
    }
}

What is necessary to get Paste and Paste shortcut to stop being greyed for the background context menu in CDefView?

UPDATE:

When a user clicks on one or more items listed in the view (ShellView), the shell invokes IShellFolder::GetUIObjectOf. When the user clicks on the background of the view (IShellView), the shell invokes, instead, IShellFolder::CreateViewObject. Note the distinction: Clicking "on" the folder causes the shell to choose which Object::Method is invoked, even though the folder is the same. This means that, when the user right-clicks in the background of a view (the blank area), technically-speaking, the user has clicked on the folder that is rendering that view (of its children). But because the user is clicking "on" the folder in this way, instead of one of a list of items, the shell chooses IShellFolder::CreateViewObject.

The following call-stacks illustrate what the shell is doing.

The user clicks on a folder in the tree-view. The shell invokes IShellFolder::CreateViewObject, asking for IDropTarget.

BlockSet.dll!PartitionShellFolder::CreateViewObject(HWND__ * hwndOwner, const _GUID & riid, void * * ppvOut) Line 610   C++
shell32.dll!CDefView::CreateViewWindow3(struct IShellBrowser *,struct IShellView *,unsigned long,enum FOLDERFLAGS,enum FOLDERFLAGS,enum FOLDERVIEWMODE,struct _GUID const *,struct tagRECT const *,struct HWND__ * *)   Unknown
ExplorerFrame.dll!FileCabinet_CreateViewWindow2(struct IShellBrowser *,struct tagFolderSetDataBase *,struct IShellView *,struct IShellView *,struct tagRECT *,struct HWND__ * *)    Unknown
ExplorerFrame.dll!CShellBrowser::CreateViewWindow(struct IShellItem *,struct IShellView *,struct IShellView *,struct tagRECT *,struct HWND__ * *)   Unknown
ExplorerFrame.dll!CShellViewFactory::_CreateNewConnection(struct HWND__ *,struct IShellItem *,struct IUnknown *,struct IUnknown * *)    Unknown
ExplorerFrame.dll!CShellViewFactory::BeginCreateConnection(struct HWND__ *,struct IShellItem *,struct IUnknown *,struct IConnectionCreatedCallback *)   Unknown
ExplorerFrame.dll!CShellBrowser::_CreateConnectionForItem(struct IShellItem *,unsigned long,unsigned long)  Unknown
ExplorerFrame.dll!CShellBrowser::_CreateNewConnection(struct IShellItem *,unsigned long,unsigned long)  Unknown
ExplorerFrame.dll!CShellBrowser::_NavigateToPidl(struct _ITEMIDLIST_ABSOLUTE const *,unsigned long,unsigned long)   Unknown
ExplorerFrame.dll!CShellBrowser::_OnGoto(void)  Unknown
ExplorerFrame.dll!CShellBrowser::WndProcBS(struct HWND__ *,unsigned int,unsigned __int64,__int64)   Unknown
ExplorerFrame.dll!IEFrameWndProc(struct HWND__ *,unsigned int,unsigned __int64,__int64) Unknown
user32.dll!UserCallWinProcCheckWow()    Unknown
user32.dll!DispatchMessageWorker()  Unknown
ExplorerFrame.dll!CExplorerFrame::FrameMessagePump(void)    Unknown
ExplorerFrame.dll!BrowserThreadProc(struct IETHREADPARAM *) Unknown
ExplorerFrame.dll!BrowserNewThreadProc(void *)  Unknown
ExplorerFrame.dll!CExplorerTask::InternalResumeRT(void) Unknown
ExplorerFrame.dll!CRunnableTask::Run(void)  Unknown
shell32.dll!CShellTask::TT_Run(bool *)  Unknown
shell32.dll!CShellTaskThread::ThreadProc(void)  Unknown
shell32.dll!CShellTaskThread::s_ThreadProc(void *)  Unknown
shlwapi.dll!WrapperThreadProc(void *)   Unknown
kernel32.dll!BaseThreadInitThunk()  Unknown
ntdll.dll!RtlUserThreadStart()  Unknown

The user right-clicks in the blank area of the view (IShellView). Note the function near the top of the call stack named Def_IsPasteAvailable. Immediately after this call stack, the shell invokes four (4) calls: IDropTarget::Enter, IDropTarget::Leave, IDropTarget::Enter, IDropTarget::Leave.

BlockSet.dll!DropTarget::DragEnter(IDataObject * pDataObj, unsigned long grfKeyState, _POINTL pt, unsigned long * pdwEffect) Line 80    C++
shell32.dll!Def_IsPasteAvailable()  Unknown
shell32.dll!Def_InitEditCommands()  Unknown
shell32.dll!CDefView::_Create_BackgrndHMENU(unsigned int,struct _GUID const &,void * *) Unknown
shell32.dll!CDefView::_CBackgrndMenu_CreateInstance(struct _GUID const &,void * *)  Unknown
shell32.dll!CDefView::GetItemObject(unsigned int,struct _GUID const &,void * *) Unknown
shell32.dll!CDefView::OnBackgroundContextMenu(struct tagPOINT const *)  Unknown
ExplorerFrame.dll!UIItemsView::ShowContextMenu(struct tagPOINT const *) Unknown
ExplorerFrame.dll!CItemsView::ShowContextMenu(struct tagPOINT const *)  Unknown
shell32.dll!CDefView::_OnContextMenu(__int64)   Unknown
shell32.dll!CDefView::WndProc(struct HWND__ *,unsigned int,unsigned __int64,__int64)    Unknown
shell32.dll!CDefView::s_WndProc(struct HWND__ *,unsigned int,unsigned __int64,__int64)  Unknown
user32.dll!UserCallWinProcCheckWow()    Unknown
user32.dll!CallWindowProcAorW() Unknown
user32.dll!CallWindowProcW()    Unknown
duser.dll!WndBridge::RawWndProc(struct HWND__ *,unsigned int,unsigned __int64,__int64)  Unknown
user32.dll!UserCallWinProcCheckWow()    Unknown
user32.dll!DispatchClientMessage()  Unknown
user32.dll!__fnDWORD()  Unknown
ntdll.dll!KiUserCallbackDispatcherContinue()    Unknown
user32.dll!NtUserMessageCall()  Unknown
user32.dll!RealDefWindowProcWorker()    Unknown
user32.dll!RealDefWindowProcW() Unknown
uxtheme.dll!_ThemeDefWindowProc(HWND__ * hwnd, unsigned int uMsg, unsigned __int64 wParam, __int64 lParam, int bUnicode) Line 1058  C++
uxtheme.dll!ThemeDefWindowProcW(HWND__ * hwnd, unsigned int uMsg, unsigned __int64 wParam, __int64 lParam) Line 1076    C++
user32.dll!DefWindowProcW() Unknown
ExplorerFrame.dll!UIItemsView::WndProc(struct HWND__ *,unsigned int,unsigned __int64,__int64)   Unknown
dui70.dll!DirectUI::HWNDElement::StaticWndProc(struct HWND__ *,unsigned int,unsigned __int64,__int64)   Unknown
user32.dll!UserCallWinProcCheckWow()    Unknown
user32.dll!CallWindowProcAorW() Unknown
user32.dll!CallWindowProcW()    Unknown
duser.dll!ExtraInfoWndProc(struct HWND__ *,unsigned int,unsigned __int64,__int64)   Unknown
user32.dll!UserCallWinProcCheckWow()    Unknown
user32.dll!CallWindowProcAorW() Unknown
user32.dll!CallWindowProcW()    Unknown
comctl32.dll!CallOriginalWndProc()  Unknown
comctl32.dll!CallNextSubclassProc() Unknown
comctl32.dll!DefSubclassProc()  Unknown
ExplorerFrame.dll!DefSubclassProc() Unknown
ExplorerFrame.dll!UIItemsView::_UIItemsViewSubclassProc(struct HWND__ *,unsigned int,unsigned __int64,__int64,unsigned __int64) Unknown
ExplorerFrame.dll!UIItemsView::s_UIItemsViewSubclassProc(struct HWND__ *,unsigned int,unsigned __int64,__int64,unsigned __int64,unsigned __int64)   Unknown
comctl32.dll!CallNextSubclassProc() Unknown
comctl32.dll!DefSubclassProc()  Unknown
ExplorerFrame.dll!DefSubclassProc() Unknown
ExplorerFrame.dll!CToolTipManager::_PropertyToolTipSubclassProc(struct HWND__ *,unsigned int,unsigned __int64,__int64,unsigned __int64) Unknown
ExplorerFrame.dll!CToolTipManager::s_PropertyToolTipSubclassProc(struct HWND__ *,unsigned int,unsigned __int64,__int64,unsigned __int64,unsigned __int64)   Unknown
comctl32.dll!CallNextSubclassProc() Unknown
comctl32.dll!DefSubclassProc()  Unknown
comctl32.dll!CallNextSubclassProc() Unknown
comctl32.dll!MasterSubclassProc()   Unknown
user32.dll!UserCallWinProcCheckWow()    Unknown
user32.dll!DispatchMessageWorker()  Unknown
ExplorerFrame.dll!CExplorerFrame::FrameMessagePump(void)    Unknown
ExplorerFrame.dll!BrowserThreadProc(struct IETHREADPARAM *) Unknown
ExplorerFrame.dll!BrowserNewThreadProc(void *)  Unknown
ExplorerFrame.dll!CExplorerTask::InternalResumeRT(void) Unknown
ExplorerFrame.dll!CRunnableTask::Run(void)  Unknown
shell32.dll!CShellTask::TT_Run(bool *)  Unknown
shell32.dll!CShellTaskThread::ThreadProc(void)  Unknown
shell32.dll!CShellTaskThread::s_ThreadProc(void *)  Unknown
shlwapi.dll!WrapperThreadProc(void *)   Unknown
kernel32.dll!BaseThreadInitThunk()  Unknown
ntdll.dll!RtlUserThreadStart()  Unknown

The shell invokes IShellFolder::CreateViewObject, asking for IContextMenu:

BlockSet.dll!PartitionShellFolder::CreateViewObject(HWND__ * hwndOwner, const _GUID & riid, void * * ppvOut) Line 571   C++
shell32.dll!CDefView::_CBackgrndMenu_CreateInstance(struct _GUID const &,void * *)  Unknown
shell32.dll!CDefView::GetItemObject(unsigned int,struct _GUID const &,void * *) Unknown
shell32.dll!CDefView::OnBackgroundContextMenu(struct tagPOINT const *)  Unknown
ExplorerFrame.dll!UIItemsView::ShowContextMenu(struct tagPOINT const *) Unknown
ExplorerFrame.dll!CItemsView::ShowContextMenu(struct tagPOINT const *)  Unknown
shell32.dll!CDefView::_OnContextMenu(__int64)   Unknown
shell32.dll!CDefView::WndProc(struct HWND__ *,unsigned int,unsigned __int64,__int64)    Unknown
shell32.dll!CDefView::s_WndProc(struct HWND__ *,unsigned int,unsigned __int64,__int64)  Unknown
user32.dll!UserCallWinProcCheckWow()    Unknown
user32.dll!CallWindowProcAorW() Unknown
user32.dll!CallWindowProcW()    Unknown
duser.dll!WndBridge::RawWndProc(struct HWND__ *,unsigned int,unsigned __int64,__int64)  Unknown
user32.dll!UserCallWinProcCheckWow()    Unknown
user32.dll!DispatchClientMessage()  Unknown
user32.dll!__fnDWORD()  Unknown
ntdll.dll!KiUserCallbackDispatcherContinue()    Unknown
user32.dll!NtUserMessageCall()  Unknown
user32.dll!RealDefWindowProcWorker()    Unknown
user32.dll!RealDefWindowProcW() Unknown
uxtheme.dll!_ThemeDefWindowProc(HWND__ * hwnd, unsigned int uMsg, unsigned __int64 wParam, __int64 lParam, int bUnicode) Line 1058  C++
uxtheme.dll!ThemeDefWindowProcW(HWND__ * hwnd, unsigned int uMsg, unsigned __int64 wParam, __int64 lParam) Line 1076    C++
user32.dll!DefWindowProcW() Unknown
ExplorerFrame.dll!UIItemsView::WndProc(struct HWND__ *,unsigned int,unsigned __int64,__int64)   Unknown
dui70.dll!DirectUI::HWNDElement::StaticWndProc(struct HWND__ *,unsigned int,unsigned __int64,__int64)   Unknown
user32.dll!UserCallWinProcCheckWow()    Unknown
user32.dll!CallWindowProcAorW() Unknown
user32.dll!CallWindowProcW()    Unknown
duser.dll!ExtraInfoWndProc(struct HWND__ *,unsigned int,unsigned __int64,__int64)   Unknown
user32.dll!UserCallWinProcCheckWow()    Unknown
user32.dll!CallWindowProcAorW() Unknown
user32.dll!CallWindowProcW()    Unknown
comctl32.dll!CallOriginalWndProc()  Unknown
comctl32.dll!CallNextSubclassProc() Unknown
comctl32.dll!DefSubclassProc()  Unknown
ExplorerFrame.dll!DefSubclassProc() Unknown
ExplorerFrame.dll!UIItemsView::_UIItemsViewSubclassProc(struct HWND__ *,unsigned int,unsigned __int64,__int64,unsigned __int64) Unknown
ExplorerFrame.dll!UIItemsView::s_UIItemsViewSubclassProc(struct HWND__ *,unsigned int,unsigned __int64,__int64,unsigned __int64,unsigned __int64)   Unknown
comctl32.dll!CallNextSubclassProc() Unknown
comctl32.dll!DefSubclassProc()  Unknown
ExplorerFrame.dll!DefSubclassProc() Unknown
ExplorerFrame.dll!CToolTipManager::_PropertyToolTipSubclassProc(struct HWND__ *,unsigned int,unsigned __int64,__int64,unsigned __int64) Unknown
ExplorerFrame.dll!CToolTipManager::s_PropertyToolTipSubclassProc(struct HWND__ *,unsigned int,unsigned __int64,__int64,unsigned __int64,unsigned __int64)   Unknown
comctl32.dll!CallNextSubclassProc() Unknown
comctl32.dll!DefSubclassProc()  Unknown
comctl32.dll!CallNextSubclassProc() Unknown
comctl32.dll!MasterSubclassProc()   Unknown
user32.dll!UserCallWinProcCheckWow()    Unknown
user32.dll!DispatchMessageWorker()  Unknown
ExplorerFrame.dll!CExplorerFrame::FrameMessagePump(void)    Unknown
ExplorerFrame.dll!BrowserThreadProc(struct IETHREADPARAM *) Unknown
ExplorerFrame.dll!BrowserNewThreadProc(void *)  Unknown
ExplorerFrame.dll!CExplorerTask::InternalResumeRT(void) Unknown
ExplorerFrame.dll!CRunnableTask::Run(void)  Unknown
shell32.dll!CShellTask::TT_Run(bool *)  Unknown
shell32.dll!CShellTaskThread::ThreadProc(void)  Unknown
shell32.dll!CShellTaskThread::s_ThreadProc(void *)  Unknown
shlwapi.dll!WrapperThreadProc(void *)   Unknown
kernel32.dll!BaseThreadInitThunk()  Unknown
ntdll.dll!RtlUserThreadStart()  Unknown

Solution

  • The "Folder View" is one of Windows Explorer five basic components:

    Windows Explorer File Basic Components

    When one right-clicks on one item (or multiple selected items) in this Folder View (or on items in the Tree View), the Shell will call IShellFolder::GetUIObjectOf for various interfaces and passes the PIDL(s) of the selected item(s).

    When one right-clicks on the Folder View where there's no item, it will call IShellFolder::CreateViewObject for various interfaces, since there's no PIDL to use, as it's about the shown folder itself.

    This is true for IContextMenu for the right-click context menu, as well as other interfaces, such as IDropTarget which is used in Drag&Drop and also Copy/Paste operations.

    And for IDropTarget to be asked, the Shell Folder must expose the SFGAO_DROPTARGET "The specified items are drop targets" attribute.

    I have created an Shell Namespace Extension sample here https://github.com/smourier/ExplorerDataProvider based on Microsoft's official one, that demonstrates (limited) IDropTarget support, enough for the "Paste" menu item to be enabled.

    PS: GetUIObjectOf and CreateViewObject can usually share a lot of code, just handling presence of PIDL(s) or absence of PIDL(s) in a common way.