#include "shellprv.h"
#pragma  hdrstop


#define ICON_MAGIC      0
#define ICO_MAGIC1      1
#define CUR_MAGIC1      2
#define BMP_MAGIC       ((WORD)'B'+((WORD)'M'<<8))
#define ANI_MAGIC       ((WORD)'R'+((WORD)'I'<<8))
#define ANI_MAGIC1      ((WORD)'F'+((WORD)'F'<<8))
#define ANI_MAGIC4      ((WORD)'A'+((WORD)'C'<<8))
#define ANI_MAGIC5      ((WORD)'O'+((WORD)'N'<<8))
#define MZMAGIC         ((WORD)'M'+((WORD)'Z'<<8))
#define PEMAGIC         ((WORD)'P'+((WORD)'E'<<8))
#define LEMAGIC         ((WORD)'L'+((WORD)'E'<<8))

typedef struct new_exe          NEWEXE,      *LPNEWEXE;
typedef struct exe_hdr          EXEHDR,      *LPEXEHDR;
typedef struct rsrc_nameinfo    RESNAMEINFO, *LPRESNAMEINFO;
typedef struct rsrc_typeinfo    RESTYPEINFO, *LPRESTYPEINFO;
typedef struct new_rsrc         RESTABLE,    *LPRESTABLE;

#define RESOURCE_VA(x)        ((x)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress)
#define RESOURCE_SIZE(x)      ((x)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size)
#define NUMBER_OF_SECTIONS(x) ((x)->FileHeader.NumberOfSections)

#define FCC(c0,c1,c2,c3) ((DWORD)(c0)|((DWORD)(c1)<<8)|((DWORD)(c2)<<16)|((DWORD)(c3)<<24))

#define COM_FILE        FCC('.', 'c', 'o', 'm')
#define BAT_FILE        FCC('.', 'b', 'a', 't')
#define CMD_FILE        FCC('.', 'c', 'm', 'd')
#define PIF_FILE        FCC('.', 'p', 'i', 'f')
#define LNK_FILE        FCC('.', 'l', 'n', 'k')
#define ICO_FILE        FCC('.', 'i', 'c', 'o')
#define EXE_FILE        FCC('.', 'e', 'x', 'e')

/****************************************************************************
* extract a single icon from a exe file, or get the count.
*
* If nIconIndex != -1
*  Returns:
*      The handle of the icon, if successful.
*      0, if the file does not exist or an icon with the "nIconIndex"
*         does not exist.
*      1, if the given file is not an EXE or ICO file.
*
* If nIconIndex == -1
*  Returns:
*      The number of icons in the file if successful.
*      0, if the file has no icons or isn't an icon file.
*
****************************************************************************/

HICON WINAPI ExtractIcon(HINSTANCE hInst, LPCTSTR szFileName, UINT nIconIndex)
{
    HICON hIcon;
    
    if (nIconIndex == (UINT) -1)
        hIcon = (HICON)IntToPtr( ExtractIcons(szFileName, 0, 0, 0, NULL, NULL, 0, 0) );
    else
        ExtractIcons(szFileName, nIconIndex, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), &hIcon, NULL, 1, 0);
    
    return hIcon;
}

/****************************************************************************
*
*  in:
*       lpIconPath      path of thing to extract icon for (may be an exe
*                       or something that is associated)
*       lpiIcon         icon index to use
*
*       lpIconPath      filled in with the real path where the icon came from
*       lpiIcon         filled in with the real icon index
*
*  returns:
*
* note: if the caller is the shell it returns special icons
* from within the shell.dll
*
****************************************************************************/

HICON WINAPI ExtractAssociatedIcon(HINSTANCE hInst, LPTSTR lpIconPath, WORD *lpiIcon)
{
    HICON hIcon = ExtractIcon(hInst, lpIconPath, *lpiIcon);
    
    if (hIcon == NULL)
        hIcon = SHGetFileIcon(NULL, lpIconPath, 0, SHGFI_LARGEICON);
    if (hIcon == NULL)
    {
        *lpiIcon = IDI_DOCUMENT;
        GetModuleFileName(HINST_THISDLL, lpIconPath, 128);
        hIcon = LoadIcon(HINST_THISDLL, MAKEINTRESOURCE(*lpiIcon));
    }
    
    return hIcon;
}


/****************************************************************************
*
*  extracts 1 or more icons from a file.
*
*  input:
*      szFileName          - EXE/DLL/ICO file to extract from
*      nIconIndex          - what icon to extract
*                              0 = first icon, 1=second icon, etc.
*                             -N = icon with id==N
*      phiconLarge         - place to return extracted icon(s)
*      phiconSmall         - place to return extracted icon(s) (small size)
*      nIcons              - number of icons to extract.
*
*  returns:
*      number of icons extracted, or the count of icons if phiconLarge==NULL
*
*  notes:
*      handles extraction from PE (Win32), NE (Win16), and ICO (Icon) files.
*      only Win16 3.x files are supported (not 2.x)
*
****************************************************************************/

UINT WINAPI ExtractIconExW(LPCWSTR szFileName, int nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIcons)
{
    return PrivateExtractIconExW( szFileName, nIconIndex, phiconLarge, phiconSmall, nIcons );
}

UINT WINAPI ExtractIconExA(LPCSTR szFileName, int nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIcons)
{
    return PrivateExtractIconExA( szFileName, nIconIndex, phiconLarge, phiconSmall, nIcons );
}

/****************************************************************************
*
*  extracts 1 or more icons from a file.
*
*  input:
*      szFileName          - EXE/DLL/ICO/CUR/ANI file to extract from
*      nIconIndex          - what icon to extract
*                              0 = first icon, 1=second icon, etc.
*                             -N = icon with id==N
*      cxIcon              - icon size wanted (if HIWORD != 0 two sizes...)
*      cyIcon              - icon size wanted (if HIWORD != 0 two sizes...)
*                            0,0 means extract at natural size.
*      phicon              - place to return extracted icon(s)
*      nIcons              - number of icons to extract.
*      flags               - LoadImage LR_* flags
*
*  returns:
*      if picon is NULL, number of icons in the file is returned.
*
*  notes:
*      handles extraction from PE (Win32), NE (Win16), ICO (Icon),
*      CUR (Cursor), ANI (Animated Cursor), and BMP (Bitmap) files.
*      only Win16 3.x files are supported (not 2.x)
*
*      cx/cyIcon are the size of the icon to extract, two sizes
*      can be extracted by putting size 1 in the loword and size 2 in the
*      hiword, ie MAKELONG(24, 48) would extract 24 and 48 size icons.
*      yea this is a hack. It is done so IExtractIcon::Extract
*      can be called by outside people with custom large/small icon
*      sizes that are not what the shell uses internaly.
*
****************************************************************************/

