How To Navigate Web Browser Using a PIDL (167834)



The information in this article applies to:

  • Microsoft Internet Explorer (Programming) 4.0
  • Microsoft Internet Explorer (Programming) 4.01
  • Microsoft Internet Explorer (Programming) 5
  • Microsoft Internet Explorer (Programming) 5.5

This article was previously published under Q167834

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 3.x, both classes implement 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 Windows NT 4.0.

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.

Here is how this could be done in C++:
#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(OleInitialize(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);
   }

   OleUninitialize();
}

// 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;
}
				
Here is the Visual Basic version. Use this code in a project with a WebBrowser control added to the form as name "WebBrowser1":
Option Explicit

Private Declare Function LocalSize Lib "kernel32" _
    (ByVal hMem As Long) As Long

Private Declare Sub MoveMemory Lib "kernel32" Alias "RtlMoveMemory" _
    (pDest As Any, pSource As Any, ByVal dwLength As Long)

Private Const CSIDL_PROGRAMS = &H6   ' Favorites Folder

' Indicates a successful HRESULT
Private Const NOERROR = 0

' Retrieves the location of a special (system) folder.
' Returns NOERROR if successful, or an OLE-defined error result otherwise.
Private Declare Function SHGetSpecialFolderLocation Lib "shell32.dll" _
    (ByVal hwndOwner As Long, ByVal nFolder As Long, pidl As Long) As Long

' Frees memory allocated by the shell. This call is used for brevity.
' The Free method of the shell's IMalloc interface should be used instead.
Private Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal pv As Long)

Private Sub Form_Load()
  Dim pidl As Long
  Dim cbpidl As Integer   ' the size of the pidl's binary data
  Dim abpidl() As Byte    ' array of bytes the pidl will be copied to
  Dim avpidl As Variant   ' variant array the array will be copied to

If (SHGetSpecialFolderLocation(hWnd, CSIDL_PROGRAMS, pidl) = NOERROR) Then

    ' Get the size of the memory allocated for the folder's item ID list
    ' * The actual size of the item ID list (the sum of each SHITEMID's cb
    '   member], may be less that the allocated memory, since memory
    '   is typically allocated in DWORD aligned chunks.
    ' * Item IDs and item ID lists are terminated by two bytes set to 0
    '   (similar to a string), so the shell knows where the end of the item
    '   ID or item ID list is.
    ' * Item ID memory is allocated with IMalloc, but can be read by the
    '   Local* APIs, since we're dealing with private process memory.
    cbpidl = LocalSize(pidl)
    If cbpidl Then

      ' Reallocate the byte array and copy the<BR/>
      'folder's item ID list to the array.
      ReDim abpidl(1 To cbpidl)
      MoveMemory abpidl(1), ByVal pidl, cbpidl

      ' Load the pidl's byte array into the Variant, tada, a SAFEARRAY...
      avpidl = abpidl

      ' Navigate the browser to the folder's pidl...
      WebBrowser1.Navigate2 avpidl
      WebBrowser1.Visible = True

    End If   ' cbpidl

    ' Free the memory the shell allocated for the pidl
    Call CoTaskMemFree(pidl)

  End If   ' SHGetSpecialFolderLocation

End Sub
				

Modification Type:MinorLast Reviewed:7/2/2004
Keywords:kbcode kbhowto KB167834