Various cosmetic changes.
[wine] / dlls / user / exticon.c
1 /*
2  *      icon extracting
3  *
4  * taken and slightly changed from shell
5  * this should replace the icon extraction code in shell32 and shell16 once
6  * it needs a serious test for compliance with the native API 
7  */
8
9 #include "config.h"
10
11 #include <string.h>
12 #include <stdlib.h>     /* abs() */
13 #include <sys/types.h>
14 #include <unistd.h>
15
16 #include "winbase.h"
17 #include "windef.h"
18 #include "winerror.h"
19 #include "wingdi.h"
20 #include "winuser.h"
21 #include "wine/winbase16.h"
22 #include "cursoricon.h"
23 #include "debugtools.h"
24
25 DEFAULT_DEBUG_CHANNEL(icon);
26
27 #include "pshpack1.h"
28
29 typedef struct
30 {
31     BYTE        bWidth;          /* Width, in pixels, of the image      */
32     BYTE        bHeight;         /* Height, in pixels, of the image     */
33     BYTE        bColorCount;     /* Number of colors in image (0 if >=8bpp) */
34     BYTE        bReserved;       /* Reserved ( must be 0)               */
35     WORD        wPlanes;         /* Color Planes                        */
36     WORD        wBitCount;       /* Bits per pixel                      */
37     DWORD       dwBytesInRes;    /* How many bytes in this resource?    */
38     DWORD       dwImageOffset;   /* Where in the file is this image?    */
39 } icoICONDIRENTRY, *LPicoICONDIRENTRY;
40
41 typedef struct
42 {
43     WORD            idReserved;   /* Reserved (must be 0) */
44     WORD            idType;       /* Resource Type (RES_ICON or RES_CURSOR) */
45     WORD            idCount;      /* How many images */
46     icoICONDIRENTRY idEntries[1]; /* An entry for each image (idCount of 'em) */
47 } icoICONDIR, *LPicoICONDIR;
48
49 #include "poppack.h"
50
51 #if 0
52 static void dumpIcoDirEnty ( LPicoICONDIRENTRY entry )
53 {       
54         TRACE("width = 0x%08x height = 0x%08x\n", entry->bWidth, entry->bHeight);
55         TRACE("colors = 0x%08x planes = 0x%08x\n", entry->bColorCount, entry->wPlanes);
56         TRACE("bitcount = 0x%08x bytesinres = 0x%08lx offset = 0x%08lx\n", 
57         entry->wBitCount, entry->dwBytesInRes, entry->dwImageOffset);
58 }
59 static void dumpIcoDir ( LPicoICONDIR entry )
60 {       
61         TRACE("type = 0x%08x count = 0x%08x\n", entry->idType, entry->idCount);
62 }
63 #endif
64
65 /**********************************************************************
66  *  find_entry_by_id
67  *
68  * Find an entry by id in a resource directory
69  * Copied from loader/pe_resource.c (FIXME: should use exported resource functions)
70  */
71 static const IMAGE_RESOURCE_DIRECTORY *find_entry_by_id( const IMAGE_RESOURCE_DIRECTORY *dir,
72                                                          WORD id, const void *root )
73 {
74     const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
75     int min, max, pos;
76
77     entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
78     min = dir->NumberOfNamedEntries;
79     max = min + dir->NumberOfIdEntries - 1;
80     while (min <= max)
81     {
82         pos = (min + max) / 2;
83         if (entry[pos].u1.s2.Id == id)
84             return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry[pos].u2.s3.OffsetToDirectory);
85         if (entry[pos].u1.s2.Id > id) max = pos - 1;
86         else min = pos + 1;
87     }
88     return NULL;
89 }
90
91 /**********************************************************************
92  *  find_entry_default
93  *
94  * Find a default entry in a resource directory
95  * Copied from loader/pe_resource.c (FIXME: should use exported resource functions)
96  */
97 static const IMAGE_RESOURCE_DIRECTORY *find_entry_default( const IMAGE_RESOURCE_DIRECTORY *dir,
98                                                            const void *root )
99 {
100     const IMAGE_RESOURCE_DIRECTORY_ENTRY *entry;
101     entry = (const IMAGE_RESOURCE_DIRECTORY_ENTRY *)(dir + 1);
102     return (IMAGE_RESOURCE_DIRECTORY *)((char *)root + entry->u2.s3.OffsetToDirectory);
103 }
104
105 /*************************************************************************
106  *                              USER32_GetResourceTable
107  */
108 static DWORD USER32_GetResourceTable(LPBYTE peimage,DWORD pesize,LPBYTE *retptr)
109 {
110         IMAGE_DOS_HEADER        * mz_header;
111
112         TRACE("%p %p\n", peimage, retptr);  
113
114         *retptr = NULL;
115
116         mz_header = (IMAGE_DOS_HEADER*) peimage;
117         
118         if (mz_header->e_magic != IMAGE_DOS_SIGNATURE)
119         {
120           if (mz_header->e_cblp == 1)   /* .ICO file ? */
121           {
122             *retptr = (LPBYTE)-1;       /* ICONHEADER.idType, must be 1 */
123             return 1;
124           }
125           else
126             return 0; /* failed */
127         }
128         if (mz_header->e_lfanew >= pesize) {
129             return 0; /* failed, happens with PKZIP DOS Exes for instance. */
130         }
131         if (*((DWORD*)(peimage + mz_header->e_lfanew)) == IMAGE_NT_SIGNATURE )
132           return IMAGE_NT_SIGNATURE;
133
134         if (*((WORD*)(peimage + mz_header->e_lfanew)) == IMAGE_OS2_SIGNATURE )
135         {
136           IMAGE_OS2_HEADER      * ne_header;
137
138           ne_header = (IMAGE_OS2_HEADER*)(peimage + mz_header->e_lfanew);
139           
140           if (ne_header->ne_magic != IMAGE_OS2_SIGNATURE)
141             return 0;
142
143           if( (ne_header->ne_restab - ne_header->ne_rsrctab) <= sizeof(NE_TYPEINFO) )
144             *retptr = (LPBYTE)-1;
145           else
146             *retptr = peimage + mz_header->e_lfanew + ne_header->ne_rsrctab;
147
148           return IMAGE_OS2_SIGNATURE;
149         }
150         return 0; /* failed */
151 }
152 /*************************************************************************
153  *                      USER32_LoadResource
154  */
155 static BYTE * USER32_LoadResource( LPBYTE peimage, NE_NAMEINFO* pNInfo, WORD sizeShift, ULONG *uSize)
156 {
157         TRACE("%p %p 0x%08x\n", peimage, pNInfo, sizeShift);
158
159         *uSize = (DWORD)pNInfo->length << sizeShift;
160         return peimage + ((DWORD)pNInfo->offset << sizeShift);
161 }
162
163 /*************************************************************************
164  *                      ICO_LoadIcon
165  */
166 static BYTE * ICO_LoadIcon( LPBYTE peimage, LPicoICONDIRENTRY lpiIDE, ULONG *uSize)
167 {
168         TRACE("%p %p\n", peimage, lpiIDE);
169
170         *uSize = lpiIDE->dwBytesInRes;
171         return peimage + lpiIDE->dwImageOffset;
172 }
173
174 /*************************************************************************
175  *                      ICO_GetIconDirectory
176  *
177  * Reads .ico file and build phony ICONDIR struct
178  * see http://www.microsoft.com/win32dev/ui/icons.htm
179  */
180 #define HEADER_SIZE             (sizeof(CURSORICONDIR) - sizeof (CURSORICONDIRENTRY))
181 #define HEADER_SIZE_FILE        (sizeof(icoICONDIR) - sizeof (icoICONDIRENTRY))
182
183 static BYTE * ICO_GetIconDirectory( LPBYTE peimage, LPicoICONDIR* lplpiID, ULONG *uSize ) 
184 {
185         CURSORICONDIR   * lpcid;        /* icon resource in resource-dir format */
186         CURSORICONDIR   * lpID;         /* icon resource in resource format */
187         int             i;
188
189         TRACE("%p %p\n", peimage, lplpiID); 
190         
191         lpcid = (CURSORICONDIR*)peimage;
192
193         if( lpcid->idReserved || (lpcid->idType != 1) || (!lpcid->idCount) )
194           return 0;
195
196         /* allocate the phony ICONDIR structure */
197         *uSize = lpcid->idCount * sizeof(CURSORICONDIRENTRY) + HEADER_SIZE;
198         if( (lpID = (CURSORICONDIR*)HeapAlloc(GetProcessHeap(),0, *uSize) ))
199         {
200           /* copy the header */
201           lpID->idReserved = lpcid->idReserved;
202           lpID->idType = lpcid->idType;
203           lpID->idCount = lpcid->idCount;
204
205           /* copy the entries */
206           for( i=0; i < lpcid->idCount; i++ )
207           {
208             memcpy((void*)&(lpID->idEntries[i]),(void*)&(lpcid->idEntries[i]), sizeof(CURSORICONDIRENTRY) - 2);
209             lpID->idEntries[i].wResId = i;
210           }
211
212           *lplpiID = (LPicoICONDIR)peimage;
213           return (BYTE *)lpID;
214         }
215         return 0;
216 }
217
218 /*************************************************************************
219  *      ICO_ExtractIconExW              [internal]
220  *
221  * NOTES
222  *  nIcons = 0: returns number of Icons in file
223  *
224  * returns
225  *  failure:0; success: icon handle or nr of icons (nIconIndex-1)
226  */
227 static HRESULT ICO_ExtractIconExW(
228         LPCWSTR lpszExeFileName,
229         HICON * RetPtr,
230         INT nIconIndex,
231         UINT nIcons,
232         UINT cxDesired,
233         UINT cyDesired )
234 {
235         HGLOBAL         hRet = E_FAIL;
236         LPBYTE          pData;
237         DWORD           sig;
238         HFILE           hFile;
239         UINT16          iconDirCount = 0,iconCount = 0;
240         LPBYTE          peimage;
241         HANDLE          fmapping;
242         ULONG           uSize;
243         DWORD           fsizeh,fsizel;
244         
245         TRACE("(file %s,start %d,extract %d\n", debugstr_w(lpszExeFileName), nIconIndex, nIcons);
246
247         hFile = CreateFileW( lpszExeFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
248         if (hFile == INVALID_HANDLE_VALUE) return hRet;
249         fsizel = GetFileSize(hFile,&fsizeh);
250
251         /* Map the file */
252         fmapping = CreateFileMappingA( hFile, NULL, PAGE_READONLY | SEC_COMMIT, 0, 0, NULL );
253         CloseHandle( hFile );
254         if (!fmapping)
255         {
256           WARN("CreateFileMapping error %ld\n", GetLastError() );
257           return hRet;
258         }
259
260         if ( !(peimage = MapViewOfFile(fmapping,FILE_MAP_READ,0,0,0))) 
261         {
262           WARN("MapViewOfFile error %ld\n", GetLastError() );
263           CloseHandle( fmapping );
264           return hRet;
265         }
266         CloseHandle( fmapping );
267
268         sig = USER32_GetResourceTable(peimage,fsizel,&pData);
269
270 /* ico file */
271         if( sig==IMAGE_OS2_SIGNATURE || sig==1 ) /* .ICO file */
272         {
273           BYTE          *pCIDir = 0;
274           NE_TYPEINFO   *pTInfo = (NE_TYPEINFO*)(pData + 2);
275           NE_NAMEINFO   *pIconStorage = NULL;
276           NE_NAMEINFO   *pIconDir = NULL;
277           LPicoICONDIR  lpiID = NULL;
278
279           TRACE("-- OS2/icon Signature (0x%08lx)\n", sig);
280
281           if( pData == (BYTE*)-1 )
282           {
283             /* FIXME: pCIDir is allocated on the heap - memory leak */
284             pCIDir = ICO_GetIconDirectory(peimage, &lpiID, &uSize);     /* check for .ICO file */
285             if( pCIDir )
286             {
287               iconDirCount = 1; iconCount = lpiID->idCount;
288               TRACE("-- icon found %p 0x%08lx 0x%08x 0x%08x\n", pCIDir, uSize, iconDirCount, iconCount);
289             }
290           }
291           else while( pTInfo->type_id && !(pIconStorage && pIconDir) )
292           {
293             if( pTInfo->type_id == NE_RSCTYPE_GROUP_ICON )      /* find icon directory and icon repository */
294             {
295               iconDirCount = pTInfo->count;
296               pIconDir = ((NE_NAMEINFO*)(pTInfo + 1));
297               TRACE("\tfound directory - %i icon families\n", iconDirCount);
298             }
299             if( pTInfo->type_id == NE_RSCTYPE_ICON ) 
300             {
301               iconCount = pTInfo->count;
302               pIconStorage = ((NE_NAMEINFO*)(pTInfo + 1));
303               TRACE("\ttotal icons - %i\n", iconCount);
304             }
305             pTInfo = (NE_TYPEINFO *)((char*)(pTInfo+1)+pTInfo->count*sizeof(NE_NAMEINFO));
306           }
307
308           if( (pIconStorage && pIconDir) || lpiID )       /* load resources and create icons */
309           {
310             if( nIcons == 0 )
311             {
312               hRet = iconDirCount;
313             }
314             else if( nIconIndex < iconDirCount )
315             {
316               UINT16   i, icon;
317               if( nIcons > iconDirCount - nIconIndex ) 
318                 nIcons = iconDirCount - nIconIndex;
319
320               for( i = nIconIndex; i < nIconIndex + nIcons; i++ ) 
321               {
322                 /* .ICO files have only one icon directory */
323                 if( lpiID == NULL )     /* *.ico */
324                   pCIDir = USER32_LoadResource( peimage, pIconDir + i, *(WORD*)pData, &uSize );
325                 RetPtr[i-nIconIndex] = LookupIconIdFromDirectoryEx( pCIDir, TRUE, cxDesired, cyDesired, 0);
326               }
327
328               for( icon = nIconIndex; icon < nIconIndex + nIcons; icon++ )
329               {
330                 pCIDir = NULL;
331                 if( lpiID )
332                   pCIDir = ICO_LoadIcon( peimage, lpiID->idEntries + RetPtr[icon-nIconIndex], &uSize);
333                 else
334                   for( i = 0; i < iconCount; i++ )
335                     if( pIconStorage[i].id == (RetPtr[icon-nIconIndex] | 0x8000) )
336                       pCIDir = USER32_LoadResource( peimage, pIconStorage + i,*(WORD*)pData, &uSize );
337
338                 if( pCIDir )
339                   RetPtr[icon-nIconIndex] = (HICON) CreateIconFromResourceEx(pCIDir,uSize,TRUE,0x00030000, cxDesired, cyDesired, LR_DEFAULTCOLOR);
340                 else
341                   RetPtr[icon-nIconIndex] = 0;
342               }
343               hRet = S_OK;
344             }
345           }
346         } 
347 /* end ico file */
348
349 /* exe/dll */
350         else if( sig == IMAGE_NT_SIGNATURE )
351         {
352           LPBYTE                idata,igdata;
353           PIMAGE_DOS_HEADER     dheader;
354           PIMAGE_NT_HEADERS     pe_header;
355           PIMAGE_SECTION_HEADER pe_sections;
356           const IMAGE_RESOURCE_DIRECTORY *rootresdir,*iconresdir,*icongroupresdir;
357           const IMAGE_RESOURCE_DATA_ENTRY *idataent,*igdataent;
358           const IMAGE_RESOURCE_DIRECTORY_ENTRY *xresent;
359           int                   i,j;
360                   
361           dheader = (PIMAGE_DOS_HEADER)peimage;
362           pe_header = (PIMAGE_NT_HEADERS)(peimage+dheader->e_lfanew);     /* it is a pe header, USER32_GetResourceTable checked that */
363           pe_sections = (PIMAGE_SECTION_HEADER)(((char*)pe_header)+sizeof(*pe_header)); /* probably makes problems with short PE headers...*/
364           rootresdir = NULL;
365
366           /* search for the root resource directory */
367           for (i=0;i<pe_header->FileHeader.NumberOfSections;i++) 
368           {
369             if (pe_sections[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
370               continue;
371             if (fsizel < pe_sections[i].PointerToRawData+pe_sections[i].SizeOfRawData) {
372               FIXME("File %s too short (section is at %ld bytes, real size is %ld)\n",
373                       debugstr_w(lpszExeFileName),
374                       pe_sections[i].PointerToRawData+pe_sections[i].SizeOfRawData,
375                       fsizel
376               );
377               goto end;
378             }
379             /* FIXME: doesn't work when the resources are not in a separate section */
380             if (pe_sections[i].VirtualAddress == pe_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress) 
381             {
382               rootresdir = (PIMAGE_RESOURCE_DIRECTORY)(peimage+pe_sections[i].PointerToRawData);
383               break;
384             }
385           }
386
387           if (!rootresdir) 
388           {
389             WARN("haven't found section for resource directory.\n");
390             goto end;           /* failure */
391           }
392
393           /* search for the group icon directory */
394           if (!(icongroupresdir = find_entry_by_id(rootresdir, LOWORD(RT_GROUP_ICONW), rootresdir))) 
395           {
396             WARN("No Icongroupresourcedirectory!\n");
397             goto end;           /* failure */
398           }
399           iconDirCount = icongroupresdir->NumberOfNamedEntries + icongroupresdir->NumberOfIdEntries;
400
401           /* only number of icons requested */
402           if( nIcons == 0 )
403           {
404             hRet = iconDirCount;
405             goto end;           /* success */
406           }
407
408           if( nIconIndex < 0 )
409           {
410             /* search resource id */
411             int n = 0;
412             int iId = abs(nIconIndex);
413             PIMAGE_RESOURCE_DIRECTORY_ENTRY xprdeTmp = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(icongroupresdir+1);
414
415             while(n<iconDirCount && xprdeTmp)
416             {              
417               if(xprdeTmp->u1.s2.Id ==  iId)
418               {
419                   nIconIndex = n;
420                   break;
421               }
422               n++;
423               xprdeTmp++;                  
424             }
425             if (nIconIndex < 0)
426             {
427               WARN("resource id %d not found\n", iId);
428               goto end;         /* failure */
429             }
430           }
431           else
432           {
433             /* check nIconIndex to be in range */
434             if (nIconIndex >= iconDirCount) 
435             {
436               WARN("nIconIndex %d is larger than iconDirCount %d\n",nIconIndex,iconDirCount);
437               goto end;         /* failure */
438             }
439           }
440
441           /* assure we don't get too much */
442           if( nIcons > iconDirCount - nIconIndex )
443             nIcons = iconDirCount - nIconIndex;
444
445           /* starting from specified index */
446           xresent = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(icongroupresdir+1) + nIconIndex;
447
448           for (i=0; i < nIcons; i++,xresent++) 
449           {
450               const IMAGE_RESOURCE_DIRECTORY *resdir;
451
452             /* go down this resource entry, name */
453             resdir = (PIMAGE_RESOURCE_DIRECTORY)((DWORD)rootresdir+(xresent->u2.s3.OffsetToDirectory));
454
455             /* default language (0) */
456             resdir = find_entry_default(resdir,rootresdir);
457             igdataent = (PIMAGE_RESOURCE_DATA_ENTRY)resdir;
458
459             /* lookup address in mapped image for virtual address */
460             igdata = NULL;
461
462             for (j=0;j<pe_header->FileHeader.NumberOfSections;j++) 
463             {
464               if (igdataent->OffsetToData < pe_sections[j].VirtualAddress)
465                 continue;
466               if (igdataent->OffsetToData+igdataent->Size > pe_sections[j].VirtualAddress+pe_sections[j].SizeOfRawData)
467                 continue;
468
469               if (igdataent->OffsetToData-pe_sections[j].VirtualAddress+pe_sections[j].PointerToRawData+igdataent->Size > fsizel) {
470                   FIXME("overflow in PE lookup (%s has len %ld, have offset %ld), short file?\n",debugstr_w(lpszExeFileName),fsizel,igdataent->OffsetToData-pe_sections[j].VirtualAddress+pe_sections[j].PointerToRawData+igdataent->Size);
471                   goto end; /* failure */
472               }
473               igdata = peimage+(igdataent->OffsetToData-pe_sections[j].VirtualAddress+pe_sections[j].PointerToRawData);
474             }
475
476             if (!igdata) 
477             {
478               FIXME("no matching real address for icongroup!\n");
479               goto end; /* failure */
480             }
481             RetPtr[i] = (HICON)LookupIconIdFromDirectoryEx(igdata, TRUE, cxDesired, cyDesired, LR_DEFAULTCOLOR);
482           }
483
484           if (!(iconresdir=find_entry_by_id(rootresdir,LOWORD(RT_ICONW),rootresdir)))
485           {
486             WARN("No Iconresourcedirectory!\n");
487             goto end;           /* failure */
488           }
489
490           for (i=0; i<nIcons; i++) 
491           {
492               const IMAGE_RESOURCE_DIRECTORY *xresdir;
493               xresdir = find_entry_by_id(iconresdir,RetPtr[i],rootresdir);
494               xresdir = find_entry_default(xresdir,rootresdir);
495             idataent = (PIMAGE_RESOURCE_DATA_ENTRY)xresdir;
496             idata = NULL;
497
498             /* map virtual to address in image */
499             for (j=0;j<pe_header->FileHeader.NumberOfSections;j++) 
500             {
501               if (idataent->OffsetToData < pe_sections[j].VirtualAddress)
502                 continue;
503               if (idataent->OffsetToData+idataent->Size > pe_sections[j].VirtualAddress+pe_sections[j].SizeOfRawData)
504                 continue;
505               idata = peimage+(idataent->OffsetToData-pe_sections[j].VirtualAddress+pe_sections[j].PointerToRawData);
506             }
507             if (!idata) 
508             {
509               WARN("no matching real address found for icondata!\n");
510               RetPtr[i]=0;
511               continue;
512             }
513             RetPtr[i] = (HICON) CreateIconFromResourceEx(idata,idataent->Size,TRUE,0x00030000, cxDesired, cyDesired, LR_DEFAULTCOLOR);
514           }
515           hRet = S_OK;  /* return first icon */
516         }                       /* if(sig == IMAGE_NT_SIGNATURE) */
517
518 end:    UnmapViewOfFile(peimage);       /* success */
519         return hRet;
520 }
521
522 /***********************************************************************
523  *           PrivateExtractIconsW                       [USER32.@]
524  *
525  * NOTES
526  *  nIndex = 1: a small and a large icon are extracted. 
527  *  the higher word of sizeXY contains the size of the small icon, the lower
528  *  word the size of the big icon. phicon points to HICON[2].
529  *
530  * RETURNS
531  *  nIcons > 0: HRESULT
532  *  nIcons = 0: the number of icons
533  */
534
535 HRESULT WINAPI PrivateExtractIconsW ( 
536         LPCWSTR lpwstrFile,
537         int nIndex,
538         DWORD sizeX,
539         DWORD sizeY,
540         HICON * phicon, /* [???] NOTE: HICON* */
541         DWORD w,        /* [in] NOTE: 0 */
542         UINT nIcons,
543         DWORD y )       /* [in] NOTE: 0x80 maybe LR_* constant */
544 {
545         DWORD ret;
546         TRACE("%s 0x%08x 0x%08lx 0x%08lx %p 0x%08lx 0x%08x 0x%08lx\n",
547         debugstr_w(lpwstrFile),nIndex, sizeX ,sizeY ,phicon,w,nIcons,y );
548
549         if ((nIcons == 2) && HIWORD(sizeX) && HIWORD(sizeY))
550         {
551           ret = ICO_ExtractIconExW(lpwstrFile, phicon, nIndex, 1, sizeX & 0xffff, sizeY & 0xffff );
552           if (!SUCCEEDED(ret)) return ret;
553           ret = ICO_ExtractIconExW(lpwstrFile, phicon+1, nIndex, 1, (sizeX>>16) & 0xffff, (sizeY>>16) & 0xffff );
554         } else
555           ret = ICO_ExtractIconExW(lpwstrFile, phicon, nIndex, nIcons, sizeX & 0xffff, sizeY & 0xffff );
556         return ret;
557 }
558
559 /***********************************************************************
560  *           PrivateExtractIconsA                       [USER32.@]
561  */
562
563 HRESULT WINAPI PrivateExtractIconsA ( 
564         LPCSTR lpstrFile,
565         INT nIndex,
566         DWORD sizeX,
567         DWORD sizeY,
568         HICON * phicon,
569         DWORD w,        /* [in] NOTE: 0 */
570         UINT  nIcons,
571         DWORD y )       /* [in] NOTE: 0x80 */
572 {
573         DWORD ret;
574         INT len = MultiByteToWideChar( CP_ACP, 0, lpstrFile, -1, NULL, 0 );
575         LPWSTR lpwstrFile = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
576
577         MultiByteToWideChar( CP_ACP, 0, lpstrFile, -1, lpwstrFile, len );
578         ret = PrivateExtractIconsW(
579                 lpwstrFile, nIndex, sizeX, sizeY, phicon, w, nIcons, y
580         );
581
582         HeapFree(GetProcessHeap(), 0, lpwstrFile);
583         return ret;
584 }
585
586 /***********************************************************************
587  *           PrivateExtractIconExW                      [USER32.@]
588  * NOTES
589  *  if nIcons = -1 it returns the number of icons in any case !!!
590  */
591 HRESULT WINAPI PrivateExtractIconExW (
592         LPCWSTR lpwstrFile,
593         DWORD nIndex,
594         HICON * phIconLarge,
595         HICON * phIconSmall,
596         UINT nIcons )
597 {
598         DWORD cyicon, cysmicon, cxicon, cxsmicon;
599         HRESULT ret = 0;
600
601         TRACE("%s 0x%08lx %p %p 0x%08x\n",
602         debugstr_w(lpwstrFile),nIndex,phIconLarge, phIconSmall, nIcons);
603         
604         if (nIndex == 1 && phIconSmall && phIconLarge)
605         {
606           HICON hIcon[2];
607           cxicon = GetSystemMetrics(SM_CXICON);
608           cyicon = GetSystemMetrics(SM_CYICON);
609           cxsmicon = GetSystemMetrics(SM_CXSMICON);
610           cysmicon = GetSystemMetrics(SM_CYSMICON);
611         
612           ret = PrivateExtractIconsW ( lpwstrFile, nIndex, cxicon | (cxsmicon<<16),  cyicon | (cysmicon<<16),
613           (HICON*) &hIcon, 0, 2, 0 );
614           *phIconLarge = hIcon[0];
615           *phIconSmall = hIcon[1];
616           return ret;
617         }
618
619         if (nIndex != -1)
620         {
621           if (phIconSmall)
622           {
623             /* extract n small icons */
624             cxsmicon = GetSystemMetrics(SM_CXSMICON);
625             cysmicon = GetSystemMetrics(SM_CYSMICON);
626             ret = PrivateExtractIconsW ( lpwstrFile, nIndex, cxsmicon, cysmicon, phIconSmall, 0, nIcons, 0 );
627           }
628           if (phIconLarge )
629           {
630             /* extract n large icons */
631             cxicon = GetSystemMetrics(SM_CXICON);
632             cyicon = GetSystemMetrics(SM_CYICON);
633             ret = PrivateExtractIconsW ( lpwstrFile, nIndex, cxicon, cyicon, phIconLarge, 0, nIcons, 0 );
634           }
635           return ret;
636         }
637
638         /* get the number of icons */
639         return PrivateExtractIconsW ( lpwstrFile, 0, 0, 0, 0, 0, 0, 0 );
640 }
641
642 /***********************************************************************
643  *           PrivateExtractIconExA                      [USER32.@]
644  */
645 HRESULT WINAPI PrivateExtractIconExA (
646         LPCSTR lpstrFile,
647         DWORD nIndex,
648         HICON * phIconLarge,
649         HICON * phIconSmall,
650         UINT nIcons )
651 {
652         DWORD ret;
653         INT len = MultiByteToWideChar( CP_ACP, 0, lpstrFile, -1, NULL, 0 );
654         LPWSTR lpwstrFile = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
655
656         TRACE("%s 0x%08lx %p %p 0x%08x\n", lpstrFile, nIndex, phIconLarge, phIconSmall, nIcons);
657
658         MultiByteToWideChar( CP_ACP, 0, lpstrFile, -1, lpwstrFile, len );
659         ret = PrivateExtractIconExW(lpwstrFile,nIndex,phIconLarge, phIconSmall, nIcons);
660         HeapFree(GetProcessHeap(), 0, lpwstrFile);
661         return ret;
662 }
663