Figure 5   IShellFolder and IPersistFolder

In Sample
Interface
Function
Description

IShellFolder
SetNameOf
Sets a new display name for the object.

IShellFolder
ParseDisplayName
Converts from a string to a PIDL.
IShellFolder
EnumObjects
Creates an enumeration object to list all the items present.

IShellFolder
BindToObject
Handles the subfolders, if any.

IShellFolder
BindToStorage
Not supported.

IShellFolder
CompareIDs
Provides a way to sort the various items.

IShellFolder
GetAttributesOf
Retrieves the attributes of the specified objects.
IShellFolder
CreateViewObject
Creates the view object.

IShellFolder
GetUIObjectOf
Associates special extensions such as context menus or icon handlers.

IShellFolder
GetDisplayNameOf
Converts from a PIDL to a string.
IPersistFolder
Initialize
Called when initializing the folder object.
IPersistFolder
GetClassID
Returns the CLSID.


Figure 6   IEnumIDList and IshellView

In Sample
Interface
Function
Description

IEnumIDList
Next
Returns the next given number of elements in the item list.

IEnumIDList
Skip
Jumps over the specified number of elements in the list.

IEnumIDList
Clone
Duplicates the enumeration object mantaining its current state.

IEnumIDList
Reset
Rewinds the enumeration object and makes it point to the first item in the list.
IShellView
CreateViewWindow
Creates the view. Usually, it is a child dialog but it may be any window.
IShellView
DestroyViewWindow
Destroys the current view.
IShellView
Refresh
Refreshes the current view. Usually, it occurs when the user hits the F5 key.

IShellView
SaveViewState
Saves the current state of the view.

IShellView
TranslateAccelerator
Handles menu accelerator keys.

IShellView
GetCurrentInfo
Returns information about the current folder settings.

IShellView
UIActivate
Called when the activation state of the window changes.

IShellView
AddPropertySheetPages
Allows adding property pages to the Explorer's Options dialog.

IShellView
EnableModeless
Enables and disables all modeless windows, if any.

IShellView
SelectItem
Changes the selection state for a given set of items.

IShellView
GetItemObject
Allows you to get the selected items from the view.


