HOWTO: Implement Explore and Open in a Namespace Extension

ID: Q179911

The information in this article applies to:

SUMMARY

When you are creating a namespace extension that has multiple levels of folders, you may have to implement the Explore and Open commands from the context menu. This article discusses how to implement Explore and Open commands from a context menu in the Windows Explorer tree. This article assumes you are already familiar with development of namespace extensions. For additional information on writing namespace extensions, see David Campbell's article "Extending the Windows Explorer with Name Space Extensions" in the July, 1996 issue of the Microsoft Systems Journal. In addition, please see the following article in the Microsoft Knowledge Base:

   ARTICLE-ID: Q178665
   TITLE     : SAMPLE: RegView.exe: Shell Namespace Extension Example

MORE INFORMATION

When you want to implement Explore and Open commands from your shell view, normally you will call IShellBrowser::BrowseObject, specifying the proper options for the desired commands. But you cannot do this for your folder items in the Windows Explorer tree because the IShellBrowser interface is not always available to use. Fortunately, you can implement these commands using ShellExecuteEx and these commands can be used by both the shell folder and shell view implementations.

If you want to implement Explore and Open commands in the context menus for your items in the Windows Explorer tree, you must first add these items to the menu in your IContextMenu::QueryContextMenu. Because you will be handling the execution of the commands, you can supply any command ID for the menu items that you want. Remember that the Explore and Open menu strings should be localized for use with other languages.

The first challenge is how to determine which item should be the default item. Normally, if the item is displayed in Explore mode (a window that contains a tree), the Explore command is the default. If the item is displayed in Open mode (no tree present), then Open is the default item in the context menu. If the context menu is being generated by the shell from a Explore mode window, it will set the CMF_EXPLORE flag when it calls your IContextMenu::QueryContextMenu. If this flag is set, you should make Explore the first and default item in the menu. If this flag is not set, then you should make Open the first and default item.

Sample Code

Following is an example of how you may accomplish this:

   #define IDM_EXPLORE  0
   #define IDM_OPEN     1

   #define IDM_LAST     IDM_OPEN

   STDMETHODIMP CContextMenu::QueryContextMenu( HMENU hMenu,
                                                UINT indexMenu,
                                                UINT idCmdFirst,
                                                UINT idCmdLast,
                                                UINT uFlags)
   {
   if(!(CMF_DEFAULTONLY & uFlags))
      {
      MENUITEMINFO   mii;

      if(uFlags & CMF_EXPLORE)
         {
         //add the Explore command first and make it the default item
         ZeroMemory(&mii, sizeof(mii));
         mii.cbSize = sizeof(mii);
         mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
         mii.wID = idCmdFirst + IDM_EXPLORE;
         mii.fType = MFT_STRING;
         mii.dwTypeData = TEXT("&Explore");
         mii.fState = MFS_ENABLED | MFS_DEFAULT;
         InsertMenuItem(   hMenu,
                           indexMenu++,
                           TRUE,
                           &mii);

         ZeroMemory(&mii, sizeof(mii));
         mii.cbSize = sizeof(mii);
         mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
         mii.wID = idCmdFirst + IDM_OPEN;
         mii.fType = MFT_STRING;
         mii.dwTypeData = TEXT("&Open");
         mii.fState = MFS_ENABLED;
         InsertMenuItem(   hMenu,
                           indexMenu++,
                           TRUE,
                           &mii);
         }
      else
         {
         //add the Open command first and make it the default item
         ZeroMemory(&mii, sizeof(mii));
         mii.cbSize = sizeof(mii);
         mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
         mii.wID = idCmdFirst + IDM_OPEN;
         mii.fType = MFT_STRING;
         mii.dwTypeData = TEXT("&Open");
         mii.fState = MFS_ENABLED | MFS_DEFAULT;
         InsertMenuItem(   hMenu,
                           indexMenu++,
                           TRUE,
                           &mii);

         ZeroMemory(&mii, sizeof(mii));
         mii.cbSize = sizeof(mii);
         mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
         mii.wID = idCmdFirst + IDM_EXPLORE;
         mii.fType = MFT_STRING;
         mii.dwTypeData = TEXT("&Explore");
         mii.fState = MFS_ENABLED;
         InsertMenuItem(   hMenu,
                           indexMenu++,
                           TRUE,
                           &mii);
         }

      return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_LAST + 1));
      }

   return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
   }

To determine if your shell view is being displayed in Open or Explore mode, use the IShellBrowser::GetControlWindow method passing FCW_TREE. If this method returns NULL, then the view is in Open mode. If this method returns a non-NULL value, then the view is in Explore mode. When the view needs to display a context menu for an item, it can then obtain the IContextMenu object using its parent's IShellFolder::GetUIObjectOf and then call the IContextMenu object's QueryContextMenu method. If the tree is present, the view should add the CMF_EXPLORE flag in the QueryContextMenu call.

