Shell

Shell Extensions

디버그정 2008. 9. 13. 08:37
 
Shell Extensions Shell Extensions *
*  *Index  *Topic Contents
*Previous Topic: Requesting Group Information
*Next Topic: Shell Library

Shell Extensions


Microsoft® Win32®-based applications can extend the shell in a number of ways. A shell extension enhances the shell by providing additional ways to manipulate file objects, by simplifying the task of browsing through file systems and networks, or by giving the user easier access to tools that manipulate objects in the file system. For example, a shell extension can assign an icon to a file or add commands to the context menu and File menu for a file.

arrowy.gifShell Extension Terms

arrowy.gifShell Extension Types

arrowy.gifRegistry Entries for Extending the Shell

arrowy.gifInstalling Handlers on Windows NT

arrowy.gifHow the Shell Accesses Shell Extension Handlers

arrowy.gifUsing Shell Extensions

Shell Extension Terms

You should be familiar with the following shell extension terms before proceeding.

  • File Object - An item within the shell. The most familiar file objects are files and directories. However, a file object may not actually be a part of a file system; it may only appear that way. For example, printers, Control Panel applications, network shares, servers, and work groups are also considered to be file objects.
  • File Class - Each file object is a member of a file class. The file class refers to the code that "owns" the manipulation of files belonging to that class. For example, text files and Microsoft® Word documents are examples of file classes. Each file class has specific shell extensions associated with it. When the shell is about to take an action involving a file object, it uses the file class to determine the shell extensions to load.
  • Handler - The code that implements a particular shell extension.

Shell Extension Types

Microsoft® Windows® supports two groups of shell extensions. The first group is registered for each type of file.

  • Context Menu Handler - Adds items to the context menu for a particular file object. The context menu is displayed when the user clicks a file object with the right mouse button.
  • Icon Handler - Typically used to add instance-specific icons for file objects. They can also be used to add icons for all files belonging to the same class.
  • Data Handler - Provides a type-specific IDataObject interface to be passed to the OLE DoDragDrop function.
  • Drop Handler - Provides type-specific drop behavior to files that can accept drag-and-drop objects.
  • Property Sheet Handler - Adds pages to the property sheet dialog box that the shell displays for a file object. The pages are specific to a class of files or a particular file object.

The second group of shell extensions is associated with file operations such as move, copy, rename, and so on.

  • Copy Hook Handler - Called when a folder object is about to be copied, moved, deleted, or renamed. The handler can either allow or prevent the operation.
  • Drag-and-Drop Handler - A context menu handler that the system calls when the user drops an object after dragging it to a new position.

The design of a shell extension is based on the OLE Component Object Model (COM). The shell accesses an object through interfaces. An application implements the interfaces in a shell extension dynamic-link library (DLL), which is essentially an OLE in-process server DLL.

Registry Entries for Extending the Shell

An application that creates and maintains files, such as a spreadsheet, word processor, or graphics application, typically adds two keys to the system registry: a file association key and an application identifier key. The file association key maps a file name extension to an application identifier. For example, a word processing application might register the following key under HKEY_CLASSES_ROOT.

HKEY_CLASSES_ROOT 
   .doc=AWordProcessor 

The value name (.doc) specifies the file name extension, and the value (AWordProcessor) denotes the key name that contains the information about the application handling the file name extension.

The application identifier key is the second registry entry made by an application handling files.

HKEY_CLASSES_ROOT 
   AWordProcessor=A Word Processor 

The value (A Word Processor) is a string describing the application that recognizes files having the given file name extension. (In this case, it is the .doc file name extension.)

Extending the shell requires that you add other entries below the file association and application identifier keys. The system checks these entries to determine the commands to add to various shell menus, when to load an extension DLL, where to find the DLL, and so on.

There are several registry keys that allow you to extend the shell without having to write any code at all. These keys let you set the default icon for a class of files or add commands to the File menu and its New submenu in Windows Explorer.

Setting the Default Icon for a Disk Drive

In Microsoft® Internet Explorer 4.0, the icon that is displayed for a disk drive can be modified by adding a key in the registry in the following location:

HKEY_LOCAL_MACHINE
    Software
        Microsoft
            Windows
                CurrentVersion
                    Explorer
                        <drive letter>
                            DefaultIcon

The <drive letter> indicates the letter of the drive that the icon is being specified for, such as "C". The default value of the DefaultIcon key is a string value that contains the path and file name of the module that contains the icon, followed by a comma and by the zero-based index of the icon. An example of this would be "c:\myapp.exe,2".

If the drive icon is changed programmatically, you should use the SHChangeNotify function to notify the shell that the icon has changed. Here's an example of how this is done:

SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH, TEXT("C:\"), NULL);

Setting Default Icons for File Classes

The system uses icons to represent file objects in the shell. Typically, all files of the same class have the same icon. By adding the DefaultIcon key to the file association key for a particular file class, you can specify the icon that the system displays for all files of the class. The value of the DefaultIcon key specifies the file that contains the icon and the index of the icon within the file. This file can be an executable, a dynamic-link library, or an icon file. The icon displayed for folders can not be modified.

HKEY_CLASSES_ROOT 
   .doc=AWordProcessor 
      DefaultIcon=C:\MYDIR\MYAPP.EXE,1 

If the registry does not contain a DefaultIcon key for a particular file class, the system uses the default icon for the class. One of the advantages of using a class icon is that it requires no programming; the shell handles displaying the icon for the class.

By writing an icon handler, you give each instance of a file a different icon. For more information about icon handlers, see Icon Handlers.

Modifying the Context Menu for a File Class

When the user clicks a file object with the right mouse button, the system displays a context menu for the object. This context menu contains a set of menu items that allow the user to perform various operations on the file object, such as opening or printing it. A context menu contains two types of items: dynamic items and static items. A context menu handler adds dynamic items to a context menu.

Static menu items are listed in the system registry and are automatically added to a context menu by the system. Because static items are listed in the system registry based on their class, the context menus for all file objects belonging to a particular class receive the same set of static items.

You specify static menu items for a file class by first adding a shell key below the application identifier key of the file class and then adding verb value and command value entries below the shell key. Following is the registry format for static items.

 HKEY_CLASSES_ROOT 
   <applicationID> = <"description"> 
      shell 
         <verb> = <"menu-item text"> 
            command = <"command string"> 

Each verb value entry specifies a menu-item text string for the system to add to the context menu. The command value entry specifies the action that the system takes when the user chooses the menu item. Typically, the command string value specifies the path and file name of an application and includes command-line options that direct the application to perform an action on the corresponding file object. For example, the following registry keys add an Open command and a Print command to the context menu for all files with the .wri file name extension.

 HKEY_CLASSES_ROOT 
   wrifile = Write Document 
      shell 
         open 
            command = C:\Progra~1\Access~1\WORDPAD.EXE %1 
         print 
            command = C:\Progra~1\Access~1\WORDPAD.EXE /p "%1" 
         printto 
            command = C:\Progra~1\Access~1\WORDPAD.EXE /pt "%1" "%2" "%3" "%4" 

In the preceding commands, the %1 parameter is the file name, %2 is the printer name, %3 is the driver name, and %4 is the port name. In Windows 95, you can ignore the %3 and %4 parameters (the printer name is unique in Windows 95).

The system defines a set of verbs, called canonical verbs, that introduce an element of language-independence to context menus. When you include a canonical verb in the registry, the system automatically generates a localized menu item string for the verb before adding it to the context menu. The canonical verbs include open, print, explore, find, openas, and properties. The printto verb is also canonical, but it is never actually displayed. It allows the user to print a file by dragging it to a printer object. Canonical verbs are also used with context menu handlers.

If the open canonical verb is included in the registry entries for a file class, the system adds an Open menu item to the corresponding context menu and makes it the default item. If the open verb is not included, the menu item corresponding to the verb listed in the registry is the default item. A context menu handler can change the default item. For more information about context menu handlers, see Context Menu Handlers.

Modifying the New Submenu

The File menu in a file system folder contains a New submenu that, by default, includes the Shortcut and Folder commands. These commands allow the user to create new shortcuts and folders within the current folder. The New submenu can also include nondefault commands that let the user create new files of various types within the current folder, such as sound files, text files, and bitmap files. For example, the New submenu might include a Sound command that creates a .wav file in the current folder.

If your application supports a type of file that the user may want to create from within a file system folder, you should consider adding a command for it to the New submenu. For example, suppose you have created a graphics application that creates files with the .xyz file name extension. You could add a command, such as XYZ Picture, that creates a new .xyz file or launches your application and opens a new .xyz file for editing.

You add a command to the New submenu by including a ShellNew key below the file association key for your file type. When the system needs to create the New submenu, it searches through the file association entries for instances of the ShellNew key. When it finds an instance of ShellNew, the system retrieves the string associated with the application identifier key (xyzfile) and adds the string to the New submenu as a new command. Note that an Open command must be registered below the application identifier key; otherwise, the system does not add the command to the New submenu.

The following example shows the registry entries needed to add the XYZ Picture command to the New submenu.

HKEY_CLASSES_ROOT
   .xyz="xyzfile"
      ShellNew 
         NullFile="" 
    . 
    . 
    . 
   xyzfile="XYZ Picture" 
      shell 
         open 
            command="C:\XYZ\XYZAPP.EXE %1 

The data names for the ShellNew key specify the method to use to create a new file of the type designated by the file name extension. There are four possible data names and values for the ShellNew key.

Data name Value Description
NullFile "" Creates an empty (null) file. If this data name is specified, Data and FileName are ignored.
Data binary-value Creates a file that contains the data specified by binary-value. This data name is ignored if either NullFile or FileName is specified.
FileName path-name Creates a copy of the file specified by path-name. This data name is ignored if NullFile is specified.
Command path-name Executes the command specified by path-name when the file is created. For example, the command might start a wizard.

Registering Shell Extensions

A shell extension must be registered in the registry database. The class identifier of each handler must be registered under the HKEY_CLASSES_ROOT\CLSID key. The CLSID key contains a list of class identifier key values, such as {00030000-0000-0000-C000-000000000046}. Each class identifier key is a globally unique identifier (GUID) generated by the UUIDGEN tool. Within each class identifier key, the handler adds an InProcServer32 key that gives the location of the handler's DLL. It is best to give the complete path for the handler; using the complete path keeps the handler independent of the current path and speeds up the load time for the DLL.

The information that the shell uses to associate a shell extension handler with a file type is stored under the shellex key. The shell also uses several other special keys under HKEY_CLASSES_ROOT to look for shell extensions. These special keys are described below. The GUID strings shown in the following examples are given for example purposes only. You must use your own GUIDs when registering your shell extensions. The "<name>" values are also for example purposes only. The name of the extension is not significant as long as it is unique among all of the other keys at the same level.

  • You can use the * key in the following manner to register handlers that the shell calls whenever it creates a context menu or property sheet for a file object.
    HKEY_CLASSES_ROOT 
       * = * 
          shellex 
             ContextMenuHandlers 
                <name> = {00000000-1111-2222-3333-00000000000001} 
             PropertySheetHandlers 
                <name> = {00000000-1111-2222-3333-00000000000002} 
    
  • The shell uses instances of the ExtraMenu and SummaryInfo handlers to add to the context menus and property sheets for every file object.
  • You can use the Folder key to register a shell extension for all folders in the system. You can register context menu handlers, copy hook handlers, and property sheet handlers in the same way you register these handlers for the * key. An additional handler, the drag-and-drop handler, applies only to the Folder and Printers keys. An example showing the Folder key follows.
    Folder = Folder 
       shellex 
          DragDropHandlers 
             <name> = {00000000-1111-2222-3333-00000000000004} 
          CopyHookHandlers 
             <name> = {00000000-1111-2222-3333-00000000000005} 
    
  • You can use the Drive key for the same registrations as the Folder key, but the Drive key is called only for root paths (for example, C: \).
  • You can use the Directory key for the same registrations as the Folder key, but the Directory key is only used for file system folders.
    • Version 4.71. Internet Explorer 4.0 adds a Background key to the Directory key. The Background key contains the following entries:
      shellex 
          ContextMenuHandlers 
              <name> = {00000000-1111-2222-3333-00000000000004} 
      

      These entries affect actions that occur on the background of a file system folder view. For example, if you register a context menu handler under the Background key, the context menu handler will be called when the user right-clicks in a file system folder view in an area that does not contain any items.

  • You can use the AudioCD key for the same registrations as the Folder key. The AudioCD key affects items that represent an audio CD that is in the system's CD drive.
  • Version 4.71. Internet Explorer 4.0 adds an AllFileSystemObjects key. You can use the AllFileSystemObjects key for the same registrations as the Folder key. The AllFileSystemObjects key affects all objects in the file system.
  • The Printers key allows the same registrations as the Folder key, but it uses additional handlers for printer events, deletion or removal of printers (through the copy hook handler), and printer properties (with property sheet handlers and context menu handlers).

Installing Handlers on Windows NT

Because shell extensions handlers run in a system process, namely the shell process, Microsoft® Windows NT® administrators require control over which shell handlers are allowed to run. Administrators have control over which shell handlers can run in much the same way they control which device drivers can run.

For the Windows NT shell to recognize and run a shell extension handler, you must follow the instructions in Registering Shell Extensions. Then the handler's CLSID must also be listed under another new registry key. This registry key contains a list of the handlers that are approved for the shell to run. By default, this key's access control permissions allow only someone with administrator privileges to modify the list.

The CLSID for the extension must be registered at the following location:

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved

To register the extension, a named value should be added to the Approved key. The name of the value must be the string form of the CLSID (for example: {00000000-1111-2222-3333-00000000000002}). The value itself should be the ProgID. The ProgID is stored here simply to make inspection of the registry easier, as it is easier to decipher than a CLSID. The Windows NT shell does not look at this value, however; it only checks for the presence or absence of the CLSID.

Your setup application may or may not be able to write to this key, depending on the privileges of the person installing the application. The setup application should attempt to open the key described above, requesting the KEY_SET_VALUE permission. If it succeeds, the new CLSID can be added to fully register the corresponding shell extension. If the request fails with a security violation, the person installing the application does not have permission to register new shell extensions. In this case, the setup application might warn the user that some application features will not be available unless an administrator turns them on (by installing the application, or by writing the registry keys directly). Or, if the shell extension is crucial for the application to function properly, the setup application might cause the installation to fail completely, notifying the user that the program must be installed by an administrator.

When setting up on Windows 95, you do not need to write the CLSID to this key, although doing so is harmless. Note, however, that the key may not exist in a Windows 95 installation, so if your setup application does attempt to write the key, it may fail.

The following sample code details this process. Full error handling has been omitted.

#include <windows.h> 
#include <string.h> 
 
void main(void) 
{ 
     // 
     // First, attempt to open the registry key where approved extensions are 
     // listed. Note the extra slashes within the second parameter (the 
     // registry path string). 
     // 
 
     long err; 
     HKEY hkApproved; 
 
     err = RegOpenKeyEx( 
               HKEY_LOCAL_MACHINE, 
               "Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved", 
               0, 
               KEY_SET_VALUE, 
               &hkApproved); 
 
 
     if (err == ERROR_ACCESS_DENIED) 
     { 
          // 
          // The user does not have permission to add a new value to this key. In this 
          // case, a reasonable action would be to warn the user that some 
          // application features will not be available unless an administrator 
          // installs the application. If the shell extension is central to the 
          // functioning of the application, tell the user that the install 
          // can only be performed by an administrator, and stop the install. 
          // 
     } 
 
     else if (err == ERROR_FILE_NOT_FOUND) 
     { 
          // 
          // The key does not exist. This should only happen if setup is running 
          // on Windows 95 instead of Windows NT, or if you are installing on an older 
          // version of either operating system that does not have the new shell. 
          // 
     } 
 
     else if (err != ERROR_SUCCESS) 
     { 
          // 
          // Some other problem... 
          // 
     } 
 
     else 
     { 
          // 
          // The open of the key succeeded. Now register the new shell extension 
          // under this key. This requires having the ProgID and string form of the 
          // CLSID handy. 
          // 
 
          // 
          // Assume that lpstrProgID contains our ProgID string. 
          // 
 
            LPSTR lpstrProgID = "My Bogus Class"; 
 
          // 
          // Assume that clsidExtension contains the CLSID struct. The code below 
          // creates a string form this CLSID. If a string version of 
          // the CLSID is already handy, skip this code. 
          // 
 
          CLSID clsidExtension = {0x11111111, 0x1111, 0x1111, 0x11, 0x11, 
                                  0x11, 0x11, 0x11, 0x11, 0x11, 0x11}; 
 
          HRESULT hr; 
          LPOLESTR lpolestrCLSID; 
          CHAR rgchCLSID[40]; 
 
          CoInitialize(NULL); 
 
          hr = StringFromCLSID(clsidExtension, &lpolestrCLSID); 
 
          // 
          // StringFromCLSID returns a Unicode string, so convert to ANSI for 
          // calling the registry. Note that on Windows NT you can call the Unicode 
          // version of the registry API instead. 
          // 
 
          WideCharToMultiByte(CP_ACP,     0, lpolestrCLSID, -1, rgchCLSID, 40, 
                                    NULL, NULL); 
 
          CoTaskMemFree(lpolestrCLSID); 
 
          CoUninitialize(); 
 
 
          // 
          // Now add the new value to the registry. 
          // Note that each new shell extension CLSID must be registered here. 
          // 
 
          err = RegSetValueEx( 
                    hkApproved, 
                    rgchCLSID, 
                    0, 
                    REG_SZ, 
                    (const BYTE *)lpstrProgID, 
                    strlen(lpstrProgID)); 
 
 
          // 
          // Finally, close the key. 
          // 
 
          err = RegCloseKey(hkApproved); 
 
     } 
} 

How the Shell Accesses Shell Extension Handlers

The shell uses one of two interfaces to initialize instances of shell extensions: IShellExtInit or IPersistFile. The shell uses the IShellExtInit interface to initialize instances of context menu handlers, drag-and-drop handlers, and property sheet handlers. The shell uses IPersistFile to initialize instances of icon handlers, data handlers, and drop handlers. The IPersistFile interface is defined by OLE.

The IShellExtInit interface adds an additional member function, Initialize, to the standard IUnknown interface. A handler should keep a copy of any necessary parameters that the shell passes to Initialize for later use. The values passed to Initialize will vary depending on the type of handler being initialized.

The following example shows how to initialize instances.

 STDMETHODIMP CShellExt::Initialize( LPCITEMIDLIST pIDFolder, 
                                    LPDATAOBJECT pDataObj, 
                                    HKEY hRegKey) 
{ 
   // Initialize can be called more than once. 
   if (m_pDataObj) 
      m_pDataObj->Release(); 
 
   // Save the object pointer. 
   if (pDataObj) 
   { 
      m_pDataObj = pDataObj; 
      pDataObj->AddRef(); 
      
      // Get the file associated with this object, if applicable.
      STGMEDIUM   medium;
      FORMATETC   fe = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
      UINT        uCount;

      if(SUCCEEDED(m_pDataObj->GetData(&fe, &medium)))
      {
         // Get the file name from the HDROP.
         uCount = DragQueryFile((HDROP)medium.hGlobal, (UINT)-1, NULL, 0);
         if(uCount)
            DragQueryFile((HDROP)medium.hGlobal, 0, m_szFile, sizeof(m_szFile));

         ReleaseStgMedium(&medium);
      }
   }

   // Duplicate the registry handle. 
   if (hRegKey) 
      RegOpenKeyEx(  hRegKey, 
                     NULL, 
                     0L, 
                     MAXIMUM_ALLOWED, 
                     &m_hRegKey); 
 
   return NOERROR; 
} 

A shell extension handler must implement three functions: an entry point function (often called DllMain or LibMain), DllCanUnloadNow, and DllGetClassObject.

DllCanUnloadNow and DllGetClassObject are essentially the same as they would be for any OLE in-process server DLL. The use of DllCanUnloadNow is shown in the following example:

STDAPI DllCanUnloadNow(void) 
{ 
    // g_cRefThisDll must be placed in the instance-specific 
    // data section. 
    return ResultFromScode((g_cRefThisDll==0) ? S_OK : S_FALSE); 
} 

DllGetClassObject needs to expose the class factory for the object in the DLL. For more information about exposing the class factory, see the OLE documentation included in the Microsoft Platform Software Development Kit (SDK). The following example shows how to expose the class factory.

// DllGetClassObject - a DLL entry point function used by 
// most in-process server DLLs. 
 
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvOut) 
{ 
    *ppvOut = NULL; // assume failure 
 
    if (IsEqualIID(rclsid, CLSID_ShellExtension)) { 
        return CShellExtSample_Create(riid, ppvOut); 
    } else { 
        return CLASS_E_CLASSNOTAVAILABLE; 
    } 
} 