Figure 7   Creating a View


 /*-------------------------------------------------------------------*/
 // Procedure....: CreateViewWindow()
 // Description..: Sets up the folder's content and view
 /*-------------------------------------------------------------------*/
 STDMETHODIMP CHtmlView::CreateViewWindow( IShellView *lpPrevView,
     LPCFOLDERSETTINGS lpfs, IShellBrowser *psb, LPRECT prcView, HWND *phWnd )
 {
   *phWnd = NULL;
   m_pShellBrowser = psb;
 
   // Gets the main window handle from the shell browser.
   psb->GetWindow( &m_hwndMain );
 
   // Creates the window to show the contents of the HTML file.
   m_hwndView = CreateDialog( g_hInstance, MAKEINTRESOURCE(IDD_HTMLVIEW),   
       m_hwndMain, (DLGPROC) View_DlgProc );
   *phWnd = m_hwndView;
 
   // Resizes the view. (Since it will contains some controls
   // you should resize them in WM_INITDIALOG).
   SetWindowPos( m_hwndView, NULL, prcView->left, prcView->top,
       prcView->right-prcView->left, prcView->bottom-prcView->top,
       SWP_NOZORDER|SWP_SHOWWINDOW );
 
   // Sets up toolbar and menu.
   SetMenu();
   SetToolbar();
 
   // Filling out the view.
   HWND hwndTree = GetDlgItem( m_hwndView, IDC_TREEVIEW );
   m_pHtmlTree = new CHtmlTree( hwndTree, m_pShellBrowser );
   m_pHtmlTree->Load( m_szFile );
   g_pHtmlTree = m_pHtmlTree;
 
   // This is necessary because initializing MSHTML.dll
   // (needed for accessing the HTML document object) might be
   // time-consuming. To avoid unexpected crashes I add menu and toolbar
   // items in a disabled state, so now it's time to unlock them...
   // The CDynHtml::Load() method terminates only when 
   // the MSHTML server is ready for use.
   EnableUI();
 
   return NOERROR;
 }
 
 /*-------------------------------------------------------------------*/
 // Procedure....: DestroyViewWindow()
 // Description..: Destroys the view objects
 /*-------------------------------------------------------------------*/
 STDMETHODIMP CHtmlView::DestroyViewWindow()
 {
   if( !m_hwndView )
      return E_UNEXPECTED;
   if( m_pHtmlTree ) 
      delete m_pHtmlTree;
 
   DestroyWindow( m_hwndView );
   return NOERROR;
 }
 
 /*-------------------------------------------------------------------*/
 // Procedure....: Refresh()
 // Description..: Update the view.
 /*-------------------------------------------------------------------*/
 STDMETHODIMP CHtmlView::Refresh( VOID )
 {
   m_pHtmlTree->Unload();
   m_pHtmlTree->Load( m_szFile );
   return NOERROR;
 }
 
 /*-------------------------------------------------------------------*/
 // Procedure....: SetToolbar()
 // Description..: Set up the folder's toolbar
 /*-------------------------------------------------------------------*/
 STDMETHODIMP CHtmlView::SetToolbar()
 {
   TBADDBITMAP tbab;
   LONG lNewIndex;
 
   // Declare an array of initially grayed buttons. 
   TBBUTTON tbb[] = {
      {0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0},
      {0,IDM_FILE_RUN,0,TBSTYLE_BUTTON,0,0},
      {1,IDM_VIEW_SOURCE,0,TBSTYLE_BUTTON,0,0}
   };
 
   // Add new bitmaps to the Explorer toolbar.
   tbab.hInst = g_hInstance;
   tbab.nID = IDB_TOOLBAR;
   m_pShellBrowser->SendControlMsg( FCW_TOOLBAR, TB_ADDBITMAP,
       2, (LPARAM) &tbab, &lNewIndex );
 
   // The folder's toolbar holds a variety of bitmaps. We need
   // to know where are our most recent bitmaps.
   // lNewIndex has been filled out with the index of the first
   // image we added with the previous instruction.
   tbb[1].iBitmap = lNewIndex;
   tbb[2].iBitmap = lNewIndex+1;
 
   // Now just adds the new buttons.
   m_pShellBrowser->SendControlMsg( FCW_TOOLBAR, TB_ADDBUTTONS,
       3, (LPARAM) &tbb, NULL );
 
   return NOERROR;
 }
 
 /*-------------------------------------------------------------------*/
 // Procedure....: SetMenu()
 // Description..: Sets up the folder's menu
 /*-------------------------------------------------------------------*/
 STDMETHODIMP CHtmlView::SetMenu()
 {
   OLEMENUGROUPWIDTHS mgw = { {0, 0, 0, 0, 0, 0} };
         
   if( m_hMenu )   DestroyMenu( m_hMenu );
 
   // Create a new menu to insert into the shell browser.
   m_hMenu = CreateMenu();
   m_pShellBrowser->InsertMenusSB( m_hMenu, &mgw );
 
   MENUITEMINFO mii;
   ZeroMemory( &mii, sizeof(MENUITEMINFO) );
   mii.cbSize = sizeof(MENUITEMINFO);
   mii.fMask = MIIM_SUBMENU;
 
   // modify menus: File
   GetMenuItemInfo( m_hMenu, FCIDM_MENU_FILE, FALSE, &mii );
   HMENU hmnuFile = mii.hSubMenu;
   InsertMenu( hmnuFile, 0, MF_BYPOSITION|MF_SEPARATOR, 0, NULL );
   InsertMenu( hmnuFile, 0, MF_BYPOSITION|MF_STRING|MF_GRAYED, 
        IDM_FILE_RUN, "Run..." );
   InsertMenu( hmnuFile, 1, MF_BYPOSITION|MF_STRING|MF_GRAYED, 
        IDM_FILE_SETASDEFAULT, "Set as default" );
 
   // modify menus: View
   GetMenuItemInfo( m_hMenu, FCIDM_MENU_VIEW, FALSE, &mii );
   HMENU hmnuView = mii.hSubMenu;
   AppendMenu( hmnuView, MF_SEPARATOR, 0, NULL );
   AppendMenu( hmnuView, MF_STRING|MF_GRAYED, IDM_VIEW_SOURCE, "Source" );
 
   // modify menus: Help
   GetMenuItemInfo( m_hMenu, FCIDM_MENU_VIEW, FALSE, &mii );
   HMENU hmnuHelp = mii.hSubMenu;
   DeleteMenu( hmnuHelp, 0, MF_BYPOSITION );
   AppendMenu( hmnuHelp, MF_STRING, IDM_HELP_ABOUT, "About..." );
 
   // remove Edit menu	
   DeleteMenu( m_hMenu, FCIDM_MENU_EDIT, MF_BYCOMMAND );
 
   // save changes
   m_pShellBrowser->SetMenuSB( m_hMenu, NULL, NULL );
 
   return NOERROR;
 }


