Shell

Shell Namespace

디버그정 2008. 9. 13. 08:32

Shell Namespace


A namespace is a collection of symbols, such as database keys or file and directory names. The shell uses a single hierarchical namespace to organize all objects of interest to the user, including files, storage devices, printers, network resources, and anything else that can be viewed using Microsoft® Windows® Explorer. The root of this unified namespace is the desktop.

In many ways, the shell namespace is analogous to a file system's directory structure. However, the namespace contains more types of objects than just files and directories. This overview discusses some of these associations, outlines the organization of the shell namespace, and describes the functions and interfaces associated with the namespace.

arrowy.gifFolders and File Objects

arrowy.gifItem Identifiers and Pointers to Item Identifier Lists

arrowy.gifFolder Locations

arrowy.gifItem Enumeration

arrowy.gifDisplay Names

arrowy.gifObject Attributes and Interfaces

arrowy.gifUsing the Shell Namespace

arrowy.gifExtending the Shell Namespace

arrowy.gifShell Namespace Reference

Folders and File Objects

A folder is a collection of items in the shell namespace. A folder is analogous to a file system directory, and many folders are, in fact, directories. However, there are also other types of folders, such as remote computers, storage devices, the Desktop folder, the Control Panel, the Printers folder, and the Fonts folder. A folder can contain other folders as well as items called file objects. Examples of file objects are Control Panel applications, printers, and actual files. Each type of folder can only contain certain kinds of file objects; for example, a Control Panel application can only exist in the Control Panel folder. It cannot exist in a file system directory.

Because there are many kinds of folders and file objects, each folder is an OLE component object model (COM) object that can enumerate its contents and carry out other actions. More precisely, each folder implements the IShellFolder interface. Retrieving the IShellFolder object for a shell folder is referred to as binding to the folder. When an object's IShellFolder interface is obtained, the interface must eventually be freed by calling its Release method.