UINT WINAPI SHExtractIconsW(LPCWSTR wszFileName, int nIconIndex, int cxIcon, int cyIcon, HICON *phicon, UINT *piconid, UINT nIcons, UINT flags)
{
#ifdef UNICODE
    return ExtractIcons(wszFileName, nIconIndex, cxIcon, cyIcon, phicon, piconid, nIcons, flags);
#else // UNICODE
    TCHAR sz[MAX_PATH];
    
    SHUnicodeToTChar(wszFileName, sz, ARRAYSIZE(sz));
    return ExtractIcons(sz, nIconIndex, cxIcon, cyIcon, phicon, piconid, nIcons, flags);
#endif // UNICODE
}

UINT WINAPI ExtractIcons(LPCTSTR szFileName, int nIconIndex, int cxIcon, int cyIcon, HICON *phicon, UINT *piconid, UINT nIcons, UINT flags)
{
#ifdef UNICODE
    return PrivateExtractIconsW( szFileName, nIconIndex, cxIcon, cyIcon, phicon, piconid, nIcons, flags );
#else
    return PrivateExtractIconsA( szFileName, nIconIndex, cxIcon, cyIcon, phicon, piconid, nIcons, flags );
#endif
}

DWORD HasExtension(LPCTSTR pszPath)
{
    LPCTSTR p = PathFindExtension(pszPath);
    
    //
    //
    // PERF: BobDay - We could make this EXTKEY based like the extension
    // matching stuff elsewhere.  EXTKEY is a QWORD value so UNICODE would fit.
    //
    if (*p == TEXT('.'))
    {
#ifdef UNICODE
        WCHAR   szExt[5];
        
        lstrcpyn(szExt,p,5);
        
        if ( lstrcmpi(szExt,TEXT(".com")) == 0 ) return COM_FILE;
        if ( lstrcmpi(szExt,TEXT(".bat")) == 0 ) return BAT_FILE;
        if ( lstrcmpi(szExt,TEXT(".cmd")) == 0 ) return CMD_FILE;
        if ( lstrcmpi(szExt,TEXT(".pif")) == 0 ) return PIF_FILE;
        if ( lstrcmpi(szExt,TEXT(".lnk")) == 0 ) return LNK_FILE;
        if ( lstrcmpi(szExt,TEXT(".ico")) == 0 ) return ICO_FILE;
        if ( lstrcmpi(szExt,TEXT(".exe")) == 0 ) return EXE_FILE;
        return 0;
#else
        return *((UNALIGNED DWORD *)p) | 0x20202000;  // make lower case
#endif
    }
    else
        return 0;
    
}


/****************************************************************************
* get the EXE type of the passed file (DOS, Win16, Win32)
*
*  returns:
*      0 = not a exe of any type.
*
*      if a windows app
*          LOWORD = NE or PE
*          HIWORD = windows version 3.0, 3.5, 4.0
*
*      if a DOS app (or a .com or batch file on non-NT)
*          LOWORD = MZ
*          HIWORD = 0
*
*      if a Win32 console app (or a batch file on NT)
*          LOWORD = PE
*          HIWORD = 0
*
*  this is so similar to the Win32 API GetBinaryType() too bad Win95
*  kernel does not support it.
*
****************************************************************************/

DWORD WINAPI GetExeType(LPCTSTR szFile)
{
    HANDLE      fh;
    DWORD       dw;
    struct exe_hdr exehdr;
    struct new_exe newexe;
    FILETIME ftAccess;
    DWORD dwRead;
    
    //
    //  check for special extensions, and fail quick
    //
    switch (HasExtension(szFile))
    {
    case COM_FILE:
        // handle the case like \\server.microsoft.com
        // PERF - Bobday - This does the same operation twice, we should really
        // make PathIsUNCServerShare return a code based on what it found...
        if (PathIsUNCServer(szFile) || PathIsUNCServerShare(szFile))
            return 0;
        return MAKELONG(MZMAGIC, 0);  // DOS exe
        
    case BAT_FILE:
    case CMD_FILE:
        return MAKELONG(PEMAGIC, 0);    // NT exe (pretend)
        
    case EXE_FILE:                   // we need to open it.
        break;
        
    default:
        return 0;                    // not a exe, or if it is we dont care
    }
    
    newexe.ne_expver = 0;
    
    fh = CreateFile(szFile, GENERIC_READ | FILE_WRITE_ATTRIBUTES,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        0, OPEN_EXISTING, 0, 0);
    
    if (fh == INVALID_HANDLE_VALUE)
    {
        //
        // We may be trying to get properties for a file on a volume where
        // we don't have write access, so try opening the file for read
        // only access.  This will mean we can't preserve the access
        // time (those calls will fail), but this is better than not returning
        // the exe type at all...
        //
        
        fh = CreateFile(szFile, GENERIC_READ,
            FILE_SHARE_READ | FILE_SHARE_WRITE,
            0, OPEN_EXISTING, 0, 0);
        
        //
        // at this point if we get an INVALID_HANDLE_VALUE, we really
        // can't do much else, so now return a failure...
        //
        
        if (fh == INVALID_HANDLE_VALUE)
        {
            return 0;
        }
    }
    
    // preserve the access time
    
    if (GetFileTime(fh, NULL, &ftAccess, NULL))
        SetFileTime(fh, NULL, &ftAccess, NULL);
    
    if (!ReadFile(fh, &exehdr, sizeof(exehdr), &dwRead, NULL) ||
        (dwRead != sizeof(exehdr)))
        goto error;
    
    if (exehdr.e_magic != EMAGIC)
        goto error;
    
    SetFilePointer(fh, exehdr.e_lfanew, NULL, FILE_BEGIN);
    ReadFile(fh,&newexe, sizeof(newexe), &dwRead, NULL);
    
    if (newexe.ne_magic == PEMAGIC)
    {
        // read the SubsystemVersion
        SetFilePointer(fh, exehdr.e_lfanew+18*4, NULL, FILE_BEGIN);
        ReadFile(fh,&dw,4, &dwRead, NULL);
        newexe.ne_expver = LOBYTE(LOWORD(dw)) << 8 | LOBYTE(HIWORD(dw));
        
        // read the Subsystem
        SetFilePointer(fh, exehdr.e_lfanew+23*4, NULL, FILE_BEGIN);
        ReadFile(fh,&dw,4, &dwRead, NULL);
        
        // if it is not a Win32 GUI app return a version of 0
        if (LOWORD(dw) != 2) // IMAGE_SUBSYSTEM_WINDOWS_GUI
            newexe.ne_expver = 0;
        
        goto exit;
    }
    else if (newexe.ne_magic == LEMAGIC)
    {
        newexe.ne_magic = MZMAGIC;      // just a DOS exe
        newexe.ne_expver = 0;
    }
    else if (newexe.ne_magic == NEMAGIC)
    {
        //
        //  we found a 'NE' it still might not be a windows
        //  app, it could be.....
        //
        //      a OS/2 app      ne_exetyp==NE_OS2
        //      a DOS4 app      ne_exetyp==NE_DOS4
        //      a VxD           ne_exetyp==DEV386
        //
        //      only treat it as a Windows app if the exetype
        //      is NE_WINDOWS or NE_UNKNOWN
        //
        if (newexe.ne_exetyp != NE_WINDOWS && newexe.ne_exetyp != NE_UNKNOWN)
        {
            newexe.ne_magic = MZMAGIC;      // just a DOS exe
            newexe.ne_expver = 0;
        }
        
        //
        //  if could also have a bogus expected windows version
        //  (treat 0 as invalid)
        //
        if (newexe.ne_expver == 0)
        {
            newexe.ne_magic = MZMAGIC;      // just a DOS exe
            newexe.ne_expver = 0;
        }
    }
    else // if (newexe.ne_magic != NEMAGIC)
    {
        newexe.ne_magic = MZMAGIC;      // just a DOS exe
        newexe.ne_expver = 0;
    }
    
exit:
    CloseHandle(fh);
    return MAKELONG(newexe.ne_magic, newexe.ne_expver);
    
error:
    CloseHandle(fh);
    return 0;
}