Sample Code

Following is an example that obtains the context menu for the specified items in a view. The view object maintains the IShellFolder interface of the folder that created it in m_pSFParent.

   #define MENU_OFFSET  1
   #define MENU_MAX     100

   HMENU CShellView::GetContextMenuForItems( UINT uItems,
                                             LPITEMIDLIST *aItems)
   {
   HMENU hMenu;

   if(aItems)
      {
      LPCONTEXTMENU  pContextMenu = NULL;
      m_pSFParent->GetUIObjectOf(   m_hwndParent,
                                    uItems,
                                    (LPCITEMIDLIST*)aItems,
                                    IID_IContextMenu,
                                    NULL,
                                    (LPVOID*)&pContextMenu);

      if(pContextMenu)
         {
         HMENU hMenu = CreatePopupMenu();

         /*
         See if we are in Explore or Open mode. If the browser's tree is
         present, then we are in Explore mode.
         */ 
         BOOL  fExplore = FALSE;
         HWND  hwndTree = NULL;
         if(SUCCEEDED(m_pShellBrowser->GetControlWindow(FCW_TREE,
               &hwndTree)) && hwndTree)
            {
            fExplore = TRUE;
            }

         if(hMenu && SUCCEEDED(pContextMenu->QueryContextMenu( hMenu,
               0,
               MENU_OFFSET,
               MENU_MAX,
               CMF_NORMAL | (fExplore ? CMF_EXPLORE : 0))))
            {
            }
         else
            {
            DestroyMenu(hMenu);
            hMenu = NULL;
            }
         }
      }
   return hMenu;
   }

When the user selects an item in the menu, your IContextMenu::InvokeCommand will be called. If the command identifier is your Explore or Open command, then you should use ShellExecuteEx to Open or Explore the folder. To use ShellexecuteEx to Open or Explore the folder, you need to do the following:

1. Specify the SEE_MASK_IDLIST flag and pass a fully-qualified PIDL for the

   item to be opened or explored.

2. Specify the SEE_MASK_CLASSNAME flag and specify the class name as
   "folder."

3. Specify the window handle of the browser window. ShellExecuteEx will
   attempt to establish a DDE conversation with this window if the folder
   should be browsed in the same window. If you are calling InvokeCommand
   from a shell view, you should pass the view's parent window handle. This
   is usually obtained in IShellView::CreateViewWindow by calling the
   IShellBrowser::GetWindow method.

4. Specify the verb as "explore" or "open", depending upon the desired
   command.

Sample Code

Following is an example of how this is performed:

   STDMETHODIMP CContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
   {
   if(HIWORD(lpcmi->lpVerb))
      {
      //the command is being sent via a verb
      return NOERROR;
      }

   if(LOWORD(lpcmi->lpVerb) > IDM_LAST)
      return E_INVALIDARG;

   switch(LOWORD(lpcmi->lpVerb))
      {
      case IDM_EXPLORE:
      case IDM_OPEN:
         {
         LPITEMIDLIST      pidlFQ;
         SHELLEXECUTEINFO  sei;

         /*
         Only one PIDL can be passed to ShellExecuteEx, so default to the
         first one in the list.
         */ 
         pidlFQ = CreateFullyQualifiedPidl(m_aPidls[0]);

         ZeroMemory(&sei, sizeof(sei));
         sei.cbSize = sizeof(sei);
         sei.fMask = SEE_MASK_IDLIST | SEE_MASK_CLASSNAME;
         sei.lpIDList = pidlFQ;
         sei.lpClass = TEXT("folder");
         sei.hwnd = lpcmi->hwnd;
         sei.nShow = SW_SHOWNORMAL;

         if(LOWORD(lpcmi->lpVerb) == IDM_EXPLORE)
            sei.lpVerb = TEXT("explore");
         else
            sei.lpVerb = TEXT("open");

         ShellExecuteEx(&sei);

         DeletePidl(pidlFQ);
         }
         break;

      }

   return NOERROR;
   }

These steps allow your namespace extension to properly implement the Explore and Open commands from a context menu.

REFERENCES

Microsoft Systems Journal, July 1996, "Extending the Windows Explorer with Name Space Extensions," page 41, David Campbell

For additional information, please see the following article in the Microsoft Knowledge Base:

   ARTICLE-ID: Q178665
   TITLE     : SAMPLE: RegView.exe: Shell Namespace Extension Example

Additional query words:
Keywords          : kbcode kbExtension kbNameSpace kbNTOS400 kbWinOS2000 kbWinOS95 kbWinOS98 kbGrpShell 
Issue type        : kbhowto

Last Reviewed: December 25, 1998