Figure 9   Declaration of CDynHtml


 #ifndef _DYNHTML_
 #define _DYNHTML_
 
 #include <mshtml.h>
 #include <urlmon.h>
 #include <objbase.h> 
 #include <mshtmdid.h>
 
 // callbacks
 typedef BOOL (CALLBACK *LPLINKENUMPROC)   (IHTMLAnchorElement*, DWORD);
 typedef BOOL (CALLBACK *LPIMAGEENUMPROC)  (IHTMLImgElement*, DWORD);
 typedef BOOL (CALLBACK *LPOBJECTENUMPROC) (IHTMLElement*, DWORD);
 typedef BOOL (CALLBACK *LPSCRIPTENUMPROC) (IHTMLElement*, DWORD);
 typedef BOOL (CALLBACK *LPAPPLETENUMPROC) (IHTMLElement*, DWORD);
 
 // definitions
 #define WM_EX_DOCUMENTREADY        (WM_USER+1)
 #define ELEM_LINK                   0x01
 #define ELEM_APPLET                 0x02
 #define ELEM_SCRIPT                 0x03
 #define ELEM_OBJECT                 0x04
 #define ELEM_IMAGE                  0x05
 
 // useful macros
 #define BSTR_TO_STRING(b,s,n)       \
     WideCharToMultiByte(CP_ACP,0,b,-1,s,n,NULL,NULL)
 #define STRING_TO_BSTR(s,b,n)     \
     MultiByteToWideChar(CP_ACP,0,s,-1,b,n);    
 
 
 // Define a Dynamic HTML class
 class CDynHtml : public IPropertyNotifySink, IDispatch
 {
   public:
     CDynHtml();
     ~CDynHtml();
     
     // IUnknown methods
     STDMETHODIMP         QueryInterface( REFIID, LPVOID FAR* );
     STDMETHODIMP_(ULONG) AddRef();
     STDMETHODIMP_(ULONG) Release();
 
     // IPropertyNotifySink methods
     STDMETHODIMP OnChanged( DISPID dispID );
     STDMETHODIMP OnRequestEdit( DISPID dispID );
 
     // IDispatch methods
     STDMETHODIMP Invoke( DISPID, REFIID, LCID, WORD,
         DISPPARAMS*, VARIANT*, EXCEPINFO*, UINT* )
         { return E_NOTIMPL; };
     STDMETHODIMP GetTypeInfoCount( UINT* )
         { return E_NOTIMPL; }
     STDMETHODIMP GetTypeInfo( UINT, LCID, ITypeInfo** )
         { return E_NOTIMPL; }
     STDMETHODIMP GetIDsOfNames( REFIID, LPOLESTR*, UINT, LCID, DISPID* )
         { return E_NOTIMPL; }
 
     // Custom methods
     STDMETHODIMP Init( VOID );
     STDMETHODIMP Close( VOID );
     STDMETHODIMP Load( LPCTSTR pszHtmlFile );
 
     STDMETHODIMP GetTitle( LPTSTR pszBuf, INT iBufSize );
     STDMETHODIMP GetNumOf( INT iElemType, LPLONG plBuf );
     STDMETHODIMP GetSource( LPTSTR pszBuf, INT iBufSize );
     STDMETHODIMP GetSourceSize( LPDWORD pdwSize );
 
     STDMETHODIMP EnumLinks( LPLINKENUMPROC, DWORD );
     STDMETHODIMP EnumImages( LPIMAGEENUMPROC, DWORD );
     STDMETHODIMP EnumObjects( LPOBJECTENUMPROC, DWORD );
     STDMETHODIMP EnumScripts( LPSCRIPTENUMPROC, DWORD );
     STDMETHODIMP EnumApplets( LPAPPLETENUMPROC, DWORD );
 
   protected:
     IHTMLDocument2* m_pMSHTML;
     DWORD m_cRef;
     LPCONNECTIONPOINT m_pCP;
     TCHAR m_szFile[MAX_PATH];
     READYSTATE m_readyState;
     DWORD m_dwCookie;
 
     IHTMLElementCollection* m_pLinkColl; 
     IHTMLElementCollection* m_pImageColl;
     IHTMLElementCollection* m_pObjectColl;
     IHTMLElementCollection* m_pScriptColl;
     IHTMLElementCollection* m_pAppletColl;
 
     STDMETHODIMP GetAllTags( LPCTSTR, IHTMLElementCollection** );
 };
 
 #endif


