//---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation 
//
// File: overlayMN.cpp
//
// This file contains the implementation of CFSIconOverlayManager, a COM object
// that manages the IShellIconOverlayIdentifiers list.
// It aslo managess the Sytem Image List OverlayIndexes, since we have limited slots,
// exactly MAX_OVERLAY_IAMGES of them. 
// History:
//         5-2-97  by dli
//------------------------------------------------------------------------
#include "shellprv.h"
#include "ovrlaymn.h"
#include "fstreex.h"
#include "filetbl.h"
extern "C" {
#include "cstrings.h"
#include "ole2dup.h"
}


extern "C" UINT const c_SystemImageListIndexes[];
extern int g_lrFlags;

// NOTE: The value of OVERLAYINDEX_RESERVED is not the same as the overall
// size of the s_ReservedOverlays array, we need to reserved the overlay slot
// #3 for the non-existent Read-Only overaly.
// The Read Only overlay was once there in Win95, but got turned off on IE4
// however, because of the of the original overlay designs,( we used to
// assign overlay 1 to share and 2 to link and 3 to readonly, and the third parties
// just copied our scheme,) we have to keep overlay #3 as a ghost. 
#define OVERLAYINDEX_RESERVED 4

typedef struct _ReservedIconOverlay
{
    int iShellResvrdImageIndex;
    int iImageIndex;
    int iOverlayIndex;
    int iPriority;
} ReservedIconOverlay;

static ReservedIconOverlay s_ReservedOverlays[] = {
    {II_SHARE, II_SHARE, 1, 10}, 
    {II_LINK, II_LINK, 2, 10},
    // Slot 3 should be reserved as a ghost slot because of the read-only overlay
    {II_SLOWFILE, II_SLOWFILE, 4, 10},
};
    
// File system Icon overlay Identifiers
typedef struct _FSIconOverlay {
    IShellIconOverlayIdentifier * psioi;  
    CLSID clsid;
    int iIconIndex;                          // Index of the Overlay Icon in szIconFile
    int iImageIndex;                         // System Image List index of the icon overlay image
    int iOverlayIndex;
    int iPriority;
    TCHAR szIconFile[MAX_PATH];              // Path of the icon overlay
} FSIconOverlay;

#define FSIconOverlay_GROW 3
#define DSA_LAST    0x7fffffff
#define MAX_OVERLAY_PRIORITY  100
class CFSIconOverlayManager : public IShellIconOverlayManager
{
public:
    CFSIconOverlayManager();
    ~CFSIconOverlayManager();
    
    // *** IUnknown Methods
    virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
    virtual STDMETHODIMP_(ULONG) AddRef(void) ;
    virtual STDMETHODIMP_(ULONG) Release(void);

    // *** IShellIconOverlay Methods
    virtual STDMETHODIMP GetFileOverlayInfo(LPCWSTR pwszPath, DWORD dwAttrib, int * pIndex, DWORD dwFlags);
    virtual STDMETHODIMP GetReservedOverlayInfo(LPCWSTR pwszPath, DWORD dwAttrib, int * pIndex, DWORD dwFlags, int iReservedID);
    virtual STDMETHODIMP RefreshOverlayImages(DWORD dwFlags);
    virtual STDMETHODIMP LoadNonloadedOverlayIdentifiers(void);
    virtual STDMETHODIMP OverlayIndexFromImageIndex(int iImage, int * piIndex, BOOL fAdd);
                                             
    // *** Public Methods

    // *** Static Methods
    static HRESULT CreateInstance(IUnknown* pUnkOuter, REFIID riid, OUT LPVOID * ppvOut);

protected:
    
    // IUnknown 
    LONG _cRef;
    HDSA _hdsaIconOverlays;      // Icon Overlay Identifiers array, this list is ordered by the IOIs' priority
    HRESULT _InitializeHdsaIconOverlays(); // Initialize the Icon Overlay Identifiers array
    HRESULT _DestroyHdsaIconOverlays();
    int     _GetImageIndex(FSIconOverlay * pfsio);
    FSIconOverlay * _FindMatchingID(LPCWSTR pwszPath, DWORD dwAttrib, int iMinPriority, int * pIOverlayIndex);
    HRESULT _SetGetOverlayInfo(FSIconOverlay * pfsio, int iOverlayIndex, int * pIndex, DWORD dwFlags);
    HRESULT _InitializeReservedOverlays();
    HRESULT _LoadIconOverlayIdentifiers(HDSA hdsaOverlays, BOOL bSkipIfLoaded);

