#include "priv.h"
#include "sccls.h"
#include "shlobj.h"

#include <tchar.h>

#ifndef UNIX
#include <webcheck.h>
#else
#include <subsmgr.h>
#endif

#include "resource.h"
#include "mshtml.h"     // for IHTMLElement
#include "mlang.h"      // fo char conversion
#include <advpub.h>     // for IE activesetup GUID
#include "winineti.h"   // For name of a mutex used in IsWininetLoadedAnywhere()
#include "htregmng.h"
#include <ntverp.h>
#include <platform.h>
#include <mobsync.h>
#include <mobsyncp.h>
#include <winuser.h>
#include <mluisupp.h>
#include "shdocfl.h"
#include <shlwapip.h>
#include "inetnot.h"
#include <shfolder.h>

#include "..\inc\brutil.cpp"

STDAPI CDelegateMalloc_Create(void *pv, SIZE_T cbSize, WORD wOuter, IMalloc **ppmalloc);

const VARIANT c_vaEmpty = {0};

const TCHAR c_szRegKeyTypedURLs[]     = TEXT("Software\\Microsoft\\Internet Explorer\\TypedURLs");

#define DM_SESSIONCOUNT     0

int     g_cxIcon = 0;
int     g_cyIcon = 0;
int     g_cxSmIcon = 0;
int     g_cySmIcon = 0;


const DISPPARAMS c_dispparamsNoArgs = {NULL, NULL, 0, 0};
const LARGE_INTEGER c_li0 = { 0, 0 };

const ITEMIDLIST s_idlNULL = { 0 } ;

// 07.28.2000 - Moved from urlhist.cpp since its used in two places now.
#ifdef UNICODE
#define SHGETFOLDERPATH "SHGetFolderPathW"
#else
#define SHGETFOLDERPATH "SHGetFolderPathA"
#endif
#undef SHGetFolderPath
typedef HRESULT (*PFNSHGETFOLDERPATH)(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath);

PFNSHGETFOLDERPATH g_pfnGetFolderPath = NULL;

HRESULT SHGetFolderPathD(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)
{
    if (!g_pfnGetFolderPath)
    {
        // note, this is already loaded so this LoadLibray() is fast
        HMODULE hmod = LoadLibrary(TEXT("shell32.dll"));
        g_pfnGetFolderPath = (PFNSHGETFOLDERPATH)GetProcAddress(hmod, SHGETFOLDERPATH);

        // not there, must be downlevel shell32, use shfolder.dll instead
        if (!g_pfnGetFolderPath)
        {
            hmod = LoadLibrary(TEXT("shfolder.dll"));
            g_pfnGetFolderPath = (PFNSHGETFOLDERPATH)GetProcAddress(hmod, SHGETFOLDERPATH);
        }
        // note, we leak the hmod, for shell32/shfolder that is OK
    }

    HRESULT hr;
    if (g_pfnGetFolderPath) 
        hr = g_pfnGetFolderPath(hwnd, csidl, hToken, dwFlags, pszPath);
    else
    {
        *pszPath = 0;
        hr = E_FAIL;
    }
    return hr;
}


int InitColorDepth(void)
{
    static int s_lrFlags = 0;              // Flags passed to LoadImage
    if (s_lrFlags == 0)
    {
        int nColorRes, nIconDepth = 0;
        HKEY hkey;

        // Determine the color depth so we can load the best image
        // (This code was stolen from FileIconInit in shell32)

        // Get the user preferred icon size (and color depth) from the
        // registry.
        //
        if (NO_ERROR == RegOpenKey(HKEY_CURRENT_USER, REGSTR_PATH_METRICS, &hkey))
        {
            nIconDepth = SHRegGetIntW(hkey, L"Shell Icon Bpp", nIconDepth);
            RegCloseKey(hkey);
        }

        nColorRes = GetCurColorRes();

        if (nIconDepth > nColorRes)
            nIconDepth = 0;

        if (nColorRes <= 8)
            nIconDepth = 0; // wouldn't have worked anyway

        if (nColorRes > 4 && nIconDepth <= 4)
            s_lrFlags = LR_VGACOLOR;
        else
            s_lrFlags = LR_DEFAULTCOLOR;
    }
    return s_lrFlags;
}

HICON   g_hiconSplat = NULL;
HICON   g_hiconSplatSm = NULL;      // small version

void LoadCommonIcons(void)
{
    if (NULL == g_hiconSplat)
    {
        // Use LoadLibraryEx so we don't load code pages
        HINSTANCE hinst = LoadLibrary(TEXT("url.dll"));
        if (hinst)
        {
            int lrFlags = InitColorDepth();
            g_hiconSplat   = (HICON)LoadImage(hinst, MAKEINTRESOURCE(IDI_URL_SPLAT), IMAGE_ICON, g_cxIcon, g_cyIcon, lrFlags);
            g_hiconSplatSm = (HICON)LoadImage(hinst, MAKEINTRESOURCE(IDI_URL_SPLAT), IMAGE_ICON, g_cxSmIcon, g_cySmIcon, lrFlags);

            FreeLibrary(hinst);
        }
    }
}

STDAPI_(BOOL) UrlHitsNetW(LPCWSTR pszURL)
{
    BOOL fResult;

    // Handle the easy ones on our own and call URLMON for the others.

    switch (GetUrlScheme(pszURL))
    {
    case URL_SCHEME_FILE:
    case URL_SCHEME_RES:
        fResult = FALSE;
        break;

    case URL_SCHEME_HTTP:
    case URL_SCHEME_HTTPS:
    case URL_SCHEME_FTP:
    case URL_SCHEME_GOPHER:
    case URL_SCHEME_TELNET:
    case URL_SCHEME_WAIS:
        fResult = TRUE;
        break;

    default:
        {
        DWORD fHitsNet;
        DWORD dwSize;
        fResult = SUCCEEDED(CoInternetQueryInfo(
                            pszURL, QUERY_USES_NETWORK,
                            0, &fHitsNet, sizeof(fHitsNet), &dwSize, 0)) && fHitsNet;
        }
    }

    return fResult;
}

STDAPI_(BOOL) CallCoInternetQueryInfo(LPCTSTR pszURL, QUERYOPTION QueryOption)
{
    DWORD fRetVal;
    DWORD dwSize;
    return SUCCEEDED(CoInternetQueryInfo(
                        pszURL, QueryOption,
                        0, &fRetVal, sizeof(fRetVal), &dwSize, 0)) && fRetVal;
}

// see if a given URL is in the cache
STDAPI_(BOOL) UrlIsInCache(LPCTSTR pszURL)
{
    return CallCoInternetQueryInfo(pszURL, QUERY_IS_CACHED);
}

// See if a give URL is actually present as an installed entry
STDAPI_(BOOL) UrlIsInstalledEntry(LPCTSTR pszURL)
{
    return CallCoInternetQueryInfo(pszURL, QUERY_IS_INSTALLEDENTRY);
}


// see if a given URL is in the cache OR if it is mapped

STDAPI_(BOOL) UrlIsMappedOrInCache(LPCTSTR pszURL)
{
    return CallCoInternetQueryInfo(pszURL, QUERY_IS_CACHED_OR_MAPPED);
}

BOOL IsFileUrlW(LPCWSTR pcwzUrl)
{
    return (GetUrlSchemeW(pcwzUrl) == URL_SCHEME_FILE);
}

BOOL IsFileUrl(LPCSTR psz)
{
    return (GetUrlSchemeA(psz) == URL_SCHEME_FILE);
}

BOOL PathIsFilePath(LPCWSTR lpszPath)
{
#ifdef UNIX
    if (lpszPath[0] == TEXT('/'))
#else
    if ((lpszPath[0] == TEXT('\\')) || (lpszPath[0] != TEXT('\0') && lpszPath[1] == TEXT(':')))
#endif
        return TRUE;

    return IsFileUrlW(lpszPath);
}

BOOL IsSubscribableW(LPCWSTR pszUrl)
{
    //  FEATURE: this should be method on the subscription mgr interface - zekel
    DWORD dwScheme = GetUrlSchemeW(pszUrl);
    return (dwScheme == URL_SCHEME_HTTP) || (dwScheme == URL_SCHEME_HTTPS);
}

BOOL IsSubscribableA(LPCSTR pszUrl)
{
    //  FEATURE: this should be method on the subscription mgr interface - zekel
    DWORD dwScheme = GetUrlSchemeA(pszUrl);
    return (dwScheme == URL_SCHEME_HTTP) || (dwScheme == URL_SCHEME_HTTPS);
}

DWORD SHRandom(void)
{
    GUID guid;
    DWORD dw;

    CoCreateGuid(&guid);
    HashData((LPBYTE)&guid, SIZEOF(guid), (LPBYTE)&dw, SIZEOF(dw));

    return dw;
}

// See if we are hosted by IE (explorer.exe or iexplore.exe)
BOOL IsInternetExplorerApp()
{
    if ((g_fBrowserOnlyProcess) ||                  // if in iexplore.exe process,
        (GetModuleHandle(TEXT("EXPLORER.EXE"))))        // or explorer.exe process,
    {
        return TRUE;                                // then we are IE
    }

    return FALSE;
}

BOOL IsTopFrameBrowser(IServiceProvider *psp, IUnknown *punk)
{
    IShellBrowser *psb;

    ASSERT(psp);
    ASSERT(punk);

    BOOL fRet = FALSE;
    if (SUCCEEDED(psp->QueryService(SID_STopFrameBrowser, IID_PPV_ARG(IShellBrowser, &psb))))
    {
        fRet = IsSameObject(psb, punk);
        psb->Release();
    }
    return fRet;
}

DWORD GetUrlSchemePidl(LPITEMIDLIST pidl)
{
    if (pidl && IsURLChild(pidl, TRUE))
    {
        TCHAR szUrl[MAX_URL_STRING];
        if (SUCCEEDED(IEGetDisplayName(pidl, szUrl, SHGDN_FORPARSING)))
            return GetUrlScheme(szUrl);
    }
    return URL_SCHEME_INVALID;
}


STDAPI_(BSTR) LoadBSTR(UINT uID)
{
    WCHAR wszBuf[MAX_PATH];
    if (MLLoadStringW(uID, wszBuf, ARRAYSIZE(wszBuf)))
    {
        return SysAllocString(wszBuf);
    }
    return NULL;
}

BOOL StringIsUTF8A(LPCSTR psz, DWORD cb)
{
    BOOL fRC = FALSE;
    CHAR *pb;
    CHAR b;
    DWORD dwCnt;
    DWORD dwUTF8Cnt;

    if (!psz || !(*psz) || cb == 0)
        return(FALSE);

    pb = (CHAR*)psz;
    while(cb-- && *pb)
    {
        if ((*pb & 0xc0) == 0xc0) // bit pattern starts with 11
        {
            dwCnt = dwUTF8Cnt = 0;
            b = *pb;
            while((b & 0xc0) == 0xc0)
            {
                dwCnt++;
                if ((*(pb+dwCnt) & 0xc0) == 0x80)   // bits at dwCnt bytes from current offset in str aren't 10
                    dwUTF8Cnt++;
                b = (b << 1) & 0xff;
            }
            if (dwCnt == dwUTF8Cnt)
                fRC = TRUE;       // Found UTF8 encoded chars
                
            pb += ++dwCnt;
        }
        else
        {
            pb++;
        }
    }

    return(fRC);
}


BOOL StringIsUTF8W(LPCWSTR pwz, DWORD cb)
{
    BOOL  fRC = FALSE;
    WCHAR *pb;
    WCHAR b;
    DWORD dwCnt;
    DWORD dwUTF8Cnt;

    if (!pwz || !(*pwz) || cb == 0)
        return(FALSE);

    pb = (WCHAR*)pwz;
    while(cb-- && *pb)
    {
        if (*pb > 255)   // Non ansi so bail
            return(FALSE);
            
        if ((*pb & 0xc0) == 0xc0) // bit pattern starts with 11
        {
            dwCnt = dwUTF8Cnt = 0;
            b = *pb;
            while((b & 0xc0) == 0xc0)
            {
                dwCnt++;
                if ((*(pb+dwCnt) & 0xc0) == 0x80)   // bits at dwCnt bytes from current offset in str aren't 10
                    dwUTF8Cnt++;
                b = (b << 1) & 0xff;
            }
            if (dwCnt == dwUTF8Cnt)
                fRC = TRUE;       // Found UTF8 encoded chars
                
            pb += ++dwCnt;
        }
        else
        {
            pb++;
        }
    }

    return(fRC);
}

//
//  StringContainsHighAnsi
//
//  Determine if string contains high-ANSI characters. Search is
//    stopped when we hit the first high-ANSI character, when we hit the terminator
//    or when we have decremented dwInLen to zero
//
//  Return Value:
//      TRUE    - pszIn contains one or more high-ANSI characters
//
//      FALSE   - pszIn (or substring of length dwInLen) does not contain
//                high-ANSI characters
//
BOOL StringContainsHighAnsiA(LPCSTR pszIn, DWORD dwInLen)
{
    while (dwInLen-- && *pszIn) 
    {
        if (*pszIn++ & 0x80)
            return TRUE;
    }
    return FALSE;
}

BOOL StringContainsHighAnsiW(LPCWSTR pszIn, DWORD dwInLen)
{
    while (dwInLen-- && *pszIn) 
    {
        if (*pszIn++ & 0x80)
            return TRUE;
    }
    return FALSE;
}

BOOL UTF8Enabled(void)
{
    static DWORD   dwIE = URL_ENCODING_NONE;
    DWORD dwOutLen = sizeof(DWORD);
    
    if (dwIE == URL_ENCODING_NONE)
        UrlMkGetSessionOption(URLMON_OPTION_URL_ENCODING, &dwIE, sizeof(DWORD), &dwOutLen, NULL);
    return dwIE == URL_ENCODING_ENABLE_UTF8;
}

//
// PrepareURLForDisplay
//
//     Decodes without stripping file:// prefix
//

#undef PrepareURLForDisplay
BOOL PrepareURLForDisplayW(LPCWSTR pwz, LPWSTR pwzOut, LPDWORD pcbOut)
{
    if (PathIsFilePath(pwz))
    {
        if (IsFileUrlW(pwz))
            return SUCCEEDED(PathCreateFromUrlW(pwz, pwzOut, pcbOut, 0));

        StrCpyNW(pwzOut, pwz, *pcbOut);
        *pcbOut = lstrlenW(pwzOut);
        return TRUE;
    }
    
    BOOL fRet = SUCCEEDED(UrlUnescapeW((LPWSTR)pwz, pwzOut, pcbOut, 0));
    if (fRet)
    {
        SHCleanupUrlForDisplay(pwzOut);
    }
    return fRet;
}

// ****************************************************************************
// BEGIN - MOVE TO SHLWAPI
//
// TODO (grzegorz): move this code to shlwapi.dll
// ****************************************************************************

#define QUERY       L'?'
#define POUND       L'#'
#define HEX_ESCAPE  L'%'
#define TERMSTR(pch)      *(pch) = L'\0'

BOOL IsHex(WCHAR ch)
{
    return (   (ch >= TEXT('0') && ch <= TEXT('9')) 
            || (ch >= TEXT('A') && ch <= TEXT('F'))
            || (ch >= TEXT('a') && ch <= TEXT('f')));
}

WORD HexToWord(WCHAR ch)
{
    if(ch >= TEXT('0') && ch <= TEXT('9'))
        return (WORD) ch - TEXT('0');
    if(ch >= TEXT('A') && ch <= TEXT('F'))
        return (WORD) ch - TEXT('A') + 10;
    if(ch >= TEXT('a') && ch <= TEXT('f'))
        return (WORD) ch - TEXT('a') + 10;

    ASSERT(FALSE);  //we have tried to use a non-hex number
    return (WORD) -1;
}

inline BOOL IsEscapedOctetW(LPCWSTR pch)
{
    return (pch[0] == HEX_ESCAPE && IsHex(pch[1]) && IsHex(pch[2])) ? TRUE : FALSE;
}

WCHAR TranslateEscapedOctetW(LPCWSTR pch)
{
    WCHAR ch;
    ASSERT(IsEscapedOctetW(pch));

    pch++;
    ch = (WCHAR) HexToWord(*pch++) * 16; // hi nibble
    ch += HexToWord(*pch); // lo nibble

    return ch;
}

HRESULT CopyOutW(PSHSTRW pstr, LPWSTR psz, LPDWORD pcch)
{
    HRESULT hr = S_OK;
    DWORD cch;
    ASSERT(pstr);
    ASSERT(psz);
    ASSERT(pcch);

    cch = pstr->GetLen();
    if ((*pcch > cch) && psz)
        StrCpyNW(psz, pstr->GetStr(), pstr->GetLen() + 1);
    else
        hr = E_POINTER;

    *pcch = cch + (FAILED(hr) ? 1 : 0);

    return hr;
}

HRESULT ShdocvwUrlUnescapeInplaceW(LPWSTR psz, DWORD dwFlags, UINT uiCP)
{
    WCHAR *pchSrc = psz;
    WCHAR *pchDst = psz;

    HRESULT hr = S_OK;

    while (*pchSrc)
    {
        if ((*pchSrc == POUND || *pchSrc == QUERY) && (dwFlags & URL_DONT_ESCAPE_EXTRA_INFO))
        {
            StrCpyNW(pchDst, pchSrc, lstrlenW(pchSrc));
            pchDst += lstrlenW(pchDst);
            break;
        }

        if (IsEscapedOctetW(pchSrc))
        {
            int cchAnsi = 0;
            int cchDst;
            SHSTRA strAnsi;
            LPSTR pchDstAnsi;

            hr = strAnsi.SetStr(pchDst);
            if (FAILED(hr))
                return hr;
            else
                pchDstAnsi = strAnsi.GetInplaceStr();

            while (*pchSrc && IsEscapedOctetW(pchSrc))
            {
                WCHAR ch =  TranslateEscapedOctetW(pchSrc);

                *pchDstAnsi++ = LOBYTE(ch);
                pchSrc += 3; // enuff for "%XX"
                cchAnsi++;
            }

            if (cchAnsi)
            {
                TERMSTR(pchDstAnsi);
                // we have min 2 extra chars in pchDst to use, so we can pass cchAnsi + 1
                cchDst = SHAnsiToUnicodeCP(uiCP, strAnsi, pchDst, cchAnsi + 1);
                pchDst += cchDst - 1;
            }
        }
        else
        {
            *pchDst++ = *pchSrc++;
        }
    }

    TERMSTR(pchDst);

    return hr;
}

HRESULT ShdocvwUrlUnescapeW(LPWSTR pszUrl, LPWSTR pszOut, LPDWORD pcchOut, DWORD dwFlags, UINT uiCP)
{
    RIPMSG(pszUrl && IS_VALID_STRING_PTRW(pszUrl, -1), "ShdocvwUrlUnescapeW: Caller passed invalid pszUrl");

    if (dwFlags & URL_UNESCAPE_INPLACE)
    {
        return ShdocvwUrlUnescapeInplaceW(pszUrl, dwFlags, uiCP);
    }

    RIPMSG(NULL != pcchOut && IS_VALID_WRITE_PTR(pcchOut, DWORD), "ShdocvwUrlUnescapeW: Caller passed invalid pcchOut");
    RIPMSG(pszOut && (NULL == pcchOut || IS_VALID_WRITE_BUFFER(pszOut, WCHAR, *pcchOut)), "ShdocvwUrlUnescapeW: Caller passed invalid pszOut");

    if (   !pszUrl
        || !pcchOut
        || !*pcchOut
        || !pszOut)
    {
        return E_INVALIDARG;
    }

    SHSTRW str;
    HRESULT hr = str.SetStr(pszUrl);
    if (SUCCEEDED(hr))
    {
        ShdocvwUrlUnescapeInplaceW(str.GetInplaceStr(), dwFlags, uiCP);
        hr = CopyOutW(&str, pszOut, pcchOut);
    }

    return hr;
}
// ****************************************************************************
// END - MOVE TO SHLWAPI
// ****************************************************************************