#define M_llseek(fh, lOff, iOrg)            SetFilePointer((HANDLE)IntToPtr( fh ), lOff, NULL, (DWORD)iOrg)
#define M_lread(fh, lpBuf, cb)              _lread((HFILE)fh, lpBuf, cb)

#define MAGIC_ICON30            0
#define MAGIC_MARKZIBO          ((WORD)'M'+((WORD)'Z'<<8))

typedef struct new_exe          NEWEXEHDR;
typedef NEWEXEHDR               *PNEWEXEHDR;

#define SEEK_FROMZERO           0
#define SEEK_FROMCURRENT        1
#define SEEK_FROMEND            2
#define NSMOVE                  0x0010
#define VER                     0x0300

#define CCHICONPATHMAXLEN 128

typedef struct
{
    HANDLE hAppInst;
    HANDLE hFileName;
    HANDLE hIconList;
    INT    nIcons;
} EXTRACTICONINFO;

EXTRACTICONINFO ExtractIconInfo = {NULL, NULL, NULL, 0};

INT nIcons;

typedef struct 
{
    HICON hIcon;
    INT   iIconId;
} MYICONINFO;

HANDLE APIENTRY InternalExtractIconW(HINSTANCE hInst, LPCWSTR lpszExeFileName, UINT nIconIndex, UINT nIcons);

HICON APIENTRY DuplicateIcon(HINSTANCE hInst, HICON hIcon)
{
    ICONINFO  IconInfo;
    
    if (!GetIconInfo(hIcon, &IconInfo))
        return NULL;
    hIcon = CreateIconIndirect(&IconInfo);
    DeleteObject(IconInfo.hbmMask);
    DeleteObject(IconInfo.hbmColor);
    
    UNREFERENCED_PARAMETER(hInst);
    return hIcon;
}

// This returns a pointer to the rsrc_nameinfo of the resource with the
// given index and type, if it is found, otherwise it returns NULL.

LPBYTE FindResWithIndex(LPBYTE lpResTable, INT iResIndex, LPBYTE lpResType)
{
    try 
    {
        LPRESTYPEINFO lpResTypeInfo = (LPRESTYPEINFO)(lpResTable + sizeof(WORD));
        
        while (lpResTypeInfo->rt_id) 
        {
            if ((lpResTypeInfo->rt_id & RSORDID) &&
                (MAKEINTRESOURCE(lpResTypeInfo->rt_id & ~RSORDID) == (LPTSTR)lpResType)) 
            {
                if (lpResTypeInfo->rt_nres > (WORD)iResIndex)
                    return((LPBYTE)(lpResTypeInfo+1) + iResIndex * sizeof(RESNAMEINFO));
                else
                    return NULL;
            }
            
            lpResTypeInfo = (LPRESTYPEINFO)((LPBYTE)(lpResTypeInfo+1) + lpResTypeInfo->rt_nres * sizeof(RESNAMEINFO));
        }
        return NULL;
    }
    except (EXCEPTION_EXECUTE_HANDLER) 
    {
        return NULL;
    }
}


/* This returns the index (1-relative) of the given resource-id
* in the resource table, if it is found, otherwise it returns NULL.
*/

INT GetResIndex(LPBYTE lpResTable, INT iResId, LPBYTE lpResType)
{
    WORD w;
    LPRESNAMEINFO lpResNameInfo;
    LPRESTYPEINFO lpResTypeInfo = (LPRESTYPEINFO)(lpResTable + sizeof(WORD));
    
    while (lpResTypeInfo->rt_id)
    {
        if ((lpResTypeInfo->rt_id & RSORDID) && (MAKEINTRESOURCE(lpResTypeInfo->rt_id & ~RSORDID) == (LPTSTR)lpResType))
        {
            lpResNameInfo = (LPRESNAMEINFO)(lpResTypeInfo+1);
            for (w=0; w < lpResTypeInfo->rt_nres; w++, lpResNameInfo++)
            {
                if ((lpResNameInfo->rn_id & RSORDID) && ((lpResNameInfo->rn_id & ~RSORDID) == iResId))
                    return(w+1);
            }
            return 0;
        }
        lpResTypeInfo = (LPRESTYPEINFO)((LPBYTE)(lpResTypeInfo+1) + lpResTypeInfo->rt_nres * sizeof(RESNAMEINFO));
    }
    return 0;
}


