HOWTO: Web Browser Navigation Using a PIDL

ID: Q167834


The information in this article applies to:


SUMMARY

This article illustrates programmatic navigation via a pointer to an item identifier list (PIDL) by providing an example of navigating to the Favorites Folder (CSIDL_FAVORITES).


MORE INFORMATION

The Web Browser object model implemented in Shdocvw.dll exposes two component classes that provide browsing services: WebBrowser and InternetExplorer. While the former represents the WebBrowser ActiveX Control, the latter represents the stand-alone browser.

In Internet Explorer (IE) 3.x, both classes implemented the IWebBrowser interface. Using the Navigate method of this interface, clients could easily navigate to a specified URL (for example, http://www.microsoft.com). Version 4.0 extends these classes to implement the IWebBrowser2 interface, which introduces the Navigate2 method. Navigate2 provides the same capabilities as Navigate, but it also can be used to navigate to a folder by specifying a pointer to an item identifier list (PIDL). PIDLs were introduced with Windows 95 and provide a way to uniquely identify an item within the shell's namespace. PIDLs are also supported on NT 4.0. For more information on PIDLs and the shell namespace, see the reference to the Internet Client SDK below.

The following code demonstrates how to obtain a PIDL from the shell, how to create an instance of the browser and obtain the IWebBrowser2 interface, and how to call the Navigate2 method. Observe that the Web browser expects the first parameter in the call to Navigate2 to be a SAFEARRAY of bytes. The following code provides a generic routine, InitVARIANTFromPidl, that packs a PIDL into a VARIANT, which holds such an array.

Note that because it is not currently possible to represent a PIDL in Visual Basic it is not possible to call the Navigate2 method from a Visual Basic application:


#include <windows.h>
#define INITGUID
#include <initguid.h>
#include <exdisp.h>
#include <shlguid.h>
#include <memory.h>
#include <shlobj.h>

// macros for walking PIDLs
#define _ILSkip(pidl, cb)       ((LPITEMIDLIST)(((BYTE*)(pidl))+cb))
#define _ILNext(pidl)           _ILSkip(pidl, (pidl)->mkid.cb)

HRESULT FreeResources(LPVOID pData);
HRESULT TestPidl(LPITEMIDLIST pidl);
LPITEMIDLIST PidlFromVARIANT(VARIANT* pvarLoc);
LPSAFEARRAY MakeSafeArrayFromData(LPBYTE pData,DWORD cbData);
HRESULT InitVARIANTFromPidl(LPVARIANT pVar, LPITEMIDLIST pidl);
UINT ILGetSize(LPITEMIDLIST pidl);

void main()
{
   HRESULT hr;
   LPMALLOC pMalloc = NULL;
   LPITEMIDLIST pidl, pidl2 = NULL;
   IWebBrowser2* pWebBrowser = NULL;
   VARIANT vPIDL = {0}, vDummy = {0};

   if (FAILED(CoInitialize(NULL)))
   {
      return;
   }

   // Get the pidl for your favorite special folder,
   // in this case literally, the Favorites folder
   if (FAILED(hr = SHGetSpecialFolderLocation(NULL,
          CSIDL_FAVORITES, &pidl)))
   {
      goto Error;
   }

   // Pack the pidl into a VARIANT
   if (FAILED(hr = InitVARIANTFromPidl(&vPIDL, pidl)))
   {
      goto Error;
   }

   // Verify for testing purposes only that the pidl was packed
   // properly. Don't clean up pidl2 because it's a copy of the
   // pointer, not a clone of the id list itself
   pidl2 = PidlFromVARIANT(&vPIDL);
   if (FAILED(hr = TestPidl(pidl2)))
   {
      OutputDebugString("PIDL test failed");
      goto Error;
   }

   // Instantiate a browser
   if (FAILED(hr = CoCreateInstance(CLSID_InternetExplorer,
      NULL, CLSCTX_SERVER, IID_IWebBrowser2,
                    (LPVOID*)&pWebBrowser)))
   {
      goto Error;
   }

   // Show the browser, and navigate to the special location
   // represented by the pidl
   hr = pWebBrowser->put_Visible(VARIANT_TRUE);
   hr = pWebBrowser->Navigate2(&vPIDL, &vDummy, &vDummy,
           &vDummy, &vDummy);

Error:
   // Clean up
   VariantClear(&vPIDL);

   if (pWebBrowser)
   {
      pWebBrowser->Release();
   }

   if (pidl)
   {
      FreeResources((LPVOID)pidl);
   }

   CoUninitialize();
}

// Exercise the PIDL by performing common operations upon it.
// 
HRESULT TestPidl(LPITEMIDLIST pidl)
{
   HRESULT hr;
   LPSHELLFOLDER pshfDesktop = NULL, pshf = NULL;
   DWORD uFlags = SHGDN_NORMAL;
   STRRET strret;

   if (!pidl)
   {
      return E_INVALIDARG;
   }

   hr = SHGetDesktopFolder(&pshfDesktop);
   if (!pshfDesktop)
   {
      return hr;
   }

   hr = pshfDesktop->BindToObject(pidl,
                NULL,
                IID_IShellFolder,
                (LPVOID*)&pshf);
   if (!pshf)
   {
      goto Error;
   }

   hr = pshfDesktop->GetDisplayNameOf(pidl, uFlags, &strret);
   if (STRRET_WSTR == strret.uType)
   {
      FreeResources((LPVOID)strret.pOleStr);
   }

   Error:
   if (pshf) pshf->Release();
   if (pshf) pshfDesktop->Release();
   return hr;
}

// Use the shell's IMalloc implementation to free resources
HRESULT FreeResources(LPVOID pData)
{
   HRESULT hr;
   LPMALLOC pMalloc = NULL;

   if (SUCCEEDED(hr = SHGetMalloc(&pMalloc)))
   {
      pMalloc->Free((LPVOID)pData);
      pMalloc->Release();
   }

   return hr;
}

// Given a VARIANT, pull out the PIDL using brute force
LPITEMIDLIST PidlFromVARIANT(LPVARIANT pvarLoc)
{
   if (pvarLoc)
   {
      if (V_VT(pvarLoc) == (VT_ARRAY|VT_UI1))
      {
         LPITEMIDLIST pidl = (LPITEMIDLIST)pvarLoc->parray->pvData;
         return pidl;
      }
   }
   return NULL;
}

// Pack a PIDL into a VARIANT
HRESULT InitVARIANTFromPidl(LPVARIANT pvar, LPITEMIDLIST pidl)
{
   if (!pidl || !pvar)
   {
      return E_POINTER;
   }

   // Get the size of the pidl and allocate a SAFEARRAY of
   // equivalent size
   UINT cb = ILGetSize(pidl);
   LPSAFEARRAY psa = MakeSafeArrayFromData((LPBYTE)pidl, cb);
   if (!psa)
   {
      VariantInit(pvar);
      return E_OUTOFMEMORY;
   }

   V_VT(pvar) = VT_ARRAY|VT_UI1;
   V_ARRAY(pvar) = psa;
   return NOERROR;
}

// Allocate a SAFEARRAY of cbData size and pack pData into it
LPSAFEARRAY MakeSafeArrayFromData(LPBYTE pData, DWORD cbData)
{
   LPSAFEARRAY psa;

   if (!pData || 0 == cbData)
   {
      return NULL;  // nothing to do
   }

   // create a one-dimensional safe array of BYTEs
   psa = SafeArrayCreateVector(VT_UI1, 0, cbData);

   if (psa)
   {
      // copy data into the area in safe array reserved for data
      // Note we party directly on the pointer instead of using locking/ 
      // unlocking functions.  Since we just created this and no one
      // else could possibly know about it or be using it, this is okay.
      memcpy(psa->pvData,pData,cbData);
   }

   return psa;
}

// Get the size of the PIDL by walking the item id list
UINT ILGetSize(LPITEMIDLIST pidl)
{
   UINT cbTotal = 0;
   if (pidl)
   {
      cbTotal += sizeof(pidl->mkid.cb);       // Null terminator
      while (pidl->mkid.cb)
      {
         cbTotal += pidl->mkid.cb;
         pidl = _ILNext(pidl);
      }
   }

   return cbTotal;
} 


REFERENCES

Internet Client SDK Documentation: Application and Internet Services, Windows Shell API, Shell's Namespace.

Additional query words:


Keywords          : kbcode kbIE400 kbIE401 kbIE500 
Version           : 4.0
Platform          : WINDOWS 
Issue type        : kbhowto 

Last Reviewed: April 29, 1999