You can bind to the desktop folder (retrieve the folder's IShellFolder interface) by using the SHGetDesktopFolder function. You can enumerate subfolders by using the IShellFolder::EnumObjects method. You can bind to a subfolder of any given folder by using the IShellFolder::BindToObject method. Using these three functions, an application can navigate throughout the entire shell namespace.

Item Identifiers and Pointers to Item Identifier Lists

Objects in the shell namespace are assigned item identifiers and item identifier lists. An item identifier uniquely identifies an item within its parent folder. An item identifier list uniquely identifies an item within the shell namespace by tracing a path to the item from a known point—usually the desktop. A pointer to an item identifier list (PIDL) is used throughout the shell to identify an item.

Item identifiers and PIDLs are much like the file names and paths, respectively, used in a file system. However, they share this important difference: item identifiers and PIDLs are binary data structures that never appear to the user. Item names that can be shown to the user (called display names) are described in Display Names.

An item identifier is defined by the variable-length SHITEMID structure. The first two bytes specify the identifier's size, and the format of the remaining bytes depends on the software that implements the parent folder's IShellFolder interface. Except for the first two bytes, item identifiers are not strictly defined, and you should make no assumptions about their format when designing applications. To determine whether two item identifiers are equal, your application can use the IShellFolder::CompareIDs method.

The ITEMIDLIST structure defines an element in an item identifier list (the only member of this structure is an SHITEMID structure). An item identifier list consists of one or more consecutive byte-aligned ITEMIDLIST structures, followed by a 16-bit zero value. An application can traverse a list of item identifiers by examining the size specified in each SHITEMID structure and stopping when it finds a size of zero.

Item identifier lists are almost always allocated using the shell's allocator (an IMalloc interface that you can retrieve using the SHGetMalloc function). For example, some shell functions create an item identifier list and return a PIDL to it. In such cases, it is usually your responsibility to make sure the application frees the PIDL that is using the shell's allocator. Note that the SHGetMalloc function retrieves the task allocator for OLE applications.

Folder Locations

Many folders have special meanings to the shell. An application can use shell functions to retrieve the locations of these special folders and to enable the user to browse for specific folders.

Some special folders are virtual folders—so called because they are not actual directories on any storage device, local or remote. Virtual folders like the Desktop folder, the My Computer folder, and the Network Neighborhood folder make a unified namespace possible by serving as containers for any number of storage devices and network resources. Other virtual folders contain file objects, such as the Control Panel and Printers, that are not part of the file system.

You can use the SHGetSpecialFolderLocation function to retrieve the location of a special folder, which can be virtual or part of the file system. The function returns a PIDL, which the application must eventually free using the shell's allocator (see SHGetMalloc). If the folder is part of the file system, you can convert the PIDL to a file system path using the SHGetPathFromIDList function. For a list of special folders, see the description of the SHGetSpecialFolderLocation function.

To display a dialog box that enables the user to browse for a folder, you can use the SHBrowseForFolder function. Your application can use this function to prompt the user for a directory or remote computer. This function can also be used to browse for network printers, though printers are not considered folders. The application can specify the root folder in which to browse. For example, to prompt the user for a program group, you could call SHBrowseForFolder, specifying the PIDL for the Programs folder as the root.

Item Enumeration

A folder's IShellFolder interface can be used to determine the folder's contents by using the IShellFolder::EnumObjects method. This method creates an item enumeration object, which is a set of item identifiers that can be retrieved by using the IEnumIDList interface.

One or more item identifiers can be retrieved from the enumeration object by using the IEnumIDList::Next method. Calling this function repeatedly allows an application to retrieve all of the item identifiers, one or more at a time. Using other methods, you can skip items in the sequence, return to the beginning of the sequence, or make a copy of the enumeration object to save its state.

When you are finished using the enumeration object, you must free it by calling the Release method.

Display Names

Because item identifiers are binary data structures, each item in a shell folder also has a display name, which is a string that can be shown to the user. You can use methods in the IShellFolder interface to retrieve an item's display name, to find an item with a specified display name, or to change an item's display name.

The IShellFolder::GetDisplayNameOf method can be used to retrieve a display name. The actual string returned depends on the type of display name specified. Values identifying the different types of display names are defined by the SHGNO enumeration.

There is a special type of display name that can be converted back to an item identifier by using the IShellFolder::ParseDisplayName method. You might use this type of display name as a parameter to the ShellExecute function or as a command-line argument for an application.

The IShellFolder::SetNameOf method can be used to change the display name of a file object or subfolder. Changing an item's display name also changes its item identifier, so the function returns a PIDL containing the new item identifier.

The SHGNO enumeration consists of two sets of values. The first set is contained in bits 0 through 7 and specifies if the name is relative to this folder or if the name is a full name. The second set is contained in bits 8 through 15 and is a modifier to the first set that specifies what the name will be used for. Here is an example of how an IShellFolder::GetDisplayNameOf implementation might handle this enumeration:

#define GET_SHGDN_FOR(dwFlags)         ((DWORD)dwFlags & (DWORD)0x0000FF00)
#define GET_SHGDN_RELATION(dwFlags)    ((DWORD)dwFlags & (DWORD)0x000000FF)

STDMETHODIMP CShellFolder::GetDisplayNameOf( LPCITEMIDLIST pidl, 
                                             DWORD dwFlags, 
                                             LPSTRRET lpName)
{
switch(GET_SHGDN_FOR(dwFlags))
   {
   case SHGDN_FORPARSING:
      switch(GET_SHGDN_RELATION(dwFlags))
         {
         case SHGDN_NORMAL:
            //get the full name
            GetParsingFullName(pidl, lpName->cStr, sizeof(lpName->cStr));
            break;

         case SHGDN_INFOLDER:
            //get the relative name
            GetParsingRelativeName(pidl, szText, sizeof(szText));
            break;
            
         default:
            return E_INVALIDARG;
         }
      break;

   case SHGDN_FORADDRESSBAR:
      switch(GET_SHGDN_RELATION(dwFlags))
         {
         case SHGDN_NORMAL:
            //get the full name
            GetAddressBarFullName(pidl, lpName->cStr, sizeof(lpName->cStr));
            break;

         case SHGDN_INFOLDER:
            //get the relative name
            GetAddressBarRelativeName(pidl, lpName->cStr, sizeof(lpName->cStr));
            break;
            
         default:
            return E_INVALIDARG;
         }
      break;

   case SHGDN_NORMAL:
      switch(GET_SHGDN_RELATION(dwFlags))
         {
         case SHGDN_NORMAL:
            //get the full name
            GetNormalFullName(pidl, lpName->cStr, sizeof(lpName->cStr));
            break;

         case SHGDN_INFOLDER:
            //get the relative name
            GetNormalRelativeName(pidl, lpName->cStr, sizeof(lpName->cStr));
            break;
            
         default:
            return E_INVALIDARG;
         }
      break;
   }

lpName->uType = STRRET_CSTR;

return S_OK;
}

Object Attributes and Interfaces

Every file object and folder has attributes that determine, among other things, what actions can be carried out on it. Your application can obtain the attributes of any file object or folder and can retrieve interfaces for items in a shell folder.

To obtain the attributes of a file object or folder, your application can use the IShellFolder::GetAttributesOf method. Information obtained by this method includes capabilities (such as whether a file object can be deleted or can be a drop target), display attributes (such as whether a folder is shared), contents flags (such as whether a folder has subfolders), and other attributes (such as whether an object is a folder, whether it is part of the file system, and so on). For a list of attributes, see the description of the IShellFolder::GetAttributesOf method.

Your application can retrieve interfaces that carry out actions on a file object or folder by using the IShellFolder::GetUIObjectOf method. This method can be used to retrieve the IContextMenu, IExtractIcon, IDataObject, or IDropTarget interface for the object.

Using the Shell Namespace

This section contains examples that demonstrate the functions and interfaces associated with the shell namespace. The code samples given here are for demonstration purposes only. If you wish to use them in your application, you must enhance them to perform proper error and parameter checking.

Using PIDLs and Display Names

This section presents an example illustrating how to retrieve the location of a special folder, traverse an item identifier list, and use the IShellFolder interface to retrieve display names. The example is a console application that prints the display names of the folders a user would have to open to get to the Programs folder. To display them, the application would carry out these steps:

  1. Retrieve the PIDL for the Programs folder by using the SHGetSpecialFolderLocation function.
  2. Bind to the Desktop folder (retrieve the folder's IShellFolder interface) by using the SHGetDesktopFolder function.
  3. Traverse the item identifier list and process elements as follows: print the subfolder's display name, bind to the subfolder, and release the parent folder's IShellFolder interface.

Before carrying out any of the preceding steps, your application should retrieve a pointer to the shell's IMalloc interface (which is saved in the g_pMalloc global variable) by using the SHGetMalloc function.

The following example shows the application's main function. This function carries out all of the steps described previously using the application-defined GetNextItemID and CopyItemID functions to traverse the item identifier list and the application-defined PrintStrRet function to print the display names. Code for these application-defined functions is shown following the code for the main function.

#include <conio.h>
#include <stdio.h>
#include <shlobj.h>

// Global pointer to the shell's IMalloc interface. 
LPMALLOC g_pMalloc; 

// main - the application's entry-point function. 
int __cdecl main() 
   { 
   LPITEMIDLIST pidlPrograms; 
   LPSHELLFOLDER pFolder; 

   // Get the shell's allocator. 
   if (!SUCCEEDED(SHGetMalloc(&g_pMalloc))) 
      return 1; 

   // Get the PIDL for the Programs folder. 
   if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_PROGRAMS, &pidlPrograms))) 
      { 
      // Start with the Desktop folder. 
      if (SUCCEEDED(SHGetDesktopFolder(&pFolder))) 
         { 
         LPITEMIDLIST pidl; 
    
         // Process each item identifier in the list. 
         for (pidl = pidlPrograms; pidl != NULL; pidl = GetNextItemID(pidl)) 
            { 
            STRRET sName; 
            LPSHELLFOLDER pSubFolder; 
            LPITEMIDLIST pidlCopy; 

            // Copy the item identifier to a list by itself. 
            if ((pidlCopy = CopyItemID(pidl)) == NULL) 
               break; 
    
            // Display the name of the subfolder. 
            if (SUCCEEDED(pFolder->lpVtbl->GetDisplayNameOf(   pFolder, 
                                                               pidlCopy, 
                                                               SHGDN_INFOLDER, 
                                                               &sName))) 
               PrintStrRet(pidlCopy, &sName); 
    
            // Bind to the subfolder. 
            if (!SUCCEEDED(pFolder->lpVtbl->BindToObject(   pFolder, 
                                                            pidlCopy, 
                                                            NULL, 
                                                            &IID_IShellFolder, 
                                                            &pSubFolder))) 
               { 
               g_pMalloc->lpVtbl->Free(g_pMalloc, pidlCopy); 
               break; 
               } 
    
            // Free the copy of the item identifier. 
            g_pMalloc->lpVtbl->Free(g_pMalloc, pidlCopy); 
    
            // Release the parent folder and point to the 
            // subfolder. 
            pFolder->lpVtbl->Release(pFolder); 
            pFolder = pSubFolder; 
            } 
    
         // Release the last folder that was bound to. 
         if (pFolder != NULL) 
            pFolder->lpVtbl->Release(pFolder); 
         } 
    
      // Free the PIDL for the Programs folder. 
      g_pMalloc->lpVtbl->Free(g_pMalloc, pidlPrograms); 
      } 
    
   // Release the shell's allocator. 
   g_pMalloc->lpVtbl->Release(g_pMalloc); 

   return 0; 
   } 

