Skip to content

Repository files navigation

MultiPathContextMenu v1.4

Show an IContextMenu for files across multiple paths (and drives!)

image

This method takes advantage of two new features in Windows 7+, Libraries, and Search Folders. Libraries were created for the express purpose of combining multiple paths as one, so it's a natural fit. Unlike some other methods for this, using Libraries helps ensure that it works smoothly even when files are spread across drive letters. We're not backed by an Explorer window here, so we need a way of getting only the folders and files we need. For that we hook it up with search.

First, the search scope is set: We take the set of full paths and create a de-duplicated list of folders, then add them to a new Shell Library object (purely virtual, it's not creating a .library-ms file).

Then, we use the SearchFolderItemFactory class and create a condition for it that matches only our exact files-- while this is a shell search, you can search by PROPERTYKEY, and the PKEY_ItemPathDisplay key is a string containing the full file path, so we can match exactly what we want but not mix up e.g. if files with the same name exist in 2+ folders but only one was requested.

Finally, that gives us a result as an IShellItem representing a folder containing our files. And only our files. So we enumerate all the items, get pidls for them, then create an IShellItemArray that's based on the search folder, so the pidls are all single level and work for a context menu. All that's left is to query it for IContextMenu and display!

If you know a better method, that displays the complete context menu you'd get in a real Library, by all means share. I tried many other methods; DEFCONTEXTMENU omitted most items even if the proper registry keys were opened, for example. Multiple people mention using an IShellFolder implementation, but never any details or source.

Requirements

  • Windows 7+
  • twinBASIC
  • Windows Development Library for twinBASIC (References->Available packages).

VB6 port:

  • oleexp.tlb with addons mIID.bas and mPKEY.bas (included with download)
  • NOTE: VB6 port is v1.0 only, it's now behind the v1.1+ updates of the main tB version.

Changelog

  • v1.4 (23 Mar 2026) - Added the IObjectWithSite and IServiceProvider calls so Share at least works as well as it does in other 3rd party browsers.
  • v1.3 (23 Jun 2025) - The original version froze if you pass a folder in the drive root, e.g. C:\folder. Numerous methods using documented features were unsuccessful in preventing this, so the standard method now resorts to minimal use of the undocumented ISearchFolderItemFactoryPriv interface. That's not present on Win10+, so we have a fallback for ISearchFolderItemFactoryPrivEx which contains the same method we're interested it. It doesn't use any scope factory or scope APIs, so should be compatible with all current Windows. The original method is included if you want to continue exploring solutions that don't rely on undocumented magic.
  • v1.2 (22 Jun 2025) - Demonstration of 2 similar more efficient methods of setting the search scope, using undocumented interfaces and APIs, see SHUndoc.twin for details. These do not work on Windows 7, and on 8 only Undoc v1 works. For most apps, I recommend sticking to the original method, which uses all documented interfaces/APIs.
  • v1.1 (18 Jun 2025) - Support IContextMenu3/2 HandleMenuMsg routing; fix custom owner/coord use
  • v1.0 (17 Jun 2025) - Initial release.

New in v1.2: Undocumented APIs

These let us skip the whole complicated routine with IShellLibrary and IObjectArray, which seems to speed up the menu appearance noticably. SHCreateScopeFromIDListsEx is the fastest and most efficient; from the call stack and symbol files, I could determine that the Scope Factory interfaces were actually just a wrapper for the APIs; unusual, as it's typically the other way around.

 [InterfaceId("18455d05-d8f8-47f0-ba4c-c3aaf9c7035f")]
[OleAutomation(False)]
Interface ISearchFolderItemFactoryPriv Extends IUnknown
SubSetScopeWithDepth(ByVal scope AsIShellItemArray, ByVal depth AsSCOPE_ITEM_DEPTH)
End Interface
[InterfaceId("BD59C2F9-F763-400D-A76E-028C35D047B8")]
[OleAutomation(False)]
Interface ISearchFolderItemFactoryPrivEx Extends IUnknown
SubSetScopeWithDepth(ByVal scope AsIShellItemArray, ByVal depth AsSCOPE_ITEM_DEPTH)
SubSetScopeDirect(ByVal scope AsIScope)
End Interface
[InterfaceId("54410B83-6787-4418-9735-5AAAABE83A9A")]
[OleAutomation(False)]
Interface IScopeFactory Extends IUnknown
SubCreateScope(ByRef riid AsUUID, ByRef ppv AsAny)
SubCreateScopeFromShellItemArray(ByVal si AsIShellItemArray, ByRef riid AsUUID, ByRef ppv Asany)
SubCreateScopeFromIDLists(ByVal cidl AsLong, apidl AsLongPtr, ByValtypeAsSCOPE_ITEM_TYPE, ByVal depth AsSCOPE_ITEM_DEPTH, ByVal flags AsSCOPE_ITEM_FLAGS, ByRef riid AsUUID, ByRef ppv Asany)
SubCreateScopeItemFromIDList(ByVal pidl AsLongPtr, ByValtypeAsSCOPE_ITEM_TYPE, ByVal depth AsSCOPE_ITEM_DEPTH, ByVal flags AsSCOPE_ITEM_FLAGS, ByRef id2 AsUUID, ByRef ppv Asany)
SubCreateScopeItemFromKnownFolder(ByRef id1 AsUUID, ByValtypeAsSCOPE_ITEM_TYPE, ByVal depth AsSCOPE_ITEM_DEPTH, ByVal flags AsSCOPE_ITEM_FLAGS, ByRef id2 AsUUID, ByRef ppv Asany)
SubCreateScopeItemFromShellItem(ByVal si AsIShellItem, ByValtypeAsSCOPE_ITEM_TYPE, ByVal depth AsSCOPE_ITEM_DEPTH, ByVal flags AsSCOPE_ITEM_FLAGS, ByRef id2 AsUUID, ByRef ppv Asany)
End Interface
[CoClassId("6746C347-576B-4F73-9012-CDFEEA251BC4")]
[Description("CLSID_ScopeFactory")]
CoClass ScopeFactory
[Default] Interface IScopeFactory
End CoClass
PublicDeclarePtrSafeFunctionSHCreateScopeLib "Windows.Storage.Search.dll" (ByRef riid AsUUID, ByRef ppv AsLongPtr) AsLongPublicDeclarePtrSafeFunctionSHCreateScopeFromShellItemArrayLib "Windows.Storage.Search.dll" (ByVal si AsIShellItemArray, ByRef riid AsUUID, ByRef ppv AsAny) AsLongPublicDeclarePtrSafeFunctionSHCreateScopeFromIDListsExLib "Windows.Storage.Search.dll" (ByVal cidl AsLong, apidl AsLongPtr, ByValtypeAsSCOPE_ITEM_TYPE, ByVal depth AsSCOPE_ITEM_DEPTH, ByVal flags AsSCOPE_ITEM_FLAGS, ByRef riid AsUUID, ByRef ppv AsAny) AsLongPublicDeclarePtrSafeFunctionSHCreateScopeItemFromIDListLib "Windows.Storage.Search.dll" (ByVal pidl AsLongPtr, ByValtypeAsSCOPE_ITEM_TYPE, ByVal depth AsSCOPE_ITEM_DEPTH, ByVal flags AsSCOPE_ITEM_FLAGS, ByRef id2 AsUUID, ByRef ppv AsAny) AsLongPublicDeclarePtrSafeFunctionSHCreateScopeItemFromKnownFolderLib "Windows.Storage.Search.dll" (ByRef id1 AsUUID, ByValtypeAsSCOPE_ITEM_TYPE, ByVal depth AsSCOPE_ITEM_DEPTH, ByVal flags AsSCOPE_ITEM_FLAGS, ByRef id2 AsUUID, ByRef ppv AsAny) AsLongPublicDeclarePtrSafeFunctionSHCreateScopeItemFromShellItemLib "Windows.Storage.Search.dll" (ByVal si AsIShellItem, ByValtypeAsSCOPE_ITEM_TYPE, ByVal depth AsSCOPE_ITEM_DEPTH, ByVal flags AsSCOPE_ITEM_FLAGS, ByRef id2 AsUUID, ByRef ppv AsAny) AsLong

About

Show an IContextMenu for files across multiple paths (and drives!)

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Contributors

Languages