The capabilities of the Shell can be extended with registry entries and .ini files. While this approach to extending the Shell is simple, and adequate for many purposes, it is limited. For example, if you use the registry to specify a custom icon for a file type, the same icon will appear for every file of that type. Extending the Shell with the registry does not allow you to vary the icon for different files of the same type. Other aspects of the Shell, such as the Properties property sheet that can be displayed when a file is right-clicked, cannot be modified at all with the registry.
A more powerful and flexible approach to extending the Shell is to implement shell extension handlers. These handlers can be implemented for a variety of actions that the Shell can perform. Before taking the action, the Shell queries the extension handler, giving it the opportunity to modify the action. A common example is a shortcut menu extension handler. If one is implemented for a file type, it will be queried every time one of the files is right-clicked. The handler can then specify additional menu items on a file-by-file basis, rather than having the same set for the entire file type.
This document discusses how to implement the extension handlers that allow you to modify a variety of Shell actions. The following handlers are associated with a particular file type and allow you to specify on a file-by-file basis:
Provides read and write access to metadata (properties) stored in a file. This can be used to extend the Details view, infotips, the property page, and grouping features.
Other handlers are not associated with a particular file type but are called before some Shell operations:
Called to launch a search engine. It enables you to implement a custom search engine accessible from the Start menu or Windows Explorer.
The details of how to implement specific extension handlers are covered in the sections listed above. The remainder of this document covers some implementation issues that are common to all Shell extension handlers.
Much of the implementation of a Shell extension handler object depends on its type. There are, however, some common elements. This section discusses those aspects of implementation that are shared by all Shell extension handlers.
Many Shell extension handlers are in-process Component Object Model (COM) objects. They must be assigned a GUID and registered as described in Registering Shell Extension Handlers. They are implemented as DLLs and must export the following standard functions:
DllCanUnloadNow. COM calls this function to determine whether the object is serving any clients. If not, the system can unload the DLL and free the associated memory.
The IPersistFile interface must be implemented by the following:
Data handlers
Drop handlers
In the past, icon handlers were also required to implement IPersistFile, but this is no longer true. For icon handlers, IPersistFile is now optional and other interfaces such as IInitializeWithItem are preferred.
The IShellExtInit interface must be implemented by the following:
Shortcut menu handlers
Drag-and-drop handlers
Property sheet handlers
Implementing IPersistFile
The IPersistFile interface is intended to permit an object to be loaded from or saved to a disk file. It has six methods in addition to IUnknown, five of its own, and the GetClassID method that it inherits from IPersist. With Shell extensions, IPersist is used only to initialize a Shell extension handler object. Because there is typically no need to read from or write to the disk, only the GetClassID and Load methods require a nontoken implementation.
The Shell calls GetClassID first, and the function returns the class identifier (CLSID) of the extension handler object. The Shell then calls Load and passes in two values. The first, pszFileName, is a Unicode string with the name of the file or folder that Shell is about to operate on. The second is dwMode, which indicates the file access mode. Because there is typically no need to access files, dwMode is usually zero. The method stores these values as needed for later reference.
The following code fragment illustrates how a typical Shell extension handler implements the GetClassID and Load methods. It is designed to handle either ANSI or Unicode. CLSID_SampleExtHandler is the extension handler object's GUID, and CSampleExtHandler is the name of the class used to implement the interface. The m_szFileName and m_dwMode variables are private variables that are used to store the file's name and access flags.
The IShellExtInit interface has only one method, IShellExtInit::Initialize, in addition to IUnknown. The method has three parameters that the Shell can use to pass in various types of information. The values passed in depend on the type of handler, and some can be set to NULL.
pIDFolder holds a folder's pointer to an item identifier list (PIDL). For property sheet extensions, it is NULL. For shortcut menu extensions, it is the PIDL of the folder that contains the item whose shortcut menu is being displayed. For nondefault drag-and-drop handlers, it is the PIDL of the target folder.
pDataObject holds a pointer to a data object's IDataObject interface. The data object holds one or more file names in CF_HDROP format.
hRegKey holds a registry key for the file object or folder type.
The IShellExtInit::Initialize method stores the file name, IDataObject pointer, and registry key as needed for later use. The following code fragment illustrates an implementation of IShellExtInit::Initialize. For simplicity, this example assumes that the data object contains only a single file. In general, it might contain multiple files that will each need to be extracted.
LPCITEMIDLIST m_pIDFolder; //The folder's PIDL
wchar_t m_szFile[MAX_PATH]; //The file name
IDataObject *m_pDataObj; //The IDataObject pointer
HKEY m_hRegKey; //The file or folder's registry key
STDMETHODIMP CShellExt::Initialize(LPCITEMIDLIST pIDFolder,
IDataObject *pDataObj,
HKEY hRegKey)
{
// If Initialize has already been called, release the old PIDL
ILFree(m_pIDFolder);
m_pIDFolder = nullptr;
// Store the new PIDL.
if (pIDFolder)
{
m_pIDFolder = ILClone(pIDFolder);
}
// If Initialize has already been called, release the old
// IDataObject pointer.
if (m_pDataObj)
{
m_pDataObj->Release();
}
// If a data object pointer was passed in, save it and
// extract the file name.
if (pDataObj)
{
m_pDataObj = pDataObj;
pDataObj->AddRef();
STGMEDIUM medium;
FORMATETC fe = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
UINT uCount;
if (SUCCEEDED(m_pDataObj->GetData(&fe, &medium)))
{
// Get the count of files dropped.
uCount = DragQueryFile((HDROP)medium.hGlobal, (UINT)-1, NULL, 0);
// Get the first file name from the CF_HDROP.
if (uCount)
DragQueryFile((HDROP)medium.hGlobal, 0, m_szFile,
sizeof(m_szFile)/sizeof(TCHAR));
ReleaseStgMedium(&medium);
}
}
// Duplicate the registry handle.
if (hRegKey)
RegOpenKeyEx(hRegKey, nullptr, 0L, MAXIMUM_ALLOWED, &m_hRegKey);
return S_OK;
}
CSampleExtHandler is the name of the class used to implement the interface. The m_pIDFolder, m_pDataObject, m_szFileName, and m_hRegKey variables are private variables used to store the information that is passed in. For simplicity, this example assumes that only one file name will be held by the data object. After the FORMATETC structure is retrieved from the data object, DragQueryFile is used to extract the file name from the FORMATETC structure's medium.hGlobal member. If a registry key is passed in, the method uses RegOpenKeyEx to open the key and assigns the handle to m_hRegKey.
Specify a fixed string or a list of specific file properties to be displayed.
To display a fixed string for a namespace extension, create an entry called InfoTip in the {CLSID} key of your namespace extension. Set the value of that entry to be either the literal string you want to display, as shown in this example, or an indirect string that specifies a resource and index within that resource (for localization purposes).
HKEY_CLASSES_ROOT
CLSID
{CLSID}
InfoTip = InfoTip string for your namespace extension
To display a fixed string for a file type, create an entry called InfoTip in the ProgID key of that file type. Set the value of that entry to be either the literal string you want to display or an indirect string that specifies a resource and index within that resource (for localization purposes), as shown in this example.
If you want the Shell to display specific file properties in the infotip for a specific file type, create an entry called InfoTip in the ProgID key for that file type. Set the value of that entry to be a semicolon-delineated list of canonical property names, format identifier (FMTID)/property identifier (PID) pairs, or both. This value must begin with "prop:" to identify it as a property list string. If you omit "prop:", the value is seen as a literal string and displayed as such.
In the following example, propname is a canonical property name (such as System.Date) and {fmtid},pid is an FMTID/PID pair.
Enhancing Windows Search with Shell Extension Handlers
Shell extension handlers may be used to enhance the user experience provided by a Windows Search protocol handler. To enable such enhancements, the supporting Shell extension handler must be designed to integrate with the search protocol handler as a data source. For information about how to enhance a Windows Search protocol handler through integration with a Shell extension handler, see Adding Icons, Previews and Shortcut Menus. For more information about Windows Search protocol handlers, see Developing Protocol Handlers.
Registering Shell Extension Handlers
A Shell extension handler object must be registered before the Shell can use it. This section is a general discussion of how to register a Shell extension handler.
Any time you create or change a Shell extension handler, it is important to notify the system that you have made a change with SHChangeNotify, specifying the SHCNE_ASSOCCHANGED event. If you do not call SHChangeNotify, the change might not be recognized until the system is rebooted.
As with all COM objects, you must create a GUID for the handler using a tool such as UUIDGEN.exe. Create a key under HKEY_CLASSES_ROOT\CLSID whose name is the string form of the GUID. Because Shell extension handlers are in-process servers, you must create an InProcServer32 key under the GUID key with the default value set to the path of the handler's DLL. Use the Apartment threading model.
Any time the Shell takes an action that can involve a Shell extension handler, it checks the appropriate registry key. The key under which an extension handler is registered thus controls when it will be called. For instance, it is a common practice to have a shortcut menu handler called when the Shell displays a shortcut menu for a member of a file type. In this case, the handler must be registered under the file type's ProgID key.
Handler Names
To enable a Shell extension handler, create a subkey with the handler subkey name (see below) under the ShellEx subkey of either the ProgID (for file types) or the Shell object type name (for Predefined Shell Objects).
For example, if you wanted to register a shortcut menu extension handler for MyProgram.1, you would begin by creating the following subkey:
For the following handlers, create a subkey underneath the "Handler Subkey name" key whose name is the string version of the CLSID of the Shell extension. Multiple extensions can be registered under the handler subkey name key by creating multiple subkeys.
Handler
Interface
Handler Subkey Name
Shortcut menu handler
IContextMenu
ContextMenuHandlers
Copyhook handler
ICopyHook
CopyHookHandlers
Drag-and-drop handler
IContextMenu
DragDropHandlers
Property sheet handler
IShellPropSheetExt
PropertySheetHandlers
Column provider handler (deprecated in Windows Vista)
IColumnProvider
ColumnHandlers
For the following handlers, the default value of the "Handler Subkey Name" key is the string version of the CLSID of the Shell extension. Only one extension can be registered for these handlers.
Handler
Interface
Handler Subkey Name
Data handler
IDataObject
DataHandler
Drop handler
IDropTarget
DropHandler
Icon handler
IExtractIconA/W
IconHandler
Image handler
IExtractImage
{BB2E617C-0920-11d1-9A0B-00C04FC2D6C1}
Thumbnail image handler
IThumbnailProvider
{E357FCCD-A995-4576-B01F-234630154E96}
Infotip handler
IQueryInfo
{00021500-0000-0000-C000-000000000046}
Shell link (ANSI )
IShellLinkA
{000214EE-0000-0000-C000-000000000046}
Shell link (UNICODE)
IShellLinkW
{000214F9-0000-0000-C000-000000000046}
Structured storage
IStorage
{0000000B-0000-0000-C000-000000000046}
Metadata
IPropertyStore
PropertyHandler
Metadata
IPropertySetStorage (deprecated in Windows Vista)
PropertyHandler
Pin to Start Menu
IStartMenuPinnedList
{a2a9545d-a0c2-42b4-9708-a0b2badd77c8}
Pin to Taskbar
{90AA3A4E-1CBA-4233-B8BB-535773D48449}
The subkeys specified to add Pin to Start Menu and Pin to Taskbar to an item's shortcut menu are only required for file types that include the IsShortCut entry.
Support for column provider handlers was removed in Windows Vista. Also, as of Windows Vista, IPropertySetStorage has been deprecated in favor of IPropertyStore.
The Shell defines additional objects under HKEY_CLASSES_ROOT which can be extended in the same way as file types. For example, to add a property sheet handler for all files, you can register under the PropertySheetHandlers key.
HKEY_CLASSES_ROOT
*
shellex
PropertySheetHandlers
The following table gives the various subkeys of HKEY_CLASSES_ROOT under which extension handlers can be registered. Note that many extension handlers cannot be registered under all of the listed subkeys. For further details, see the specific handler's documentation.
Subkey
Description
Possible Handlers
Version
*
All files
Shortcut Menu, Property Sheet, Verbs (see below)
All
AllFileSystemObjects
All files and file folders
Shortcut Menu, Property Sheet, Verbs
4.71
Folder
All folders
Shortcut Menu, Property Sheet, Verbs
All
Directory
File folders
Shortcut Menu, Property Sheet, Verbs
All
Directory\Background
File folder background
Shortcut Menu only
4.71
Drive
All drives in MyComputer, such as "C:\"
Shortcut Menu, Property Sheet, Verbs
All
Network
Entire network (under My Network Places)
Shortcut Menu, Property Sheet, Verbs
All
Network\Type\#
All objects of type # (see below)
Shortcut menu, Property Sheet, Verbs
4.71
NetShare
All network shares
Shortcut menu, Property Sheet, Verbs
4.71
NetServer
All network servers
Shortcut menu, Property Sheet, Verbs
4.71
network_provider_name
All objects provided by network provider "network_provider_name"
Shortcut menu, Property Sheet, Verbs
All
Printers
All printers
Shortcut Menu, Property Sheet
All
AudioCD
Audio CD in CD drive
Verbs only
All
DVD
DVD drive (Windows 2000)
Shortcut Menu, Property Sheet, Verbs
4.71
Notes:
The file folder background shortcut menu is accessed by right-clicking within a file folder, but not over any of the folder's contents.
"Verbs" are special commands registered under HKEY_CLASSES_ROOT\Subkey\Shell\Verb .
For Network\Type\# , "#" is a network provider type code in decimal. The network provider type code is the high word of a network type. The list of network types is given in the Winnetwk.h header file (WNNC_NET_* values). For example, WNNC_NET_SHIVA is 0x00330000, so the corresponding type key would be HKEY_CLASSES_ROOT\Network\Type\51 .
"network_provider_name" is a network provider name as specified by WNetGetProviderName, with the spaces converted into underscores. For example, if the Microsoft Networking network provider is installed, its provider name is "Microsoft Windows Network", and the corresponding network_provider_name is Microsoft_Windows_Network.
Example of an Extension Handler Registration
To enable a particular handler, create a subkey under the extension handler type key with the name of the handler. The Shell does not use the handler's name, but it must be different from all other names under that type subkey. Set the default value of the name subkey to the string form of the handler's GUID.
The following example illustrates registry entries that enable shortcut menu and property sheet extension handlers, using an example .myp file type:
Finance and operations apps are customized by using extensions, which let you add functionality to model elements and source code in the Application Object Tree (AOT) by using Visual Studio.