//
// PrepareURLForDisplayUTF8W
//
// pwz -          [In] UTF8 encoded string like "%e6%aa%e4%a6.doc".
// pwzOut -       [Out]  UTF8 decoded string.
// pcchOut -      [In/Out] Count of characters in pwzOut on input.  Number of chars copies to pwzOut on output
//                         including the terminating null.
// fUTF8Enabled - [In] Flag to indicated whether UTF8 is enabled.
// uiCP -         [In] Codepage used to convert escaped characters, when fUTF8Enabled is false
//
// pwz and pwzOut can be the same buffer.
//
// Returns:
//    S_OK upon success.
//    E_FAIL for failure.
//    ERROR_BUFFER_OVERFLOW if the number of converted chars is greater than the passed in size of output buffer.
//
//    Note: If UTF8 is not enabled or the string does not contain UTF8 the output string will be unescaped
//    and will return S_OK.
//
HRESULT _PrepareURLForDisplayUTF8W(LPCWSTR pwz, LPWSTR pwzOut, LPDWORD pcchOut, BOOL fUTF8Enabled, UINT uiCP)
{
    HRESULT hr = E_FAIL;
    DWORD   cch;
    DWORD   cch1;
    CHAR    szBuf[MAX_URL_STRING];
    CHAR    *pszBuf = szBuf;

    if (!pwz || !pwzOut || !pcchOut)
    {
        if (pcchOut)
            *pcchOut = 0;
        return(hr);
    }
        
    cch = *pcchOut;
    cch1 = ARRAYSIZE(szBuf);
    if (uiCP != (UINT)-1)
        hr = ShdocvwUrlUnescapeW((LPWSTR)pwz, pwzOut, pcchOut, 0, fUTF8Enabled ? CP_UTF8 : uiCP);
    else
    {
        hr = UrlUnescapeW((LPWSTR)pwz, pwzOut, pcchOut, 0);
        if (SUCCEEDED(hr))
        {
            if (fUTF8Enabled && StringIsUTF8W(pwzOut, cch))
            {
                if (*pcchOut > ARRAYSIZE(szBuf)) // Internal buffer not big enough so alloc one
                {
                    if ((pszBuf = (CHAR *)LocalAlloc(LPTR, ((*pcchOut)+1) * sizeof(CHAR))) == NULL)
                    {
                        *pcchOut = 0;
                        return(E_OUTOFMEMORY);
                    }
                    cch1 = *pcchOut;
                }

                // Compress wide string
                CHAR *pIn = (CHAR *)pwzOut;
                CHAR *pOut = pszBuf;
                while((*pIn != '\0') || (*(pIn+1) != '\0') && --cch1)
                {
                    if (*pIn != '\0')
                    {
                        *pOut = *pIn;
                        pOut++;
                    }
                    pIn++;
                }
                *pOut = '\0';

                // Convert to UTF8 wide string
                if ((cch1 = SHAnsiToUnicodeCP(CP_UTF8, pszBuf, pwzOut, cch)) != 0)
                {
                    hr = S_OK;
                    *pcchOut = cch1;
                }

                // SHAnsiToUnicode doesn't tell us if it has truncated the convertion to fit the output buffer
                RIPMSG(cch1 != cch, "_PrepareURLForDisplayUTF8W: Passed in size of out buf equal to converted size; buffer might be truncated");

                if ((pszBuf != NULL) && (pszBuf != szBuf))
                {
                    LocalFree((CHAR *)pszBuf);
                    pszBuf = NULL;
                }
            }
            else
            {
                hr = S_OK;;
            }
        }
    }

    if (SUCCEEDED(hr))
    {
        SHCleanupUrlForDisplay(pwzOut);
    }

    return(hr);
}

HRESULT PrepareURLForDisplayUTF8W(LPCWSTR pwz, LPWSTR pwzOut, LPDWORD pcchOut, BOOL fUTF8Enabled)
{
    return _PrepareURLForDisplayUTF8W(pwz, pwzOut, pcchOut, fUTF8Enabled, (UINT)-1);
}

//
// PrepareURLForExternalApp -
//
//   Decodes and strips, if needed, file:// prefix
//

//  APPCOMPAT - for IE30 compatibility reasons, we have to Unescape all Urls - zekel - 1-JUL-97
//  before passing them to an APP.  this does limit their use, but
//  people already depend on this behavior.  specifically MS Chat.
BOOL PrepareURLForExternalApp (LPCWSTR psz, LPWSTR pszOut, LPDWORD pcchOut)
{
    if (IsFileUrlW(psz))
        return SUCCEEDED(PathCreateFromUrl(psz, pszOut, pcchOut, 0));
    else
        return SUCCEEDED(UrlUnescape((LPWSTR)psz, pszOut, pcchOut, 0));

}

SHDOCAPI
IURLQualifyWithContext(
    IN  LPCWSTR pcszURL, 
    IN  DWORD   dwFlags,         // UQF_*
    OUT LPWSTR  pszTranslatedURL,
    LPBOOL      pbWasSearchURL,
    LPBOOL      pbWasCorrected,
    ISearchContext *  pSC);

BOOL ParseURLFromOutsideSourceWithContextW (LPCWSTR psz, LPWSTR pszOut, LPDWORD pcchOut, LPBOOL pbWasSearchURL, ISearchContext * pSC)
{
    // This is our hardest case.  Users and outside applications might
    // type fully-escaped, partially-escaped, or unescaped URLs at us.
    // We need to handle all these correctly.  This API will attempt to
    // determine what sort of URL we've got, and provide us a returned URL
    // that is guaranteed to be FULLY escaped.

    IURLQualifyWithContext(psz, UQF_DEFAULT, pszOut, pbWasSearchURL, NULL, pSC);

    //
    //  go ahead and canonicalize this appropriately
    //
    if (FAILED(UrlCanonicalize(pszOut, pszOut, pcchOut, URL_ESCAPE_SPACES_ONLY)))
    {
        //
        //  we cant resize from here.
        //  NOTE UrlCan will return E_POINTER if it is an insufficient buffer
        //
        TraceMsg(DM_ERROR, "sdv PUFOS:UC() failed.");
        return FALSE;
    }

    return TRUE;
} // ParseURLFromOutsideSource

BOOL ParseURLFromOutsideSourceW (LPCWSTR psz, LPWSTR pszOut, LPDWORD pcchOut, LPBOOL pbWasSearchURL)
{
    return ParseURLFromOutsideSourceWithContextW(psz, pszOut, pcchOut, pbWasSearchURL, NULL);
} // ParseURLFromOutsideSource
BOOL ParseURLFromOutsideSourceA (LPCSTR psz, LPSTR pszOut, LPDWORD pcchOut, LPBOOL pbWasSearchURL)
{
    SHSTRW strw;
    DWORD cch ;

    ASSERT(psz);
    ASSERT(pszOut);
    ASSERT(pcchOut && *pcchOut);

    //
    //  WARNING: we arent guaranteed to have the correct cch's here - zekel - 27-jan-97
    //  but for now this is adequate.
    //
    if (SUCCEEDED(strw.SetStr(psz)) && SUCCEEDED(strw.SetSize(cch = *pcchOut)) &&
        ParseURLFromOutsideSourceW(strw, strw.GetInplaceStr(), pcchOut, pbWasSearchURL))
    {
        return SHUnicodeToAnsi((LPCWSTR)strw, pszOut, cch);
    }

    return FALSE;
}

int DPA_ILFreeCallback(void * p, void * d)
{
    Pidl_Set((LPITEMIDLIST*)&p, NULL);
    return 1;
}

void _DeletePidlDPA(HDPA hdpa)
{
    DPA_DestroyCallback(hdpa, (PFNDPAENUMCALLBACK)DPA_ILFreeCallback, 0);
    hdpa = NULL;
}

BOOL _InitComCtl32()
{
    static BOOL fInitialized = FALSE;

    if (!fInitialized)
    {
        INITCOMMONCONTROLSEX icc;

        icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
        icc.dwICC = ICC_USEREX_CLASSES | ICC_COOL_CLASSES | ICC_INTERNET_CLASSES | ICC_PAGESCROLLER_CLASS | ICC_NATIVEFNTCTL_CLASS;
        fInitialized = InitCommonControlsEx(&icc);
    }
    return fInitialized;
}

#ifndef ALPHA_WARNING_IS_DUMB

#pragma message("building with alpha warning enabled")

void AlphaWarning(HWND hwnd)
{
    static BOOL fShown = FALSE;
    TCHAR szTemp[265];
    TCHAR szFull[2048];
    szFull[0] = TEXT('\0');
    int i = IDS_ALPHAWARNING;

    if (fShown)
        return;

    fShown = TRUE;

    while(MLLoadShellLangString (i++, szTemp, ARRAYSIZE(szTemp))) {
        StrCatBuff(szFull, szTemp, ARRAYSIZE(szFull));
    }

    MessageBox(hwnd, szFull, TEXT("Internet Explorer"), MB_ICONINFORMATION | MB_OK);
}
#endif


#define DM_NAV              TF_SHDNAVIGATE
#define DM_ZONE             TF_SHDNAVIGATE
#define DM_IEDDE            DM_TRACE
#define DM_CANCELMODE       0
#define DM_UIWINDOW         0
#define DM_ENABLEMODELESS   0
#define DM_EXPLORERMENU     0
#define DM_BACKFORWARD      0
#define DM_PROTOCOL         0
#define DM_ITBAR            0
#define DM_STARTUP          0
#define DM_AUTOLIFE         0
#define DM_PALETTE          0

PFNSHCHANGENOTIFYREGISTER    g_pfnSHChangeNotifyRegister = NULL;
PFNSHCHANGENOTIFYDEREGISTER  g_pfnSHChangeNotifyDeregister = NULL;

BOOL g_fNewNotify = FALSE;   // Are we using classic mode (W95 or new mode?

BOOL CALLBACK AddPropSheetPage(HPROPSHEETPAGE hpage, LPARAM lParam)
{
    PROPSHEETHEADER * ppsh = (PROPSHEETHEADER *)lParam;

    if (ppsh->nPages < MAX_PAGES)
    {
        ppsh->phpage[ppsh->nPages++] = hpage;
        return TRUE;
    }
    return FALSE;
}

BOOL SHIsRegisteredClient(LPCTSTR pszClient)
{
    LONG cbSize = 0;
    TCHAR szKey[80];

    wnsprintf(szKey, ARRAYSIZE(szKey), TEXT("Software\\Clients\\%s"), pszClient);
    return (RegQueryValue(HKEY_LOCAL_MACHINE, szKey, NULL, &cbSize) == ERROR_SUCCESS) &&
           (cbSize > sizeof(TCHAR));
}

// Exporting by ordinal is not available on UNIX.
// But we have all these symbols exported because it's UNIX default.
#ifdef UNIX
#define GET_PRIVATE_PROC_ADDRESS(_hinst, _fname, _ord) GetProcAddress(_hinst, _fname)
#else
#define GET_PRIVATE_PROC_ADDRESS(_hinst, _fname, _ord) GetProcAddress(_hinst, _ord)
#endif

ULONG RegisterNotify(HWND hwnd, UINT nMsg, LPCITEMIDLIST pidl, DWORD dwEvents, UINT uFlags, BOOL fRecursive)
{
    SHChangeNotifyEntry fsne;

    // See if we need to still figure out which version of SHChange Notify to call?
    if  (g_pfnSHChangeNotifyDeregister == NULL)
    {

        HMODULE hmodShell32 = ::GetModuleHandle(TEXT("SHELL32"));
        if (!hmodShell32)
            return 0;   // Nothing registered...

        g_pfnSHChangeNotifyRegister = (PFNSHCHANGENOTIFYREGISTER)GET_PRIVATE_PROC_ADDRESS(hmodShell32,
                                                                                          "NTSHChangeNotifyRegister",
                                                                                          (LPSTR)640);
        if (g_pfnSHChangeNotifyRegister && (WhichPlatform() == PLATFORM_INTEGRATED))
        {
            g_pfnSHChangeNotifyDeregister = (PFNSHCHANGENOTIFYDEREGISTER)GET_PRIVATE_PROC_ADDRESS(hmodShell32,
                                                                                                  "NTSHChangeNotifyDeregister",
                                                                                                  (LPSTR)641);
            g_fNewNotify = TRUE;
        }
        else
        {
            g_pfnSHChangeNotifyRegister = (PFNSHCHANGENOTIFYREGISTER)GET_PRIVATE_PROC_ADDRESS(hmodShell32,
                                                                                              "SHChangeNotifyRegister",
                                                                                              (LPSTR)2);
            g_pfnSHChangeNotifyDeregister = (PFNSHCHANGENOTIFYDEREGISTER)GET_PRIVATE_PROC_ADDRESS(hmodShell32,
                                                                                                  "SHChangeNotifyDeregister",
                                                                                                  (LPSTR)4);
        }

        if  (g_pfnSHChangeNotifyDeregister == NULL)
            return 0;   // Could not get either to work...
    }

    uFlags |= SHCNRF_ShellLevel | SHCNRF_InterruptLevel;
    if (g_fNewNotify)
        uFlags |= SHCNRF_NewDelivery;

    fsne.fRecursive = fRecursive;
    fsne.pidl = pidl;
    return g_pfnSHChangeNotifyRegister(hwnd, uFlags, dwEvents, nMsg, 1, &fsne);
}

//----------------------------------------------------------------------------
// Just like shells SHRestricted() only this put up a message if the restricion
// is in effect.
// REARCHITECT: this function is identical to shell32's SHIsRestricted
BOOL SHIsRestricted(HWND hwnd, RESTRICTIONS rest)
{
    if (SHRestricted(rest))
    {
        ULONG_PTR uCookie = 0;
        SHActivateContext(&uCookie);
        SHRestrictedMessageBox(hwnd);
        if (uCookie)
        {
            SHDeactivateContext(uCookie);
        }
        return TRUE;
    }
    return FALSE;
}

BOOL SHIsRestricted2W(HWND hwnd, BROWSER_RESTRICTIONS rest, LPCWSTR pwzUrl, DWORD dwReserved)
{
    if (SHRestricted2W(rest, pwzUrl, dwReserved))
    {
        ULONG_PTR uCookie = 0;
        SHActivateContext(&uCookie);
        SHRestrictedMessageBox(hwnd);
        if (uCookie)
        {
            SHDeactivateContext(uCookie);
        }
        return TRUE;
    }
    return FALSE;
}


BOOL bIsValidString(LPCSTR pszString, ULONG cbLen)
{
    if (cbLen == 0) return TRUE;
    while (cbLen--)
        if (*pszString++ == '\0') return TRUE;
    return FALSE;
}

BOOL ViewIDFromViewMode(UINT uViewMode, SHELLVIEWID *pvid)
{
    switch (uViewMode)
    {
    case FVM_ICON:
        *pvid = VID_LargeIcons;
        break;

    case FVM_SMALLICON:
        *pvid = VID_SmallIcons;
        break;

    case FVM_LIST:
        *pvid = VID_List;
        break;

    case FVM_DETAILS:
        *pvid = VID_Details;
        break;

    case FVM_THUMBNAIL:
        *pvid = VID_Thumbnails;
        break;

    case FVM_TILE:
        *pvid = VID_Tile;
        break;

    default:
        *pvid = VID_LargeIcons;
        return(FALSE);
    }

    return(TRUE);
}

HIMAGELIST g_himlSysSmall = NULL;
HIMAGELIST g_himlSysLarge = NULL;

void _InitSysImageLists()
{
    if (!g_himlSysSmall)
    {
        Shell_GetImageLists(&g_himlSysLarge, &g_himlSysSmall);

        ImageList_GetIconSize(g_himlSysLarge, &g_cxIcon, &g_cyIcon);
        ImageList_GetIconSize(g_himlSysSmall, &g_cxSmIcon, &g_cySmIcon);
    }
}

// Copied from shell32 (was _ILCreate), which does not export this.
// The fsmenu code needs this function.
STDAPI_(LPITEMIDLIST) IEILCreate(UINT cbSize)
{
    LPITEMIDLIST pidl = (LPITEMIDLIST)SHAlloc(cbSize);
    if (pidl)
        memset(pidl, 0, cbSize);      // needed for external task allicator

    return pidl;
}

DWORD CommonDragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt)
{
    DWORD dwEffect = DROPEFFECT_NONE;
    FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};

    if (pdtobj->QueryGetData(&fmte) == S_OK)
        dwEffect = DROPEFFECT_COPY | DROPEFFECT_LINK;
    else
    {
        InitClipboardFormats();

        fmte.cfFormat = g_cfHIDA;
        if (pdtobj->QueryGetData(&fmte) == S_OK)
            dwEffect = DROPEFFECT_LINK;
        else {
            fmte.cfFormat = g_cfURL;

            if (pdtobj->QueryGetData(&fmte) == S_OK)
                dwEffect = DROPEFFECT_LINK | DROPEFFECT_COPY | DROPEFFECT_MOVE;
        }
    }

    return dwEffect;
}



// MapNbspToSp
//
// Purpose:
//     Unicode character code point 0x00a0 is designated to HTML
//     entity &nbsp, but some windows code pages don't have code
//     point that can map from 0x00a0. In the most occasion in the
//     shell, NBSP is just a space when it's rendered so we can
//     replace it with 0x0020 safely.
//     This function takes lpwszIn as a string that has
//     non-displayable characters in it, and tries to translate
//     it again after removing NBSP (00a0) from it.
//     returns S_OK if this re-translation is successful.
//
#define nbsp 0x00a0
HRESULT SHMapNbspToSp(LPCWSTR lpwszIn, LPSTR lpszOut, int cbszOut)
{
    BOOL fFoundNbsp = FALSE;
    BOOL fNotDisplayable = TRUE; // assumes FAIL
    LPWSTR pwsz, p;

    if (!lpwszIn || !lpszOut || cbszOut == 0)
        return E_FAIL;

    ASSERT(IS_VALID_STRING_PTRW(lpwszIn, -1));
    ASSERT(IS_VALID_WRITE_BUFFER(lpszOut, TCHAR, cbszOut));

    int cch = lstrlenW(lpwszIn) + 1;
    pwsz = (LPWSTR)LocalAlloc(LPTR, cch * sizeof(WCHAR));
    if (pwsz)
    {
        StrCpyNW(pwsz, lpwszIn, cch);
        p = pwsz;
        while (*p)
        {
            if (*p== nbsp)
            {
                *p= 0x0020; // replace with space
                if (!fFoundNbsp)
                    fFoundNbsp = TRUE;
            }
            p++;
        }

        // don't call WC2MB unless we found Nbsp - for perf reason
        if (fFoundNbsp)
        {
            int iret = WideCharToMultiByte(CP_ACP, 0, pwsz, -1, lpszOut,
                                           cbszOut, NULL, &fNotDisplayable);

            if (!fNotDisplayable && iret == 0)
            {
                // truncated. make it dbcs safe.
                SHTruncateString(lpszOut, cbszOut);
            }
        }

        LocalFree((LOCALHANDLE)pwsz);
        pwsz = NULL;
    }

    return (fFoundNbsp && !fNotDisplayable) ? S_OK : S_FALSE;
}
#undef nbsp


int PropBag_ReadInt4(IPropertyBag* pPropBag, LPWSTR pszKey, int iDefault)
{
    SHPropertyBag_ReadInt(pPropBag, pszKey, &iDefault);
    return iDefault;
}

