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