//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
// exticon.cpp 
//
//   IExtractIcon com object.  Used by the shell to obtain icons.
//
//   History:
//
//       3/21/97  edwardp   Created.
//
////////////////////////////////////////////////////////////////////////////////

//
// Includes
//

#include "stdinc.h"
#include "resource.h"
#include "cdfidl.h"
#include "xmlutil.h"
#include "exticon.h"
#include "dll.h"
#include "persist.h"

//
// Constructor and destructor.
//

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
// *** CExtractIcon::CExtractIcon ***
//
//    Constructor.
//
////////////////////////////////////////////////////////////////////////////////
CExtractIcon::CExtractIcon (
    PCDFITEMIDLIST pcdfidl,
    IXMLElementCollection *pIXMLElementCollection
)
: m_cRef(1)
{
    ASSERT(CDFIDL_IsValid(pcdfidl));
    ASSERT(ILIsEmpty(_ILNext((LPITEMIDLIST)pcdfidl)));
    ASSERT(XML_IsCdfidlMemberOf(pIXMLElementCollection, pcdfidl));

    ASSERT(NULL == m_bstrIconURL);
    ASSERT(FALSE == m_fGleam);

    //
    // Set the default icon type.
    //

    if (CDFIDL_IsFolderId(&pcdfidl->mkid))
    {
        m_iconType = IDI_CLOSESUBCHANNEL;
    }
    else
    {
        m_iconType = IDI_STORY;
    }

    //
    // Get the URL for the custom icon.
    //

    if (pIXMLElementCollection)
    {
        IXMLElement* pIXMLElement;

        HRESULT hr;

        if (CDFIDL_GetIndex(pcdfidl) != -1)
        {
            hr = XML_GetElementByIndex(pIXMLElementCollection,
                                       CDFIDL_GetIndex(pcdfidl), &pIXMLElement);
        }
        else
        {
            IXMLElement *pIXMLElementChild;

            hr = XML_GetElementByIndex(pIXMLElementCollection, 0, &pIXMLElementChild);

            if (pIXMLElementChild)
            {
                hr = pIXMLElementChild->get_parent(&pIXMLElement);
                if (!pIXMLElement)
                {
                    ASSERT(FALSE);
                    hr = E_FAIL;
                }
                pIXMLElementChild->Release();
            }
        }

        if (SUCCEEDED(hr))
        {
            ASSERT(pIXMLElement);

            m_bstrIconURL = XML_GetAttribute(pIXMLElement, XML_ICON);

            pIXMLElement->Release();
        }
    }

    //
    // Don't allow the DLL to unload.
    //

    TraceMsg(TF_OBJECTS, "+ IExtractIcon");

    DllAddRef();

    return;
}

// Used for initializing the Root Element
CExtractIcon::CExtractIcon (
    PCDFITEMIDLIST pcdfidl,
    IXMLElement *pElem
)
: m_cRef(1)
{
    ASSERT(CDFIDL_IsValid(pcdfidl));
    ASSERT(ILIsEmpty(_ILNext((LPITEMIDLIST)pcdfidl)));
    ASSERT(NULL == m_bstrIconURL);
    ASSERT(FALSE == m_fGleam);

    //
    // Set the default icon type.
    //


    m_iconType = IDI_CHANNEL;
    

    //
    // Get the URL for the custom icon.
    //

    if (pElem)
    {
        HRESULT hr; 
        IXMLElement *pDeskElem;
        LONG nIndex;
        
        hr = XML_GetDesktopElementFromChannelElement(pElem, &pDeskElem, &nIndex);
        if (SUCCEEDED(hr))
        {
            m_iconType = IDI_DESKTOP;
            pDeskElem->Release();
        }
            
        m_bstrIconURL = XML_GetAttribute(pElem, XML_ICON);
    }

    //
    // Don't allow the DLL to unload.
    //

    TraceMsg(TF_OBJECTS, "+ IExtractIcon");

    DllAddRef();

    return;
}

// this constructor is used for the default channel case where
// we draw the icon information from the desktop.ini case 
// to avoid having to parse the XML stuff