HRESULT _SetPreferredDropEffect(IDataObject *pdtobj, DWORD dwEffect)
{
    InitClipboardFormats();

    HRESULT hres = E_OUTOFMEMORY;
    DWORD *pdw = (DWORD *)GlobalAlloc(GPTR, sizeof(DWORD));
    if (pdw)
    {
        STGMEDIUM medium;
        FORMATETC fmte = {g_cfPreferredEffect, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
        *pdw = dwEffect;

        medium.tymed = TYMED_HGLOBAL;
        medium.hGlobal = pdw;
        medium.pUnkForRelease = NULL;

        hres = pdtobj->SetData(&fmte, &medium, TRUE);

        if (FAILED(hres))
        {
            GlobalFree((HGLOBAL)pdw);
            pdw = NULL;
        }
    }
    return hres;
}

HRESULT DragDrop(HWND hwnd, IShellFolder * psfParent, LPCITEMIDLIST pidl, DWORD dwPrefEffect, DWORD *pdwEffect)
{
    HRESULT hres = E_FAIL;
    LPCITEMIDLIST pidlChild;

    if (!psfParent)
        IEBindToParentFolder(pidl, &psfParent, &pidlChild);
    else 
    {
        pidlChild = pidl;
        psfParent->AddRef();
    }

    if (psfParent)
    {
        DWORD dwAttrib = DROPEFFECT_MOVE | DROPEFFECT_COPY | DROPEFFECT_LINK;

        psfParent->GetAttributesOf(1, &pidlChild, &dwAttrib);

        IDataObject *pdtobj;
        hres = psfParent->GetUIObjectOf(NULL, 1, &pidlChild, IID_IDataObject, NULL, (void**)&pdtobj);
        if (SUCCEEDED(hres))
        {
            DWORD dwEffect = (DROPEFFECT_MOVE | DROPEFFECT_COPY | DROPEFFECT_LINK) & dwAttrib;

            if (dwPrefEffect)
            {
                //win95 shell32 doesn't know about preferred drop effect, so make it the only effect
                if (IsOS(OS_WIN95ORGREATER) && (WhichPlatform() == PLATFORM_BROWSERONLY))
                {
                    dwEffect = DROPEFFECT_LINK & dwAttrib;
                }
                else if (dwPrefEffect & dwEffect)
                {
                    _SetPreferredDropEffect(pdtobj, dwPrefEffect);
                }
            }
            ASSERT(dwEffect);

            // Win95 Browser Only - the shell32 in this process doesn't know
            // ole is loaded, even though it is.
            SHLoadOLE(SHELLNOTIFY_OLELOADED);
            IDragSourceHelper* pDragImages;

            if (SUCCEEDED(CoCreateInstance(CLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER, IID_IDragSourceHelper, (void**)&pDragImages)))
            {
                pDragImages->InitializeFromWindow(hwnd, 0, pdtobj);
                pDragImages->Release();
            }

            hres = SHDoDragDrop(hwnd, pdtobj, NULL, dwEffect, &dwEffect);
            if (pdwEffect)
                *pdwEffect = dwEffect;

            pdtobj->Release();
        }

        psfParent->Release();
    }

    return hres;
}

#define IEICONTYPE_GETFILEINFO              0x00000001
#define IEICONTYPE_DEFAULTICON              0x00000002

typedef struct tagIEICONS
{
    int nDefaultIcon;
    int nIEIcon;
    LPCTSTR szFile;
    LPCTSTR szFileExt;
    int nIconResourceNum;
    LPCTSTR szCLSID;
    DWORD dwType;
} IEICONS;

IEICONS g_IEIcons[] = {
    {-1, -1, TEXT("MSHTML.DLL"), TEXT(".htm"), 1, NULL, IEICONTYPE_GETFILEINFO},
    {-1, -1, TEXT("URL.DLL"), TEXT("http\\DefaultIcon"), 0, TEXT("{FBF23B42-E3F0-101B-8488-00AA003E56F8}"), IEICONTYPE_DEFAULTICON}
};

//This function returns the IE icon regardless of the which browser is  default


void _GenerateIEIcons(void)
{
    int nIndex;

    for (nIndex = 0; nIndex < ARRAYSIZE(g_IEIcons); nIndex++)
    {
        SHFILEINFO sfi;
        TCHAR szModule[MAX_PATH];

        HMODULE hmod = GetModuleHandle(g_IEIcons[nIndex].szFile);
        if (hmod)
        {
            GetModuleFileName(hmod, szModule, ARRAYSIZE(szModule));
        }
        else
        {   //HACKHACK : This is a hack to get the mstml
            TCHAR   szKey[GUIDSTR_MAX * 4];
            TCHAR   szGuid[GUIDSTR_MAX];

            //The CLSID used here belongs to MS HTML Generic Page. If someone changes the guid then we
            // are  tossed.
            if (!g_IEIcons[nIndex].szCLSID)
                SHStringFromGUID(CLSID_HTMLDocument, szGuid, GUIDSTR_MAX);
            wnsprintf(szKey, ARRAYSIZE(szKey), TEXT("CLSID\\%s\\InProcServer32"), g_IEIcons[nIndex].szCLSID ? g_IEIcons[nIndex].szCLSID : szGuid);

            long cb = SIZEOF(szModule);
            RegQueryValue(HKEY_CLASSES_ROOT, szKey, szModule, &cb);

        }
        g_IEIcons[nIndex].nIEIcon = Shell_GetCachedImageIndex(szModule, g_IEIcons[nIndex].nIconResourceNum, 0);

        switch(g_IEIcons[nIndex].dwType)
        {
        case IEICONTYPE_GETFILEINFO:
            sfi.iIcon = 0;
            StrCpyN(szModule, TEXT("c:\\notexist"), ARRAYSIZE(szModule));
            StrCatBuff(szModule, g_IEIcons[nIndex].szFileExt, ARRAYSIZE(szModule));
            SHGetFileInfo(szModule, FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES);
            g_IEIcons[nIndex].nDefaultIcon = sfi.iIcon;
            break;

        case IEICONTYPE_DEFAULTICON:
            {
                TCHAR szPath[MAX_PATH];
                DWORD cbSize = SIZEOF(szPath);

                SHGetValue(HKEY_CLASSES_ROOT, g_IEIcons[nIndex].szFileExt, TEXT(""), NULL, szPath, &cbSize);
                g_IEIcons[nIndex].nDefaultIcon = Shell_GetCachedImageIndex(szPath, PathParseIconLocation(szPath), 0);
            }
            break;
        }
    }
}

int IEMapPIDLToSystemImageListIndex(IShellFolder *psfParent, LPCITEMIDLIST pidlChild, int *piSelectedImage)
{
    int nIndex;
    int nIcon = SHMapPIDLToSystemImageListIndex(psfParent, pidlChild, piSelectedImage);

    if (-1 == g_IEIcons[0].nDefaultIcon)
        _GenerateIEIcons();

    for (nIndex = 0; nIndex < ARRAYSIZE(g_IEIcons); nIndex++)
    {
        if ((nIcon == g_IEIcons[nIndex].nDefaultIcon) ||
            (piSelectedImage && *piSelectedImage == g_IEIcons[nIndex].nDefaultIcon))
        {
            nIcon = g_IEIcons[nIndex].nIEIcon;
            if (piSelectedImage)
                *piSelectedImage = nIcon;
            break;
        }
    }
    return nIcon;
}

void IEInvalidateImageList(void)
{
    g_IEIcons[0].nDefaultIcon = -1;
}

int _GetIEHTMLImageIndex()
{
    if (-1 == g_IEIcons[0].nDefaultIcon)
        _GenerateIEIcons();

    return g_IEIcons[0].nIEIcon;
}

// Checks to see if any process at all
// has loaded wininet
static BOOL g_fWininetLoadedSomeplace = FALSE;
BOOL IsWininetLoadedAnywhere()
{
    HANDLE hMutex = NULL;
    BOOL fRet;

    if (g_fWininetLoadedSomeplace)
        return TRUE;

    //
    // Use OpenMutexA so it works on W95.
    // wininet is ansi and created this mutex with CreateMutexA
    hMutex = OpenMutexA(SYNCHRONIZE, FALSE, WININET_STARTUP_MUTEX);

    if (hMutex)
    {
        fRet = TRUE;
        g_fWininetLoadedSomeplace = TRUE;
        CloseHandle(hMutex);
    }
    else
    {
        fRet = FALSE;
    }
    return fRet;
}



//   Checks if global state is offline
BOOL SHIsGlobalOffline(void)
{
    DWORD   dwState = 0, dwSize = sizeof(DWORD);
    BOOL    fRet = FALSE;

    if (!IsWininetLoadedAnywhere())
        return FALSE;

    // Since wininet is already loaded someplace
    // We have to load wininet to check if offline

    if (InternetQueryOptionA(NULL, INTERNET_OPTION_CONNECTED_STATE, &dwState,
        &dwSize))
    {
        if (dwState & INTERNET_STATE_DISCONNECTED_BY_USER)
            fRet = TRUE;
    }

    return fRet;
}

void SetGlobalOffline(BOOL fOffline)
{
    INTERNET_CONNECTED_INFO ci;

    memset(&ci, 0, sizeof(ci));
    if (fOffline) {
        ci.dwConnectedState = INTERNET_STATE_DISCONNECTED_BY_USER;
        ci.dwFlags = ISO_FORCE_DISCONNECTED;
    } else {
        ci.dwConnectedState = INTERNET_STATE_CONNECTED;
    }

    InternetSetOption(NULL, INTERNET_OPTION_CONNECTED_STATE, &ci, sizeof(ci));
}

// This API is documented and is called by apps outside
// the shell such as OE
STDAPI_(void) SetShellOfflineState(BOOL fPutOffline)
{
    BOOL fWasOffline = SHIsGlobalOffline();
    if (fWasOffline != fPutOffline)
    {   
        SetGlobalOffline(fPutOffline); // Set the state
        // Tell all browser windows to update their title   
        SendShellIEBroadcastMessage(WM_WININICHANGE,0,0, 1000); 
    }
}


BOOL GetHistoryFolderPath(LPTSTR pszPath, int cchPath)
{
    INTERNET_CACHE_CONFIG_INFO cci;
    DWORD cbcci = sizeof(INTERNET_CACHE_CONFIG_INFO);

    if (GetUrlCacheConfigInfo(&cci, &cbcci, CACHE_CONFIG_HISTORY_PATHS_FC))
    {
        StrCpyN(pszPath, cci.CachePaths[0].CachePath, cchPath);
        return TRUE;
    }
    return FALSE;
}

// in:
//      pidlRoot    root part of pidl.
//      pidl        equal to or child below pidlRoot
//      pszKey      root key to store stuff under, should match pidlRoot
//      grfMode     read/write
//
// example:
//      pidlRoot = c:\win\favorites
//      pidl     = c:\win\favorites\channels
//      pszKey   = "MenuOrder\Favorites"
//      result -> stream comes from HKCU\...\MenuOrder\Favorites\channels
//

IStream * OpenPidlOrderStream(LPCITEMIDLIST pidlRoot, LPCITEMIDLIST pidl, LPCSTR pszKey, DWORD grfMode)
{
    LPITEMIDLIST pidlAlloc = NULL;
    TCHAR   szRegPath[MAX_URL_STRING];
    TCHAR szKey[MAXIMUM_SUB_KEY_LENGTH];

    SHAnsiToTChar(pszKey, szKey, ARRAYSIZE(szKey));
    StrCpyN(szRegPath, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"), ARRAYSIZE(szRegPath));
    StrCatBuff(szRegPath, szKey, ARRAYSIZE(szRegPath));

    // deal with ordinal vs true pidls
    if (HIWORD(pidlRoot) == 0)
    {
        // Sundown: coercion to int since we are assuming ordinal pidl
        SHGetSpecialFolderLocation(NULL, PtrToLong(pidlRoot), &pidlAlloc);
        pidlRoot = pidlAlloc;
    }

    // build a reg key from the names of the items below the pidlRoot folder. we do
    // this because IEGetDisplayName(SFGAO_FORPARSING) has a bug for file system
    // junctions (channel contents) that returns garbage path names.

    if (pidlRoot)
    {
        LPITEMIDLIST pidlCopy = ILClone(pidl);
        if (pidlCopy)
        {
            LPCITEMIDLIST pidlTail = ILFindChild(pidlRoot, pidlCopy);
            if (pidlTail)
            {
                LPITEMIDLIST pidlNext;
                for (pidlNext = ILGetNext(pidlTail); pidlNext; pidlNext = ILGetNext(pidlNext))
                {
                    WORD cbSave = pidlNext->mkid.cb;
                    pidlNext->mkid.cb = 0;

                    IShellFolder *psf;
                    LPCITEMIDLIST pidlChild;

                    // we do a full bind every time, we could skip this for sub items
                    // and bind from this point down but this code is simpler and binds
                    // aren't that bad...

                    if (SUCCEEDED(IEBindToParentFolder(pidlCopy, &psf, &pidlChild)))
                    {
                        LPWSTR pszName;
                        if (SUCCEEDED(DisplayNameOfAsOLESTR(psf, pidlChild, SHGDN_NORMAL, &pszName)))
                        {
                            StrCatBuff(szRegPath, TEXT("\\"), ARRAYSIZE(szRegPath));
                            StrCatBuff(szRegPath, pszName, ARRAYSIZE(szRegPath));
                            CoTaskMemFree(pszName);
                        }
                        psf->Release();
                    }
                    pidlNext->mkid.cb = cbSave;
                }
            }
            ILFree(pidlCopy);
        }
        if (pidlAlloc)
            ILFree(pidlAlloc);
        return SHOpenRegStream(HKEY_CURRENT_USER, szRegPath, TEXT("Order"), grfMode);
    }
    return NULL;
}

HRESULT GetHTMLElementID(IHTMLElement *pielem, LPTSTR pszName, DWORD cchSize)
{
    // only do this persistence thing if we're in ( or completing ) an operation
    BSTR bstrID = NULL;
    HRESULT hr;

    if (!pielem)
        hr = E_INVALIDARG;
    else if (SUCCEEDED(hr = pielem->get_id(&bstrID)))
    {
        SHUnicodeToTChar(bstrID, pszName, cchSize);
        SysFreeString(bstrID);
    }

    return hr;
}

/**********************************************************************
* SHRestricted2
*
* These are new restrictions that apply to browser only and integrated
* mode.  (Since we're not changing shell32 in browser only mode, we
* need to duplicate the functionality.)
*
* FEATURE: What window will listen to the WM_WININICHANGE
*           lParam="Policy" message and invalidate the cache?
*           Remember not to cache the per zone values.
\**********************************************************************/

// The ZAW compliant policy location.
const TCHAR c_szInfodeliveryBase[] = TEXT("Software\\Policies\\Microsoft\\Internet Explorer\\Infodelivery");
const TCHAR c_szInfodeliveryKey[] = TEXT("Restrictions");

// The normal policy location.
const TCHAR c_szExplorerBase[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies");
const TCHAR c_szExplorerKey[] = TEXT("Explorer");

// The browser policy location that SP2 used
const TCHAR c_szBrowserBase[] = TEXT("Software\\Policies\\Microsoft\\Internet Explorer");
const TCHAR c_szBrowserKey[]  = TEXT("Restrictions");
const TCHAR c_szToolbarKey[]  = TEXT("Toolbars\\Restrictions");

const SHRESTRICTIONITEMS c_rgRestrictionItems[] =
{
    // explorer restrictions
    { REST_NOTOOLBARCUSTOMIZE,      c_szExplorerKey,    TEXT("NoToolbarCustomize") },
    { REST_NOBANDCUSTOMIZE,         c_szExplorerKey,    TEXT("NoBandCustomize")    },
    { REST_SMALLICONS,              c_szExplorerKey,    TEXT("SmallIcons")        },
    { REST_LOCKICONSIZE,            c_szExplorerKey,    TEXT("LockIconSize")      },
    { REST_SPECIFYDEFAULTBUTTONS,   c_szExplorerKey,    TEXT("SpecifyDefaultButtons") },
    { REST_BTN_BACK,                c_szExplorerKey,    TEXT("Btn_Back")      },
    { REST_BTN_FORWARD,             c_szExplorerKey,    TEXT("Btn_Forward")   },
    { REST_BTN_STOPDOWNLOAD,        c_szExplorerKey,    TEXT("Btn_Stop")      },
    { REST_BTN_REFRESH,             c_szExplorerKey,    TEXT("Btn_Refresh")    },
    { REST_BTN_HOME,                c_szExplorerKey,    TEXT("Btn_Home")      },
    { REST_BTN_SEARCH,              c_szExplorerKey,    TEXT("Btn_Search")    },
    { REST_BTN_HISTORY,             c_szExplorerKey,    TEXT("Btn_History")   },
    { REST_BTN_FAVORITES,           c_szExplorerKey,    TEXT("Btn_Favorites") },
    { REST_BTN_ALLFOLDERS,          c_szExplorerKey,    TEXT("Btn_Folders")       },
    { REST_BTN_THEATER,             c_szExplorerKey,    TEXT("Btn_Fullscreen") },
    { REST_BTN_TOOLS,               c_szExplorerKey,    TEXT("Btn_Tools")     },
    { REST_BTN_MAIL,                c_szExplorerKey,    TEXT("Btn_MailNews")  },
    { REST_BTN_FONTS,               c_szExplorerKey,    TEXT("Btn_Size")      },
    { REST_BTN_PRINT,               c_szExplorerKey,    TEXT("Btn_Print")     },
    { REST_BTN_EDIT,                c_szExplorerKey,    TEXT("Btn_Edit")          },
    { REST_BTN_DISCUSSIONS,         c_szExplorerKey,    TEXT("Btn_Discussions")   },
    { REST_BTN_CUT,                 c_szExplorerKey,    TEXT("Btn_Cut")           },
    { REST_BTN_COPY,                c_szExplorerKey,    TEXT("Btn_Copy")          },
    { REST_BTN_PASTE,               c_szExplorerKey,    TEXT("Btn_Paste")         },
    { REST_BTN_ENCODING,            c_szExplorerKey,    TEXT("Btn_Encoding")          },
    { REST_BTN_PRINTPREVIEW,        c_szExplorerKey,    TEXT("Btn_PrintPreview")        },
    { REST_NoUserAssist,            c_szExplorerKey,    TEXT("NoInstrumentation"),      },
    { REST_NoWindowsUpdate,         c_szExplorerKey,    TEXT("NoWindowsUpdate"),        },
    { REST_NoExpandedNewMenu,       c_szExplorerKey,    TEXT("NoExpandedNewMenu"),      },
    { REST_BTN_MEDIABAR,            c_szExplorerKey,    TEXT("Btn_Media"),      },
    // ported from SP1
    { REST_NOFILEURL,               c_szExplorerKey,       TEXT("NoFileUrl"),          },
    // infodelivery restrictions
    { REST_NoChannelUI,             c_szInfodeliveryKey,   TEXT("NoChannelUI")        },
    { REST_NoAddingChannels,        c_szInfodeliveryKey,   TEXT("NoAddingChannels") },
    { REST_NoEditingChannels,       c_szInfodeliveryKey,   TEXT("NoEditingChannels") },
    { REST_NoRemovingChannels,      c_szInfodeliveryKey,   TEXT("NoRemovingChannels") },
    { REST_NoAddingSubscriptions,   c_szInfodeliveryKey,   TEXT("NoAddingSubscriptions") },
    { REST_NoEditingSubscriptions,  c_szInfodeliveryKey,   TEXT("NoEditingSubscriptions") },
    { REST_NoRemovingSubscriptions, c_szInfodeliveryKey,   TEXT("NoRemovingSubscriptions") },
    { REST_NoChannelLogging,        c_szInfodeliveryKey,   TEXT("NoChannelLogging")         },
    { REST_NoManualUpdates,         c_szInfodeliveryKey,   TEXT("NoManualUpdates")        },
    { REST_NoScheduledUpdates,      c_szInfodeliveryKey,   TEXT("NoScheduledUpdates")     },
    { REST_NoUnattendedDialing,     c_szInfodeliveryKey,   TEXT("NoUnattendedDialing")    },
    { REST_NoChannelContent,        c_szInfodeliveryKey,   TEXT("NoChannelContent")       },
    { REST_NoSubscriptionContent,   c_szInfodeliveryKey,   TEXT("NoSubscriptionContent")  },
    { REST_NoEditingScheduleGroups, c_szInfodeliveryKey,   TEXT("NoEditingScheduleGroups") },
    { REST_MaxChannelSize,          c_szInfodeliveryKey,   TEXT("MaxChannelSize")         },
    { REST_MaxSubscriptionSize,     c_szInfodeliveryKey,   TEXT("MaxSubscriptionSize")    },
    { REST_MaxChannelCount,         c_szInfodeliveryKey,   TEXT("MaxChannelCount")        },
    { REST_MaxSubscriptionCount,    c_szInfodeliveryKey,   TEXT("MaxSubscriptionCount")   },
    { REST_MinUpdateInterval,       c_szInfodeliveryKey,   TEXT("MinUpdateInterval")      },
    { REST_UpdateExcludeBegin,      c_szInfodeliveryKey,   TEXT("UpdateExcludeBegin")     },
    { REST_UpdateExcludeEnd,        c_szInfodeliveryKey,   TEXT("UpdateExcludeEnd")       },
    { REST_UpdateInNewProcess,      c_szInfodeliveryKey,   TEXT("UpdateInNewProcess")     },
    { REST_MaxWebcrawlLevels,       c_szInfodeliveryKey,   TEXT("MaxWebcrawlLevels")      },
    { REST_MaxChannelLevels,        c_szInfodeliveryKey,   TEXT("MaxChannelLevels")       },
    { REST_NoSubscriptionPasswords, c_szInfodeliveryKey,   TEXT("NoSubscriptionPasswords")},
    { REST_NoBrowserSaveWebComplete,c_szInfodeliveryKey,   TEXT("NoBrowserSaveWebComplete") },
    { REST_NoSearchCustomization,   c_szInfodeliveryKey,   TEXT("NoSearchCustomization"),  },
    { REST_NoSplash,                c_szInfodeliveryKey,   TEXT("NoSplash"),  },

    // browser restrictions ported from SP2
    { REST_NoFileOpen,              c_szBrowserKey,         TEXT("NoFileOpen"),             },
    { REST_NoFileNew,               c_szBrowserKey,         TEXT("NoFileNew"),              },
    { REST_NoBrowserSaveAs ,        c_szBrowserKey,         TEXT("NoBrowserSaveAs"),        },
    { REST_NoBrowserOptions,        c_szBrowserKey,         TEXT("NoBrowserOptions"),       },
    { REST_NoFavorites,             c_szBrowserKey,         TEXT("NoFavorites"),            },
    { REST_NoSelectDownloadDir,     c_szBrowserKey,         TEXT("NoSelectDownloadDir"),    },
    { REST_NoBrowserContextMenu,    c_szBrowserKey,         TEXT("NoBrowserContextMenu"),   },
    { REST_NoBrowserClose,          c_szBrowserKey,         TEXT("NoBrowserClose"),         },
    { REST_NoOpeninNewWnd,          c_szBrowserKey,         TEXT("NoOpeninNewWnd"),         },
    { REST_NoTheaterMode,           c_szBrowserKey,         TEXT("NoTheaterMode"),          },
    { REST_NoFindFiles,             c_szBrowserKey,         TEXT("NoFindFiles"),            },
    { REST_NoViewSource,            c_szBrowserKey,         TEXT("NoViewSource"),           },
    { REST_GoMenu,                  c_szBrowserKey,         TEXT("RestGoMenu"),             },
    { REST_NoToolbarOptions,        c_szToolbarKey,         TEXT("NoToolbarOptions"),       },
    { REST_AlwaysPromptWhenDownload,c_szBrowserKey,         TEXT("AlwaysPromptWhenDownload"),},

    { REST_NoHelpItem_TipOfTheDay,  c_szBrowserKey,         TEXT("NoHelpItemTipOfTheDay"),  },
    { REST_NoHelpItem_NetscapeHelp, c_szBrowserKey,         TEXT("NoHelpItemNetscapeHelp"), },
    { REST_NoHelpItem_Tutorial,     c_szBrowserKey,         TEXT("NoHelpItemTutorial"),     },
    { REST_NoHelpItem_SendFeedback, c_szBrowserKey,         TEXT("NoHelpItemSendFeedback"), },

    { REST_NoNavButtons,            c_szBrowserKey,         TEXT("NoNavButtons"),           },
    { REST_NoHelpMenu,              c_szBrowserKey,         TEXT("NoHelpMenu"),             },
    { REST_NoBrowserBars,           c_szBrowserKey,         TEXT("NoBrowserBars"),          },
    { REST_NoToolBar,               c_szToolbarKey,         TEXT("NoToolBar"),              },
    { REST_NoAddressBar,            c_szToolbarKey,         TEXT("NoAddressBar"),           },
    { REST_NoLinksBar,              c_szToolbarKey,         TEXT("NoLinksBar"),             },
    
    { REST_NoPrinting,              c_szBrowserKey,         TEXT("NoPrinting")              },

    { REST_No_LaunchMediaBar,       c_szBrowserKey,         TEXT("No_LaunchMediaBar")       },
    { REST_No_MediaBarOnlineContent, c_szBrowserKey,        TEXT("No_MediaBarOnlineContent") },

    {0, NULL, NULL},
};

typedef struct {
    BROWSER_RESTRICTIONS rest;
    DWORD dwAction;    
} ACTIONITEM;

const ACTIONITEM c_ActionItems[] = {
    { REST_NoAddingChannels,        URLACTION_INFODELIVERY_NO_ADDING_CHANNELS },
    { REST_NoEditingChannels,       URLACTION_INFODELIVERY_NO_EDITING_CHANNELS },
    { REST_NoRemovingChannels,      URLACTION_INFODELIVERY_NO_REMOVING_CHANNELS },
    { REST_NoAddingSubscriptions,   URLACTION_INFODELIVERY_NO_ADDING_SUBSCRIPTIONS },
    { REST_NoEditingSubscriptions,  URLACTION_INFODELIVERY_NO_EDITING_SUBSCRIPTIONS },
    { REST_NoRemovingSubscriptions, URLACTION_INFODELIVERY_NO_REMOVING_SUBSCRIPTIONS },
    { REST_NoChannelLogging,        URLACTION_INFODELIVERY_NO_CHANNEL_LOGGING },
};

#define REST_WITHACTION_FIRST   REST_NoAddingChannels
#define REST_WITHACTION_LAST    REST_NoChannelLogging

#define RESTRICTIONMAX (c_rgRestrictionItems[ARRAYSIZE(c_rgRestrictionItems) - 1].rest)

DWORD g_rgRestrictionItemValues[ARRAYSIZE(c_rgRestrictionItems)];

DWORD SHRestricted2W(BROWSER_RESTRICTIONS rest, LPCWSTR pwzUrl, DWORD dwReserved)
{
    // Validate restriction and dwReserved
    if (dwReserved)
    {
        RIPMSG(0, "SHRestricted2W: Invalid dwReserved");
        return 0;
    }

    if (!(InRange(rest, REST_EXPLORER_FIRST, REST_EXPLORER_LAST))
        && !(InRange(rest, REST_INFO_FIRST, REST_INFO_LAST))
        && !(InRange(rest, REST_BROWSER_FIRST, REST_BROWSER_LAST)))
    {
        RIPMSG(0, "SHRestricted2W: Invalid browser restriction");
        return 0;
    }

    // See if the restriction is in place in the URL zone
    // FEATURE: Should we assert on NULL URLs if the restriction is per zone?
    // It might be reasonable to query the global setting.
    if (pwzUrl && InRange(rest, REST_WITHACTION_FIRST, REST_WITHACTION_LAST))
    {
        // Compute the index into the table
        int index = rest - REST_WITHACTION_FIRST;

        ASSERT(c_ActionItems[index].dwAction);

        IInternetSecurityManager *pism = NULL;
        HRESULT hr;
        hr = CoCreateInstance(CLSID_InternetSecurityManager, NULL, CLSCTX_INPROC_SERVER,
                              IID_IInternetSecurityManager, (void**)&pism);
        if (SUCCEEDED(hr) && pism)
        {
            DWORD dwPolicy = 0;
            DWORD dwContext = 0;
            hr = pism->ProcessUrlAction(pwzUrl,
                                        c_ActionItems[index].dwAction,
                                        (BYTE *)&dwPolicy,
                                        sizeof(dwPolicy),
                                        (BYTE *)&dwContext,
                                        sizeof(dwContext),
                                        PUAF_NOUI,
                                        0);
            pism->Release();
            if (SUCCEEDED(hr))
            {
                if (GetUrlPolicyPermissions(dwPolicy) == URLPOLICY_ALLOW)
                    return 0;
                else
                    return 1;    // restrict for query and disallow
            }
        }
    }

    // The cache may be invalid. Check first! We have to use
    // a global named semaphore in case this function is called
    // from a process other than the shell process. (And we're
    // sharing the same count between shell32 and shdocvw.)
    static HANDLE hRestrictions = NULL;
    static long lRestrictionCount = -1;
    if (hRestrictions == NULL)
        hRestrictions = SHGlobalCounterCreate(GUID_Restrictions);
    long lGlobalCount = SHGlobalCounterGetValue(hRestrictions);
    if (lGlobalCount != lRestrictionCount)
    {
        memset((LPBYTE)g_rgRestrictionItemValues, (BYTE)-1, SIZEOF(g_rgRestrictionItemValues));

        lRestrictionCount = lGlobalCount;
    }

    LPCWSTR pszBaseKey;
    if (InRange(rest, REST_EXPLORER_FIRST, REST_EXPLORER_LAST))
        pszBaseKey = c_szExplorerBase;
    else
    {
        if (InRange(rest, REST_BROWSER_FIRST, REST_BROWSER_LAST))
            pszBaseKey = c_szBrowserBase;
        else 
            pszBaseKey = c_szInfodeliveryBase;
    }

    return SHRestrictionLookup(rest, pszBaseKey, c_rgRestrictionItems, g_rgRestrictionItemValues);
}

DWORD SHRestricted2A(BROWSER_RESTRICTIONS rest, LPCSTR pszUrl, DWORD dwReserved)
{
    if (pszUrl)
    {
        WCHAR wzUrl[MAX_URL_STRING];

        ASSERT(ARRAYSIZE(wzUrl) > lstrlenA(pszUrl));        // We only work for Urls of MAX_URL_STRING or shorter.
        AnsiToUnicode(pszUrl, wzUrl, ARRAYSIZE(wzUrl));

        return SHRestricted2W(rest, wzUrl, dwReserved);
    }
    else
    {
        return SHRestricted2W(rest, NULL, dwReserved);
    }
}

/**********************************************************************
*
\**********************************************************************/

#define MAX_SUBSTR_SIZE     100
typedef struct tagURLSub
{
    LPCTSTR szTag;
    DWORD dwType;
} URLSUB;

const static URLSUB c_UrlSub[] = {
    {TEXT("{SUB_PRD}"),     URLSUB_PRD},
    {TEXT("{SUB_PVER}"),    URLSUB_PVER},
    {TEXT("{SUB_OS}"),      URLSUB_OS},
    {TEXT("{SUB_OVER}"),    URLSUB_OVER},
    {TEXT("{SUB_OLCID}"),   URLSUB_OLCID},
    {TEXT("{SUB_CLCID}"),   URLSUB_CLCID},
    {TEXT("{SUB_CLSID}"),   URLSUB_CLCID},  // legacy support (do NOT use "SUB_CLSID" in new URLs)
    {TEXT("{SUB_RFC1766}"), URLSUB_RFC1766}
};

void GetWebLocaleAsRFC1766(LPTSTR pszLocale, int cchLocale)
{
    LCID lcid;
    TCHAR szValue[MAX_PATH];

    DWORD cbVal = sizeof(szValue);
    DWORD dwType;

    ASSERT(NULL != pszLocale);

    *pszLocale = TEXT('\0');
    
    if ((SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_INTERNATIONAL,
                    REGSTR_VAL_ACCEPT_LANGUAGE, 
                    &dwType, szValue, &cbVal) == ERROR_SUCCESS) &&
        (REG_SZ == dwType))
    {
        TCHAR *psz = szValue;

        //  Use the first one we find so terminate at the comma or semicolon
        while (*psz && (*psz != TEXT(',')) && (*psz != TEXT(';')))
        {
            psz = CharNext(psz);
        }
        *psz = TEXT('\0');

        //  If it's user defined, this will fail and we will fall back
        //  to the system default.
        if (SUCCEEDED(Rfc1766ToLcid(&lcid, szValue)))
        {
            StrCpyN(pszLocale, szValue, cchLocale);
        }
    }

    if (TEXT('\0') == *pszLocale)
    {
        //  No entry in the registry or it's a user defined header.
        //  Either way we fall back to the system default.

        LcidToRfc1766(GetUserDefaultLCID(), pszLocale, cchLocale);
    }
}

HRESULT URLSubstitution(LPCWSTR pszUrlIn, LPWSTR pszUrlOut, DWORD cchSize, DWORD dwSubstitutions)
{
    HRESULT hr = S_OK;
    DWORD dwIndex;
    WCHAR szTempUrl[MAX_URL_STRING];
    ASSERT(cchSize <= ARRAYSIZE(szTempUrl));    // We will truncate anything longer than MAX_URL_STRING

    StrCpyNW(szTempUrl, pszUrlIn, ARRAYSIZE(szTempUrl));

    for (dwIndex = 0; dwIndex < ARRAYSIZE(c_UrlSub); dwIndex++)
    {
        while (IsFlagSet(dwSubstitutions, c_UrlSub[dwIndex].dwType))
        {
            LPWSTR pszTag = StrStr(szTempUrl, c_UrlSub[dwIndex].szTag);

            if (pszTag)
            {
                TCHAR szCopyUrl[MAX_URL_STRING];
                TCHAR szSubStr[MAX_SUBSTR_SIZE];  // The Substitution

                // Copy URL Before Substitution.
                StrCpyN(szCopyUrl, szTempUrl, (int)(pszTag-szTempUrl+1));
                pszTag += lstrlen(c_UrlSub[dwIndex].szTag);

                switch (c_UrlSub[dwIndex].dwType)
                {
                case URLSUB_PRD:
                    MLLoadString(IDS_SUBSTR_PRD, szSubStr, ARRAYSIZE(szSubStr));
                    break;

                case URLSUB_PVER:
                    MLLoadString(IDS_SUBSTR_PVER, szSubStr, ARRAYSIZE(szSubStr));
                    break;

                case URLSUB_OS:
                    {
                        LPCTSTR pszWin95    = _T("95"); // Windows 95
                        LPCTSTR pszWin98    = _T("98"); // Windows 98 (Memphis)
                        LPCTSTR pszWinME    = _T("ME"); // Windows Millenium
                        LPCTSTR pszWinNT4   = _T("N4"); // Windows NT 4
                        LPCTSTR pszWinNT5   = _T("N5"); // Windows 2000
                        LPCTSTR pszWinNT6   = _T("N6"); // Windows XP (Whistler)
                        LPCTSTR pszUnknown  = _T("");   // error
                        LPCTSTR psz         = pszUnknown;

                        if (IsOS(OS_WINDOWS))
                        {
                            if (IsOS(OS_MILLENNIUMORGREATER))
                                psz = pszWinME;
                            else if (IsOS(OS_WIN98ORGREATER))
                                psz = pszWin98;
                            else if (IsOS(OS_WIN95ORGREATER))
                                psz = pszWin95;
                            else
                            {
                                ASSERT(FALSE); // What OS is this?
                            }
                        }
                        else if (IsOS(OS_NT))
                        {
                            if (IsOS(OS_WHISTLERORGREATER))
                                psz = pszWinNT6;
                            else if (IsOS(OS_WIN2000ORGREATER))
                                psz = pszWinNT5;
                            else if (IsOS(OS_NT4ORGREATER))
                                psz = pszWinNT4;
                            else
                            {
                                ASSERT(FALSE); // What OS is this?
                            }
                        }
                        else
                        {
                            ASSERT(FALSE); // What OS is this?
                        }

                        StrCpyN(szSubStr, psz, ARRAYSIZE(szSubStr));
                    }
                    break;

                case URLSUB_OVER:
                    {
                        LPCTSTR pszVersion_5_1  = _T("5.1");    // Version 5.1 (Whistler)
                        LPCTSTR pszUnknown      = _T("");       // error
                        LPCTSTR psz             = pszUnknown;

                        if (IsOS(OS_WINDOWS))
                        {
                            ASSERT(FALSE); // Not supported under Windows Millenium or lesser.
                        }
                        else if (IsOS(OS_NT))
                        {
                            if (IsOS(OS_WHISTLERORGREATER))
                                psz = pszVersion_5_1;
                            else
                            {
                                ASSERT(FALSE);  // Not supported under Windows 2000 or lesser.
                            }
                        }
                        else
                        {
                            ASSERT(FALSE);  // What OS is this?
                        }

                        StrCpyN(szSubStr, psz, ARRAYSIZE(szSubStr));
                    }
                    break;

                case URLSUB_OLCID:
                    wnsprintf(szSubStr, ARRAYSIZE(szSubStr), _T("%#04lx"), GetSystemDefaultLCID());
                    break;

                case URLSUB_CLCID:
                    wnsprintf(szSubStr, ARRAYSIZE(szSubStr), _T("%#04lx"), GetUserDefaultLCID());
                    break;

                case URLSUB_RFC1766:
                    GetWebLocaleAsRFC1766(szSubStr, ARRAYSIZE(szSubStr));
                    break;

                default:
                    szSubStr[0] = TEXT('\0');
                    ASSERT(FALSE);  // Not Impl.
                    hr = E_NOTIMPL;
                    break;
                }
                // Add the Substitution String to the end (will become the middle)
                StrCatBuff(szCopyUrl, szSubStr, ARRAYSIZE(szCopyUrl));
                // Add the rest of the URL after the substitution substring.
                StrCatBuff(szCopyUrl, pszTag, ARRAYSIZE(szCopyUrl));
                StrCpyN(szTempUrl, szCopyUrl, ARRAYSIZE(szTempUrl));
            }
            else
                break;  // This will allow us to replace all the occurances of this string.
        }
    }
    StrCpyN(pszUrlOut, szTempUrl, cchSize);

    return hr;
}


// inetcpl.cpl uses this.
STDAPI URLSubRegQueryA(LPCSTR pszKey, LPCSTR pszValue, BOOL fUseHKCU,
                           LPSTR pszUrlOut, DWORD cchSizeOut, DWORD dwSubstitutions)
{
    HRESULT hr;
    TCHAR szKey[MAX_PATH];
    TCHAR szValue[MAX_PATH];
    TCHAR szUrlOut[MAX_URL_STRING];

    AnsiToTChar(pszKey, szKey, ARRAYSIZE(szKey));
    AnsiToTChar(pszValue, szValue, ARRAYSIZE(szValue));
    hr = URLSubRegQueryW(szKey, szValue, fUseHKCU, szUrlOut, ARRAYSIZE(szUrlOut), dwSubstitutions);
    TCharToAnsi(szUrlOut, pszUrlOut, cchSizeOut);

    return hr;
}


HRESULT URLSubRegQueryW(LPCWSTR pszKey, LPCWSTR pszValue, BOOL fUseHKCU,
                           LPWSTR pszUrlOut, DWORD cchSizeOut, DWORD dwSubstitutions)
{
    HRESULT hr = E_FAIL;
    WCHAR szTempUrl[MAX_URL_STRING];
    DWORD ccbSize = sizeof(szTempUrl);
    if (ERROR_SUCCESS == SHRegGetUSValueW(pszKey, pszValue, NULL, szTempUrl,
                                &ccbSize, !fUseHKCU, NULL, NULL))
    {
        hr = URLSubstitution(szTempUrl, pszUrlOut, cchSizeOut, dwSubstitutions);
    }

    return hr;
}

// note that anyone inside shdocvw should pass hInst==NULL to
// ensure that pluggable UI works correctly. anyone outside of shdocvw
// must pass an hInst for their appropriate resource dll
HRESULT URLSubLoadString(HINSTANCE hInst, UINT idRes, LPWSTR pszUrlOut,
                         DWORD cchSizeOut, DWORD dwSubstitutions)
{
    HRESULT hr = E_FAIL;
    WCHAR   szTempUrl[MAX_URL_STRING];
    int     nStrLen;

    nStrLen = 0;

    if (hInst == NULL)
    {
        // this is for internal users who want pluggable UI to work
        nStrLen = MLLoadStringW(idRes, szTempUrl, ARRAYSIZE(szTempUrl));
    }
    else
    {
        // this is for external users who use us to load some
        // of their own resources but whom we can't change (like shell32)
        nStrLen = LoadStringWrap(hInst, idRes, szTempUrl, ARRAYSIZE(szTempUrl));
    }

    if (nStrLen > 0)
    {
        hr = URLSubstitution(szTempUrl, pszUrlOut, cchSizeOut, dwSubstitutions);
    }

    return hr;
}


/**********************************************************************\
        ILIsUrlChild() will find pidls that exist under "Desktop\The Internet"
    section of the Shell Name Space.  This function includes those items
    and file system items that have a "txt/html.
\**********************************************************************/
BOOL ILIsWeb(LPCITEMIDLIST pidl)
{
    BOOL fIsWeb = FALSE;

    if (pidl)
    {
        if (IsURLChild(pidl, TRUE))
            fIsWeb = TRUE;
        else
        {
            TCHAR szPath[MAX_PATH];

            fIsWeb = (!ILIsRooted(pidl)
            && SUCCEEDED(SHGetPathFromIDList(pidl, szPath)) 
            && (PathIsHTMLFile(szPath) ||
                 PathIsContentType(szPath, TEXT("text/xml"))));
        }
    }

    return fIsWeb;
}

//
// in:
//      pidlTo

STDAPI CreateLinkToPidl(LPCITEMIDLIST pidlTo, LPCTSTR pszDir, LPCTSTR pszTitle, LPTSTR pszOut, int cchOut)
{
    HRESULT hr = E_FAIL;
    TCHAR szPathDest[MAX_URL_STRING];
    BOOL fCopyLnk;

    if (SHGetNewLinkInfo((LPCTSTR)pidlTo, pszDir, szPathDest, &fCopyLnk, SHGNLI_PIDL))
    {
        IShellLinkA *pslA;  // Use A version for W95.
        if (SUCCEEDED(CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellLinkA, &pslA))))
        {
            TCHAR szPathSrc[MAX_URL_STRING];
            DWORD dwAttributes = SFGAO_FILESYSTEM | SFGAO_FOLDER;
            SHGetNameAndFlags(pidlTo, SHGDN_FORPARSING | SHGDN_FORADDRESSBAR, szPathSrc, ARRAYSIZE(szPathSrc), &dwAttributes);

            if (fCopyLnk) 
            {
                if (((dwAttributes & (SFGAO_FILESYSTEM | SFGAO_FOLDER)) == SFGAO_FILESYSTEM) && CopyFile(szPathSrc, szPathDest, TRUE))
                {
                    SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, szPathDest, NULL);
                    SHChangeNotify(SHCNE_FREESPACE, SHCNF_PATH, szPathDest, NULL);
                    hr = S_OK;
                }
                else
                {
                    // load the source object that will be "copied" below (with the ::Save call)
                    SAFERELEASE(pslA);
                    hr = SHGetUIObjectFromFullPIDL(pidlTo, NULL, IID_PPV_ARG(IShellLinkA, &pslA));
                    // this pslA is released at the end of the topmost if
                    if (SUCCEEDED(hr))
                    {
                        IPersistFile *ppf;
                        hr = pslA->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
                        if (SUCCEEDED(hr))
                        {
                            hr = ppf->Save(szPathDest, TRUE);
                            ppf->Release();
                        }
                    }
                }
            } 
            else 
            {
                pslA->SetIDList(pidlTo);

                // make sure the working directory is set to the same
                // directory as the app (or document).
                //
                // dont do this for non-FS pidls (ie control panel)

                if (SFGAO_FILESYSTEM == (dwAttributes & SFGAO_FILESYSTEM | SFGAO_FOLDER)) 
                {
                    ASSERT(!PathIsRelative(szPathSrc));
                    PathRemoveFileSpec(szPathSrc);
                    // Try to get the W version.
                    IShellLinkW* pslW;
                    if (SUCCEEDED(pslA->QueryInterface(IID_PPV_ARG(IShellLinkW, &pslW))))
                    {
                        pslW->SetWorkingDirectory(szPathSrc);
                        pslW->Release();
                    }
                    else
                    {
                        CHAR szPathSrcA[MAX_URL_STRING];
                        SHUnicodeToAnsi(szPathSrc, szPathSrcA, ARRAYSIZE(szPathSrcA));
                        pslA->SetWorkingDirectory(szPathSrcA);
                    }
                }

                IPersistFile *ppf;
                hr = pslA->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
                if (SUCCEEDED(hr)) 
                {
                    if (pszTitle && pszTitle[0]) 
                    {
                        PathRemoveFileSpec(szPathDest);
                        PathAppend(szPathDest, pszTitle);
                        StrCatBuff(szPathDest, TEXT(".lnk"), ARRAYSIZE(szPathDest));
                    }
                    hr = ppf->Save(szPathDest, TRUE);
                    if (pszOut)
                    {
                        StrCpyN(pszOut, szPathDest, cchOut);
                    }
                    ppf->Release();
                }
            }

            SAFERELEASE(pslA);
        }
    }

    return hr;
}

