[THEMES]
[reactos.git] / reactos / 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 if (e1->dwSourceIndex != e2->dwSourceIndex || /* first the faster one */
68 (e1->dwFlags & GIL_FORSHORTCUT) != (e2->dwFlags & GIL_FORSHORTCUT))
69 return 1;
70
71 if (wcsicmp(e1->sSourceFile,e2->sSourceFile))
72 return 1;
73
74 return 0;
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.hbmColor);
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_InsertPtr(sic_hdpa, 0x7fff, lpsice);
342 if ( -1 == indexDPA )
343 {
344 ret = INVALID_INDEX;
345 goto leave;
346 }
347
348 index = ImageList_AddIcon (ShellSmallIconList, hSmallIcon);
349 index1= ImageList_AddIcon (ShellBigIconList, hBigIcon);
350
351 /* Something went wrong when allocating a new image in the list. Abort. */
352 if((index == -1) || (index1 == -1))
353 {
354 WARN("Something went wrong when adding the icon to the list: small - 0x%x, big - 0x%x.\n",
355 index, index1);
356 if(index != -1) ImageList_Remove(ShellSmallIconList, index);
357 if(index1 != -1) ImageList_Remove(ShellBigIconList, index1);
358 ret = INVALID_INDEX;
359 goto leave;
360 }
361
362 if (index!=index1)
363 {
364 FIXME("iconlists out of sync 0x%x 0x%x\n", index, index1);
365 /* What to do ???? */
366 }
367 lpsice->dwListIndex = index;
368 ret = lpsice->dwListIndex;
369
370 leave:
371 if(ret == INVALID_INDEX)
372 {
373 if(indexDPA != -1) DPA_DeletePtr(sic_hdpa, indexDPA);
374 HeapFree(GetProcessHeap(), 0, lpsice->sSourceFile);
375 SHFree(lpsice);
376 }
377 LeaveCriticalSection(&SHELL32_SicCS);
378 return ret;
379 }
380 /****************************************************************************
381 * SIC_LoadIcon [internal]
382 *
383 * NOTES
384 * gets small/big icon by number from a file
385 */
386 static INT SIC_LoadIcon (LPCWSTR sSourceFile, INT dwSourceIndex, DWORD dwFlags)
387 {
388 HICON hiconLarge=0;
389 HICON hiconSmall=0;
390 UINT ret;
391 static UINT (WINAPI*PrivateExtractIconExW)(LPCWSTR,int,HICON*,HICON*,UINT) = NULL;
392
393 if (!PrivateExtractIconExW)
394 {
395 HMODULE hUser32 = GetModuleHandleA("user32");
396 PrivateExtractIconExW = (UINT(WINAPI*)(LPCWSTR,int,HICON*,HICON*,UINT)) GetProcAddress(hUser32, "PrivateExtractIconExW");
397 }
398
399 if (PrivateExtractIconExW)
400 {
401 PrivateExtractIconExW(sSourceFile, dwSourceIndex, &hiconLarge, &hiconSmall, 1);
402 }
403 else
404 {
405 PrivateExtractIconsW(sSourceFile, dwSourceIndex, 32, 32, &hiconLarge, NULL, 1, 0);
406 PrivateExtractIconsW(sSourceFile, dwSourceIndex, 16, 16, &hiconSmall, NULL, 1, 0);
407 }
408
409 if ( !hiconLarge || !hiconSmall)
410 {
411 WARN("failure loading icon %i from %s (%p %p)\n", dwSourceIndex, debugstr_w(sSourceFile), hiconLarge, hiconSmall);
412 if(hiconLarge) DestroyIcon(hiconLarge);
413 if(hiconSmall) DestroyIcon(hiconSmall);
414 return INVALID_INDEX;
415 }
416
417 if (0 != (dwFlags & GIL_FORSHORTCUT))
418 {
419 HICON hiconLargeShortcut = SIC_OverlayShortcutImage(hiconLarge, TRUE);
420 HICON hiconSmallShortcut = SIC_OverlayShortcutImage(hiconSmall, FALSE);
421 if (NULL != hiconLargeShortcut && NULL != hiconSmallShortcut)
422 {
423 DestroyIcon(hiconLarge);
424 DestroyIcon(hiconSmall);
425 hiconLarge = hiconLargeShortcut;
426 hiconSmall = hiconSmallShortcut;
427 }
428 else
429 {
430 WARN("Failed to create shortcut overlayed icons\n");
431 if (NULL != hiconLargeShortcut) DestroyIcon(hiconLargeShortcut);
432 if (NULL != hiconSmallShortcut) DestroyIcon(hiconSmallShortcut);
433 dwFlags &= ~ GIL_FORSHORTCUT;
434 }
435 }
436
437 ret = SIC_IconAppend (sSourceFile, dwSourceIndex, hiconSmall, hiconLarge, dwFlags);
438 DestroyIcon(hiconLarge);
439 DestroyIcon(hiconSmall);
440 return ret;
441 }
442 /*****************************************************************************
443 * SIC_GetIconIndex [internal]
444 *
445 * Parameters
446 * sSourceFile [IN] filename of file containing the icon
447 * index [IN] index/resID (negated) in this file
448 *
449 * NOTES
450 * look in the cache for a proper icon. if not available the icon is taken
451 * from the file and cached
452 */
453 INT SIC_GetIconIndex (LPCWSTR sSourceFile, INT dwSourceIndex, DWORD dwFlags )
454 {
455 SIC_ENTRY sice;
456 INT ret, index = INVALID_INDEX;
457 WCHAR path[MAX_PATH];
458
459 TRACE("%s %i\n", debugstr_w(sSourceFile), dwSourceIndex);
460
461 GetFullPathNameW(sSourceFile, MAX_PATH, path, NULL);
462 sice.sSourceFile = path;
463 sice.dwSourceIndex = dwSourceIndex;
464 sice.dwFlags = dwFlags;
465
466 EnterCriticalSection(&SHELL32_SicCS);
467
468 if (NULL != DPA_GetPtr (sic_hdpa, 0))
469 {
470 /* search linear from position 0*/
471 index = DPA_Search (sic_hdpa, &sice, 0, SIC_CompareEntries, 0, 0);
472 }
473
474 if ( INVALID_INDEX == index )
475 {
476 ret = SIC_LoadIcon (sSourceFile, dwSourceIndex, dwFlags);
477 }
478 else
479 {
480 TRACE("-- found\n");
481 ret = ((LPSIC_ENTRY)DPA_GetPtr(sic_hdpa, index))->dwListIndex;
482 }
483
484 LeaveCriticalSection(&SHELL32_SicCS);
485 return ret;
486 }
487
488 /*****************************************************************************
489 * SIC_Initialize [internal]
490 */
491 BOOL SIC_Initialize(void)
492 {
493 HICON hSm = NULL, hLg = NULL;
494 INT cx_small, cy_small;
495 INT cx_large, cy_large;
496 HDC hDC;
497 INT bpp;
498 DWORD ilMask;
499 BOOL result = FALSE;
500
501 TRACE("Entered SIC_Initialize\n");
502
503 if (sic_hdpa)
504 {
505 TRACE("Icon cache already initialized\n");
506 return TRUE;
507 }
508
509 sic_hdpa = DPA_Create(16);
510 if (!sic_hdpa)
511 {
512 return FALSE;
513 }
514
515 hDC = CreateICW(L"DISPLAY", NULL, NULL, NULL);
516 if (!hDC)
517 {
518 ERR("Failed to create information context (error %d)\n", GetLastError());
519 goto end;
520 }
521
522 bpp = GetDeviceCaps(hDC, BITSPIXEL);
523 DeleteDC(hDC);
524
525 if (bpp <= 4)
526 ilMask = ILC_COLOR4;
527 else if (bpp <= 8)
528 ilMask = ILC_COLOR8;
529 else if (bpp <= 16)
530 ilMask = ILC_COLOR16;
531 else if (bpp <= 24)
532 ilMask = ILC_COLOR24;
533 else if (bpp <= 32)
534 ilMask = ILC_COLOR32;
535 else
536 ilMask = ILC_COLOR;
537
538 ilMask |= ILC_MASK;
539
540 cx_small = GetSystemMetrics(SM_CXSMICON);
541 cy_small = GetSystemMetrics(SM_CYSMICON);
542 cx_large = GetSystemMetrics(SM_CXICON);
543 cy_large = GetSystemMetrics(SM_CYICON);
544
545 ShellSmallIconList = ImageList_Create(cx_small,
546 cy_small,
547 ilMask,
548 100,
549 100);
550 if (!ShellSmallIconList)
551 {
552 ERR("Failed to create the small icon list.\n");
553 goto end;
554 }
555
556 ShellBigIconList = ImageList_Create(cx_large,
557 cy_large,
558 ilMask,
559 100,
560 100);
561 if (!ShellBigIconList)
562 {
563 ERR("Failed to create the big icon list.\n");
564 goto end;
565 }
566
567 /* Load the document icon, which is used as the default if an icon isn't found. */
568 hSm = (HICON)LoadImageW(shell32_hInstance,
569 MAKEINTRESOURCEW(IDI_SHELL_DOCUMENT),
570 IMAGE_ICON,
571 cx_small,
572 cy_small,
573 LR_SHARED | LR_DEFAULTCOLOR);
574 if (!hSm)
575 {
576 ERR("Failed to load small IDI_SHELL_DOCUMENT icon!\n");
577 goto end;
578 }
579
580 hLg = (HICON)LoadImageW(shell32_hInstance,
581 MAKEINTRESOURCEW(IDI_SHELL_DOCUMENT),
582 IMAGE_ICON,
583 cx_large,
584 cy_large,
585 LR_SHARED | LR_DEFAULTCOLOR);
586 if (!hLg)
587 {
588 ERR("Failed to load large IDI_SHELL_DOCUMENT icon!\n");
589 goto end;
590 }
591
592 if(SIC_IconAppend(swShell32Name, IDI_SHELL_DOCUMENT-1, hSm, hLg, 0) == INVALID_INDEX)
593 {
594 ERR("Failed to add IDI_SHELL_DOCUMENT icon to cache.\n");
595 goto end;
596 }
597 if(SIC_IconAppend(swShell32Name, -IDI_SHELL_DOCUMENT, hSm, hLg, 0) == INVALID_INDEX)
598 {
599 ERR("Failed to add IDI_SHELL_DOCUMENT icon to cache.\n");
600 goto end;
601 }
602
603 /* Everything went fine */
604 result = TRUE;
605
606 end:
607 /* The image list keeps a copy of the icons, we must destroy them */
608 if(hSm) DestroyIcon(hSm);
609 if(hLg) DestroyIcon(hLg);
610
611 /* Clean everything if something went wrong */
612 if(!result)
613 {
614 if(sic_hdpa) DPA_Destroy(sic_hdpa);
615 if(ShellSmallIconList) ImageList_Destroy(ShellSmallIconList);
616 if(ShellBigIconList) ImageList_Destroy(ShellSmallIconList);
617 sic_hdpa = NULL;
618 ShellSmallIconList = NULL;
619 ShellBigIconList = NULL;
620 }
621
622 TRACE("hIconSmall=%p hIconBig=%p\n",ShellSmallIconList, ShellBigIconList);
623
624 return result;
625 }
626
627 /*************************************************************************
628 * SIC_Destroy
629 *
630 * frees the cache
631 */
632 static INT CALLBACK sic_free( LPVOID ptr, LPVOID lparam )
633 {
634 HeapFree(GetProcessHeap(), 0, ((LPSIC_ENTRY)ptr)->sSourceFile);
635 SHFree(ptr);
636 return TRUE;
637 }
638
639 void SIC_Destroy(void)
640 {
641 TRACE("\n");
642
643 EnterCriticalSection(&SHELL32_SicCS);
644
645 if (sic_hdpa) DPA_DestroyCallback(sic_hdpa, sic_free, NULL );
646
647 sic_hdpa = NULL;
648 ImageList_Destroy(ShellSmallIconList);
649 ShellSmallIconList = 0;
650 ImageList_Destroy(ShellBigIconList);
651 ShellBigIconList = 0;
652
653 LeaveCriticalSection(&SHELL32_SicCS);
654 //DeleteCriticalSection(&SHELL32_SicCS); //static
655 }
656
657 /*****************************************************************************
658 * SIC_LoadOverlayIcon [internal]
659 *
660 * Load a shell overlay icon and return its icon cache index.
661 */
662 static int SIC_LoadOverlayIcon(int icon_idx)
663 {
664 WCHAR buffer[1024], wszIdx[8];
665 HKEY hKeyShellIcons;
666 LPCWSTR iconPath;
667 int iconIdx;
668
669 static const WCHAR wszShellIcons[] = {
670 'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
671 'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
672 'E','x','p','l','o','r','e','r','\\','S','h','e','l','l',' ','I','c','o','n','s',0
673 };
674 static const WCHAR wszNumFmt[] = {'%','d',0};
675
676 iconPath = swShell32Name; /* default: load icon from shell32.dll */
677 iconIdx = icon_idx;
678
679 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszShellIcons, 0, KEY_READ, &hKeyShellIcons) == ERROR_SUCCESS)
680 {
681 DWORD count = sizeof(buffer);
682
683 swprintf(wszIdx, wszNumFmt, icon_idx);
684
685 /* read icon path and index */
686 if (RegQueryValueExW(hKeyShellIcons, wszIdx, NULL, NULL, (LPBYTE)buffer, &count) == ERROR_SUCCESS)
687 {
688 LPWSTR p = wcschr(buffer, ',');
689
690 if (p)
691 *p++ = 0;
692
693 iconPath = buffer;
694 iconIdx = _wtoi(p);
695 }
696
697 RegCloseKey(hKeyShellIcons);
698 }
699
700 return SIC_LoadIcon(iconPath, iconIdx, 0);
701 }
702
703 /*************************************************************************
704 * Shell_GetImageLists [SHELL32.71]
705 *
706 * PARAMETERS
707 * imglist[1|2] [OUT] pointer which receives imagelist handles
708 *
709 */
710 BOOL WINAPI Shell_GetImageLists(HIMAGELIST * lpBigList, HIMAGELIST * lpSmallList)
711 { TRACE("(%p,%p)\n",lpBigList,lpSmallList);
712 if (lpBigList)
713 { *lpBigList = ShellBigIconList;
714 }
715 if (lpSmallList)
716 { *lpSmallList = ShellSmallIconList;
717 }
718
719 return TRUE;
720 }
721 /*************************************************************************
722 * PidlToSicIndex [INTERNAL]
723 *
724 * PARAMETERS
725 * sh [IN] IShellFolder
726 * pidl [IN]
727 * bBigIcon [IN]
728 * uFlags [IN] GIL_*
729 * pIndex [OUT] index within the SIC
730 *
731 */
732 BOOL PidlToSicIndex (
733 IShellFolder * sh,
734 LPCITEMIDLIST pidl,
735 BOOL bBigIcon,
736 UINT uFlags,
737 int * pIndex)
738 {
739 CComPtr<IExtractIconW> ei;
740 WCHAR szIconFile[MAX_PATH]; /* file containing the icon */
741 INT iSourceIndex; /* index or resID(negated) in this file */
742 BOOL ret = FALSE;
743 UINT dwFlags = 0;
744 int iShortcutDefaultIndex = INVALID_INDEX;
745
746 TRACE("sf=%p pidl=%p %s\n", sh, pidl, bBigIcon?"Big":"Small");
747
748 if (SUCCEEDED (sh->GetUIObjectOf(0, 1, &pidl, IID_IExtractIconW, 0, (void **)&ei)))
749 {
750 if (SUCCEEDED(ei->GetIconLocation(uFlags, szIconFile, MAX_PATH, &iSourceIndex, &dwFlags)))
751 {
752 *pIndex = SIC_GetIconIndex(szIconFile, iSourceIndex, uFlags);
753 ret = TRUE;
754 }
755 }
756
757 if (INVALID_INDEX == *pIndex) /* default icon when failed */
758 {
759 if (0 == (uFlags & GIL_FORSHORTCUT))
760 {
761 *pIndex = 0;
762 }
763 else
764 {
765 if (INVALID_INDEX == iShortcutDefaultIndex)
766 {
767 iShortcutDefaultIndex = SIC_LoadIcon(swShell32Name, 0, GIL_FORSHORTCUT);
768 }
769 *pIndex = (INVALID_INDEX != iShortcutDefaultIndex ? iShortcutDefaultIndex : 0);
770 }
771 }
772
773 return ret;
774
775 }
776
777 /*************************************************************************
778 * SHMapPIDLToSystemImageListIndex [SHELL32.77]
779 *
780 * PARAMETERS
781 * sh [IN] pointer to an instance of IShellFolder
782 * pidl [IN]
783 * pIndex [OUT][OPTIONAL] SIC index for big icon
784 *
785 */
786 int WINAPI SHMapPIDLToSystemImageListIndex(
787 IShellFolder *sh,
788 LPCITEMIDLIST pidl,
789 int *pIndex)
790 {
791 int Index;
792 UINT uGilFlags = 0;
793
794 TRACE("(SF=%p,pidl=%p,%p)\n",sh,pidl,pIndex);
795 pdump(pidl);
796
797 if (SHELL_IsShortcut(pidl))
798 uGilFlags |= GIL_FORSHORTCUT;
799
800 if (pIndex)
801 if (!PidlToSicIndex ( sh, pidl, 1, uGilFlags, pIndex))
802 *pIndex = -1;
803
804 if (!PidlToSicIndex ( sh, pidl, 0, uGilFlags, &Index))
805 return -1;
806
807 return Index;
808 }
809
810 /*************************************************************************
811 * SHMapIDListToImageListIndexAsync [SHELL32.148]
812 */
813 EXTERN_C HRESULT WINAPI SHMapIDListToImageListIndexAsync(IShellTaskScheduler *pts, IShellFolder *psf,
814 LPCITEMIDLIST pidl, UINT flags,
815 PFNASYNCICONTASKBALLBACK pfn, void *pvData, void *pvHint,
816 int *piIndex, int *piIndexSel)
817 {
818 FIXME("(%p, %p, %p, 0x%08x, %p, %p, %p, %p, %p)\n",
819 pts, psf, pidl, flags, pfn, pvData, pvHint, piIndex, piIndexSel);
820 return E_FAIL;
821 }
822
823 /*************************************************************************
824 * Shell_GetCachedImageIndex [SHELL32.72]
825 *
826 */
827 INT WINAPI Shell_GetCachedImageIndexA(LPCSTR szPath, INT nIndex, UINT bSimulateDoc)
828 {
829 INT ret, len;
830 LPWSTR szTemp;
831
832 WARN("(%s,%08x,%08x) semi-stub.\n",debugstr_a(szPath), nIndex, bSimulateDoc);
833
834 len = MultiByteToWideChar( CP_ACP, 0, szPath, -1, NULL, 0 );
835 szTemp = (LPWSTR)HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
836 MultiByteToWideChar( CP_ACP, 0, szPath, -1, szTemp, len );
837
838 ret = SIC_GetIconIndex( szTemp, nIndex, 0 );
839
840 HeapFree( GetProcessHeap(), 0, szTemp );
841
842 return ret;
843 }
844
845 INT WINAPI Shell_GetCachedImageIndexW(LPCWSTR szPath, INT nIndex, UINT bSimulateDoc)
846 {
847 WARN("(%s,%08x,%08x) semi-stub.\n",debugstr_w(szPath), nIndex, bSimulateDoc);
848
849 return SIC_GetIconIndex(szPath, nIndex, 0);
850 }
851
852 EXTERN_C INT WINAPI Shell_GetCachedImageIndexAW(LPCVOID szPath, INT nIndex, BOOL bSimulateDoc)
853 { if( SHELL_OsIsUnicode())
854 return Shell_GetCachedImageIndexW((LPCWSTR)szPath, nIndex, bSimulateDoc);
855 return Shell_GetCachedImageIndexA((LPCSTR)szPath, nIndex, bSimulateDoc);
856 }
857
858 /*************************************************************************
859 * ExtractIconExW [SHELL32.@]
860 * RETURNS
861 * 0 no icon found
862 * -1 file is not valid
863 * or number of icons extracted
864 */
865 UINT WINAPI ExtractIconExW(LPCWSTR lpszFile, INT nIconIndex, HICON * phiconLarge, HICON * phiconSmall, UINT nIcons)
866 {
867 /* get entry point of undocumented function PrivateExtractIconExW() in user32 */
868 #if defined(__CYGWIN__) || defined (__MINGW32__) || defined(_MSC_VER)
869 static UINT (WINAPI*PrivateExtractIconExW)(LPCWSTR,int,HICON*,HICON*,UINT) = NULL;
870
871 if (!PrivateExtractIconExW) {
872 HMODULE hUser32 = GetModuleHandleA("user32");
873 PrivateExtractIconExW = (UINT(WINAPI*)(LPCWSTR,int,HICON*,HICON*,UINT)) GetProcAddress(hUser32, "PrivateExtractIconExW");
874
875 if (!PrivateExtractIconExW)
876 return 0;
877 }
878 #endif
879
880 TRACE("%s %i %p %p %i\n", debugstr_w(lpszFile), nIconIndex, phiconLarge, phiconSmall, nIcons);
881
882 return PrivateExtractIconExW(lpszFile, nIconIndex, phiconLarge, phiconSmall, nIcons);
883 }
884
885 /*************************************************************************
886 * ExtractIconExA [SHELL32.@]
887 */
888 UINT WINAPI ExtractIconExA(LPCSTR lpszFile, INT nIconIndex, HICON * phiconLarge, HICON * phiconSmall, UINT nIcons)
889 {
890 UINT ret = 0;
891 INT len = MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, NULL, 0);
892 LPWSTR lpwstrFile = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
893
894 TRACE("%s %i %p %p %i\n", lpszFile, nIconIndex, phiconLarge, phiconSmall, nIcons);
895
896 if (lpwstrFile)
897 {
898 MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, lpwstrFile, len);
899 ret = ExtractIconExW(lpwstrFile, nIconIndex, phiconLarge, phiconSmall, nIcons);
900 HeapFree(GetProcessHeap(), 0, lpwstrFile);
901 }
902 return ret;
903 }
904
905 /*************************************************************************
906 * ExtractAssociatedIconA (SHELL32.@)
907 *
908 * Return icon for given file (either from file itself or from associated
909 * executable) and patch parameters if needed.
910 */
911 HICON WINAPI ExtractAssociatedIconA(HINSTANCE hInst, LPSTR lpIconPath, LPWORD lpiIcon)
912 {
913 HICON hIcon = NULL;
914 INT len = MultiByteToWideChar(CP_ACP, 0, lpIconPath, -1, NULL, 0);
915 /* Note that we need to allocate MAX_PATH, since we are supposed to fill
916 * the correct executable if there is no icon in lpIconPath directly.
917 * lpIconPath itself is supposed to be large enough, so make sure lpIconPathW
918 * is large enough too. Yes, I am puking too.
919 */
920 LPWSTR lpIconPathW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR));
921
922 TRACE("%p %s %p\n", hInst, debugstr_a(lpIconPath), lpiIcon);
923
924 if (lpIconPathW)
925 {
926 MultiByteToWideChar(CP_ACP, 0, lpIconPath, -1, lpIconPathW, len);
927 hIcon = ExtractAssociatedIconW(hInst, lpIconPathW, lpiIcon);
928 WideCharToMultiByte(CP_ACP, 0, lpIconPathW, -1, lpIconPath, MAX_PATH , NULL, NULL);
929 HeapFree(GetProcessHeap(), 0, lpIconPathW);
930 }
931 return hIcon;
932 }
933
934 /*************************************************************************
935 * ExtractAssociatedIconW (SHELL32.@)
936 *
937 * Return icon for given file (either from file itself or from associated
938 * executable) and patch parameters if needed.
939 */
940 HICON WINAPI ExtractAssociatedIconW(HINSTANCE hInst, LPWSTR lpIconPath, LPWORD lpiIcon)
941 {
942 HICON hIcon = NULL;
943 WORD wDummyIcon = 0;
944
945 TRACE("%p %s %p\n", hInst, debugstr_w(lpIconPath), lpiIcon);
946
947 if(lpiIcon == NULL)
948 lpiIcon = &wDummyIcon;
949
950 hIcon = ExtractIconW(hInst, lpIconPath, *lpiIcon);
951
952 if( hIcon < (HICON)2 )
953 { if( hIcon == (HICON)1 ) /* no icons found in given file */
954 { WCHAR tempPath[MAX_PATH];
955 HINSTANCE uRet = FindExecutableW(lpIconPath,NULL,tempPath);
956
957 if( uRet > (HINSTANCE)32 && tempPath[0] )
958 { wcscpy(lpIconPath,tempPath);
959 hIcon = ExtractIconW(hInst, lpIconPath, *lpiIcon);
960 if( hIcon > (HICON)2 )
961 return hIcon;
962 }
963 }
964
965 if( hIcon == (HICON)1 )
966 *lpiIcon = 2; /* MSDOS icon - we found .exe but no icons in it */
967 else
968 *lpiIcon = 6; /* generic icon - found nothing */
969
970 if (GetModuleFileNameW(hInst, lpIconPath, MAX_PATH))
971 hIcon = LoadIconW(hInst, MAKEINTRESOURCEW(*lpiIcon));
972 }
973 return hIcon;
974 }
975
976 /*************************************************************************
977 * ExtractAssociatedIconExW (SHELL32.@)
978 *
979 * Return icon for given file (either from file itself or from associated
980 * executable) and patch parameters if needed.
981 */
982 EXTERN_C HICON WINAPI ExtractAssociatedIconExW(HINSTANCE hInst, LPWSTR lpIconPath, LPWORD lpiIconIdx, LPWORD lpiIconId)
983 {
984 FIXME("%p %s %p %p): stub\n", hInst, debugstr_w(lpIconPath), lpiIconIdx, lpiIconId);
985 return 0;
986 }
987
988 /*************************************************************************
989 * ExtractAssociatedIconExA (SHELL32.@)
990 *
991 * Return icon for given file (either from file itself or from associated
992 * executable) and patch parameters if needed.
993 */
994 EXTERN_C HICON WINAPI ExtractAssociatedIconExA(HINSTANCE hInst, LPSTR lpIconPath, LPWORD lpiIconIdx, LPWORD lpiIconId)
995 {
996 HICON ret;
997 INT len = MultiByteToWideChar( CP_ACP, 0, lpIconPath, -1, NULL, 0 );
998 LPWSTR lpwstrFile = (LPWSTR)HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
999
1000 TRACE("%p %s %p %p)\n", hInst, lpIconPath, lpiIconIdx, lpiIconId);
1001
1002 MultiByteToWideChar( CP_ACP, 0, lpIconPath, -1, lpwstrFile, len );
1003 ret = ExtractAssociatedIconExW(hInst, lpwstrFile, lpiIconIdx, lpiIconId);
1004 HeapFree(GetProcessHeap(), 0, lpwstrFile);
1005 return ret;
1006 }
1007
1008
1009 /****************************************************************************
1010 * SHDefExtractIconW [SHELL32.@]
1011 */
1012 HRESULT WINAPI SHDefExtractIconW(LPCWSTR pszIconFile, int iIndex, UINT uFlags,
1013 HICON* phiconLarge, HICON* phiconSmall, UINT nIconSize)
1014 {
1015 UINT ret;
1016 HICON hIcons[2];
1017 WARN("%s %d 0x%08x %p %p %d, semi-stub\n", debugstr_w(pszIconFile), iIndex, uFlags, phiconLarge, phiconSmall, nIconSize);
1018
1019 ret = PrivateExtractIconsW(pszIconFile, iIndex, nIconSize, nIconSize, hIcons, NULL, 2, LR_DEFAULTCOLOR);
1020 /* FIXME: deal with uFlags parameter which contains GIL_ flags */
1021 if (ret == 0xFFFFFFFF)
1022 return E_FAIL;
1023 if (ret > 0) {
1024 if (phiconLarge)
1025 *phiconLarge = hIcons[0];
1026 else
1027 DestroyIcon(hIcons[0]);
1028 if (phiconSmall)
1029 *phiconSmall = hIcons[1];
1030 else
1031 DestroyIcon(hIcons[1]);
1032 return S_OK;
1033 }
1034 return S_FALSE;
1035 }
1036
1037 /****************************************************************************
1038 * SHDefExtractIconA [SHELL32.@]
1039 */
1040 HRESULT WINAPI SHDefExtractIconA(LPCSTR pszIconFile, int iIndex, UINT uFlags,
1041 HICON* phiconLarge, HICON* phiconSmall, UINT nIconSize)
1042 {
1043 HRESULT ret;
1044 INT len = MultiByteToWideChar(CP_ACP, 0, pszIconFile, -1, NULL, 0);
1045 LPWSTR lpwstrFile = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1046
1047 TRACE("%s %d 0x%08x %p %p %d\n", pszIconFile, iIndex, uFlags, phiconLarge, phiconSmall, nIconSize);
1048
1049 MultiByteToWideChar(CP_ACP, 0, pszIconFile, -1, lpwstrFile, len);
1050 ret = SHDefExtractIconW(lpwstrFile, iIndex, uFlags, phiconLarge, phiconSmall, nIconSize);
1051 HeapFree(GetProcessHeap(), 0, lpwstrFile);
1052 return ret;
1053 }
1054
1055 /****************************************************************************
1056 * SHGetIconOverlayIndexA [SHELL32.@]
1057 *
1058 * Returns the index of the overlay icon in the system image list.
1059 */
1060 EXTERN_C INT WINAPI SHGetIconOverlayIndexA(LPCSTR pszIconPath, INT iIconIndex)
1061 {
1062 FIXME("%s, %d\n", debugstr_a(pszIconPath), iIconIndex);
1063
1064 return -1;
1065 }
1066
1067 /****************************************************************************
1068 * SHGetIconOverlayIndexW [SHELL32.@]
1069 *
1070 * Returns the index of the overlay icon in the system image list.
1071 */
1072 EXTERN_C INT WINAPI SHGetIconOverlayIndexW(LPCWSTR pszIconPath, INT iIconIndex)
1073 {
1074 FIXME("%s, %d\n", debugstr_w(pszIconPath), iIconIndex);
1075
1076 return -1;
1077 }