Using Shell Extensions

The following sections describe how to program the handlers supported by the shell. These sections assume you are familiar with the registry, C++, and OLE COM.

Context Menu Handlers

A context menu handler is a shell extension that adds menu items to any of the shell's context menus. There are two types of context menu handlers. They have different purposes but the same implementation. When a user right-clicks a file object, context menu extensions are used; when a user drags a file object with the right mouse button, drag-and-drop handlers are used. This section describes the types of context menu handlers, how they can be used, how to add them to the registry, and the interfaces that they must implement.

Context menu extensions

When the user right-clicks an item in the shell's namespace (a file, directory, server, or work group, for example), the shell creates the default context menu for the type of item. The shell then loads context menu extensions that are registered for the type (and its base type) so that the extensions can add extra menu items. The context menu extensions are registered at the following location:

HKCR\{ProgID}\shellex\ContextMenuHandlers 

The IContextMenu interface

An application implements a context menu handler interface, IContextMenu, to add menu items to the context menu for a file object. The shell displays the object's context menu when the user clicks the object with the right mouse button. The menu items can be either class-specific (applicable to all files of a particular type) or instance-specific (applicable to an individual file).

When the user right-clicks a file object, the system passes the handle to the object's context menu to the context menu handler, which can only add items to the menu. You must not delete or modify existing menu items because other handlers may add items either before or after this one does. In addition, the shell adds items to the menu after all context menu handlers have been called.