int GetColorComponent(LPTSTR *ppsz)
{
    int iColor = 0;
    if (*ppsz) {
        LPTSTR pBuf = *ppsz;
        iColor = StrToInt(pBuf);

        // find the next comma
        while(pBuf && *pBuf && *pBuf!=TEXT(','))
            pBuf++;

        // if valid and not NULL...
        if (pBuf && *pBuf)
            pBuf++;         // increment

        *ppsz = pBuf;
    }
    return iColor;
}

// Read the registry for a string (REG_SZ) of comma separated RGB values
COLORREF RegGetColorRefString( HKEY hkey, LPTSTR RegValue, COLORREF Value)
{
    TCHAR SmallBuf[80];
    TCHAR *pBuf;
    int iRed, iGreen, iBlue;

    DWORD cb = ARRAYSIZE(SmallBuf);
    if (RegQueryValueEx(hkey, RegValue, NULL, NULL, (LPBYTE)&SmallBuf, &cb)
        == ERROR_SUCCESS)
    {
        pBuf = SmallBuf;

        iRed = GetColorComponent(&pBuf);
        iGreen = GetColorComponent(&pBuf);
        iBlue = GetColorComponent(&pBuf);

        // make sure all values are valid
        iRed    %= 256;
        iGreen  %= 256;
        iBlue   %= 256;

        Value = RGB(iRed, iGreen, iBlue);
    }

    return Value;
}