CExtractIcon::CExtractIcon( BSTR pstrPath ) : m_cRef(1)
{
    ASSERT(NULL == m_bstrIconURL);
    ASSERT(FALSE == m_fGleam);
    
    m_iconType = IDI_CHANNEL;
    
    m_bstrIconURL = SysAllocString( pstrPath );
    
    DllAddRef();
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
// *** CExtractIcon::~CExtractIcon ***
//
//    Destructor.
//
////////////////////////////////////////////////////////////////////////////////
CExtractIcon::~CExtractIcon (
    void
)
{
    ASSERT(0 == m_cRef);

    if (m_bstrIconURL)
        SysFreeString(m_bstrIconURL);

    //
    // Matching Release for the constructor Addref.
    //

    TraceMsg(TF_OBJECTS, "- IExtractIcon");

    DllRelease();

    return;
}


//
// IUnknown methods.
//

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
// *** CExtractIcon::QueryInterface ***
//
//    CExtractIcon QI.
//
////////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
CExtractIcon::QueryInterface (
    REFIID riid,
    void **ppv
)
{
    ASSERT(ppv);

    HRESULT hr;

    if (IID_IUnknown == riid || IID_IExtractIcon == riid)
    {
        AddRef();
        *ppv = (IExtractIcon*)this;
        hr = S_OK;
    }
#ifdef UNICODE
    else if (IID_IExtractIconA == riid)
    {
        AddRef();
        *ppv = (IExtractIconA*)this;
        hr = S_OK;
	}
#endif
    else
    {
        *ppv = NULL;
        hr = E_NOINTERFACE;
    }

    ASSERT((SUCCEEDED(hr) && *ppv) || (FAILED(hr) && NULL == *ppv));

    return hr;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
// *** CExtractIcon::AddRef ***
//
//    CExtractIcon AddRef.
//
////////////////////////////////////////////////////////////////////////////////
STDMETHODIMP_(ULONG)
CExtractIcon::AddRef (
    void
)
{
    ASSERT(m_cRef != 0);
    ASSERT(m_cRef < (ULONG)-1);

    return ++m_cRef;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
// *** CExtractIcon::Release ***
//
//    CExtractIcon Release.
//
////////////////////////////////////////////////////////////////////////////////
STDMETHODIMP_(ULONG)
CExtractIcon::Release (
    void
)
{
    ASSERT (m_cRef != 0);

    ULONG cRef = --m_cRef;
    
    if (0 == cRef)
        delete this;

    return cRef;
}


//
// IExtractIcon methods.
//

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
// *** CExtractIcon::GetIconLocation ***
//
//
// Description:
//     Returns a name index pair for the icon associated with this cdf item.
//
// Parameters:
//     [In]  uFlags     - GIL_FORSHELL, GIL_OPENICON.
//     [Out] szIconFile - The address of the buffer that receives the associated
//                        icon name.  It can be a filename, but doesn't have to
//                        be.
//     [In]  cchMax     - Size of the buffer that receives the icon location.
//     [Out] piIndex    - A pointer that receives the icon's index.
//     [Out] pwFlags    - A pointer the receives flags about the icon.
//
// Return:
//     S_OK if an was found.
//     S_FALSE if the shell should supply a default icon.
//
// Comments:
//     The shell can cache an icon associated with a name index pair. This
//     improves performance on subsequent calls for an icon with the same name
//     index pair.
//
////////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
CExtractIcon::GetIconLocation(
    UINT uFlags,
    LPTSTR szIconFile,
    UINT cchMax,
    int *piIndex,
    UINT *pwFlags
)
{
    ASSERT(szIconFile);
    ASSERT(piIndex);
    ASSERT(pwFlags);

    HRESULT hr = E_FAIL;

    //TraceMsg(TF_CDFICON, "<IN>  CExtractIcon::GetLocation (icon) tid:0x%x",
    //         GetCurrentThreadId());

    if (m_bstrIconURL && (uFlags & GIL_ASYNC))
    {
        hr = E_PENDING;
    }
    else
    {
        if (m_bstrIconURL)
        {
            hr = GetCustomIconLocation(uFlags, szIconFile, cchMax, piIndex,
                                       pwFlags);

            if (FAILED(hr))
            {
                SysFreeString(m_bstrIconURL);
                m_bstrIconURL = NULL;
            }
        }

        if (FAILED(hr))
        {
            hr = GetDefaultIconLocation(uFlags, szIconFile, cchMax, piIndex,
                                        pwFlags);
        }

        //
        // If szIconFile is a path the shell will only use the filename part
        // of the path as the cache index.  To ensure a unique index the full
        // path must be used.  This is accomplished by modifying the path string
        // so it is no longer recognized as a path.
        //

        if (SUCCEEDED(hr) && INDEX_IMAGE == *piIndex)
            MungePath(szIconFile);

        if (FAILED(hr))
        {
            *szIconFile = TEXT('\0');
            *piIndex = 0;

            hr = S_FALSE;  // The shell will use a default icon.
        }


        ASSERT((S_OK == hr && *szIconFile) ||
               (S_FALSE == hr && 0 == *szIconFile));
    }

    //TraceMsg(TF_CDFICON, "<OUT> CExtractIcon::GetLocation (icon) tid:0x%x",
    //         GetCurrentThreadId());

    return hr;
}
#ifdef UNICODE
// IExtractIconA methods.
STDMETHODIMP
CExtractIcon::GetIconLocation(
    UINT uFlags,
    LPSTR szIconFile,
    UINT cchMax,
    int *piIndex,
    UINT *pwFlags
)
{
    HRESULT hr;
    WCHAR* pszIconFileW = new WCHAR[cchMax];
    if (pszIconFileW == NULL)
        return ERROR_OUTOFMEMORY;

    hr = GetIconLocation(uFlags, pszIconFileW, cchMax, piIndex, pwFlags);
    if (SUCCEEDED(hr))
        SHUnicodeToAnsi(pszIconFileW, szIconFile, cchMax);

    delete [] pszIconFileW;
    return hr;
}
#endif
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
// *** CExtractIcon::Extract ***
//
//
// Description:
//     Return an icon given the name index pair returned from GetIconLocation.
//
// Parameters:
//     [In]  pszFile     - A pointer to the name associated with the requested
//                         icon.
//     [In]  nIconIndex  - An index associated with the requested icon.
//     [Out] phiconLarge - Pointer to the variable that receives the handle of
//                         the large icon.
//     [Out] phiconSmall - Pointer to the variable that receives the handle of
//                         the small icon.
//     [Out] nIconSize   - Value specifying the size, in pixels, of the icon
//                         required. The LOWORD and HIWORD specify the size of
//                         the large and small icons, respectively.
//
// Return:
//     S_OK if the icon was extracted.
//     S_FALSE if the shell should extract the icon assuming the name is a
//     filename and the index is the icon index.
//
// Comments:
//     The shell may cache the icon returned from this function.
//
//     If the icon index indicates that the icon is specified by an internet
//     image then custom extraction is required.
//
////////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
CExtractIcon::Extract(
    LPCTSTR pszFile,
    UINT nIconIndex,
    HICON *phiconLarge,
    HICON *phiconSmall,
    UINT nIconSize
)
{
    HRESULT hr;

    TCHAR  szPath[MAX_PATH];
    TCHAR* pszPath = szPath;

    StrCpyN(szPath, pszFile, ARRAYSIZE(szPath) - 1);

    //TraceMsg(TF_CDFICON, "<IN>  CExtractIcon::Extract (icon) tid:0x%x",
    //         GetCurrentThreadId());

    if (INDEX_IMAGE == nIconIndex)
    {
        DemungePath(pszPath);

        if (m_fGleam && *pszPath == TEXT('G'))
        {
            pszPath++;
        }

        IImgCtx* pIImgCtx;

        HANDLE hExitThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

        if (hExitThreadEvent)
        {
#ifdef UNIX
            unixEnsureFileScheme(pszPath);
#endif /* UNIX */
            hr = SynchronousDownload(pszPath, &pIImgCtx, hExitThreadEvent);

            if (SUCCEEDED(hr))
            {
                ASSERT(pIImgCtx);

                *phiconLarge = ExtractImageIcon(LOWORD(nIconSize), pIImgCtx,
                                                m_fGleam);
                *phiconSmall = ExtractImageIcon(HIWORD(nIconSize), pIImgCtx,
                                                m_fGleam);
                pIImgCtx->Release();
            }

            SetEvent(hExitThreadEvent);
        }
        else
        {
            hr = E_OUTOFMEMORY;
        }
    }
    else if (m_fGleam)
    {
        // Add gleam to icon for the shell

        hr = ExtractGleamedIcon(pszPath + 1, nIconIndex, 0, 
                phiconLarge, phiconSmall, nIconSize);
    }
    else
    {
        hr = S_FALSE;  // Let shell extract it.
    }

    //TraceMsg(TF_CDFICON, "<OUT> CExtractIcon::Extract (icon) tid:0x%x",
    //         GetCurrentThreadId());

    return hr;
}
#ifdef UNICODE
STDMETHODIMP
CExtractIcon::Extract(
    LPCSTR pszFile,
    UINT nIconIndex,
    HICON *phiconLarge,
    HICON *phiconSmall,
    UINT nIconSize)
{
    HRESULT hr;
    int    cch = lstrlenA(pszFile) + 1; 
    WCHAR* pszFileW = new WCHAR[cch];
    if (pszFileW == NULL)
        return ERROR_OUTOFMEMORY;

    SHAnsiToUnicode(pszFile, pszFileW, cch);

    hr = Extract(pszFileW, nIconIndex, phiconLarge, phiconSmall, nIconSize);

    delete [] pszFileW;
    return hr;
}
#endif
//
// Helper functions.
//

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
// *** CExtractIcon::GetCustomIconLocation ***
//
//
// Description:
//     Gets the location string name for an icon specified via a LOGO element
//     in a cdf.
//
// Parameters:
//     [In]  uFlags     - GIL_FORSHELL, GIL_OPENICON.
//     [Out] szIconFile - The address of the buffer that receives the associated
//                        icon name.
//     [In]  cchMax     - Size of the buffer that receives the icon location.
//     [Out] piIndex    - A pointer that receives the icon's index.
//     [Out] pwFlags    - A pointer the receives flags about the icon.
//
// Return:
//     S_OK if the custom icon location was determined.
//     E_FAIL if the location couldn't be determined.
//
// Comments:
//     If the extension of the image url isn't .ico then it's treated as an
//     internet image file.  IImgCtx is used to convert these files into icons.
//
////////////////////////////////////////////////////////////////////////////////
HRESULT
CExtractIcon::GetCustomIconLocation(
    UINT uFlags,
    LPTSTR szIconFile,
    UINT cchMax,
    int *piIndex,
    UINT *pwFlags
)
{
    ASSERT(szIconFile);
    ASSERT(piIndex);
    ASSERT(pwFlags);

    HRESULT hr;

    ASSERT(m_bstrIconURL);

    *piIndex = 0;
    *pwFlags = 0;

    TCHAR szURL[INTERNET_MAX_URL_LENGTH];
  
    if (SHUnicodeToTChar(m_bstrIconURL, szURL, ARRAYSIZE(szURL)))
    {
        hr = URLGetLocalFileName(szURL, szIconFile, cchMax, NULL);

        #ifdef DEBUG
            if (SUCCEEDED(hr))
            {
                TraceMsg(TF_CDFICON, "[URLGetLocalFileName %s]", szIconFile);
            }
            else
            {
                TraceMsg(TF_CDFICON, "[URLGetLocalFileName %s FAILED]",
                         szURL);
            }
        #endif // DEBUG

        //hr = URLDownloadToCacheFile(NULL, szURL, szIconFile, cchMax, 0, NULL);

        if (SUCCEEDED(hr))
        {
            LPTSTR pszExt = PathFindExtension(szIconFile);

            if (*pszExt != TEXT('.') || 0 != StrCmpI(pszExt, TSTR_ICO_EXT))
                *piIndex = INDEX_IMAGE;
        }
    }
    else
    {
        *szIconFile = TEXT('\0');

        hr = E_FAIL;
    }

    return hr;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
// *** CExtractIcon::GetDefaultIconLocation ***
//
//
// Description:
//     Return the location of the defualt icon.
//
// Parameters:
//     [In]  uFlags     - GIL_FORSHELL, GIL_OPENICON.
//     [Out] szIconFile - The address of the buffer that receives the associated
//                        icon name.
//     [In]  cchMax     - Size of the buffer that receives the icon location.
//     [Out] piIndex    - A pointer that receives the icon's index.
//     [Out] pwFlags    - A pointer the receives flags about the icon.
//
// Return:
//     S_OK if the default location is returned.
//     E_FAIL otherwise.
//
// Comments:
//     The default icons are in the resource file.
//
////////////////////////////////////////////////////////////////////////////////
HRESULT
CExtractIcon::GetDefaultIconLocation(
    UINT uFlags,
    LPTSTR szIconFile,
    UINT cchMax,
    int *piIndex,
    UINT *pwFlags
)
{
    ASSERT(szIconFile);
    ASSERT(piIndex);
    ASSERT(pwFlags);

    HRESULT hr;

    *pwFlags = 0;

    ASSERT(g_szModuleName[0]);

    StrCpyN(szIconFile, g_szModuleName, cchMax);

    if (*szIconFile)
    {
        switch (m_iconType)
        {
            case IDI_STORY:
            case IDI_CHANNEL:
            case IDI_DESKTOP:
                *piIndex = - m_iconType;
                break;

            default:
                *piIndex = (uFlags & GIL_OPENICON) ? 
                                (-IDI_OPENSUBCHANNEL) : 
                                (-IDI_CLOSESUBCHANNEL);
                break;
        }
        hr = S_OK;
    }
    else
    {
        hr = E_FAIL;
    }

    ASSERT((SUCCEEDED(hr) && *szIconFile) || FAILED(hr));

    return hr;
}

struct ThreadData
{
    HANDLE hEvent;
    HANDLE hExitThreadEvent;
    IImgCtx * pImgCtx;
    LPCWSTR pszBuffer;
    HRESULT * pHr;
};

DWORD CALLBACK SyncDownloadThread( LPVOID pData )
{
    ThreadData * pTD = (ThreadData * ) pData;

    HANDLE hExitThreadEvent = pTD->hExitThreadEvent;

    CoInitialize(NULL);
    pTD->pImgCtx = NULL;
    
    HRESULT hr;
    hr = CoCreateInstance(CLSID_IImgCtx, NULL, CLSCTX_INPROC_SERVER,
                          IID_IImgCtx, (void**)&(pTD->pImgCtx));
    if (SUCCEEDED(hr))
    {
        hr = pTD->pImgCtx->Load(pTD->pszBuffer, 0);

        if (SUCCEEDED(hr))
        {
            ULONG fState;
            SIZE  sz;

            pTD->pImgCtx->GetStateInfo(&fState, &sz, TRUE);

            if (!(fState & (IMGLOAD_COMPLETE | IMGLOAD_ERROR)))
            {
                BOOL fDone = FALSE;

                hr = pTD->pImgCtx->SetCallback(ImgCtx_Callback, &fDone);

                if (SUCCEEDED(hr))
                {
                    hr = pTD->pImgCtx->SelectChanges(IMGCHG_COMPLETE, 0, TRUE);

                    if (SUCCEEDED(hr))
                    {
                        MSG msg;
                        BOOL fMsg;

                        // HACK: restrict the message pump to those messages we know that URLMON and
                        // HACK: the imageCtx stuff needs, otherwise we will be pumping messages for
                        // HACK: windows we shouldn't be pumping right now...
                        while(!fDone )
                        {
                            fMsg = PeekMessage(&msg, NULL, WM_USER + 1, WM_USER + 4, PM_REMOVE );

                            if (!fMsg)
                            {
                                fMsg = PeekMessage( &msg, NULL, WM_APP + 2, WM_APP + 2, PM_REMOVE );
                            }

                            if (!fMsg)
                            {
                                // go to sleep until we get a new message....
                                WaitMessage();
                                continue;
                            }

                            TranslateMessage(&msg);
                            DispatchMessage(&msg);
                        }

                    }
                }

            }

            hr = pTD->pImgCtx->GetStateInfo(&fState, &sz, TRUE);

            if (SUCCEEDED(hr))
                hr = (fState & IMGLOAD_ERROR) ? E_FAIL : S_OK;
        }

        // Must disconnect on the same thread that SetCallback is
        // done.  This object becomes a primary object on the thread
        // which connects the callback function.  The primary object
        // count is decremented when Disconnect is called, or when the
        // object is released.  In this case, the release is definitely
        // going to happen on a different thread than this one, so we
        // need to disconnect the callback function right now before
        // returning.  There is no further needs for callbacks at this
        // point.

        pTD->pImgCtx->Disconnect();
    }

    if ( FAILED( hr ) && pTD->pImgCtx )
    {
        pTD->pImgCtx->Release();
        pTD->pImgCtx = NULL;
    }
    
    *(pTD->pHr) = hr;
    
    SetEvent( pTD->hEvent );

    //
    // Wait for the calling thread to finish up with IImgCtx before 
    // CoUninitialize gets called.
    //

    WaitForSingleObject(hExitThreadEvent, INFINITE);
    CloseHandle(hExitThreadEvent);

    CoUninitialize();

    return 0;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
// *** CExtractIcon::SynchronousDownload ***
//
//
// Description:
//     Synchronously downloads the image associated with the image context.
//
// Parameters:
//     [In]  szFile           - The local (already in cache) file name of the
//                              image.
//     [In]  pIImgCtx         - A pointer to the image context.
//     [In]  hExitThreadEvent - An event that gets signaled when the IImgCtx
//                              object is no longer in use.
//
// Return:
//     S_OK if the image was successfully downloaded.
//     E_FAIL if the image wasn't downloaded.
//
// Comments:
//     The image context object doesn't directly support synchronous download.
//     Here a message loop is used to make sure ulrmon keeps geeting messages
//     and the download progresses.
//
////////////////////////////////////////////////////////////////////////////////
HRESULT
CExtractIcon::SynchronousDownload(
    LPCTSTR  pszFile,
    IImgCtx** ppIImgCtx,
    HANDLE hExitThreadEvent
)
{
    ASSERT(ppIImgCtx);

    HRESULT hr;

    TraceMsg(TF_CDFPARSE, "[*** IImgCtx downloading logo %s ***]",
             pszFile);
    TraceMsg(TF_CDFICON, "[*** IImgCtx downloading logo %s ***]",
             pszFile);

    WCHAR szFileW[MAX_PATH];

    SHTCharToUnicode(pszFile, szFileW, ARRAYSIZE(szFileW));

    ThreadData rgData;
    rgData.hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
    if ( rgData.hEvent == NULL )
    {
        CloseHandle(hExitThreadEvent);
        return E_OUTOFMEMORY;
    }

    rgData.hExitThreadEvent = hExitThreadEvent;
    rgData.pszBuffer = szFileW;
    rgData.pHr = &hr;

    *ppIImgCtx = NULL;
    
    if ( SHCreateThread( SyncDownloadThread, &rgData, 0, NULL ))
    {
        WaitForSingleObject( rgData.hEvent, INFINITE );
        *ppIImgCtx = rgData.pImgCtx;
    }
    else
    {
        CloseHandle(hExitThreadEvent);
        hr = E_OUTOFMEMORY;
    }

    CloseHandle( rgData.hEvent );

    return hr;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
// *** CExtractIcon::ExtractImageIcon ***
//
//
// Description:
//     Returns an HICON for the image in IImgCtx.
//
// Parameters:
//     [In]  wSize      - The height and width of the icon,
//     [In]  pIImgCtx   - The image to convert into an icon.
//     [In]  fDrawGleam - TRUE if a gleam should be added, FALSE otherwise.
//
// Return:
//     An hicon of size nSize for the given IImgCtx.
//     NULL on failure.
//
// Comments:
//     Uses the image in IImgCtx to create bitmaps to pass to
//     CreateIconIndirect.
//
////////////////////////////////////////////////////////////////////////////////
HICON
CExtractIcon::ExtractImageIcon(
    WORD wSize,
    IImgCtx* pIImgCtx,
    BOOL fDrawGleam
)
{
    ASSERT(pIImgCtx);

    HICON hiconRet = NULL;

    HDC hdcScreen = GetDC(NULL);

    if (hdcScreen)
    {
        HBITMAP hbmImage = CreateCompatibleBitmap(hdcScreen, wSize, wSize);

        if (hbmImage)
        {
            HBITMAP hbmMask = CreateBitmap(wSize, wSize, 1, 1, NULL);

            if (hbmMask)
            {
                SIZE sz;
                sz.cx = sz.cy = wSize;

                if (SUCCEEDED(CreateImageAndMask(pIImgCtx, hdcScreen, &sz,
                                                 &hbmImage, &hbmMask,
                                                 fDrawGleam)))
                {
                    ICONINFO ii;

                    ii.fIcon    = TRUE;
                    ii.hbmMask  = hbmMask;
                    ii.hbmColor = hbmImage;

                    hiconRet = CreateIconIndirect(&ii); 
                }

                DeleteObject(hbmMask);
            }

            DeleteObject(hbmImage);
        }

        ReleaseDC(NULL, hdcScreen);
    }

    return hiconRet;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
// *** CExtractIcon::CreateImageAndMask ***
//
//
// Description:
//     Create the image and mask bitmaps that get used by CreateIconIndirect,
//
// Parameters:
//    [In]  IImgCtx       - The internet image.
//    [In]  hdcScreen     - The screen hdc.
//    [In]  pSize         - The size of the image and mask bitmaps.
//    [In Out] phbmImage  - A pointer to the handle of the Image bitmap.
//    [In Out] phbmMask   - A pointer to the handle of the Mask bitmap.
//    [In]  fDrawGleam    - TRUE if a gleam should be added, FALSE otherwise.
//
// Return:
//    S_OK if the image and mask bitmaps where successfully created.
//    E_FAIL if the image or mask couldn't be created.
//
// Comments:
//    The image bitmap has the opaque section come through and the transparent
//    sections set to black.
//
//    The mask has the transparent sections set to 1 and the opaque sections to
//    0.
//
////////////////////////////////////////////////////////////////////////////////
HRESULT
CExtractIcon::CreateImageAndMask(
    IImgCtx* pIImgCtx,
    HDC hdcScreen,
    SIZE* pSize,
    HBITMAP* phbmImage,
    HBITMAP* phbmMask,
    BOOL fDrawGleam
)
{
    ASSERT(pIImgCtx);
    ASSERT(phbmImage);
    ASSERT(phbmMask);

    HRESULT hr = E_FAIL;

    HDC hdcImgDst = CreateCompatibleDC(NULL);
    if (hdcImgDst)
    {
        HGDIOBJ hbmOld = SelectObject(hdcImgDst, *phbmImage);
        if (hbmOld)
        {
            if (ColorFill(hdcImgDst, pSize, COLOR1))
            {
                hr = StretchBltImage(pIImgCtx, pSize, hdcImgDst, fDrawGleam);

                if (SUCCEEDED(hr))
                {
                    hr = CreateMask(pIImgCtx, hdcScreen, hdcImgDst, pSize,
                                    phbmMask, fDrawGleam); 
                }
            }
            SelectObject(hdcImgDst, hbmOld);
        }
        DeleteDC(hdcImgDst);
    }

    return hr;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
// *** CExtractIcon::StretchBltImage ***
//
//
// Description:
//     Stretches the image associated with IImgCtx to the given size and places
//     the result in the given DC.
//
// Parameters:
//     [In]  pIImgCtx  - The image context for the image.
//     [In]  pSize     - The size of the resultant image.
//     [In/Out] hdcDst - The destination DC of the stretch blt.
//
// Return:
//     S_OK if the image was successfully resized into the destination DC.
//     E_FAIL otherwise.
//
// Comments:
//     The destination DC already has a bitmap of pSize selected into it.
//
////////////////////////////////////////////////////////////////////////////////
HRESULT
CExtractIcon::StretchBltImage(
    IImgCtx* pIImgCtx,
    const SIZE* pSize,
    HDC hdcDst,
    BOOL fDrawGleam
)
{
    ASSERT(pIImgCtx);
    ASSERT(hdcDst);

    HRESULT hr;

    SIZE    sz;
    ULONG   fState;

    hr = pIImgCtx->GetStateInfo(&fState, &sz, FALSE);

    if (SUCCEEDED(hr))
    {
        hr = pIImgCtx->StretchBlt(hdcDst, 0, 0, pSize->cx, pSize->cy, 0, 0,
                                  sz.cx, sz.cy, SRCCOPY);

        ASSERT(SUCCEEDED(hr) && "Icon extraction pIImgCtx->StretchBlt failed!");

        if (fDrawGleam)
        {
            hr = E_FAIL;

            HANDLE hGleam = LoadImage(g_hinst, TEXT("ICONGLEAM"),
                                      IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);

            if (hGleam)
            {
                if (DrawIconEx(hdcDst, 0, 0, (HICON)hGleam, pSize->cx, pSize->cy, 0, NULL,DI_NORMAL))
                    hr = S_OK;

                DeleteObject(hGleam);
            }            
        }
    }

    return hr;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
// *** CExtractIcon::CreateMask ***
//
//
// Description:
//     Creates the mask for an icon and also adjusts the image bitmap for use
//     with the mask.
//
// Parameters:
//     [In]  pIImgCtx   - The original image.
//     [In]  hdcScreen  - A screen dc.
//     [In/Out] hdc1    - The DC containing the image bitmap.
//     [In]  pSize      - The size of the bitmaps.
//     [In/Out] phbMask - A pointer to the handle of the mask bitmap
//
// Return:
//     S_OK if the mask is properly constructed.
//     E_FAIL otherwise.
//
// Comments:
//     The mask is created by first drawing the original image into a bitmap
//     with background COLOR1.  Then the same image is drawn into another
//     bitmap but this bitmap has background of COLOR2.  These two bitmaps
//     are XOR'ed and the opaque sections come out 0 while the transparent
//     sections are COLOR1 XOR COLOR2.
//
////////////////////////////////////////////////////////////////////////////////
HRESULT
CExtractIcon::CreateMask(
    IImgCtx* pIImgCtx,
    HDC hdcScreen,
    HDC hdc1,
    const SIZE* pSize,
    HBITMAP* phbMask,
    BOOL fDrawGleam
)
{
    ASSERT(hdc1);
    ASSERT(pSize);
    ASSERT(phbMask);

    HRESULT hr = E_FAIL;

    HDC hdc2 = CreateCompatibleDC(NULL);
    if (hdc2)
    {
        HBITMAP hbm2 = CreateCompatibleBitmap(hdcScreen, pSize->cx, pSize->cy);
        if (hbm2)
        {
            HGDIOBJ hbmOld2 = SelectObject(hdc2, hbm2);
            if (hbmOld2)
            {
                ColorFill(hdc2, pSize, COLOR2);

                hr = StretchBltImage(pIImgCtx, pSize, hdc2, fDrawGleam);

#ifndef UNIX
                if (SUCCEEDED(hr) &&
                    BitBlt(hdc2, 0, 0, pSize->cx, pSize->cy, hdc1, 0, 0,
                           SRCINVERT))
                {
                    if (GetDeviceCaps(hdcScreen, BITSPIXEL) <= 8)
                    {
                        //
                        // 6 is the XOR of the index for COLOR1 and the index
                        // for COLOR2.
                        //

                        SetBkColor(hdc2, PALETTEINDEX(6));
                    }
                    else
                    {
                        SetBkColor(hdc2, (COLORREF)(COLOR1 ^ COLOR2));
                    }

                    HDC hdcMask = CreateCompatibleDC(NULL);
                    if (hdcMask)
                    {
                        HGDIOBJ hbmOld = SelectObject(hdcMask, *phbMask);
                        if (hbmOld)
                        {
                            if (BitBlt(hdcMask, 0, 0, pSize->cx, pSize->cy, hdc2, 0,
                                       0, SRCCOPY))
                            {
                                //
                                // RasterOP 0x00220326 does a copy of the ~mask bits
                                // of hdc1 and sets everything else to 0 (Black).
                                //

                                if (BitBlt(hdc1, 0, 0, pSize->cx, pSize->cy, hdcMask,
                                           0, 0, 0x00220326))
                                {
                                    hr = S_OK;
                                }
                            }
                            SelectObject(hdcMask, hbmOld);
                        }
                        DeleteDC(hdcMask);
                    }
                }
#else
        SetBkColor(hdc2, COLOR2);
        HDC hdcMask = CreateCompatibleDC(NULL);
        if (hdcMask)
        {
            HGDIOBJ hbmOld = SelectObject(hdcMask, *phbMask);
            if (hbmOld)
            {
            if (BitBlt(hdcMask, 0, 0, pSize->cx, pSize->cy, hdc2, 0,
                   0, SRCCOPY))
                {
                            //
                            // RasterOP 0x00220326 does a copy of the ~mask bits
                            // of hdc1 and sets everything else to 0 (Black).
                            //

                if (BitBlt(hdc1, 0, 0, pSize->cx, pSize->cy, hdcMask,
                       0, 0, 0x00220326))
                  {
                hr = S_OK;
                  }
              }
              SelectObject(hdcMask, hbmOld);
              }
              DeleteDC(hdcMask);
          }
#endif /* UNIX */
                SelectObject(hdc2, hbmOld2);
            }

            DeleteObject(hbm2);
        }

        DeleteDC(hdc2);
    }

    return hr;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
// *** ImgCtx_Callback ***
//
//
// Description:
//     Callback function for IImgCtx loads.
//
// Parameters:
//     [In]  pIImgCtx - Not Used.
//     [Out] pfDone   - Set to TRUE on this callback.  
//
// Return:
//     None.
//
// Comments:
//     This callback gets called if IImgCtx is finished downloading an image.
//     It is used in CExtractIcon and CIconHandler.
//
////////////////////////////////////////////////////////////////////////////////
void
CALLBACK
ImgCtx_Callback(
    void* pIImgCtx,
    void* pfDone
)
{
    ASSERT(pfDone);

    *(BOOL*)pfDone = TRUE;

    return;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
// *** CExtractIcon::ColorFill ***
//
//
// Description:
//     Fills the given bitmap with the given color.
//
// Parameters:
//     [In/Out] hdc - The hdc that contains the bitmap.
//     [In]  pSize  - the size of the bitmap.
//     [In]  clr    - The color used to fill in the bitmap.
//
// Return:
//     TRUE if the bitmap was filled with color clr.
//     FALSE if the itmap wasn't filled.
//
////////////////////////////////////////////////////////////////////////////////
BOOL
CExtractIcon::ColorFill(
    HDC hdc,
    const SIZE* pSize,
    COLORREF clr
)
{
    ASSERT(hdc);

    BOOL fRet = FALSE;

    HBRUSH hbSolid = CreateSolidBrush(clr);
    if (hbSolid)
    {
        HGDIOBJ hbOld = SelectObject(hdc, hbSolid);
        if (hbOld)
        {
            PatBlt(hdc, 0, 0, pSize->cx, pSize->cy, PATCOPY);
            fRet = TRUE;

            SelectObject(hdc, hbOld);
        }
        DeleteObject(hbSolid);
    }

    return fRet;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
// *** CExtractIcon::ExtractGleamedIcon ***
//
//
// Description:
//     Extracts icon resources and applies gleams to them.
//
// Parameters:
//      [In] pszIconFile - path to the icon
//      [In] iIndex - index of icon with the file
//      [In] uFlags - ignore, pass 0
//      [Out] phiconLarge - HICON in large format with gleam
//      [Out] phiconSmall - HICON in small format with gleam
//
// Return:
//      S_OK if success
//      S_FALSE if the file has no icons (or not the asked for icon)
//      E_FAIL for files on a slow link.
//      E_FAIL if cant access the file
//      E_FAIL if gleam icon construction failed
//
////////////////////////////////////////////////////////////////////////////////
HRESULT 
CExtractIcon::ExtractGleamedIcon(
    LPCTSTR pszIconFile, 
    int iIndex, 
    UINT uFlags,
    HICON *phiconLarge, 
    HICON *phiconSmall, 
    UINT nIconSize)
{
    HICON   hIconLargeShell, hIconSmallShell;
    HRESULT hr;

    hr = Priv_SHDefExtractIcon(pszIconFile, iIndex, uFlags, 
                &hIconLargeShell, &hIconSmallShell, nIconSize);

    if (FAILED(hr))
        goto cleanup1;

    if (hIconLargeShell)
    {
        hr = ApplyGleamToIcon(hIconLargeShell, LOWORD(nIconSize), phiconLarge);
        if (FAILED(hr))
            goto cleanup2;
    }

    if (hIconSmallShell)
    {
        hr = ApplyGleamToIcon(hIconSmallShell, HIWORD(nIconSize), phiconSmall);
        if (FAILED(hr))
            goto cleanup3;
    }
    
cleanup3:
    if (FAILED(hr) && *phiconLarge)
    {
        DestroyIcon(*phiconLarge);
        *phiconLarge = NULL;
    }
    
cleanup2:
    if (hIconLargeShell)
        DestroyIcon(hIconLargeShell);
        
    if (hIconSmallShell)        
        DestroyIcon(hIconSmallShell);
        
cleanup1:
    return hr;
}

//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
//
// *** CExtractIcon::ApplyGleamToIcon ***
//
//
// Description:
//     Makes a gleamed version of an icon.
//
// Parameters:
//      [In] hIcon - icon that needs to be gleamed
//      [In] nSize - size of icon in pixels
//      [Out] phGleamIcon - variable to contain the gleamed icon
//      
//
// Return:
//      S_OK if success
//      E_FAIL if unsuccessful
//
////////////////////////////////////////////////////////////////////////////////
HRESULT 
CExtractIcon::ApplyGleamToIcon(
    HICON hIcon1, 
    ULONG nSize,
    HICON *phGleamedIcon)
{
    HRESULT hr = E_FAIL;

    HICON hIcon2 = (HICON)LoadImage(g_hinst, TEXT("ICONGLEAM"), IMAGE_ICON,
                                    nSize, nSize, 0);

    if (hIcon2)
    {
        HDC dc = GetDC(NULL);

        if (dc)
        {
            ICONINFO ii1, ii2;

            if (GetIconInfo(hIcon1, &ii1) && GetIconInfo(hIcon2, &ii2))
            {
                HDC dcSrc = CreateCompatibleDC(dc);

                if (dcSrc)
                {

                    HDC dcDst = CreateCompatibleDC(dc);

                    if (dcDst)
                    {
                        HBITMAP bmMask = CreateBitmap(nSize, nSize, 1, 1, NULL);

                        if (bmMask)
                        {
                            HBITMAP bmImage = CreateCompatibleBitmap(dc, nSize,
                                                                     nSize);

                            if (bmImage)
                            {
                                int cx1, cy1, cx2, cy2;
                                GetBitmapSize(ii1.hbmMask, &cx1, &cy1);
                                GetBitmapSize(ii2.hbmMask, &cx2, &cy2);

                                //
                                // Mask
                                //

                                HBITMAP hbmpOldDst = (HBITMAP)SelectObject(
                                                                        dcDst,
                                                                        bmMask);

                                HBITMAP hbmpOldSrc = (HBITMAP)SelectObject(
                                                                   dcSrc,
                                                                   ii1.hbmMask);
                                StretchBlt(dcDst, 0, 0, nSize, nSize, dcSrc, 0,
                                           0, cx1, cy1, SRCCOPY);

                                SelectObject(dcSrc, ii2.hbmMask);

                                StretchBlt(dcDst, 0, 0, nSize, nSize, dcSrc, 0,
                                           0, cx2, cy2, SRCAND);

                                //
                                // Image.
                                //

                                SelectObject(dcDst, bmImage);

                                SelectObject(dcSrc, ii1.hbmColor);

                                StretchBlt(dcDst, 0, 0, nSize, nSize, dcSrc, 0,
                                           0, cx1, cy1, SRCCOPY);

                                SelectObject(dcSrc, ii2.hbmMask);

                                StretchBlt(dcDst, 0, 0, nSize, nSize, dcSrc, 0,
                                           0, cx2, cy2, SRCAND);

                                SelectObject(dcSrc, ii2.hbmColor);

                                StretchBlt(dcDst, 0, 0, nSize, nSize, dcSrc, 0,
                                           0, cx2, cy2, SRCINVERT);

                                ii1.hbmMask  = bmMask;
                                ii1.hbmColor = bmImage;

                                *phGleamedIcon = CreateIconIndirect(&ii1);

                                if (*phGleamedIcon)
                                    hr = S_OK;

                                SelectObject(dcSrc, hbmpOldSrc);
                                SelectObject(dcDst, hbmpOldDst);

                                DeleteObject(bmImage);
                            }

                            DeleteObject(bmMask);
                        }

                        DeleteDC(dcDst);
                    }

                    DeleteDC(dcSrc);
                }
            }

            ReleaseDC(NULL, dc);
        }

        DestroyIcon(hIcon2);
    }

    return hr;
}

//
// Get the size of the given bitmap.
//

BOOL
CExtractIcon::GetBitmapSize(HBITMAP hbmp, int* pcx, int* pcy)
{
    BOOL fRet;

    BITMAP bm;

    if (GetObject(hbmp, sizeof(bm), &bm))
    {
        *pcx = bm.bmWidth;
        *pcy = bm.bmHeight;
        fRet = TRUE;
    }
    else
    {
        fRet = FALSE;
    }

    return fRet;
}

//
//  Replace '\' with '*' so the path is nolonger a recognized path name.  This
//  is done in-place and can be called multiple times on the same string.
//

void
MungePath(LPTSTR pszPath)
{
    ASSERT(pszPath);

    while(*pszPath)
    {
        if (TEXT(FILENAME_SEPARATOR) == *pszPath)
            *pszPath = TEXT('*');

        pszPath++;
    }

    return;
}

//
//  Replace '*' with '\'.
//

void
DemungePath(LPTSTR pszPath)
{
    ASSERT(pszPath);

    while(*pszPath)
    {
        if (TEXT('*') == *pszPath)
            *pszPath = TEXT(FILENAME_SEPARATOR);

        pszPath++;
    }

    return;
}