Following is the GetNextItemID function. Given a pointer to an element in an item identifier list, this function returns a pointer to the next element (or NULL if there are no more elements). The main function calls this function to traverse the item identifier list for the Programs folder.

// GetNextItemID - points to the next element in an item identifier list. 
// Returns a PIDL if successful, or NULL if at the end of the list. 
// pidl - previous element
LPITEMIDLIST GetNextItemID(LPITEMIDLIST pidl) 
   { 
   // Get the size of the specified item identifier. 
   int cb = pidl->mkid.cb; 

   // If the size is zero, it is the end of the list. 
   if (cb == 0) 
      return NULL; 

   // Add cb to pidl (casting to increment by bytes). 
   pidl = (LPITEMIDLIST) (((LPBYTE) pidl) + cb); 

   // Return NULL if it is null-terminating, or a pidl otherwise. 
   return (pidl->mkid.cb == 0) ? NULL : pidl; 
   } 

Following is the CopyItemID function. Given a pointer to an element in an item identifier list, this function allocates a new list containing only the specified element followed by a terminating zero. The main function uses this function to create single-element PIDLs, which it passes to IShellFolder interface methods.

// CopyItemID - creates an item identifier list containing the first 
//     item identifier in the specified list. 
// Returns a PIDL if successful, or NULL if out of memory. 
LPITEMIDLIST CopyItemID(LPITEMIDLIST pidl) 
   { 
   // Get the size of the specified item identifier. 
   int cb = pidl->mkid.cb; 

   // Allocate a new item identifier list. 
   LPITEMIDLIST pidlNew = (LPITEMIDLIST) 
   g_pMalloc->lpVtbl->Alloc(g_pMalloc, cb + sizeof(USHORT)); 
   if (pidlNew == NULL) 
      return NULL; 

   // Copy the specified item identifier. 
   CopyMemory(pidlNew, pidl, cb); 

   // Append a terminating zero. 
   *((USHORT *) (((LPBYTE) pidlNew) + cb)) = 0; 

   return pidlNew; 
   } 

