Developing Shell Extensions for File Explorer Context Menus in PowerToys: A Complete Guide
PowerToys implements File Explorer context menu extensions using the Windows Explorer Command API (IExplorerCommand), creating hierarchical sub-menus through COM objects that dynamically enumerate template items in the New+ module.
The New+ feature in microsoft/PowerToys demonstrates advanced shell extension development by adding dynamic context menus to Windows File Explorer. This implementation showcases how to build scalable, settings-aware context menu extensions using modern Windows APIs. Understanding this architecture provides a blueprint for developing shell extensions for File Explorer context menus in PowerToys or standalone Windows applications.
Architecture of the Explorer Command API Implementation
PowerToys’s New+ shell extension follows a three-layer COM architecture based on the Explorer Command API.
Layer 1: Top-level command (shell_context_menu.cpp)
Implements IExplorerCommand to expose the root "New+" menu item. Key methods include GetTitle for localization, GetIcon for the menu icon, and GetFlags returning ECF_HASSUBCOMMANDS to indicate sub-menu availability.
Layer 2: Sub-command enumerator (shell_context_sub_menu.cpp)
Implements IEnumExplorerCommand to dynamically generate the menu list. This scans the user's New+ Templates folder and instantiates individual commands for each template file.
Layer 3: Individual template commands (shell_context_sub_menu_item.cpp)
Each template item implements IExplorerCommand with GetTitle, GetIcon, and Invoke methods that execute the copy operation when selected.
Core Components for Developing File Explorer Context Menu Extensions
Top-Level Menu Command (shell_context_menu.cpp)
Located at src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_menu.cpp, this file contains the shell_context_menu class implementing the root menu item.
The class exposes four critical methods:
GetTitle– Returns the localized string "New+" from resourcesGetIcon– Resolves the icon path viautilities::get_new_icon_resource_filepathGetState– QueriesNewSettingsInstance().GetEnabled()to hide the menu when the feature is disabledEnumSubCommands– Instantiatesshell_context_sub_menuto provide the sub-menu enumerator
Dynamic Sub-Menu Enumeration (shell_context_sub_menu.cpp)
The shell_context_sub_menu class in shell_context_sub_menu.cpp implements IEnumExplorerCommand to provide dynamic content.
During construction, it:
- Calls
utilities::get_new_template_folder_location()to locate user templates - Iterates through template definitions
- Creates
shell_context_sub_menu_iteminstances stored inexplorer_menu_item_commands - Appends static items: a separator (
separator_context_menu_item) and "Open templates" entry (template_folder_context_menu_item)
The class implements standard enumerator methods: Next, Reset, and Skip.
Per-Template Command Execution (shell_context_sub_menu_item.cpp)
Individual menu items are handled by shell_context_sub_menu_item in shell_context_sub_menu_item.cpp.
Each instance:
- Formats display titles via
GetTitle, applying settings like hiding extensions or leading digits - Resolves template-specific icons through
GetIcon - Executes
Invoketo callnewplus::utilities::copy_template(), copying template contents to the selected folder
Integrating Settings and Utilities
Settings Integration (settings.cpp)
The extension respects PowerToys configuration through settings.cpp. The NewSettingsInstance() singleton reads JSON configuration to determine:
- Whether the feature is enabled (affects
GetState) - Display preferences like hiding file extensions or leading digits in template names
Utility Layer (new_utilities.cpp)
Helper functions in new_utilities.cpp handle filesystem operations:
copy_template()– Performs the actual file copy from template folder to destinationopen_template_folder()– Launches the templates directory when users select "Open templates"create_folder_if_not_exist()– Ensures the template directory exists before enumeration
COM Registration and Module Entry Point
Registration occurs in powertoys_module.cpp, which declares the COM class under the ExplorerCommand CLSID. The PowerToys module loader ensures the DLL loads when Explorer constructs the context menu, registering the extension with Windows Explorer's command infrastructure.
Practical Implementation: Adding a Custom Template Item
Below is a minimal example adapted from template_folder.cpp showing how to programmatically add a template item within the sub-menu enumerator:
// Resolve the template folder location
const std::filesystem::path templateRoot = utilities::get_new_template_folder_location();
utilities::create_folder_if_not_exist(templateRoot);
// Create a template item manually (typically loaded from JSON)
auto myProjectTemplate = std::make_unique<template_item>();
myProjectTemplate->set_name(L"My Project");
myProjectTemplate->set_icon(L"myproject.ico");
myProjectTemplate->set_content(L"// My C++ project skeleton");
// Add to the command list for enumeration
explorer_menu_item_commands.push_back(
Make<shell_context_sub_menu_item>(myProjectTemplate.get(), site_of_folder));
When selected, shell_context_sub_menu_item::Invoke forwards to newplus::utilities::copy_template(), creating the file at the target location.
Summary
- PowerToys uses
IExplorerCommandto implement File Explorer context menu extensions, not legacy context menu handlers - Three-layer architecture: Top-level command (
shell_context_menu.cpp), enumerator (shell_context_sub_menu.cpp), and item commands (shell_context_sub_menu_item.cpp) - Dynamic enumeration: The extension scans
%LOCALAPPDATA%\Microsoft\PowerToys\New+\Templatesto build menus at runtime - Settings-aware:
settings.cppintegrates with PowerToys JSON configuration to control visibility and formatting - COM registration:
powertoys_module.cppregisters the extension using the ExplorerCommand CLSID for automatic loading by Explorer
Frequently Asked Questions
What Windows API does PowerToys use for File Explorer context menus?
PowerToys implements the Explorer Command API using IExplorerCommand and IEnumExplorerCommand interfaces. This modern API, used in the New+ module's shell_context_menu.cpp, provides better performance and reliability than legacy context menu shell extensions.
How does the New+ extension dynamically populate its sub-menu?
The shell_context_sub_menu class in shell_context_sub_menu.cpp implements IEnumExplorerCommand::Next to enumerate template files from the user's New+ Templates folder. It creates a shell_context_sub_menu_item for each template, plus static entries for the separator and "Open templates" command.
Where does PowerToys store the template files for the New+ menu?
Templates are stored in the user's local application data folder, resolved via utilities::get_new_template_folder_location() in new_utilities.cpp. The path typically resolves to %LOCALAPPDATA%\Microsoft\PowerToys\New+\Templates, where the extension reads JSON or text definitions.
How can I disable the New+ context menu without uninstalling PowerToys?
The extension checks NewSettingsInstance().GetEnabled() in shell_context_menu::GetState (located in shell_context_menu.cpp). When disabled in PowerToys Settings, the method returns ECS_HIDDEN, preventing the menu from appearing in File Explorer context menus.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →