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