    BOOL _IsIdentifierLoaded(REFCLSID clsid);
//    int  _GetAvailableOverlayIndex(int imyhdsa);
//    HRESULT _SortIOIList();      // Sort the IOI's in the list according to their priority
}; 


HRESULT CFSIconOverlayManager::RefreshOverlayImages(DWORD dwFlags)
{
    ENTERCRITICAL;

    _InitializeReservedOverlays();

    if (dwFlags && _hdsaIconOverlays)
    {
        for (int ihdsa = 0; ihdsa < DSA_GetItemCount(_hdsaIconOverlays); ihdsa++)
        {
            FSIconOverlay * pfsio = (FSIconOverlay *)DSA_GetItemPtr(_hdsaIconOverlays, ihdsa);
            if (dwFlags & SIOM_ICONINDEX)
                pfsio->iImageIndex = -1;
            if (dwFlags & SIOM_OVERLAYINDEX)
                pfsio->iOverlayIndex = -1;
        }
    }

    LEAVECRITICAL;
    return S_OK;
}


HRESULT CFSIconOverlayManager::OverlayIndexFromImageIndex(int iImage, int * piIndex, BOOL fAdd)
{
    HRESULT hres = E_FAIL;
    *piIndex = -1;
    int i;
    for (i = 0; i < ARRAYSIZE(s_ReservedOverlays); i++)
    {
        if (s_ReservedOverlays[i].iImageIndex == iImage)
        {
            *piIndex = s_ReservedOverlays[i].iOverlayIndex;
            hres = S_OK;
            break;
        }
    }

    if (i == ARRAYSIZE(s_ReservedOverlays))
    {
        ENTERCRITICAL;

        if (_hdsaIconOverlays)
        {
            int nOverlays = DSA_GetItemCount(_hdsaIconOverlays);

            // 1. Try to find this overlay image in the list 
            int i;
            for (i = 0; i < nOverlays; i++)
            {
                FSIconOverlay * pfsio = (FSIconOverlay *)DSA_GetItemPtr(_hdsaIconOverlays, i);
                if (pfsio && pfsio->iImageIndex == iImage)
                {
                    *piIndex = pfsio->iOverlayIndex;
                    hres = S_OK;
                    break;
                }
            }

            // 2. Can't find it, let's add it (if requested)
            if (fAdd && (i == nOverlays) && (nOverlays < NUM_OVERLAY_IMAGES))
            {
                FSIconOverlay fsio = {0};
                fsio.iImageIndex = iImage;
                fsio.iOverlayIndex = nOverlays + OVERLAYINDEX_RESERVED + 1;
                if (DSA_InsertItem(_hdsaIconOverlays, DSA_LAST, &fsio) >= 0)
                {
                    hres = S_OK;
                    for (int j = 0; j < ARRAYSIZE(g_rgshil); j++)
                    {
                        if (!ImageList_SetOverlayImage(g_rgshil[j].himl, iImage, fsio.iOverlayIndex))
                        {
                            hres = E_FAIL;
                            break;
                        }
                    }

                    if (SUCCEEDED(hres))
                    {
                        *piIndex = fsio.iOverlayIndex;
                    }
                    else
                    {
                        DSA_DeleteItem(_hdsaIconOverlays, nOverlays);
                    }
                }
            }
        }
        LEAVECRITICAL;
    }
    return hres;
}