HANDLE SimpleLoadResource(HFILE fh, LPBYTE lpResTable, INT iResIndex, LPBYTE lpResType)
{
    INT      iShiftCount;
    HICON    hIcon;
    LPBYTE            lpIcon;
    DWORD             dwSize;
    DWORD             dwOffset;
    LPRESNAMEINFO     lpResPtr;
    
    /* The first 2 bytes in ResTable indicate the amount other values should be
    * shifted left.
    */
    iShiftCount = *((WORD *)lpResTable);
    
    lpResPtr = (LPRESNAMEINFO)FindResWithIndex(lpResTable, iResIndex, lpResType);
    
    if (!lpResPtr)
        return NULL;
    
    /* Left shift the offset to form a LONG. */
    dwOffset = MAKELONG(lpResPtr->rn_offset << iShiftCount, (lpResPtr->rn_offset) >> (16 - iShiftCount));
    dwSize = lpResPtr->rn_length << iShiftCount;
    
    if (M_llseek(fh, dwOffset, SEEK_FROMZERO) == -1L)
        return NULL;
    
    if (!(hIcon = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE, dwSize)))
        return NULL;
    
    if (!(lpIcon = GlobalLock(hIcon)))
        goto SLRErr1;
    
    if (_lread(fh, (LPVOID)lpIcon, dwSize) < dwSize)
        goto SLRErr2;
    
    GlobalUnlock(hIcon);
    return hIcon;
    
SLRErr2:
    GlobalUnlock(hIcon);
SLRErr1:
    GlobalFree(hIcon);
    return NULL;
}


INT __cdecl CompareIconId(MYICONINFO *lpIconInfo1, MYICONINFO *lpIconInfo2)
{
    return lpIconInfo1->iIconId - lpIconInfo2->iIconId;
}

VOID FreeIconList(HANDLE hIconList, int iKeepIcon)
{
    MYICONINFO *lpIconList;
    INT i;
    
    if (ExtractIconInfo.hIconList == hIconList) {
        ExtractIconInfo.hIconList = NULL;
    }
    if (NULL != (lpIconList = (MYICONINFO *)GlobalLock(hIconList))) {
        for (i = 0; i < ExtractIconInfo.nIcons; i++) {
            if (i != iKeepIcon) {
                DestroyIcon((lpIconList + i)->hIcon);
            }
        }
        GlobalUnlock(hIconList);
        GlobalFree(hIconList);
    }
}

VOID FreeExtractIconInfo(INT iKeepIcon)
{
    MYICONINFO *lpIconList;
    INT i;
    
    if (ExtractIconInfo.hIconList) {
        if (NULL != (lpIconList = (MYICONINFO *)GlobalLock(ExtractIconInfo.hIconList))) {
            for (i = 0; i < ExtractIconInfo.nIcons; i++) {
                if (i != iKeepIcon) {
                    DestroyIcon((lpIconList + i)->hIcon);
                }
            }
            GlobalUnlock(ExtractIconInfo.hIconList);
        }
        GlobalFree(ExtractIconInfo.hIconList);
        ExtractIconInfo.hIconList = NULL;
    }
    
    ExtractIconInfo.hAppInst = NULL;
    ExtractIconInfo.nIcons = 0;
    
    if (ExtractIconInfo.hFileName) {
        GlobalFree(ExtractIconInfo.hFileName);
        ExtractIconInfo.hFileName = NULL;
    }
}


#ifdef UNICODE
HICON APIENTRY ExtractIconA(HINSTANCE hInst, LPCSTR lpszExeFileName, UINT nIconIndex)
{
    if (lpszExeFileName) {
        LPWSTR lpszExeFileNameW;
        WORD wLen  = lstrlenA(lpszExeFileName) + 1;
        
        if (!(lpszExeFileNameW = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, (wLen * sizeof(WCHAR))))) {
            return NULL;
        } else {
            HICON hIcon;
            
            MultiByteToWideChar(CP_ACP, 0, lpszExeFileName, -1, lpszExeFileNameW, wLen-1);
            
            hIcon = ExtractIconW(hInst, lpszExeFileNameW, nIconIndex);
            
            LocalFree(lpszExeFileNameW);
            return hIcon;
            
        }
    } else {
        return NULL;
    }
}
#else
HICON APIENTRY ExtractIconW(HINSTANCE hInst, LPCWSTR lpszExeFileName, UINT nIconIndex)
{
    return NULL;
}
#endif

// Returns a handle to a list of icons

#ifdef UNICODE

HANDLE APIENTRY InternalExtractIconListW(HANDLE hInst, LPWSTR lpszExeFileName, LPINT lpnIcons)
{
    UINT cIcons, uiResult, i;
    UINT * lpIDs = NULL;
    HICON * lpIcons = NULL;
    HGLOBAL hIconInfo = NULL;
    MYICONINFO *lpIconInfo = NULL;
    
    
    //
    // Determine the number of icons
    //
    
    cIcons = PtrToUlong( ExtractIconW(hInst, lpszExeFileName, (UINT)-1));
    
    if (cIcons <= 0)
        return NULL;
    
    
    //
    // Allocate space for an array of UINT's and HICON's
    //
    
    lpIDs = GlobalAlloc(GPTR, cIcons * sizeof(UINT));
    if (!lpIDs) {
        goto IconList_Exit;
    }
    
    lpIcons = GlobalAlloc(GPTR, cIcons * sizeof(HICON));
    if (!lpIcons) {
        goto IconList_Exit;
    }
    
    
    //
    // Allocate space for the array of icons
    //
    
    hIconInfo = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, cIcons * sizeof(MYICONINFO));
    if (!hIconInfo) {
        goto IconList_Exit;
    }
    
    
    //
    // This has to be GlobalLock'ed since the handle is going to
    // be passed back to the application.
    //
    
    lpIconInfo = GlobalLock(hIconInfo);
    if (!lpIconInfo) {
        goto IconList_Exit;
    }
    
    
    //
    // Call ExtractIcons to do the real work.
    //
    
    uiResult = ExtractIcons(lpszExeFileName,
        0,
        GetSystemMetrics(SM_CXICON),
        GetSystemMetrics(SM_CYICON),
        lpIcons,
        lpIDs,
        cIcons,
        0);
    
    if (uiResult <= 0) {
        goto IconList_Exit;
    }
    
    
    //
    // Loop through the icons and fill in the array.
    //
    
    for (i=0; i < cIcons; i++) {
        lpIconInfo[i].hIcon   = lpIcons[i];
        lpIconInfo[i].iIconId = lpIDs[i];
    }
    
    
    //
    // Unlock the array handle.
    //
    
    GlobalUnlock(hIconInfo);
    
    
    //
    // Clean up allocations
    //
    
    GlobalFree(lpIDs);
    GlobalFree(lpIcons);
    
    
    //
    // Success.
    //
    
    return hIconInfo;
    
    
IconList_Exit:
    
    //
    // Error case.  Clean up and return NULL
    //
    
    if (lpIconInfo)
        GlobalUnlock(hIconInfo);
    
    if (hIconInfo)
        GlobalFree(hIconInfo);
    
    if (lpIcons)
        GlobalFree(lpIcons);
    
    if (lpIDs)
        GlobalFree(lpIDs);
    
    return NULL;
}