You enter a context menu handler in the registry under the shellex key within an application's information area. The ContextMenuHandlers key lists the names of subkeys that contain the CLSID of each context menu handler. An example showing the ContextMenuHandlers key follows.

ContextMenuHandlers 
    {00000000-1111-2222-3333-00000000000001} 

You can register multiple context menu handlers for a file type.

In addition to the standard IUnknown methods, the context menu handler interface uses the QueryContextMenu, InvokeCommand, and GetCommandString methods.

When the shell is about to display a context menu (or the File menu on the menu bar) for a file object, the system calls the context menu handler's QueryContextMenu method. The menu handle to which any menu items should be added is provided in the hMenu parameter. Menu items are inserted by position starting at the indexMenu position. All menu items must be string items (MF_STRING). To avoid command conflicts, the command identifier for each menu item must be between the idCmdFirst and idCmdLast parameters, inclusive. The uFlags parameter contains a flag that indicates what type of operation is being performed.

If successful, QueryContextMenu returns an HRESULT value that contains SEVERITY_SUCCESS in the severity section and the largest menu item identifier plus one in the code section. The shell will use the return value as the idCmdFirst for the next context menu handler's QueryContextMenu, if necessary. Therefore, all of the command identifiers between idCmdFirst and the return value minus one cannot be used for other context menu handlers. A context menu handler should attempt to use sequential command identifiers so that the largest number of identifiers will remain available. The following example shows a typical QueryContextMenu implementation.