HRESULT CFSIconOverlayManager::_InitializeReservedOverlays()
{
    int i;
    TCHAR szModule[MAX_PATH];

    BOOL fInit = _IsSHILInited();
    if (!fInit)
        fInit = FileIconInit(FALSE);

    if (!fInit)
        return E_OUTOFMEMORY;

    HKEY hkeyIcons = SHGetShellKey(SHELLKEY_HKLM_EXPLORER, TEXT("Shell Icons"), FALSE);
        
    GetModuleFileName(HINST_THISDLL, szModule, ARRAYSIZE(szModule));

    for (i = 0; i < ARRAYSIZE(s_ReservedOverlays); i++)
    {
        ASSERT(s_ReservedOverlays[i].iShellResvrdImageIndex > 0);
        ASSERT(s_ReservedOverlays[i].iOverlayIndex > 0);
        ASSERT(s_ReservedOverlays[i].iOverlayIndex <= MAX_OVERLAY_IMAGES);
        
        //
        // Warning: This is used by non explorer processes on NT only
        // because their image list was initialized with only 4 icons
        //
        int iIndex = s_ReservedOverlays[i].iShellResvrdImageIndex;

        // re-acquire the image index
        s_ReservedOverlays[i].iImageIndex = LookupIconIndex(szModule, iIndex, 0);

        if (s_ReservedOverlays[i].iImageIndex == -1)
        {
            HICON rghicon[ARRAYSIZE(g_rgshil)] = {0};

            // check to see if icon is overridden in the registry
            if (hkeyIcons)
            {
                TCHAR val[10];
                TCHAR ach[MAX_PATH];
                DWORD cb = SIZEOF(ach);

                wsprintf(val, TEXT("%d"), iIndex);

                ach[0] = 0;
                SHQueryValueEx(hkeyIcons, val, NULL, NULL, (LPBYTE)ach, &cb);

                if (ach[0])
                {
                    int iIcon = PathParseIconLocation(ach);

                    for (int j = 0; j < ARRAYSIZE(rghicon); j++)
                    {
                        ExtractIcons(ach, iIcon, g_rgshil[j].size.cx, g_rgshil[j].size.cy,
                                        &rghicon[j], NULL, 1, g_lrFlags);
                    }
                }
            }

            // if we got a large icon, run with that for everyone.  otherwise fall back to loadimage.
            if (rghicon[SHIL_LARGE] == NULL)
            {
                for (int j = 0; j < ARRAYSIZE(g_rgshil); j++)
                {
                    if (rghicon[j] == NULL)
                    {
                        rghicon[j] = (HICON)LoadImage(HINST_THISDLL, MAKEINTRESOURCE(c_SystemImageListIndexes[iIndex]),
                                        IMAGE_ICON, g_rgshil[j].size.cx, g_rgshil[j].size.cy, g_lrFlags);
                    }
                }
            }

            s_ReservedOverlays[i].iImageIndex = SHAddIconsToCache(rghicon, szModule, iIndex, 0);

            _DestroyIcons(rghicon, ARRAYSIZE(rghicon));
        }
        for (int j = 0; j < ARRAYSIZE(g_rgshil); j++)
        {
            ImageList_SetOverlayImage(g_rgshil[j].himl, s_ReservedOverlays[i].iImageIndex, s_ReservedOverlays[i].iOverlayIndex);
        }
    }

    if (hkeyIcons)
        RegCloseKey(hkeyIcons);
        
    return S_OK;
}

//===========================================================================
// Initialize the IShellIconOverlayIdentifiers 
//===========================================================================
HRESULT CFSIconOverlayManager::_InitializeHdsaIconOverlays() 
{
    HRESULT hres = S_FALSE; // Already initialized.

    if (NULL == _hdsaIconOverlays)
    {
        hres = _InitializeReservedOverlays();
        if (SUCCEEDED(hres))
        {
            _hdsaIconOverlays = DSA_Create(SIZEOF(FSIconOverlay), FSIconOverlay_GROW);

            if(NULL != _hdsaIconOverlays)
            {
                hres = _LoadIconOverlayIdentifiers(_hdsaIconOverlays, FALSE);
            }
            else
            {
                hres = E_OUTOFMEMORY;
            }
        }
    }
    return hres;
}



