HOWTO: Web Browser Navigation Using a PIDL
ID: Q167834
|
The information in this article applies to:
-
Microsoft Internet Explorer (Programming) versions 4.0, 4.01, 5
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
REFERENCES
Reusing the WebBrowser Control documentation
PIDLs are discussed in the basic documentation for the Shell Namespace.
Additional query words:
PIDL navigate2 SHGetSpecialFolderLocation visual basic webbrowser navigation
Keywords : kbcode kbIE400 kbIE401 kbGrpInet kbIE500
Version : WINDOWS:4.0,4.01,5
Platform : WINDOWS
Issue type : kbhowto