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 */
SIC_CompareEntries(LPVOID p1,LPVOID p2,LPARAM lparam)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 */
SIC_OverlayShortcutImage(HICON SourceIcon,BOOL large)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 */
SIC_IconAppend(LPCWSTR sSourceFile,INT dwSourceIndex,HICON hSmallIcon,HICON hBigIcon,DWORD dwFlags)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 */
SIC_LoadIcon(LPCWSTR sSourceFile,INT dwSourceIndex,DWORD dwFlags)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 */
SIC_GetIconIndex(LPCWSTR sSourceFile,INT dwSourceIndex,DWORD dwFlags)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 */
SIC_Initialize(void)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 */
sic_free(LPVOID ptr,LPVOID lparam)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
SIC_Destroy(void)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 */
SIC_LoadOverlayIcon(int icon_idx)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 */
Shell_GetImageLists(HIMAGELIST * lpBigList,HIMAGELIST * lpSmallList)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 */
PidlToSicIndex(IShellFolder * sh,LPCITEMIDLIST pidl,BOOL bBigIcon,UINT uFlags,int * pIndex)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 */
SHMapPIDLToSystemImageListIndex(IShellFolder * sh,LPCITEMIDLIST pidl,int * pIndex)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 */
SHMapIDListToImageListIndexAsync(IShellTaskScheduler * pts,IShellFolder * psf,LPCITEMIDLIST pidl,UINT flags,PFNASYNCICONTASKBALLBACK pfn,void * pvData,void * pvHint,int * piIndex,int * piIndexSel)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 */
Shell_GetCachedImageIndexA(LPCSTR szPath,INT nIndex,UINT bSimulateDoc)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
Shell_GetCachedImageIndexW(LPCWSTR szPath,INT nIndex,UINT bSimulateDoc)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
Shell_GetCachedImageIndexAW(LPCVOID szPath,INT nIndex,BOOL bSimulateDoc)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
Shell_GetCachedImageIndex(LPCWSTR szPath,INT nIndex,UINT bSimulateDoc)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 */
ExtractIconExW(LPCWSTR lpszFile,INT nIconIndex,HICON * phiconLarge,HICON * phiconSmall,UINT nIcons)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 */
ExtractIconExA(LPCSTR lpszFile,INT nIconIndex,HICON * phiconLarge,HICON * phiconSmall,UINT nIcons)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 */
ExtractAssociatedIconA(HINSTANCE hInst,LPSTR lpIconPath,LPWORD lpiIcon)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 */
ExtractAssociatedIconW(HINSTANCE hInst,LPWSTR lpIconPath,LPWORD lpiIcon)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 */
ExtractAssociatedIconExW(HINSTANCE hInst,LPWSTR lpIconPath,LPWORD lpiIconIdx,LPWORD lpiIconId)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 */
ExtractAssociatedIconExA(HINSTANCE hInst,LPSTR lpIconPath,LPWORD lpiIconIdx,LPWORD lpiIconId)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 */
SHDefExtractIconW(LPCWSTR pszIconFile,int iIndex,UINT uFlags,HICON * phiconLarge,HICON * phiconSmall,UINT nIconSize)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 */
SHDefExtractIconA(LPCSTR pszIconFile,int iIndex,UINT uFlags,HICON * phiconLarge,HICON * phiconSmall,UINT nIconSize)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 */
SHGetIconOverlayIndexA(LPCSTR pszIconPath,INT iIconIndex)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 */
SHGetIconOverlayIndexW(LPCWSTR pszIconPath,INT iIconIndex)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