HRESULT CFSIconOverlayManager::LoadNonloadedOverlayIdentifiers(void)
{
    HRESULT hres;

    ENTERCRITICAL;

    if (NULL == _hdsaIconOverlays)
    {
        //
        // No overlay HDSA yet.  We should never hit this but just in case,
        // this will be valid behavior.
        //
        hres = _InitializeHdsaIconOverlays();
    }
    else
    {
        //
        // Load unloaded identifiers into existing HDSA.
        //
        hres = _LoadIconOverlayIdentifiers(_hdsaIconOverlays, TRUE);
    }

    LEAVECRITICAL;
    return hres;
}


HRESULT CFSIconOverlayManager::_LoadIconOverlayIdentifiers(HDSA hdsaOverlays, BOOL bSkipIfLoaded)
{
    ASSERT(NULL != hdsaOverlays);

    HDCA hdca = DCA_Create();
    if (!hdca)
        return E_OUTOFMEMORY;

    HRESULT hrInit = SHCoInitialize();

    // Enumerate all of the Icon Identifiers in
    DCA_AddItemsFromKey(hdca, HKEY_LOCAL_MACHINE, REGSTR_ICONOVERLAYID);
    if (DCA_GetItemCount(hdca) <= 0)
        goto EXIT;

    int idca;
    for (idca = 0; idca < DCA_GetItemCount(hdca); idca++)
    {
        const CLSID * pclsid = DCA_GetItem(hdca, idca);

        if (bSkipIfLoaded && _IsIdentifierLoaded(*pclsid))
            continue;

        FSIconOverlay fsio;
        ZeroMemory(&fsio, sizeof(fsio));
        // These came from HKLM which only administrators can write to,
        // so don't need to go through administrator approval
        if (FAILED(DCA_CreateInstance(hdca, idca, IID_PPV_ARG(IShellIconOverlayIdentifier, &fsio.psioi))))
            continue;       

        SHPinDllOfCLSID(pclsid);
        
        DWORD dwFlags = 0;
        int iIndex;
        WCHAR wszIconFile[MAX_PATH];
        // Initialize the Overlay Index to -1
        fsio.iOverlayIndex = -1;

        // Try get the overlay icon information from the Overlay Identifiers 
        if (S_OK == fsio.psioi->GetOverlayInfo(wszIconFile, ARRAYSIZE(wszIconFile), &iIndex, &dwFlags))
        {
            if (dwFlags & ISIOI_ICONFILE)
            {
                SHUnicodeToTChar(wszIconFile, fsio.szIconFile, ARRAYSIZE(fsio.szIconFile));
                fsio.iImageIndex = -1;
                if (dwFlags & ISIOI_ICONINDEX)
                    fsio.iIconIndex = iIndex;
                else
                    fsio.iIconIndex = 0;
            }

            if (FAILED(fsio.psioi->GetPriority(&fsio.iPriority)))
                fsio.iPriority = MAX_OVERLAY_PRIORITY;

            CopyMemory(&fsio.clsid, pclsid, sizeof(fsio.clsid));
            DSA_InsertItem(hdsaOverlays, DSA_LAST, &fsio);
        }
        // Now try to look in the registry for the Overlay Icons 
        else
        {
            fsio.iImageIndex = -1;
            const CLSID * pclsid = DCA_GetItem(hdca, idca);
            if (pclsid)
            {
                TCHAR szCLSID[GUIDSTR_MAX];
                TCHAR szRegKey[GUIDSTR_MAX + 40];
                HKEY hkeyIcon;
                SHStringFromGUID(*pclsid, szCLSID, ARRAYSIZE(szCLSID));
                wsprintf(szRegKey, REGSTR_ICONOVERLAYCLSID, szCLSID);
                if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, szRegKey, &hkeyIcon))
                {
                    LONG cb = SIZEOF(fsio.szIconFile);
                    if (SHRegQueryValue(hkeyIcon, c_szDefaultIcon, fsio.szIconFile, &cb) == ERROR_SUCCESS && fsio.szIconFile[0])
                    {
                        fsio.iIconIndex = PathParseIconLocation(fsio.szIconFile);
                        CopyMemory(&fsio.clsid, pclsid, sizeof(fsio.clsid));
                        DSA_InsertItem(hdsaOverlays, DSA_LAST, &fsio);
                    }

                    // Unfinished !!! Code to retrieve the priority here
                    fsio.iPriority = MAX_OVERLAY_PRIORITY;
                    RegCloseKey(hkeyIcon);
                }
            }
        }

        // Stop when we have more than we can handle
        if (DSA_GetItemCount(hdsaOverlays) >= (MAX_OVERLAY_IMAGES - OVERLAYINDEX_RESERVED))
            break;
    }
    