The IShellFolder::GetDisplayNameOf method returns a display name in a STRRET structure. The display name may be returned in one of three ways, which is specified by the uType member of the STRRET structure. The main function calls the following PrintStrRet function to print the display name.

// PrintStrRet - prints the contents of a STRRET structure. 
// pidl - PIDL containing the display name if lpStr->uType is STRRET_OFFSET. 
// lpStr - address of the STRRET structure. 
void PrintStrRet(LPITEMIDLIST pidl, LPSTRRET lpStr) 
   { 
   LPSTR lpsz; 
   int cch; 
    
   switch (lpStr->uType) 
      { 
      case STRRET_WSTR: 
         cch = WideCharToMultiByte( CP_ACP, 
                                    0, 
                                    lpStr->pOleStr, 
                                    -1, 
                                    NULL, 
                                    0, 
                                    NULL, 
                                    NULL); 
         lpsz = (LPSTR) g_pMalloc->lpVtbl->Alloc(g_pMalloc, cch); 
         if (lpsz != NULL) 
            { 
            WideCharToMultiByte( CP_ACP, 
                                 0, 
                                 lpStr->pOleStr, 
                                 -1, 
                                 lpsz, 
                                 cch, 
                                 NULL, 
                                 NULL); 
            printf("%s\n", lpsz); 
            g_pMalloc->lpVtbl->Free(g_pMalloc, lpsz); 
            } 
         break; 
    
      case STRRET_OFFSET: 
         printf("%s\n", ((char *) pidl) + lpStr->uOffset); 
         break; 
    
      case STRRET_CSTR: 
         printf("%s\n", lpStr->cStr); 
         break; 
      } 
   } 