Figure 10   Loading an HTML Document


 /*-------------------------------------------------------------------*/
 // Procedure....: ::OnRequestEdit()
 // Description..: Allow or not the editing of a property
 /*-------------------------------------------------------------------*/
 STDMETHODIMP CDynHtml::OnRequestEdit( DISPID dispID )
 {
   return NOERROR;
 }
 
 /*-------------------------------------------------------------------*/
 // Procedure....: ::OnChanged()
 // Description..: The specified property has changed
 /*-------------------------------------------------------------------*/
 STDMETHODIMP CDynHtml::OnChanged( DISPID dispID )
 {
   HRESULT hr;
 
   if( m_pMSHTML==NULL )
     return E_UNEXPECTED;
 
   if( dispID==DISPID_READYSTATE )
   {
     VARIANT vResult = {0};
     EXCEPINFO ei;
     UINT err;
     DISPPARAMS dp = {NULL, NULL, 0, 0};
 
     hr = m_pMSHTML->Invoke( DISPID_READYSTATE, IID_NULL, LOCALE_SYSTEM_DEFAULT, 
        DISPATCH_PROPERTYGET, &dp, &vResult, &ei, &err );
     if( SUCCEEDED(hr) )
     {
        m_readyState = (READYSTATE) V_I4(&vResult);
        switch( m_readyState )
        {
           case READYSTATE_UNINITIALIZED: 
           case READYSTATE_LOADING: 
           case READYSTATE_LOADED: 
           case READYSTATE_INTERACTIVE:
              break;
           case READYSTATE_COMPLETE: 
              PostThreadMessage( GetCurrentThreadId(),
                  WM_EX_DOCUMENTREADY, 0, 0 );
              break;
        }
        VariantClear( &vResult );
     }
     else
        return E_UNEXPECTED;
   }
 
   return NOERROR;
 }
 
 /*-------------------------------------------------------------------*/
 // Procedure....: ::Init()
 // Description..:  Create an instance of the document object
 /*-------------------------------------------------------------------*/
 STDMETHODIMP CDynHtml::Init( VOID )
 {
   HRESULT hr;
   LPCONNECTIONPOINTCONTAINER pCPC = NULL;
 
   // OleInitialize()'s been called by the class constructor.
 
   // Create an instance of the MSHTML server.
   hr = CoCreateInstance( CLSID_HTMLDocument, NULL, CLSCTX_INPROC_SERVER,
          IID_IHTMLDocument2, (LPVOID*) &m_pMSHTML );
   if( FAILED(hr) )  return E_UNEXPECTED;
 
   // Set up a hook to detect property change event.
   hr = m_pMSHTML->QueryInterface( IID_IConnectionPointContainer, (LPVOID*) &pCPC );
   if( FAILED(hr) )
   {
     pCPC->Release();
     return hr;
   }
 
   hr = pCPC->FindConnectionPoint( IID_IPropertyNotifySink, &m_pCP );
   if (FAILED(hr) )
   {
     pCPC->Release();
     return hr;
   }
 
   m_pCP->Advise( (LPUNKNOWN) (IPropertyNotifySink*)this, &m_dwCookie );
   pCPC->Release();
   return hr;
 }
 
 /*-------------------------------------------------------------------*/
 // Procedure....: ::Close()
 // Description..:  Clean up connection point
 /*-------------------------------------------------------------------*/
 STDMETHODIMP CDynHtml::Close( VOID )
 {
   HRESULT hr=NOERROR;
 
   if( m_pCP )
   {
     hr = m_pCP->Unadvise( m_dwCookie );
     m_pCP->Release();
   }
 
   return hr;
 }
 
 /*-------------------------------------------------------------------*/
 // Procedure....: ::Load()
 // Description..:  Load the given HTML file
 /*-------------------------------------------------------------------*/
 STDMETHODIMP CDynHtml::Load( LPCTSTR pszHtmlFile )
 {
   HRESULT hr=NOERROR;
   OLECHAR wszHtmlFile[MAX_PATH];
 
   if( !pszHtmlFile || !m_pMSHTML )  
       return E_UNEXPECTED;    
 
   // turn the name into wide chars
   STRING_TO_BSTR( pszHtmlFile, wszHtmlFile, MAX_PATH );
 
 
 // Check whether IPersistMoniker or IPersistFile is needed.
   if( memcmp( pszHtmlFile, "file:", 5 )==0 ||
       memcmp( pszHtmlFile, "http:", 5 )==0 )
   {
      // Ask the system for a URL Moniker.
      IMoniker* pIMoniker;
      hr = CreateURLMoniker( NULL, (LPWSTR)wszHtmlFile, &pIMoniker );
      if( SUCCEEDED(hr) )
      {
        // Get the IPersistMoniker interface.
       IPersistMoniker* pPMk;
       hr = m_pMSHTML->QueryInterface( IID_IPersistMoniker, (LPVOID*)&pPMk );
       if( SUCCEEDED(hr) )
         {
          IBindCtx *pBCtx;
          hr = CreateBindCtx(0, &pBCtx);
          if( SUCCEEDED(hr) )
            {
            // Call Load on the IPersistMoniker.
            hr = pPMk->Load(FALSE, pIMoniker, pBCtx, STGM_READ);
            pBCtx->Release();
          }
          pPMk->Release();
       }
       pIMoniker->Release();
     }
  }
  else {
     IPersistFile*  pPFile;
     hr = m_pMSHTML->QueryInterface( IID_IPersistFile, (LPVOID*) &pPFile );
     if( SUCCEEDED(hr) )
     {
        // Call Load on the IPersistFile.
        hr = pPFile->Load( (LPWSTR)wszHtmlFile, 0 );
        pPFile->Release();
     }
  }
 
  // Wait for the liberating message before proceeding.
  if( SUCCEEDED(hr) || hr==E_PENDING )
  {
     MSG msg;
     while( GetMessage(&msg, NULL, 0, 0) )
     {
       if( msg.message==WM_EX_DOCUMENTREADY && msg.hwnd==NULL )
         break;
       else
         DispatchMessage( &msg );
     }
 
     // Now save the pointers to some useful collections for a later use.
     // For images and links we have a built-in collection. In other case 
     // we need to prepare our own list, extracting elements via tag name.
     
     // links collection
     if( m_pLinkColl != NULL )     
         m_pLinkColl->Release();
     m_pMSHTML->get_links( &m_pLinkColl );
 
     // images collection
     if( m_pImageColl != NULL )    
         m_pImageColl->Release();
     m_pMSHTML->get_images( &m_pImageColl );
 
     // objects collection
     if( m_pObjectColl != NULL )   
         m_pObjectColl->Release();
     GetAllTags( "OBJECT", &m_pObjectColl );
 
     // scripts collection
     if( m_pScriptColl != NULL )   
         m_pScriptColl->Release();
     GetAllTags( "SCRIPT", &m_pScriptColl );
 
     // applets collection
     if( m_pAppletColl != NULL )   
         m_pAppletColl->Release();
     GetAllTags( "APPLET", &m_pAppletColl );
 
     return hr;
 }