#ifdef DEBUG // {
//***   SearchDW -- scan for DWORD in buffer
// ENTRY/EXIT
//  pdwBuf  buffer
//  cbBuf   size of buffer in *bytes* (*not* DWORDs)
//  dwVal   DWORD we're looking for
//  dOff    (return) byte offset in buffer; o.w. -1 if not found
//
int SearchDW(DWORD *pdwBuf, int cbBuf, DWORD dwVal)
{
    int dOff;

    for (dOff = 0; dOff < cbBuf; dOff += SIZEOF(DWORD), pdwBuf++) {
        if (*pdwBuf == dwVal)
            return dOff;
    }

    return -1;
}
#endif // }

// NOTE: These are directly copied from fsnotify.c in shell32. Please make sure
// NOTE: any changes are reflected there also.

// this is the NEW SHCNE_UPDATEIMAGE stuff, it passes renough data so that the recieving process
// has a vague chance that it can find the right index to refresh.
STDAPI_(void) _SHUpdateImageA( LPCSTR pszHashItem, int iIndex, UINT uFlags, int iImageIndex )
{
    WCHAR szWHash[MAX_PATH];

    MultiByteToWideChar( CP_ACP, 0, pszHashItem, -1, szWHash, MAX_PATH );

    _SHUpdateImageW( szWHash, iIndex, uFlags, iImageIndex );
}

STDAPI_(void) _SHUpdateImageW( LPCWSTR pszHashItem, int iIndex, UINT uFlags, int iImageIndex )
{
    SHChangeUpdateImageIDList rgPidl;
    SHChangeDWORDAsIDList rgDWord;

    int cLen = MAX_PATH - (lstrlenW( pszHashItem ) + 1);
    cLen *= sizeof( WCHAR );

    if ( cLen < 0 )
        cLen = 0;

    // make sure we send a valid index
    if ( iImageIndex == -1 )
        iImageIndex = II_DOCUMENT;

    rgPidl.dwProcessID = GetCurrentProcessId();
    rgPidl.iIconIndex = iIndex;
    rgPidl.iCurIndex = iImageIndex;
    rgPidl.uFlags = uFlags;
    StrCpyNW( rgPidl.szName, pszHashItem, MAX_PATH );
    rgPidl.cb = (USHORT)(sizeof( rgPidl ) - cLen);
    _ILNext( (LPITEMIDLIST) &rgPidl )->mkid.cb = 0;

    rgDWord.cb = (unsigned short) PtrDiff(&rgDWord.cbZero, &rgDWord);
    rgDWord.dwItem1 = (DWORD) iImageIndex;
    rgDWord.dwItem2 = 0;
    rgDWord.cbZero = 0;

    // pump it as an extended event
    SHChangeNotify( SHCNE_UPDATEIMAGE, SHCNF_IDLIST, &rgDWord, &rgPidl );
}

STDAPI_(int) _SHHandleUpdateImage( LPCITEMIDLIST pidlExtra )
{
    SHChangeUpdateImageIDList * pUs = (SHChangeUpdateImageIDList*) pidlExtra;

    if ( !pUs )
    {
        return -1;
    }

    // if in the same process, or an old style notification
    if ( pUs->dwProcessID == GetCurrentProcessId())
    {
        return (int) pUs->iCurIndex;
    }
    else
    {
        WCHAR szBuffer[MAX_PATH];
        int iIconIndex = *(int UNALIGNED *)((BYTE *)&pUs->iIconIndex);
        UINT uFlags = *(UINT UNALIGNED *)((BYTE *)&pUs->uFlags);

        ualstrcpynW( szBuffer, pUs->szName, ARRAYSIZE(szBuffer) );

        // we are in a different process, look up the hash in our index to get the right one...

        return Shell_GetCachedImageIndex( szBuffer, iIconIndex, uFlags );
    }
}

VOID CleanExploits(PWSTR psz)
{
    while (*psz)
    {
        if (*psz<L' ')
        {
            *psz = L' ';
        }
        psz++;
    }
}

HRESULT FormatUrlForDisplay(LPWSTR pwzURL, LPWSTR pwzFriendly, UINT cchBuf, LPWSTR pwzFrom, UINT cbFrom, BOOL fSeperate, DWORD dwCodePage, PWSTR pwzCachedFileName)
{
    const   DWORD       dwMaxPathLen        = 32;
    const   DWORD       dwMaxHostLen        = 32;
    const   DWORD       dwMaxTemplateLen    = 64;
    const   DWORD       dwElipsisLen        = 3;
    const   CHAR        rgchElipsis[]       = "...";
    const   WCHAR       rgwchElipsis[]       = L"...";

    HRESULT hrRC = E_FAIL;
    HRESULT hr;

    if (pwzURL==NULL || pwzFriendly==NULL)
        return E_POINTER;

    *pwzFriendly = '\0';

    if (!*pwzURL)
        return S_OK;

    if (!cchBuf)
        return E_FAIL;

    // Wininet can't deal with code pages other than CP_ACP so convert the URL ourself and call InterCrackUrlA
    URL_COMPONENTSA urlComp;
    CHAR   rgchScheme[INTERNET_MAX_SCHEME_LENGTH];
    CHAR   rgchHostName[INTERNET_MAX_HOST_NAME_LENGTH];
    CHAR   rgchUrlPath[MAX_PATH];
    CHAR   rgchCanonicalUrl[MAX_URL_STRING];
    LPSTR  pszURL;
    DWORD  dwLen;

    dwLen = MAX_URL_STRING * 2;
    if ((pszURL = (LPSTR)LocalAlloc(LPTR, dwLen * sizeof(CHAR))) != NULL)
    {
        SHUnicodeToAnsiCP(dwCodePage, pwzURL, pszURL, dwLen);

        dwLen = ARRAYSIZE(rgchCanonicalUrl);
        hr = UrlCanonicalizeA(pszURL, rgchCanonicalUrl, &dwLen, 0);
        if (SUCCEEDED(hr))
        {
            ZeroMemory(&urlComp, sizeof(urlComp));

            urlComp.dwStructSize = sizeof(urlComp);
            urlComp.lpszHostName = rgchHostName;
            urlComp.dwHostNameLength = ARRAYSIZE(rgchHostName);
            urlComp.lpszUrlPath = rgchUrlPath;
            urlComp.dwUrlPathLength = ARRAYSIZE(rgchUrlPath);
            urlComp.lpszScheme = rgchScheme;
            urlComp.dwSchemeLength = ARRAYSIZE(rgchScheme);

            hr = InternetCrackUrlA(rgchCanonicalUrl, lstrlenA(rgchCanonicalUrl), 0, &urlComp);
            if (SUCCEEDED(hr))
            {
                DWORD dwPathLen = lstrlenA(rgchUrlPath);
                DWORD dwHostLen = lstrlenA(rgchHostName);
                DWORD dwSchemeLen = lstrlenA(rgchScheme);

                CHAR   rgchHostForDisplay[INTERNET_MAX_HOST_NAME_LENGTH];
                CHAR   rgchPathForDisplay[MAX_PATH];

                ZeroMemory(rgchHostForDisplay, sizeof(rgchHostForDisplay));
                ZeroMemory(rgchPathForDisplay, sizeof(rgchPathForDisplay));

                if (dwHostLen>dwMaxHostLen)
                {
                    DWORD   dwOverFlow = dwHostLen - dwMaxHostLen + dwElipsisLen + 1;
                    wnsprintfA(rgchHostForDisplay, ARRAYSIZE(rgchHostForDisplay), "%s%s", rgchElipsis, rgchHostName+dwOverFlow);
                    dwHostLen = dwMaxHostLen;
                }
                else
                    StrCpyNA(rgchHostForDisplay, rgchHostName, ARRAYSIZE(rgchHostForDisplay));

                if (dwPathLen>dwMaxPathLen)
                {
                    DWORD   dwOverFlow = dwPathLen - dwMaxPathLen + dwElipsisLen;
                    wnsprintfA(rgchPathForDisplay, ARRAYSIZE(rgchPathForDisplay), "/%s%s", rgchElipsis, rgchUrlPath+dwOverFlow);
                    dwPathLen = dwMaxPathLen;
                }
                else
                    StrCpyNA(rgchPathForDisplay, rgchUrlPath, ARRAYSIZE(rgchPathForDisplay));

                WCHAR   rgwchScheme[INTERNET_MAX_SCHEME_LENGTH];
                WCHAR   rgwchHostForDisplay[INTERNET_MAX_HOST_NAME_LENGTH];
                WCHAR   rgwchPathForDisplay[MAX_PATH];
                WCHAR   rgwchUrlPath[MAX_PATH];

                SHAnsiToUnicodeCP(dwCodePage, rgchScheme, rgwchScheme, ARRAYSIZE(rgwchScheme));
                SHAnsiToUnicodeCP(dwCodePage, rgchHostForDisplay, rgwchHostForDisplay, ARRAYSIZE(rgwchHostForDisplay));
                SHAnsiToUnicodeCP(dwCodePage, rgchPathForDisplay, rgwchPathForDisplay, ARRAYSIZE(rgwchPathForDisplay));
                SHAnsiToUnicodeCP(dwCodePage, rgchUrlPath, rgwchUrlPath, ARRAYSIZE(rgwchUrlPath));

                if (pwzCachedFileName && *pwzCachedFileName)
                {
                    WCHAR szUrlPath[MAX_PATH];
                    DWORD cc = ARRAYSIZE(rgchUrlPath);
                    if (FAILED(_PrepareURLForDisplayUTF8W(pwzCachedFileName, szUrlPath, &cc, TRUE, dwCodePage)))
                    {
                        StrCpyNW(szUrlPath, pwzCachedFileName, ARRAYSIZE(szUrlPath));
                    }
                    CleanExploits(szUrlPath);

                    dwPathLen = lstrlenW(szUrlPath);
                    if (dwPathLen>dwMaxPathLen)
                    {
                        DWORD   dwOverFlow = dwPathLen - dwMaxPathLen + dwElipsisLen;
                        wnsprintfW(rgwchPathForDisplay, ARRAYSIZE(rgwchPathForDisplay), L"/%s%s", rgwchElipsis, szUrlPath+dwOverFlow);
                        dwPathLen = dwMaxPathLen;
                    }
                    else
                        StrCpyNW(rgwchPathForDisplay, szUrlPath, ARRAYSIZE(rgwchPathForDisplay));
                }
                
                if (fSeperate)
                {
                    // Format string as "X from Y"
                    WCHAR   rgwchTemplate[dwMaxTemplateLen];
                    WCHAR  *pwzFileName = PathFindFileNameW(rgwchPathForDisplay);
                    DWORD   dwCount;

                    //
                    // remove cache decoration goop to map ie5setup[1].exe to ie5setup.exe
                    //
                    PathUndecorateW(pwzFileName);

                    ZeroMemory(rgwchTemplate, sizeof(rgwchTemplate));
                    dwCount = MLLoadString(IDS_TARGETFILE, rgwchTemplate, ARRAYSIZE(rgwchTemplate));
                    if (dwCount > 0)
                    {
                        if (urlComp.nScheme == INTERNET_SCHEME_FILE)
                        {
                            StrCpyNW(rgwchHostForDisplay, rgwchUrlPath, ARRAYSIZE(rgwchPathForDisplay));
                            PathRemoveFileSpecW(rgwchHostForDisplay);
                        }

                        if (dwPathLen+lstrlenW(rgwchTemplate)+dwHostLen <= cchBuf)
                        {
                            //avoid formatting the string as "X from " in the event internetcrackurl fails us
                            if (rgwchHostForDisplay[0] != TEXT('\0'))  
                            {
                                // if necessary return host separately
                                if (pwzFrom && cbFrom)
                                {
                                    StrCpyNW(pwzFriendly, pwzFileName, cchBuf);
                                    StrCpyNW(pwzFrom, rgwchHostForDisplay, cbFrom);
                                }
                                else
                                    _FormatMessage(rgwchTemplate, pwzFriendly, cchBuf, pwzFileName, rgwchHostForDisplay);
                            }
                            else  //hostname is blank, just use filename
                                StrCpyNW(pwzFriendly, pwzFileName, cchBuf);
                            hrRC = S_OK;
                        }
                    }
                }
                else    // !fSeperate
                {
                    if (3+dwPathLen+dwHostLen+dwSchemeLen < cchBuf)
                    {
                        wnsprintf(pwzFriendly, cchBuf, TEXT("%ws://%ws%ws"), rgwchScheme, rgwchHostForDisplay, rgwchPathForDisplay);
                        hrRC = S_OK;
                    }
                }
            }
        }

        LocalFree(pszURL);
        pszURL = NULL;
    }
    
    return(hrRC);
}

BOOL __cdecl _FormatMessage(LPCWSTR szTemplate, LPWSTR szBuf, UINT cchBuf, ...)
{
    BOOL fRet;
    va_list ArgList;
    va_start(ArgList, cchBuf);

    fRet = FormatMessage(FORMAT_MESSAGE_FROM_STRING, szTemplate, 0, 0, szBuf, cchBuf, &ArgList);

    va_end(ArgList);
    return fRet;
}


// Navigate to a given Url (wszUrl) using IE. Returns an error if IE does not exist.
// fNewWindow = TRUE ==> A new window is compulsory
// fNewWindow = FALSE ==> Do not launch a new window if one already is open.
HRESULT NavToUrlUsingIEW(LPCWSTR wszUrl, BOOL fNewWindow)
{
    HRESULT hr = S_OK;

    if (!EVAL(wszUrl))
        return E_INVALIDARG;

    if (IsIEDefaultBrowser() && !fNewWindow)
    {
        // ShellExecute navigates to the Url using the same browser window,
        // if one is already open.

        SHELLEXECUTEINFOW sei = {0};

        sei.cbSize = sizeof(sei);
        sei.lpFile = wszUrl;
        sei.nShow  = SW_SHOWNORMAL;

        ShellExecuteExW(&sei);

    }
    else
    {
        IWebBrowser2 *pwb2;
        hr = CoCreateInstance(CLSID_InternetExplorer, NULL,
                              CLSCTX_LOCAL_SERVER, IID_PPV_ARG(IWebBrowser2, &pwb2));
        if (SUCCEEDED(hr))
        {
            LBSTR::CString          strUrl( wszUrl );

            VARIANT varURL;
            varURL.vt = VT_BSTR;
            varURL.bstrVal = strUrl;

            VARIANT varFlags;
            varFlags.vt = VT_I4;
            varFlags.lVal = 0;

            hr = pwb2->Navigate2(&varURL, &varFlags, PVAREMPTY, PVAREMPTY, PVAREMPTY);
            ASSERT(SUCCEEDED(hr)); // mikesh sez there's no way for Navigate2 to fail
            hr = pwb2->put_Visible( TRUE );
            pwb2->Release();
        }
    }
    return hr;
}

HRESULT NavToUrlUsingIEA(LPCSTR szUrl, BOOL fNewWindow)
{
    WCHAR   wszUrl[INTERNET_MAX_URL_LENGTH];

    AnsiToUnicode(szUrl, wszUrl, ARRAYSIZE(wszUrl));

    return NavToUrlUsingIEW(wszUrl, fNewWindow);
}


// MultiByteToWideChar doesn't truncate if the buffer is too small.
// these utils do.
// returns:
//      # of chars converted (WIDE chars) into out buffer (pwstr)

int _AnsiToUnicode(UINT uiCP, LPCSTR pstr, LPWSTR pwstr, int cch)
{
    int cchDst = 0;

    ASSERT(IS_VALID_STRING_PTRA(pstr, -1));
    ASSERT(NULL == pwstr || IS_VALID_WRITE_BUFFER(pwstr, WCHAR, cch));

    if (cch && pwstr)
        pwstr[0] = 0;

    switch (uiCP)
    {
        case 1200:                      // UCS-2 (Unicode)
            uiCP = 65001;
            // fall through
        case 50000:                     // "User Defined"
        case 65000:                     // UTF-7
        case 65001:                     // UTF-8
        {
            INT cchSrc, cchSrcOriginal;

            cchSrc = cchSrcOriginal = lstrlenA(pstr) + 1;
            cchDst = cch;

            if (SUCCEEDED(ConvertINetMultiByteToUnicode(NULL, uiCP, pstr,
                &cchSrc, pwstr, &cchDst)) &&
                cchSrc < cchSrcOriginal)
            {
                LPWSTR pwsz = (LPWSTR)LocalAlloc(LPTR, cchDst * SIZEOF(WCHAR));
                if (pwsz)
                {
                    if (SUCCEEDED(ConvertINetMultiByteToUnicode( NULL, uiCP, pstr,
                        &cchSrcOriginal, pwsz, &cchDst )))
                    {
                        StrCpyNW( pwstr, pwsz, cch );
                        cchDst = cch;
                    }

                    LocalFree(pwsz);
                    pwsz = NULL;
                }
            }
            break;
        }

        default:
            cchDst = MultiByteToWideChar(uiCP, 0, pstr, -1, pwstr, cch);
            if (!cchDst) {

                // failed.

                if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
                    int cchNeeded = MultiByteToWideChar(uiCP, 0, pstr, -1, NULL, 0);

                    if (cchNeeded) {
                        LPWSTR pwsz = (LPWSTR)LocalAlloc(LPTR, cchNeeded * SIZEOF(WCHAR));
                        if (pwsz) {
                            cchDst = MultiByteToWideChar(uiCP, 0, pstr, -1, pwsz, cchNeeded);
                            if (cchDst) {
                                StrCpyNW(pwstr, pwsz, cch);
                                cchDst = cch;
                            }

                            LocalFree(pwsz);
                            pwsz = NULL;
                        }
                    }
                }
            }
            break;
    }
    return cchDst;
}

UINT    g_cfURL = 0;
UINT    g_cfURLW = 0;
UINT    g_cfFileDescA = 0;
UINT    g_cfFileContents = 0;
UINT    g_cfPreferredEffect = 0;
UINT    g_cfPerformedEffect = 0;
UINT    g_cfTargetCLSID = 0;

UINT    g_cfHIDA = 0;
UINT    g_cfFileDescW = 0;

void InitClipboardFormats()
{
    if (g_cfURL == 0)
    {
        g_cfURL = RegisterClipboardFormat(CFSTR_SHELLURL);
        g_cfURLW = RegisterClipboardFormat(CFSTR_INETURLW);
        g_cfFileDescA = RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA);
        g_cfFileContents = RegisterClipboardFormat(CFSTR_FILECONTENTS);
        g_cfPreferredEffect = RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT);
        g_cfPerformedEffect = RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT);
        g_cfTargetCLSID = RegisterClipboardFormat(CFSTR_TARGETCLSID);
        g_cfHIDA = RegisterClipboardFormat(CFSTR_SHELLIDLIST);
        g_cfFileDescW = RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
    }
}


// FEATURE [raymondc] use SHGlobalCounter

// We need to use a cross process browser count.
// We use a named semaphore.
//
EXTERN_C HANDLE g_hSemBrowserCount = NULL;

#define SESSION_COUNT_SEMAPHORE_NAME _T("_ie_sessioncount")

HANDLE GetSessionCountSemaphoreHandle()
{
    if (!g_hSemBrowserCount)
    {
        g_hSemBrowserCount = SHGlobalCounterCreateNamed( SESSION_COUNT_SEMAPHORE_NAME, 0 );
    }

    ASSERT( g_hSemBrowserCount );

    return g_hSemBrowserCount;
}

LONG GetSessionCount()
{
    LONG lPrevCount = 0x7FFFFFFF;
    HANDLE hSem = GetSessionCountSemaphoreHandle();

    ASSERT(hSem);
    if (hSem)
    {
        ReleaseSemaphore(hSem, 1, &lPrevCount);
        WaitForSingleObject(hSem, 0);
    }
    return lPrevCount;


}

LONG IncrementSessionCount()
{
    LONG lPrevCount = 0x7FFFFFFF;
    HANDLE hSem = GetSessionCountSemaphoreHandle();

    ASSERT(hSem);
    if (hSem)
    {
        ReleaseSemaphore(hSem, 1, &lPrevCount);
    }
    return lPrevCount;
}

LONG DecrementSessionCount()
{
    LONG lPrevCount = 0x7FFFFFFF;
    HANDLE hSem = GetSessionCountSemaphoreHandle();
    ASSERT(hSem);
    if (hSem)
    {
        ReleaseSemaphore(hSem, 1, &lPrevCount); // increment first to make sure deadlock
                                                 // never occurs
        ASSERT(lPrevCount > 0);
        if (lPrevCount > 0)
        {
            WaitForSingleObject(hSem, 0);
            WaitForSingleObject(hSem, 0);
            lPrevCount--;
        }
        else
        {
            // Oops - Looks like a bug !
            // Just return it back to normal and leave
            WaitForSingleObject(hSem, 0);
        }
    }
    return lPrevCount;
}




//
// The following is the message that autodial monitors expect to receive
// when it's a good time to hang up
//
#define WM_IEXPLORER_EXITING    (WM_USER + 103)