EXIT:
    DCA_Destroy(hdca);
    SHCoUninitialize(hrInit);
    return S_OK;
}
     

BOOL CFSIconOverlayManager::_IsIdentifierLoaded(REFCLSID clsid)
{
    if (NULL != _hdsaIconOverlays)
    {
        int cEntries = DSA_GetItemCount(_hdsaIconOverlays);
        for (int i = 0; i < cEntries; i++)
        {
            FSIconOverlay *pfsio = (FSIconOverlay *)DSA_GetItemPtr(_hdsaIconOverlays, i);            
            if (pfsio->clsid == clsid)
                return TRUE;
        }
    }
    return FALSE;
}


CFSIconOverlayManager::CFSIconOverlayManager() : _cRef(1) // _hdsaIconOverlays(NULL)
{
}

HRESULT CFSIconOverlayManager::_DestroyHdsaIconOverlays()
{
    if (_hdsaIconOverlays)
    {
        DSA_Destroy(_hdsaIconOverlays);
    }
    
    return S_OK;
}

CFSIconOverlayManager::~CFSIconOverlayManager()
{
    if (_hdsaIconOverlays)
        _DestroyHdsaIconOverlays();

}

//
// CFSFolder_GetAvailableOverlayIndex:
// This function first tries to find an empty slot in all the available overlay indexes
// If none found, it goes through the _hdsaIconOverlays array elements who have lower
// priorities and grab their overlay indexes if they are using one
//
/*int CFSIconOverlayManager::_GetAvailableOverlayIndex(int imyhdsa)
{
    int ib;
    for (ib = 0; ib < MAX_OVERLAY_IMAGES; ib++)
        if (_bOverlayIndexOccupied[ib] == FALSE)
            break;

    // Add code to grab indexes here.
    return ++ib;
}*/

HRESULT CFSIconOverlayManager::QueryInterface(REFIID riid, LPVOID * ppvObj)
{ 
    // ppvObj must not be NULL
    ASSERT(ppvObj != NULL);
    
    if (IsEqualIID(riid, IID_IUnknown))
    {    
        *ppvObj = SAFECAST(this, IUnknown *);
        DebugMsg(DM_TRACE, TEXT("QI IUnknown succeeded"));
    }
    else if (IsEqualIID(riid, IID_IShellIconOverlayManager))
    {
        *ppvObj = SAFECAST(this, IShellIconOverlayManager*);
        DebugMsg(DM_TRACE, TEXT("QI IShellIconOverlayManager succeeded"));
    } 
    else
    {
        *ppvObj = NULL;
        return E_NOINTERFACE;  // Otherwise, don't delegate to HTMLObj!!
    }
    
    AddRef();
    return S_OK;
}


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

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

    delete this;
    return 0;
}

int CFSIconOverlayManager::_GetImageIndex(FSIconOverlay * pfsio)
{
    int iImage = LookupIconIndex(pfsio->szIconFile, pfsio->iIconIndex, GIL_FORSHELL);

    if (iImage == -1)
    {
        // we couldn't find it from the cache
        HICON rghicon[ARRAYSIZE(g_rgshil)] = {0};

        for (int i = 0; i < ARRAYSIZE(g_rgshil); i++)
        {
            SHDefExtractIcon(pfsio->szIconFile, pfsio->iIconIndex, GIL_FORSHELL, &rghicon[i],
                             NULL, g_rgshil[i].size.cx);
        }

        iImage = SHAddIconsToCache(rghicon, pfsio->szIconFile, pfsio->iIconIndex, GIL_FORSHELL);

        _DestroyIcons(rghicon, ARRAYSIZE(rghicon));
    }
    
    return iImage;
}

