xref: /reactos/dll/win32/shell32/iconcache.cpp (revision 190b3da9)
1 /*
2  *    shell icon cache (SIC)
3  *
4  * Copyright 1998, 1999 Juergen Schmied
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include "precomp.h"
22 
23 WINE_DEFAULT_DEBUG_CHANNEL(shell);
24 
25 /********************** THE ICON CACHE ********************************/
26 
27 #define INVALID_INDEX -1
28 
29 typedef struct
30 {
31     LPWSTR sSourceFile;    /* file (not path!) containing the icon */
32     DWORD dwSourceIndex;    /* index within the file, if it is a resoure ID it will be negated */
33     DWORD dwListIndex;    /* index within the iconlist */
34     DWORD dwFlags;        /* GIL_* flags */
35     DWORD dwAccessTime;
36 } SIC_ENTRY, * LPSIC_ENTRY;
37 
38 static HDPA        sic_hdpa = 0;
39 
40 static HIMAGELIST ShellSmallIconList;
41 static HIMAGELIST ShellBigIconList;
42 
43 namespace
44 {
45 extern CRITICAL_SECTION SHELL32_SicCS;
46 CRITICAL_SECTION_DEBUG critsect_debug =
47 {
48     0, 0, &SHELL32_SicCS,
49     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
50       0, 0, { (DWORD_PTR)(__FILE__ ": SHELL32_SicCS") }
51 };
52 CRITICAL_SECTION SHELL32_SicCS = { &critsect_debug, -1, 0, 0, 0, 0 };
53 }
54 
55 /*****************************************************************************
56  * SIC_CompareEntries
57  *
58  * NOTES
59  *  Callback for DPA_Search
60  */
61 static INT CALLBACK SIC_CompareEntries( LPVOID p1, LPVOID p2, LPARAM lparam)
62 {    LPSIC_ENTRY e1 = (LPSIC_ENTRY)p1, e2 = (LPSIC_ENTRY)p2;
63 
64     TRACE("%p %p %8lx\n", p1, p2, lparam);
65 
66     /* Icons in the cache are keyed by the name of the file they are
67      * loaded from, their resource index and the fact if they have a shortcut
68      * icon overlay or not.
69      */
70     /* first the faster one */
71     if (e1->dwSourceIndex != e2->dwSourceIndex)
72       return (e1->dwSourceIndex < e2->dwSourceIndex) ? -1 : 1;
73 
74     if ((e1->dwFlags & GIL_FORSHORTCUT) != (e2->dwFlags & GIL_FORSHORTCUT))
75       return ((e1->dwFlags & GIL_FORSHORTCUT) < (e2->dwFlags & GIL_FORSHORTCUT)) ? -1 : 1;
76 
77     return wcsicmp(e1->sSourceFile,e2->sSourceFile);
78 }
79 
80 /* declare SIC_LoadOverlayIcon() */
81 static int SIC_LoadOverlayIcon(int icon_idx);
82 
83 /*****************************************************************************
84  * SIC_OverlayShortcutImage            [internal]
85  *
86  * NOTES
87  *  Creates a new icon as a copy of the passed-in icon, overlayed with a
88  *  shortcut image.
89  * FIXME: This should go to the ImageList implementation!
90  */
91 static HICON SIC_OverlayShortcutImage(HICON SourceIcon, BOOL large)
92 {
93     ICONINFO ShortcutIconInfo, TargetIconInfo;
94     HICON ShortcutIcon = NULL, TargetIcon;
95     BITMAP TargetBitmapInfo, ShortcutBitmapInfo;
96     HDC ShortcutDC = NULL,
97       TargetDC = NULL;
98     HBITMAP OldShortcutBitmap = NULL,
99       OldTargetBitmap = NULL;
100 
101     static int s_imgListIdx = -1;
102     ZeroMemory(&ShortcutIconInfo, sizeof(ShortcutIconInfo));
103     ZeroMemory(&TargetIconInfo, sizeof(TargetIconInfo));
104 
105     /* Get information about the source icon and shortcut overlay.
106      * We will write over the source bitmaps to get the final ones */
107     if (! GetIconInfo(SourceIcon, &TargetIconInfo))
108         return NULL;
109 
110     /* Is it possible with the ImageList implementation? */
111     if(!TargetIconInfo.hbmColor)
112     {
113         /* Maybe we'll support this at some point */
114         FIXME("1bpp icon wants its overlay!\n");
115         goto fail;
116     }
117 
118     if(!GetObjectW(TargetIconInfo.hbmColor, sizeof(BITMAP), &TargetBitmapInfo))
119     {
120         goto fail;
121     }
122 
123     /* search for the shortcut icon only once */
124     if (s_imgListIdx == -1)
125         s_imgListIdx = SIC_LoadOverlayIcon(IDI_SHELL_SHORTCUT - 1);
126 
127     if (s_imgListIdx != -1)
128     {
129         if (large)
130             ShortcutIcon = ImageList_GetIcon(ShellBigIconList, s_imgListIdx, ILD_TRANSPARENT);
131         else
132             ShortcutIcon = ImageList_GetIcon(ShellSmallIconList, s_imgListIdx, ILD_TRANSPARENT);
133     } else
134         ShortcutIcon = NULL;
135 
136     if (!ShortcutIcon || !GetIconInfo(ShortcutIcon, &ShortcutIconInfo))
137     {
138         goto fail;
139     }
140 
141     /* Is it possible with the ImageLists ? */
142     if(!ShortcutIconInfo.hbmColor)
143     {
144         /* Maybe we'll support this at some point */
145         FIXME("Should draw 1bpp overlay!\n");
146         goto fail;
147     }
148 
149     if(!GetObjectW(ShortcutIconInfo.hbmColor, sizeof(BITMAP), &ShortcutBitmapInfo))
150     {
151         goto fail;
152     }
153 
154     /* Setup the masks */
155     ShortcutDC = CreateCompatibleDC(NULL);
156     if (NULL == ShortcutDC) goto fail;
157     OldShortcutBitmap = (HBITMAP)SelectObject(ShortcutDC, ShortcutIconInfo.hbmMask);
158     if (NULL == OldShortcutBitmap) goto fail;
159 
160     TargetDC = CreateCompatibleDC(NULL);
161     if (NULL == TargetDC) goto fail;
162     OldTargetBitmap = (HBITMAP)SelectObject(TargetDC, TargetIconInfo.hbmMask);
163     if (NULL == OldTargetBitmap) goto fail;
164 
165     /* Create the complete mask by ANDing the source and shortcut masks.
166      * NOTE: in an ImageList, all icons have the same dimensions */
167     if (!BitBlt(TargetDC, 0, 0, ShortcutBitmapInfo.bmWidth, ShortcutBitmapInfo.bmHeight,
168                 ShortcutDC, 0, 0, SRCAND))
169     {
170       goto fail;
171     }
172 
173     /*
174      * We must remove or add the alpha component to the shortcut overlay:
175      * If we don't, SRCCOPY will copy it to our resulting icon, resulting in a
176      * partially transparent icons where it shouldn't be, and to an invisible icon
177      * if the underlying icon don't have any alpha channel information. (16bpp only icon for instance).
178      * But if the underlying icon has alpha channel information, then we must mark the overlay information
179      * as opaque.
180      * NOTE: This code sucks(tm) and should belong to the ImageList implementation.
181      * NOTE2: there are better ways to do this.
182      */
183     if(ShortcutBitmapInfo.bmBitsPixel == 32)
184     {
185         BOOL add_alpha;
186         BYTE buffer[sizeof(BITMAPINFO) + 256 * sizeof(RGBQUAD)];
187         BITMAPINFO* lpbmi = (BITMAPINFO*)buffer;
188         PVOID bits;
189         PULONG pixel;
190         INT i, j;
191 
192         /* Find if the source bitmap has an alpha channel */
193         if(TargetBitmapInfo.bmBitsPixel != 32) add_alpha = FALSE;
194         else
195         {
196             ZeroMemory(buffer, sizeof(buffer));
197             lpbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
198             lpbmi->bmiHeader.biWidth = TargetBitmapInfo.bmWidth;
199             lpbmi->bmiHeader.biHeight = TargetBitmapInfo.bmHeight;
200             lpbmi->bmiHeader.biPlanes = 1;
201             lpbmi->bmiHeader.biBitCount = 32;
202 
203             bits = HeapAlloc(GetProcessHeap(), 0, TargetBitmapInfo.bmHeight * TargetBitmapInfo.bmWidthBytes);
204 
205             if(!bits) goto fail;
206 
207             if(!GetDIBits(TargetDC, TargetIconInfo.hbmColor, 0, TargetBitmapInfo.bmHeight, bits, lpbmi, DIB_RGB_COLORS))
208             {
209                 ERR("GetBIBits failed!\n");
210                 HeapFree(GetProcessHeap(), 0, bits);
211                 goto fail;
212             }
213 
214             i = j = 0;
215             pixel = (PULONG)bits;
216 
217             for(i=0; i<TargetBitmapInfo.bmHeight; i++)
218             {
219                 for(j=0; j<TargetBitmapInfo.bmWidth; j++)
220                 {
221                     add_alpha = (*pixel++ & 0xFF000000) != 0;
222                     if(add_alpha) break;
223                 }
224                 if(add_alpha) break;
225             }
226             HeapFree(GetProcessHeap(), 0, bits);
227         }
228 
229         /* Allocate the bits */
230         bits = HeapAlloc(GetProcessHeap(), 0, ShortcutBitmapInfo.bmHeight*ShortcutBitmapInfo.bmWidthBytes);
231         if(!bits) goto fail;
232 
233         ZeroMemory(buffer, sizeof(buffer));
234         lpbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
235         lpbmi->bmiHeader.biWidth = ShortcutBitmapInfo.bmWidth;
236         lpbmi->bmiHeader.biHeight = ShortcutBitmapInfo.bmHeight;
237         lpbmi->bmiHeader.biPlanes = 1;
238         lpbmi->bmiHeader.biBitCount = 32;
239 
240         if(!GetDIBits(TargetDC, ShortcutIconInfo.hbmColor, 0, ShortcutBitmapInfo.bmHeight, bits, lpbmi, DIB_RGB_COLORS))
241         {
242             ERR("GetBIBits failed!\n");
243             HeapFree(GetProcessHeap(), 0, bits);
244             goto fail;
245         }
246 
247         pixel = (PULONG)bits;
248         /* Remove alpha channel component or make it totally opaque */
249         for(i=0; i<ShortcutBitmapInfo.bmHeight; i++)
250         {
251             for(j=0; j<ShortcutBitmapInfo.bmWidth; j++)
252             {
253                 if(add_alpha) *pixel++ |= 0xFF000000;
254                 else *pixel++ &= 0x00FFFFFF;
255             }
256         }
257 
258         /* GetDIBits return BI_BITFIELDS with masks set to 0, and SetDIBits fails when masks are 0. The irony... */
259         lpbmi->bmiHeader.biCompression = BI_RGB;
260 
261         /* Set the bits again */
262         if(!SetDIBits(TargetDC, ShortcutIconInfo.hbmColor, 0, ShortcutBitmapInfo.bmHeight, bits, lpbmi, DIB_RGB_COLORS))
263         {
264             ERR("SetBIBits failed!, %lu\n", GetLastError());
265             HeapFree(GetProcessHeap(), 0, bits);
266             goto fail;
267         }
268         HeapFree(GetProcessHeap(), 0, bits);
269     }
270 
271     /* Now do the copy. We overwrite the original icon data */
272     if (NULL == SelectObject(ShortcutDC, ShortcutIconInfo.hbmColor) ||
273         NULL == SelectObject(TargetDC, TargetIconInfo.hbmColor))
274         goto fail;
275     if (!MaskBlt(TargetDC, 0, 0, ShortcutBitmapInfo.bmWidth, ShortcutBitmapInfo.bmHeight,
276                  ShortcutDC, 0, 0, ShortcutIconInfo.hbmMask, 0, 0,
277                  MAKEROP4(0xAA0000, SRCCOPY)))
278     {
279         goto fail;
280     }
281 
282     /* Clean up, we're not goto'ing to 'fail' after this so we can be lazy and not set
283        handles to NULL */
284     SelectObject(TargetDC, OldTargetBitmap);
285     DeleteDC(TargetDC);
286     SelectObject(ShortcutDC, OldShortcutBitmap);
287     DeleteDC(ShortcutDC);
288 
289     /* Create the icon using the bitmaps prepared earlier */
290     TargetIcon = CreateIconIndirect(&TargetIconInfo);
291 
292     /* CreateIconIndirect copies the bitmaps, so we can release our bitmaps now */
293     DeleteObject(TargetIconInfo.hbmColor);
294     DeleteObject(TargetIconInfo.hbmMask);
295     /* Delete what GetIconInfo gave us */
296     DeleteObject(ShortcutIconInfo.hbmColor);
297     DeleteObject(ShortcutIconInfo.hbmMask);
298     DestroyIcon(ShortcutIcon);
299 
300     return TargetIcon;
301 
302 fail:
303     /* Clean up scratch resources we created */
304     if (NULL != OldTargetBitmap) SelectObject(TargetDC, OldTargetBitmap);
305     if (NULL != TargetDC) DeleteDC(TargetDC);
306     if (NULL != OldShortcutBitmap) SelectObject(ShortcutDC, OldShortcutBitmap);
307     if (NULL != ShortcutDC) DeleteDC(ShortcutDC);
308     if (NULL != TargetIconInfo.hbmColor) DeleteObject(TargetIconInfo.hbmColor);
309     if (NULL != TargetIconInfo.hbmMask) DeleteObject(TargetIconInfo.hbmMask);
310     if (NULL != ShortcutIconInfo.hbmColor) DeleteObject(ShortcutIconInfo.hbmColor);
311     if (NULL != ShortcutIconInfo.hbmMask) DeleteObject(ShortcutIconInfo.hbmMask);
312     if (NULL != ShortcutIcon) DestroyIcon(ShortcutIcon);
313 
314     return NULL;
315 }
316 
317 /*****************************************************************************
318  * SIC_IconAppend            [internal]
319  *
320  * NOTES
321  *  appends an icon pair to the end of the cache
322  */
323 static INT SIC_IconAppend (LPCWSTR sSourceFile, INT dwSourceIndex, HICON hSmallIcon, HICON hBigIcon, DWORD dwFlags)
324 {
325     LPSIC_ENTRY lpsice;
326     INT ret, index, index1, indexDPA;
327     WCHAR path[MAX_PATH];
328     TRACE("%s %i %p %p\n", debugstr_w(sSourceFile), dwSourceIndex, hSmallIcon ,hBigIcon);
329 
330     lpsice = (LPSIC_ENTRY) SHAlloc (sizeof (SIC_ENTRY));
331 
332     GetFullPathNameW(sSourceFile, MAX_PATH, path, NULL);
333     lpsice->sSourceFile = (LPWSTR)HeapAlloc( GetProcessHeap(), 0, (wcslen(path)+1)*sizeof(WCHAR) );
334     wcscpy( lpsice->sSourceFile, path );
335 
336     lpsice->dwSourceIndex = dwSourceIndex;
337     lpsice->dwFlags = dwFlags;
338 
339     EnterCriticalSection(&SHELL32_SicCS);
340 
341     indexDPA = DPA_Search (sic_hdpa, lpsice, 0, SIC_CompareEntries, 0, DPAS_SORTED|DPAS_INSERTAFTER);
342     indexDPA = DPA_InsertPtr(sic_hdpa, indexDPA, lpsice);
343     if ( -1 == indexDPA )
344     {
345         ret = INVALID_INDEX;
346         goto leave;
347     }
348 
349     index = ImageList_AddIcon (ShellSmallIconList, hSmallIcon);
350     index1= ImageList_AddIcon (ShellBigIconList, hBigIcon);
351 
352     /* Something went wrong when allocating a new image in the list. Abort. */
353     if((index == -1) || (index1 == -1))
354     {
355         WARN("Something went wrong when adding the icon to the list: small - 0x%x, big - 0x%x.\n",
356             index, index1);
357         if(index != -1) ImageList_Remove(ShellSmallIconList, index);
358         if(index1 != -1) ImageList_Remove(ShellBigIconList, index1);
359         ret = INVALID_INDEX;
360         goto leave;
361     }
362 
363     if (index!=index1)
364     {
365         FIXME("iconlists out of sync 0x%x 0x%x\n", index, index1);
366         /* What to do ???? */
367     }
368     lpsice->dwListIndex = index;
369     ret = lpsice->dwListIndex;
370 
371 leave:
372     if(ret == INVALID_INDEX)
373     {
374         if(indexDPA != -1) DPA_DeletePtr(sic_hdpa, indexDPA);
375         HeapFree(GetProcessHeap(), 0, lpsice->sSourceFile);
376         SHFree(lpsice);
377     }
378     LeaveCriticalSection(&SHELL32_SicCS);
379     return ret;
380 }
381 /****************************************************************************
382  * SIC_LoadIcon                [internal]
383  *
384  * NOTES
385  *  gets small/big icon by number from a file
386  */
387 static INT SIC_LoadIcon (LPCWSTR sSourceFile, INT dwSourceIndex, DWORD dwFlags)
388 {
389     HICON hiconLarge=0;
390     HICON hiconSmall=0;
391     UINT ret;
392 
393     PrivateExtractIconsW(sSourceFile, dwSourceIndex, 32, 32, &hiconLarge, NULL, 1, LR_COPYFROMRESOURCE);
394     PrivateExtractIconsW(sSourceFile, dwSourceIndex, 16, 16, &hiconSmall, NULL, 1, LR_COPYFROMRESOURCE);
395 
396     if ( !hiconLarge ||  !hiconSmall)
397     {
398         WARN("failure loading icon %i from %s (%p %p)\n", dwSourceIndex, debugstr_w(sSourceFile), hiconLarge, hiconSmall);
399         if(hiconLarge) DestroyIcon(hiconLarge);
400         if(hiconSmall) DestroyIcon(hiconSmall);
401         return INVALID_INDEX;
402     }
403 
404     if (0 != (dwFlags & GIL_FORSHORTCUT))
405     {
406         HICON hiconLargeShortcut = SIC_OverlayShortcutImage(hiconLarge, TRUE);
407         HICON hiconSmallShortcut = SIC_OverlayShortcutImage(hiconSmall, FALSE);
408         if (NULL != hiconLargeShortcut && NULL != hiconSmallShortcut)
409         {
410             DestroyIcon(hiconLarge);
411             DestroyIcon(hiconSmall);
412             hiconLarge = hiconLargeShortcut;
413             hiconSmall = hiconSmallShortcut;
414         }
415         else
416         {
417             WARN("Failed to create shortcut overlayed icons\n");
418             if (NULL != hiconLargeShortcut) DestroyIcon(hiconLargeShortcut);
419             if (NULL != hiconSmallShortcut) DestroyIcon(hiconSmallShortcut);
420             dwFlags &= ~ GIL_FORSHORTCUT;
421         }
422     }
423 
424     ret = SIC_IconAppend (sSourceFile, dwSourceIndex, hiconSmall, hiconLarge, dwFlags);
425     DestroyIcon(hiconLarge);
426     DestroyIcon(hiconSmall);
427     return ret;
428 }
429 /*****************************************************************************
430  * SIC_GetIconIndex            [internal]
431  *
432  * Parameters
433  *    sSourceFile    [IN]    filename of file containing the icon
434  *    index        [IN]    index/resID (negated) in this file
435  *
436  * NOTES
437  *  look in the cache for a proper icon. if not available the icon is taken
438  *  from the file and cached
439  */
440 INT SIC_GetIconIndex (LPCWSTR sSourceFile, INT dwSourceIndex, DWORD dwFlags )
441 {
442     SIC_ENTRY sice;
443     INT ret, index = INVALID_INDEX;
444     WCHAR path[MAX_PATH];
445 
446     TRACE("%s %i\n", debugstr_w(sSourceFile), dwSourceIndex);
447 
448     GetFullPathNameW(sSourceFile, MAX_PATH, path, NULL);
449     sice.sSourceFile = path;
450     sice.dwSourceIndex = dwSourceIndex;
451     sice.dwFlags = dwFlags;
452 
453     if (!sic_hdpa)
454         SIC_Initialize();
455 
456     EnterCriticalSection(&SHELL32_SicCS);
457 
458     if (NULL != DPA_GetPtr (sic_hdpa, 0))
459     {
460       /* search linear from position 0*/
461       index = DPA_Search (sic_hdpa, &sice, 0, SIC_CompareEntries, 0, DPAS_SORTED);
462     }
463 
464     if ( INVALID_INDEX == index )
465     {
466           ret = SIC_LoadIcon (sSourceFile, dwSourceIndex, dwFlags);
467     }
468     else
469     {
470       TRACE("-- found\n");
471       ret = ((LPSIC_ENTRY)DPA_GetPtr(sic_hdpa, index))->dwListIndex;
472     }
473 
474     LeaveCriticalSection(&SHELL32_SicCS);
475     return ret;
476 }
477 
478 /*****************************************************************************
479  * SIC_Initialize            [internal]
480  */
481 BOOL SIC_Initialize(void)
482 {
483     HICON hSm = NULL, hLg = NULL;
484     INT cx_small, cy_small;
485     INT cx_large, cy_large;
486     HDC hDC;
487     INT bpp;
488     DWORD ilMask;
489     BOOL result = FALSE;
490 
491     TRACE("Entered SIC_Initialize\n");
492 
493     if (sic_hdpa)
494     {
495         TRACE("Icon cache already initialized\n");
496         return TRUE;
497     }
498 
499     sic_hdpa = DPA_Create(16);
500     if (!sic_hdpa)
501     {
502         return FALSE;
503     }
504 
505     hDC = CreateICW(L"DISPLAY", NULL, NULL, NULL);
506     if (!hDC)
507     {
508         ERR("Failed to create information context (error %d)\n", GetLastError());
509         goto end;
510     }
511 
512     bpp = GetDeviceCaps(hDC, BITSPIXEL);
513     DeleteDC(hDC);
514 
515     if (bpp <= 4)
516         ilMask = ILC_COLOR4;
517     else if (bpp <= 8)
518         ilMask = ILC_COLOR8;
519     else if (bpp <= 16)
520         ilMask = ILC_COLOR16;
521     else if (bpp <= 24)
522         ilMask = ILC_COLOR24;
523     else if (bpp <= 32)
524         ilMask = ILC_COLOR32;
525     else
526         ilMask = ILC_COLOR;
527 
528     ilMask |= ILC_MASK;
529 
530     cx_small = GetSystemMetrics(SM_CXSMICON);
531     cy_small = GetSystemMetrics(SM_CYSMICON);
532     cx_large = GetSystemMetrics(SM_CXICON);
533     cy_large = GetSystemMetrics(SM_CYICON);
534 
535     ShellSmallIconList = ImageList_Create(cx_small,
536                                           cy_small,
537                                           ilMask,
538                                           100,
539                                           100);
540     if (!ShellSmallIconList)
541     {
542         ERR("Failed to create the small icon list.\n");
543         goto end;
544     }
545 
546     ShellBigIconList = ImageList_Create(cx_large,
547                                         cy_large,
548                                         ilMask,
549                                         100,
550                                         100);
551     if (!ShellBigIconList)
552     {
553         ERR("Failed to create the big icon list.\n");
554         goto end;
555     }
556 
557     /* Load the document icon, which is used as the default if an icon isn't found. */
558     hSm = (HICON)LoadImageW(shell32_hInstance,
559                             MAKEINTRESOURCEW(IDI_SHELL_DOCUMENT),
560                             IMAGE_ICON,
561                             cx_small,
562                             cy_small,
563                             LR_SHARED | LR_DEFAULTCOLOR);
564     if (!hSm)
565     {
566         ERR("Failed to load small IDI_SHELL_DOCUMENT icon!\n");
567         goto end;
568     }
569 
570     hLg = (HICON)LoadImageW(shell32_hInstance,
571                             MAKEINTRESOURCEW(IDI_SHELL_DOCUMENT),
572                             IMAGE_ICON,
573                             cx_large,
574                             cy_large,
575                             LR_SHARED | LR_DEFAULTCOLOR);
576     if (!hLg)
577     {
578         ERR("Failed to load large IDI_SHELL_DOCUMENT icon!\n");
579         goto end;
580     }
581 
582     if(SIC_IconAppend(swShell32Name, IDI_SHELL_DOCUMENT-1, hSm, hLg, 0) == INVALID_INDEX)
583     {
584         ERR("Failed to add IDI_SHELL_DOCUMENT icon to cache.\n");
585         goto end;
586     }
587     if(SIC_IconAppend(swShell32Name, -IDI_SHELL_DOCUMENT, hSm, hLg, 0) == INVALID_INDEX)
588     {
589         ERR("Failed to add IDI_SHELL_DOCUMENT icon to cache.\n");
590         goto end;
591     }
592 
593     /* Everything went fine */
594     result = TRUE;
595 
596 end:
597     /* The image list keeps a copy of the icons, we must destroy them */
598     if(hSm) DestroyIcon(hSm);
599     if(hLg) DestroyIcon(hLg);
600 
601     /* Clean everything if something went wrong */
602     if(!result)
603     {
604         if(sic_hdpa) DPA_Destroy(sic_hdpa);
605         if(ShellSmallIconList) ImageList_Destroy(ShellSmallIconList);
606         if(ShellBigIconList) ImageList_Destroy(ShellSmallIconList);
607         sic_hdpa = NULL;
608         ShellSmallIconList = NULL;
609         ShellBigIconList = NULL;
610     }
611 
612     TRACE("hIconSmall=%p hIconBig=%p\n",ShellSmallIconList, ShellBigIconList);
613 
614     return result;
615 }
616 
617 /*************************************************************************
618  * SIC_Destroy
619  *
620  * frees the cache
621  */
622 static INT CALLBACK sic_free( LPVOID ptr, LPVOID lparam )
623 {
624     HeapFree(GetProcessHeap(), 0, ((LPSIC_ENTRY)ptr)->sSourceFile);
625     SHFree(ptr);
626     return TRUE;
627 }
628 
629 void SIC_Destroy(void)
630 {
631     TRACE("\n");
632 
633     EnterCriticalSection(&SHELL32_SicCS);
634 
635     if (sic_hdpa) DPA_DestroyCallback(sic_hdpa, sic_free, NULL );
636 
637     sic_hdpa = NULL;
638     ImageList_Destroy(ShellSmallIconList);
639     ShellSmallIconList = 0;
640     ImageList_Destroy(ShellBigIconList);
641     ShellBigIconList = 0;
642 
643     LeaveCriticalSection(&SHELL32_SicCS);
644     //DeleteCriticalSection(&SHELL32_SicCS); //static
645 }
646 
647 /*****************************************************************************
648  * SIC_LoadOverlayIcon            [internal]
649  *
650  * Load a shell overlay icon and return its icon cache index.
651  */
652 static int SIC_LoadOverlayIcon(int icon_idx)
653 {
654     WCHAR buffer[1024];
655     LPWSTR iconPath;
656     int iconIdx;
657 
658     iconPath = swShell32Name;    /* default: load icon from shell32.dll */
659     iconIdx = icon_idx;
660 
661     if (HLM_GetIconW(icon_idx, buffer, _countof(buffer), &iconIdx))
662     {
663         iconPath = buffer;
664     }
665     else
666     {
667         WARN("Failed to load icon with index %d, using default one\n", icon_idx);
668     }
669 
670     if (!sic_hdpa)
671         SIC_Initialize();
672 
673     return SIC_LoadIcon(iconPath, iconIdx, 0);
674 }
675 
676 /*************************************************************************
677  * Shell_GetImageLists            [SHELL32.71]
678  *
679  * PARAMETERS
680  *  imglist[1|2] [OUT] pointer which receives imagelist handles
681  *
682  */
683 BOOL WINAPI Shell_GetImageLists(HIMAGELIST * lpBigList, HIMAGELIST * lpSmallList)
684 {
685     TRACE("(%p,%p)\n",lpBigList,lpSmallList);
686 
687     if (!sic_hdpa)
688         SIC_Initialize();
689 
690     if (lpBigList)
691         *lpBigList = ShellBigIconList;
692 
693     if (lpSmallList)
694         *lpSmallList = ShellSmallIconList;
695 
696     return TRUE;
697 }
698 /*************************************************************************
699  * PidlToSicIndex            [INTERNAL]
700  *
701  * PARAMETERS
702  *    sh    [IN]    IShellFolder
703  *    pidl    [IN]
704  *    bBigIcon [IN]
705  *    uFlags    [IN]    GIL_*
706  *    pIndex    [OUT]    index within the SIC
707  *
708  */
709 BOOL PidlToSicIndex (
710     IShellFolder * sh,
711     LPCITEMIDLIST pidl,
712     BOOL bBigIcon,
713     UINT uFlags,
714     int * pIndex)
715 {
716     CComPtr<IExtractIconW>        ei;
717     WCHAR        szIconFile[MAX_PATH];    /* file containing the icon */
718     INT        iSourceIndex;        /* index or resID(negated) in this file */
719     BOOL        ret = FALSE;
720     UINT        dwFlags = 0;
721     int        iShortcutDefaultIndex = INVALID_INDEX;
722 
723     TRACE("sf=%p pidl=%p %s\n", sh, pidl, bBigIcon?"Big":"Small");
724 
725     if (!sic_hdpa)
726         SIC_Initialize();
727 
728     if (SUCCEEDED (sh->GetUIObjectOf(0, 1, &pidl, IID_NULL_PPV_ARG(IExtractIconW, &ei))))
729     {
730       if (SUCCEEDED(ei->GetIconLocation(uFlags &~ GIL_FORSHORTCUT, szIconFile, MAX_PATH, &iSourceIndex, &dwFlags)))
731       {
732         *pIndex = SIC_GetIconIndex(szIconFile, iSourceIndex, uFlags);
733         ret = TRUE;
734       }
735     }
736 
737     if (INVALID_INDEX == *pIndex)    /* default icon when failed */
738     {
739       if (0 == (uFlags & GIL_FORSHORTCUT))
740       {
741         *pIndex = 0;
742       }
743       else
744       {
745         if (INVALID_INDEX == iShortcutDefaultIndex)
746         {
747           iShortcutDefaultIndex = SIC_LoadIcon(swShell32Name, 0, GIL_FORSHORTCUT);
748         }
749         *pIndex = (INVALID_INDEX != iShortcutDefaultIndex ? iShortcutDefaultIndex : 0);
750       }
751     }
752 
753     return ret;
754 
755 }
756 
757 /*************************************************************************
758  * SHMapPIDLToSystemImageListIndex    [SHELL32.77]
759  *
760  * PARAMETERS
761  *    sh    [IN]        pointer to an instance of IShellFolder
762  *    pidl    [IN]
763  *    pIndex    [OUT][OPTIONAL]    SIC index for big icon
764  *
765  */
766 int WINAPI SHMapPIDLToSystemImageListIndex(
767     IShellFolder *sh,
768     LPCITEMIDLIST pidl,
769     int *pIndex)
770 {
771     int Index;
772     UINT uGilFlags = 0;
773 
774     TRACE("(SF=%p,pidl=%p,%p)\n",sh,pidl,pIndex);
775     pdump(pidl);
776 
777     if (SHELL_IsShortcut(pidl))
778         uGilFlags |= GIL_FORSHORTCUT;
779 
780     if (pIndex)
781         if (!PidlToSicIndex ( sh, pidl, 1, uGilFlags, pIndex))
782             *pIndex = -1;
783 
784     if (!PidlToSicIndex ( sh, pidl, 0, uGilFlags, &Index))
785         return -1;
786 
787     return Index;
788 }
789 
790 /*************************************************************************
791  * SHMapIDListToImageListIndexAsync  [SHELL32.148]
792  */
793 EXTERN_C HRESULT WINAPI SHMapIDListToImageListIndexAsync(IShellTaskScheduler *pts, IShellFolder *psf,
794                                                 LPCITEMIDLIST pidl, UINT flags,
795                                                 PFNASYNCICONTASKBALLBACK pfn, void *pvData, void *pvHint,
796                                                 int *piIndex, int *piIndexSel)
797 {
798     FIXME("(%p, %p, %p, 0x%08x, %p, %p, %p, %p, %p)\n",
799             pts, psf, pidl, flags, pfn, pvData, pvHint, piIndex, piIndexSel);
800     return E_FAIL;
801 }
802 
803 /*************************************************************************
804  * Shell_GetCachedImageIndex        [SHELL32.72]
805  *
806  */
807 INT WINAPI Shell_GetCachedImageIndexA(LPCSTR szPath, INT nIndex, UINT bSimulateDoc)
808 {
809     INT ret, len;
810     LPWSTR szTemp;
811 
812     WARN("(%s,%08x,%08x) semi-stub.\n",debugstr_a(szPath), nIndex, bSimulateDoc);
813 
814     len = MultiByteToWideChar( CP_ACP, 0, szPath, -1, NULL, 0 );
815     szTemp = (LPWSTR)HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
816     MultiByteToWideChar( CP_ACP, 0, szPath, -1, szTemp, len );
817 
818     ret = SIC_GetIconIndex( szTemp, nIndex, 0 );
819 
820     HeapFree( GetProcessHeap(), 0, szTemp );
821 
822     return ret;
823 }
824 
825 INT WINAPI Shell_GetCachedImageIndexW(LPCWSTR szPath, INT nIndex, UINT bSimulateDoc)
826 {
827     WARN("(%s,%08x,%08x) semi-stub.\n",debugstr_w(szPath), nIndex, bSimulateDoc);
828 
829     return SIC_GetIconIndex(szPath, nIndex, 0);
830 }
831 
832 EXTERN_C INT WINAPI Shell_GetCachedImageIndexAW(LPCVOID szPath, INT nIndex, BOOL bSimulateDoc)
833 {    if( SHELL_OsIsUnicode())
834       return Shell_GetCachedImageIndexW((LPCWSTR)szPath, nIndex, bSimulateDoc);
835     return Shell_GetCachedImageIndexA((LPCSTR)szPath, nIndex, bSimulateDoc);
836 }
837 
838 EXTERN_C INT WINAPI Shell_GetCachedImageIndex(LPCWSTR szPath, INT nIndex, UINT bSimulateDoc)
839 {
840     return Shell_GetCachedImageIndexAW(szPath, nIndex, bSimulateDoc);
841 }
842 
843 /*************************************************************************
844  * ExtractIconExW            [SHELL32.@]
845  * RETURNS
846  *  0 no icon found (or the file is not valid)
847  *  or number of icons extracted
848  */
849 UINT WINAPI ExtractIconExW(LPCWSTR lpszFile, INT nIconIndex, HICON * phiconLarge, HICON * phiconSmall, UINT nIcons)
850 {
851     UINT ret = 0;
852 
853     /* get entry point of undocumented function PrivateExtractIconExW() in user32 */
854 #if defined(__CYGWIN__) || defined (__MINGW32__) || defined(_MSC_VER)
855     static UINT (WINAPI*PrivateExtractIconExW)(LPCWSTR,int,HICON*,HICON*,UINT) = NULL;
856 
857     if (!PrivateExtractIconExW) {
858         HMODULE hUser32 = GetModuleHandleA("user32");
859         PrivateExtractIconExW = (UINT(WINAPI*)(LPCWSTR,int,HICON*,HICON*,UINT)) GetProcAddress(hUser32, "PrivateExtractIconExW");
860 
861         if (!PrivateExtractIconExW)
862         return ret;
863     }
864 #endif
865 
866     TRACE("%s %i %p %p %i\n", debugstr_w(lpszFile), nIconIndex, phiconLarge, phiconSmall, nIcons);
867     ret = PrivateExtractIconExW(lpszFile, nIconIndex, phiconLarge, phiconSmall, nIcons);
868 
869     /* PrivateExtractIconExW() may return -1 if the provided file is not a valid PE image file or the said
870      * file couldn't be found. The behaviour is correct although ExtractIconExW() only returns the successfully
871      * extracted icons from a file. In such scenario, simply return 0.
872     */
873     if (ret == 0xFFFFFFFF)
874     {
875         WARN("Invalid file or couldn't be found - %s\n", debugstr_w(lpszFile));
876         ret = 0;
877     }
878 
879     return ret;
880 }
881 
882 /*************************************************************************
883  * ExtractIconExA            [SHELL32.@]
884  */
885 UINT WINAPI ExtractIconExA(LPCSTR lpszFile, INT nIconIndex, HICON * phiconLarge, HICON * phiconSmall, UINT nIcons)
886 {
887     UINT ret = 0;
888     INT len = MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, NULL, 0);
889     LPWSTR lpwstrFile = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
890 
891     TRACE("%s %i %p %p %i\n", lpszFile, nIconIndex, phiconLarge, phiconSmall, nIcons);
892 
893     if (lpwstrFile)
894     {
895         MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, lpwstrFile, len);
896         ret = ExtractIconExW(lpwstrFile, nIconIndex, phiconLarge, phiconSmall, nIcons);
897         HeapFree(GetProcessHeap(), 0, lpwstrFile);
898     }
899     return ret;
900 }
901 
902 /*************************************************************************
903  *                ExtractAssociatedIconA (SHELL32.@)
904  *
905  * Return icon for given file (either from file itself or from associated
906  * executable) and patch parameters if needed.
907  */
908 HICON WINAPI ExtractAssociatedIconA(HINSTANCE hInst, LPSTR lpIconPath, LPWORD lpiIcon)
909 {
910     HICON hIcon = NULL;
911     INT len = MultiByteToWideChar(CP_ACP, 0, lpIconPath, -1, NULL, 0);
912     /* Note that we need to allocate MAX_PATH, since we are supposed to fill
913      * the correct executable if there is no icon in lpIconPath directly.
914      * lpIconPath itself is supposed to be large enough, so make sure lpIconPathW
915      * is large enough too. Yes, I am puking too.
916      */
917     LPWSTR lpIconPathW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
918 
919     TRACE("%p %s %p\n", hInst, debugstr_a(lpIconPath), lpiIcon);
920 
921     if (lpIconPathW)
922     {
923         MultiByteToWideChar(CP_ACP, 0, lpIconPath, -1, lpIconPathW, len);
924         hIcon = ExtractAssociatedIconW(hInst, lpIconPathW, lpiIcon);
925         WideCharToMultiByte(CP_ACP, 0, lpIconPathW, -1, lpIconPath, MAX_PATH , NULL, NULL);
926         HeapFree(GetProcessHeap(), 0, lpIconPathW);
927     }
928     return hIcon;
929 }
930 
931 /*************************************************************************
932  *                ExtractAssociatedIconW (SHELL32.@)
933  *
934  * Return icon for given file (either from file itself or from associated
935  * executable) and patch parameters if needed.
936  */
937 HICON WINAPI ExtractAssociatedIconW(HINSTANCE hInst, LPWSTR lpIconPath, LPWORD lpiIcon)
938 {
939     HICON hIcon = NULL;
940     WORD wDummyIcon = 0;
941 
942     TRACE("%p %s %p\n", hInst, debugstr_w(lpIconPath), lpiIcon);
943 
944     if(lpiIcon == NULL)
945         lpiIcon = &wDummyIcon;
946 
947     hIcon = ExtractIconW(hInst, lpIconPath, *lpiIcon);
948 
949     if( hIcon < (HICON)2 )
950     { if( hIcon == (HICON)1 ) /* no icons found in given file */
951       { WCHAR tempPath[MAX_PATH];
952         HINSTANCE uRet = FindExecutableW(lpIconPath,NULL,tempPath);
953 
954         if( uRet > (HINSTANCE)32 && tempPath[0] )
955         { wcscpy(lpIconPath,tempPath);
956           hIcon = ExtractIconW(hInst, lpIconPath, *lpiIcon);
957           if( hIcon > (HICON)2 )
958             return hIcon;
959         }
960       }
961 
962       if( hIcon == (HICON)1 )
963         *lpiIcon = 2;   /* MSDOS icon - we found .exe but no icons in it */
964       else
965         *lpiIcon = 6;   /* generic icon - found nothing */
966 
967       if (GetModuleFileNameW(hInst, lpIconPath, MAX_PATH))
968         hIcon = LoadIconW(hInst, MAKEINTRESOURCEW(*lpiIcon));
969     }
970     return hIcon;
971 }
972 
973 /*************************************************************************
974  *                ExtractAssociatedIconExW (SHELL32.@)
975  *
976  * Return icon for given file (either from file itself or from associated
977  * executable) and patch parameters if needed.
978  */
979 EXTERN_C HICON WINAPI ExtractAssociatedIconExW(HINSTANCE hInst, LPWSTR lpIconPath, LPWORD lpiIconIdx, LPWORD lpiIconId)
980 {
981   FIXME("%p %s %p %p): stub\n", hInst, debugstr_w(lpIconPath), lpiIconIdx, lpiIconId);
982   return 0;
983 }
984 
985 /*************************************************************************
986  *                ExtractAssociatedIconExA (SHELL32.@)
987  *
988  * Return icon for given file (either from file itself or from associated
989  * executable) and patch parameters if needed.
990  */
991 EXTERN_C HICON WINAPI ExtractAssociatedIconExA(HINSTANCE hInst, LPSTR lpIconPath, LPWORD lpiIconIdx, LPWORD lpiIconId)
992 {
993   HICON ret;
994   INT len = MultiByteToWideChar( CP_ACP, 0, lpIconPath, -1, NULL, 0 );
995   LPWSTR lpwstrFile = (LPWSTR)HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
996 
997   TRACE("%p %s %p %p)\n", hInst, lpIconPath, lpiIconIdx, lpiIconId);
998 
999   MultiByteToWideChar( CP_ACP, 0, lpIconPath, -1, lpwstrFile, len );
1000   ret = ExtractAssociatedIconExW(hInst, lpwstrFile, lpiIconIdx, lpiIconId);
1001   HeapFree(GetProcessHeap(), 0, lpwstrFile);
1002   return ret;
1003 }
1004 
1005 
1006 /****************************************************************************
1007  * SHDefExtractIconW        [SHELL32.@]
1008  */
1009 HRESULT WINAPI SHDefExtractIconW(LPCWSTR pszIconFile, int iIndex, UINT uFlags,
1010                                  HICON* phiconLarge, HICON* phiconSmall, UINT nIconSize)
1011 {
1012     UINT ret;
1013     HICON hIcons[2];
1014     WARN("%s %d 0x%08x %p %p %d, semi-stub\n", debugstr_w(pszIconFile), iIndex, uFlags, phiconLarge, phiconSmall, nIconSize);
1015 
1016     ret = PrivateExtractIconsW(pszIconFile, iIndex, nIconSize, nIconSize, hIcons, NULL, 2, LR_DEFAULTCOLOR);
1017     /* FIXME: deal with uFlags parameter which contains GIL_ flags */
1018     if (ret == 0xFFFFFFFF)
1019       return E_FAIL;
1020     if (ret > 0) {
1021       if (phiconLarge)
1022         *phiconLarge = hIcons[0];
1023       else
1024         DestroyIcon(hIcons[0]);
1025       if (phiconSmall)
1026         *phiconSmall = hIcons[1];
1027       else
1028         DestroyIcon(hIcons[1]);
1029       return S_OK;
1030     }
1031     return S_FALSE;
1032 }
1033 
1034 /****************************************************************************
1035  * SHDefExtractIconA        [SHELL32.@]
1036  */
1037 HRESULT WINAPI SHDefExtractIconA(LPCSTR pszIconFile, int iIndex, UINT uFlags,
1038                                  HICON* phiconLarge, HICON* phiconSmall, UINT nIconSize)
1039 {
1040   HRESULT ret;
1041   INT len = MultiByteToWideChar(CP_ACP, 0, pszIconFile, -1, NULL, 0);
1042   LPWSTR lpwstrFile = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1043 
1044   TRACE("%s %d 0x%08x %p %p %d\n", pszIconFile, iIndex, uFlags, phiconLarge, phiconSmall, nIconSize);
1045 
1046   MultiByteToWideChar(CP_ACP, 0, pszIconFile, -1, lpwstrFile, len);
1047   ret = SHDefExtractIconW(lpwstrFile, iIndex, uFlags, phiconLarge, phiconSmall, nIconSize);
1048   HeapFree(GetProcessHeap(), 0, lpwstrFile);
1049   return ret;
1050 }
1051 
1052 /****************************************************************************
1053  * SHGetIconOverlayIndexA    [SHELL32.@]
1054  *
1055  * Returns the index of the overlay icon in the system image list.
1056  */
1057 EXTERN_C INT WINAPI SHGetIconOverlayIndexA(LPCSTR pszIconPath, INT iIconIndex)
1058 {
1059   FIXME("%s, %d\n", debugstr_a(pszIconPath), iIconIndex);
1060 
1061   return -1;
1062 }
1063 
1064 /****************************************************************************
1065  * SHGetIconOverlayIndexW    [SHELL32.@]
1066  *
1067  * Returns the index of the overlay icon in the system image list.
1068  */
1069 EXTERN_C INT WINAPI SHGetIconOverlayIndexW(LPCWSTR pszIconPath, INT iIconIndex)
1070 {
1071   FIXME("%s, %d\n", debugstr_w(pszIconPath), iIconIndex);
1072 
1073   return -1;
1074 }
1075