Browsing for Folders

The following example uses the SHBrowseForFolder function to prompt the user for a program group. The Programs directory is specified as the root.

// Main_OnBrowse - browses for a program folder. 
// hwnd - handle to the application's main window. 
// 
// Uses the global variable g_pMalloc, which is assumed to point 
//     to the shell's IMalloc interface. 
void Main_OnBrowse(HWND hwnd) 
{ 
    BROWSEINFO bi; 
    LPSTR lpBuffer; 
    LPITEMIDLIST pidlPrograms;  // PIDL for Programs folder 
    LPITEMIDLIST pidlBrowse;    // PIDL selected by user 
 
    // Allocate a buffer to receive browse information. 
    if ((lpBuffer = (LPSTR) g_pMalloc->lpVtbl->Alloc( 
            g_pMalloc, MAX_PATH)) == NULL) 
        return; 
 
    // Get the PIDL for the Programs folder. 
    if (!SUCCEEDED(SHGetSpecialFolderLocation( 
            hwnd, CSIDL_PROGRAMS, &pidlPrograms))) { 
        g_pMalloc->lpVtbl->Free(g_pMalloc, lpBuffer); 
        return; 
    } 
 
    // Fill in the BROWSEINFO structure. 
    bi.hwndOwner = hwnd; 
    bi.pidlRoot = pidlPrograms; 
    bi.pszDisplayName = lpBuffer; 
    bi.lpszTitle = "Choose a Program Group"; 
    bi.ulFlags = 0; 
    bi.lpfn = NULL; 
    bi.lParam = 0; 
 
    // Browse for a folder and return its PIDL. 
    pidlBrowse = SHBrowseForFolder(&bi); 
    if (pidlBrowse != NULL) { 
 
        // Show the display name, title, and file system path. 
        MessageBox(hwnd, lpBuffer, "Display name", MB_OK); 
        if (SHGetPathFromIDList(pidlBrowse, lpBuffer)) 
            SetWindowText(hwnd, lpBuffer); 
 
        // Free the PIDL returned by SHBrowseForFolder. 
        g_pMalloc->lpVtbl->Free(g_pMalloc, pidlBrowse); 
    } 
 
    // Clean up. 
    g_pMalloc->lpVtbl->Free(g_pMalloc, pidlPrograms); 
    g_pMalloc->lpVtbl->Free(g_pMalloc, lpBuffer); 
} 

Extending the Shell Namespace

A namespace extension provides a way for you to define a new object that a browser, such as Windows Explorer, can view. The code you provide and the registry entries you make define the icon images and text that the user sees while viewing your data. It also defines the menus, toolbars, and status information that apply to your data objects.