FSIconOverlay * CFSIconOverlayManager::_FindMatchingID(LPCWSTR pwszPath, DWORD dwAttrib, int iMinPriority, int * pIOverlayIndex)
{
    // If we got here, we must have the DSA array
    ASSERT(_hdsaIconOverlays);
    if (_hdsaIconOverlays)
    {
        int ihdsa;
        for (ihdsa = 0; ihdsa < DSA_GetItemCount(_hdsaIconOverlays); ihdsa++)
        {
            FSIconOverlay * pfsio = (FSIconOverlay *)DSA_GetItemPtr(_hdsaIconOverlays, ihdsa);
            ASSERT(pfsio);
            if (pfsio->iPriority >= iMinPriority)
                continue;
            if (pfsio->psioi && pfsio->psioi->IsMemberOf(pwszPath, dwAttrib) == S_OK)
            {
                // Overlay indexes start from 1, and let's not use the reserved ones
                ASSERT(pIOverlayIndex);
                *pIOverlayIndex = ihdsa + OVERLAYINDEX_RESERVED + 1; 
                return pfsio;
            }
        }
    }
    return NULL;
}

HRESULT CFSIconOverlayManager::_SetGetOverlayInfo(FSIconOverlay * pfsio, int iOverlayIndex, int * pIndex, DWORD dwFlags)
{
    HRESULT hres = E_FAIL;
    RIP(pIndex);
    *pIndex = -1;
#if 0    // we don't want to return the priority for now
    if (dwFlags == SIOM_PRIORITY)
    {
        // This must have been initialized in the initialization function
        *pIndex = pfsio->iPriority;
    }
#endif
    if (pfsio->iImageIndex == -1)
    {
        int iImage = _GetImageIndex(pfsio);

        // Either we couldn't get it or we couldn't put it in cache 
        if (iImage == -1)
        {
            // leave this as a zombie
            pfsio->iImageIndex = 0;
            pfsio->iOverlayIndex = 0;
        }
        else
            pfsio->iImageIndex = iImage;
    }

    // Only if we have a reasonable image index will we proceed. 
    if (pfsio->iImageIndex > 0)
    {
        if (dwFlags == SIOM_ICONINDEX)
        {
            *pIndex = pfsio->iImageIndex;
        }
        else
        {
            ASSERT(iOverlayIndex > 0);
            ASSERT(iOverlayIndex <= MAX_OVERLAY_IMAGES);
            if (pfsio->iOverlayIndex == -1)
            {
                // Now set the overlay
                ASSERT(_IsSHILInited());

                for (int i = 0; i < ARRAYSIZE(g_rgshil); i++)
                {
                    ImageList_SetOverlayImage(g_rgshil[i].himl, pfsio->iImageIndex, iOverlayIndex);
                }

                pfsio->iOverlayIndex = iOverlayIndex;
            }

            // Must be the overlayindex flag
            ASSERT(dwFlags == SIOM_OVERLAYINDEX);
            *pIndex = pfsio->iOverlayIndex;
        }
        hres = S_OK;

    }
    return hres;
}

HRESULT CFSIconOverlayManager::GetFileOverlayInfo(LPCWSTR pwszPath, DWORD dwAttrib, int * pIndex, DWORD dwFlags)
{
    ASSERT((dwFlags == SIOM_OVERLAYINDEX) || (dwFlags == SIOM_ICONINDEX)); // || (dwFlags == SIOM_PRIORITY));

    HRESULT hres = E_FAIL;
    int iOverlayIndex;
    *pIndex = 0;

    ENTERCRITICAL;
    if (_hdsaIconOverlays)
    {
        FSIconOverlay * pfsio = _FindMatchingID(pwszPath, dwAttrib, MAX_OVERLAY_PRIORITY, &iOverlayIndex);
        if (pfsio)
            hres = _SetGetOverlayInfo(pfsio, iOverlayIndex, pIndex, dwFlags);
    }
    LEAVECRITICAL;
    return hres;
}

