# 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.
src="https://i.imgur.com/1TvQu4u.png" caption="Search result for the symbols named /> 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 src="https://i.imgur.com/HQxqpBL.png" caption="Xrefs of the method" /> 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 The The 1. Menu Creation: Converting classic Win32 HMENU handles to modern XAML MenuFlyout items 2. Item Filtering: Removing unwanted or duplicate menu items from classic menus 3. Extension Integration: Loading and inserting app extensions into menus 4. Icon Management: Asynchronously loading menu item icons 5. Verb Management: Tracking canonical verbs and command IDs 6. Telemetry: Recording context menu usage analytics --- The context menu creation process follows a well-defined call chain from the shell view (DefView) to the context menu presenter: Address: Size: 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: Key Validation: --- Address: Size: 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: High-Level Flow: Critical Decision Points: Desktop Spotlight Detection: Curated Menu Decision: IContextMenu::QueryContextMenu Call: Menu Item Merging: Dark Mode Setup: Final Dispatch: Exception Handling: Key Constants: Telemetry Points: 1. ContextMenuCreationTest: Tracks menu creation performance 2. Shell32_GeneratingContextMenu_Start/Stop: ETW events 3. DesktopSpotlightIconRightClicked: Spotlight interaction 4. SpotlightFlyoutPositionLogging: Position debugging 5. DesktopSpotlightRejuvenatedFlyout: Feature usage --- Address: Size: 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: Execution Flow: Detailed Analysis: Getting the Presenter: TryGetContextMenuPresenter Implementation (0x1800a56a0): The Critical Call to ContextMenuPresenter::DoContextMenu: Looking at the assembly at Vtable Layout Analysis: Full Parameter Marshaling: Spotlight Special Case: Exception Handling and Fallback: Key Data Structure: c_DefViewIDMAP Integration Points: 1. Service Query: Uses IUnknown_QueryService to locate presenter 2. Vtable Dispatch: CFG-protected indirect call through vtable 3. Parameter Passing: Mix of register and stack parameters (x64 calling convention) 4. Exception Safety: Try/catch with graceful fallback to classic menu 5. Telemetry Integration: TIP test framework tracks success/failure --- 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. Address: Size: 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: Key Steps: Important Notes: --- Address: Size: 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. The Evidence in binary: Observed call chain (in this binary): Creation point (classic menu): The 1. 2. 3. That populated The concrete function that calls #### Phase 1: Initialization Key Points: #### Phase 2: Feature Flag Decision Decision Factors: 1. Feature Flag: 2. Context Type: 3. Location: Must be desktop context ( 4. Spotlight State: Exception Handling: #### Phase 3: Menu Creation Menu ID Assignment: #### Phase 4: Telemetry Setup Telemetry Captured: #### Phase 5: XAML Menu Creation This is the most complex phase, creating the modern XAML UI: Event Handler Pattern: Error Recovery: FlyoutState Enum: State Transitions in DoContextMenuHelper: The function has 3 levels of exception handling: Level 1: ShowSpotlightControl Exceptions Level 2: ShowXamlFlyout Exceptions Level 3: Outer Exception Handler Menu ID Counter: Reference Counting: Resource Management: Called By: Calls To: --- Address: Size: Creates a "mini menu" (curated context menu) from a classic Win32 context menu by filtering, copying, and enhancing menu items. --- The context menu items come from multiple sources, loaded and merged in a specific order: Source: Legacy shell extensions implementing Loading Process: Key Functions: Source: Loading Process: 1. Get file associations for selected items via 2. Extract ProgIDs from association elements 3. Query app extensions for each ProgID using 4. Build app extension map with verb information 5. Asynchronously load menu items for each extension Key Functions: Extension Types: Source: Cloud storage provider extensions Loading Process: Special Handling: Source: IExplorerCommand-based commands from Command Store Loading Process: Key Functions: Canonical Verbs Checked: These are standard file operations with well-known verb names: --- Address: Purpose: Filters a classic menu and copies allowed items to a new menu Algorithm: Key Data Structures: Address: Size: Purpose: Inserts app extension menu items into the mini menu Complex Algorithm: Error Handling: Address: Purpose: Inserts a single app extension into the menu Behavior Based on Verb Count: Single Verb: Multiple Verbs: Address: Purpose: Copies a menu item from one menu to another with icon support Process: Async Pattern: Address: Purpose: Builds a map of canonical verb names to menu item indices Algorithm: Where canonical verbs are stored: --- Canonical Verb Map: App Extension Map: Verb Multimap: --- Menu item icons and some properties are loaded asynchronously to prevent UI blocking during context menu creation. LoadMenuItemAsync (0x180059fea) LoadGroupMenuItemForAppAsync (0x180055eb6) SetHMenuItemIconAsync The functions use C++/WinRT coroutines with --- The context menu system uses two distinct entry points: DoContextMenuHelper (0x1800a9f8b) - Main orchestration function ShowSpotlightContextMenu (0x1800b21d0) - Spotlight-specific path This dual-path architecture provides: DoContextMenuHelper implements a 6-state flyout state machine: State Transitions: Purpose: The context menu is built from a union of multiple sources: Not all classic menu items appear in the mini menu: Icons are loaded asynchronously to improve performance: The system is designed for extensibility: Menu IDs are carefully managed: The class includes telemetry support: Multiple Windows feature flags control behavior: These allow A/B testing and gradual rollout of changes. Some functions are exceptionally large: This suggests complex state management and error handling. The code uses Windows Internal Library (WIL) for exception handling: Deep integration with Windows Shell: Weak Pointer Pattern: Purpose: Event Token Management: --- 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: Layer 1: Shell View (CDefView) Layer 2: Bridge Layer (DoCuratedContextMenu) Layer 3: Presenter Layer (ContextMenuPresenter) The implementation demonstrates several sophisticated design principles: 1. 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 2. Performance Optimization - Asynchronous icon loading prevents UI blocking - Menu appears immediately, populates progressively - Parallel operations where possible - CFG-protected indirect calls for security 3. Backward Compatibility - Full support for IContextMenu, IContextMenu2, IContextMenu3 - Classic menu items preserved and filtered - Shell extensions continue to work - Command ID mapping maintains routing 4. Forward Extensibility - App extension framework for modern apps - Cloud provider verb integration - Feature flags enable gradual rollout - XAML-based UI allows rich experiences 5. Observability - Comprehensive telemetry (TIP framework) - ETW events for performance analysis - Detailed error logging - Feature usage tracking CDefView::_DoContextMenuPopup (3,595 bytes) ContextMenuPresenter::DoContextMenuHelper (1,656 bytes) TryCreateMiniMenuFromClassic (296 bytes) The final context menu is a curated union from multiple sources: 1. Classic Shell Extensions (IContextMenu) - Legacy handlers registered in registry - Filtered by canonical verb configuration - Maintains compatibility with existing software 2. App Extensions (Windows.Internal.FileExplorerAppExtension) - Modern UWP/AppX integrations - Dynamically discovered via AppExtension API - Supports async loading and icons 3. Cloud Provider Verbs - Special handling for cloud storage providers - Featured provider gets priority placement - Supports grouped submenus 4. Command Store Commands (IExplorerCommand) - Provides consistent command implementations - Can override classic menu items - Supports state-based visibility 5. Built-in Shell Commands - DefView's own menu items (Open, etc.) - Merged via Shell_MergeMenus - Applied via _SHPrettyMenu for consistent styling COM/WinRT Bridge: Telemetry Systems: Feature Management: The architecture enables several future enhancements: 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. --- --- Synchronous Operations: Asynchronous Operations: Critical Timing: Icons Load Asynchronously: --- Address: Size: 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. This function is called from multiple contexts: The assembly reveals important parameter manipulation: This implements the conditional logic: Parameter Meanings: #### CDefView::_GetItems (Core Selection Logic) Behavior: Result: #### CDefView::_TryModifyMenu (Extension Support) Purpose: Apply shell folder view context menu extensions via IShellFolderViewCM Algorithm: Extension Interfaces: This is where Key Observations: 1. Wait Cursor: Shows busy cursor while getting items 2. Single Selection vs Multiple: Function handles both transparently 3. Mode Selection: Caller decides if showing curated or classic menu 4. Flag 0x90: 5. Exception Safety: RAII cursor cleanup The cryptic The function handles failures gracefully: Possible return values: CFG (Control Flow Guard) Protection: Data Validation: Pattern 1: Simple Menu Query Pattern 2: Bulk Operations Pattern 3: Interface Discovery The flow when selecting items: Integration Points: --- When users interact with the file system, DefView needs to determine which Address: Size: 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. The Detection Pattern (from assembly): The decompiled code shows the complete routing logic: The low-bit extraction for context detection: This demonstrates: The function uses Comparison Order: 1. IContextMenu (most common) 2. IContextMenu2 (has callbacks) 3. IContextMenu3 (extended version) 4. IDispatch (for automation) 5. IPersistHistory (for navigation) This ordering prioritizes frequently-requested interfaces for performance. Address: Size: 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. Address: Size: Purpose: Creates the HMENU that represents the background context menu This function creates the actual Win32 The background menu is loaded from resource ID This object merges two context menus: How it works: When the user clicks on a selected item instead of empty space: _CreateSelectionContextMenu (analyzed in previous section): Stack Usage: Allocation Patterns: Performance Characteristics: GetItemObject Error Handling: Graceful Degradation: The IContextMenu created here flows into the curated menu pipeline: COM Interface Validation: Menu ID Ranges: Resource Integrity: The architecture supports: --- Address: Size: 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 Based on assembly analysis: Address: Size: Purpose: Converts generic IObjectCollection to the shell's IShellItemArray Key Insight: This function attempts two strategies: 1. Fast path: Check if IObjectCollection already wraps an IShellItemArray via a special internal GUID 2. Slow path: Create a new IShellItemArray from the raw collection if needed Address: Size: Purpose: Creates UI objects from item arrays using the older shell API This is the fallback path when IContextMenuFactory is unavailable: Key Steps: 1. Extract child ID array (PIDLs) from collection 2. Call shell's GetUIObject with the item IDs 3. Associate mouse points if dealing with IDataObject 4. Clean up DPA and temporary data From cross-referencing the assembly: Here's the complete flow when dealing with object collections: Multi-Level Fallback: This provides graceful degradation: Time Complexity: Memory Usage: This function fits into the broader system: 1. Factory Pattern: 2. Strategy Pattern: 3. Adapter Pattern: 4. DPA (Dynamic Pointer Array): Data Flow Validation: Interface Type Checking: Memory Safety: This function creates the initial raw IContextMenu that gets passed up to: When this function is called: 1. Multi-item selection: User selects multiple files, right-clicks - Creates collection of selected items - Calls this function to build multi-item context menu 2. Batch operations: Drag-drop multiple items - Collection represents dropped items - Menu shows operations valid for all items 3. Advanced selections: Ctrl+click selections - Mixed file types in collection - Factory determines valid operations When GetItemObject is called instead: 1. Single item click: Single file right-click 2. Background click: Empty space right-click 3. Folder context: Right-click on folder itself --- End of Analysis*Right*"*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.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
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.hClass Responsibilities
ContextMenuPresenter class is responsible for:Architecture: Shell View to Context Menu Presenter
Complete Call Chain from User Action
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
0x18029f6b0 0x73 bytes (115 bytes) 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
}
}CDefView::_DoContextMenuPopup
0x1802b3204 0xe0b bytes (3,595 bytes) HRESULT CDefView::_DoContextMenuPopup(
CDefView* this,
IUnknown* punkSite,
unsigned int flags,
POINT pt,
int mode // 0 = Curated, 1 = Classic
)_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 testif (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);
}
}
}
}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
}// 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
);// 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);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)// 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();
}try
{
// Desktop Spotlight detection and positioning
if (Feature_DesktopSpotlightRightClickContextMenu::IsEnabled() && ...)
{
// ... Spotlight logic ...
}
}
catch (...)
{
wil::Log_CaughtException();
// Continue with regular menu - graceful degradation
}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 CDefView::DoCuratedContextMenu
0x18029f72c 0x337 bytes (823 bytes) void CDefView::DoCuratedContextMenu(
CDefView* this,
IContextMenu* pContextMenu,
HMENU hMenu,
unsigned int flags,
POINT* ppt
)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)IContextMenuPresenter* presenter = NULL;
TryGetContextMenuPresenter(&presenter);
if (!presenter)
{
wil::Throw_Hr(
E_FAIL,
"shell\\SrcPkg\\FileExplorer\\DefView\\src\\DefView.cpp",
Line 2127
);
}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;
}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 call// IContextMenuPresenter vtable layout (x64)
0x00: QueryInterface
0x08: AddRef
0x10: Release
0x18: (method 3)
0x20: DoContextMenu ← Called here (vtable+0x20)
0x28: DoContextMenuWithClassicMenu (variant method)// 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
);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
);
}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
);
}// 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
// ...
};Context Menu Entry Points
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
0x1800b21d0 0x329 bytes (809 bytes)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
);
}DoContextMenuHelper - Main Orchestration
0x1800a9f8b 0x678 bytes (1,656 bytes) 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
hClassicMenu passed into DoContextMenuHelper (and then into TryCreateMiniMenuFromClassic) is created by the caller of ContextMenuPresenter::DoContextMenu, not inside ContextMenuPresenter itself.FileExplorerFolderViewController::ShowContextMenuForSelection / ShowContextMenuForBackground
└─► IListControlHost2::ShowContextMenu... (vtable call)
└─► ContextMenuPresenter::DoContextMenu(HMENU hClassicMenu, flags, invokePoint)
└─► ContextMenuPresenter::DoContextMenuHelper(hClassicMenu, tipTest)
└─► TryCreateMiniMenuFromClassic(&hMenu, hClassicMenu)HMENU itself is produced by the shell context menu host using the classic Win32 flow:CreatePopupMenu() creates the base menu.IContextMenu::QueryContextMenu() (plus IContextMenu2/3 hooks) populates it.HMENU is then handed to ContextMenuPresenter::DoContextMenu.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 flyoutDetailed Phase Analysis
// 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");
}// 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
);
}Feature_DesktopSpotlightRightClickContextMenu must be enabledm_isSpotlightContext flag set during initializationLOCATION_CONTEXT_DESKTOP = 0x400)m_spotlightEnabled must be true// 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();
}// 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;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);
}
}State Management
enum FlyoutState
{
Idle = 0,
Creating = 1,
WaitingForAsync = 2,
ShowingXaml = 3,
ShowingWin32 = 4,
Dismissed = 5
};Idle (0)
↓ [TransitionFlyoutState called]
Creating (1)
↓ [Menu created]
ShowingXaml (3)
↓ [User interacts/dismisses]
Dismissed (5)Error Handling Strategy
try {
ShowSpotlightControl(tipTest);
return;
}
catch (...) {
Log_CaughtException();
testData->spotlightException = wil::ResultFromCaughtException();
// Continue to standard menu
}try {
ContextMenuHost::ShowXamlFlyout(...);
}
catch (...) {
Log_CaughtException();
// Record in telemetry
tip_test::complete(m_contextMenuSelectionTest);
CancelCurrentFlyout(CancelReason::Exception);
}Performance Considerations
Integration Points
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
0x180031818 0x128 bytes (296 bytes)Purpose
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 stylingPseudo-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
1. Classic Context Menu Items (IContextMenu)
IContextMenu interface2. App Extensions (Modern UWP/AppX)
Windows::Internal::FileExplorerAppExtension APIGetAssociationElementsGetExtensions3. Cloud Provider Verbs
4. Command Store Commands
5. Canonical Verbs
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
0x180053b3e 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 menuInsertAppExtensionsIntoMiniMenu
0x180031f7a 0x973 bytes (2,419 bytes) 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 itemsInsertAppExtensionIntoMiniMenu
0x18000e744 if (verbCount == 1)
{
// Insert directly without submenu
TryInsertMenuItemForAppExtension(hMenu, extension.guid, extension.displayName);
return 1;
}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
0x18000eaba 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 IDGetCanonicalVerbsAndIndices
0x1800107e5 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++;
}
}
}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
unordered_map<wstring, int> canonicalVerbMap;
// Key: Canonical verb name (e.g., "open", "edit")
// Value: Menu item index in classic menumap<hstring, AppExtensionElement> extensionMap;
// Key: App name/identifier
// Value: App extension informationunordered_multimap<hstring, VerbElement> verbMap;
// Key: App identifier
// Value: Verb information (one app can have multiple verbs)Async Loading Mechanism
Overview
Async Functions
Coroutine Pattern
$_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
2. Sophisticated State Machine
Idle (0) → Creating (1) → WaitingForAsync (2) → ShowingXaml (3) → Dismissed (5)
↓ ↓
└──────────────────────────────────→ ShowingWin32 (4)3. Multi-Source Menu Construction
2. Filtering and Curation
3. Asynchronous Icon Loading
4. Extensibility Architecture
5. Menu ID Management
6. Telemetry and Diagnostics
7. Feature Flags
8. High Complexity Functions
9. Exception Handling
10. Shell Integration
13. Reference Counting and Lifetime Management
auto weakThis = get_weak();
m_xamlFlyout.Closed([weakThis](auto sender, auto args) {
if (auto strong = weakThis.get())
strong->OnFlyoutClosed();
});Conclusions
Architectural Layers
Key Design Principles
Critical Functions
Menu Item Sources
Technology Integration
Future Extensibility
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 UserData 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 userTiming and Synchronization
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)CDefView::_CreateSelectionContextMenu
0x1801e1a3c 0xa4 bytes (164 bytes) 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
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
; 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)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
HRESULT CDefView::_GetItems(
unsigned int svgio, // Selection type
int fMultiple, // 1 = single object
const GUID& riid, // Interface requested
void** ppv) // [out] ResultHRESULT CDefView::_TryModifyMenu(
IContextMenu* pContextMenu,
int a3, // Unused (0)
const GUID* riid, // Requested interface
void** ppv) // [out] ResultHRESULT 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);
}Call Flow in DoSelectionContextMenu
_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;
}DROPEFFECT_COPY | 0x10 indicates selection contextParameter Decoding
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
// _GetItems returned error
if (Items < 0)
return Items; // Propagate error
// _TryModifyMenu handled error internally
// (returns default if extension unavailable)Security Considerations
Usage Patterns
void* pMenu;
_CreateSelectionContextMenu(this, 0, &IID_IContextMenu, &pMenu);
// Use pMenu...
((IContextMenu*)pMenu)->Release();// Get menu for all selected items
_CreateSelectionContextMenu(this, 0x80000001, &IID_IContextMenu, &pMenu);
// Perform operation on all items simultaneouslyvoid* pInterface;
GUID riid = { /* some interface */ };
_CreateSelectionContextMenu(this, 0, &riid, &pInterface);
// pInterface points to requested COM interface for selectionIntegration with Curated Menu System
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 displayedIContextMenu Creation Based on Object Type
Overview
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
0x180100120 0x30f bytes (783 bytes) 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
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 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 interfacesDecompiled Implementation
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
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 pathType Checks Via QISearch
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
}Background Context: _CBackgrndMenu_CreateInstance
0x180100438 0x190 bytes (400 bytes) 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
0x180100754 0x3b9 bytes (953 bytes) 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)
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
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)
};// 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 → TrackPopupMenuSelection Context Routing
if ((a2 & 0xF) == 0x1) // Selection context
{
return CDefView::_CreateSelectionContextMenu(
this,
0,
a3, // Requested interface
a4); // [out] Result
}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
// Local variables in GetItemObject
const GUID* v4, v5;
int v6;
__int64 v7-v13; // ~110 bytes on stackError Conditions
// 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; // 0x8007000EIntegration with Curated Menu System
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 menuSecurity Considerations
Future Extensions
_GetContextMenuFromObjectCollection - Factory Pattern Routing
0x1802b46b0 0xe6 bytes (230 bytes) 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 resultDecompiled 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
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
0x1800a4764 0xb7 bytes (183 bytes) 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;
}_GetUIObjectFromItemArray - Legacy Fallback
0x180171e60 0x10e bytes (270 bytes) 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;
}Member Variable Analysis
// 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
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 itemsMemory 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
Level 1: IContextMenuFactory available
↓ (if fails)
Level 2: CreateItemArrayFromObjectArray
↓ (if fails)
Level 3: Legacy GetUIObject path
↓ (if fails)
Return error with context preservedPerformance Characteristics
Integration with Main Context Menu Flow
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 menuComparison 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
Security Considerations
Relationship to Curated Menu System
_GetContextMenuFromObjectCollection
↓ (returns IContextMenu)
_DoContextMenuPopup
↓ (IContextMenu::QueryContextMenu populates HMENU)
DoCuratedContextMenu
↓ (receives populated HMENU)
ContextMenuPresenter::DoContextMenuHelper
↓ (receives HMENU)
TryCreateMiniMenuFromClassic
↓ (filters and curates)
XAML flyoutUsage Scenarios