#else  // Unicode

HANDLE APIENTRY InternalExtractIconListW(HANDLE hInst, LPWSTR lpszExeFileName, LPINT lpnIcons)
{
    return NULL;
}

#endif

HANDLE APIENTRY InternalExtractIconListA(HANDLE hInst, LPSTR lpszExeFileName, LPINT lpnIcons)
{
    return NULL;
}

#ifdef UNICODE
HANDLE APIENTRY InternalExtractIconA(HINSTANCE hInst, LPCSTR lpszExeFileName, UINT wIconIndex, UINT nIcons)
{
    return NULL;
}
#endif

/* ExtractVersionResource16W
* Retrieves a resource from win16 images.  Most of this code
* is stolen from ExtractIconResInfoW in ..\library\extract.c
*
* LPWSTR   lpwstrFilename - file to extract
* LPHANDLE lpData         - return buffer for handle, NULL if not needed
*
* Returns: size of buffer needed
*/

DWORD ExtractVersionResource16W(LPCWSTR  lpwstrFilename, LPHANDLE lphData)
{
    HFILE    fh;
    WORD     wMagic;
    
    INT       iTableSize;
    LPBYTE    lpResTable;
    DWORD     lOffset;
    HANDLE    hResTable;
    NEWEXEHDR NEHeader;
    HANDLE    hRes;
    DWORD     dwSize =0;
    
    //
    // Try to open the specified file.
    //
    
    fh = HandleToLong(CreateFileW(lpwstrFilename,
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL));
    
    if (fh == HandleToLong(INVALID_HANDLE_VALUE)) {
        fh = HandleToLong(CreateFileW(lpwstrFilename,
            GENERIC_READ,
            0,
            NULL,
            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
    }
    
    if (fh == HandleToLong(INVALID_HANDLE_VALUE))
        return 0;
    
    //
    // Read the first two bytes in the file.
    //
    if (_lread(fh, (LPVOID)&wMagic, 2) != 2)
        goto EIExit;
    
    switch (wMagic) {
    case MAGIC_MARKZIBO:
        
        //
        // Make sure that the file is in the NEW EXE format.
        //
        if (M_llseek(fh, (LONG)0x3C, SEEK_FROMZERO) == -1L)
            goto EIExit;
        
        if (_lread(fh, (LPVOID)&lOffset, sizeof(LONG)) != sizeof(LONG))
            goto EIExit;
        
        if (lOffset == 0L)
            goto EIExit;
        
        //
        // Read in the EXE header.
        //
        if (M_llseek(fh, lOffset, SEEK_FROMZERO) == -1L)
            goto EIExit;
        
        if (_lread(fh, (LPVOID)&NEHeader, sizeof(NEWEXEHDR)) != sizeof(NEWEXEHDR))
            goto EIExit;
        
        //
        // Is it a NEW EXE?
        //
        if (NE_MAGIC(NEHeader) != NEMAGIC)
            goto EIExit;
        
        if ((NE_EXETYP(NEHeader) != NE_WINDOWS) &&
            (NE_EXETYP(NEHeader) != NE_DEV386) &&
            (NE_EXETYP(NEHeader) != NE_UNKNOWN))  /* Some Win2.X apps have NE_UNKNOWN in this field */
            goto EIExit;
        
        //
        // Are there any resources?
        //
        if (NE_RSRCTAB(NEHeader) == NE_RESTAB(NEHeader))
            goto EIExit;
        
        //
        // Allocate space for the resource table.
        //
        iTableSize = NE_RESTAB(NEHeader) - NE_RSRCTAB(NEHeader);
        hResTable = GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, (DWORD)iTableSize);
        
        if (!hResTable)
            goto EIExit;
        
        //
        // Lock down the resource table.
        lpResTable = GlobalLock(hResTable);
        
        if (!lpResTable) {
            GlobalFree(hResTable);
            goto EIExit;
        }
        
        //
        // Copy the resource table into memory.
        //
        if (M_llseek(fh,
            (LONG)(lOffset + NE_RSRCTAB(NEHeader)),
            SEEK_FROMZERO) == -1) {
            
            goto EIErrExit;
        }
        
        if (_lread(fh, (LPBYTE)lpResTable, iTableSize) != (DWORD)iTableSize)
            goto EIErrExit;
        
        //
        // Simply load the specified icon.
        //
        hRes = SimpleLoadResource(fh, lpResTable, 0, (LPBYTE)RT_VERSION);
        
        if (hRes) {
            dwSize = (DWORD) GlobalSize(hRes);
            
            if (lphData) {
                
                *lphData = hRes;
            } else {
                
                GlobalFree(hRes);
            }
        }
        
EIErrExit:
        GlobalUnlock(hResTable);
        GlobalFree(hResTable);
        break;
        
    }
EIExit:
    _lclose(fh);
    
    return dwSize;
}

/*  Returns the file's format: 2 for WIndows 2.X, 3 for WIndows 3.X,        */
/*                             0 if error.                                  */
/*  Returns the handle to the Icon resource corresponding to wIconIndex     */
/*  in lphIconRes, and the size of the resource in lpwSize.                 */
/*  This is used only by Progman which needs to save the icon resource      */
/*  itself in the .GRP files (the actual icon handle is not wanted).        */
/*                                                                          */
/*  08-04-91 JohanneC      Created.                                         */

WORD APIENTRY ExtractIconResInfoW(HANDLE hInst, LPWSTR lpszFileName, WORD wIconIndex, LPWORD lpwSize, LPHANDLE lphIconRes)
{
    HFILE    fh;
    WORD     wMagic;
    BOOL     bNewResFormat;
    HANDLE   hIconDir;         /* Icon directory */
    LPBYTE   lpIconDir;
    HICON    hIcon = NULL;
    BOOL     bFormatOK = FALSE;
    INT      nIconId;
    WCHAR    szFullPath[MAX_PATH];
    int      cbPath;
    
    /* Try to open the specified file. */
    /* Try to open the specified file. */
    cbPath = SearchPathW(NULL, lpszFileName, NULL, MAX_PATH, szFullPath, NULL);
    if (cbPath == 0 || cbPath >= MAX_PATH)
        return 0;
    
    fh = HandleToLong(CreateFileW((LPCWSTR)szFullPath, GENERIC_READ, FILE_SHARE_READ, NULL,
        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
    
    if (fh == HandleToLong(INVALID_HANDLE_VALUE)) {
        fh = HandleToLong(CreateFileW((LPCWSTR)szFullPath, GENERIC_READ, 0, NULL,
            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
    }
    
    if (fh == HandleToLong(INVALID_HANDLE_VALUE))
        return 0;
    
    /* Read the first two bytes in the file. */
    if (_lread(fh, (LPVOID)&wMagic, 2) != 2)
        goto EIExit;
    
    switch (wMagic) {
    case MAGIC_ICON30:
        {
            INT           i;
            LPVOID        lpIcon;
            NEWHEADER     NewHeader;
            LPNEWHEADER   lpHeader;
            LPRESDIR      lpResDir;
            RESDIRDISK    ResDirDisk;
#define MAXICONS      10
            DWORD Offsets[MAXICONS];
            
            /* Only one icon per .ICO file. */
            if (wIconIndex) {
                break;
            }
            
            /* Read the header and check if it is a valid ICO file. */
            if (_lread(fh, ((LPBYTE)&NewHeader)+2, sizeof(NEWHEADER)-2) != sizeof(NEWHEADER)-2)
                goto EICleanup1;
            
            NewHeader.Reserved = MAGIC_ICON30;
            
            /* Check if the file is in correct format */
            if (NewHeader.ResType != 1)
                goto EICleanup1;
            
            /* Allocate enough space to create a Global Directory Resource. */
            hIconDir = GlobalAlloc(GHND, (LONG)(sizeof(NEWHEADER)+NewHeader.ResCount*sizeof(RESDIR)));
            if (hIconDir == NULL)
                goto EICleanup1;
            
            if ((lpHeader = (LPNEWHEADER)GlobalLock(hIconDir)) == NULL)
                goto EICleanup2;
            
            NewHeader.ResCount = (WORD)min((int)NewHeader.ResCount, MAXICONS);
            
            // fill in this structure for user
            
            *lpHeader = NewHeader;
            
            // read in the stuff from disk, transfer it to a memory structure
            // that user can deal with
            
            lpResDir = (LPRESDIR)(lpHeader + 1);
            for (i = 0; (WORD)i < NewHeader.ResCount; i++) {
                
                if (_lread(fh, (LPVOID)&ResDirDisk, sizeof(RESDIRDISK)) < sizeof(RESDIR))
                    goto EICleanup3;
                
                Offsets[i] = ResDirDisk.Offset;
                
                *lpResDir = *((LPRESDIR)&ResDirDisk);
                lpResDir->idIcon = (WORD)(i+1);         // fill in the id
                
                lpResDir++;
            }
            
            /* Now that we have the Complete resource directory, let us find out the
            * suitable form of icon (that matches the current display driver).
            */
            lpIconDir = GlobalLock(hIconDir);
            if (!lpIconDir) {
                GlobalFree(hIconDir);
                goto EIErrExit;
            }
            wIconIndex = (WORD)(LookupIconIdFromDirectory(lpIconDir, TRUE) - 1);
            GlobalUnlock(hIconDir);
            lpResDir = (LPRESDIR)(lpHeader+1) + wIconIndex;
            
            /* Allocate memory for the Resource to be loaded. */
            if ((hIcon = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE, (DWORD)lpResDir->BytesInRes)) == NULL)
                goto EICleanup3;
            if ((lpIcon = GlobalLock(hIcon)) == NULL)
                goto EICleanup4;
            
            /* Seek to the correct place and read in the resource */
            if (M_llseek(fh, Offsets[wIconIndex], SEEK_FROMZERO) == -1L)
                goto EICleanup5;
            if (_lread(fh, (LPVOID)lpIcon, (DWORD)lpResDir->BytesInRes) < lpResDir->BytesInRes)
                goto EICleanup5;
            GlobalUnlock(hIcon);
            
            *lphIconRes = hIcon;
            *lpwSize = (WORD)lpResDir->BytesInRes;
            bFormatOK = TRUE;
            bNewResFormat = TRUE;
            goto EICleanup3;
            
EICleanup5:
            GlobalUnlock(hIcon);
EICleanup4:
            GlobalFree(hIcon);
            hIcon = (HICON)1;
EICleanup3:
            GlobalUnlock(hIconDir);
EICleanup2:
            GlobalFree(hIconDir);
EICleanup1:
            break;
        }
        
      case MAGIC_MARKZIBO:
          {
              INT           iTableSize;
              LPBYTE         lpResTable;
              DWORD         lOffset;
              HANDLE        hResTable;
              NEWEXEHDR     NEHeader;
              
              /* Make sure that the file is in the NEW EXE format. */
              if (M_llseek(fh, (LONG)0x3C, SEEK_FROMZERO) == -1L)
                  goto EIExit;
              if (_lread(fh, (LPVOID)&lOffset, sizeof(LONG)) != sizeof(LONG))
                  goto EIExit;
              if (lOffset == 0L)
                  goto EIExit;
              
              /* Read in the EXE header. */
              if (M_llseek(fh, lOffset, SEEK_FROMZERO) == -1L)
                  goto EIExit;
              if (_lread(fh, (LPVOID)&NEHeader, sizeof(NEWEXEHDR)) != sizeof(NEWEXEHDR))
                  goto EIExit;
              
              /* Is it a NEW EXE? */
              if (NE_MAGIC(NEHeader) != NEMAGIC)
                  goto EIExit;
              
              if ((NE_EXETYP(NEHeader) != NE_WINDOWS) &&
                  (NE_EXETYP(NEHeader) != NE_DEV386) &&
                  (NE_EXETYP(NEHeader) != NE_UNKNOWN))  /* Some Win2.X apps have NE_UNKNOWN in this field */
                  goto EIExit;
              
              hIcon = NULL;
              
              /* Are there any resources? */
              if (NE_RSRCTAB(NEHeader) == NE_RESTAB(NEHeader))
                  goto EIExit;
              
              /* Remember whether or not this is a Win3.0 EXE. */
              bNewResFormat = (NEHeader.ne_expver >= VER);
              
              /* Allocate space for the resource table. */
              iTableSize = NE_RESTAB(NEHeader) - NE_RSRCTAB(NEHeader);
              hResTable = GlobalAlloc(GMEM_ZEROINIT, (DWORD)iTableSize);
              if (!hResTable)
                  goto EIExit;
              
              /* Lock down the resource table. */
              lpResTable = GlobalLock(hResTable);
              if (!lpResTable) {
                  GlobalFree(hResTable);
                  goto EIExit;
              }
              
              /* Copy the resource table into memory. */
              if (M_llseek(fh, (LONG)(lOffset + NE_RSRCTAB(NEHeader)), SEEK_FROMZERO) == -1)
                  goto EIErrExit;
              if (_lread(fh, (LPBYTE)lpResTable, iTableSize) != (DWORD)iTableSize)
                  goto EIErrExit;
              
              
              /* Is this a Win3.0 EXE? */
              if (bNewResFormat) {
                  /* First, load the Icon directory. */
                  hIconDir = SimpleLoadResource(fh, lpResTable, (int)wIconIndex, (LPBYTE)RT_GROUP_ICON);
                  
                  if (!hIconDir)
                      goto EIErrExit;
                  lpIconDir = GlobalLock(hIconDir);
                  if (!lpIconDir) {
                      GlobalFree(hIconDir);
                      goto EIErrExit;
                  }
                  nIconId = LookupIconIdFromDirectory(lpIconDir, TRUE);
                  wIconIndex = (WORD)(GetResIndex(lpResTable, nIconId, (LPBYTE)RT_ICON) - 1);
                  GlobalUnlock(hIconDir);
                  /* We're finished with the icon directory. */
                  GlobalFree(hIconDir);
                  
                  
                  /* Now load the selected icon. */
                  *lphIconRes = SimpleLoadResource(fh, lpResTable, (int)wIconIndex, (LPBYTE)RT_ICON);
              }
              else {
                  /* Simply load the specified icon. */
                  *lphIconRes = SimpleLoadResource(fh, lpResTable, (int)wIconIndex, (LPBYTE)RT_ICON);
              }
              
              if (*lphIconRes) {
                  *lpwSize = (WORD)GlobalSize(*lphIconRes);
              }
              bFormatOK = TRUE;
              
EIErrExit:
              GlobalUnlock(hResTable);
              GlobalFree(hResTable);
              break;
          }
    }
EIExit:
    _lclose(fh);
    hInst;
    if (bFormatOK)
        return (WORD)(bNewResFormat ? 3 : 2);
    else
        return 0;
}

WORD APIENTRY ExtractIconResInfoA(HANDLE hInst, LPSTR lpszFileName, WORD wIconIndex, LPWORD lpwSize, LPHANDLE lphIconRes)
{
    if (lpszFileName) 
    {
        LPWSTR lpszFileNameW;
        WORD wLen = lstrlenA(lpszFileName) + 1;
        
        if (!(lpszFileNameW = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, (wLen * sizeof(WCHAR))))) 
        {
            return 0;
        }
        else
        {
            WORD wRet;
            MultiByteToWideChar(CP_ACP, 0, lpszFileName, -1, lpszFileNameW, wLen - 1);
            wRet = ExtractIconResInfoW(hInst, lpszFileNameW, wIconIndex, lpwSize, lphIconRes);
            
            LocalFree(lpszFileNameW);
            return wRet;
        }
    }
    else 
    {
        return 0;
    }
}


LPCWSTR HasAnyExtension(LPCWSTR lpszPath)
{
    LPCWSTR p;
    
    for (p = lpszPath + lstrlenW(lpszPath); p > lpszPath && *p != L'.' && *p != L'\\'; p--)
    {
        // do nothing
    }
    
    if (*p == L'.')
        return p+1;
    else
        return NULL;
}


//
// in:
//      lpIconPath      path of thing to extract icon for (may be an exe
//                      or something that is associated)
//      lpiIconIndex    icon index to use
//
// out:
//      lpIconPath      filled in with the real path where the icon came from
//      lpiIconIndex    filled in with the real icon index
//      lpiIconId       filled in with the icon id
//
// returns:
//      icon handle
//
// note: if the caller is progman it returns special icons from within progman
//
//
#if defined(UNICODE)

HICON APIENTRY ExtractAssociatedIconExW(HINSTANCE hInst, LPWSTR lpIconPath, LPWORD lpiIconIndex, LPWORD lpiIconId)
{
    WCHAR wszExePath[MAX_PATH];
    HICON hIcon;
    UINT idIcon = (UINT)-1;     // Don't know value
    BOOL fAssociated = FALSE;
    
    if ((INT)*lpiIconIndex == -1)
        return (HICON)NULL;
    
Retry:
    ExtractIcons(lpIconPath, *lpiIconIndex, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON),
        &hIcon, &idIcon, 1, 0);
    
    if (hIcon == NULL)
    {
        wszExePath[0] = TEXT('\0');
        
        FindExecutable(lpIconPath,NULL,wszExePath);
        
        //
        // If FindExecutable fails, or fAssociated
        // is true, or FindExecutable returns the
        // extact same filename it was looking for,
        // then issue the default icon from progman.
        //
        
        if (!*wszExePath || fAssociated ||
            (*wszExePath && (lstrcmpi(lpIconPath, wszExePath) == 0)))
        {
            LPTSTR lpId;
            WORD wDefIconId;
            HANDLE h;
            LPVOID p;
            //
            // Magic values from NT's old progman
            //
#define ITEMICON          7
#define DOSAPPICON        2
#define ITEMICONINDEX     6
#define DOSAPPICONINDEX   1
            
            if ( *wszExePath && (HasExtension(wszExePath) == 0) )
            {
                //
                // Generic Document icon processing
                //
                lpId = MAKEINTRESOURCE(ITEMICON);
                wDefIconId = ITEMICONINDEX;
            }
            else
            {
                //
                // Generic Program icon processing
                //
                lpId = MAKEINTRESOURCE(DOSAPPICON);
                wDefIconId = DOSAPPICONINDEX;
            }
            GetModuleFileName(hInst, lpIconPath, CCHICONPATHMAXLEN);
            /*
            * Look up the icon id from the directory.
            */
            if (NULL != (h = FindResource(hInst, lpId, RT_GROUP_ICON))) {
                h = LoadResource(hInst, h);
                p = LockResource(h);
                *lpiIconId = (WORD)LookupIconIdFromDirectory(p, TRUE);
                UnlockResource(h);
                FreeResource(h);
            }
            *lpiIconIndex = wDefIconId;
            return LoadIcon(hInst, lpId);
        }
        SheRemoveQuotes(wszExePath);
        lstrcpy(lpIconPath,wszExePath);
        fAssociated = TRUE;
        goto Retry;
    }
    
    *lpiIconId = (WORD) idIcon;    // Fill in with whatever we've found (or -1)
    
    return hIcon;
}

#else // defined(UNICODE)

HICON APIENTRY ExtractAssociatedIconExW(HINSTANCE hInst, LPWSTR lpIconPath, LPWORD lpiIconIndex, LPWORD lpiIconId)
{
    HICON hIcon = NULL;
    WCHAR szIconExe[MAX_PATH];
    HANDLE hIconList = NULL;
    MYICONINFO *lpIconList;
    HANDLE h;
    PBYTE p;
    DWORD dwBinaryType;
    BOOL bRet;
    int cIcons;
    
#define ITEMICON          7               // taken from progman!
#define DOSAPPICON        2
#define ITEMICONINDEX     6
#define DOSAPPICONINDEX   1
    
    if (!lpIconPath)
        return NULL;

    FreeExtractIconInfo(-1);
    
    hIcon = ExtractIconW(hInst, lpIconPath, (UINT)*lpiIconIndex);
    
    if (!hIcon) 
    {
        // lpIconPath is a windows EXE, no icons found
GenericDocument:
        FreeExtractIconInfo(-1);
        GetModuleFileNameW(hInst, lpIconPath, CCHICONPATHMAXLEN);
        // Look up the icon id from the directory.
        if (NULL != (h = FindResource(hInst, MAKEINTRESOURCE(ITEMICON), RT_GROUP_ICON))) 
        {
            h = LoadResource(hInst, h);
            if (h != NULL)
            {
                p = LockResource(h);
                if (p != NULL)
                {
                    *lpiIconId = (WORD)LookupIconIdFromDirectory(p, TRUE);
                    UnlockResource(h);
                }
                FreeResource(h);
            }
        }
        *lpiIconIndex = ITEMICONINDEX;
        return LoadIcon(hInst, MAKEINTRESOURCE(ITEMICON));
    }

    if ((int)hIcon == 1) 
    {
        // lpIconPath is not a windows EXE
        // this fills in szIconExe with the thing that would be exected
        // for lpIconPath (applying associations)
        
        FindExecutableW(lpIconPath, NULL, szIconExe);
        
        if (!*szIconExe) 
        {
            // not associated, assume things with extension are
            // programs, things without are documents
            
            if (!HasAnyExtension(lpIconPath))
                goto GenericDocument;
            else
                goto GenericProgram;
        }
        
        //
        // If FindExecutable returned an icon path with quotes, we must
        // remove them because ExtractIcon fails with quoted paths.
        //
        SheRemoveQuotesW(szIconExe);
        
        lstrcpyW(lpIconPath, szIconExe);
        
        if (!HasAnyExtension(lpIconPath))
            lstrcatW(lpIconPath, L".EXE");
        
        hIcon = ExtractIconW(hInst, lpIconPath, (UINT)*lpiIconIndex);
        if (!hIcon)
            goto GenericDocument;
        
        if ((int)hIcon == 1) 
        {
            // this is a DOS exe
GenericProgram:
            FreeExtractIconInfo(-1);
            GetModuleFileNameW(hInst, lpIconPath, CCHICONPATHMAXLEN);
            // Look up the icon id from the directory.
            if (NULL != (h = FindResource(hInst, MAKEINTRESOURCE(DOSAPPICON), RT_GROUP_ICON))) 
            {
                h = LoadResource(hInst, h);
                if (h != NULL)
                {
                    p = LockResource(h);
                    if ( p != NULL)
                    {
                        *lpiIconId = (WORD)LookupIconIdFromDirectory(p, TRUE);
                        UnlockResource(h);
                    }
                    FreeResource(h);
                }
            }
            *lpiIconIndex = DOSAPPICONINDEX;
            return LoadIcon(hInst, MAKEINTRESOURCE(DOSAPPICON));
        }
        else 
        {
            goto GotIcon;
        }
    }
    else 
    {
GotIcon:
        bRet = GetBinaryTypeW(lpIconPath, &dwBinaryType);
        if (bRet) 
        {
            if (dwBinaryType != SCS_32BIT_BINARY) 
            {
                *lpiIconId = *lpiIconIndex;
                return hIcon;
            }
        }
    
        ExtractIconW(hInst, lpIconPath, (UINT)-1);
    
        if (NULL == (hIconList = ExtractIconInfo.hIconList))
            return hIcon;
    
        // since the icon exe is a WIN32 app, then we must update *lpiIcon.
        if (NULL != (hIconList = InternalExtractIconListW(hInst, lpIconPath, &cIcons))) 
        {
            if (NULL != (lpIconList = (MYICONINFO *)GlobalLock(hIconList))) 
            {
                hIcon = (lpIconList + *lpiIconIndex)->hIcon;
                *lpiIconId = (lpIconList + *lpiIconIndex)->iIconId;
                GlobalUnlock(hIconList);
            }
            FreeIconList(hIconList, *lpiIconIndex);
            return hIcon;
        }
    }
    return hIcon;
}
#endif

HICON APIENTRY ExtractAssociatedIconExA(HINSTANCE hInst, LPSTR lpIconPath, LPWORD lpiIconIndex, LPWORD lpiIconId)
{
    HICON hIcon = NULL;
    
    if (lpIconPath) 
    {
        BOOL fDefCharUsed;
        WCHAR IconPathW[MAX_PATH] = L"";
        
        MultiByteToWideChar(CP_ACP, 0, lpIconPath, -1 , (LPWSTR)IconPathW, MAX_PATH);
        hIcon = ExtractAssociatedIconExW(hInst, (LPWSTR)IconPathW, lpiIconIndex, lpiIconId);
        
        try 
        {
            WideCharToMultiByte(CP_ACP, 0, (LPWSTR)IconPathW, -1, lpIconPath, CCHICONPATHMAXLEN,
                NULL, &fDefCharUsed);
        }
        except(EXCEPTION_EXECUTE_HANDLER) 
        {
            hIcon = NULL;
        }
    }
    return hIcon;
}

//
// in:
//      lpIconPath      path of thing to extract icon for (may be an exe
//                      or something that is associated)
//      lpiIcon         icon index to use
//
// out:
//      lpIconPath      filled in with the real path where the icon came from
//      lpiIcon         filled in with the real icon index
//
// returns:
//      icon handle
//
// note: if the caller is progman it returns special icons from within progman
//
//

#ifdef UNICODE
HICON APIENTRY ExtractAssociatedIconA(HINSTANCE hInst, LPSTR lpIconPath, LPWORD lpiIcon)
{
    HICON hIcon = NULL;
    
    if (lpIconPath) 
    {
        BOOL fDefCharUsed;
        WCHAR IconPathW[MAX_PATH] = L"";
        
        MultiByteToWideChar(CP_ACP, 0, lpIconPath, -1 , (LPWSTR)IconPathW, MAX_PATH);
        hIcon = ExtractAssociatedIconW(hInst, (LPWSTR)IconPathW, lpiIcon);
        
        try 
        {
            WideCharToMultiByte(CP_ACP, 0, (LPWSTR)IconPathW, -1, lpIconPath, CCHICONPATHMAXLEN,
                NULL, &fDefCharUsed);
        }
        except(EXCEPTION_EXECUTE_HANDLER)
        {
            hIcon = NULL;
        }
    }
    return hIcon;
}
#endif