long SetQueryNetSessionCount(enum SessionOp Op)
{
    long lCount = 0;
    
    switch(Op) {
        case SESSION_QUERY:
            lCount = GetSessionCount();
            TraceMsg(DM_SESSIONCOUNT, "SetQueryNetSessionCount SessionCount=%d (query)", lCount);
            break;

        case SESSION_INCREMENT_NODEFAULTBROWSERCHECK:
        case SESSION_INCREMENT:
            lCount = IncrementSessionCount();
            TraceMsg(DM_SESSIONCOUNT, "SetQueryNetSessionCount SessionCount=%d (incr)", lCount);

            
            if ((PLATFORM_INTEGRATED == WhichPlatform()))
            {
                // Weird name here... But in integrated mode we make every new browser window
                // look like a new session wrt how we use the cache. Basically this is the way things appear to the
                // user. This effects the way we look for new pages vs doing an if modified
                // since.  The ie3/ie4 switch says "look for new pages on each session start"
                // but wininet folks implemented this as a end session name. Woops.
                // Note that things like authentication etc aren't reset by this, but rather
                // only when all browsers are closed via the INTERNET_OPTION_END_BROWSER_SESSION option.
                InternetSetOption(NULL, INTERNET_OPTION_RESET_URLCACHE_SESSION, NULL, 0);
            }

            if (!lCount && (Op == SESSION_INCREMENT))
            {
                // this forces a reload of the title
                DetectAndFixAssociations();
            }
            break;

        case SESSION_DECREMENT:
            lCount = DecrementSessionCount();
            TraceMsg(DM_SESSIONCOUNT, "SetQueryNetSessionCount SessionCount=%d (decr)", lCount);

            if (!lCount) {
                // if we've closed all the net browsers, we need to flush the cache
                InternetSetOption(NULL, INTERNET_OPTION_END_BROWSER_SESSION, NULL, 0);
                InternetSetOption(NULL, INTERNET_OPTION_RESET_URLCACHE_SESSION, NULL, 0);

                // flush the Java VM cache too (if the Java VM is loaded in this process
                // and we're in integrated mode)
                if (WhichPlatform() == PLATFORM_INTEGRATED)
                {
                    HMODULE hmod = GetModuleHandle(TEXT("msjava.dll"));
                    if (hmod)
                    {
                        typedef HRESULT (*PFNNOTIFYBROWSERSHUTDOWN)(void *);
                        FARPROC fp = GetProcAddress(hmod, "NotifyBrowserShutdown");
                        if (fp)
                        {
                            HRESULT hr = ((PFNNOTIFYBROWSERSHUTDOWN)fp)(NULL);
                            ASSERT(SUCCEEDED(hr));
                        }
                    }
                }

                // Inform dial monitor that it's a good time to hang up
                HWND hwndMonitorWnd = FindWindow(TEXT("MS_AutodialMonitor"),NULL);
                if (hwndMonitorWnd) {
                    PostMessage(hwndMonitorWnd,WM_IEXPLORER_EXITING,0,0);
                }
                hwndMonitorWnd = FindWindow(TEXT("MS_WebcheckMonitor"),NULL);
                if (hwndMonitorWnd) {
                    PostMessage(hwndMonitorWnd,WM_IEXPLORER_EXITING,0,0);
                }

                // reset offline mode on all platforms except Win2K.
                OSVERSIONINFOA vi;
                vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
                GetVersionExA(&vi);
                if ( vi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
                    vi.dwMajorVersion < 5)
                {
                    // wininet is loaded - tell it to go online
                    INTERNET_CONNECTED_INFO ci;
                    memset(&ci, 0, sizeof(ci));
                    ci.dwConnectedState = INTERNET_STATE_CONNECTED;
                    InternetSetOption(NULL, INTERNET_OPTION_CONNECTED_STATE, &ci, sizeof(ci));
                }
            }
            break;
    }

    return lCount;
}


#ifdef DEBUG
//---------------------------------------------------------------------------
// Copy the exception info so we can get debug info for Raised exceptions
// which don't go through the debugger.
void _CopyExceptionInfo(LPEXCEPTION_POINTERS pep)
{
    PEXCEPTION_RECORD per;

    per = pep->ExceptionRecord;
    TraceMsg(DM_ERROR, "Exception %x at %#08x.", per->ExceptionCode, per->ExceptionAddress);

    if (per->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
    {
        // If the first param is 1 then this was a write.
        // If the first param is 0 then this was a read.
        if (per->ExceptionInformation[0])
        {
            TraceMsg(DM_ERROR, "Invalid write to %#08x.", per->ExceptionInformation[1]);
        }
        else
        {
            TraceMsg(DM_ERROR, "Invalid read of %#08x.", per->ExceptionInformation[1]);
        }
    }
}
#else
#define _CopyExceptionInfo(x) TRUE
#endif


int WELCallback(void * p, void * pData)
{
    STATURL* pstat = (STATURL*)p;
    if (pstat->pwcsUrl) {
        OleFree(pstat->pwcsUrl);
    }
    return 1;
}

int CALLBACK WELCompare(void * p1, void * p2, LPARAM lParam)
{
    HDSA hdsa = (HDSA)lParam;
    // Sundown: coercion to long because parameter is an index
    STATURL* pstat1 = (STATURL*)DSA_GetItemPtr(hdsa, PtrToLong(p1));
    STATURL* pstat2 = (STATURL*)DSA_GetItemPtr(hdsa, PtrToLong(p2));
    if (pstat1 && pstat2) {
        return CompareFileTime(&pstat2->ftLastVisited, &pstat1->ftLastVisited);
    }

    ASSERT(0);
    return 0;
}

#define MACRO_STR(x) #x
#define VERSION_HEADER_STR "Microsoft Internet Explorer 5.0 Error Log -- " \
                           MACRO_STR(VER_MAJOR_PRODUCTVER) "." \
                           MACRO_STR(VER_MINOR_PRODUCTVER) "." \
                           MACRO_STR(VER_PRODUCTBUILD) "." \
                           MACRO_STR(VER_PRODUCTBUILD_QFE) "\r\n"

SHDOCAPI_(void) IEWriteErrorLog(const EXCEPTION_RECORD* pexr)
{
    HANDLE hfile = INVALID_HANDLE_VALUE;
    _try
    {
        TCHAR szWindows[MAX_PATH];
        GetWindowsDirectory(szWindows, ARRAYSIZE(szWindows));
        PathAppend(szWindows, TEXT("IE4 Error Log.txt"));
        HANDLE hfile = CreateFile(szWindows, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

        if (hfile != INVALID_HANDLE_VALUE)
        {
            const static CHAR c_szCRLF[] = "\r\n";
            DWORD cbWritten;
            CHAR szBuf[MAX_URL_STRING];

            // Write the title and product version.
            WriteFile(hfile, VERSION_HEADER_STR, lstrlenA(VERSION_HEADER_STR), &cbWritten, NULL);

            // Write the current time.
            SYSTEMTIME st;
            FILETIME ft;
            GetSystemTime(&st);
            SystemTimeToFileTime(&st, &ft);
            SHFormatDateTimeA(&ft, NULL, szBuf, SIZECHARS(szBuf));
            const static CHAR c_szCurrentTime[] = "CurrentTime: ";
            WriteFile(hfile, c_szCurrentTime, SIZEOF(c_szCurrentTime)-1, &cbWritten, NULL);
            WriteFile(hfile, szBuf, lstrlenA(szBuf), &cbWritten, NULL);
            WriteFile(hfile, c_szCRLF, SIZEOF(c_szCRLF)-1, &cbWritten, NULL);

            if (pexr) {
                const static CHAR c_szExcCode[] = "Exception Info: Code=%x Flags=%x Address=%x\r\n";
                const static CHAR c_szExcParam[] = "Exception Param:";
                wnsprintfA(szBuf, ARRAYSIZE(szBuf), c_szExcCode, pexr->ExceptionCode, pexr->ExceptionFlags, pexr->ExceptionAddress);
                WriteFile(hfile, szBuf, lstrlenA(szBuf), &cbWritten, NULL);

                if (pexr->NumberParameters) {
                    WriteFile(hfile, c_szExcParam, SIZEOF(c_szExcParam)-1, &cbWritten, NULL);
                    for (UINT iParam=0; iParam<pexr->NumberParameters; iParam++) {
                        wnsprintfA(szBuf, ARRAYSIZE(szBuf), " %x", pexr->ExceptionInformation[iParam]);
                        WriteFile(hfile, szBuf, lstrlenA(szBuf), &cbWritten, NULL);
                    }
                }

                WriteFile(hfile, c_szCRLF, SIZEOF(c_szCRLF)-1, &cbWritten, NULL);
                WriteFile(hfile, c_szCRLF, SIZEOF(c_szCRLF)-1, &cbWritten, NULL);
            }

            IUrlHistoryStg* pUrlHistStg;
            HRESULT hres = CoCreateInstance(CLSID_CUrlHistory, NULL, CLSCTX_INPROC_SERVER,
                IID_PPV_ARG(IUrlHistoryStg, &pUrlHistStg));
            if (SUCCEEDED(hres)) 
            {
                IEnumSTATURL* penum;
                hres = pUrlHistStg->EnumUrls(&penum);
                if (SUCCEEDED(hres)) 
                {
                    // Allocate DSA for an array of STATURL
                    HDSA hdsa = DSA_Create(SIZEOF(STATURL), 32);
                    if (hdsa) 
                    {
                        // Allocate DPA for sorting
                        HDPA hdpa = DPA_Create(32);
                        if (hdpa) 
                        {
                            STATURL stat;
                            stat.cbSize = SIZEOF(stat.cbSize);
                            while(penum->Next(1, &stat, NULL)==S_OK && stat.pwcsUrl) 
                            {
                                DSA_AppendItem(hdsa, &stat);
                                DPA_AppendPtr(hdpa, IntToPtr(DSA_GetItemCount(hdsa)-1));
                            }

                            DPA_Sort(hdpa, WELCompare, (LPARAM)hdsa);
                            for (int i=0; i<10 && i<DPA_GetPtrCount(hdpa) ; i++) 
                            {
                                // Sundown: typecast to long is OK
                                STATURL* pstat = (STATURL*)DSA_GetItemPtr(hdsa, PtrToLong(DPA_GetPtr(hdpa, i)));
                                if (pstat && pstat->pwcsUrl) 
                                {
                                    SHFormatDateTimeA(&pstat->ftLastVisited, NULL, szBuf, SIZECHARS(szBuf));
                                    WriteFile(hfile, szBuf, lstrlenA(szBuf), &cbWritten, NULL);
                                    const static TCHAR c_szColumn[] = TEXT(" -- ");
                                    WriteFile(hfile, c_szColumn, SIZEOF(c_szColumn)-1, &cbWritten, NULL);

                                    WideCharToMultiByte(CP_ACP, 0, pstat->pwcsUrl, -1,
                                                        szBuf, ARRAYSIZE(szBuf), NULL, NULL);
                                    WriteFile(hfile, szBuf, lstrlenA(szBuf), &cbWritten, NULL);

                                    WriteFile(hfile, c_szCRLF, SIZEOF(c_szCRLF)-1, &cbWritten, NULL);
                                } 
                                else 
                                {
                                    ASSERT(0);
                                }
                            }

                            DPA_Destroy(hdpa);
                            hdpa = NULL;
                        }

                        DSA_DestroyCallback(hdsa, WELCallback, NULL);
                        hdsa = NULL;
                    }
                    penum->Release();
                }
                else
                {
                    ASSERT(0);
                }
                pUrlHistStg->Release();
            }
            else 
            {
                ASSERT(0);
            }

            CloseHandle( hfile );
            hfile = INVALID_HANDLE_VALUE;
        }
    }
    _except((SetErrorMode(SEM_NOGPFAULTERRORBOX),
            _CopyExceptionInfo(GetExceptionInformation()),
            UnhandledExceptionFilter(GetExceptionInformation())
            ))
    {
        // We hit an exception while handling an exception.
        // Do nothing; we have already displayed the error dialog box.
        if (hfile != INVALID_HANDLE_VALUE) {
            CloseHandle(hfile);
        }
    }
    __endexcept
}

IStream* SHGetViewStream(LPCITEMIDLIST pidl, DWORD grfMode, LPCTSTR pszName, LPCTSTR pszStreamMRU, LPCTSTR pszStreams)
{
    IStream *pstm = NULL;
    static DWORD s_dwMRUSize = 0;
    DWORD dwSize = sizeof(s_dwMRUSize);

    if ((0 == s_dwMRUSize) &&
        (ERROR_SUCCESS != SHGetValue(HKEY_CURRENT_USER, pszStreamMRU, TEXT("MRU Size"), NULL, (void *) &s_dwMRUSize, &dwSize)))
    {
        s_dwMRUSize = 200;          // The default.
    }

    ASSERT(pidl);

    // should be checked by caller - if this is not true we'll flush the
    // MRU cache with internet pidls!  FTP and other URL Shell Extension PIDLs
    // that act like a folder and need similar persistence and fine.  This
    // is especially true because recently the cache size was increased from
    // 30 or so to 200.
    ASSERT(ILIsEqual(pidl, c_pidlURLRoot) || !IsBrowserFrameOptionsPidlSet(pidl, BFO_BROWSER_PERSIST_SETTINGS));

    // Now lets try to save away the other information associated with view.
    IMruDataList *pmru;
    if (SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_MruLongList, NULL, IID_PPV_ARG(IMruDataList, &pmru))))
    {
        if (SUCCEEDED(pmru->InitData(s_dwMRUSize, MRULISTF_USE_ILISEQUAL, HKEY_CURRENT_USER, pszStreamMRU, NULL)))
        {
            DWORD cbPidl = ILGetSize(pidl);
            //  need to walk the list
            //  and find this guy
            int iIndex;
            BOOL fFoundPidl = SUCCEEDED(pmru->FindData((const BYTE *)pidl, cbPidl, &iIndex));

            // Did we find the item?
            if (!fFoundPidl && ((grfMode & (STGM_READ|STGM_WRITE|STGM_READWRITE)) == STGM_READ))
            {
                // Do not  create the stream if it does not exist and we are
                // only reading
            }
            else
            {
                // Note that we always create the key here, since we have
                // already checked whether we are just reading and the MRU
                // thing does not exist
                HKEY hkCabStreams = SHGetShellKey(SHELLKEY_HKCU_EXPLORER, pszStreams, TRUE);
                if (hkCabStreams )
                {
                    DWORD dwSlot;
                    if (SUCCEEDED(pmru->AddData((const BYTE *)pidl, cbPidl, &dwSlot)))
                    {
                        HKEY hkValues;
                        TCHAR szValue[32], szSubVal[64];
                        wnsprintf(szValue, ARRAYSIZE(szValue), TEXT("%d"), dwSlot);

                        if (!fFoundPidl && RegOpenKey(hkCabStreams, szValue, &hkValues) == ERROR_SUCCESS)
                        {
                            // This means that we have created a new MRU
                            // item for this PIDL, so clear out any
                            // information residing at this slot
                            // Note that we do not just delete the key,
                            // since that could fail if it has any sub-keys
                            DWORD dwType, dwSize = ARRAYSIZE(szSubVal);

                            while (RegEnumValue(hkValues, 0, szSubVal, &dwSize, NULL, &dwType, NULL, NULL) == ERROR_SUCCESS)
                            {
                                if (RegDeleteValue(hkValues, szSubVal) != ERROR_SUCCESS)
                                {
                                    break;
                                }
                            }

                            RegCloseKey(hkValues);
                        }
                        pstm = OpenRegStream(hkCabStreams, szValue, pszName, grfMode);
                    }

                    RegCloseKey(hkCabStreams);
                }
            }
        }

        pmru->Release();
    }


    return pstm;
}


#define c_szExploreClass TEXT("ExploreWClass")
#define c_szIExploreClass TEXT("IEFrame")
#ifdef IE3CLASSNAME
#define c_szCabinetClass TEXT("IEFrame")
#else
#define c_szCabinetClass TEXT("CabinetWClass")
#endif


BOOL IsNamedWindow(HWND hwnd, LPCTSTR pszClass)
{
    TCHAR szClass[32];

    GetClassName(hwnd, szClass, ARRAYSIZE(szClass));
    return StrCmp(szClass, pszClass) == 0;
}

BOOL IsTrayWindow(HWND hwnd)
{
    return IsNamedWindow(hwnd, TEXT(WNDCLASS_TRAYNOTIFY));
}

BOOL IsExplorerWindow(HWND hwnd)
{
    return IsNamedWindow(hwnd, c_szExploreClass);
}

BOOL IsFolderWindow(HWND hwnd)
{
    TCHAR szClass[32];

    GetClassName(hwnd, szClass, ARRAYSIZE(szClass));
    return (StrCmp(szClass, c_szCabinetClass) == 0) || (StrCmp(szClass, c_szIExploreClass) == 0);
}

HRESULT _SendOrPostDispatchMessage(HWND hwnd, WPARAM wParam, LPARAM lParam, BOOL fPostMessage, BOOL fCheckFirst)
{
    HRESULT hr = HRESULT_FROM_WIN32(ERROR_BUSY);
    DWORD idProcess;

    // in case of wParam = DSID_NAVIGATEIEBROWSER, lParam is LocalAlloced structure
    // so we better make sure we are in process 'coz otherwise will fault
    GetWindowThreadProcessId(hwnd, &idProcess);
    if (idProcess == GetCurrentProcessId() && IsWindowEnabled(hwnd) && IsWindowVisible(hwnd))
    {
        if (!fPostMessage || fCheckFirst)
        {
            //  sync or we are querying the windows readiness
            ULONG_PTR result;
            if (SendMessageTimeoutA(hwnd, WMC_DISPATCH, (fCheckFirst ? DSID_NOACTION : wParam),
                lParam, SMTO_ABORTIFHUNG, 400, &result))
                hr = (HRESULT) result;
        }

        //  handle the post only if the window was ready
        if (fPostMessage && (!fCheckFirst || SUCCEEDED(hr)))
            hr = (PostMessage(hwnd, WMC_DISPATCH, wParam, lParam) ? S_OK : E_FAIL);
    }

    return hr;
}

//---------------------------------------------------------------------------

HRESULT FindBrowserWindowOfClass(LPCTSTR pszClass, WPARAM wParam, LPARAM lParam, BOOL fPostMessage, HWND* phwnd)
{
    //If there is no window, assume the user is in the process of shutting down IE, and return E_FAIL

    //Otherwise, if there is at least one window, start cycling through the windows until you find
    //one that's not busy, and give it our message.  If all are busy, return
    //HRESULT_FROM_WIN32(ERROR_BUSY)
    HWND hwnd = NULL;
    HRESULT hr = E_FAIL;

    while (FAILED(hr)
        && (hwnd = FindWindowEx(NULL, hwnd, pszClass, NULL)) != NULL)
    {
        hr = _SendOrPostDispatchMessage(hwnd, wParam, lParam, fPostMessage, fPostMessage);
    }

    *phwnd = hwnd;
    return hr;
}

//This common function gets called when the DDE engine doesn't seem to care in which window something
//happens.  It returns in which window that something happened.  0 means all windows are busy.
//
//phwnd: a pointer the hwnd to which to send the message.  <= 0 means any window will do.
//       this is also an out parameter that specifies in which window it happened.
//fPostMessage: when doing navigations, we have to do a PostMessage instead of a SendMessageTimeout
//       or a CoCreateInstance later on in CDocObjectHost::_BindFileMoniker will fail.  So when
//       this function is called from CDDEAuto_Navigate, we make this flag TRUE
HRESULT CDDEAuto_Common(WPARAM wParam, LPARAM lParam, HWND *phwnd, BOOL fPostMessage)
{
    HRESULT hr = HRESULT_FROM_WIN32(ERROR_BUSY);
    HWND hwnd;

    //if we're told to go to a specific window
    if (phwnd && (*phwnd != (HWND)-1))
    {
        hr = _SendOrPostDispatchMessage(*phwnd, wParam, lParam, fPostMessage, FALSE);
    }

    if (HRESULT_FROM_WIN32(ERROR_BUSY) == hr)
    {
        hr = FindBrowserWindowOfClass(c_szIExploreClass, wParam, lParam, fPostMessage, &hwnd);
        if (!hwnd)
            hr = FindBrowserWindowOfClass(c_szCabinetClass, wParam, lParam, fPostMessage, &hwnd);

        if (phwnd)
            *phwnd = hwnd;
    }
    return hr;
}