One of the reasons it is relatively easy to extend the shell namespace is that Windows Explorer can be viewed as two independent parts: the browser code and the system namespace provider code. Because these parts were written polymorphically and communicate by using COM-based interfaces, they do not depend on each other's implementation. Thus, anyone can provide either browser code that browses the system namespace or a system namespace extension that can be browsed using Windows Explorer.

Your extension has to provide Windows Explorer with the icons, names, and details that you want to represent the items in your namespace. It can provide Explorer with a custom context menu for your data as well as drag-and-drop capabilities. It is important to remember that only you know the contents of the items in your namespace. Therefore, Explorer cannot make any assumptions about these objects and can only display the information that you provide.

Structuring a Namespace Extension

As is the case with shell extensions, a namespace extension is implemented as a COM in-process server DLL. To implement even the simplest extensions, you should have experience with COM and OLE, and you should be knowledgeable about the behavior of Windows Explorer. You must also properly register your extension, or it will not work.

Remember that the construction of an in-process server DLL requires the implementation of a DLL that exports the following functions:

The DLL also implements an object that exposes the IClassFactory interface for the creation of the other objects contained in the DLL. Those objects will expose the IUnknown interface and any other interfaces necessary to implement a namespace extension, including IPersistFolder, IShellFolder, IEnumIDList, and IShellView. These interfaces allow Windows Explorer and your extension to interact.

It is important to remember that Windows Explorer communicates with your extension by using the IShellFolder and IShellView interfaces you have implemented in your extension. The IShellBrowser interface (implemented by Explorer), allows your extension to communicate with Explorer. This is very similar to site-type interfaces commonly found in OLE hosting situations.

Beyond these required interfaces, your extension will need to implement other interfaces that will be created by the IShellFolder object. These include IExtractIcon to provide icons, IContextMenu to provide context menus for your items, drag-and-drop interfaces ( IDropSource and IDropTarget), and IDataObject for data transfer.

Studying examples of in-process server DLLs, especially any that contain namespace or shell extensions, is helpful in learning to build namespace extension DLLs. The RegView sample included with the Internet Client SDK is a good example of a namespace extension.

Identifying Items in the Namespace

One of the operations that must be handled when you are extending the shell namespace is the enumeration of items. A call to the IShellFolder::EnumObjects method returns an IEnumIDList interface that will return a set of identifiers for the items within a specific folder. Called a shell item IDLIST, it is an array of bytes that contains enough data to identify items by the parent folder. Only the first two bytes are defined, and they contain the size of the identifier list. The rest of the identifier list contains data that is specific to the folder implementation. This data has no meaning to the caller.

A shell item IDLIST must contain sufficient information to identify an object within a folder, but it may also contain additional information for efficient manipulation (such as sorting or retrieving display names). You have the option to store additional information in the shell IDLIST because Windows Explorer does not compare two item identifiers directly for either sorting or identification. Instead it uses IShellFolder::CompareIDs to perform this task.

Nonrooted and Rooted Explorers

Your namespace extension can be implemented in one of two ways: you can implement your extension so the user can view it using the standard Windows Explorer, or you can create a completely separate namespace. There are no set criteria for determining which to use; you should evaluate which is more logical and better suited for your particular application.

If you choose to implement your extension so the user can browse into it using the standard Explorer, your new namespace is presented as a subnamespace to the system namespace already present. Since the Desktop is the root folder of the system's namespace, it also serves this purpose for your extension. Accordingly, your extension resides within the existing hierarchy of objects on the desktop and appears to the user as just another item in the system namespace.

If you analyze your application and determine that implementing a completely separate namespace makes more sense, the user will not be able to view it without running a special instance of Windows Explorer rooted in an item of your choosing. The rooted Explorer's top level is referred to as a junction point. The junction point can be a file or a folder, but if the extension uses any junction point, it is, by definition, rooted. This is because Explorer does not support browsing directly into files.

As noted earlier, you should decide whether to implement your extension as rooted or nonrooted based on which implementation will work best for your application. There are no set rules. If your extension logically blends into the existing hierarchy of objects, a nonrooted Explorer might be best. If not, it will probably be better to implement a rooted Explorer with a specific file as your entry point to the new namespace.

