This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.


MIND


This article assumes you're familiar with Internet Explorer 4.0, C++, and COMx.
Download the code (29KB)

Adding Internet Explorer Favorites to Your Application
Scott Roberts

The Favorites menu is one of the most popular features of any browser. With the right code, you can access this list and enumerate the whole darned thing in your own program.
It's hard to imagine surfing the Web without accumulating a list of favorite sites. Everyone has them. My favorite is the Internet Client SDK support site, http://msdn.microsoft.com/workshop/essentials/inetsdk/inetsdk_map.asp. What would Web navigation be like if you couldn't save links to your favorite Web sites? If you have more than a few favorites, remembering and retyping all those long URL strings becomes a real chore.
      That's where Microsoft® Internet Explorer 4.0 Favorites menu comes to the rescue. You undoubtedly know that the Favorites menu lets you save Web site addresses. You can even organize these favorites into separate folders so that they are easy to find. Then, when you want to navigate to a saved Web address, you just click on its shortcut from the Favorites menu. The Favorites menu also lets you update and organize your subscriptions. Obviously, this is the best thing since sliced bread. But what if you want to offer the same functionality in your applications? This is not a difficult task to accomplish, as I'll show you.
      If you've hosted the WebBrowser control in your applications, you've undoubtedly discovered that the Favorites menu is not offered through that component. The favorites features are implemented in shdocvw.dll (the home of the WebBrowser control), but the Favorites menu itself is implemented as part of the shell portion of the DLL, not the browser portion. (In Internet Explorer 4.0, the app is actually a shell that hosts the browser control inside it. Michael Heydt's article in this issue explains the WebBrowser control more fully.) The shell portion includes each and every part of the Favorites menu, from the Add to Favorites command to the actual favorites links. Shdocvw.dll implements a handful of COM interfaces—IShellUIHelper, ISubscriptionMgr, and IShellLink—that provide this functionality.
      Creating a Favorites menu similar to the one in Internet Explorer 4.0 is fairly straightforward. Among other things, shdocvw.dll provides interfaces whose methods provide the functionality of the Add to Favorites and Update Subscriptions menu items. Shdocvw.dll also exports two functions that give your app the functionality of the Add to Favorites and Organize Favorites commands. Implementing the equivalent of the Manage Subscriptions command is easy as well. Creating the menu items for the favorites themselves is a bit more complicated than you might expect. There is currently no interface that provides the favorites from which you can create the links portion of the menu. I'll explain how to implement those as well.

Add to Favorites
      The Add to Favorites command lets you add a Web page to your list of favorites command (see Figure 1). In addition, this command allows you to subscribe to the chosen page. If you decide to subscribe to a page, you have two options: you can have Internet Explorer tell you when the page is updated and have the page downloaded automatically for offline viewing, or you can receive just the notification. You can also customize how you are notified and how often the page is checked for changes. Pretty convenient, huh?

Figure 1: Add Favorite Dialog
Figure 1: Add Favorite Dialog


      In addition to these options, you can change the name (as shown on the Favorites menu) under which the link will be stored. By default, Internet Explorer uses the page's title. This isn't always descriptive of the page's contents, so you may want to change this to something that makes it easy to find. Finally, you can choose a folder in which to store the favorite by pressing the Create In button. This button expands the Add Favorite dialog so you can choose an existing folder or create a new one.
      So how can you invoke this dialog from your own application? There are two ways to do this. The first way to invoke the Add to Favorites menu item is by using the ShellUIHelper object. ShellUIHelper is implemented by the Windows® shell mainly to let developers using Visual Basic® access and script to some of the features available in the shell. Using this object to add a favorite from Visual Basic is very easy. Just set a reference to shdocvw.dll in your project, dim an object of type ShellUIHelper (using the New keyword to actually create the object), and call the object's AddFavorite method:


 Dim suh as New ShellUIHelper
 suh.AddFavorite "http://www.microsoft.com/mind", "MIND home"
If you want to use this object from Visual C++®, all you have to do is create an instance of ShellUIHelper and retrieve a pointer to IShellUIHelper. (The CLSID for ShellUIHelper and the IID for IShellUIHelper are defined in ExDisp.h, which comes with the Internet Client SDK.) Then call the AddFavorite method. Since this is the same call you'd make from Visual Basic, the method takes the same two input parameters: a string value that specifies the URL of the item to add to the Favorites folder, and an optional string value that represents the title of the item.
      Note that without the Internet Explorer 4.0 (or newer) desktop update, the ShellUIHelper object is not registered. For more information and a workaround to this problem, check out article Q184418 in the Microsoft Knowledge Base (http://support.microsoft.com/support/kb/articles/q184/4/18.asp). In the C++ code, the URL for the AddFavorite method is a BSTR and the title is a VARIANT that contains a BSTR value.
      The MFC code in Figure 2 demonstrates how you would implement the Add to Favorites command using ShellUIHelper in C++. This code assumes that you have a menu item whose ID is mapped to the CMainFrame::OnAddToFavorites method.This code first creates an instance of ShellUIHelper, then asks for a pointer to the IShellUIHelper interface. The title and URL of the current page are retrieved from the WebBrowser control that is hosted in the view class. Then, the AddFavorite method is called to invoke the dialog that adds the Web page to your favorites.
      The other way to invoke the Add to Favorites menu item from a Visual C++-based application is to call the DoAddToFavDlg function that is exported from shdocvw.dll. This method seems simple at first, but works out to be a little more difficult in the long run because it only invokes the Add Favorite dialog. You then have to create the link yourself. DoAddToFavDlg takes six input parameters. hwnd is the parent window for the dialog. pszInitDir represents the initial path. This is typically the path to the Favorites folder, but if the user selects a directory and file name without error, pszInitDir will contain this new destination. cchInitDir is the length of the pszInitDir buffer. pszFile is the initial display name of the favorite. cchFile is the length of the pszFile buffer, and pidlBrowse is the pointer to an item identifier list (PIDL) associated with pszInitDir.
      To use this function, you have to complete eight steps:
  1. Load shdocvw.dll with LoadLibrary.
  2. Get the address of the DoAddToFavDlg function using GetProcAddress.
  3. Get the path to the Favorites folder. You can use the Internet Explorer 4.0-provided function SHGetSpecialFolderPath to retrieve the location of the Favorites folder. However, you must install the desktop update to use this function, since it's exported from the updated shell32.dll. (Most of the code samples in this article require that the desktop update be installed.) As an alternative, you can retrieve the location of the Favorites folder from the following registry key:
 HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
  1. Get a PIDL that identifies the Favorites folder. You can retrieve this through a call to SHGetSpecialFolderLocation by specifying CSIDL_FAVORITES as the second parameter. You can also create this PIDL using the path to the Favorites folder itself. See Knowledge Base article Q132750 for more information (http://support.microsoft.com/support/kb/articles/q132/7/50.asp).
  2. Call DoAddToFavDlg.
  3. Create the favorite shortcut.
  4. Free the PIDL by calling IMalloc::Free.
  5. Unload shdocvw.dll.
      The MFC code in Figure 3 demonstrates how to use this method to invoke the Add Favorite dialog box. Since this code only invokes the dialog box, you will have to create the shortcut when the user presses the OK button by using either the IShellLink or IUniformResourceLocator interface. Please refer to the Internet Client SDK for more information regarding the creation of shell and URL links.
      Note that DoAddToFavDlg takes a count of characters for the cchInitDir and cchFile parameters. To make this code work in a Unicode environment, I divide the size of the buffer by the size of one of its elements. This is done for any functions that take a count of characters as an input parameter.

Organize Favorites
      When you click on the Organize Favorites command, the dialog shown in Figure 4 is displayed. With this dialog you can move, delete, or rename your favorites or group them into different folders.

Figure 4: Organize Favorites Dialog
Figure 4: Organize Favorites Dialog

      The Organize Favorites dialog is implemented in shdocvw.dll, which provides a DoOrganizeFavDlg exported function that you can call to invoke the dialog. DoOrganizeFavDlg takes two input parameters: a handle to the window that will own the dialog and a pointer to a string buffer that holds the location of the Favorites folder to be organized. Figure 5 shows code that invokes the Organize Favorites dialog from C++.
      To use this function, you have to do the following:
  1. Load shdocvw.dll using LoadLibrary.
  2. Get the address of the DoOrganizeFavDlg function using GetProcAddress.
  3. Get the path to the favorites folder using SHGetSpecialFolderPath or by reading the path from the registry.
  4. Call DoOrganizeFavDlg.
  5. Unload shdocvw.dll.

Manage Subscriptions
      What is the difference between favorites and subscriptions? Aren't they pretty much the same? Favorites are really just Internet shortcuts. They allow you to navigate to a Web site without having to remember its URL. You just click on a link in the Favorites menu and you're there.
      Subscriptions, on the other hand, allow you to automatically download the content of any Web site, up to three levels deep, from the specified URL. You can schedule this download to occur when you are away from your computer so that you can browse it at a later time, possibly when you are offline. If you normally access the Internet via a modem connection, the subscription can be configured to automatically dial up and download the pages without any intervention on your part.
      Manage Subscriptions provides functionality similar to the Organize Favorites command. When you select it, the Subscriptions folder is opened in a separate window (see Figure 6). Using this folder, you can organize, rename, and delete any of your subscriptions.

Figure 6: Manage Subscriptions Dialog
Figure 6: Manage Subscriptions Dialog

      To implement the Manage Subscriptions functionality in your applications, you must do a couple of things. First, retrieve the location of the Subscriptions folder. Unfortunately, SHGetSpecialFolderPath does not currently support a Subscription folder flag and this location isn't kept anywhere in the registry, so you must retrieve this path yourself. For the purpose of this article, I'll assume that the Subscriptions folder is a subdirectory of the Windows directory. In future versions of Internet Explorer, this may not be the case.
      The second step is to call ShellExecute to open the Subscriptions folder. The following MFC code shows how to implement the Manage Subscriptions menu item in your own application:

 void CMainFrame::OnManageSubscriptions() 
 {
    TCHAR szPath[MAX_PATH];
 
    ::GetWindowsDirectory(szPath, 
        sizeof(szPath)/sizeof(szPath[0]);
    lstrcat(szPath, "\\Subscriptions");
 
    ShellExecute(m_hWnd, "open", szPath, NULL, NULL,   
                 SW_SHOWNORMAL);
 }

Update All Subscriptions
      The Update All Subscriptions command invokes the Downloading Subscriptions dialog box. This dialog will update all locally cached subscription content for each of your subscriptions. Figure 7 shows this dialog after the Details button has been pressed. You can choose to skip the download of a particular subscription by selecting it and pressing the Skip button.

Figure 7: Downloading Subscriptions
Figure 7: Downloading Subscriptions

      Invoking the Downloading Subscriptions dialog in your application is easy. All you have to do is create an instance of the SubscriptionMgr object by calling CoCreateInstance with a CLSID of CLSID_SubscriptionMgr and ask for the ISubscriptionMgr interface. This CLSID is defined in the subsmgr.h header file that comes with the Internet Client SDK. This will return a pointer to the ISubscriptionMgr interface, from which you can call the UpdateAll method. That's it. The following code demonstrates how to invoke this dialog from an MFC-based application:

   void CMainFrame::OnUpdateAllSubscriptions() 
   {
      ISubscriptionMgr* pSubscriptionMgr;

      HRESULT hr = CoCreateInstance(
          CLSID_SubscriptionMgr, NULL, 
          CLSCTX_INPROC_SERVER,
          IID_ISubscriptionMgr, 
          (LPVOID*)&pSubscriptionMgr);

      if (SUCCEEDED(hr))
      {
       pSubscriptionMgr->UpdateAll();
       pSubscriptionMgr->Release();
      }
   }

Favorites
      Now on to the good stuff: the favorites themselves. This is where you'll do the bulk of the work. There is currently no API or interface method that performs this, so you have to enumerate the Favorites folder yourself. As you may have guessed, enumerating this special folder is not as easy as getting information from a normal file system folder.
      There are a number of things you must do to connect with the Favorites folder. First, call SHGetSpecialFolderLocation to get a PIDL that identifies the Favorites folder, like this:


 LPITEMIDLIST pidlFavorites = NULL;
 SHGetSpecialFolderLocation(NULL, CSIDL_FAVORITES, &pidlFavorites);
Second, you must retrieve a pointer to the desktop by using SHGetDesktopFolder:

 IShellFolder* psfDesktop;
 SHGetDesktopFolder(&psfDesktop);
The desktop can be represented by an instance of the IShellFolder interface. A pointer to the desktop is needed so you can bind to the Favorites folder and iterate through it. Third, you must bind to the Favorites folder using the IShellFolder::BindToObject method. This method will return a pointer to an instance of the IShellFolder interface that represents the Favorites folder:

 IShellFolder* psfFavorites;
 hr = psfDesktop->BindToObject(pidlFavorites, NULL, IID_IShellFolder,
                               (LPVOID*)&psfFavorites);
      Now that you have a pointer to a shell folder that represents the Favorites folder, you can start iterating through it and creating menu items for each favorite that's stored there. Each time you encounter a new folder, you'll need to create a new submenu on your own Favorites menu. Because this code may be enumerating through subfolders below the Favorites folder and even through subfolders of those subfolders, it's a good idea to recurse through these directories to create the Favorites menu. Since this process is complex, I will only discuss the most important points. The complete code that demonstrates how to do this is contained in the IEFavMnu sample code.
      The first thing you must do when enumerating a special folder is call IShellFolder::EnumObjects. This method returns a pointer to an enumerator for item identifier lists. Since this is a standard enumerator object, you can simply call the Next method until it returns something other than NOERROR. Since it's an enumerator for an item identifier list, the Next method will return a PIDL representing the next item in the folder.
      After you retrieve a PIDL that represents a folder item, you must get the item's actual name with the IShellFolder::GetDisplayNameOf method. This method returns a STRRET structure that contains string values describing the item. The values contained in the STRRET structure depend on the value of the second parameter passed to GetDisplayNameOf. If you specify SHGDN_NORMAL, the STRRET structure will contain the display name of the item as it would be displayed on the menu or submenu. If you specify SHGDN_ FORPARSING, you'll get a name that can be resolved to the actual URL pointed to by the favorites item. You must store this URL in some data structure, such as an array, map, or linked list, so that your application can navigate to the correct URL when a favorite is chosen from the menu.
      All this may seem very confusing, but some code will help clarify things. Figure 8 contains the AddFavToMnu method that is part of the IEFavMnu sample. This code demonstrates how to enumerate the Favorites folder and add the items to the menu. If a folder is encountered, a new submenu is created. One thing to watch out for is channels, which look like folders when enumerated because they have the SFGAO_ FOLDER attribute. To avoid creating submenus for channels, check to see if the item's type name is Channel Shortcut.
      There are three different ways (hence three separate functions) to resolve a favorites item to its URL depending on whether the item is a Channel Shortcut, an Internet Shortcut, or a normal shell link. Since resolving an item involves a lot of code, I will not talk about it here. However, this code is included in the IEFavMnu sample. One thing I do in this code is check the szTypeName member of the SHFILEINFO structure to determine the type of the favorite. If you are worried about localization, you should check the extension of lpszFileName instead of checking szTypeName to determine the type of favorite you are processing. Internet shortcuts have a .url extension, while normal shell links have a .lnk (or possibly .pif) extension. Channel shortcuts have no extension at all.
      The code in Figure 8 builds the entire Favorites menu. This can potentially cause the startup time for your application to increase depending on the number of favorites you must process. Another drawback is that IEFavMnu recreates the Favorites menu each time a favorite is added or the favorites are organized. A more efficient way to create the Favorites menu is to create it popup by popup. In other words, create each popup menu only when it is needed. Since this article and the accompanying sample code just demonstrate how to create the Favorites menu, it is up to you to determine the most efficient method for your needs.
Figure 9: The IEFavMnu Favorites Menu
Figure 9: The IEFavMnu Favorites Menu

      The IEFavMnu sample (shown in Figure 9) puts all these concepts to work. IEFavMnu is a basic MFC SDI application that hosts the WebBrowser control. It allows you to surf the Web just like Internet Explorer 4.x. (Well, sort of. Internet Explorer provides many more features.) As you can see from Figure 9, IEFavMnu has implemented every item on the Internet Explorer 4.0 Favorites menu.

Internet Explorer 5.0 Sneak Preview
      And now, since you've read this far, here's some inside information based on a preview version of Internet Explorer 5.0. In the preview version, all of the Favorites menu items look about the same, although a few have new names. Others have the same names, but are completely different. (Note that these features may be different in the shipped version of the product.)
      The first minor change is in the Add to Favorites menu item. In Internet Explorer 5.0, this will be basically the same. The main difference is that the subscription radio buttons will be gone, replaced with a checkbox for making the page available offline. A new button will customize how the browser downloads a Web page and its associated links, similar to subscribing to a page.

Figure 10: New Organize Favorites Dialog
Figure 10: New Organize Favorites Dialog

      The biggest proposed change is to the Organize Favorites dialog box (see Figure 10). In Internet Explorer 4.0 this is a standard Windows dialog box, but Internet Explorer 5.0 should sport an HTML-based dialog. The dialog should let you do the same things as its predecessor: move, delete, and rename your favorites. It will also sort, import, export, and synchronize your favorites.
      Subscriptions are expected to remain in Internet Explorer 5.0, but they will be known as offline content. Offline content is a more natural name for the feature, which is a means of downloading Web pages to view them offline. This is a big advantage if, for instance, you travel a lot and cannot be connected to the Internet at all times.
      One of the first things I noticed about the proposed Internet Explorer 5.0 Favorites menu was that the Manage Subscriptions and Update All Subscriptions menu items were renamed to Manage Offline Pages and Synchronize, respectively. Manage Offline Pages should work just like Manage Subscriptions.
      The Synchronize menu item should invoke a dialog like the one shown in Figure 11. This dialog displays all the items that you have selected for offline access. Using this dialog, you could set the download schedule for each of the items you specify. You could also specify different update options for different network connections. For instance, you could choose to synchronize offline pages when your computer is idle if you connect to the Internet through a LAN. Or you could choose to synchronize your pages at midnight if you connect to the Internet using a modem.
Figure 11: New Synchronize Dialog
Figure 11: New Synchronize Dialog

Conclusion
      By following the simple steps outlined here, you should be ready to upgrade your applications to include a Favorites menu similar to the one provided in Internet Explorer 4.0. Just a little knowledge of COM and C++ will go a long way. Plus, you've gotten a sneak preview of the planned Internet Explorer 5.0 Favorites menu.

From the July 1998 issue of Microsoft Interactive Developer.