STDMETHODIMP CShellExt::QueryContextMenu(HMENU hMenu, UINT indexMenu, 
UINT idCmdFirst, UINT idCmdLast, UINT uFlags) 
{ 
   UINT idCmd = idCmdFirst; 
   char szMenuText1[64]; 
   char szMenuText2[64]; 
   char szMenuText3[64]; 
   char szMenuText4[64]; 
   BOOL bAppendItems=TRUE; 

   if ((uFlags & 0x000F) == CMF_NORMAL) 
   { 
      lstrcpy(szMenuText1, "New menu item 1, Normal File"); 
      lstrcpy(szMenuText2, "New menu item 2, Normal File"); 
      lstrcpy(szMenuText3, "New menu item 3, Normal File"); 
      lstrcpy(szMenuText4, "New menu item 4, Normal File"); 
   } 
   else if (uFlags & CMF_VERBSONLY) 
   { 
      lstrcpy(szMenuText1, "New menu item 1, Shortcut File"); 
      lstrcpy(szMenuText2, "New menu item 2, Shortcut File"); 
      lstrcpy(szMenuText3, "New menu item 3, Shortcut File"); 
      lstrcpy(szMenuText4, "New menu item 4, Shortcut File"); 
   } 
   else if (uFlags & CMF_EXPLORE) 
   { 
      lstrcpy(szMenuText1, "New menu item 1, Normal File right click in Explorer"); 
      lstrcpy(szMenuText2, "New menu item 2, Normal File right click in Explorer"); 
      lstrcpy(szMenuText3, "New menu item 3, Normal File right click in Explorer"); 
      lstrcpy(szMenuText4, "New menu item 4, Normal File right click in Explorer"); 
   } 
   else 
   { 
      bAppendItems = FALSE; 
   } 

   if (bAppendItems) 
   { 
      // Insert a separator
      InsertMenu(hMenu, indexMenu++, MF_SEPARATOR | MF_BYPOSITION, 0, NULL); 
      
      // Insert the menu items
      InsertMenu(hMenu, indexMenu++, MF_STRING | MF_BYPOSITION, idCmd++, szMenuText1); 
      InsertMenu(hMenu, indexMenu++, MF_STRING | MF_BYPOSITION, idCmd++, szMenuText2); 
      InsertMenu(hMenu, indexMenu++, MF_STRING | MF_BYPOSITION, idCmd++, szMenuText3); 
      InsertMenu(hMenu, indexMenu++, MF_STRING | MF_BYPOSITION, idCmd++, szMenuText4); 

      // Must return the greatest menu item identifier plus one. 
      return MAKE_HRESULT(SEVERITY_SUCCESS, 0, idCmd); 
   } 
   
   return NOERROR; 
} 

