Add debug info for IExtractIconW.
[wine] / dlls / shell32 / iconcache.c
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22
23 #include <string.h>
24 #include <sys/types.h>
25 #ifdef HAVE_UNISTD_H
26 # include <unistd.h>
27 #endif
28 #include "winbase.h"
29 #include "windef.h"
30 #include "wingdi.h"
31 #include "winuser.h"
32 #include "winreg.h"
33 #include "wine/winuser16.h"
34 #include "wine/winbase16.h"
35 #include "heap.h"
36 #include "wine/debug.h"
37
38 #include "shellapi.h"
39 #include "shlguid.h"
40 #include "pidl.h"
41 #include "shell32_main.h"
42 #include "undocshell.h"
43 #include "shlwapi.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(shell);
46
47 /********************** THE ICON CACHE ********************************/
48
49 #define INVALID_INDEX -1
50
51 typedef struct
52 {
53         LPSTR sSourceFile;      /* file (not path!) containing the icon */
54         DWORD dwSourceIndex;    /* index within the file, if it is a resoure ID it will be negated */
55         DWORD dwListIndex;      /* index within the iconlist */
56         DWORD dwFlags;          /* GIL_* flags */
57         DWORD dwAccessTime;
58 } SIC_ENTRY, * LPSIC_ENTRY;
59
60 static HDPA             sic_hdpa = 0;
61 static CRITICAL_SECTION SHELL32_SicCS = CRITICAL_SECTION_INIT("SHELL32_SicCS");
62
63 /*****************************************************************************
64  * SIC_CompareEntries
65  *
66  * NOTES
67  *  Callback for DPA_Search
68  */
69 static INT CALLBACK SIC_CompareEntries( LPVOID p1, LPVOID p2, LPARAM lparam)
70 {       TRACE("%p %p %8lx\n", p1, p2, lparam);
71
72         if (((LPSIC_ENTRY)p1)->dwSourceIndex != ((LPSIC_ENTRY)p2)->dwSourceIndex) /* first the faster one*/
73           return 1;
74
75         if (strcasecmp(((LPSIC_ENTRY)p1)->sSourceFile,((LPSIC_ENTRY)p2)->sSourceFile))
76           return 1;
77
78         return 0;
79 }
80 /*****************************************************************************
81  * SIC_IconAppend                       [internal]
82  *
83  * NOTES
84  *  appends a icon pair to the end of the cache
85  */
86 static INT SIC_IconAppend (LPCSTR sSourceFile, INT dwSourceIndex, HICON hSmallIcon, HICON hBigIcon)
87 {       LPSIC_ENTRY lpsice;
88         INT ret, index, index1;
89         char *path;
90         TRACE("%s %i %p %p\n", sSourceFile, dwSourceIndex, hSmallIcon ,hBigIcon);
91
92         lpsice = (LPSIC_ENTRY) SHAlloc (sizeof (SIC_ENTRY));
93
94         path = PathFindFileNameA(sSourceFile);
95         lpsice->sSourceFile = HeapAlloc( GetProcessHeap(), 0, strlen(path)+1 );
96         strcpy( lpsice->sSourceFile, path );
97
98         lpsice->dwSourceIndex = dwSourceIndex;
99
100         EnterCriticalSection(&SHELL32_SicCS);
101
102         index = DPA_InsertPtr(sic_hdpa, 0x7fff, lpsice);
103         if ( INVALID_INDEX == index )
104         {
105           SHFree(lpsice);
106           ret = INVALID_INDEX;
107         }
108         else
109         {
110           index = ImageList_AddIcon (ShellSmallIconList, hSmallIcon);
111           index1= ImageList_AddIcon (ShellBigIconList, hBigIcon);
112
113           if (index!=index1)
114           {
115             FIXME("iconlists out of sync 0x%x 0x%x\n", index, index1);
116           }
117           lpsice->dwListIndex = index;
118           ret = lpsice->dwListIndex;
119         }
120
121         LeaveCriticalSection(&SHELL32_SicCS);
122         return ret;
123 }
124 /****************************************************************************
125  * SIC_LoadIcon                         [internal]
126  *
127  * NOTES
128  *  gets small/big icon by number from a file
129  */
130 static INT SIC_LoadIcon (LPCSTR sSourceFile, INT dwSourceIndex)
131 {       HICON   hiconLarge=0;
132         HICON   hiconSmall=0;
133
134         PrivateExtractIconsA( sSourceFile, dwSourceIndex, 32, 32, &hiconLarge, 0, 1, 0 );
135         PrivateExtractIconsA( sSourceFile, dwSourceIndex, 16, 16, &hiconSmall, 0, 1, 0 );
136
137         if ( !hiconLarge ||  !hiconSmall)
138         {
139           WARN("failure loading icon %i from %s (%p %p)\n", dwSourceIndex, sSourceFile, hiconLarge, hiconSmall);
140           return -1;
141         }
142         return SIC_IconAppend (sSourceFile, dwSourceIndex, hiconSmall, hiconLarge);
143 }
144 /*****************************************************************************
145  * SIC_GetIconIndex                     [internal]
146  *
147  * Parameters
148  *      sSourceFile     [IN]    filename of file containing the icon
149  *      index           [IN]    index/resID (negated) in this file
150  *
151  * NOTES
152  *  look in the cache for a proper icon. if not available the icon is taken
153  *  from the file and cached
154  */
155 INT SIC_GetIconIndex (LPCSTR sSourceFile, INT dwSourceIndex )
156 {       SIC_ENTRY sice;
157         INT ret, index = INVALID_INDEX;
158
159         TRACE("%s %i\n", sSourceFile, dwSourceIndex);
160
161         sice.sSourceFile = PathFindFileNameA(sSourceFile);
162         sice.dwSourceIndex = dwSourceIndex;
163
164         EnterCriticalSection(&SHELL32_SicCS);
165
166         if (NULL != DPA_GetPtr (sic_hdpa, 0))
167         {
168           /* search linear from position 0*/
169           index = DPA_Search (sic_hdpa, &sice, 0, SIC_CompareEntries, 0, 0);
170         }
171
172         if ( INVALID_INDEX == index )
173         {
174           ret = SIC_LoadIcon (sSourceFile, dwSourceIndex);
175         }
176         else
177         {
178           TRACE("-- found\n");
179           ret = ((LPSIC_ENTRY)DPA_GetPtr(sic_hdpa, index))->dwListIndex;
180         }
181
182         LeaveCriticalSection(&SHELL32_SicCS);
183         return ret;
184 }
185 /****************************************************************************
186  * SIC_GetIcon                          [internal]
187  *
188  * NOTES
189  *  retrieves the specified icon from the iconcache. if not found tries to load the icon
190  */
191 static HICON WINE_UNUSED SIC_GetIcon (LPCSTR sSourceFile, INT dwSourceIndex, BOOL bSmallIcon )
192 {       INT index;
193
194         TRACE("%s %i\n", sSourceFile, dwSourceIndex);
195
196         index = SIC_GetIconIndex(sSourceFile, dwSourceIndex);
197
198         if (INVALID_INDEX == index)
199         {
200           return (HICON)INVALID_INDEX;
201         }
202
203         if (bSmallIcon)
204           return ImageList_GetIcon(ShellSmallIconList, index, ILD_NORMAL);
205         return ImageList_GetIcon(ShellBigIconList, index, ILD_NORMAL);
206
207 }
208 /*****************************************************************************
209  * SIC_Initialize                       [internal]
210  *
211  * NOTES
212  *  hack to load the resources from the shell32.dll under a different dll name
213  *  will be removed when the resource-compiler is ready
214  */
215 BOOL SIC_Initialize(void)
216 {
217         HICON           hSm, hLg;
218         UINT            index;
219
220         TRACE("\n");
221
222         if (sic_hdpa)   /* already initialized?*/
223           return TRUE;
224
225         sic_hdpa = DPA_Create(16);
226
227         if (!sic_hdpa)
228         {
229           return(FALSE);
230         }
231
232         ShellSmallIconList = ImageList_Create(16,16,ILC_COLORDDB | ILC_MASK,0,0x20);
233         ShellBigIconList = ImageList_Create(32,32,ILC_COLORDDB | ILC_MASK,0,0x20);
234
235         ImageList_SetBkColor(ShellSmallIconList, GetSysColor(COLOR_WINDOW));
236         ImageList_SetBkColor(ShellBigIconList, GetSysColor(COLOR_WINDOW));
237
238         for (index=1; index<39; index++)
239         {
240           hSm = LoadImageA(shell32_hInstance, MAKEINTRESOURCEA(index), IMAGE_ICON, 16, 16,LR_SHARED);
241           hLg = LoadImageA(shell32_hInstance, MAKEINTRESOURCEA(index), IMAGE_ICON, 32, 32,LR_SHARED);
242
243           if(!hSm)
244           {
245             hSm = LoadImageA(shell32_hInstance, MAKEINTRESOURCEA(0), IMAGE_ICON, 16, 16,LR_SHARED);
246             hLg = LoadImageA(shell32_hInstance, MAKEINTRESOURCEA(0), IMAGE_ICON, 32, 32,LR_SHARED);
247           }
248           SIC_IconAppend ("shell32.dll", index, hSm, hLg);
249         }
250
251         TRACE("hIconSmall=%p hIconBig=%p\n",ShellSmallIconList, ShellBigIconList);
252
253         return TRUE;
254 }
255 /*************************************************************************
256  * SIC_Destroy
257  *
258  * frees the cache
259  */
260 void SIC_Destroy(void)
261 {
262         LPSIC_ENTRY lpsice;
263         int i;
264
265         TRACE("\n");
266
267         EnterCriticalSection(&SHELL32_SicCS);
268
269         if (sic_hdpa && NULL != DPA_GetPtr (sic_hdpa, 0))
270         {
271           for (i=0; i < DPA_GetPtrCount(sic_hdpa); ++i)
272           {
273             lpsice = DPA_GetPtr(sic_hdpa, i);
274             SHFree(lpsice);
275           }
276           DPA_Destroy(sic_hdpa);
277         }
278
279         sic_hdpa = NULL;
280
281         LeaveCriticalSection(&SHELL32_SicCS);
282         DeleteCriticalSection(&SHELL32_SicCS);
283 }
284 /*************************************************************************
285  * Shell_GetImageList                   [SHELL32.71]
286  *
287  * PARAMETERS
288  *  imglist[1|2] [OUT] pointer which recive imagelist handles
289  *
290  */
291 BOOL WINAPI Shell_GetImageList(HIMAGELIST * lpBigList, HIMAGELIST * lpSmallList)
292 {       TRACE("(%p,%p)\n",lpBigList,lpSmallList);
293         if (lpBigList)
294         { *lpBigList = ShellBigIconList;
295         }
296         if (lpSmallList)
297         { *lpSmallList = ShellSmallIconList;
298         }
299
300         return TRUE;
301 }
302 /*************************************************************************
303  * PidlToSicIndex                       [INTERNAL]
304  *
305  * PARAMETERS
306  *      sh      [IN]    IShellFolder
307  *      pidl    [IN]
308  *      bBigIcon [IN]
309  *      uFlags  [IN]    GIL_*
310  *      pIndex  [OUT]   index within the SIC
311  *
312  */
313 BOOL PidlToSicIndex (
314         IShellFolder * sh,
315         LPITEMIDLIST pidl,
316         BOOL bBigIcon,
317         UINT uFlags,
318         UINT * pIndex)
319 {
320         IExtractIconA   *ei;
321         char            szIconFile[MAX_PATH];   /* file containing the icon */
322         INT             iSourceIndex;           /* index or resID(negated) in this file */
323         BOOL            ret = FALSE;
324         UINT            dwFlags = 0;
325
326         TRACE("sf=%p pidl=%p %s\n", sh, pidl, bBigIcon?"Big":"Small");
327
328         if (SUCCEEDED (IShellFolder_GetUIObjectOf(sh, 0, 1, &pidl, &IID_IExtractIconA, 0, (void **)&ei)))
329         {
330           if (SUCCEEDED(IExtractIconA_GetIconLocation(ei, uFlags, szIconFile, MAX_PATH, &iSourceIndex, &dwFlags)))
331           {
332             *pIndex = SIC_GetIconIndex(szIconFile, iSourceIndex);
333             ret = TRUE;
334           }
335           IExtractIconA_Release(ei);
336         }
337
338         if (INVALID_INDEX == *pIndex)   /* default icon when failed */
339           *pIndex = 1;
340
341         return ret;
342
343 }
344
345 /*************************************************************************
346  * SHMapPIDLToSystemImageListIndex      [SHELL32.77]
347  *
348  * PARAMETERS
349  *      sh      [IN]            pointer to an instance of IShellFolder
350  *      pidl    [IN]
351  *      pIndex  [OUT][OPTIONAL] SIC index for big icon
352  *
353  */
354 int WINAPI SHMapPIDLToSystemImageListIndex(
355         LPSHELLFOLDER sh,
356         LPCITEMIDLIST pidl,
357         UINT * pIndex)
358 {
359         UINT    Index;
360
361         TRACE("(SF=%p,pidl=%p,%p)\n",sh,pidl,pIndex);
362         pdump(pidl);
363
364         if (pIndex)
365           PidlToSicIndex ( sh, pidl, 1, 0, pIndex);
366         PidlToSicIndex ( sh, pidl, 0, 0, &Index);
367         return Index;
368 }
369
370 /*************************************************************************
371  * Shell_GetCachedImageIndex            [SHELL32.72]
372  *
373  */
374 INT WINAPI Shell_GetCachedImageIndexA(LPCSTR szPath, INT nIndex, BOOL bSimulateDoc)
375 {
376         WARN("(%s,%08x,%08x) semi-stub.\n",debugstr_a(szPath), nIndex, bSimulateDoc);
377         return SIC_GetIconIndex(szPath, nIndex);
378 }
379
380 INT WINAPI Shell_GetCachedImageIndexW(LPCWSTR szPath, INT nIndex, BOOL bSimulateDoc)
381 {       INT ret;
382         LPSTR sTemp = HEAP_strdupWtoA (GetProcessHeap(),0,szPath);
383
384         WARN("(%s,%08x,%08x) semi-stub.\n",debugstr_w(szPath), nIndex, bSimulateDoc);
385
386         ret = SIC_GetIconIndex(sTemp, nIndex);
387         HeapFree(GetProcessHeap(),0,sTemp);
388         return ret;
389 }
390
391 INT WINAPI Shell_GetCachedImageIndexAW(LPCVOID szPath, INT nIndex, BOOL bSimulateDoc)
392 {       if( SHELL_OsIsUnicode())
393           return Shell_GetCachedImageIndexW(szPath, nIndex, bSimulateDoc);
394         return Shell_GetCachedImageIndexA(szPath, nIndex, bSimulateDoc);
395 }
396
397 /*************************************************************************
398  * ExtractIconEx                        [SHELL32.@]
399  */
400 UINT WINAPI ExtractIconExAW(LPCVOID lpszFile, INT nIconIndex, HICON * phiconLarge, HICON * phiconSmall, UINT nIcons)
401 {       if (SHELL_OsIsUnicode())
402           return ExtractIconExW ( lpszFile, nIconIndex, phiconLarge, phiconSmall, nIcons);
403         return ExtractIconExA ( lpszFile, nIconIndex, phiconLarge, phiconSmall, nIcons);
404 }
405
406 /*************************************************************************
407  * ExtractIconExW                       [SHELL32.@]
408  * RETURNS: 
409  *  0 no icon found
410  *  -1 file is not valid
411  *  or number of icons extracted
412  */
413 UINT WINAPI ExtractIconExW(LPCWSTR lpszFile, INT nIconIndex, HICON * phiconLarge, HICON * phiconSmall, UINT nIcons)
414 {
415         TRACE("%s %i %p %p %i\n", debugstr_w(lpszFile), nIconIndex, phiconLarge, phiconSmall, nIcons);
416
417         return PrivateExtractIconExW(lpszFile, nIconIndex, phiconLarge, phiconSmall, nIcons);
418 }
419
420 /*************************************************************************
421  * ExtractIconExA                       [SHELL32.@]
422  */
423 UINT WINAPI ExtractIconExA(LPCSTR lpszFile, INT nIconIndex, HICON * phiconLarge, HICON * phiconSmall, UINT nIcons)
424 {
425     UINT ret;
426     INT len = MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, NULL, 0);
427     LPWSTR lpwstrFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
428
429     TRACE("%s %i %p %p %i\n", lpszFile, nIconIndex, phiconLarge, phiconSmall, nIcons);
430
431     MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, lpwstrFile, len);
432     ret = ExtractIconExW (lpwstrFile, nIconIndex, phiconLarge, phiconSmall, nIcons);
433     HeapFree(GetProcessHeap(), 0, lpwstrFile);
434     return ret;
435 }
436
437 /*************************************************************************
438  *                              ExtractAssociatedIconA (SHELL32.@)
439  *
440  * Return icon for given file (either from file itself or from associated
441  * executable) and patch parameters if needed.
442  */
443 HICON WINAPI ExtractAssociatedIconA(HINSTANCE hInst, LPSTR lpIconPath, LPWORD lpiIcon)
444 {       
445         HICON hIcon;
446         WORD wDummyIcon = 0;
447         
448         TRACE("\n");
449
450         if(lpiIcon == NULL)
451             lpiIcon = &wDummyIcon;
452
453         hIcon = ExtractIconA(hInst, lpIconPath, *lpiIcon);
454
455         if( hIcon < (HICON)2 )
456         { if( hIcon == (HICON)1 ) /* no icons found in given file */
457           { char  tempPath[0x80];
458             HINSTANCE uRet = FindExecutableA(lpIconPath,NULL,tempPath);
459
460             if( uRet > (HINSTANCE)32 && tempPath[0] )
461             { strcpy(lpIconPath,tempPath);
462               hIcon = ExtractIconA(hInst, lpIconPath, *lpiIcon);
463               if( hIcon > (HICON)2 )
464                 return hIcon;
465             }
466             else hIcon = 0;
467           }
468
469           if( hIcon == (HICON)1 )
470             *lpiIcon = 2;   /* MSDOS icon - we found .exe but no icons in it */
471           else
472             *lpiIcon = 6;   /* generic icon - found nothing */
473
474           GetModuleFileNameA(hInst, lpIconPath, 0x80);
475           hIcon = LoadIconA( hInst, MAKEINTRESOURCEA(*lpiIcon));
476         }
477         return hIcon;
478 }
479
480 /*************************************************************************
481  *                              ExtractAssociatedIconExW (SHELL32.@)
482  *
483  * Return icon for given file (either from file itself or from associated
484  * executable) and patch parameters if needed.
485  */
486 HICON WINAPI ExtractAssociatedIconExW(HINSTANCE hInst, LPWSTR lpIconPath, LPWORD lpiIconIdx, LPWORD lpiIconId)
487 {
488   FIXME("%p %s %p %p): stub\n", hInst, debugstr_w(lpIconPath), lpiIconIdx, lpiIconId);
489   return 0;
490 }
491
492 /*************************************************************************
493  *                              ExtractAssociatedIconExA (SHELL32.@)
494  *
495  * Return icon for given file (either from file itself or from associated
496  * executable) and patch parameters if needed.
497  */
498 HICON WINAPI ExtractAssociatedIconExA(HINSTANCE hInst, LPSTR lpIconPath, LPWORD lpiIconIdx, LPWORD lpiIconId)
499 {
500   HICON ret;
501   INT len = MultiByteToWideChar( CP_ACP, 0, lpIconPath, -1, NULL, 0 );
502   LPWSTR lpwstrFile = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
503
504   TRACE("%p %s %p %p)\n", hInst, lpIconPath, lpiIconIdx, lpiIconId);
505
506   MultiByteToWideChar( CP_ACP, 0, lpIconPath, -1, lpwstrFile, len );
507   ret = ExtractAssociatedIconExW(hInst, lpwstrFile, lpiIconIdx, lpiIconId);
508   HeapFree(GetProcessHeap(), 0, lpwstrFile);
509   return ret;
510 }
511
512
513 /****************************************************************************
514  * SHDefExtractIconW            [SHELL32.@]
515  */
516 HRESULT WINAPI SHDefExtractIconW(LPCWSTR pszIconFile, int iIndex, UINT uFlags,
517                                  HICON* phiconLarge, HICON* phiconSmall, UINT nIconSize)
518 {
519         UINT ret;
520         HICON hIcons[2];
521         WARN("%s %d 0x%08x %p %p %d, semi-stub\n", debugstr_w(pszIconFile), iIndex, uFlags, phiconLarge, phiconSmall, nIconSize);
522
523         ret = PrivateExtractIconsW(pszIconFile, iIndex, nIconSize, nIconSize, hIcons, NULL, 2, LR_DEFAULTCOLOR);
524         /* FIXME: deal with uFlags parameter which contains GIL_ flags */
525         if (ret == 0xFFFFFFFF)
526           return E_FAIL;
527         if (ret > 0) {
528           *phiconLarge = hIcons[0];
529           *phiconSmall = hIcons[1];
530           return S_OK;
531         }
532         return S_FALSE;
533 }
534
535 /****************************************************************************
536  * SHDefExtractIconA            [SHELL32.@]
537  */
538 HRESULT WINAPI SHDefExtractIconA(LPCSTR pszIconFile, int iIndex, UINT uFlags,
539                                  HICON* phiconLarge, HICON* phiconSmall, UINT nIconSize)
540 {
541   HRESULT ret;
542   INT len = MultiByteToWideChar(CP_ACP, 0, pszIconFile, -1, NULL, 0);
543   LPWSTR lpwstrFile = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
544
545   TRACE("%s %d 0x%08x %p %p %d\n", pszIconFile, iIndex, uFlags, phiconLarge, phiconSmall, nIconSize);
546
547   MultiByteToWideChar(CP_ACP, 0, pszIconFile, -1, lpwstrFile, len);
548   ret = SHDefExtractIconW(lpwstrFile, iIndex, uFlags, phiconLarge, phiconSmall, nIconSize);
549   HeapFree(GetProcessHeap(), 0, lpwstrFile);
550   return ret;
551 }