Creating a junction point

Creating a junction point can be done in several different ways, depending on the item you choose for the junction point, such as a file or directory. For example, to make a junction point in either the Desktop or the My Computer folder, add the following key to the registry:

HKEY_LOCAL_MACHINE
    SOFTWARE
        Microsoft
          Windows
            CurrentVersion
               Explorer
                  [MyComputer or Desktop]
                       {CLSID}

You can also use a directory as your junction point. If your operating system supports long file names, you can use the CLSID of your namespace extension as the file extension of a folder (MyFolder.{20D....}. Otherwise, you can create a directory, change its file attributes to read-only, and place a file called Desktop.ini in it. This simple text file is made up of the following:

[.ShellClassInfo]

CLSID={clsid}

Opening a rooted Windows Explorer

To open a rooted Windows Explorer for the namespace you have created, you must provide a way to start the new instance of Explorer.exe using the /root switch on the command line. There are several ways to accomplish this. For example, you can call ShellExecute directly, or you can create a shortcut file that contains one of the following as a command line.

  • If the junction point is an item under the desktop:
    explorer.exe /e,/root,::{CLSID of item}
    
  • If the junction point is an item under My Computer:
    explorer.exe /e,/root,,::{20D04FE0 - 3AEA - 1069 - A2D8 - 08002B30309D}
               \::{CLSID of item}}
    
  • If the junction point is a file system folder:
    explorer.exe /e,/root, [path to a junction point]
    

When Windows Explorer is opened using the /root::{CLSID} option, it sets the junction point object as the root of the hierarchy and calls the IShellFolder interface.

When the user opens a junction point object or one of its subfolders, Windows Explorer causes the extension to create a view object by calling the IShellFolder::CreateViewObject method and requesting an IShellView interface. Explorer then calls the IShellView::CreateViewWindow method to cause the extension to create the view window of the folder. One of the parameters passed is a pointer to the IShellBrowser interface, which allows the extension to communicate with Explorer. The view object is able to do any combination of the following: add menu items to the menu bar, add toolbar buttons, display status information on the status bar, and process shortcut keys.

UI Negotiation (Menu, Toolbar, and Status Bar)

The mechanism to determine which items will appear in the view window while the contents are visible is similar to OLE in-place activation, but notable differences do exist. Three of them are discussed here.

First, the view window always exists, even if it does not have the input focus. Therefore, it should maintain three states:

  • Deactivated
  • Activated with the focus
  • Activated without the focus

The view window may present different sets of menu items depending on the focus state. Windows Explorer notifies the extension of state changes by calling the IShellView::UIActivate method. The view object should call the IShellBrowser::OnViewWindowActive method when the view window is activated by the user.

Second, Windows Explorer does not support layout negotiation. Instead, it allows the view window to add toolbar buttons or set status bar text. The view window can also create modeless pop-up windows, calling either the IShellBrowser::GetControlWindow or IShellBrowser::SendControlMsg method to control them. Explorer forwards appropriate notification messages from those controls by sending a WM_COMMAND message to the window returned by the IShellView::CreateViewWindow method.

Third, Windows Explorer allows the view window to add menu items to Explorer's pull-down menus (in addition to inserting top-level pull-down menus). In other words, the view object is allowed to insert menu items into submenus returned from the IShellBrowser::InsertMenusSB method. To let Explorer dispatch menu messages correctly, a certain range of menu item identifiers (between FCIDM_SHVIEWFIRST and FCIDM_SHVIEWLAST) must be used.

Persistent View State

Windows Explorer defines standard view states that can be used by a view implementation. These include:

  • View mode (such as large icon, small icon, and detail)
  • View attributes (such as snap to grid)

Windows Explorer provides a persistent medium in which to store these states, and although using them is not required, it is recommended. The settings are stored in a FOLDERSETTINGS structure. Explorer supplies the current settings to the view object with the IShellView::CreateViewWindow method. Explorer retrieves the view object's current settings by calling the IShellView::GetCurrentInfo method.