//
//  Before changing the behavior of this function look at itemmenu.cpp in
//  cdfview.
//
HRESULT CDDEAuto_Navigate(BSTR str, HWND *phwnd, long lLaunchNewWindow) // the long used to be for lTransID, but it was always ignored...
{                                                                       // so I am using it to tell us if we want to absolutely create an new window or not...
    DDENAVIGATESTRUCT *pddens = NULL;
    HRESULT hres = E_FAIL;

    if (phwnd == NULL)
        return E_INVALIDARG;

    pddens = new DDENAVIGATESTRUCT;
    if (!pddens)
        hres = E_OUTOFMEMORY;
    else
    {
        pddens->wszUrl = StrDupW(str);
        if (!pddens->wszUrl)
        {
            hres = E_OUTOFMEMORY;
        }
        else
        {
            // Don't do the navigate if *phwnd == 0, in that case we want to either
            // create a new window or activate an existing one that already is viewing
            // this URL.
            
            if ((*phwnd != NULL) && !lLaunchNewWindow)
            {
                BOOL fForceWindowReuse = FALSE;
                BSTR bstrUrl = NULL;
                // If there is even a single window with a location 
                // you are basically assured that you cannot force a 
                // reuse of windows. essentially

               // case 1 : only iexplore -nohome windows implies we want to force reuse
               // case 2 : only windows that have a location - we don't want to force reuse
               //          just follow user's preference
               // case 3: mix of iexplore -nohome windows and windows with location. we don't
               //         know what state we are in - don't force reuse
                hres = CDDEAuto_get_LocationURL(&bstrUrl, *phwnd);

                if (FAILED(hres) ||
                   (!bstrUrl)   ||
                   (SUCCEEDED(hres) && (*bstrUrl == L'\0')))
                {
                    fForceWindowReuse = TRUE;
                }
                if (bstrUrl)
                    SysFreeString(bstrUrl);
                    
                if ( !(GetAsyncKeyState(VK_SHIFT) < 0)
                    && (fForceWindowReuse || SHRegGetBoolUSValue(REGSTR_PATH_MAIN, TEXT("AllowWindowReuse"), FALSE, TRUE)))
                {
                    hres = CDDEAuto_Common(DSID_NAVIGATEIEBROWSER, (LPARAM)pddens, phwnd, FALSE);
                }
            }

            if (SUCCEEDED(hres) && (*phwnd != 0) && (*phwnd != (HWND)-1))
            {
                // We found an existing browser window and successfully sent the
                // navigate message to it. Make the window foreground.
                SetForegroundWindow(*phwnd);

                if (IsIconic(*phwnd))
                    ShowWindowAsync(*phwnd, SW_RESTORE);
            }

            //
            // If we are using whatever window and all the browser windows are busy
            // (*phwnd == 0), or if there's no browser window opened (*phwnd == -1)
            // or we are asked to create a new one, then take the official OLE automation
            // route to start a new window.
            //
            if ((*phwnd == 0) ||
                (*phwnd == (HWND)-1))
            {
                //WARNING: this route doesn't give us the ability to return the hwnd of the window
                //in which the navigation took place (while we could - it's too hard and not worth it)
                LPITEMIDLIST pidlNew;
                hres = IECreateFromPathW(str, &pidlNew);
                if (SUCCEEDED(hres))
                {
                    if (!lLaunchNewWindow)
                    {
                        // See if there is already a browser viewing this URL, if so just
                        // make him foreground otherwise create a new browser.
                        hres = WinList_FindFolderWindow(pidlNew, NULL, phwnd, NULL);
                    }
                    else
                    {
                        // we don't look in the winlist if the caller explicitly wants a new window
                        hres = S_FALSE;
                    }

                    if (S_OK == hres)
                    {
                        ILFree(pidlNew);
                        SetForegroundWindow(*phwnd);
                        ShowWindow(*phwnd, SW_SHOWNORMAL);
                    }
                    else
                    {
                        SHOpenNewFrame(pidlNew, NULL, 0, COF_IEXPLORE);
                    }
                }
            }
        }

        // It will be set to NULL if we don't need to free it.
        if (pddens)
        {
            if (pddens->wszUrl)
            {
                LocalFree(pddens->wszUrl);
                pddens->wszUrl = NULL;
            }

            delete pddens;
        }    
    }

    return hres;
}

HRESULT CDDEAuto_get_LocationURL(BSTR * pstr, HWND hwnd)
{
    return CDDEAuto_Common(DSID_GETLOCATIONURL, (LPARAM)pstr, &hwnd, FALSE);
}

HRESULT CDDEAuto_get_LocationTitle(BSTR * pstr, HWND hwnd)
{
    return CDDEAuto_Common(DSID_GETLOCATIONTITLE, (LPARAM)pstr, &hwnd, FALSE);
}

HRESULT CDDEAuto_get_HWND(long * phwnd)
{
    return CDDEAuto_Common(DSID_GETHWND, (LPARAM)phwnd, NULL, FALSE);
}

HRESULT CDDEAuto_Exit()
{
    return CDDEAuto_Common(DSID_EXIT, (LPARAM)NULL, NULL, FALSE);
}

#define DXTRACK 1
void FrameTrack(HDC hdc, LPRECT prc, UINT uFlags)
{
    COLORREF clrSave, clr;
    RECT    rc;

    // upperleft
    switch (uFlags)
    {
    case TRACKHOT:
        clr = GetSysColor(COLOR_BTNHILIGHT);
        break;
    case TRACKNOCHILD:
    case TRACKEXPAND:
        clr = GetSysColor(COLOR_BTNSHADOW);
        break;
    default:
        ASSERT(FALSE);
        clr = GetSysColor(COLOR_BTNSHADOW);
        break;
    }
    clrSave = SetBkColor(hdc, clr);
    rc = *prc;
    rc.bottom = rc.top + DXTRACK;
    ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
    rc.bottom = prc->bottom;
    rc.right = rc.left + DXTRACK;
    rc.top = prc->top + DXTRACK;
    ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
    // lowerright
    switch (uFlags)
    {
    case TRACKHOT:
        clr = GetSysColor(COLOR_BTNSHADOW);
        break;
    case TRACKNOCHILD:
    case TRACKEXPAND:
        clr = GetSysColor(COLOR_BTNHILIGHT);
        break;
    default:
        ASSERT(FALSE);
        break;
    }
    SetBkColor(hdc, clr);
    if (uFlags & (TRACKHOT | TRACKNOCHILD))
    {
        rc.right = prc->right;
        rc.top = rc.bottom - DXTRACK;
        rc.left = prc->left;
        ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
    }
    rc.right = prc->right;
    rc.left = prc->right - DXTRACK;
    rc.top = prc->top;
    rc.bottom = prc->bottom;
    ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
    SetBkColor(hdc, clrSave);
    return;
}


#undef new // Hack!! Need to remove this (edwardp)

class CDelagateMalloc : public IMalloc
{
public:
    // IUnknown
    virtual STDMETHODIMP QueryInterface(REFIID,void **);
    virtual STDMETHODIMP_(ULONG) AddRef(void);
    virtual STDMETHODIMP_(ULONG) Release(void);

    // IMalloc
    virtual STDMETHODIMP_(void *)   Alloc(SIZE_T cb);
    virtual STDMETHODIMP_(void *)   Realloc(void *pv, SIZE_T cb);
    virtual STDMETHODIMP_(void)     Free(void *pv);
    virtual STDMETHODIMP_(SIZE_T)    GetSize(void *pv);
    virtual STDMETHODIMP_(int)      DidAlloc(void *pv);
    virtual STDMETHODIMP_(void)     HeapMinimize();

private:
    CDelagateMalloc(void *pv, SIZE_T cbSize, WORD wOuter);
    ~CDelagateMalloc() {}
    void* operator new(size_t cbClass, SIZE_T cbSize)
    {
        return ::operator new(cbClass + cbSize);
    }


    friend HRESULT CDelegateMalloc_Create(void *pv, SIZE_T cbSize, WORD wOuter, IMalloc **ppmalloc);

protected:
    LONG _cRef;
    WORD _wOuter;           // delegate item outer signature
    WORD _wUnused;          // to allign
#ifdef DEBUG
    UINT _cAllocs;
#endif
    SIZE_T _cb;
    BYTE _data[EMPTY_SIZE];
};

CDelagateMalloc::CDelagateMalloc(void *pv, SIZE_T cbSize, WORD wOuter)
{
    _cRef = 1;
    _wOuter = wOuter;
    _cb = cbSize;

    memcpy(_data, pv, _cb);
}

HRESULT CDelagateMalloc::QueryInterface(REFIID riid, void **ppvObj)
{
    static const QITAB qit[] = {
        QITABENT(CDelagateMalloc, IMalloc),
        { 0 },
    };
    return QISearch(this, qit, riid, ppvObj);
}

ULONG CDelagateMalloc::AddRef()
{
    return InterlockedIncrement(&_cRef);
}

ULONG CDelagateMalloc::Release()
{
    if (InterlockedDecrement(&_cRef))
        return _cRef;

    delete this;
    return 0;
}

void *CDelagateMalloc::Alloc(SIZE_T cb)
{
    WORD cbActualSize = (WORD)(
                        SIZEOF(DELEGATEITEMID) - 1 +    // header (-1 sizeof(rgb[0])
                        cb +                            // inner
                        _cb);                           // outer data

    PDELEGATEITEMID pidl = (PDELEGATEITEMID)SHAlloc(cbActualSize + 2);  // +2 for pidl term
    if (pidl)
    {
        pidl->cbSize = cbActualSize;
        pidl->wOuter = _wOuter;
        pidl->cbInner = (WORD)cb;
        memcpy(&pidl->rgb[cb], _data, _cb);
        *(UNALIGNED WORD *)&(((BYTE *)pidl)[cbActualSize]) = 0;
#ifdef DEBUG
        _cAllocs++;
#endif
    }
    return pidl;
}

void *CDelagateMalloc::Realloc(void *pv, SIZE_T cb)
{
    return NULL;
}

void CDelagateMalloc::Free(void *pv)
{
    SHFree(pv);
}

SIZE_T CDelagateMalloc::GetSize(void *pv)
{
    return (SIZE_T)-1;
}

int CDelagateMalloc::DidAlloc(void *pv)
{
    return -1;
}

void CDelagateMalloc::HeapMinimize()
{
}

STDAPI CDelegateMalloc_Create(void *pv, SIZE_T cbSize, WORD wOuter, IMalloc **ppmalloc)
{
    CDelagateMalloc *pdm = new(cbSize) CDelagateMalloc(pv, cbSize, wOuter);
    if (pdm)
    {
        HRESULT hres = pdm->QueryInterface(IID_PPV_ARG(IMalloc, ppmalloc));
        pdm->Release();
        return hres;
    }
    return E_OUTOFMEMORY;
}


//+-------------------------------------------------------------------------
// This function scans the head of an html document for the desired element
// with a particular attribute.  If a match is found, the first occurance
// of that element is returned in punkDesired and S_OK is returned.
// Otherwise, E_FAIL is returned.
//
// Example:  Find the first meta element with name="ProgID":
//
//   SearchForElementInHead(pHTMLDoc, OLESTR("Name"), OLESTR("ProgId"),
//           IID_IHTMLMetaElement, (IUnknown**)&pMetaElement);
//
//--------------------------------------------------------------------------
HRESULT SearchForElementInHead
(
    IHTMLDocument2* pHTMLDocument,  // [in] document to search
    LPOLESTR        pszAttribName,  // [in] attribute to check for
    LPOLESTR        pszAttrib,      // [in] value the attribute must have
    REFIID          iidDesired,     // [in] element interface to return
    IUnknown**      ppunkDesired    // [out] returned interface
)
{
    ASSERT(NULL != pHTMLDocument);
    ASSERT(NULL != pszAttribName);
    ASSERT(NULL != pszAttrib);
    ASSERT(NULL != ppunkDesired);

    HRESULT hr = E_FAIL;
    *ppunkDesired = NULL;

    BSTR bstrAttribName = SysAllocString(pszAttribName);
    if (NULL == bstrAttribName)
    {
        return E_OUTOFMEMORY;
    }

    //
    // First get all document elements.  Note that this is very fast in
    // ie5 because the collection directly accesses the internal tree.
    //
    IHTMLElementCollection * pAllCollection;
    if (SUCCEEDED(pHTMLDocument->get_all(&pAllCollection)))
    {
        IUnknown* punk;
        IHTMLBodyElement* pBodyElement;
        IHTMLFrameSetElement* pFrameSetElement;
        IDispatch* pDispItem;

        //
        // Now we scan the document for the desired tags.  Since we're only
        // searching the head, and since Trident always creates a body tag
        // (unless there is a frameset), we can stop looking when we hit the
        // body or frameset.
        //
        // Note, the alternative of using pAllCollection->tags to return the
        // collection of desired tags is likely more expensive because it will
        // walk the whole tree (unless Trident optimizes this).
        //
        long lItemCnt;
        VARIANT vEmpty;
        V_VT(&vEmpty) = VT_EMPTY;

        VARIANT vIndex;
        V_VT(&vIndex) = VT_I4;

        EVAL(SUCCEEDED(pAllCollection->get_length(&lItemCnt)));

        for (long lItem = 0; lItem < lItemCnt; lItem++)
        {
            V_I4(&vIndex) = lItem;

            if (S_OK == pAllCollection->item(vIndex, vEmpty, &pDispItem))
            {
                //
                // First see if it's the desired element type
                //
                if (SUCCEEDED(pDispItem->QueryInterface(iidDesired, (void **)&punk)))
                {
                    //
                    // Next see if it has the desired attribute
                    //
                    IHTMLElement* pElement;

                    if (SUCCEEDED(pDispItem->QueryInterface(IID_PPV_ARG(IHTMLElement, &pElement))))
                    {
                        VARIANT varAttrib;
                        V_VT(&varAttrib) = VT_EMPTY;

                        if (SUCCEEDED(pElement->getAttribute(bstrAttribName, FALSE, &varAttrib)) &&
                            (V_VT(&varAttrib) == VT_BSTR) && varAttrib.bstrVal &&
                            (StrCmpIW(varAttrib.bstrVal, pszAttrib) == 0) )
                        {
                            // Found it!
                            *ppunkDesired = punk;
                            punk = NULL;
                            hr = S_OK;

                            // Terminate the search;
                            lItem = lItemCnt;
                        }
                        pElement->Release();

                        VariantClear(&varAttrib);
                    }

                    if (punk)
                        punk->Release();
                }
                //
                // Next check for the body tag
                //
                else if (SUCCEEDED(pDispItem->QueryInterface(IID_PPV_ARG(IHTMLBodyElement,&pBodyElement))))
                {
                    // Found a body tag, so terminate the search
                    lItem = lItemCnt;
                    pBodyElement->Release();
                }
                //
                // Finally, check for a frameset tag
                //
                else if (SUCCEEDED(pDispItem->QueryInterface(IID_PPV_ARG(IHTMLFrameSetElement, &pFrameSetElement))))
                {
                    // Found a frameset tag, so terminate the search
                    lItem = lItemCnt;
                    pFrameSetElement->Release();
                }

                pDispItem->Release();
            }
        }
        // Make sure that these don't have to be cleared (should not have been modified)
        ASSERT(vEmpty.vt == VT_EMPTY);
        ASSERT(vIndex.vt == VT_I4);

        pAllCollection->Release();
    }

    SysFreeString(bstrAttribName);

    return hr;
}


//+-------------------------------------------------------------------
//     JITCoCreateInstance
//
//  This function makes sure that the Option pack which
//  has this class id is installed.
//  It attempts to make sure that the Option pack corresponding
//  to the current IE Build.
//  If the feature does get installed correctly, it will
//  attempt to CoCreate the specified CLSID
//
//+------------------------------------------------------------------
HRESULT JITCoCreateInstance(REFCLSID rclsid, IUnknown *pUnkOuter, DWORD dwClsContext, REFIID riid, void ** ppv, HWND hwndParent, DWORD dwJitFlags)
{
    uCLSSPEC ucs;
    QUERYCONTEXT qc = { 0 };
    ucs.tyspec = TYSPEC_CLSID;
    ucs.tagged_union.clsid = rclsid;

    ASSERT((dwJitFlags & ~(FIEF_FLAG_FORCE_JITUI | FIEF_FLAG_PEEK)) == 0);

    HRESULT hr = FaultInIEFeature(hwndParent, &ucs, &qc, dwJitFlags);
    if (SUCCEEDED(hr))
    {
        hr = CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv);
    }

    return hr;
}

BOOL IsFeaturePotentiallyAvailable(REFCLSID rclsid)
{
    uCLSSPEC ucs;
    QUERYCONTEXT qc = { 0 };

    ucs.tyspec = TYSPEC_CLSID;
    ucs.tagged_union.clsid = rclsid;

    return (FaultInIEFeature(NULL, &ucs, &qc, FIEF_FLAG_FORCE_JITUI | FIEF_FLAG_PEEK) != E_ACCESSDENIED);
}

BOOL CreateFromDesktop(PNEWFOLDERINFO pfi)
{
    //
    //  APPCOMPAT:HACKHACK - we need to handle differences in the way we parse the command line
    //  on IE4 integrated.  we should not be called by anybody but IE4's Explorer.exe
    //
    ASSERT(GetUIVersion() == 4);
    if (!pfi->pidl) 
    {
        if ((pfi->uFlags & (COF_ROOTCLASS | COF_NEWROOT)) || pfi->pidlRoot)
        {
            pfi->pidl = ILRootedCreateIDList(pfi->uFlags & COF_ROOTCLASS ? &pfi->clsid : NULL, pfi->pidlRoot);
            pfi->uFlags &= ~(COF_ROOTCLASS | COF_NEWROOT);
            ILFree(pfi->pidlRoot);
            pfi->pidlRoot = NULL;
            pfi->clsid = CLSID_NULL;
        }
        else if (!PathIsURLA(pfi->pszPath))
        {
           CHAR szTemp[MAX_PATH];
           GetCurrentDirectoryA(ARRAYSIZE(szTemp), szTemp);
           PathCombineA(szTemp, szTemp, pfi->pszPath);
           Str_SetPtrA(&(pfi->pszPath), szTemp);
        } 
    }

    ASSERT(!(pfi->uFlags & (COF_ROOTCLASS | COF_NEWROOT)));
    
    return SHCreateFromDesktop(pfi);
}

//***   IsVK_TABCycler -- is key a TAB-equivalent
// ENTRY/EXIT
//  dir     0 if not a TAB, non-0 if a TAB
// NOTES
//  NYI: -1 for shift+tab, 1 for tab
//
int IsVK_TABCycler(MSG *pMsg)
{
    if (!pMsg)
        return 0;

    if (pMsg->message != WM_KEYDOWN)
        return 0;
    if (! (pMsg->wParam == VK_TAB || pMsg->wParam == VK_F6))
        return 0;

    return (GetKeyState(VK_SHIFT) < 0) ? -1 : 1;
}

#ifdef DEBUG
//
// This function fully fills the input buffer, trashing stuff so we fault
// when the size is wrong :-)
//
// We should hopefully catch people passing buffers that are too small here by
// having a reproable stack corruption.
//
#define CH_BADCHARA          ((CHAR)0xCC)
#define CH_BADCHARW          ((WCHAR)0xCCCC)

#ifdef UNICODE
#define CH_BADCHAR          CH_BADCHARW
#else // UNICODE
#define CH_BADCHAR          CH_BADCHARA
#endif // UNICODE

void DebugFillInputString(LPTSTR pszBuffer, DWORD cchSize)
{
    while (cchSize--)
    {
        pszBuffer[0] = CH_BADCHAR;
        pszBuffer++;
    }
}

void DebugFillInputStringA(LPSTR pszBuffer, DWORD cchSize)
{
    while (cchSize--)
    {
        pszBuffer[0] = CH_BADCHARA;
        pszBuffer++;
    }
}


#endif // DEBUG

// Review chrisny:  this can be moved into an object easily to handle generic droptarget, dropcursor
// , autoscrool, etc. . .
void _DragEnter(HWND hwndTarget, const POINTL ptStart, IDataObject *pdtObject)
{
    RECT    rc;
    POINT   pt;

    GetWindowRect(hwndTarget, &rc);
    if (IS_WINDOW_RTL_MIRRORED(hwndTarget))
        pt.x = rc.right - ptStart.x;
    else
        pt.x = ptStart.x - rc.left;
    pt.y = ptStart.y - rc.top;
    DAD_DragEnterEx2(hwndTarget, pt, pdtObject);
    return;
}

void _DragMove(HWND hwndTarget, const POINTL ptStart)
{
    RECT rc;
    POINT pt;

    GetWindowRect(hwndTarget, &rc);
    if (IS_WINDOW_RTL_MIRRORED(hwndTarget))
        pt.x = rc.right - ptStart.x;
    else
        pt.x = ptStart.x - rc.left;
    pt.y = ptStart.y - rc.top;
    DAD_DragMove(pt);
    return;
}

STDAPI_(IBindCtx *) CreateBindCtxForUI(IUnknown * punkSite)
{
    IBindCtx * pbc = NULL;

    if (EVAL(punkSite && SUCCEEDED(CreateBindCtx(0, &pbc))))
    {
        if (FAILED(pbc->RegisterObjectParam(STR_DISPLAY_UI_DURING_BINDING, punkSite)))
        {
            // It failed
            ATOMICRELEASE(pbc);
        }
    }

    return pbc;
}

//
// Return the location of the internet cache
// HRESULT GetCacheLocation(
// dwSize          no. of chars in pszCacheLocation

STDAPI GetCacheLocation(LPTSTR pszCacheLocation, DWORD dwSize)
{
    HRESULT hr = S_OK;
    DWORD dwLastErr;
    LPINTERNET_CACHE_CONFIG_INFO lpCCI = NULL;  // init to suppress bogus C4701 warning
    DWORD dwCCISize = sizeof(INTERNET_CACHE_CONFIG_INFO);
    BOOL fOnceErrored = FALSE;

    while (TRUE)
    {
        if ((lpCCI = (LPINTERNET_CACHE_CONFIG_INFO) LocalAlloc(LPTR,
                                                        dwCCISize)) == NULL)
        {
            hr = E_OUTOFMEMORY;
            goto cleanup;
        }

        if (!GetUrlCacheConfigInfo(lpCCI, &dwCCISize,
                                            CACHE_CONFIG_CONTENT_PATHS_FC))
        {
            if ((dwLastErr = GetLastError()) != ERROR_INSUFFICIENT_BUFFER  ||
                fOnceErrored)
            {
                hr = HRESULT_FROM_WIN32(dwLastErr);
                goto cleanup;
            }

            //
            // We have insufficient buffer size; reallocate a buffer with the
            //      new dwCCISize set by GetUrlCacheConfigInfo
            // Set fOnceErrored to TRUE so that we don't loop indefinitely
            //
            fOnceErrored = TRUE;
        }
        else
        {
            LPTSTR pszPath = lpCCI->CachePaths[0].CachePath;
            INT iLen;

            PathRemoveBackslash(pszPath);
            iLen = lstrlen(pszPath) + 1;        // + 1 is for the null char

            if ((DWORD) iLen < dwSize)
            {
                StrCpyN(pszCacheLocation, pszPath, iLen);
            }
            else
            {
                hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
            }

            break;
        }

        LocalFree(lpCCI);
        lpCCI = NULL;
    }

cleanup:
    if (lpCCI != NULL)
    {
        LocalFree(lpCCI);
        lpCCI = NULL;
    }

    return hr;
}

