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