When the user selects one of the menu items added by a context menu handler, the shell calls the handler's InvokeCommand method to let the handler process the command. If multiple context menu handlers are registered for a file type, the value of the ContextMenuHandlers key determines the order of the commands. The InvokeCommand function in the following example handles the commands associated with the menu items added by the previous example.

 STDMETHODIMP CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) 
{ 
   HRESULT hr = E_INVALIDARG; 

   // If the high-order word of lpcmi->lpVerb is not NULL, this 
   // function was called by an application and lpVerb is a command 
   // that should be activated. Otherwise, the shell has called this 
   // function, and the low-order word of lpcmi->lpVerb is the 
   // identifier of the menu item that the user selected. 
   if (!HIWORD(lpcmi->lpVerb)) 
   { 
      switch (LOWORD(lpcmi->lpVerb)) 
      { 
         case 0: 
            hr = DoMenu1(lpcmi->hwnd, lpcmi->lpDirectory, 
                  lpcmi->lpVerb, lpcmi->lpParameters, lpcmi->nShow); 
            break; 

         case 1: 
            hr = DoMenu2(lpcmi->hwnd, lpcmi->lpDirectory, 
                  lpcmi->lpVerb, lpcmi->lpParameters, lpcmi->nShow); 
            break; 

         case 2: 
            hr = DoMenu3(lpcmi->hwnd, lpcmi->lpDirectory, 
                  lpcmi->lpVerb, lpcmi->lpParameters, lpcmi->nShow); 
            break; 

         case 3: 
            hr = DoMenu4(lpcmi->hwnd, lpcmi->lpDirectory, 
                  lpcmi->lpVerb, lpcmi->lpParameters, lpcmi->nShow); 
            break; 
      } 
   } 
   return hr; 
} 

Windows calls the GetCommandString method to retrieve a language-independent command string or the Help text for a context menu item.

Modifying the Find submenu

The Start menu and Windows Explorer's Tools menu contain a Find submenu that includes all of the registered search applications. If your application includes a search utility, you can add an item under this menu to allow the user to access your search utility without launching or switching to your application. These types of extensions are called Find extensions. There are two types of Find extensions—static and dynamic.

Static Find extensions

A static Find extension will only be loaded when the user selects one of the Find commands. This will slow the Find submenu creation and the execution of the command. The static method is best if your extension DLL is small and can load quickly. You add a static Find extension by making a registry entry under:

HKEY_LOCAL_MACHINE
   Software
      Microsoft
         Windows
            CurrentVersion
               Explorer
                  FindExtensions
                     Static

You create a unique key under this key. The key name is not significant as long as it is unique among the other key names at the same level. The value for this key is a string that contains the class identifier of the context menu handler. Under this key, you create one or more command keys. The command key name corresponds to the command identifier for the menu item. For example, a name of "0" (zero) corresponds to a command identifier of 0. More than one command key can be created, but the commands must start at 0 and be sequential. The value for each command key is a string that contains the text that will be displayed in the menu. This text can also contain an "&" character in front of the desired accelerator character.

The Start menu's Find submenu can display small icons next to the command string for the Find command. The icon that is displayed is entered in the registry under your static Find extension's command key (the numeric entry described in the previous paragraph) by inserting a key under the command key named "DefaultIcon". The default string value for this key is the path and file name of the module containing the icon, followed by a comma and then a number the represents the zero-based index of the icon in the module. The following example shows a static Find extension that registers one command and an associated icon.

HKEY_LOCAL_MACHINE
   Software
      Microsoft
         Windows
            CurrentVersion
               Explorer
                  FindExtensions
                     Static
                        MyFind (default value = "<clsid>")
                           0 (default value = "&My Find Utility")
                              DefaultIcon (default value = "c:\MyFind.dll,0")

When the user selects Find from the Tools menu, the browser enumerates the keys under the FindExtensions\Static key and adds the registered menu items. When the user selects one of the Find commands, the browser loads the context menu handler specified by the class identifier and calls IContextMenu::InvokeCommand. The low-order word of the lpVerb member of the CMINVOKECOMMANDINFO structure passed to InvokeCommand contains the numeric representation of the command key name. In response to the command, the context menu handler launches the find utility.

Dynamic Find extensions

A dynamic Find extension is loaded when the shell loads at startup, and it is kept loaded until the shell exits. This can slow the load time of the shell, but the responsiveness of the Find submenu creation and the execution of the command will be increased. The dynamic method is best if your extension DLL may take a long time to load or if it is likely to be loaded and unloaded often. You add a dynamic Find extension by creating a registry entry under:

HKEY_LOCAL_MACHINE
   Software
      Microsoft
         Windows
            CurrentVersion
               Explorer
                  FindExtensions

You create a unique key under this key. The key name is not significant as long as it is unique among the other key names at the same level. The default value for this key is a string that contains the class identifier of the context menu handler. The following example shows a properly registered dynamic Find extension.

HKEY_LOCAL_MACHINE
   Software
      Microsoft
         Windows
            CurrentVersion
               Explorer
                  FindExtensions
                     MyFind (default value = "<clsid>")

The loading of a dynamic Find extension occurs in the same manner as a file-based context menu extension except that the pidlFolder and lpdobj arguments in the extension's IShellExtInit::Initialize will be NULL. When the menu is built, the extension is loaded and initialized and the extension's IContextMenu::QueryContextMenu is called to add the item(s) to the Find menu. When the user selects an item, the proper extension's IContextMenu::InvokeCommand is called with the proper menu identifier. In response to the command, the context menu handler launches the Find utility.

Menu icons are not supported for dynamic Find extensions.

Drag-and-Drop Handlers

Drag-and-drop handlers implement the IContextMenu interface. In fact, a drag-and-drop handler is simply a context menu handler that affects the menu the shell displays when a user drags a file object with the right mouse button. Because this menu is called the Drag-and-Drop menu, shell extensions that add items to this menu are called drag-and-drop handlers. Drag-and-drop handlers work the same way as context menu handlers.

Note that drag-and-drop handlers are registered under the folder type key (typically the Directory key). To change the behavior of the dragged object (IDataObject), you need to implement a data handler.

Icon Handlers

An application can customize the icons that the shell displays for the application's file types. The icon displayed for folders cannot be modified.

An application can specify icons for its file types in two ways. The simplest way is to specify a class icon to be used for all files of a particular file type by adding a DefaultIcon key to the registry under the program information. For information about specifying a class icon, see Setting Default Icons for File Classes.

An application can also use the %1 value with the DefaultIcon key. This value denotes that each file instance of this type can have a different icon. The application must supply an icon handler for the file type and add an IconHandler key to the shellex key for the application. An application can have only one entry for the IconHandler key, and the value of its key denotes the CLSID of the icon handler.

shellex 
    IconHandler 
        {00000000-1111-2222-3333-00000000000003} 
DefaultIcon = %1 

To have customized icons, an application must provide an icon handler that implements the IExtractIcon interface. The IExtractIcon interface has the IExtractIcon::GetIconLocation and IExtractIcon::Extract methods in addition to the usual IUnknown methods.

The system follows these steps when it is about to display an icon for a file type that has instance-specific icons:

  1. Retrieves the class identifier of the handler.
  2. Creates a handler object by calling the CoCreateInstance function with the CLSID.
  3. Initializes the instance by calling the IPersistFile::Load method.
  4. Uses the QueryInterface method to retrieve the IExtractIcon interface.
  5. Calls the GetIconLocation and Extract methods.

The system calls the GetIconLocation method to get the location and index of an icon to display. Typically, the icon location is an executable or DLL file name, but it can be any file.

The system calls the Extract method when it needs to display an icon for a file that does not reside in an executable or DLL file. Applications usually have the file icons in their executable or DLL files, so icon handlers can simply implement this method as a return-only function that returns the E_FAIL error value. You only need to implement the Extract method if the icon image is stored in a file in an application-defined format. When the icon for a file is in a separate .ico file (or any other type of file), the icon handler must extract the icon for the shell and return it in this method.

Property Sheet Handlers

Another way the shell can be extended is with custom property sheets. When the user selects the properties for a file, the shell displays a standard property sheet. If the registered file type has a property sheet handler, the shell allows the user to access additional sheets that the handler provides. Property sheet handlers implement the IShellPropSheetExt interface.

Property sheet handlers are entered in the registry under the shellex key within an application's information area. The PropertySheetHandlers key contains subkeys that contain the class identifier of each context menu handler, as shown in the following example:

PropertySheetHandlers 
    {00000000-1111-2222-3333-00000000000002} 

You can register multiple property sheet handlers for a file type. In this case, the order of the subkey names in the PropertySheetHandlers key determines the order of the additional property sheets. You can use a maximum of 24 (the value of MAXPROPPAGES) pages in a property sheet.

Adding property sheet pages

The property sheet handler implements the AddPages method in addition to the usual IUnknown methods. The system calls the AddPages method when a property sheet is about to be displayed. The system calls each property sheet handler registered to the file type to allow the handlers to add pages to the property sheets. The following example shows a typical implementation of the AddPages method:

STDMETHODIMP CSamplePageExt::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam) 
{ 
   PROPSHEETPAGE  psp; 
   HPROPSHEETPAGE hpage; 

   // Size of the structure.
   psp.dwSize = sizeof(psp);

   // Flags.
   psp.dwFlags = PSP_USEREFPARENT; 
   
   // Instance handle to the DLL that contains the property page dialog resource.
   psp.hInstance = (HINSTANCE)g_hinstDll;
   
   // Identifier of the property page dialog resource.
   psp.pszTemplate = MAKEINTRESOURCE(IDD_PAGE); 
   
   // Address of the dialog procedure.
   psp.pfnDlgProc = DlgProc; 
   
   /* 
   Allow the shell access to the reference count. This prevents the 
   DLL from being unloaded while the property page still exists. 
   */
   psp.pcRefParent = &g_cRefThisDll; 
   
   
   // Application-defined data.
   psp.lParam = (LPARAM)this; 

   hpage = CreatePropertySheetPage(&psp); 
   if (hpage) 
   { 
      if (!lpfnAddPage(hpage, lParam)) 
         DestroyPropertySheetPage(hpage); 
   } 
   return NOERROR; 
} 

Replacing Control Panel pages

The ReplacePage method is called only by Control Panel applications. It allows you to replace the property sheet of a standard Control Panel application with a custom page. For example, if a mouse manufacturer adds extra buttons to its mouse, the manufacturer can replace the Buttons page in the Control Panel's Mouse Properties property sheet. The ReplacePage method is not called by the shell because the shell does not have any property pages that can be replaced by a shell extension. Currently, only Control Panel applications call this method, but other property sheet suppliers could use it to allow their property pages to be replaced.

Each property sheet handler that allows a property page to be replaced must specify the registry location where other handlers that replace pages register themselves. For standard Control Panel applications, this location is defined by the REGSTR_PATH_CONTROLSFOLDER macro in the Regstr.h file. The macro defines the key under the HKEY_LOCAL_MACHINE key in which all Control Panel property page replacement handlers must register. For example, a property sheet handler that needs to replace a property page in the Mouse Properties Control Panel would register a property sheet extension handler in the following registry location:

HKEY_LOCAL_MACHINE 
  REGSTR_PATH_CONTROLSFOLDER 
    Mouse 
      shellex 
        PropertySheetHandlers = NewMousePage 
            NewMousePage = {00000000-1111-2222-3333-00000000000002} 

In addition, a property sheet handler that allows replaceable pages must define identifiers for each page that can be replaced.

Standard Control Panel applications define this location in the Regstr.h and Cplext.h header files. In Regstr.h, the REGSTR_PATH_CONTROLSFOLDER macro defines the key under the HKEY_LOCAL_MACHINE key in which all Control Panel property sheet page replacement handlers must register. Cplext.h defines the subkey for each Control Panel application that contains a replaceable property page: Mouse for a Mouse Properties Control Panel application and Keyboard for a Keyboard Properties Control Panel application.

Standard Control Panel applications define replaceable page identifiers in Cplext.h. For example, CPLPAGE_MOUSE_BUTTONS is the identifier for the Buttons page in the Mouse Properties property sheet, and CPLPAGE_KEYBOARD_SPEED is the identifier for the Speed page in the Keyboard Properties property sheet.

Copy Hook Handlers

A copy hook handler is a shell extension that the shell calls before copying, moving, deleting, or renaming a folder object. The copy hook handler does not perform the task itself; it only approves the task. When the shell receives approval from the copy hook handler, the shell performs the actual file system operation (copies, moves, deletes, or renames). Copy hook handlers are not informed about the success of an operation, so they cannot monitor actions that occur on folder objects.

The shell initializes the copy hook handler interface directly, without using an IShellExtInit or IPersistFile interface first. A folder object can have multiple copy hook handlers. The copy hook handler interface has one method—CopyCallback—in addition to the standard IUnknown methods.

The shell calls the CopyCallback method before it copies, moves, deletes, or renames a folder object. The method returns an integer value that indicates whether the shell should perform the operation. The shell will call each copy hook handler registered for a folder object until either all the handlers have been called or any handler returns the IDCANCEL value. The handler can also return the IDYES value to specify that the operation should be carried out or the IDNO value to specify that the operation should not be performed.

Data Handlers

When a file is dragged from the shell (or copied to the clipboard from the shell), the shell creates a default IDataObject interface that supports standard clipboard formats (CF_HDROP, "Shell IDList Array", and so on). An application can add more clipboard formats by providing a data handler for the file type. A data handler must support both the IPersistFile and IDataObject interfaces. The shell initializes a data handler by calling the IPersistFile::Load method. When a data handler is provided, the default IDataObject interface delegates some method calls to the data handler so that the additional clipboard data formats become available to the drop target.

You register a data handler by adding a DataHandler key and class identifier for the handler under the shellex key for the file type as shown in the following example:

shellex 
    DataHandler = {00000000-1111-2222-3333-00000000000003} 

Drop Handlers

By default, a file is not a drop target. However, by providing a drop handler for the file types created by your application, you can make the files into drop targets. A drop handler must support both the IPersistFile and IDropTarget interfaces. The shell initializes a drop handler by calling the IPersistFile::Load method. When the user drags an object over one of your application's files or drops an object onto one of its files, the system calls the appropriate methods of the IDropTarget interface.

You register a drop handler by adding a DropHandler key and class identifier for the handler under the shellex key for the file type as shown in the following example:

shellex 
    DropHandler = {00000000-1111-2222-3333-00000000000003} 

Up Top of Page
© 1997 Microsoft Corporation. All rights reserved. Terms of Use.