STDAPI_(UINT) GetWheelMsg()
{
    static UINT s_msgMSWheel = 0;
    if (s_msgMSWheel == 0)
        s_msgMSWheel = RegisterWindowMessage(TEXT("MSWHEEL_ROLLMSG"));
    return s_msgMSWheel;
}

STDAPI StringToStrRet(LPCTSTR pString, STRRET *pstrret)
{
    HRESULT hr = SHStrDup(pString, &pstrret->pOleStr);
    if (SUCCEEDED(hr))
    {
        pstrret->uType = STRRET_WSTR;
    }
    return hr;
}

// these two functions are duplicated from browseui
HINSTANCE GetComctl32Hinst()
{
    static HINSTANCE s_hinst = NULL;
    if (!s_hinst)
        s_hinst = GetModuleHandle(TEXT("comctl32.dll"));
    return s_hinst;
}

// since we don't define the proper WINVER we do this ourselves
#ifndef IDC_HAND
#define IDC_HAND            MAKEINTRESOURCE(32649)
#endif

STDAPI_(HCURSOR) LoadHandCursor(DWORD dwRes)
{
    if (g_bRunOnNT5 || g_bRunOnMemphis)
    {
        HCURSOR hcur = LoadCursor(NULL, IDC_HAND);  // from USER, system supplied
        if (hcur)
            return hcur;
    }
    return LoadCursor(GetComctl32Hinst(), IDC_HAND_INTERNAL);
}



//+-------------------------------------------------------------------------
// Returns true if this type of url may not be available when offline unless
// it is cached by wininet
//--------------------------------------------------------------------------
BOOL MayBeUnavailableOffline(LPTSTR pszUrl)
{
    BOOL fRet = FALSE;
    URL_COMPONENTS uc = {0};
    uc.dwStructSize = sizeof(uc);

    if (SUCCEEDED(InternetCrackUrl(pszUrl, 0, 0, &uc)))
    {
        fRet = uc.nScheme == INTERNET_SCHEME_HTTP ||
            uc.nScheme == INTERNET_SCHEME_HTTPS ||
            uc.nScheme == INTERNET_SCHEME_FTP ||
            uc.nScheme == INTERNET_SCHEME_GOPHER;
    }
    return fRet;
}

//+-------------------------------------------------------------------------
// If the folder is a link, the associated URL is returned.
//--------------------------------------------------------------------------
HRESULT GetNavTargetName(IShellFolder* psf, LPCITEMIDLIST pidl, LPTSTR pszUrl, UINT cMaxChars)
{
    LPITEMIDLIST pidlTarget;
    HRESULT hr = SHGetNavigateTarget(psf, pidl, &pidlTarget, NULL);
    if (SUCCEEDED(hr))
    {
        // Get the URL
        // NOTE (andrewgu): ie5.5 b#109391 - if SHGDN_FORPARSING is used the result will be
        // consitently the fully qualified path regardless of the protocol
        hr = IEGetNameAndFlags(pidlTarget, SHGDN_FORPARSING, pszUrl, cMaxChars, NULL);
        ILFree(pidlTarget);
    }
    else
        *pszUrl = 0;
    return hr;
}

//+-------------------------------------------------------------------------
// Returns info about whether this item is available offline. Returns E_FAIL
// if the item is not a link.
// if we navigate to this item
//  (true if we're online, items in the cache or otherwise available)
// if item is a sticky cache entry
//--------------------------------------------------------------------------
// FEATURE: this should use an interface to bind to this information abstractly
// psf->GetUIObjectOf(IID_IAvailablility, ...);
HRESULT GetLinkInfo(IShellFolder* psf, LPCITEMIDLIST pidlItem, BOOL* pfAvailable, BOOL* pfSticky)
{
    if (pfAvailable)
        *pfAvailable = TRUE;

    if (pfSticky)
        *pfSticky = FALSE;
    //
    // See if it is a link. If it is not, then it can't be in the wininet cache and can't
    // be pinned (sticky cache entry) or greyed (unavailable when offline)
    //
    WCHAR szUrl[MAX_URL_STRING];
    DWORD dwFlags = 0;

    HRESULT hr = GetNavTargetName(psf, pidlItem, szUrl, ARRAYSIZE(szUrl));

    if (SUCCEEDED(hr))
    {
        CHAR szUrlAnsi[MAX_URL_STRING];

        //
        // Get the cache info for this item.  Note that we use GetUrlCacheEntryInfoEx instead
        // of GetUrlCacheEntryInfo because it follows any redirects that occured.  This wacky
        // api uses a variable length buffer, so we have to guess the size and retry if the
        // call fails.
        //
        BOOL fInCache = FALSE;
        WCHAR szBuf[512];
        LPINTERNET_CACHE_ENTRY_INFOA pCE = (LPINTERNET_CACHE_ENTRY_INFOA)szBuf;
        DWORD dwEntrySize = ARRAYSIZE(szBuf);

        SHTCharToAnsi(szUrl, szUrlAnsi, ARRAYSIZE(szUrlAnsi));
        if (!(fInCache = GetUrlCacheEntryInfoExA(szUrlAnsi, pCE, &dwEntrySize, NULL, NULL, NULL, 0)))
        {
            if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
            {
                // We guessed too small for the buffer so allocate the correct size & retry
                pCE = (LPINTERNET_CACHE_ENTRY_INFOA)LocalAlloc(LPTR, dwEntrySize);
                if (pCE)
                {
                    fInCache = GetUrlCacheEntryInfoExA(szUrlAnsi, pCE, &dwEntrySize, NULL, NULL, NULL, 0);
                }
            }
        }

        //
        // If we are offline, see if the item is in the cache.
        //
        if (pfAvailable && SHIsGlobalOffline() && MayBeUnavailableOffline(szUrl) && !fInCache)
        {
            // Not available
            *pfAvailable = FALSE;
        }

        //
        // See if it's a sticky cache entry
        //
        if (pCE)
        {
            if (pfSticky && fInCache && (pCE->CacheEntryType & STICKY_CACHE_ENTRY))
            {
                *pfSticky = TRUE;
            }

            if ((TCHAR*)pCE != szBuf)
            {
                LocalFree(pCE);
                pCE = NULL;
            }
        }
    }

    // 
    // Somebody is obviously interested in in the offline availability so listen
    // to wininet for changes to the cache and rebroadcast as a SHChangeNotify
    //
    CWinInetNotify::GlobalEnable();

    return hr;
}

//
// Get the avg char width given an hwnd
//

int GetAvgCharWidth(HWND hwnd)
{
    ASSERT(hwnd);

    int nWidth = 0;

    HFONT hfont = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0);

    if (hfont)
    {
        HDC hdc = GetDC(NULL);

        if (hdc)
        {
            HFONT hfontOld = (HFONT)SelectObject(hdc, hfont);

            TEXTMETRIC tm;

            if (GetTextMetrics(hdc, &tm))
                nWidth = tm.tmAveCharWidth;

            SelectObject(hdc, hfontOld);

            ReleaseDC(NULL, hdc);
        }
    }

    return nWidth;
}

//
// Converts all "&" into "&&" so that they show up
// in menus
//
void FixAmpersands(LPWSTR pszToFix, UINT cchMax)
{
    ASSERT(pszToFix && cchMax > 0);

    WCHAR szBuf[MAX_URL_STRING];
    LPWSTR pszBuf = szBuf;
    LPWSTR pszSrc = pszToFix;
    UINT cch = 0;

    while (*pszSrc && cch < ARRAYSIZE(szBuf)-2)
    {
        if (*pszSrc == '&')
        {
            *pszBuf++ = '&';
            ++cch;
        }
        *pszBuf++ = *pszSrc++;
        ++cch;
    }
    *pszBuf = 0;

    StrCpyN(pszToFix, szBuf, cchMax);
}

BOOL IsInetcplRestricted(LPCWSTR pszCommand)
{
    BOOL fDisabled = FALSE;
    DWORD dwData, dwType;
    DWORD dwSize = sizeof(dwData);

    if (ERROR_SUCCESS == SHRegGetUSValue(TEXT("Software\\Policies\\Microsoft\\Internet Explorer\\Control Panel"),
        pszCommand, &dwType, (void *)&dwData, &dwSize, FALSE, NULL, 0))
    {
        fDisabled = dwData;
    }
    return fDisabled;
}

BOOL HasExtendedChar(LPCWSTR pszQuery)
{
    BOOL fNonAscii = FALSE;
    for (LPCWSTR psz = pszQuery; *psz; psz++)
    {
        if (*psz > 0x7f)
        {
            fNonAscii = TRUE;
            break;
        }
    }
    return fNonAscii;
}

void ConvertToUtf8Escaped(LPWSTR pszUrl, int cch)
{
    // Convert to utf8
    char szBuf[MAX_URL_STRING];
    SHUnicodeToAnsiCP(CP_UTF8, pszUrl, szBuf, ARRAYSIZE(szBuf));

    // Escape the string into the original buffer
    LPSTR pchIn; 
    LPWSTR pchOut = pszUrl;
    WCHAR ch;
    static const WCHAR hex[] = L"0123456789ABCDEF";

    for (pchIn = szBuf; *pchIn && cch > 3; pchIn++)
    {
        ch = *pchIn;

        if (ch > 0x7f)
        {
            cch -= 3;
            *pchOut++ = L'%';
            *pchOut++ = hex[(ch >> 4) & 15];
            *pchOut++ = hex[ch & 15];
        }
        else
        {
            --cch;
            *pchOut++ = *pchIn;
        }
    }

    *pchOut = L'\0';
}




HRESULT IExtractIcon_GetIconLocation(
    IUnknown *punk,
    IN  UINT   uInFlags,
    OUT LPTSTR pszIconFile,
    IN  UINT   cchIconFile,
    OUT PINT   pniIcon,
    OUT PUINT  puOutFlags)
{
    ASSERT(punk);
    HRESULT hr;
    
    if (g_fRunningOnNT)
    {
        IExtractIcon *pxi;
        hr = punk->QueryInterface(IID_PPV_ARG(IExtractIcon, &pxi));

        if (SUCCEEDED(hr))
        {
            hr = pxi->GetIconLocation(uInFlags, pszIconFile, cchIconFile, pniIcon, puOutFlags);

            pxi->Release();
        }
    }
    else
    {
        IExtractIconA *pxi;
        hr = punk->QueryInterface(IID_PPV_ARG(IExtractIconA, &pxi));

        if (SUCCEEDED(hr))
        {
            CHAR sz[MAX_PATH];
            hr = pxi->GetIconLocation(uInFlags, sz, SIZECHARS(sz), pniIcon, puOutFlags);

            if (SUCCEEDED(hr))
                SHAnsiToTChar(sz, pszIconFile, cchIconFile);

            pxi->Release();
        }
    }

    return hr;
}
        

HRESULT IExtractIcon_Extract(
    IUnknown *punk,
    IN  LPCTSTR pszIconFile,
    IN  UINT    iIcon,
    OUT HICON * phiconLarge,
    OUT HICON * phiconSmall,
    IN  UINT    ucIconSize)
{
    ASSERT(punk);
    HRESULT hr;
    
    if (g_fRunningOnNT)
    {
        IExtractIcon *pxi;
        hr = punk->QueryInterface(IID_PPV_ARG(IExtractIcon, &pxi));

        if (SUCCEEDED(hr))
        {
            hr = pxi->Extract(pszIconFile, iIcon, phiconLarge, phiconSmall, ucIconSize);

            pxi->Release();
        }
    }
    else
    {
        IExtractIconA *pxi;
        hr = punk->QueryInterface(IID_PPV_ARG(IExtractIconA, &pxi));

        if (SUCCEEDED(hr))
        {
            CHAR sz[MAX_PATH];
            SHTCharToAnsi(pszIconFile, sz, SIZECHARS(sz));
            hr = pxi->Extract(sz, iIcon, phiconLarge, phiconSmall, ucIconSize);

            pxi->Release();
        }
    }

    return hr;
}


typedef EXECUTION_STATE (__stdcall *PFNSTES) (EXECUTION_STATE);

EXECUTION_STATE _SetThreadExecutionState(EXECUTION_STATE esFlags)
{
    static PFNSTES _pfnSetThreadExecutionState = (PFNSTES)-1;
    
    if (_pfnSetThreadExecutionState == (PFNSTES)-1)
        _pfnSetThreadExecutionState = (PFNSTES)GetProcAddress(GetModuleHandleA("kernel32.dll"), "SetThreadExecutionState");

    if (_pfnSetThreadExecutionState != (PFNSTES)NULL)
        return(_pfnSetThreadExecutionState(esFlags));
    else
        return((EXECUTION_STATE)NULL);
}


HRESULT SHPathPrepareForWriteWrap(HWND hwnd, IUnknown *punkEnableModless, LPCTSTR pszPath, UINT wFunc, DWORD dwFlags)
{
    HRESULT hr;

    if (g_bRunOnNT5)
    {
        // NT5's version of the API is better.
        hr = SHPathPrepareForWriteW(hwnd, punkEnableModless, pszPath, dwFlags);
    }
    else
    {
        hr = SHCheckDiskForMedia(hwnd, punkEnableModless, pszPath, wFunc);
    }

    return hr;
}
void GetPathOtherFormA(LPSTR lpszPath, LPSTR lpszNewPath, DWORD dwSize)
{
    BOOL bQuotes = FALSE;
    LPSTR szStart = lpszPath;
    LPSTR szEnd = NULL;
    LPSTR szNewStart = lpszNewPath;

    ZeroMemory(lpszNewPath, dwSize);

    // Cull out the starting and ending " because GetShortPathName does not
    // like it.
    if (*lpszPath == '"')
    {
        bQuotes = TRUE;

        szStart = lpszPath + 1;
        szEnd   = lpszPath + lstrlenA(lpszPath) - 1; // Point to the last "
        *szEnd  = '\0';

        szNewStart = lpszNewPath + 1;  // So that we can insert the " in it.
        dwSize = dwSize - 2;  // for the two double quotes to be added.
    }

    if (GetShortPathNameA(szStart, szNewStart, dwSize) != 0)
    {
        if (StrCmpIA(szStart, szNewStart) == 0)
        {   // The original Path is a SFN. So NewPath needs to be LFN.
            GetLongPathNameA((LPCSTR)szStart, szNewStart, dwSize);
        }
    }
                                             
    // Now add the " to the NewPath so that it is in the expected form
    if (bQuotes)
    {
        int len = 0;

        // Fix the Original path.
        *szEnd = '"';

        // Fix the New path.
        *lpszNewPath = '"';        // Insert " in the beginning.
        len = lstrlenA(lpszNewPath);
        *(lpszNewPath + len) = '"'; // Add the " in the end.
        *(lpszNewPath + len + 1) = '\0'; // Terminate the string.
    }

    return;
}

int GetUrlSchemeFromPidl(LPCITEMIDLIST pidl)
{
    ASSERT(pidl);
    ASSERT(IsURLChild(pidl, FALSE));

    int nRet = URL_SCHEME_INVALID;

    WCHAR szUrl[MAX_URL_STRING];

    if (SUCCEEDED(IEGetNameAndFlags(pidl, SHGDN_FORPARSING, szUrl,
                                    ARRAYSIZE(szUrl), NULL)))
    {
        nRet = GetUrlScheme(szUrl);
    }

    return nRet;
}

//
// Check if it is safe to create a shortcut for the given url.  Used by add
// to favorites code.
//
BOOL IEIsLinkSafe(HWND hwnd, LPCITEMIDLIST pidl, ILS_ACTION ilsFlag)
{
    ASSERT(pidl);

    BOOL fRet = TRUE;

    if (IsURLChild(pidl, FALSE))
    {
        int nScheme = GetUrlSchemeFromPidl(pidl);

        if (URL_SCHEME_JAVASCRIPT == nScheme || URL_SCHEME_VBSCRIPT == nScheme)
        {
            WCHAR szTitle[MAX_PATH];
            WCHAR szText[MAX_PATH];

            MLLoadString(IDS_SECURITYALERT, szTitle, ARRAYSIZE(szTitle));
            MLLoadString(IDS_ADDTOFAV_WARNING + ilsFlag, szText,
                         ARRAYSIZE(szText));

            ULONG_PTR uCookie = 0;
            SHActivateContext(&uCookie);
            fRet = (IDYES == MLShellMessageBox(hwnd, szText, szTitle, MB_YESNO |
                                               MB_ICONWARNING | MB_APPLMODAL |
                                               MB_DEFBUTTON2));
            if (uCookie)
            {
                SHDeactivateContext(uCookie);
            }
        }
    }

    return fRet;
}

HRESULT GetSearchStyle(IServiceProvider * psp, LPDWORD pdwSearchStyle)
{
    RIP(pdwSearchStyle != NULL);

    HRESULT hr = E_FAIL;

    // first see if there is an ISearchContext to get this information from
    if (psp != NULL)
    {
        ISearchContext * pSC = NULL;
        hr = psp->QueryService(SID_STopWindow, IID_PPV_ARG(ISearchContext, &pSC));
        if (SUCCEEDED(hr))
        {
            RIP(pSC != NULL);

            hr = pSC->GetSearchStyle(pdwSearchStyle);
            pSC->Release();
        }
    }

    // there wasn't a viable search context, so try the reg key
    if (FAILED(hr))
    {
        DWORD dwType;
        DWORD dwAutoSearch;
        DWORD cb = sizeof(dwAutoSearch);

        if (ERROR_SUCCESS == SHRegGetUSValue(REGSTR_PATH_MAIN, L"AutoSearch", &dwType, &dwAutoSearch, &cb, FALSE, NULL, 0))
        {
            *pdwSearchStyle = dwAutoSearch;
            hr = S_OK;
        }
    }

    // return a default value
    if (FAILED(hr))
    {
        hr = S_FALSE;

        // Default to "display results in search pane and go to most likely site"
        *pdwSearchStyle = 3;
    }

    return hr;
}

BOOL AccessAllowed(LPCWSTR pwszURL1, LPCWSTR pwszURL2)
{
    BOOL fRet = FALSE;
    IInternetSecurityManager *pSecMgr = NULL;

    if (pwszURL1 && pwszURL2 && SUCCEEDED(CoCreateInstance(CLSID_InternetSecurityManager, 
                                   NULL, 
                                   CLSCTX_INPROC_SERVER,
                                   IID_PPV_ARG(IInternetSecurityManager, &pSecMgr))))
    {
        BYTE reqSid[MAX_SIZE_SECURITY_ID], docSid[MAX_SIZE_SECURITY_ID];
        DWORD cbReqSid = ARRAYSIZE(reqSid);
        DWORD cbDocSid = ARRAYSIZE(docSid);

        if (   SUCCEEDED(pSecMgr->GetSecurityId(pwszURL1, reqSid, &cbReqSid, 0))
            && SUCCEEDED(pSecMgr->GetSecurityId(pwszURL2, docSid, &cbDocSid, 0))
            && (cbReqSid == cbDocSid)
            && (memcmp(reqSid, docSid, cbReqSid) == 0))                    
        {
            fRet = TRUE;
        }
        pSecMgr->Release();
    }
    return fRet;
}

BOOL IsFrameWindow(IHTMLWindow2 * pHTMLWindow)
{
    BOOL fIsFrame = FALSE;

    HRESULT hr = E_FAIL;

    IHTMLWindow2 * pParentWindow = NULL;
    IHTMLWindow2 * pSelfWindow   = NULL;
    
    ASSERT(pHTMLWindow);

    hr = pHTMLWindow->get_self(&pSelfWindow);

    if (FAILED(hr) || (pSelfWindow == NULL))
    {
        goto cleanup;
    }

    hr = pHTMLWindow->get_parent(&pParentWindow);

    if (FAILED(hr) || (pParentWindow == NULL))
    {
        goto cleanup;
    }
    
    if (!IsSameObject(pSelfWindow, pParentWindow))
    {
        fIsFrame = TRUE;
    }

cleanup:

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

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

    return fIsFrame;
}

// For compatability with the Win2k debug SHELL32.DLL, this function needs to exist at ordinal 161.
// It's fine for it to return FALSE.
STDAPI_(BOOL) GetLeakDetectionFunctionTable(void *pTable)
{
    return FALSE;
}

STDAPI_(BOOL) IsCSIDLFolder(UINT csidl, LPCITEMIDLIST pidl)
{
    BOOL bRet = FALSE;
    TCHAR szPath[MAX_PATH];
    if (SUCCEEDED(SHGetFolderPathD(NULL, csidl, NULL, 0, szPath)))
    {
        PathRemoveBackslash(szPath);    // some platforms return version with slash on the end
        TCHAR szFolder[MAX_PATH];
        if (SUCCEEDED(SHGetNameAndFlags(pidl, SHGDN_FORPARSING, szFolder, ARRAYSIZE(szFolder), NULL)))
        {
            bRet = (StrCmpI(szFolder, szPath) == 0);
        }
    }
    return bRet;
}

// Determines if the classic toolbar strips should be used, or the new
// Whistler toolbar strips.
//
// To use the new Whistler strips, the user must be running Whistler or later
// and have a display with greater than 256 colors.

STDAPI_(BOOL) SHUseClassicToolbarGlyphs (void)
{
    BOOL bRet = TRUE;

    if (SHGetCurColorRes() > 8)
    {
        if (GetUIVersion() > 5)
        {
            bRet = FALSE;
        }
    }

    return bRet;
}
