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