HRESULT CFSIconOverlayManager::GetReservedOverlayInfo(LPCWSTR pwszPath, DWORD dwAttrib, int * pIndex, DWORD dwFlags, int iReservedID)
{
    ASSERT(iReservedID < OVERLAYINDEX_RESERVED);
    HRESULT hres = S_OK;

    ENTERCRITICAL;
    if (_hdsaIconOverlays && pwszPath)
    {
        int iOverlayIndex;
        FSIconOverlay * pfsio = _FindMatchingID(pwszPath, dwAttrib, s_ReservedOverlays[iReservedID].iPriority, &iOverlayIndex);
        if (pfsio)
        {
            hres = _SetGetOverlayInfo(pfsio, iOverlayIndex, pIndex, dwFlags);
            LEAVECRITICAL;
            return hres;
        }
    }
    
    if (dwFlags == SIOM_ICONINDEX)
        *pIndex =  s_ReservedOverlays[iReservedID].iImageIndex;
    else
    {
        ASSERT(dwFlags == SIOM_OVERLAYINDEX);
        *pIndex =  s_ReservedOverlays[iReservedID].iOverlayIndex;
    }
    LEAVECRITICAL;

    return hres;
}


HRESULT CFSIconOverlayManager::CreateInstance(IUnknown* pUnkOuter, REFIID riid, OUT LPVOID * ppvOut)
{
    HRESULT hr;
    
    DebugMsg(DM_TRACE, TEXT("CFSIconOverlayManager::CreateInstance()"));
    
    *ppvOut = NULL;                     // null the out param

    CFSIconOverlayManager *pcfsiom = new CFSIconOverlayManager;

    if (!pcfsiom)
        return E_OUTOFMEMORY;

    hr = pcfsiom->_InitializeHdsaIconOverlays();
    if (SUCCEEDED(hr))
        hr = pcfsiom->QueryInterface(riid, ppvOut);
    pcfsiom->Release();

    return hr;
}


STDAPI CFSIconOverlayManager_CreateInstance(IUnknown* pUnkOuter, REFIID riid, OUT LPVOID *  ppvOut)
{
    return CFSIconOverlayManager::CreateInstance(pUnkOuter, riid, ppvOut);
}

STDAPI_(int) SHGetIconOverlayIndexW(LPCWSTR pwszIconPath, int iIconIndex)
{

    TCHAR szIconPath[MAX_PATH];
    int iRet = -1;
    int iImage = -1;

    // If NULL path is passed in, see if the index matches one of our special indexes
    if (pwszIconPath == NULL)
    {
        switch (iIconIndex)
        {
            case IDO_SHGIOI_SHARE:
                iImage = s_ReservedOverlays[0].iImageIndex;
                break;
            case IDO_SHGIOI_LINK:
                iImage = s_ReservedOverlays[1].iImageIndex;
                break;
            case IDO_SHGIOI_SLOWFILE:
                iImage = s_ReservedOverlays[2].iImageIndex;
                break;
        }
    }
    else if (SHUnicodeToTChar(pwszIconPath, szIconPath, ARRAYSIZE(szIconPath)))        
            // Try to load the image into the shell icon cache            
            iImage = Shell_GetCachedImageIndex(szIconPath, iIconIndex, 0);
    
    if (iImage >= 0)
    {
        IShellIconOverlayManager *psiom;
        if (SUCCEEDED(GetIconOverlayManager(&psiom)))
        {
            int iCandidate = -1;
            if (SUCCEEDED(psiom->OverlayIndexFromImageIndex(iImage, &iCandidate, TRUE)))
            {
                iRet = iCandidate;
            }
            psiom->Release();
        }
    }
    
    return iRet;
}

STDAPI_(int) SHGetIconOverlayIndexA(LPCSTR pszIconPath, int iIconIndex)
{
    int iRet = -1;
    WCHAR wszIconPath[MAX_PATH];
    LPCWSTR pwszIconPath = NULL;
    if (pszIconPath)
    {
        wszIconPath[0] = L'\0';
        SHAnsiToUnicode(pszIconPath, wszIconPath, ARRAYSIZE(wszIconPath));
        pwszIconPath = wszIconPath;
    }
    
    return  SHGetIconOverlayIndexW(pwszIconPath, iIconIndex);
}