Windows Explorer also provides a persistent medium (a stream) to let the view object store view-specific information (such as scroll positions or icon layout). The view object can access this stream by calling the IShellBrowser::GetViewStateStream method.

When the user is browsing from one folder to another, Explorer passes the pointer to the previously viewed instance of the IShellView interface as a parameter to IShellView::CreateViewWindow (before calling the previous view's IShellView::DestroyViewWindow method). This allows the view object being created to determine if the previous view object is of a known type. It does this by calling the QueryInterface method to obtain an interface that is only known between the two objects. If the previous view object is of a known type, current private settings can be exchanged using an application-dependent method.

Registering Your Namespace Extension

Registering your extension is not difficult, but it must be done precisely or users will not be able to find your namespace extension.

Namespace extensions, like all COM objects, must be registered in the CLSID section. In addition, they must also have the named value ThreadingModel set to Apartment, as shown below.

HKEY_CLASSES_ROOT\CLSID\{CLSID} 
HKEY_CLASSES_ROOT_CLSID\{CLSID}\InProcServer32\(default)=path\filename.dll
HKEY_CLASSES_ROOT_CLSID\{CLSID}	\InProcServer32\"ThreadingModel" = "Apartment"

If your namespace extension is going to run under a rooted Windows Explorer, you should add the following entries so the Open and Explore verbs appear on your namespace extension.

HKEY_CLASSES_ROOT_CLSID\{CLSID}\Shell\Open\Command\(default)
                  =c:\windows\explorer.exe/root,%1
HKEY_CLASSES_ROOT_CLSID\{CLSID}\Shell\Explore\Command\(default) 
                  =c:\windows\explorer.exe/e,/root/%1

You can also include entries for the default icon under the following key:

HKEY_CLASSES_ROOT_CLSID\{CLSID}\DefaultIcon\(default)="path\filename.dll",IconIndex

The following is an optional key, but it's one that can be very important. If you include the "Attributes" named value under ...{CLSID}\ShellFolder, you specify the attributes of a junction point by using the SFGAO_* flags returned by the IShellFolder::GetAttributesOf method.

HKEY_CLASSES_ROOT_CLSID\{CLSID}\ShellFolder\Attributes = 0000 00 00 

For example, if the SFGAO_FOLDER flag is set and its junction point exists in the system namespace, the user sees your extension's icon in Windows Explorer's left pane and is able to browse into it using the standard (nonrooted) Explorer. If this flag is not set, you must provide a rooted Explorer for browsing.

Another example is the SFGAO_HASSUBFOLDER flag. If it is set, Explorer can make the extension's icon expandable to the next level by displaying the "+" icon to its left.

Other registry entries you should know about include those required for putting items into My Computer or on the desktop. The following entries list all the items in those two locations:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer
               \MyComputer\NameSpace
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer
               \Desktop\NameSpace

Finally, the following entry is currently required only for Microsoft® Windows NT&rg, but it should always be added to the registry for future compatibility with all operating systems. This is the list of extensions that the shell will load. Extensions that don't appear on this list will not be loaded by the shell on Windows NT.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\ShellExtensions
               \Approved\{CLSID}="Extension Name"

Shell Namespace Reference

The following programming elements are used with the shell namespace.

Interfaces
ICommDlgBrowser
IDockingWindow
IDockingWindowFrame
IDockingWindowSite
IEnumIDList
IPersistFolder
IShellBrowser
IShellExecuteHook
IShellFolder
IShellIcon
IShellView

Functions
BrowseCallbackProc
SHAddToRecentDocs
SHBrowseForFolder
SHChangeNotify
SHFileOperation
SHFreeNameMappings
SHGetDataFromIDList
SHGetDesktopFolder
SHGetFileInfo
SHGetInstanceExplorer
SHGetMalloc
SHGetPathFromIDList
SHGetSpecialFolderLocation
SHLoadInProc

Structures
BROWSEINFO
CIDA
FOLDERSETTINGS
SHFILEINFO
SHFILEOPSTRUCT
SHNAMEMAPPING
STRRET

Enumerations
SHCONTF
SHGNO