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