Another slow one, yes
The original point of introducing a new menu system was to bla bla bla according to this blog. However, ...
What happens when you right-click on item(s)
I have no idea where the right click operation is handled, where can I start from? I knew a lot happen in explorerframe.dll so I looked into that dll with IDA. Let's find a demangled symbol named *Right* in the Functions tab.
Found these two! The second one looks to be the destructor, let's looks into the first one.

As you can see, it looks like the one we should look into. Hmm, but how we can trace the calls down to the context menu manager if there is? Statically analyzing this can be a lot of work, why not use WinDbg for the runtime debugging.
Let's set breakpoints at functions named *ContextMenu* this time. The flyout itself looks to be WUX (Windows.UI.Xaml) so we also can assume it's located at somewhere in windows.ui.*.dll.

Looking through these functions, what do you think is the function that loads commands for the default simple menu?
The context menu manager
According to OleView.NET, it is implemented in Windows.UI.FileExplorer.dll, which is mainly developed for XAML in File Explorer. If I search for ContextMenu in the Names tab in IDA, yes, we find it. It's named ContextMenuPresenter.
class ContextMenuPresenter
: IClosable
, IInspectable
, IOleWindow
, IServiceProvider
, IUnknown
, IWeakReferenceSource
{
// ...
}Executive Summary
The ContextMenuPresenter class is responsible for creating and managing modern "curated" context menus in Windows File Explorer. The TryCreateMiniMenuFromClassic function serves as the primary bridge between legacy Win32 context menus and the modern XAML-based menu system. This report provides a detailed analysis of how the class initializes context menus and loads menu items from multiple sources.
Component Location
Source File: pcshell\shell\windows.ui.fileexplorer\src\ContextMenuPresenter.cpp
Header File: pcshell\shell\windows.ui.fileexplorer\src\ContextMenuPresenter.h
Class Responsibilities
The ContextMenuPresenter class is responsible for:
- Menu Creation: Converting classic Win32 HMENU handles to modern XAML MenuFlyout items
- Item Filtering: Removing unwanted or duplicate menu items from classic menus
- Extension Integration: Loading and inserting app extensions into menus
- Icon Management: Asynchronously loading menu item icons
- Verb Management: Tracking canonical verbs and command IDs
- Telemetry: Recording context menu usage analytics
Architecture: Shell View to Context Menu Presenter
Complete Call Chain from User Action
The context menu creation process follows a well-defined call chain from the shell view (DefView) to the context menu presenter:
User Right-Click Event
│
↓
CDefView::DoContextMenuPopup (0x18029f6b0)
│
↓
CDefView::_DoContextMenuPopup (0x1802b3204)
│
├─► Desktop Spotlight Path (if enabled)
│ └─► [Spotlight-specific handling]
│
└─► Standard Context Menu Path
│
├─► IContextMenu::QueryContextMenu
│ └─► Populates HMENU with classic items
│
└─► CDefView::DoCuratedContextMenu (0x18029f72c)
│
├─► CDefView::TryGetContextMenuPresenter (0x1800a56a0)
│ └─► IUnknown_QueryService → IContextMenuPresenter
│
└─► IContextMenuPresenter::DoContextMenu (vtable+0x20)
└─► ContextMenuPresenter::DoContextMenuHelper (0x1800a9f8b)
└─► [Modern XAML flyout creation]
CDefView::DoContextMenuPopup
Address: 0x18029f6b0
Size: 0x73 bytes (115 bytes)
Purpose: Public interface method for showing context menus
This is the public entry point exposed through the COM interface. Acts as a simple wrapper that validates window state before delegating to the internal implementation.
Implementation:
HRESULT CDefView::DoContextMenuPopup(
CDefView* this,
IUnknown* punkSite,
unsigned int flags,
POINT pt)
{
HWND hwnd = *(this + 68); // Get view window handle
if (hwnd && IsWindowVisible(hwnd))
{
// Call internal implementation with Mode = 0 (curated)
return CDefView::_DoContextMenuPopup(
this - 38, // Adjust this pointer
punkSite,
flags,
pt,
0 // Mode: 0 = Curated menu
);
}
else
{
return E_UNEXPECTED; // 0x8000FFFF
}
}Key Validation:
- Checks if view window exists and is visible
- Returns
E_UNEXPECTEDif window is not ready - Adjusts
thispointer for internal class layout - Always passes
Mode = 0for curated menu behavior
CDefView::_DoContextMenuPopup
Address: 0x1802b3204
Size: 0xe0b bytes (3,595 bytes)
Purpose: Core context menu orchestration and HMENU creation
This is the massive internal implementation that handles the complete context menu creation pipeline. It's one of the most complex functions in the DefView component.
Function Signature:
HRESULT CDefView::_DoContextMenuPopup(
CDefView* this,
IUnknown* punkSite,
unsigned int flags,
POINT pt,
int mode // 0 = Curated, 1 = Classic
)High-Level Flow:
_DoContextMenuPopup
│
├─► 1. TELEMETRY & INITIALIZATION
│ ├─► Start ContextMenuCreationTest telemetry
│ ├─► Check Shift key state (add 0x100 to flags if pressed)
│ └─► ETW event: Shell32_GeneratingContextMenu_Start
│
├─► 2. DESKTOP SPOTLIGHT DETECTION
│ ├─► Check Feature_DesktopSpotlightRightClickContextMenu
│ ├─► Check location context == LOCATION_CONTEXT_DESKTOP (0x400)
│ ├─► If Spotlight: Calculate flyout position from icon rect
│ └─► Log Spotlight telemetry
│
├─► 3. CURATED vs CLASSIC DECISION
│ ├─► Call: ShouldShowMiniMenu(flags)
│ ├─► TryGetContextMenuPresenter()
│ ├─► IsReadyForNewContextMenu()
│ └─► Set mode based on result
│
├─► 4. CLASSIC MENU CREATION (IContextMenu)
│ ├─► QueryInterface → IContextMenu
│ ├─► Setup site if needed (CLauncherUIMode)
│ ├─► CreatePopupMenu() → hMenu
│ ├─► QueryInterface → IContextMenu3 (set 0x20000 flag)
│ ├─► Add IDefaultFolderMenuInitialize restrictions
│ ├─► SetContextMenuCapability()
│ └─► IContextMenu::QueryContextMenu(hMenu, 0, 30977, 31487, flags)
│ └─► Shell extensions populate the menu
│
├─► 5. MERGE ADDITIONAL ITEMS
│ ├─► Load "Open" menu from resources (0x104)
│ ├─► SetMenuItemInfoW (custom text)
│ ├─► Set shield bitmap if needed
│ ├─► Shell_MergeMenus(hMenu, openMenu, ...)
│ ├─► CItemsView::MergeSelectionContextMenu
│ └─► _SHPrettyMenu(hMenu)
│
├─► 6. DARK MODE SUPPORT
│ ├─► Feature_FileExplorerDarkTheme check
│ ├─► AllowDarkModeContextMenu()
│ └─► SetDarkModeForWindows wrapper
│
├─► 7. DISPATCH TO MENU TYPE
│ ├─► If mode == 0 (Curated):
│ │ └─► DoCuratedContextMenu(pContextMenu, hMenu, flags, pt)
│ │ └─► [Calls ContextMenuPresenter]
│ │
│ └─► If mode == 1 (Classic):
│ └─► DoClassicMenu(pContextMenu, hMenu, pt, shellItems)
│ └─► [Traditional Win32 TrackPopupMenu]
│
└─► 8. CLEANUP
├─► DestroyMenu(hMenu)
├─► Release(pContextMenu)
├─► ETW event: Shell32_InvokingContextMenu_Stop
└─► Complete telemetry test
Critical Decision Points:
Desktop Spotlight Detection:
if (Feature_DesktopSpotlightRightClickContextMenu::IsEnabled() &&
(this->flags & 0x10000000) != 0) // Spotlight context flag
{
IShellItemArray* items = NULL;
_GetItems(1, 0, IID_IShellItemArray, &items);
if ((GetLocationContext(items) & 0x7FF) == 0x400) // DESKTOP context
{
// Calculate Spotlight flyout position
int itemIndex = GetItemIndex(-1, 1, 0);
if (itemIndex != -1)
{
RECT rectItem;
GetItemRect(itemIndex, &rectItem);
pt = GetSpotlightFlyoutPosition(&rectItem, m_someFlag);
if (Feature_DesktopSpotlightFlyoutPositionLogging::IsEnabled())
{
DesktopIconLayoutTelemetry::SpotlightFlyoutPositionLogging(
&rectItem, &pt);
}
}
}
}Curated Menu Decision:
bool shouldShowMini = ShouldShowMiniMenu(flags);
testData->shouldShowCuratedMenu = shouldShowMini;
if (shouldShowMini)
{
IContextMenuPresenter* presenter = NULL;
TryGetContextMenuPresenter(&presenter);
if (presenter)
{
GUID testId = GetTestId();
if (!presenter->IsReadyForNewContextMenu(&testId, pt))
{
// Presenter busy - bail out
testData->presenterNotReady = 1;
Complete(telemetryTest);
return S_OK;
}
}
else
{
mode = 1; // Fall back to classic menu
}
}
else
{
mode = 1; // Force classic menu
}IContextMenu::QueryContextMenu Call:
// Set up parameters
UINT idCmdFirst = 30977; // 0x7901
UINT idCmdLast = 31487; // 0x7AFF
UINT uFlags = flags | 0x20 | 0x400; // CMF_DEFAULTONLY | CMF_ASYNCVERBS
if (!mode) // Curated mode
uFlags |= 0x8000; // CMF_CURATED flag
// Set menu restrictions for curated mode
if (!mode)
{
IDefaultFolderMenuInitialize* pInit = NULL;
if (pContextMenu->QueryInterface(
IID_IDefaultFolderMenuInitialize,
&pInit) >= 0)
{
pInit->SetMenuRestrictions(0x2040);
pInit->Release();
}
}
SetContextMenuCapability(hMenu);
// Populate menu with shell extensions
HRESULT hr = pContextMenu->QueryContextMenu(
hMenu,
0, // Index to insert at
idCmdFirst,
idCmdLast,
uFlags
);Menu Item Merging:
// Load additional menu items for file selection
if (m_pShellBrowser && (flags & 0x80)) // DROPEFFECT_COPY flag
{
ICommDlgBrowser2* pCommDlg = NULL;
m_pShellBrowser->QueryInterface(IID_ICommDlgBrowser2, &pCommDlg);
DWORD commDlgFlags = 0;
if (pCommDlg)
pCommDlg->GetCommDlgOptions(&commDlgFlags);
if (!(commDlgFlags & 8)) // CDB2GCO_NO_OPENMENU
{
// Load "Open" menu from resources
HMENU hOpenMenu = SHLoadPopupMenu(g_hinst, 0x104);
if (hOpenMenu)
{
// Customize menu item text if needed
if (pCommDlg)
{
WCHAR szText[260];
if (pCommDlg->GetDefaultMenuText(this, szText, 260) == S_OK)
{
MENUITEMINFOW mii = {0};
mii.cbSize = 80;
mii.fMask = MIIM_STRING;
mii.dwTypeData = szText;
SetMenuItemInfoW(hOpenMenu, 0, TRUE, &mii);
}
}
// Add shield icon if needed
if ((commDlgFlags & 0x40) && m_hShieldBitmap)
{
SHEnableMenuCheckMarkOrBmp(hOpenMenu);
MENUITEMINFOW mii = {0};
mii.cbSize = 80;
mii.fMask = MIIM_BITMAP;
mii.hbmpItem = m_hShieldBitmap;
SetMenuItemInfoW(hOpenMenu, 0, TRUE, &mii);
SHEnableMenuCheckMarkOrBmp(hMenu);
}
// Merge into main menu
Shell_MergeMenus(
hMenu, // Target menu
hOpenMenu, // Source menu
0, // Insert position
0x7026, // First ID
0xFFFFFFFF, // Last ID
1 // Flags
);
SetMenuDefaultItem(hMenu, 0, MF_BYPOSITION);
DestroyMenu(hOpenMenu);
}
}
}
// Merge items from CItemsView
if (flags & 0x80) // Selection context
{
IItemsView* pItemsView = m_pItemsView;
if (pItemsView)
{
pItemsView->MergeSelectionContextMenu(
hMenu,
mergeIndex,
m_pidl // Item ID list
);
}
}
// Apply shell menu styling
_SHPrettyMenu(hMenu);Dark Mode Setup:
Feature_FileExplorerDarkTheme::ReportUsage(
UsageReportingMode::ReportUsage,
TelemetryReportingStage::InUsePhase
);
SetDarkModeForWindows* pDarkMode = NULL;
int darkModeFlags = 0;
bool allowDarkMode = false;
if (allowDarkMode)
{
pDarkMode = new (std::nothrow) SetDarkModeForWindows();
if (pDarkMode)
{
if (AllowDarkModeContextMenu(this, pDarkMode, &darkModeFlags) >= 0)
allowDarkMode = true;
}
}
// Store dark mode context for menu display
// (cleanup via RAII wrapper)Final Dispatch:
// Store context menu state
m_pContextMenu = pContextMenu;
pContextMenu->AddRef();
// Set UI display flag in data object
IDataObject* pDataObj = NULL;
if (IUnknown_GetSelection(m_pContextMenu, &pDataObj) >= 0)
{
DWORD displayed = 1;
DataObj_SetBlobWithIndex(pDataObj, g_cfUIDisplayed, &displayed, 4);
pDataObj->Release();
}
// ETW event
if (ETW_enabled)
McGenEventWrite_EtwEventWriteTransfer(&Shell32_GeneratingContextMenu_Stop);
// Dispatch based on mode
if (mode == 0) // Curated
{
LocationContext = GetLocationContext(shellItems);
testData->locationContextFlags = LocationContext;
Complete(telemetryTest);
DoCuratedContextMenu(pContextMenu, hMenu, flags, &pt);
}
else // Classic
{
LocationContext = GetLocationContext(shellItems);
testData->locationContextFlags = LocationContext;
Complete(telemetryTest);
DoClassicMenu(pContextMenu, hMenu, &pt, shellItems);
CleanupAfterContextMenu();
}Exception Handling:
try
{
// Desktop Spotlight detection and positioning
if (Feature_DesktopSpotlightRightClickContextMenu::IsEnabled() && ...)
{
// ... Spotlight logic ...
}
}
catch (...)
{
wil::Log_CaughtException();
// Continue with regular menu - graceful degradation
}Key Constants:
| Constant | Value | Purpose |
|---|---|---|
idCmdFirst | 30977 (0x7901) | First command ID for shell extensions |
idCmdLast | 31487 (0x7AFF) | Last command ID for shell extensions |
CMF_DEFAULTONLY | 0x20 | Only default menu items |
CMF_ASYNCVERBS | 0x400 | Allow async verb execution |
CMF_CURATED | 0x8000 | Request curated menu |
LOCATION_CONTEXT_DESKTOP | 0x400 | Desktop location flag |
Telemetry Points:
- ContextMenuCreationTest: Tracks menu creation performance
- Shell32_GeneratingContextMenu_Start/Stop: ETW events
- DesktopSpotlightIconRightClicked: Spotlight interaction
- SpotlightFlyoutPositionLogging: Position debugging
- DesktopSpotlightRejuvenatedFlyout: Feature usage
CDefView::DoCuratedContextMenu
Address: 0x18029f72c
Size: 0x337 bytes (823 bytes)
Purpose: Bridge between DefView and ContextMenuPresenter for curated menus
This function is the critical bridge that connects the shell view's classic HMENU to the modern ContextMenuPresenter's XAML flyout.
Function Signature:
void CDefView::DoCuratedContextMenu(
CDefView* this,
IContextMenu* pContextMenu,
HMENU hMenu,
unsigned int flags,
POINT* ppt
)Execution Flow:
DoCuratedContextMenu
│
├─► 1. GET SHELL ITEMS
│ ├─► _GetItems(1, 0, IID_IShellItemArray, &items)
│ └─► GetLocationContext(items) → locationContext
│
├─► 2. ACQUIRE CONTEXT MENU PRESENTER
│ ├─► TryGetContextMenuPresenter(&presenter)
│ ├─► If NULL: Throw E_FAIL (0x80004005)
│ └─► Store current focus: m_hWndFocus = GetFocus()
│
├─► 3. SET PRESENTER FLAGS
│ ├─► If (this->flags & 0x100): presenter->flags |= 0x8
│ ├─► If (screenReaderEnabled): presenter->flags |= 0x1
│ └─► If (Spotlight + Desktop): presenter->flags |= 0x10
│
├─► 4. CALL CONTEXTMENUPRESENTER::DOCONTEXTMENU
│ ├─► Get vtable: vtable = *presenter
│ ├─► Get method: fn = vtable[0x20] // 4th virtual method
│ │
│ ├─► Parameters:
│ │ ├─► rcx = presenter (this)
│ │ ├─► rdx = locationContext
│ │ ├─► r8 = CDefView* (callback context)
│ │ ├─► r9 = POINT (cursor position)
│ │ │
│ │ ├─► Stack parameters:
│ │ │ ├─► hMenu (classic menu handle)
│ │ │ ├─► flags (menu flags)
│ │ │ ├─► testId (telemetry GUID)
│ │ │ ├─► pContextMenu
│ │ │ ├─► idCmdFirst = 30977
│ │ │ ├─► idCmdLast = 31487
│ │ │ ├─► hasIdMap = 1
│ │ │ ├─► pIdMap = &c_DefViewIDMAP
│ │ │ └─► (more params)
│ │ │
│ │ └─► Call via CFG: _guard_dispatch_icall(fn)
│ │
│ └─► If FAILED: Throw_Hr()
│
├─► 5. CONDITIONAL SECONDARY CALLS
│ │
│ ├─► If (Spotlight && Desktop):
│ │ ├─► Destroy classic menu: hMenu = NULL
│ │ ├─► Get vtable[0x28] → DoContextMenuWithClassicMenu
│ │ └─► Call with alt flags + extensions
│ │
│ ├─► If (hasOptionalPresenter):
│ │ ├─► hMenu = NULL
│ │ ├─► Get vtable[0x28]
│ │ └─► Call DoContextMenu variant
│ │
│ └─► Else:
│ ├─► hMenu = NULL
│ ├─► Get vtable[0x28]
│ └─► Standard DoContextMenu call
│
└─► 6. EXCEPTION HANDLING
├─► catch(...):
│ ├─► Log_CaughtException()
│ ├─► testData->curatedMenuException = 1
│ ├─► Complete(telemetryTest)
│ └─► DoExpandedContextMenu (fallback)
│
└─► Release(presenter)
Detailed Analysis:
Getting the Presenter:
IContextMenuPresenter* presenter = NULL;
TryGetContextMenuPresenter(&presenter);
if (!presenter)
{
wil::Throw_Hr(
E_FAIL,
"shell\\SrcPkg\\FileExplorer\\DefView\\src\\DefView.cpp",
Line 2127
);
}TryGetContextMenuPresenter Implementation (0x1800a56a0):
ComPtr<IContextMenuPresenter> CDefView::TryGetContextMenuPresenter()
{
ComPtr<IContextMenuPresenter> result;
// Query for presenter service from shell browser
HRESULT hr = IUnknown_QueryService(
m_pShellBrowser, // Service provider
&SID_ContextMenuPresenter, // GUID_b306c5b1_b4f2_473c_b6ff_701b246ce2d2
&IID_IContextMenuPresenter, // GUID_37a472f7_63cf_4ccf_a88b_5231a3c7d8b6
&result
);
// Handle expected failure codes
if (hr == CLASS_E_CLASSNOTAVAILABLE || // -2147221164 (0x80040154)
hr == E_NOINTERFACE || // -2147467259 (0x80004002)
hr == E_NOTIMPL || // -2147467263 (0x80004001)
hr == ERROR_MOD_NOT_FOUND) // -2147024770 (0x8007007E)
{
// Mark in telemetry that presenter was not available
auto testData = EnsureTestData();
testData->contextMenuPresenterNotAvailable = 1;
}
return result;
}The Critical Call to ContextMenuPresenter::DoContextMenu:
Looking at the assembly at 0x18029f82f - 0x18029f8a0:
; Get the presenter pointer
18029f82f mov r10, [rsp+0E8h+var_50] ; r10 = IContextMenuPresenter*
; Load vtable
18029f837 mov rax, [r10] ; rax = vtable pointer
; Load function pointer at vtable+0x20 (4th virtual method)
18029f83a mov r11, [rax+20h] ; r11 = vtable[4]
; Prepare parameters
18029f892 mov r9, [r14] ; r9 = &POINT
18029f895 mov r8, rbx ; r8 = CDefView* (this)
18029f898 mov edx, edi ; edx = locationContext
18029f89a mov rcx, r10 ; rcx = presenter (this)
; Stack parameters already set up above
; ...
; Make the CFG-protected call
18029f89d mov rax, r11 ; Load function pointer
18029f8a0 call _guard_dispatch_icall$thunk ; CFG validated indirect callVtable Layout Analysis:
// IContextMenuPresenter vtable layout (x64)
0x00: QueryInterface
0x08: AddRef
0x10: Release
0x18: (method 3)
0x20: DoContextMenu ← Called here (vtable+0x20)
0x28: DoContextMenuWithClassicMenu (variant method)Full Parameter Marshaling:
// From the assembly and decompiled code:
HRESULT hr = presenter->DoContextMenu(
// rcx: this pointer (presenter)
locationContext, // rdx: LOCATION_CONTEXT_FLAGS
(CDefView*)this, // r8: callback context
*ppt, // r9: POINT structure
// Stack parameters (pushed right-to-left):
hMenu, // Classic menu handle
presenter->flags, // Flags set above
&testId, // Telemetry test GUID
pContextMenu, // IContextMenu interface
30977, // idCmdFirst
31487, // idCmdLast
1, // hasIdMap
&c_DefViewIDMAP, // Command ID mapping table
&testId, // Test ID again
NULL // Reserved
);Spotlight Special Case:
if (Feature_DesktopSpotlightIconHoverAndSingleClick::IsEnabled() &&
(locationContext & 0x7FF) == 0x400 && // DESKTOP context
m_spotlightEnabled)
{
// Desktop Spotlight gets special handling
presenter->flags |= 0x10; // Spotlight flag
// Destroy classic menu - Spotlight doesn't use it
hMenu = NULL;
// Call different vtable method at offset 0x28
// This method handles Spotlight-specific XAML extension loading
presenter->DoContextMenuWithClassicMenu(
hMenu,
presenter->flags,
&testId,
pContextMenu,
30977,
31487,
1,
&c_DefViewIDMAP,
&testId,
NULL
);
}Exception Handling and Fallback:
try
{
// ... Call presenter ...
}
catch (...)
{
wil::Log_CaughtException();
// Mark failure in telemetry
auto testData = GetTestData();
testData->curatedMenuException = 1;
// Complete telemetry test
Complete(telemetryTest);
// Fall back to expanded context menu (classic Win32)
DoExpandedContextMenu(
*ppt,
flags >> 7, // Extract high byte
NULL,
NULL,
NULL
);
}Key Data Structure: c_DefViewIDMAP
// Command ID mapping table for DefView
const struct QITAB c_DefViewIDMAP[] = {
// Maps command IDs to interface offsets
// Used by ContextMenuPresenter to route commands back to DefView
// ...
};Integration Points:
- Service Query: Uses IUnknown_QueryService to locate presenter
- Vtable Dispatch: CFG-protected indirect call through vtable
- Parameter Passing: Mix of register and stack parameters (x64 calling convention)
- Exception Safety: Try/catch with graceful fallback to classic menu
- Telemetry Integration: TIP test framework tracks success/failure
Context Menu Entry Points
The context menu creation process begins with one of two entry points, depending on whether the user is interacting with Desktop Spotlight or regular file/folder items.
Entry Point Decision Tree
User Right-Click
│
├─► Is Desktop Spotlight Context?
│ └─► Feature Flag: DesktopSpotlightRightClickContextMenu
│ ├─► Enabled + Spotlight Active
│ │ └─► ShowSpotlightContextMenu (0x1800b21d0)
│ │ └─► Loads XAML extension
│ │ └─► DesktopSpotlightHost::ShowXamlFlyout
│ │
│ └─► Disabled or Not Spotlight
│ └─► DoContextMenuHelper (0x1800a9f8b)
│
└─► Regular File/Folder Context
└─► DoContextMenuHelper (0x1800a9f8b)
├─► Initialize telemetry
├─► Query browser services
├─► Decision: Spotlight vs Curated Menu
└─► [Main Flow - See Next Section]
ShowSpotlightContextMenu
Address: 0x1800b21d0
Size: 0x329 bytes (809 bytes)
This function handles the special case of Desktop Spotlight right-click menus, which use a completely separate XAML extension instead of the standard curated menu.
Purpose:
- Loads the Desktop Spotlight XAML extension dynamically
- Creates a flyout UI specifically for Spotlight interactions
- Bypasses the standard context menu creation entirely
Key Steps:
void ShowSpotlightContextMenu(ContextMenuPresenter* this, TipTest* tipTest)
{
// 1. Load the Spotlight XAML extension
hstring category = L"com.microsoft.windows.extensions.xaml.desktopspotlight";
auto xamlExtensions = XamlExtensions::GetForCategory(category);
if (!xamlExtensions)
throw E_FAIL; // Extension not found
// 2. Create the flyout host
hstring hostName = L"DesktopSpotlightFlyoutHost";
hstring contentName = L"DesktopSpotlight";
IInspectable flyoutObject;
xamlExtensions.CreateForPackageFullName(
&flyoutObject,
contentName,
hostName
);
// 3. Query for IFlyout interface
IFlyout flyout;
flyoutObject->QueryInterface(IID_IFlyout, &flyout);
// 4. Convert to FlyoutBase and store
FlyoutBase flyoutBase = flyout.as<FlyoutBase>();
m_xamlFlyout = flyoutBase;
if (!m_xamlFlyout)
throw E_FAIL;
// 5. Register event handlers
auto weakThis = get_weak();
// Closed event handler
m_xamlFlyout.Closed([weakThis](auto sender, auto args) {
if (auto strongThis = weakThis.get())
strongThis->OnSpotlightFlyoutClosed();
});
// Opened event handler
m_xamlFlyout.Opened([weakThis](auto sender, auto args) {
if (auto strongThis = weakThis.get())
strongThis->OnSpotlightFlyoutOpened();
});
// 6. Show the flyout via DesktopSpotlightHost
CuratedContextMenuTelemetry::CuratedContextMenu_ShowMenu();
tipTest.operator=(m_contextMenuCreationTest);
DesktopSpotlightHost::ShowXamlFlyout(
m_spotlightHost,
m_xamlFlyout,
m_parentWindow,
m_invokePoint
);
}Important Notes:
- This is a completely separate code path from the standard context menu
- Only activated when Desktop Spotlight feature flag is enabled
- Uses AppExtension system to load XAML UI dynamically
- Package name:
com.microsoft.windows.extensions.xaml.desktopspotlight - No classic menu filtering - entirely modern UI
DoContextMenuHelper - Main Orchestration
Address: 0x1800a9f8b
Size: 0x678 bytes (1,656 bytes)
Purpose: Main orchestration function that coordinates context menu creation
This is the primary entry point for standard file/folder context menus. It orchestrates the entire menu creation pipeline from initialization through display.
Function Signature
void ContextMenuPresenter::DoContextMenuHelper(
ContextMenuPresenter* this,
HMENU hClassicMenu, // [in] Classic context menu from shell extensions
TipTest* tipTest // [in/out] Telemetry test data
)Where hClassicMenu Comes From
The hClassicMenu passed into DoContextMenuHelper (and then into TryCreateMiniMenuFromClassic) is created by the caller of ContextMenuPresenter::DoContextMenu, not inside ContextMenuPresenter itself.
Evidence in binary:
ContextMenuPresenter::DoContextMenu(0x180053500) receivesHMENU a2and immediately stores it in a memberwil::unique_any_twithout creating it.- The presenter never calls
CreatePopupMenuinDoContextMenuorDoContextMenuHelper.
Observed call chain (in this binary):
FileExplorerFolderViewController::ShowContextMenuForSelection / ShowContextMenuForBackground
└─► IListControlHost2::ShowContextMenu... (vtable call)
└─► ContextMenuPresenter::DoContextMenu(HMENU hClassicMenu, flags, invokePoint)
└─► ContextMenuPresenter::DoContextMenuHelper(hClassicMenu, tipTest)
└─► TryCreateMiniMenuFromClassic(&hMenu, hClassicMenu)
Creation point (classic menu):
The HMENU itself is produced by the shell context menu host using the classic Win32 flow:
CreatePopupMenu()creates the base menu.IContextMenu::QueryContextMenu()(plusIContextMenu2/3hooks) populates it.- That populated
HMENUis then handed toContextMenuPresenter::DoContextMenu.
The concrete function that calls QueryContextMenu is part of the list control host / shell context menu host, not in ContextMenuPresenter.
High-Level Flow
DoContextMenuHelper
│
├─► 1. INITIALIZATION PHASE
│ ├─► Start telemetry watch_errors
│ ├─► Mark ContextMenuCreationTest as started
│ ├─► Transition flyout state to "Creating"
│ └─► Query IServiceProvider for browser services
│ ├─► Get SID_STopLevelBrowser
│ └─► Get frame/window information
│
├─► 2. FEATURE FLAG DECISION
│ ├─► Check: DesktopSpotlightRightClickContextMenu flag
│ │ └─► If Spotlight context
│ │ ├─► Report DesktopSpotlightRejuvenatedFlyout usage
│ │ ├─► If location_context == LOCATION_CONTEXT_DESKTOP (0x400)
│ │ │ └─► If Spotlight enabled for this presenter
│ │ │ └─► ShowSpotlightControl()
│ │ │ └─► RETURN EARLY
│ │ │
│ │ └─► Otherwise: Fall through to curated menu
│ │
│ └─► Regular Context Menu Flow
│ └─► Report DesktopSpotlightRejuvenatedFlyout usage
│
├─► 3. MENU CREATION PHASE
│ ├─► Set next menu ID counter to 32000 (0x7D00)
│ ├─► Create temporary HMENU: hMenu = NULL
│ │
│ ├─► Call: TryCreateMiniMenuFromClassic(&hMenu, hClassicMenu)
│ │ └─► [Filters and creates curated menu - See dedicated section]
│ │
│ ├─► Mark in telemetry: miniMenuCreated = true
│ │
│ └─► If GetMenuItemCount(hMenu) > 0
│ ├─► Mark in telemetry: hasContextMenuItems = true
│ └─► Store menu: swap(m_curatedMenu, hMenu)
│
├─► 4. TELEMETRY SETUP
│ ├─► Create ContextMenuSelectionTest data
│ ├─► Start timing for selection test
│ ├─► Store in m_contextMenuSelectionTest
│ └─► Record location_context flags
│
├─► 5. XAML MENU CREATION (if enabled)
│ ├─► If Spotlight extensions enabled
│ │ │
│ │ ├─► Get IFileExplorerContextMenuExtension2
│ │ │
│ │ ├─► CreateMenuItems(m_curatedMenu)
│ │ │ └─► Convert HMENU to IIterable<MenuItem>
│ │ │
│ │ ├─► CreateMicrosoftUIXamlControlsPrimitivesMenu(menuItems)
│ │ │ └─► Creates CommandBarFlyout
│ │ │
│ │ ├─► Cast to FlyoutBase
│ │ │ └─► Store in m_xamlFlyout
│ │ │
│ │ ├─► Query for ICommandBarFlyout interface
│ │ │
│ │ ├─► RegisterTappedOnShowMoreOptions()
│ │ │ └─► Register handler for "Show more options" button
│ │ │
│ │ ├─► Set flyout state = FlyoutState::ShowingXaml (3)
│ │ │
│ │ ├─► Register Closed event handler
│ │ │ └─► Lambda captures weak_ptr<this>
│ │ │ └─► Calls OnFlyoutClosed when menu dismissed
│ │ │
│ │ ├─► Register Opened event handler
│ │ │ └─► Lambda captures weak_ptr<this>
│ │ │ └─► Calls OnFlyoutOpened when menu shown
│ │ │
│ │ ├─► Get strong reference: get_strong()
│ │ │
│ │ ├─► Update telemetry: m_contextMenuCreationTest = tipTest
│ │ │
│ │ ├─► Call telemetry: CuratedContextMenu_ShowMenu()
│ │ │
│ │ └─► ContextMenuHost::ShowXamlFlyout()
│ │ ├─► Parameters:
│ │ │ ├─► m_contextMenuHost (host window)
│ │ │ ├─► m_xamlFlyout (flyout to show)
│ │ │ ├─► m_parentWindow (parent HWND)
│ │ │ └─► m_invokePoint (screen coordinates)
│ │ │
│ │ └─► [Shows XAML flyout on screen]
│ │
│ └─► Destroy temporary HMENU if still exists
│
└─► 6. CLEANUP & EXCEPTION HANDLING
├─► Release service provider interface
├─► Destroy test watcher
└─► Exception handlers at multiple points:
├─► ShowSpotlightControl exceptions
│ └─► Log exception, record in telemetry
│ └─► Fall back to regular menu
│
└─► ShowXamlFlyout exceptions
└─► Log exception
└─► Complete selection test
└─► Cancel current flyout
Detailed Phase Analysis
Phase 1: Initialization
// Initialize telemetry tracking
TipTestWatcher<ContextMenuCreationTest> watcher;
tip_test::watch_errors(tipTest, &watcher);
// Mark that context menu creation has started
auto testData = tipTest.operator->();
testData->contextMenuCreationStarted = true;
// Transition state machine
TransitionFlyoutState(FlyoutState::Creating); // State = 1
// Query browser services for window/frame information
IServiceProvider* pServiceProvider = m_site;
IShellBrowser* pBrowser = nullptr;
HRESULT hr = IUnknown_QueryService(
pServiceProvider,
SID_STopLevelBrowser,
IID_IShellBrowser,
&pBrowser
);
if (SUCCEEDED(hr))
{
// Get frame and window handles
pBrowser->QueryInterface(IID_IShellFrame, &m_frame);
pBrowser->GetWindow(&m_parentWindow);
}
else
{
// Log warning but continue
wil::Log_Hr(hr, "Failed to get IShellBrowser");
}Key Points:
- Telemetry tracking begins immediately
- State machine transitions:
Idle→Creating - Browser services provide parent window context
- Failures are logged but don't abort the process
Phase 2: Feature Flag Decision
// Check if Desktop Spotlight menu should be shown
if (wil::Feature_DesktopSpotlightRightClickContextMenu::IsEnabled())
{
if (m_isSpotlightContext) // Set by caller
{
// Report usage of rejuvenated flyout feature
wil::Feature_DesktopSpotlightRejuvenatedFlyout::ReportUsage(
UsageReportingMode::ReportUsage,
TelemetryReportingStage::InUsePhase
);
// Check location context
LOCATION_CONTEXT_FLAGS flags = m_locationContextFlags;
if ((flags & 0x7FF) == LOCATION_CONTEXT_DESKTOP &&
m_spotlightEnabled)
{
try
{
// Show Spotlight-specific menu
ShowSpotlightControl(tipTest);
return; // EARLY EXIT
}
catch (...)
{
// Log exception
int hr = wil::ResultFromCaughtException();
testData->spotlightControlException = hr;
Log("ShowSpotlightControl exception: %d", hr);
// Fall through to standard menu
}
}
}
}
else
{
// Feature disabled - report and use standard flow
wil::Feature_DesktopSpotlightRejuvenatedFlyout::ReportUsage(
UsageReportingMode::ReportUsage,
TelemetryReportingStage::InUsePhase
);
}Decision Factors:
- Feature Flag:
Feature_DesktopSpotlightRightClickContextMenumust be enabled - Context Type:
m_isSpotlightContextflag set during initialization - Location: Must be desktop context (
LOCATION_CONTEXT_DESKTOP = 0x400) - Spotlight State:
m_spotlightEnabledmust be true
Exception Handling:
- If ShowSpotlightControl throws, exception is caught
- Error is logged to telemetry
- Flow continues to standard curated menu (graceful fallback)
Phase 3: Menu Creation
// Reset menu ID counter to start of range
_InterlockedExchange(&m_nextMenuId, 32000); // 0x7D00
// Create curated menu from classic menu
HMENU hMenu = nullptr;
TryCreateMiniMenuFromClassic(&hMenu, hClassicMenu);
// [Details in dedicated TryCreateMiniMenuFromClassic section]
// Mark in telemetry
testData->miniMenuCreated = true;
// Check if menu has any items
if (GetMenuItemCount(hMenu) > 0)
{
// Mark success in telemetry
testData->hasContextMenuItems = true;
// Store the menu handle
// Uses wil::unique_any_t for RAII cleanup
m_curatedMenu.swap(hMenu);
hMenu = m_curatedMenu.get();
}Menu ID Assignment:
- Range starts at
32000(0x7D00) - Upper limit is
32255(0x7DFF) - checked during insertion - Each menu item gets sequential ID via
_InterlockedExchangeAdd - IDs used to map back to VerbInfo structures for command routing
Phase 4: Telemetry Setup
// Create selection test data
winrt::com_ptr<TipTestData<ContextMenuSelectionTest>> selectionTest;
selectionTest = tip_test::ensure_data();
// Start timing
selectionTest->timestamp.start();
// Store in member variable
m_contextMenuSelectionTest = selectionTest;
// Record context flags
selectionTest->locationContextFlags = m_locationContextFlags;Telemetry Captured:
- Selection start time
- Location context flags
- User interactions with menu
- Final selection or dismissal
- Performance metrics
Phase 5: XAML Menu Creation
This is the most complex phase, creating the modern XAML UI:
if (m_spotlightEnabled)
{
// 1. Get the context menu extension interface
IFileExplorerContextMenuExtension2* pExtension = nullptr;
TryGetContextMenuExtension2(&pExtension);
// 2. Convert HMENU to XAML MenuItem collection
IIterable<MenuItem> menuItems;
CreateMenuItems(hMenu, &menuItems);
// 3. Create XAML menu flyout
IInspectable menuUIElement;
pExtension->CreateMicrosoftUIXamlControlsPrimitivesMenu(
menuItems,
&menuUIElement
);
// 4. Cast to FlyoutBase
FlyoutBase flyout = menuUIElement.as<FlyoutBase>();
m_xamlFlyout = flyout;
// 5. Query for CommandBarFlyout interface (optional)
ICommandBarFlyout* pCommandBarFlyout = nullptr;
if (m_xamlFlyout)
{
m_xamlFlyout->QueryInterface(
IID_ICommandBarFlyout,
&pCommandBarFlyout
);
}
// 6. Register "Show more options" handler
RegisterTappedOnShowMoreOptions(pCommandBarFlyout);
// 7. Update state
m_flyoutState = FlyoutState::ShowingXaml; // = 3
// 8. Register Closed event
auto weakThis = get_weak();
event_token closedToken = m_xamlFlyout.Closed(
[weakThis](auto sender, auto args)
{
if (auto strong = weakThis.get())
{
strong->OnFlyoutClosed();
}
}
);
m_flyoutClosedRevoker = closedToken;
// 9. Register Opened event
event_token openedToken = m_xamlFlyout.Opened(
[weakThis](auto sender, auto args)
{
if (auto strong = weakThis.get())
{
strong->OnFlyoutOpened();
}
}
);
m_flyoutOpenedRevoker = openedToken;
// 10. Get strong reference for callback
winrt::com_ptr<ContextMenuPresenter> strong;
get_strong(&strong);
// 11. Record telemetry
CuratedContextMenu_ShowMenu();
m_contextMenuCreationTest = tipTest;
// 12. Show the flyout
try
{
ContextMenuHost::ShowXamlFlyout(
m_contextMenuHost, // Host control
m_xamlFlyout, // Flyout to show
m_parentWindow, // Parent HWND
m_invokePoint // Screen coordinates
);
}
catch (...)
{
// Log exception but don't crash
wil::Log_CaughtException();
// Update telemetry
auto selTest = tip_test::ensure_data(m_contextMenuSelectionTest);
// ... record exception ...
// Complete test and cancel
tip_test::complete(m_contextMenuSelectionTest);
CancelCurrentFlyout(CancelReason::Exception);
}
}Event Handler Pattern:
- Uses
weak_ptrto avoid circular references - Lambda captures weak pointer
- Checks if object still exists before calling methods
- Event tokens stored in member variables for cleanup
Error Recovery:
- ShowXamlFlyout exceptions are caught
- Telemetry updated with error information
- Flyout is cancelled gracefully
- No crash or hang - degrades gracefully
State Management
FlyoutState Enum:
enum FlyoutState
{
Idle = 0,
Creating = 1,
WaitingForAsync = 2,
ShowingXaml = 3,
ShowingWin32 = 4,
Dismissed = 5
};State Transitions in DoContextMenuHelper:
Idle (0)
↓ [TransitionFlyoutState called]
Creating (1)
↓ [Menu created]
ShowingXaml (3)
↓ [User interacts/dismisses]
Dismissed (5)
Error Handling Strategy
The function has 3 levels of exception handling:
Level 1: ShowSpotlightControl Exceptions
try {
ShowSpotlightControl(tipTest);
return;
}
catch (...) {
Log_CaughtException();
testData->spotlightException = wil::ResultFromCaughtException();
// Continue to standard menu
}Level 2: ShowXamlFlyout Exceptions
try {
ContextMenuHost::ShowXamlFlyout(...);
}
catch (...) {
Log_CaughtException();
// Record in telemetry
tip_test::complete(m_contextMenuSelectionTest);
CancelCurrentFlyout(CancelReason::Exception);
}Level 3: Outer Exception Handler
- Catches any unhandled exceptions
- Ensures cleanup of resources
- Prevents crashes during menu display
Performance Considerations
Menu ID Counter:
- Uses
_InterlockedExchangeand_InterlockedExchangeAdd - Thread-safe atomic operations
- Prevents ID collisions in multi-threaded scenarios
Reference Counting:
- Weak pointers prevent circular references
- Strong pointers only obtained when needed
- Event handler lifetime managed via event_token revokers
Resource Management:
- RAII wrappers (
wil::unique_any_t) for HMENU - Automatic cleanup on exception or early return
- Event tokens stored for proper unregistration
Integration Points
Called By:
- Public interface methods (e.g.,
IContextMenuPresenter::Show) - Window message handlers
- Shell hook callbacks
Calls To:
TryCreateMiniMenuFromClassic- Menu filtering and creationCreateMenuItems- HMENU to XAML conversionContextMenuHost::ShowXamlFlyout- Display managementShowSpotlightControl- Special Spotlight UI- Various telemetry functions
TryCreateMiniMenuFromClassic Function
Function Signature
HMENU* ContextMenuPresenter::TryCreateMiniMenuFromClassic(
ContextMenuPresenter* this,
HMENU* phMenu, // [out] Receives the created popup menu
HMENU hMenu // [in] Source classic menu to filter
)Location
Address: 0x180031818
Size: 0x128 bytes (296 bytes)
Purpose
Creates a "mini menu" (curated context menu) from a classic Win32 context menu by filtering, copying, and enhancing menu items.
Execution Flow
TryCreateMiniMenuFromClassic
│
├─► FilterClassicMenu // Filter and copy menu items
│ │
│ ├─► CreatePopupMenu // Create new menu
│ ├─► GetCanonicalVerbsAndIndices // Map verbs to indices
│ ├─► Loop through MenuItem collection
│ │ ├─► Check if item is separator
│ │ ├─► Check if canonical verb exists
│ │ ├─► GetMenuItemInfo // Get item properties
│ │ └─► CopyMenuItem // Copy to new menu
│ └─► Return filtered menu
│
├─► GetMenuDefaultItem // Find default item in source
│
├─► Copy default item if needed
│
├─► InsertAppExtensionsIntoMiniMenu // Add app extensions
│ │
│ ├─► GetAssociationElements // Get file associations
│ ├─► GetProgIdsFromAssociationElements
│ ├─► ShouldAddCloudProviderVerbs
│ │
│ ├─► AddCloudProviderVerbs // Add cloud provider items
│ │
│ ├─► Loop through ProgIDs
│ │ ├─► GetExtensions // Get extensions for ProgID
│ │ ├─► Loop through extensions
│ │ │ ├─► Build app extension map
│ │ │ └─► GetVerbsForApp
│ │ └─► InsertAppExtensionIntoMiniMenu
│ │
│ └─► Process featured/cloud provider extensions
│
├─► Insert separator
│
└─► _SHPrettyMenu // Apply shell menu styling
Pseudo-Code Implementation
HMENU* TryCreateMiniMenuFromClassic(HMENU* phMenu, HMENU hMenu)
{
*phMenu = NULL;
// Step 1: Filter the classic menu
FilterClassicMenu(this, phMenu, hMenu);
if (*phMenu)
{
// Step 2: Copy default menu item if it exists
UINT defaultIndex = GetMenuDefaultItem(hMenu, TRUE, 0);
UINT newDefaultIndex = GetMenuDefaultItem(*phMenu, TRUE, 0);
if (defaultIndex != -1 && newDefaultIndex == -1)
{
CopyMenuItem(this, hMenu, defaultIndex, *phMenu, 0, NULL);
}
// Step 3: Insert app extensions
if (InsertAppExtensionsIntoMiniMenu(this, *phMenu))
{
// Insert separator before extensions
MENUITEMINFOW mi = {0};
mi.cbSize = 80;
mi.fMask = MIIM_FTYPE | MIIM_STATE;
mi.fType = MFT_SEPARATOR;
InsertMenuItemW(*phMenu, 0x7C30, FALSE, &mi);
}
// Step 4: Apply shell menu styling
_SHPrettyMenu(*phMenu);
}
return phMenu;
}Menu Item Sources
The context menu items come from multiple sources, loaded and merged in a specific order:
1. Classic Context Menu Items (IContextMenu)
Source: Legacy shell extensions implementing IContextMenu interface
Loading Process:
- Queries the classic context menu via
GetMenuItemInfoW - Extracts menu item properties (text, icon, state, ID)
- Maps canonical verb names to menu item indices
- Filters based on verb configuration
Key Functions:
FilterClassicMenu(0x180053b3e)GetCanonicalVerbsAndIndices(0x1800107e5)CreateMenuItems(0x180011bbf)
2. App Extensions (Modern UWP/AppX)
Source: Windows::Internal::FileExplorerAppExtension API
Loading Process:
- Get file associations for selected items via
GetAssociationElements - Extract ProgIDs from association elements
- Query app extensions for each ProgID using
GetExtensions - Build app extension map with verb information
- Asynchronously load menu items for each extension
Key Functions:
InsertAppExtensionsIntoMiniMenu(0x180031f7a)InsertAppExtensionIntoMiniMenu(0x18000e744)LoadMenuItemAsync(0x180059fea)LoadGroupMenuItemForAppAsync(0x180055eb6)
Extension Types:
- File type handlers
- Cloud storage providers
- Share targets
- Quick actions
3. Cloud Provider Verbs
Source: Cloud storage provider extensions
Loading Process:
- Checks if cloud provider verbs should be added via
ShouldAddCloudProviderVerbs - Calls
AddCloudProviderVerbs(0x1800152f9) to enumerate cloud operations - Creates verb elements for cloud-specific actions
- Merges with app extension map
Special Handling:
- Featured cloud provider gets priority placement
- Can create grouped submenu for multiple cloud operations
4. Command Store Commands
Source: IExplorerCommand-based commands from Command Store
Loading Process:
- Matches canonical verb names against VerbConfig entries
- Queries
GetCommandStoreCommand(0x1800162e1) - Validates command state via
IExplorerCommand::GetState - Creates menu item from command properties
Key Functions:
TryCreateMenuItemFromVerbConfig(0x18005fc7e)GetCommandStoreCommand(0x1800162e1)TryGetMenuItemInfoFromExplorerCommand(0x18005fa51)
5. Canonical Verbs
Canonical Verbs Checked: These are standard file operations with well-known verb names:
static const VerbConfig s_VerbConfigs[] = {
{ L"open", IDC_OPEN, IDS_OPEN, ... },
{ L"edit", IDC_EDIT, IDS_EDIT, ... },
{ L"print", IDC_PRINT, IDS_PRINT, ... },
{ L"opencontaining", IDC_OPENCONTAINING, IDS_OPENCONTAINING, ... },
{ L"delete", IDC_DELETE, IDS_DELETE, ... },
{ L"rename", IDC_RENAME, IDS_RENAME, ... },
{ L"properties", IDC_PROPERTIES, IDS_PROPERTIES, ... },
{ L"cut", IDC_CUT, IDS_CUT, ... },
{ L"copy", IDC_COPY, IDS_COPY, ... },
{ L"paste", IDC_PASTE, IDS_PASTE, ... },
// ... more verbs
};Detailed Function Analysis
FilterClassicMenu
Address: 0x180053b3e
Purpose: Filters a classic menu and copies allowed items to a new menu
Algorithm:
1. Create new popup menu
2. Get canonical verbs and their indices from source menu
3. For each item in the MenuItem collection:
a. Check if item is "_separator_" → append separator
b. Try to find canonical verb in hash map
c. If found:
- Get menu item info
- Try to get override from VerbConfig
- Copy menu item to new menu
d. If not found:
- Check against VerbConfig array
- If valid, create menu item from verb config
4. Return filtered menu
Key Data Structures:
unordered_map<wstring, int>- Maps canonical verb names to menu indicesVerbConfigarray - Configuration for known verbsMenuItemcollection - XAML menu item objects
InsertAppExtensionsIntoMiniMenu
Address: 0x180031f7a
Size: 0x973 bytes (2,419 bytes)
Purpose: Inserts app extension menu items into the mini menu
Complex Algorithm:
1. Get association elements for selected items
2. Extract ProgIDs if cloud provider verbs are needed
3. Initialize data structures:
- map<hstring, AppExtensionElement> - Extension info map
- unordered_multimap<hstring, VerbElement> - Verb map
4. Try to add cloud provider verbs
5. For each ProgID:
a. Get extensions via FileExplorerAppExtension API
b. For each extension:
- Get app info (name, icon)
- Build AppExtensionElement entry
- Call AddVerbsForAppExtension to populate verb map
6. Process featured cloud provider extension (if any):
- Get verbs for the featured app
- Call InsertAppExtensionIntoMiniMenu to insert
7. Insert separator
8. Loop through remaining extensions in map:
- Get verbs for each app
- Insert into mini menu
- Check ID limit (0x7DFF)
9. Return count of inserted items
Error Handling:
- Logs caught exceptions but continues processing
- Checks menu ID limit to prevent overflow
- Uses feature flags for performance optimizations
InsertAppExtensionIntoMiniMenu
Address: 0x18000e744
Purpose: Inserts a single app extension into the menu
Behavior Based on Verb Count:
Single Verb:
if (verbCount == 1)
{
// Insert directly without submenu
TryInsertMenuItemForAppExtension(hMenu, extension.guid, extension.displayName);
return 1;
}Multiple Verbs:
if (verbCount > 1)
{
HMENU hSubMenu = CreatePopupMenu();
// Create parent menu item
MENUITEMINFOW mi = {0};
mi.wID = InterlockedIncrement(&nextMenuId);
mi.dwTypeData = extension.name;
mi.hSubMenu = hSubMenu;
if (extension.hasIcon)
{
// Load icon asynchronously
LoadGroupMenuItemForAppAsync(...);
}
else
{
// Insert verbs directly as submenu items
for (auto& verb : extension.verbs)
{
TryInsertMenuItemForAppExtension(hSubMenu, verb.guid, verb.name);
}
SetMenuItemIcon(extension.name, &mi, verbInfo);
}
InsertMenuItemW(hMenu, 0x7C30, FALSE, &mi);
}CopyMenuItem
Address: 0x18000eaba
Purpose: Copies a menu item from one menu to another with icon support
Process:
1. Get menu item info via GetMenuItemInfoW
- Flags: MIIM_STRING | MIIM_CHECKMARKS | MIIM_SUBMENU | ...
2. Insert menu item into destination menu
3. If icon stream is provided:
- Call SetHMenuItemIconAsync to load icon asynchronously
- Add async operation to tracking vector
4. Return menu item ID
Async Pattern:
- Icons are loaded asynchronously to avoid blocking UI
- Uses
IAsyncActionfrom WinRT - Operations tracked in vector for cancellation
GetCanonicalVerbsAndIndices
Address: 0x1800107e5
Purpose: Builds a map of canonical verb names to menu item indices
Algorithm:
void GetCanonicalVerbsAndIndices(HMENU hMenu,
unordered_map<wstring, int>& verbMap)
{
verbMap.clear();
int menuItemCount = GetMenuItemCount(hMenu);
int validIndex = 0;
for (int i = 0; i < menuItemCount; i++)
{
MENUITEMINFOW mi = {0};
mi.cbSize = sizeof(mi);
mi.fMask = MIIM_FTYPE | MIIM_STATE;
GetMenuItemInfoW(hMenu, i, TRUE, &mi);
// Skip separators
if ((mi.fType & MFT_SEPARATOR) == 0)
{
// Get canonical verb for this menu item
wstring verb;
if (TryGetCanonicalVerb(mi.wID, &verb) && !verb.empty())
{
verbMap[verb] = validIndex;
}
validIndex++;
}
}
}Where canonical verbs are stored:
- Transient map (per-filter pass):
FilterClassicMenubuilds a localunordered_map<wstring, int>(viaGetCanonicalVerbsAndIndices) to map canonical verb → classic menu index. This is a temporary map used only during filtering. - Persistent mapping (per presenter instance): When a menu item is copied into the curated menu,
FilterClassicMenuinsertsmenuId → canonical verbinto a member hash map insideContextMenuPresenter(seen asstd::_Hash<...>::_Try_emplaceon(char*)this + 440in the decompile). This is used later for command invocation and telemetry.
Data Structures
AppExtensionElement
struct AppExtensionElement
{
winrt::hstring appName; // Display name of the app
winrt::hstring appId; // App package ID
IRandomAccessStreamReference icon; // Icon stream reference
GUID extensionGuid; // Extension GUID
};VerbElement
struct VerbElement
{
winrt::hstring verbName; // Verb name (e.g., "Open with")
winrt::hstring targetName; // Target display name
GUID commandGuid; // Command GUID
IRandomAccessStreamReference icon; // Icon stream
};VerbInfo
struct VerbInfo
{
UINT menuId; // Menu item ID
winrt::hstring verbName; // Canonical verb name
winrt::hstring displayName; // Display string
IExplorerCommand* pCommand; // Command object
IRandomAccessStreamReference icon; // Icon stream
bool hasIcon; // Icon availability flag
// ... additional fields
};VerbConfig
struct VerbConfig
{
const wchar_t* pszCanonicalName; // Canonical verb name
UINT commandId; // Command ID constant
UINT stringResourceId; // String resource ID
function<bool(IShellItemArray*)> shouldShow; // Visibility predicate
// ... additional configuration
};Hash Maps Used
Canonical Verb Map:
unordered_map<wstring, int> canonicalVerbMap;
// Key: Canonical verb name (e.g., "open", "edit")
// Value: Menu item index in classic menuApp Extension Map:
map<hstring, AppExtensionElement> extensionMap;
// Key: App name/identifier
// Value: App extension informationVerb Multimap:
unordered_multimap<hstring, VerbElement> verbMap;
// Key: App identifier
// Value: Verb information (one app can have multiple verbs)Async Loading Mechanism
Overview
Menu item icons and some properties are loaded asynchronously to prevent UI blocking during context menu creation.
Async Functions
LoadMenuItemAsync (0x180059fea)
- Loads individual menu item asynchronously
- Sets menu item properties when data arrives
- Updates icon bitmap
- Size: 0x57b8 bytes (22,456 bytes) - very large coroutine
LoadGroupMenuItemForAppAsync (0x180055eb6)
- Loads grouped menu item for app with multiple verbs
- Creates submenu structure
- Size: 0x3504 bytes (13,572 bytes)
SetHMenuItemIconAsync
- Asynchronously loads and sets icon for HMENU item
- Uses
IRandomAccessStreamReference::OpenReadAsync - Converts stream to bitmap and applies to menu
Coroutine Pattern
The functions use C++/WinRT coroutines with $_ResumeCoro$1 suffix:
IAsyncAction LoadMenuItemAsync(
hstring appName,
GUID extensionGuid,
HMENU hMenu,
UINT menuId)
{
// Coroutine body
co_await GetAppExtensionInfo(extensionGuid);
co_await LoadIconStream();
// Update menu item on UI thread
MENUITEMINFOW mi = {0};
mi.cbSize = sizeof(mi);
mi.fMask = MIIM_STRING | MIIM_BITMAP;
mi.dwTypeData = displayName.c_str();
mi.hbmpItem = iconBitmap;
SetMenuItemInfoW(hMenu, menuId, FALSE, &mi);
}Async Operation Tracking
// Vector of active async operations
vector<IAsyncAction> m_asyncOperations;
// Add operation
m_asyncOperations.push_back(LoadMenuItemAsync(...));
// Operations can be cancelled when menu is dismissedKey Findings
1. Two-Tier Entry Point Architecture
The context menu system uses two distinct entry points:
DoContextMenuHelper (0x1800a9f8b) - Main orchestration function
- Handles 99% of context menu scenarios
- Coordinates telemetry, menu creation, and UI display
- 1,656 bytes of compiled code - highly complex
- Implements graceful fallbacks on errors
ShowSpotlightContextMenu (0x1800b21d0) - Spotlight-specific path
- Completely separate code path for Desktop Spotlight
- Loads XAML extension dynamically
- Only activated with specific feature flag
- Bypasses standard filtering entirely
This dual-path architecture provides:
- Isolation - Spotlight features don't impact standard menus
- Flexibility - Can evolve independently
- Safety - Failures in Spotlight fall back to standard menu
2. Sophisticated State Machine
DoContextMenuHelper implements a 6-state flyout state machine:
Idle (0) → Creating (1) → WaitingForAsync (2) → ShowingXaml (3) → Dismissed (5)
↓ ↓
└──────────────────────────────────→ ShowingWin32 (4)
State Transitions:
Idle→Creating: When DoContextMenuHelper startsCreating→ShowingXaml: After XAML flyout creation succeedsCreating→ShowingWin32: Fallback for non-XAML scenariosCreating→WaitingForAsync: When waiting for async operationsAny State→Dismissed: User closes menu or error occurs
Purpose:
- Prevents race conditions during async menu creation
- Ensures proper cleanup on early dismissal
- Enables telemetry tracking of menu lifecycle
- Guards against re-entrant calls
3. Multi-Source Menu Construction
The context menu is built from a union of multiple sources:
- Classic extensions provide compatibility with legacy handlers
- App extensions enable modern UWP app integration
- Cloud providers add cloud-specific operations
- Command store provides consistent command implementation
2. Filtering and Curation
Not all classic menu items appear in the mini menu:
- Items must have canonical verb names OR appear in VerbConfig
- Duplicate entries are removed
- Separators are preserved but not counted
- Items can be overridden by Command Store entries
3. Asynchronous Icon Loading
Icons are loaded asynchronously to improve performance:
- Menu appears immediately with text
- Icons populate as they load
- Prevents blocking on slow icon providers
- Operations tracked for cancellation
4. Extensibility Architecture
The system is designed for extensibility:
- New verb configs can be added via VerbConfig array
- App extensions are discovered dynamically via AppExtension API
- Cloud providers can register as featured provider
- Command Store provides command definition separation
5. Menu ID Management
Menu IDs are carefully managed:
- Each ContextMenuPresenter instance maintains a counter
- IDs are assigned sequentially starting from base value
- Maximum ID is 0x7DFF (checked to prevent overflow)
- IDs map back to VerbInfo structures for invocation
6. Telemetry and Diagnostics
The class includes telemetry support:
CuratedContextMenu_AppInfoevents log app extension usage- Menu creation events are logged
- Error conditions are logged but don't block menu creation
- TIP (Test in Production) framework integration
7. Feature Flags
Multiple Windows feature flags control behavior:
Feature_Servicing_FEStateRepoVerbsCacheReducedLockingFeature_53343352(for ExplorerCommand info retrieval method)Feature_ContextMenuEnumVerbConfigSubCommands
These allow A/B testing and gradual rollout of changes.
8. High Complexity Functions
Some functions are exceptionally large:
CreateMenuItems: 3,637 bytes of compiled codeInsertAppExtensionsIntoMiniMenu: 2,419 bytesLoadMenuItemAsync: 22,456 bytes (coroutine state machine)FilterClassicMenu: 1,194 bytes
This suggests complex state management and error handling.
9. Exception Handling
The code uses Windows Internal Library (WIL) for exception handling:
- Exceptions are caught and logged but don't crash the menu
wil::details::in1diag3::Log_CaughtExceptionlogs errors- Failed menu items are skipped rather than failing entire menu
10. Shell Integration
Deep integration with Windows Shell:
- Uses
IShellItemArrayfor file selection context - Calls
_SHPrettyMenufor shell-standard menu styling - Integrates with shell associations system
- Supports
IContextMenuandIExplorerCommandinterfaces
13. Reference Counting and Lifetime Management
Weak Pointer Pattern:
auto weakThis = get_weak();
m_xamlFlyout.Closed([weakThis](auto sender, auto args) {
if (auto strong = weakThis.get())
strong->OnFlyoutClosed();
});Purpose:
- Prevents circular references between flyout and presenter
- Event handlers don't keep presenter alive
- Safe cleanup when menu dismissed
- Avoids memory leaks
Event Token Management:
m_flyoutClosedRevokerstores event token- Automatically unregisters on destruction
- RAII pattern ensures cleanup
- Even if exception occurs
Conclusions
The context menu system in Windows File Explorer is a multi-layered architecture that seamlessly bridges classic Win32 components with modern XAML-based UI. The analysis reveals several key design patterns:
Architectural Layers
Layer 1: Shell View (CDefView)
- Entry point from user interaction
- Validates window state and context
- Creates classic HMENU via IContextMenu::QueryContextMenu
- Decides between curated and classic menu modes
- Manages shell extension integration
Layer 2: Bridge Layer (DoCuratedContextMenu)
- Obtains IContextMenuPresenter via service query
- Marshals parameters from Win32 to presenter interface
- Handles exception recovery and fallback
- Maintains telemetry throughout the pipeline
Layer 3: Presenter Layer (ContextMenuPresenter)
- Filters and curates classic menu items
- Augments with modern app extensions
- Converts HMENU to XAML MenuFlyout
- Manages asynchronous operations
- Handles Desktop Spotlight special cases
Key Design Principles
The implementation demonstrates several sophisticated design principles:
-
Graceful Degradation
- Multiple fallback paths ensure menu always appears
- Classic menu available if curated fails
- Exception handling at every layer
- Telemetry tracks all failure points
-
Performance Optimization
- Asynchronous icon loading prevents UI blocking
- Menu appears immediately, populates progressively
- Parallel operations where possible
- CFG-protected indirect calls for security
-
Backward Compatibility
- Full support for IContextMenu, IContextMenu2, IContextMenu3
- Classic menu items preserved and filtered
- Shell extensions continue to work
- Command ID mapping maintains routing
-
Forward Extensibility
- App extension framework for modern apps
- Cloud provider verb integration
- Feature flags enable gradual rollout
- XAML-based UI allows rich experiences
-
Observability
- Comprehensive telemetry (TIP framework)
- ETW events for performance analysis
- Detailed error logging
- Feature usage tracking
Critical Functions
CDefView::_DoContextMenuPopup (3,595 bytes)
- Largest, most complex function in the pipeline
- Orchestrates entire menu creation process
- Handles Desktop Spotlight special cases
- Creates and populates classic HMENU
- Decides curated vs classic routing
ContextMenuPresenter::DoContextMenuHelper (1,656 bytes)
- Main presenter orchestration
- Implements state machine for flyout lifecycle
- Converts HMENU to XAML flyout
- Manages async operations
- Handles multiple entry points (standard + Spotlight)
TryCreateMiniMenuFromClassic (296 bytes)
- Compact but critical filtering function
- Applies verb configurations
- Integrates app extensions
- Maintains separation of concerns
Menu Item Sources
The final context menu is a curated union from multiple sources:
-
Classic Shell Extensions (IContextMenu)
- Legacy handlers registered in registry
- Filtered by canonical verb configuration
- Maintains compatibility with existing software
-
App Extensions (Windows.Internal.FileExplorerAppExtension)
- Modern UWP/AppX integrations
- Dynamically discovered via AppExtension API
- Supports async loading and icons
-
Cloud Provider Verbs
- Special handling for cloud storage providers
- Featured provider gets priority placement
- Supports grouped submenus
-
Command Store Commands (IExplorerCommand)
- Provides consistent command implementations
- Can override classic menu items
- Supports state-based visibility
-
Built-in Shell Commands
- DefView's own menu items (Open, etc.)
- Merged via Shell_MergeMenus
- Applied via _SHPrettyMenu for consistent styling
Technology Integration
COM/WinRT Bridge:
- Seamless interop between Win32 COM and WinRT
- ComPtr/winrt::com_ptr for RAII lifetime management
- CFG (Control Flow Guard) for security
- Exception translation between layers
Telemetry Systems:
- TIP (Test in Production) framework
- ETW (Event Tracing for Windows)
- Feature flag usage tracking
- Performance metrics throughout
Feature Management:
- WIL (Windows Implementation Library) feature flags
- A/B testing support via FeatureImpl
- Usage reporting for gradual rollout
- Runtime enable/disable without rebuilds
Future Extensibility
The architecture enables several future enhancements:
- Richer XAML UI: Flyout can support complex controls
- Dynamic Filtering: Context-aware verb visibility
- Cloud Integration: Enhanced cloud provider experiences
- AI/ML: Predictive verb ordering and suggestions
- Async Commands: Long-running operations with progress
This architecture allows Windows to present a curated, modern context menu experience while maintaining compatibility with decades of existing shell extensions and providing a clear path forward for modern app integration.
Appendix: Function Address Reference
| Function Name | Address | Size | Purpose |
|---|---|---|---|
| Shell View (CDefView) | |||
| DoContextMenuPopup | 0x18029f6b0 | 0x73 | Public COM interface entry point |
| _DoContextMenuPopup | 0x1802b3204 | 0xe0b | Internal implementation - HMENU creation |
| DoCuratedContextMenu | 0x18029f72c | 0x337 | Bridge to ContextMenuPresenter |
| TryGetContextMenuPresenter | 0x1800a56a0 | 0xc7 | Service query for presenter interface |
| ContextMenuPresenter Entry Points | |||
| DoContextMenuHelper | 0x1800a9f8b | 0x678 | Main orchestration function |
| ShowSpotlightContextMenu | 0x1800b21d0 | 0x329 | Spotlight-specific menu |
| Menu Creation | |||
| TryCreateMiniMenuFromClassic | 0x180031818 | 0x128 | Main entry point for mini menu creation |
| FilterClassicMenu | 0x180053b3e | 0x4aa | Filters classic menu items |
| InsertAppExtensionsIntoMiniMenu | 0x180031f7a | 0x973 | Inserts app extensions |
| InsertAppExtensionIntoMiniMenu | 0x18000e744 | 0x3a6 | Inserts single app extension |
| CreateMenuItems | 0x180011bbf | 0xe35 | Creates MenuItem collection |
| CopyMenuItem | 0x18000eaba | 0x186 | Copies menu item with icon |
| CopyClassicMenu | 0x180048e84 | 0xb1 | Copies entire classic menu |
| GetCanonicalVerbsAndIndices | 0x1800107e5 | 0x183 | Maps verbs to indices |
| TryInsertMenuItemForAppExtension | 0x18001b0a1 | 0x1ad | Inserts single extension item |
| TryCreateMenuItemFromVerbConfig | 0x18005fc7e | 0x360 | Creates item from verb config |
| Async Operations | |||
| LoadMenuItemAsync | 0x180059fea | 0x57b8 | Async menu item loading |
| LoadGroupMenuItemForAppAsync | 0x180055eb6 | 0x3504 | Async group item loading |
| Support Functions | |||
| GetCommandStoreCommand | 0x1800162e1 | 0xe5 | Gets command from store |
| AddCloudProviderVerbs | 0x1800152f9 | 0x681 | Adds cloud provider verbs |
| UpdateXamlMenuItem | 0x180012e0d | 0x5a5 | Updates XAML menu item |
Complete Flow Summary
Full Call Chain
User Right-Clicks on Item
│
↓
CDefView::DoContextMenuPopup (0x18029f6b0)
│ - Validate window is visible
│ - Return E_UNEXPECTED if not ready
↓
CDefView::_DoContextMenuPopup (0x1802b3204)
│ - Initialize telemetry (ContextMenuCreationTest)
│ - Check Desktop Spotlight feature flags
│ - Decide: Curated vs Classic menu mode
│ - CreatePopupMenu() → hMenu
│ - IContextMenu::QueryContextMenu(hMenu, 0, 30977, 31487, flags)
│ └─► Shell extensions populate the classic menu
│ - Merge additional items (Open menu, etc.)
│ - Apply dark mode settings
│ - SetContextMenuCapability()
↓
┌─────────────────────────────────────────────────────────────┐
│ CDefView::DoCuratedContextMenu (0x18029f72c) │
│ [If curated mode selected] │
│ │
│ ├─► TryGetContextMenuPresenter (0x1800a56a0) │
│ │ └─► IUnknown_QueryService(SID_ContextMenuPresenter) │
│ │ → IContextMenuPresenter interface │
│ │ │
│ ├─► GetLocationContext(shellItems) → locationContext │
│ │ │
│ ├─► Set presenter flags: │
│ │ ├─► If shift key: flags |= 0x8 │
│ │ ├─► If screen reader: flags |= 0x1 │
│ │ └─► If Spotlight+Desktop: flags |= 0x10 │
│ │ │
│ └─► Call IContextMenuPresenter::DoContextMenu │
│ └─► vtable[0x20](locationContext, this, pt, ...) │
│ - Parameters: hMenu, flags, testId, pContextMenu │
│ - CFG-protected indirect call │
└─────────────────────────────────────────────────────────────┘
│
↓
┌───────────────────────────────────────────────────┐
│ DoContextMenuHelper (0x1800a9f8b) │
│ │
│ ┌────────────────────────────────────────────┐ │
│ │ 1. Initialize Telemetry & State │ │
│ └────────────────────────────────────────────┘ │
│ ↓ │
│ ┌────────────────────────────────────────────┐ │
│ │ 2. Check Desktop Spotlight Feature │ │
│ │ ├─► If Enabled + Spotlight Context │ │
│ │ │ └─► ShowSpotlightContextMenu │ │
│ │ │ └─► RETURN (separate flow) │ │
│ │ └─► Else: Continue │ │
│ └────────────────────────────────────────────┘ │
│ ↓ │
│ ┌────────────────────────────────────────────┐ │
│ │ 3. TryCreateMiniMenuFromClassic │◄──┼─┐
│ │ (0x180031818) │ │ │
│ └────────────────────────────────────────────┘ │ │
│ ↓ │ │
│ ┌────────────────────────────────────────────┐ │ │
│ │ 4. If menu has items: │ │ │
│ │ └─► CreateMenuItems (HMENU→XAML) │ │ │
│ │ └─► Create XAML Flyout │ │ │
│ │ └─► Register Event Handlers │ │ │
│ │ └─► ShowXamlFlyout │ │ │
│ └────────────────────────────────────────────┘ │ │
└───────────────────────────────────────────────────┘ │
│
Detailed Breakdown of Step 3 ─────────────────────┘
│
↓
┌────────────────────────────────────────────────────────┐
│ TryCreateMiniMenuFromClassic (0x180031818) │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ A. FilterClassicMenu (0x180053b3e) │ │
│ │ ├─► Create new popup menu │ │
│ │ ├─► GetCanonicalVerbsAndIndices │ │
│ │ ├─► For each MenuItem in collection: │ │
│ │ │ ├─► Check if canonical verb exists │ │
│ │ │ ├─► GetMenuItemInfo from classic menu │ │
│ │ │ ├─► Check VerbConfig overrides │ │
│ │ │ └─► CopyMenuItem to new menu │ │
│ │ └─► Return filtered menu │ │
│ └─────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ B. Check/Copy Default Menu Item │ │
│ │ ├─► GetMenuDefaultItem from source │ │
│ │ ├─► GetMenuDefaultItem from filtered │ │
│ │ └─► Copy if source has one but filtered doesn't│ │
│ └─────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ C. InsertAppExtensionsIntoMiniMenu (0x180031f7a)│ │
│ │ ├─► GetAssociationElements │ │
│ │ ├─► GetProgIdsFromAssociationElements │ │
│ │ ├─► ShouldAddCloudProviderVerbs? │ │
│ │ │ └─► AddCloudProviderVerbs │ │
│ │ ├─► For each ProgID: │ │
│ │ │ ├─► GetExtensions (FileExplorerAppExt) │ │
│ │ │ ├─► Build AppExtensionElement map │ │
│ │ │ └─► GetVerbsForApp │ │
│ │ ├─► Process featured cloud provider │ │
│ │ │ └─► InsertAppExtensionIntoMiniMenu │ │
│ │ └─► For remaining extensions: │ │
│ │ └─► InsertAppExtensionIntoMiniMenu │ │
│ │ ├─► If 1 verb: Insert directly │ │
│ │ └─► If multiple: Create submenu │ │
│ │ └─► LoadGroupMenuItemForAppAsync │ │
│ └─────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ D. Insert Separator │ │
│ └─────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ E. _SHPrettyMenu (Apply shell styling) │ │
│ └─────────────────────────────────────────────────┘ │
│ ↓ │
│ [Return menu] │
└────────────────────────────────────────────────────────┘
│
↓
Back to DoContextMenuHelper
│
↓
┌────────────────────────────────────────────────────────┐
│ CreateMenuItems (0x180011bbf) │
│ - Converts HMENU to IIterable<MenuItem> │
│ - Creates XAML MenuItem objects │
│ - Preserves structure (submenus, separators) │
│ - Registers event handlers for each item │
└────────────────────────────────────────────────────────┘
│
↓
┌────────────────────────────────────────────────────────┐
│ ContextMenuHost::ShowXamlFlyout │
│ - Positions flyout at cursor │
│ - Manages XAML island lifecycle │
│ - Handles input routing │
│ - Theme/DPI awareness │
└────────────────────────────────────────────────────────┘
│
↓
Menu Displayed to User
Data Flow
Classic IContextMenu
│
├─► Shell Extensions populate HMENU
│ └─► Classic menu items (Win32)
│
↓
FilterClassicMenu
│
├─► Canonical verb filtering
├─► VerbConfig matching
└─► Selected items copied
│
↓
Filtered HMENU
│
↓
InsertAppExtensionsIntoMiniMenu
│
├─► File associations → ProgIDs
├─► ProgIDs → App Extensions
├─► App Extensions → Verb Elements
└─► Verb Elements → Menu Items
│
↓
Augmented HMENU (Curated Menu)
│
↓
CreateMenuItems
│
├─► HMENU → MenuItem collection
├─► Text, icons, state preserved
└─► Event handlers attached
│
↓
IIterable<MenuItem>
│
↓
CreateMicrosoftUIXamlControlsPrimitivesMenu
│
└─► CommandBarFlyout (XAML)
│
↓
ShowXamlFlyout
│
└─► Displayed to user
Timing and Synchronization
Synchronous Operations:
- DoContextMenuHelper initialization
- TryCreateMiniMenuFromClassic
- FilterClassicMenu
- GetCanonicalVerbsAndIndices
- Menu handle operations (Win32 APIs)
Asynchronous Operations:
- LoadMenuItemAsync (for app extension icons)
- LoadGroupMenuItemForAppAsync (for grouped items)
- SetHMenuItemIconAsync (for icon loading)
Critical Timing:
T=0ms : User right-click
T=0-20ms : DoContextMenuHelper starts, telemetry init
T=20-50ms: TryCreateMiniMenuFromClassic filters menu
T=50-100ms: InsertAppExtensionsIntoMiniMenu queries extensions
T=100-150ms: CreateMenuItems converts to XAML
T=150-200ms: ShowXamlFlyout displays menu
T=200ms+ : Async icon loads complete (menu already visible)
Icons Load Asynchronously:
- Menu appears with text immediately
- Icons populate as they become available
- No blocking on slow icon providers
- Background threads handle bitmap decoding
CDefView::_CreateSelectionContextMenu
Address: 0x1801e1a3c
Size: 0xa4 bytes (164 bytes)
Purpose: Wrapper function for querying context menus for selected items
This is a compact wrapper function that bridges the legacy Win32 IContextMenu mechanism with the modern curated menu system for selected items specifically.
Function Signature
HRESULT CDefView::_CreateSelectionContextMenu(
CDefView* this,
int flags, // 0 or 0x80000001
const GUID* riid, // Interface ID (IID_IContextMenu)
void** ppv // [out] Returned interface
)Location in Call Hierarchy
This function is called from multiple contexts:
User Right-Click on Selected Items
│
├─► GetItemObject(this, 1, IID_IContextMenu, &ppv)
│ └─► _CreateSelectionContextMenu(this, 0, IID_IContextMenu, &ppv)
│
├─► DoSelectionContextMenu(pt, mode)
│ └─► _CreateSelectionContextMenu(this, 0, IID_IContextMenu_0, &pContextMenu)
│ │
│ ├─► SetWaitCursor()
│ ├─► _DoContextMenuPopup(pContextMenu, 0x90, pt, mode)
│ │ └─► [Displays menu with classic or curated path]
│ └─► RestoreCursor()
│
└─► _OnInitMenuPopup / _ExplorerCommand
└─► [Other selection-based operations]
Decompiled Implementation
HRESULT __fastcall CDefView::_CreateSelectionContextMenu(
CDefView *this,
int a2, // 0 or 0x80000001
const struct _GUID *a3, // IID
void **ppv)
{
int Items;
struct IContextMenu *pContextMenu;
*ppv = 0; // Clear output
pContextMenu = 0;
// a2 determines which items to get:
// If a2 != 0: use 0x80000001 (SVGIO_FLAG_VIEWORDER)
// If a2 == 0: use 1 (SVGIO_SELECTION)
Items = CDefView::_GetItems(
this,
a2 != 0 ? 0x80000001 : 1, // Selection type
1, // Get as single object
&IID_IContextMenu_0, // Request IContextMenu interface
&pContextMenu // [out] Menu object
);
if (Items >= 0) // Success
{
// Try to modify the context menu
// (e.g., add shell folder view extensions)
Items = CDefView::_TryModifyMenu(
this,
pContextMenu,
0, // a3: unused
a3, // Request interface (IID_IContextMenu)
ppv // [out] Result
);
// Release the context menu object
(pContextMenu->lpVtbl->Release)(pContextMenu);
}
return Items; // Return HRESULT
}Assembly Analysis
The assembly reveals important parameter manipulation:
; Parameter handling
1801e1a69 neg edx ; Negate a2 (flags parameter)
1801e1a6b sbb edx, edx ; SBB: if borrow, edx = -1, else 0
1801e1a7a and edx, 80000000h ; Mask to 0x80000000
1801e1a87 add edx, r8d ; Add 1 (r8d contains 1)This implements the conditional logic:
- If
a2 == 0: EDX becomes0 + 1 = 1(SVGIO_SELECTION) - If
a2 != 0: EDX becomes0x80000000 + 1 = 0x80000001(SVGIO_FLAG_VIEWORDER)
Parameter Meanings:
| Parameter | Value | Meaning |
|---|---|---|
SVGIO_SELECTION | 1 | Get currently selected items |
SVGIO_FLAG_VIEWORDER | 0x80000000 | Selection in view order (not used here with OR) |
SVGIO_ALLASSTRING | 0x80000001 | Combined: get selection, return as string |
Called Functions
CDefView::_GetItems (Core Selection Logic)
HRESULT CDefView::_GetItems(
unsigned int svgio, // Selection type
int fMultiple, // 1 = single object
const GUID& riid, // Interface requested
void** ppv) // [out] ResultBehavior:
svgio = 1: Gets currently selected items in the folder viewsvgio = 0x80000001: Gets items in view order (used for certain contexts)- Returns as single object: Aggregates items into one IContextMenu if multiple selected
- If only 1 item selected: Direct IContextMenu from that item
- If multiple items selected: Composite menu from all selections
Result:
- Returns
IContextMenuinterface for the selected item(s) - This menu is not yet populated - it's just the interface
- Menu population happens later via
IContextMenu::QueryContextMenu
CDefView::_TryModifyMenu (Extension Support)
HRESULT CDefView::_TryModifyMenu(
IContextMenu* pContextMenu,
int a3, // Unused (0)
const GUID* riid, // Requested interface
void** ppv) // [out] ResultPurpose: Apply shell folder view context menu extensions via IShellFolderViewCM
Algorithm:
HRESULT hr = IUnknown_QueryService(
m_pShellBrowser,
&SID_SShellFolderViewCM, // Service ID
&GUID_SShellFolderViewCM, // Interface ID
&pFolderViewCM // [out] Extension interface
);
if (FAILED(hr))
// Fall back to direct QueryInterface
return pContextMenu->QueryInterface(riid, ppv);
// Get single item for context
IDataObject* pDataObj = NULL;
if (a3 == 0) // Selection context (not root)
{
CDefView::GetItemObject(
this,
1, // Single item
&IID_IDataObject, // Request data object
&pDataObj // [out] Data object
);
}
// Initialize the extension
IShellExtInit_Initialize(
pFolderViewCM,
m_pidlRoot, // Current folder pidl
pDataObj, // Selected item data
NULL // HKEY reserved
);
// Get modified menu
hr = pFolderViewCM->GetContextMenu(
pContextMenu, // Original menu
&pModifiedMenu // [out] Modified menu
);
if (SUCCEEDED(hr))
{
// Query for requested interface
return pModifiedMenu->QueryInterface(riid, ppv);
}Extension Interfaces:
IID_SShellFolderViewCM: Service ID for folder view context menu extensionsGUID_SShellFolderViewCM: Interface GUID- Extensions can modify menu items before display
Call Flow in DoSelectionContextMenu
This is where _CreateSelectionContextMenu is primarily used:
HRESULT CDefView::DoSelectionContextMenu(
POINT* ppt,
FileExplorerContextMenu::Mode mode)
{
// Auto wait cursor during menu creation
CAutoWaitCursor waitCursor;
// Get context menu for selected items
IContextMenu* pContextMenu = NULL;
CDefView::_CreateSelectionContextMenu(
this,
0, // Get selected items
&IID_IContextMenu_0,
&pContextMenu
);
if (pContextMenu)
{
// Show the context menu
// This will either use:
// - Curated mode: DoCuratedContextMenu() → XAML flyout
// - Classic mode: DoClassicMenu() → TrackPopupMenu()
HRESULT hr = CDefView::_DoContextMenuPopup(
pContextMenu,
0x90, // Flags: DROPEFFECT_COPY | other
*ppt, // Cursor position
mode // 0 = auto, 1 = force classic
);
// Release menu object
pContextMenu->Release();
return hr;
}
return E_FAIL;
}Key Observations:
- Wait Cursor: Shows busy cursor while getting items
- Single Selection vs Multiple: Function handles both transparently
- Mode Selection: Caller decides if showing curated or classic menu
- Flag 0x90:
DROPEFFECT_COPY | 0x10indicates selection context - Exception Safety: RAII cursor cleanup
Parameter Decoding
The cryptic a2 parameter uses sign extension and masking:
// Visual C++ compiler optimization for conditional:
if (a2 != 0)
flags = 0x80000001; // SVGIO_FLAG_VIEWORDER
else
flags = 1; // SVGIO_SELECTION
// Compiled as:
neg edx // EDX = -flags (two's complement)
sbb edx, edx // If borrow (sign bit set): EDX = -1, else EDX = 0
and edx, 0x80000000 // Mask: EDX = 0 or 0x80000000
add edx, 1 // Add 1: result is 1 or 0x80000001Error Handling
The function handles failures gracefully:
// _GetItems returned error
if (Items < 0)
return Items; // Propagate error
// _TryModifyMenu handled error internally
// (returns default if extension unavailable)Possible return values:
S_OK(0): Success - menu createdE_NOINTERFACE: Interface not availableE_FAIL: General failure- Other COM errors from shell extension host
Security Considerations
CFG (Control Flow Guard) Protection:
- Vtable calls through
pContextMenu->lpVtbl->Releaseare CFG-protected - Ensures only legitimate virtual functions called
- Prevents return-oriented programming attacks
Data Validation:
- Input pointers validated implicitly by COM
- Output pointer (
ppv) cleared before use - Exception safety via RAII cleanup
Usage Patterns
Pattern 1: Simple Menu Query
void* pMenu;
_CreateSelectionContextMenu(this, 0, &IID_IContextMenu, &pMenu);
// Use pMenu...
((IContextMenu*)pMenu)->Release();Pattern 2: Bulk Operations
// Get menu for all selected items
_CreateSelectionContextMenu(this, 0x80000001, &IID_IContextMenu, &pMenu);
// Perform operation on all items simultaneouslyPattern 3: Interface Discovery
void* pInterface;
GUID riid = { /* some interface */ };
_CreateSelectionContextMenu(this, 0, &riid, &pInterface);
// pInterface points to requested COM interface for selectionIntegration with Curated Menu System
The flow when selecting items:
1. User right-clicks → OnContextMenu event
2. CDefView detects SVGIO_SELECTION context
3. Calls _CreateSelectionContextMenu()
4. Gets IContextMenu for selected item(s)
5. Passes to DoSelectionContextMenu()
6. Checks if curated menu should be shown
7. DoCuratedContextMenu() → ContextMenuPresenter::DoContextMenu()
8. TryCreateMiniMenuFromClassic() filters items
9. InsertAppExtensionsIntoMiniMenu() adds modern verbs
10. XAML flyout created and displayed
Integration Points:
- Provides classic menu for curating
- Maintains backward compatibility with IContextMenu
- Supports shell extensions via IShellFolderViewCM
- Telemetry tracking via telemetry test framework
IContextMenu Creation Based on Object Type
Overview
When users interact with the file system, DefView needs to determine which IContextMenu to create based on the context type: is the user clicking on a selected item, the background (empty space), or something else? This section analyzes the type-based routing system that dispatches menu creation.
GetItemObject - Main Type Dispatcher
Address: 0x180100120
Size: 0x30f bytes (783 bytes)
Purpose: Central dispatcher that routes IContextMenu creation based on object type
This is the primary routing function that determines which specific IContextMenu creation path to use. It examines the context type and delegates to the appropriate handler.
Function Signature
HRESULT __fastcall CDefView::GetItemObject(
CDefView *this,
int a2, // Context type (encoded in low bits)
const struct _GUID *a3, // Requested interface ID
void **a4) // [out] Returned interface pointerParameter Encoding: Context Type Bits
The a2 parameter encodes the context type in its low 4 bits:
| Value (a2 & 0xF) | Meaning | Context | Routing |
|---|---|---|---|
0x0 | Background | Empty space / desktop background | _CBackgrndMenu_CreateInstance |
0x1 | Selection | One or more selected items | _CreateSelectionContextMenu |
0x2 | Multiple Items | Batch operation on items | _CreateSelectionContextMenu |
| Other | (reserved) | Not currently used | Fallback handling |
Detection Pattern (from assembly):
mov eax, a2 ; eax = context parameter
and eax, 0xF ; eax = eax & 0xF (extract low 4 bits)
cmp eax, 1 ; Compare with 0x1 (selection)
jne .background_context ; If not equal, handle as backgroundDecision Tree
GetItemObject(a2, riid, &ppv)
│
├─► Extract context type: a2 & 0xF
│
├─► If (a2 & 0xF) == 0x0: BACKGROUND CONTEXT
│ │
│ └─► GUID Comparison Chain
│ ├─► If riid == IID_IContextMenu
│ │ └─► _CBackgrndMenu_CreateInstance(...)
│ │
│ ├─► Else if riid == IID_IContextMenu2
│ │ └─► _CBackgrndMenu_CreateInstance(...)
│ │ └─► QueryInterface → IContextMenu2
│ │
│ ├─► Else if riid == IID_IContextMenu3
│ │ └─► _CBackgrndMenu_CreateInstance(...)
│ │ └─► QueryInterface → IContextMenu3
│ │
│ ├─► Else if riid == IID_IDispatch
│ │ └─► Create automation object
│ │ └─► Return IDispatch interface
│ │
│ ├─► Else if riid == IID_IPersistHistory
│ │ └─► Create history object
│ │ └─► Return IPersistHistory interface
│ │
│ └─► Else
│ └─► Return E_NOINTERFACE
│
└─► Else if (a2 & 0xF) == 0x1: SELECTION CONTEXT
│
└─► _CreateSelectionContextMenu(...)
└─► Handles IContextMenu* versions
└─► May also handle IDispatch/other interfaces
Decompiled Implementation
The decompiled code shows the complete routing logic:
HRESULT __fastcall CDefView::GetItemObject(
CDefView *this,
int a2,
const struct _GUID *a3,
void **a4)
{
const GUID *v4;
const GUID *v5;
int v6;
__int64 v7;
__int64 v8;
__int64 v9;
__int64 v10;
__int64 v11;
__int64 v12;
__int64 v13;
const GUID *v14;
const GUID *riid_a3;
*a4 = 0; // Clear output pointer
// ***CRITICAL: Check context type***
if ((a2 & 0xF) != 0) // Selection or other context
{
// ***SELECTION CONTEXT ROUTING***
// Delegate to selection-specific handler
return CDefView::_CreateSelectionContextMenu(
this,
0, // flags
a3, // Requested interface
a4); // [out] Result
}
// ***BACKGROUND CONTEXT ROUTING***
// User clicked on empty space / background
// Prepare for type checking below
v4 = a3;
v5 = a3; // v5 = requested interface GUID
v6 = 0;
// ***IID_IContextMenu CHECK***
if (QISearch_0(&IID_IContextMenu, a3) == 0)
{
// Requested interface is IContextMenu
return _CBackgrndMenu_CreateInstance(
this,
0, // flags
a3,
a4); // [out] ppv
}
// ***IID_IContextMenu2 CHECK***
if (QISearch_0(&IID_IContextMenu2, a3) == 0)
{
// Requested interface is IContextMenu2
return _CBackgrndMenu_CreateInstance(
this,
0,
a3,
a4);
}
// ***IID_IContextMenu3 CHECK***
if (QISearch_0(&IID_IContextMenu3, a3) == 0)
{
// Requested interface is IContextMenu3
return _CBackgrndMenu_CreateInstance(
this,
0,
a3,
a4);
}
// ***IID_IDispatch CHECK*** (for automation)
if (QISearch_0(&IID_IDispatch, a3) == 0)
{
// Create automation dispatch wrapper
// This allows scripts to interact with the context
return SHCoCreateInstance(
NULL,
&g_ClassID_Automation,
NULL,
a3,
a4);
}
// ***IID_IPersistHistory CHECK*** (navigation history)
if (QISearch_0(&IID_IPersistHistory, a3) == 0)
{
// Return the DefView itself as history object
return CDefView_QueryInterface(
this + 130, // IPersistHistory offset in vtable
a3,
a4);
}
// ***DEFAULT: Unsupported interface***
return E_NOINTERFACE; // 0x80004002
}Key Assembly Insights
The low-bit extraction for context detection:
18010012d mov r8d, edx ; r8d = a2 (context parameter)
18010133 and r8d, 0xF ; r8d = a2 & 0xF (extract type)
1801013d cmp r8d, 1 ; Compare with 0x1
1801014e jne .background ; If != 0x1, process background
18010150 call _CreateSelectionContextMenu ; Selection path
180101c5 .background:
180101ca call _CBackgrndMenu_CreateInstance ; Background pathThis demonstrates:
- Efficient bit extraction using
and r8d, 0xF - Quick comparison with
cmp r8d, 1 - Conditional branching for two paths
- Only two main context types are actively processed
Type Checks Via QISearch
The function uses QISearch_0 for GUID comparisons:
// Pseudocode for QISearch_0
int QISearch_0(const GUID* guid1, const GUID* guid2)
{
// Compare 128-bit GUIDs byte-by-byte
// Returns 0 if equal, non-zero if different
if (*guid1 == *guid2)
return 0; // Match
else
return 1; // No match
}Comparison Order:
- IContextMenu (most common)
- IContextMenu2 (has callbacks)
- IContextMenu3 (extended version)
- IDispatch (for automation)
- IPersistHistory (for navigation)
This ordering prioritizes frequently-requested interfaces for performance.
Background Context: _CBackgrndMenu_CreateInstance
Address: 0x180100438
Size: 0x190 bytes (400 bytes)
Purpose: Creates IContextMenu for background (empty space) right-clicks
When the user clicks on empty space in the folder view, this function orchestrates the creation of a context menu specific to the background area.
Function Signature
HRESULT __fastcall CDefView::_CBackgrndMenu_CreateInstance(
CDefView *this,
int a2, // flags (unused: 0)
const struct _GUID *a3, // Requested interface (IID_IContextMenu*)
void **a4) // [out] Returned interfaceImplementation
HRESULT __fastcall CDefView::_CBackgrndMenu_CreateInstance(
CDefView *this,
int a2,
const struct _GUID *a3,
void **a4)
{
HMENU hBackgroundHMENU;
IBindCtx *pBindCtx;
IContextMenu *pFolderContextMenu;
void *pContextMenuArray;
int v8;
*a4 = 0; // Clear output
// ***STEP 1: CREATE BACKGROUND HMENU***
hBackgroundHMENU = CDefView::_Create_BackgrndHMENU(
this,
0, // uPosition
a3, // a3 (interface request, unused)
&pBindCtx); // [out] Bind context
if (!hBackgroundHMENU)
return E_OUTOFMEMORY; // 0x8007000E
// ***STEP 2: GET PARENT FOLDER'S ICONTEXTMENU***
// The parent folder (e.g., C:\Users) also has a context menu
// We need to merge the background menu with the parent's menu
// Get parent folder IContextMenu from cached member (offset +87)
pFolderContextMenu = *(IContextMenu**)(this + 87); // Member at offset 87
// ***STEP 3: CREATE MENU WRAPPER***
// CContextMenuOnContextMenuArray merges two context menus
pContextMenuArray = operator_new(0xD8); // Allocate wrapper object
if (!pContextMenuArray)
{
DestroyMenu(hBackgroundHMENU);
return E_OUTOFMEMORY;
}
// ***STEP 4: INITIALIZE MERGER WITH TWO MENUS***
// Array pattern: [BackgroundHMENU, ParentFolderMenu]
CContextMenuOnContextMenuArray::CContextMenuOnContextMenuArray(
pContextMenuArray,
2, // Number of menus to merge
hBackgroundHMENU, // First: Background HMENU
pFolderContextMenu); // Second: Parent folder menu
// ***STEP 5: APPLY FOLDER VIEW EXTENSIONS***
// Allow IShellFolderViewCM to modify the menu
CDefView::_TryModifyMenu(
this,
pContextMenuArray, // The merged menu
1, // a3 = 1 (background context flag)
a3, // Requested interface
a4); // [out] Result
// If _TryModifyMenu succeeded, ppv is filled
// Otherwise, fall through to return error
return v8; // Return from _TryModifyMenu
}Background HMENU Creation: _Create_BackgrndHMENU
Address: 0x180100754
Size: 0x3b9 bytes (953 bytes)
Purpose: Creates the HMENU that represents the background context menu
This function creates the actual Win32 HMENU that represents all possible operations available when right-clicking the background (empty space) in a folder view.
Implementation Overview
HRESULT __fastcall CDefView::_Create_BackgrndHMENU(
CDefView *this,
UINT uPosition, // Submenu extraction position (usually 0)
const struct _GUID *a3, // Unused parameter
void **a4) // [out] Bind context
{
HMENU hMenu;
int result;
*a4 = 0; // Clear output
// ***STEP 1: LOAD MENU TEMPLATE FROM RESOURCES***
// Resource ID 0xD7 = POPUP_DDM_BACKGROUNDPOPUP
hMenu = SHLoadPopupMenu(g_hinst, 0xD7);
if (!hMenu)
return E_OUTOFMEMORY;
// ***STEP 2: INITIALIZE EDIT COMMANDS***
// Add Cut/Copy/Paste/Delete commands
// Parameters:
// - 0: Start from beginning
// - hMenu: Target menu handle
// - 28672 (0x7000): Command ID base for standard commands
// - this+97: Clipboard owner
// - 1: Enable edit operations
// - this+77: Data source
Def_InitEditCommands(0, hMenu, 28672, *(this + 97), 1u, *(this + 77));
// ***STEP 3: CONDITIONAL VIEW MENU ITEMS***
// this+216 contains view mode flags (0x10000000 = some mode bit)
if ((*(this + 216) & 0x10000000) != 0)
{
// ***MODERN VIEW MODE***
// Delete obsolete menu item
DeleteMenu(hMenu, 0x7033u, 0);
// Configure View submenu
MENUITEMINFOW mii = {0};
mii.cbSize = 80;
mii.fMask = 4; // MIIM_SUBMENU
GetMenuItemInfoW(hMenu, 0x7002u, 0, &mii); // Get submenu
HMENU hSubMenu = mii.hSubMenu;
if (hSubMenu)
{
// Remove obsolete view modes
DeleteMenu(hSubMenu, 0x704Bu, 0); // List view obsolete
DeleteMenu(hSubMenu, 0x704Cu, 0); // Details view obsolete
DeleteMenu(hSubMenu, 0x704Du, 0); // Small icons obsolete
DeleteMenu(hSubMenu, 0x7051u, 0); // Tile view
DeleteMenu(hSubMenu, 0x7052u, 0); // Extra large icons
// Check current view mode
CDefView::_CheckCurrentViewMenuItem(this, hSubMenu);
// Load resource string for current sort order
wchar_t* pSortText = NULL;
TResourceStringAllocCopyEx<unsigned short*>(
g_hinst,
0x79B5u, // Resource string ID
v9,
ResourceStringAllocCopyExLocalAlloc,
lpNewItem,
&pSortText);
// Update sort menu item
if (pSortText)
{
ModifyMenuW(hSubMenu, 0x7071u, 0, 0x7071u, pSortText);
LocalFree(pSortText);
}
// Similar for other string resources
// ...and check for classic shell restrictions
if (!SHRestricted(REST_CLASSICSHELL))
{
// Load and merge classic shell menu
HMENU hClassicMenu = SHLoadMenuPopup(g_hinst, 218);
if (hClassicMenu)
{
// Get shell settings (e.g., show hidden files)
SHELLSTATE shellState = {0};
SHGetSetSettings(&shellState, 0x4000u, 0);
// Update menu based on settings
if ((shellState & 0x1000) == 0)
CheckMenuItem(hClassicMenu, 0x7402u, 8u);
// Merge classic menu items
Shell_MergeMenus(
hSubMenu, // Target menu
hClassicMenu, // Source menu
0xFFFFFFFF, // Insert position (-1 = end)
0, // Start ID
0xFFFFFFFF, // End ID
3); // Merge flags
DestroyMenu(hClassicMenu);
}
}
}
}
else
{
// ***CLASSIC VIEW MODE***
DeleteMenu(hMenu, 0x7033u, 0); // Delete modern-only item
// Apply lambda-based extensions
// [Details in actual implementation]
}
// ***STEP 4: INITIALIZE VIEW MENU***
CDefView::_InitViewMenu(this, hMenu);
// ***STEP 5: EXTRACT SUBMENU IF REQUESTED***
if (uPosition) // Non-zero position specified
{
// Get submenu at the specified position
MENUITEMINFOW mii = {0};
mii.cbSize = 80;
mii.fMask = 4; // MIIM_SUBMENU
GetMenuItemInfoW(hMenu, uPosition, 0, &mii);
HMENU hSubMenu = mii.hSubMenu;
RemoveMenu(hMenu, uPosition, 0);
DestroyMenu(hMenu);
hMenu = hSubMenu; // Return submenu instead
}
// ***STEP 6: WRAP IN CDVBackgroundHMENU***
// Create special wrapper that tracks menu item state
return Create_CDVBackgroundHMENU(
hMenu, // The HMENU to wrap
*(this + 87), // Parent folder context
this, // DefView context
a3, // Requested interface (unused)
a4); // [out] Wrapped menu
}Menu Resource Template (0xD7)
The background menu is loaded from resource ID 0xD7:
BACKGROUND_MENU (ID: 0xD7)
├─ Edit Menu (0x7002)
│ ├─ Undo (0x7001)
│ ├─ Cut (0x7003)
│ ├─ Copy (0x7004)
│ ├─ Paste (0x7005)
│ ├─ Separator
│ └─ Delete (0x7006)
│
├─ View Menu (0x7020)
│ ├─ View Modes
│ │ ├─ List (0x704B)
│ │ ├─ Details (0x704C)
│ │ ├─ Icons (0x704D)
│ │ └─ ...
│ ├─ Separator
│ ├─ Sort By
│ │ ├─ Name (0x7071)
│ │ ├─ Date (0x7074)
│ │ └─ ...
│ └─ Group By
│ └─ ...
│
├─ Refresh (0x7030)
├─ Separator
├─ New (0x7033) [Modern only]
├─ Personalize (0x7024) [Modern only]
├─ Separator
├─ Undo/Redo (0x7001)
└─ Properties (0x7025)
Data Structure: CContextMenuOnContextMenuArray
This object merges two context menus:
struct CContextMenuOnContextMenuArray
{
// VTable pointer (IContextMenu interface)
void** lpVtbl; // Offset 0x0
// Reference count
LONG refCount; // Offset 0x8
// Array of IContextMenu pointers
IContextMenu** ppContextMenus; // Offset 0x10
// Number of menus in array
ULONG cMenus; // Offset 0x18
// Cached state for each menu
struct {
UINT uFlags; // Flags from QueryContextMenu
UINT idCmdFirst; // First command ID
UINT idCmdLast; // Last command ID
} menuState[2]; // Space for 2 menus
// Total size: 0xD8 bytes (216 bytes)
};How it works:
// When IContextMenu::QueryContextMenu is called on the merger:
// 1. Call QueryContextMenu on first menu (BackgroundHMENU)
// 2. Call QueryContextMenu on second menu (Parent folder menu)
// 3. Merge the resulting HMENUs via Shell_MergeMenus
// 4. Return merged HMENU to caller
// When a command is invoked:
// 1. Check which menu owns the command ID
// 2. Route to that menu's IContextMenu::InvokeCommandFlow Diagram: Background Menu Creation
User Right-Clicks Empty Space
│
├─► CDefView::_DoContextMenuPopup(...)
│ │
│ └─► GetItemObject(this, a2=0x0, IID_IContextMenu, &ppv)
│ │
│ ├─► Check: (a2 & 0xF) == 0 ? YES
│ │
│ └─► Route to _CBackgrndMenu_CreateInstance
│
├─► _CBackgrndMenu_CreateInstance (0x180100438)
│ │
│ ├─► Step 1: _Create_BackgrndHMENU
│ │ │
│ │ ├─► SHLoadPopupMenu(g_hinst, 0xD7)
│ │ │ └─► Load background menu template from resources
│ │ │
│ │ ├─► Def_InitEditCommands
│ │ │ └─► Add Cut/Copy/Paste/Delete
│ │ │
│ │ ├─► Check view mode: (*(this+216) & 0x10000000)
│ │ │ ├─► If modern: Delete old mode items, check current
│ │ │ │ └─► May load classic shell menu
│ │ │ │ └─► Shell_MergeMenus (merge into View submenu)
│ │ │ │
│ │ │ └─► If classic: Apply lambda extensions
│ │ │
│ │ ├─► _InitViewMenu
│ │ │ └─► Initialize view-related menu items
│ │ │
│ │ ├─► Create_CDVBackgroundHMENU (wrapper)
│ │ │ └─► Wraps HMENU with state tracking
│ │ │
│ │ └─► Return wrapped menu
│ │
│ ├─► Step 2: Get parent folder's IContextMenu
│ │ └─► *(this + 87) → Cached parent menu
│ │
│ ├─► Step 3: Create CContextMenuOnContextMenuArray
│ │ ├─► Allocate 0xD8 bytes
│ │ └─► Initialize with [BackgroundHMENU, ParentMenu]
│ │
│ ├─► Step 4: _TryModifyMenu (extend via IShellFolderViewCM)
│ │ ├─► Query IShellFolderViewCM service
│ │ ├─► Allow extensions to modify menu
│ │ └─► Return modified IContextMenu
│ │
│ └─► Return merged IContextMenu interface
│
└─► Returned to _DoContextMenuPopup for display
├─► DoCuratedContextMenu → XAML flyout
└─► Or DoClassicMenu → TrackPopupMenu
Selection Context Routing
When the user clicks on a selected item instead of empty space:
if ((a2 & 0xF) == 0x1) // Selection context
{
return CDefView::_CreateSelectionContextMenu(
this,
0,
a3, // Requested interface
a4); // [out] Result
}_CreateSelectionContextMenu (analyzed in previous section):
- Gets the selected item(s)
- Queries their IContextMenu
- Applies shell folder view extensions
- Returns the configured menu
Key Differences: Selection vs Background
| Aspect | Selection Context | Background Context |
|---|---|---|
| Entry Function | _CreateSelectionContextMenu | _CBackgrndMenu_CreateInstance |
| Context Type Bit | a2 & 0xF == 0x1 | a2 & 0xF == 0x0 |
| HMENU Source | From selected item's IContextMenu | From resource template (0xD7) |
| Menu Items | Item-specific operations | View-level operations |
| Parent Menu | Not merged | Merged with parent folder |
| Typical Items | Open, Edit, Delete, Properties | Cut, Copy, Paste, View, Refresh |
| Shell Extensions | Via IContextMenu | Via IShellFolderViewCM |
| Automation Support | IDispatch if item supports it | IDispatch wrapper |
Memory and Performance
Stack Usage:
// Local variables in GetItemObject
const GUID* v4, v5;
int v6;
__int64 v7-v13; // ~110 bytes on stackAllocation Patterns:
CContextMenuOnContextMenuArray: Heap allocation (0xD8 bytes)- HMENU handles: Windows Manager resources (not heap)
- Temporary strings: Stack or local allocation
Performance Characteristics:
- Fast path (background): ~5-10ms (resource load + SetUp)
- Selection path: Depends on shell extensions (10-50ms typical)
- GUID comparisons: 128-bit equality check (very fast)
- No async operations at this layer (async happens later in filtering)
Error Conditions
GetItemObject Error Handling:
// If riid is not supported
return E_NOINTERFACE; // 0x80004002
// If context type is invalid
return E_INVALIDARG; // 0x80070057
// If HMENU creation fails
return E_OUTOFMEMORY; // 0x8007000EGraceful Degradation:
- Selection context falls back to item's implementation
- Background context has built-in fallback menu
- Shell extensions errors don't crash the menu
- Missing resources use hardcoded fallback
Integration with Curated Menu System
The IContextMenu created here flows into the curated menu pipeline:
1. GetItemObject() creates IContextMenu
└─► For selection: Item's native menu
└─► For background: Merged BackgroundHMENU + parent menu
2. IContextMenu::QueryContextMenu() populates an HMENU
└─► Shell extensions add their items
└─► Result cached for display
3. CDefView::_DoContextMenuPopup receives the HMENU
└─► Passes to DoCuratedContextMenu
4. DoCuratedContextMenu sends to ContextMenuPresenter
└─► DoContextMenuHelper receives the HMENU
5. TryCreateMiniMenuFromClassic filters and curates it
└─► Produces the final curated menu
6. CreateMenuItems converts HMENU to XAML
└─► Produces MenuItem collection
7. ShowXamlFlyout displays the XAML flyout
└─► User sees the curated menu
Security Considerations
COM Interface Validation:
- GUID comparisons protect against type confusion
- QISearch validates interface requests
- No unchecked casting
Menu ID Ranges:
- Background items use 0x7000+ range
- Shell extensions use 32000+ range (0x7D00+)
- Prevents command ID collisions
Resource Integrity:
- Menu templates come from trusted resources
- SHLoadPopupMenu validates resource handles
- SHLoadMenuPopup protects against resource attacks
Future Extensions
The architecture supports:
- New context types via
a2bit patterns - Custom GUID handling for new interfaces
- Extended menu merging (>2 menus)
- Async menu item loading (not currently used at this layer)
_GetContextMenuFromObjectCollection - Factory Pattern Routing
Address: 0x1802b46b0
Size: 0xe6 bytes (230 bytes)
Purpose: Creates IContextMenu for collections of items using a modern factory pattern
This function handles a different code path for context menu creation compared to GetItemObject. Instead of examining individual selections, this function operates on collections of objects via IObjectCollection, enabling more flexible batch operations and multi-item menus.
Function Signature
HRESULT __fastcall CDefView::_GetContextMenuFromObjectCollection(
CDefView *this,
struct IObjectCollection *a2, // Collection of items
struct _GUID *a3, // Requested interface (riid)
void **a4) // [out] Returned interfaceHigh-Level Algorithm
_GetContextMenuFromObjectCollection(IObjectCollection pCollection, riid, &ppv)
│
├─► TRY: Query this+75 for IContextMenuFactory
│
├─► IF IContextMenuFactory available (SUCCESS PATH):
│ │
│ ├─► Step 1: Convert IObjectCollection → IShellItemArray
│ │ └─► _GetShellItemArrayFromObjectCollection(pCollection, &shellItemArray)
│ │
│ ├─► Step 2: Call factory method (vtable+0x18)
│ │ └─► IContextMenuFactory::CreateContextMenu(
│ │ this+86, // Parameter 1 (purpose unknown, cached data?)
│ │ shellItemArray, // Parameter 2 (the items)
│ │ 0, // Parameter 3 (reserved)
│ │ riid, // Parameter 4 (requested interface)
│ │ &ppv) // Parameter 5 (output)
│ │
│ └─► Return result
│
└─► ELSE: IContextMenuFactory NOT available (FALLBACK PATH)
│
├─► Step 1: Extract child IDs from collection
│ └─► _GetUIObjectFromItemArray(
│ pCollection, // Items collection
│ 0, // a3 parameter
│ riid, // Requested interface
│ &ppv) // Output
│
└─► Return result
Decompiled Implementation
HRESULT __fastcall CDefView::_GetContextMenuFromObjectCollection(
CDefView *this,
struct IObjectCollection *a2,
struct _GUID *a3,
void **a4)
{
int result;
void *pShellItemArray;
ComPtr<IContextMenuFactory> pFactory;
*a4 = 0; // Clear output
pFactory = NULL;
// ***STEP 1: TRY TO ACQUIRE ICONTEXTMENUFACTORY***
// Query member at offset +75 (likely a service provider or cached object)
// for the IContextMenuFactory interface
if ((***(this + 75))(*(this + 75), &IID_IContextMenuFactory, &pFactory) < 0)
{
// ***FACTORY NOT AVAILABLE: USE FALLBACK***
// If factory QueryInterface fails, fall back to legacy path
result = CDefView::_GetUIObjectFromItemArray(
this,
a2, // Item collection
0, // a3 parameter
a3, // Requested interface
a4); // [out] Result
}
else
{
// ***FACTORY AVAILABLE: USE MODERN PATH***
pShellItemArray = NULL;
// ***STEP 2: CONVERT IOBJECTCOLLECTION TO ISHELLITEMARRAY***
result = CDefView::_GetShellItemArrayFromObjectCollection(
this,
a2, // Input: IObjectCollection
&IID_IShellItemArray, // Interface to query for
&pShellItemArray); // [out] Resulting IShellItemArray
if (result >= 0) // Success
{
// ***STEP 3: CALL FACTORY METHOD***
// Vtable offset +0x18 = 3rd method (after QueryInterface, AddRef, Release)
// Get factory vtable
IContextMenuFactory **ppVtable = *(IContextMenuFactory**)pFactory;
// Call factory method at offset 0x18 (3rd virtual method)
result = (*ppVtable[3])(
pFactory, // this
*(this + 86), // Parameter 1: Some cached data from DefView
pShellItemArray, // Parameter 2: The items
0, // Parameter 3: Reserved
a3, // Parameter 4: Requested interface
a4); // Parameter 5: Output
}
// Release the shell item array
if (pShellItemArray)
pShellItemArray->Release();
}
// Release factory COM object via RAII
pFactory.reset();
return result;
}Key Member Variables
Based on assembly analysis:
| Offset | Purpose | Usage |
|---|---|---|
+75 (0x4B) | Query provider for IContextMenuFactory | QueryInterface target |
+86 (0x56) | Cached context data (HWND? pointer?) | Passed to factory method |
+2B0 (0x2B0) | Additional factory parameter | Loaded at factory call site |
Code Path Comparisons
| Aspect | GetItemObject | _GetContextMenuFromObjectCollection |
|---|---|---|
| Entry Point | CDefView method | CDefView method |
| Input | Context type bits (a2 & 0xF) | IObjectCollection interface |
| Routing | Selection vs Background bits | Factory availability |
| Primary Path | Direct handler functions | IContextMenuFactory pattern |
| Fallback Path | E_NOINTERFACE | _GetUIObjectFromItemArray |
| Usage | Individual selections | Multi-item collections |
_GetShellItemArrayFromObjectCollection - Collection Conversion
Address: 0x1800a4764
Size: 0xb7 bytes (183 bytes)
Purpose: Converts generic IObjectCollection to the shell's IShellItemArray
HRESULT CDefView::_GetShellItemArrayFromObjectCollection(
CDefView *this,
struct IObjectCollection *a2,
const struct _GUID *a3, // IID_IShellItemArray
void **a4) // [out] Result
{
unsigned int result;
void *pArrayAsObj; // Temporary for interface casting
*a4 = 0; // Clear output
// ***STEP 1: TRY DIRECT CAST***
// Check if the IObjectCollection already has a special GUID interface
// (likely IShellItemArray wrapped in IObjectCollection)
if (a2->QueryInterface(
&GUID_7465aad4_d469_4305_9c0f_a4403853f4e6, // Internal GUID for array wrapper
&pArrayAsObj) < 0) // Failed - not pre-wrapped
{
// ***FALLBACK: USE CREATION FUNCTION***
// Create IShellItemArray from raw IObjectCollection
result = CreateItemArrayFromObjectArray(
*(this + 75), // Context/service provider
a2, // Collection to convert
a3, // Interface to request
a4); // [out] Result
}
else
{
// ***SUCCESS: CAST WORKED***
// The array was already wrapped - call its method (vtable+0x18)
result = (*(*pArrayAsObj + 0x18))(
pArrayAsObj,
*(this + 75), // Context/service provider
a3, // Interface to request
a4); // [out] Result
// Release temporary reference
if (pArrayAsObj)
pArrayAsObj->Release();
}
return result;
}Key Insight: This function attempts two strategies:
- Fast path: Check if IObjectCollection already wraps an IShellItemArray via a special internal GUID
- Slow path: Create a new IShellItemArray from the raw collection if needed
_GetUIObjectFromItemArray - Legacy Fallback
Address: 0x180171e60
Size: 0x10e bytes (270 bytes)
Purpose: Creates UI objects from item arrays using the older shell API
This is the fallback path when IContextMenuFactory is unavailable:
HRESULT CDefView::_GetUIObjectFromItemArray(
CDefView *this,
struct IObjectCollection *a2,
int a3, // Flags or selector
struct _GUID *a4, // Requested interface
void **a5) // [out] Result
{
int result;
DPA hdpa; // Dynamic Pointer Array of IChildId objects
unsigned int itemCount; // Number of items
void *pItemIds; // Array of item identifiers (PIDLs)
itemCount = 0;
pItemIds = NULL;
hdpa = NULL;
// ***STEP 1: EXTRACT ITEM IDENTIFIERS FROM COLLECTION***
// Convert IObjectCollection → array of child IDs (PIDLs/IChildIds)
result = CDefView::_GetChildIdArrayFromItemArray(
this,
a2, // Collection to extract from
&hdpa, // [out] Dynamic pointer array
&itemCount, // [out] Item count
&pItemIds); // [out] Array of identifier pointers
if (result >= 0) // Success
{
// ***STEP 2: INVOKE SHELL API GETUILOBJECT***
// Call the shell's GetUIObject API (vtable method at offset 0x50)
// this+75 = provider object with vtable
result = (*(**(this + 75) + 0x50))(
*(this + 75), // Provider/context object
*(this + 86), // HWND or context handle
itemCount, // Number of items
pItemIds, // Array of PIDLs
a4, // Requested interface
0, // Reserved
a5); // [out] Result
// ***STEP 3: CONDITIONAL DATA POINT SETTING***
// If we got an IDataObject and a3 flag is set:
if (result >= 0 && IID_IDataObject == *a4 && a3)
{
// Associate mouse coordinates with the data object
_SetPoints(this, itemCount, pItemIds, *a5);
}
// Clean up temporary array
CoTaskMemFree(pItemIds);
}
// ***STEP 4: CLEANUP DPA***
if (hdpa)
{
// Release all IChildId objects in the array
DPA_DestroyCallback(hdpa, DPA_ReleaseCB<IChildId>, 0);
DPA_Destroy(hdpa);
}
return result;
}Key Steps:
- Extract child ID array (PIDLs) from collection
- Call shell's GetUIObject with the item IDs
- Associate mouse points if dealing with IDataObject
- Clean up DPA and temporary data
Member Variable Analysis
From cross-referencing the assembly:
// At offset +75 (0x4B):
// Likely: IShellBrowser* or IServiceProvider
// Used for: QueryInterface(IContextMenuFactory)
// Stored result passed to factory creation functions
// At offset +86 (0x56):
// Likely: HWND or context handle
// Used for: First parameter to factory methods
// First parameter to GetUIObject
// At offset +2B0 (0x2B0):
// Purpose: Unknown, loaded in assembly but not used in decompile
// Access pattern: Conditional parameter loadingCall Chain: From Collection to Menu
Here's the complete flow when dealing with object collections:
User Right-Clicks on Selection
│
└─► Multiple items selected
│
├─► Create IObjectCollection from selected items
│ └─► IObjectCollection = {item1, item2, ..., itemN}
│
└─► _GetContextMenuFromObjectCollection(pCollection, IID_IContextMenu, &ppv)
│
├─► TRY: Query this+75 for IContextMenuFactory
│
├─► IF Factory Available:
│ │
│ ├─► _GetShellItemArrayFromObjectCollection
│ │ │
│ │ ├─► Try: Query by GUID_7465aad4_d469_4305_9c0f_a4403853f4e6
│ │ │ └─► If success: Already-wrapped array, call method
│ │ │
│ │ └─► Else: CreateItemArrayFromObjectArray
│ │ └─► Create new IShellItemArray from collection
│ │
│ ├─► Call IContextMenuFactory::CreateContextMenu
│ │ └─► Modern multi-item context menu creation
│ │
│ └─► Return result
│
└─► ELSE: Factory Not Available
│
├─► _GetUIObjectFromItemArray (Fallback)
│ │
│ ├─► _GetChildIdArrayFromItemArray
│ │ └─► Extract PIDLs from collection
│ │
│ ├─► Call shell GetUIObject with PIDL array
│ │ └─► Legacy shell API path
│ │
│ └─► Return result
│
└─► Return fallback result
Result: IContextMenu interface for multiple items
Memory Layout: CDefView Member Access
struct CDefView {
// ... members ...
/* +0x4B (75) */ IShellBrowser* pShellBrowser; // Used for factory queries
// ... members ...
/* +0x56 (86) */ HWND hwndView; // Passed to factory/GetUIObject
// ... members ...
/* +0x2B0 (688) */ (unknown context data)
};Error Handling Strategy
Multi-Level Fallback:
Level 1: IContextMenuFactory available
↓ (if fails)
Level 2: CreateItemArrayFromObjectArray
↓ (if fails)
Level 3: Legacy GetUIObject path
↓ (if fails)
Return error with context preservedThis provides graceful degradation:
- Modern factory-based path for newer systems
- Shell API fallback for compatibility
- Multiple error recovery points
- No data loss if retrieval fails
Performance Characteristics
Time Complexity:
- Fast path (cached array): O(1) - single method call
- Moderate path (array creation): O(n) - iterate collection items
- Legacy path (GetUIObject): O(n log n) - PIDL extraction + shell call
Memory Usage:
- Factory path: ~100-200 bytes (temporary COM pointers)
- Fallback path: O(n) - DPA allocation for n items
Integration with Main Context Menu Flow
This function fits into the broader system:
CDefView::_DoContextMenuPopup
│
├─► Determine context type
├─► Get items as collection via IObjectCollection
│
├─► Call _GetContextMenuFromObjectCollection
│ └─► Returns IContextMenu for collection
│
├─► Pass IContextMenu to DoCuratedContextMenu
├─► Call IContextMenu::QueryContextMenu
│
└─► Proceed to TryCreateMiniMenuFromClassic
└─► Filter and curate the menu
Comparison with GetItemObject
| Function | Purpose | Path |
|---|---|---|
| GetItemObject | Route by context type bits | Selection vs Background |
| _GetContextMenuFromObjectCollection | Create menu for collection | Factory vs Legacy |
| GetItemObject | Fast, bit-based dispatch | Direct handler selection |
| _GetContextMenuFromObjectCollection | Flexible collection handling | Multi-strategy fallback |
Key Design Patterns
1. Factory Pattern:
- Delegate menu creation to IContextMenuFactory
- Allows extensibility without modifying CDefView
- Modern path for new functionality
2. Strategy Pattern:
- Try preferred method (factory)
- Fall back to legacy method if needed
- Choose strategy based on availability
3. Adapter Pattern:
- _GetShellItemArrayFromObjectCollection adapts IObjectCollection to IShellItemArray
- Allows code reuse with different input types
4. DPA (Dynamic Pointer Array):
- Efficient array management for variable-sized collections
- Automatic cleanup via DPA_DestroyCallback
- RAII pattern for resource safety
Security Considerations
Data Flow Validation:
- Item collections are validated before use
- PIDLs extracted under error handling
- Interface queries protected by RAII COM pointers
Interface Type Checking:
- GUID comparisons ensure correct interface types
- No unsafe casts on com pointers
- QueryInterface validates availability
Memory Safety:
- All allocations use CoTaskMemFree (proper cleanup)
- DPA cleanup guaranteed via callback pattern
- No raw pointer manipulations
Relationship to Curated Menu System
This function creates the initial raw IContextMenu that gets passed up to:
_GetContextMenuFromObjectCollection
↓ (returns IContextMenu)
_DoContextMenuPopup
↓ (IContextMenu::QueryContextMenu populates HMENU)
DoCuratedContextMenu
↓ (receives populated HMENU)
ContextMenuPresenter::DoContextMenuHelper
↓ (receives HMENU)
TryCreateMiniMenuFromClassic
↓ (filters and curates)
XAML flyout
Usage Scenarios
When this function is called:
-
Multi-item selection: User selects multiple files, right-clicks
- Creates collection of selected items
- Calls this function to build multi-item context menu
-
Batch operations: Drag-drop multiple items
- Collection represents dropped items
- Menu shows operations valid for all items
-
Advanced selections: Ctrl+click selections
- Mixed file types in collection
- Factory determines valid operations
When GetItemObject is called instead:
- Single item click: Single file right-click
- Background click: Empty space right-click
- Folder context: Right-click on folder itself
End of Analysis