Fixed the displaying of the FOURCC codes in _dump_pixelformat.
[wine] / dlls / shell32 / shell.c
1 /*
2  *                              Shell Library Functions
3  *
4  *  1998 Marcus Meissner
5  */
6 #include <stdlib.h>
7 #include <string.h>
8 #include <unistd.h>
9 #include <ctype.h>
10 #include "windef.h"
11 #include "wingdi.h"
12 #include "wine/winuser16.h"
13 #include "wine/winbase16.h"
14 #include "wine/shell16.h"
15 #include "winerror.h"
16 #include "heap.h"
17 #include "ldt.h"
18 #include "module.h"
19 #include "neexe.h"
20 #include "dlgs.h"
21 #include "cursoricon.h"
22 #include "shellapi.h"
23 #include "shlobj.h"
24 #include "debugtools.h"
25 #include "winreg.h"
26 #include "syslevel.h"
27 #include "shlwapi.h"
28
29 DEFAULT_DEBUG_CHANNEL(shell);
30 DECLARE_DEBUG_CHANNEL(exec);
31
32 /* .ICO file ICONDIR definitions */
33
34 #include "pshpack1.h"
35
36 typedef struct
37 {
38     BYTE        bWidth;          /* Width, in pixels, of the image      */
39     BYTE        bHeight;         /* Height, in pixels, of the image     */
40     BYTE        bColorCount;     /* Number of colors in image (0 if >=8bpp) */
41     BYTE        bReserved;       /* Reserved ( must be 0)               */
42     WORD        wPlanes;         /* Color Planes                        */
43     WORD        wBitCount;       /* Bits per pixel                      */
44     DWORD       dwBytesInRes;    /* How many bytes in this resource?    */
45     DWORD       dwImageOffset;   /* Where in the file is this image?    */
46 } icoICONDIRENTRY, *LPicoICONDIRENTRY;
47
48 typedef struct
49 {
50     WORD            idReserved;   /* Reserved (must be 0)               */
51     WORD            idType;       /* Resource Type (1 for icons)        */
52     WORD            idCount;      /* How many images?                   */
53     icoICONDIRENTRY idEntries[1]; /* An entry for each image (idCount of 'em) */
54 } icoICONDIR, *LPicoICONDIR;
55
56 #include "poppack.h"
57
58 static const char*      lpstrMsgWndCreated = "OTHERWINDOWCREATED";
59 static const char*      lpstrMsgWndDestroyed = "OTHERWINDOWDESTROYED";
60 static const char*      lpstrMsgShellActivate = "ACTIVATESHELLWINDOW";
61
62 static HWND16   SHELL_hWnd = 0;
63 static HHOOK    SHELL_hHook = 0;
64 static UINT16   uMsgWndCreated = 0;
65 static UINT16   uMsgWndDestroyed = 0;
66 static UINT16   uMsgShellActivate = 0;
67 HINSTANCE16     SHELL_hInstance = 0;
68 HINSTANCE SHELL_hInstance32;
69 static int SHELL_Attach = 0;
70
71 /***********************************************************************
72  * SHELL_DllEntryPoint [SHELL.entry]
73  *
74  * Initialization code for shell.dll. Automatically loads the
75  * 32-bit shell32.dll to allow thunking up to 32-bit code.
76  *
77  * RETURNS:
78  */
79 BOOL WINAPI SHELL_DllEntryPoint(DWORD Reason, HINSTANCE16 hInst,
80                                 WORD ds, WORD HeapSize, DWORD res1, WORD res2)
81 {
82     TRACE("(%08lx, %04x, %04x, %04x, %08lx, %04x)\n",
83           Reason, hInst, ds, HeapSize, res1, res2);
84
85     switch(Reason)
86     {
87     case DLL_PROCESS_ATTACH:
88         SHELL_Attach++;
89         if (SHELL_hInstance)
90         {
91             ERR("shell.dll instantiated twice!\n");
92             /*
93              * We should return FALSE here, but that will break
94              * most apps that use CreateProcess because we do
95              * not yet support seperate address-spaces.
96              */
97             return TRUE;
98         }
99
100         SHELL_hInstance = hInst;
101         if(!SHELL_hInstance32)
102         {
103             if(!(SHELL_hInstance32 = LoadLibraryA("shell32.dll")))
104             {
105                 ERR("Could not load sibling shell32.dll\n");
106                 return FALSE;
107             }
108         }
109         break;
110
111     case DLL_PROCESS_DETACH:
112         if(!--SHELL_Attach)
113         {
114             SHELL_hInstance = 0;
115             if(SHELL_hInstance32)
116                 FreeLibrary(SHELL_hInstance32);
117         }
118         break;
119     }
120     return TRUE;
121 }
122
123 /*************************************************************************
124  *                              DragAcceptFiles16               [SHELL.9]
125  */
126 void WINAPI DragAcceptFiles16(HWND16 hWnd, BOOL16 b)
127 {
128   DragAcceptFiles(hWnd, b);
129 }
130
131 /*************************************************************************
132  *                              DragQueryFile16         [SHELL.11]
133  */
134 UINT16 WINAPI DragQueryFile16(
135         HDROP16 hDrop,
136         WORD wFile,
137         LPSTR lpszFile,
138         WORD wLength)
139 {
140         LPSTR lpDrop;
141         UINT i = 0;
142         LPDROPFILESTRUCT16 lpDropFileStruct = (LPDROPFILESTRUCT16) GlobalLock16(hDrop); 
143    
144         TRACE("(%04x, %x, %p, %u)\n", hDrop,wFile,lpszFile,wLength);
145     
146         if(!lpDropFileStruct) goto end;
147     
148         lpDrop = (LPSTR) lpDropFileStruct + lpDropFileStruct->wSize;
149         wFile = (wFile==0xffff) ? 0xffffffff : wFile;
150
151         while (i++ < wFile)
152         {
153           while (*lpDrop++); /* skip filename */
154           if (!*lpDrop) 
155           {
156             i = (wFile == 0xFFFFFFFF) ? i : 0; 
157             goto end;
158           }
159         }
160     
161         i = strlen(lpDrop);
162         i++;
163         if (!lpszFile ) goto end;   /* needed buffer size */
164         i = (wLength > i) ? i : wLength;
165         lstrcpynA (lpszFile,  lpDrop,  i);
166 end:
167         GlobalUnlock16(hDrop);
168         return i;
169 }
170
171 /*************************************************************************
172  *                              DragFinish16            [SHELL.12]
173  */
174 void WINAPI DragFinish16(HDROP16 h)
175 {
176     TRACE("\n");
177     GlobalFree16((HGLOBAL16)h);
178 }
179
180
181 /*************************************************************************
182  *                              DragQueryPoint16                [SHELL.13]
183  */
184 BOOL16 WINAPI DragQueryPoint16(HDROP16 hDrop, POINT16 *p)
185 {
186   LPDROPFILESTRUCT16 lpDropFileStruct;  
187   BOOL16           bRet;
188   TRACE("\n");
189   lpDropFileStruct = (LPDROPFILESTRUCT16) GlobalLock16(hDrop);
190   
191   memcpy(p,&lpDropFileStruct->ptMousePos,sizeof(POINT16));
192   bRet = lpDropFileStruct->fInNonClientArea;
193   
194   GlobalUnlock16(hDrop);
195   return bRet;
196 }
197
198 /*************************************************************************
199  *      SHELL_FindExecutable [Internal]
200  *
201  * Utility for code sharing between FindExecutable and ShellExecute
202  */
203 HINSTANCE SHELL_FindExecutable( LPCSTR lpFile, 
204                                          LPCSTR lpOperation,
205                                          LPSTR lpResult)
206 { char *extension = NULL; /* pointer to file extension */
207     char tmpext[5];         /* local copy to mung as we please */
208     char filetype[256];     /* registry name for this filetype */
209     LONG filetypelen=256;   /* length of above */
210     char command[256];      /* command from registry */
211     LONG commandlen=256;    /* This is the most DOS can handle :) */
212     char buffer[256];       /* Used to GetProfileString */
213     HINSTANCE retval=31;  /* default - 'No association was found' */
214     char *tok;              /* token pointer */
215     int i;                  /* random counter */
216     char xlpFile[256] = ""; /* result of SearchPath */
217
218   TRACE("%s\n", (lpFile != NULL?lpFile:"-") );
219
220     lpResult[0]='\0'; /* Start off with an empty return string */
221
222     /* trap NULL parameters on entry */
223     if (( lpFile == NULL ) || ( lpResult == NULL ) || ( lpOperation == NULL ))
224   { WARN_(exec)("(lpFile=%s,lpResult=%s,lpOperation=%s): NULL parameter\n",
225            lpFile, lpOperation, lpResult);
226         return 2; /* File not found. Close enough, I guess. */
227     }
228
229     if (SearchPathA( NULL, lpFile,".exe",sizeof(xlpFile),xlpFile,NULL))
230   { TRACE("SearchPathA returned non-zero\n");
231         lpFile = xlpFile;
232     }
233
234     /* First thing we need is the file's extension */
235     extension = strrchr( xlpFile, '.' ); /* Assume last "." is the one; */
236                                         /* File->Run in progman uses */
237                                         /* .\FILE.EXE :( */
238   TRACE("xlpFile=%s,extension=%s\n", xlpFile, extension);
239
240     if ((extension == NULL) || (extension == &xlpFile[strlen(xlpFile)]))
241   { WARN("Returning 31 - No association\n");
242         return 31; /* no association */
243     }
244
245     /* Make local copy & lowercase it for reg & 'programs=' lookup */
246     lstrcpynA( tmpext, extension, 5 );
247     CharLowerA( tmpext );
248   TRACE("%s file\n", tmpext);
249     
250     /* Three places to check: */
251     /* 1. win.ini, [windows], programs (NB no leading '.') */
252     /* 2. Registry, HKEY_CLASS_ROOT\<filetype>\shell\open\command */
253     /* 3. win.ini, [extensions], extension (NB no leading '.' */
254     /* All I know of the order is that registry is checked before */
255     /* extensions; however, it'd make sense to check the programs */
256     /* section first, so that's what happens here. */
257
258     /* See if it's a program - if GetProfileString fails, we skip this
259      * section. Actually, if GetProfileString fails, we've probably
260      * got a lot more to worry about than running a program... */
261     if ( GetProfileStringA("windows", "programs", "exe pif bat com",
262                                                   buffer, sizeof(buffer)) > 0 )
263   { for (i=0;i<strlen(buffer); i++) buffer[i]=tolower(buffer[i]);
264
265                 tok = strtok(buffer, " \t"); /* ? */
266                 while( tok!= NULL)
267                   {
268                         if (strcmp(tok, &tmpext[1])==0) /* have to skip the leading "." */
269                           {
270                                 strcpy(lpResult, xlpFile);
271                                 /* Need to perhaps check that the file has a path
272                                  * attached */
273         TRACE("found %s\n", lpResult);
274         return 33;
275
276                 /* Greater than 32 to indicate success FIXME According to the
277                  * docs, I should be returning a handle for the
278                  * executable. Does this mean I'm supposed to open the
279                  * executable file or something? More RTFM, I guess... */
280                           }
281                         tok=strtok(NULL, " \t");
282                   }
283           }
284
285     /* Check registry */
286     if (RegQueryValue16( HKEY_CLASSES_ROOT, tmpext, filetype,
287                          &filetypelen ) == ERROR_SUCCESS )
288     {
289         filetype[filetypelen]='\0';
290         TRACE("File type: %s\n", filetype);
291
292         /* Looking for ...buffer\shell\lpOperation\command */
293         strcat( filetype, "\\shell\\" );
294         strcat( filetype, lpOperation );
295         strcat( filetype, "\\command" );
296         
297         if (RegQueryValue16( HKEY_CLASSES_ROOT, filetype, command,
298                              &commandlen ) == ERROR_SUCCESS )
299         {
300             LPSTR tmp;
301             char param[256];
302             LONG paramlen = 256;
303
304
305             /* Get the parameters needed by the application 
306                from the associated ddeexec key */ 
307             tmp = strstr(filetype,"command");
308             tmp[0] = '\0';
309             strcat(filetype,"ddeexec");
310
311             if(RegQueryValue16( HKEY_CLASSES_ROOT, filetype, param,&paramlen ) == ERROR_SUCCESS)
312             {
313               strcat(command," ");
314               strcat(command,param);
315               commandlen += paramlen;
316             }
317
318             /* Is there a replace() function anywhere? */
319             command[commandlen]='\0';
320             strcpy( lpResult, command );
321             tok=strstr( lpResult, "%1" );
322             if (tok != NULL)
323             {
324                 tok[0]='\0'; /* truncate string at the percent */
325                 strcat( lpResult, xlpFile ); /* what if no dir in xlpFile? */
326                 tok=strstr( command, "%1" );
327                 if ((tok!=NULL) && (strlen(tok)>2))
328                 {
329                     strcat( lpResult, &tok[2] );
330                 }
331             }
332             retval=33; /* FIXME see above */
333         }
334     }
335     else /* Check win.ini */
336     {
337         /* Toss the leading dot */
338         extension++;
339         if ( GetProfileStringA( "extensions", extension, "", command,
340                                   sizeof(command)) > 0)
341           {
342                 if (strlen(command)!=0)
343                   {
344                         strcpy( lpResult, command );
345                         tok=strstr( lpResult, "^" ); /* should be ^.extension? */
346                         if (tok != NULL)
347                           {
348                                 tok[0]='\0';
349                                 strcat( lpResult, xlpFile ); /* what if no dir in xlpFile? */
350                                 tok=strstr( command, "^" ); /* see above */
351                                 if ((tok != NULL) && (strlen(tok)>5))
352                                   {
353                                         strcat( lpResult, &tok[5]);
354                                   }
355                           }
356                         retval=33; /* FIXME - see above */
357                   }
358           }
359         }
360
361     TRACE("returning %s\n", lpResult);
362     return retval;
363 }
364
365 /*************************************************************************
366  *                              ShellExecute16          [SHELL.20]
367  */
368 HINSTANCE16 WINAPI ShellExecute16( HWND16 hWnd, LPCSTR lpOperation,
369                                    LPCSTR lpFile, LPCSTR lpParameters,
370                                    LPCSTR lpDirectory, INT16 iShowCmd )
371 {   HINSTANCE16 retval=31;
372     char old_dir[1024];
373     char cmd[1024] = "";
374
375     TRACE("(%04x,'%s','%s','%s','%s',%x)\n",
376                 hWnd, lpOperation ? lpOperation:"<null>", lpFile ? lpFile:"<null>",
377                 lpParameters ? lpParameters : "<null>", 
378                 lpDirectory ? lpDirectory : "<null>", iShowCmd);
379
380     if (lpFile==NULL) return 0; /* should not happen */
381     if (lpOperation==NULL) /* default is open */
382       lpOperation="open";
383
384     if (lpDirectory)
385     { GetCurrentDirectoryA( sizeof(old_dir), old_dir );
386         SetCurrentDirectoryA( lpDirectory );
387     }
388
389     /* First try to execute lpFile with lpParameters directly */ 
390     strcpy(cmd,lpFile);
391     strcat(cmd,lpParameters ? lpParameters : "");
392
393     retval = WinExec16( cmd, iShowCmd );
394
395     /* Unable to execute lpFile directly
396        Check if we can match an application to lpFile */
397     if(retval < 32)
398     { 
399       cmd[0] = '\0';
400       retval = SHELL_FindExecutable( lpFile, lpOperation, cmd );
401
402       if (retval > 32)  /* Found */
403       {
404         if (lpParameters)
405         {
406             strcat(cmd," ");
407             strcat(cmd,lpParameters);
408         }
409         retval = WinExec16( cmd, iShowCmd );
410       }
411       else if(PathIsURLA((LPSTR)lpFile))    /* File not found, check for URL */
412       {
413         char lpstrProtocol[256];
414         LONG cmdlen = 512;
415         LPSTR lpstrRes;
416         INT iSize;
417       
418         lpstrRes = strchr(lpFile,':');
419         iSize = lpstrRes - lpFile;
420         
421         /* Looking for ...protocol\shell\lpOperation\command */
422         strncpy(lpstrProtocol,lpFile,iSize);
423         lpstrProtocol[iSize]='\0';
424         strcat( lpstrProtocol, "\\shell\\" );
425         strcat( lpstrProtocol, lpOperation );
426         strcat( lpstrProtocol, "\\command" );
427         
428         /* Remove File Protocol from lpFile */
429         /* In the case file://path/file     */
430         if(!strncasecmp(lpFile,"file",iSize))
431         {
432           lpFile += iSize;
433           while(*lpFile == ':') lpFile++;
434         }
435         
436
437         /* Get the application for the protocol and execute it */
438         if (RegQueryValue16( HKEY_CLASSES_ROOT, lpstrProtocol, cmd,
439                              &cmdlen ) == ERROR_SUCCESS )
440         {
441             LPSTR tok;
442             LPSTR tmp;
443             char param[256] = "";
444             LONG paramlen = 256;
445
446             /* Get the parameters needed by the application 
447                from the associated ddeexec key */ 
448             tmp = strstr(lpstrProtocol,"command");
449             tmp[0] = '\0';
450             strcat(lpstrProtocol,"ddeexec");
451
452             if(RegQueryValue16( HKEY_CLASSES_ROOT, lpstrProtocol, param,&paramlen ) == ERROR_SUCCESS)
453             {
454               strcat(cmd," ");
455               strcat(cmd,param);
456               cmdlen += paramlen;
457             }
458             
459             /* Is there a replace() function anywhere? */
460             cmd[cmdlen]='\0';
461
462             tok=strstr( cmd, "%1" );
463             if (tok != NULL)
464             {
465                 tok[0]='\0'; /* truncate string at the percent */
466                 strcat( cmd, lpFile ); /* what if no dir in xlpFile? */
467                 tok=strstr( cmd, "%1" );
468                 if ((tok!=NULL) && (strlen(tok)>2))
469                 {
470                     strcat( cmd, &tok[2] );
471                 }
472             }
473  
474             retval = WinExec16( cmd, iShowCmd );
475         }
476       }
477     /* Check if file specified is in the form www.??????.*** */
478       else if(!strncasecmp(lpFile,"www",3))
479       {
480         /* if so, append lpFile http:// and call ShellExecute */ 
481         char lpstrTmpFile[256] = "http://" ;
482         strcat(lpstrTmpFile,lpFile);
483         retval = ShellExecuteA(hWnd,lpOperation,lpstrTmpFile,NULL,NULL,0);
484       }
485     }
486     if (lpDirectory)
487       SetCurrentDirectoryA( old_dir );
488     return retval;
489 }
490
491 /*************************************************************************
492  *             FindExecutable16   (SHELL.21)
493  */
494 HINSTANCE16 WINAPI FindExecutable16( LPCSTR lpFile, LPCSTR lpDirectory,
495                                      LPSTR lpResult )
496 { return (HINSTANCE16)FindExecutableA( lpFile, lpDirectory, lpResult );
497 }
498
499
500 /*************************************************************************
501  *             AboutDlgProc16   (SHELL.33)
502  */
503 BOOL16 WINAPI AboutDlgProc16( HWND16 hWnd, UINT16 msg, WPARAM16 wParam,
504                                LPARAM lParam )
505 { return AboutDlgProc( hWnd, msg, wParam, lParam );
506 }
507
508
509 /*************************************************************************
510  *             ShellAbout16   (SHELL.22)
511  */
512 BOOL16 WINAPI ShellAbout16( HWND16 hWnd, LPCSTR szApp, LPCSTR szOtherStuff,
513                             HICON16 hIcon )
514 { return ShellAboutA( hWnd, szApp, szOtherStuff, hIcon );
515 }
516
517 /*************************************************************************
518  *                              SHELL_GetResourceTable
519  */
520 static DWORD SHELL_GetResourceTable(HFILE hFile,LPBYTE *retptr)
521 {       IMAGE_DOS_HEADER        mz_header;
522         char                    magic[4];
523         int                     size;
524
525         TRACE("\n");  
526
527         *retptr = NULL;
528         _llseek( hFile, 0, SEEK_SET );
529         if ((_lread(hFile,&mz_header,sizeof(mz_header)) != sizeof(mz_header)) || (mz_header.e_magic != IMAGE_DOS_SIGNATURE))
530         { /* .ICO file ? */
531           if (mz_header.e_cblp == 1) 
532           { /* ICONHEADER.idType, must be 1 */
533             *retptr = (LPBYTE)-1;
534             return 1;
535           }
536           else
537             return 0; /* failed */
538         }
539         _llseek( hFile, mz_header.e_lfanew, SEEK_SET );
540
541         if (_lread( hFile, magic, sizeof(magic) ) != sizeof(magic))
542           return 0;
543
544         _llseek( hFile, mz_header.e_lfanew, SEEK_SET);
545
546         if (*(DWORD*)magic  == IMAGE_NT_SIGNATURE)
547           return IMAGE_NT_SIGNATURE;
548
549         if (*(WORD*)magic == IMAGE_OS2_SIGNATURE)
550         { IMAGE_OS2_HEADER      ne_header;
551           LPBYTE                pTypeInfo = (LPBYTE)-1;
552
553           if (_lread(hFile,&ne_header,sizeof(ne_header))!=sizeof(ne_header))
554             return 0;
555
556           if (ne_header.ne_magic != IMAGE_OS2_SIGNATURE)
557             return 0;
558
559           size = ne_header.ne_restab - ne_header.ne_rsrctab;
560
561           if( size > sizeof(NE_TYPEINFO) )
562           { pTypeInfo = (BYTE*)HeapAlloc( GetProcessHeap(), 0, size);
563             if( pTypeInfo ) 
564             { _llseek(hFile, mz_header.e_lfanew+ne_header.ne_rsrctab, SEEK_SET);
565               if( _lread( hFile, (char*)pTypeInfo, size) != size )
566               { HeapFree( GetProcessHeap(), 0, pTypeInfo); 
567                 pTypeInfo = NULL;
568               }
569             }
570           }
571           *retptr = pTypeInfo;
572           return IMAGE_OS2_SIGNATURE;
573         }
574         return 0; /* failed */
575 }
576
577 /*************************************************************************
578  *                      SHELL_LoadResource
579  */
580 static HGLOBAL16 SHELL_LoadResource(HINSTANCE16 hInst, HFILE hFile, NE_NAMEINFO* pNInfo, WORD sizeShift)
581 {       BYTE*  ptr;
582         HGLOBAL16 handle = DirectResAlloc16( hInst, 0x10, (DWORD)pNInfo->length << sizeShift);
583
584         TRACE("\n");
585
586         if( (ptr = (BYTE*)GlobalLock16( handle )) )
587         { _llseek( hFile, (DWORD)pNInfo->offset << sizeShift, SEEK_SET);
588           _lread( hFile, (char*)ptr, pNInfo->length << sizeShift);
589           return handle;
590         }
591         return 0;
592 }
593
594 /*************************************************************************
595  *                      ICO_LoadIcon
596  */
597 static HGLOBAL16 ICO_LoadIcon(HINSTANCE16 hInst, HFILE hFile, LPicoICONDIRENTRY lpiIDE)
598 {       BYTE*  ptr;
599         HGLOBAL16 handle = DirectResAlloc16( hInst, 0x10, lpiIDE->dwBytesInRes);
600         TRACE("\n");
601         if( (ptr = (BYTE*)GlobalLock16( handle )) )
602         { _llseek( hFile, lpiIDE->dwImageOffset, SEEK_SET);
603           _lread( hFile, (char*)ptr, lpiIDE->dwBytesInRes);
604           return handle;
605         }
606         return 0;
607 }
608
609 /*************************************************************************
610  *                      ICO_GetIconDirectory
611  *
612  *  Read .ico file and build phony ICONDIR struct for GetIconID
613  */
614 static HGLOBAL16 ICO_GetIconDirectory(HINSTANCE16 hInst, HFILE hFile, LPicoICONDIR* lplpiID ) 
615 { WORD    id[3];  /* idReserved, idType, idCount */
616   LPicoICONDIR  lpiID;
617   int           i;
618  
619   TRACE("\n"); 
620   _llseek( hFile, 0, SEEK_SET );
621   if( _lread(hFile,(char*)id,sizeof(id)) != sizeof(id) ) return 0;
622
623   /* check .ICO header 
624    *
625    * - see http://www.microsoft.com/win32dev/ui/icons.htm
626    */
627
628   if( id[0] || id[1] != 1 || !id[2] ) return 0;
629
630   i = id[2]*sizeof(icoICONDIRENTRY) ;
631
632   lpiID = (LPicoICONDIR)HeapAlloc( GetProcessHeap(), 0, i + sizeof(id));
633
634   if( _lread(hFile,(char*)lpiID->idEntries,i) == i )
635   { HGLOBAL16 handle = DirectResAlloc16( hInst, 0x10,
636                                      id[2]*sizeof(CURSORICONDIRENTRY) + sizeof(id) );
637      if( handle ) 
638     { CURSORICONDIR*     lpID = (CURSORICONDIR*)GlobalLock16( handle );
639        lpID->idReserved = lpiID->idReserved = id[0];
640        lpID->idType = lpiID->idType = id[1];
641        lpID->idCount = lpiID->idCount = id[2];
642        for( i=0; i < lpiID->idCount; i++ )
643       { memcpy((void*)(lpID->idEntries + i), 
644                    (void*)(lpiID->idEntries + i), sizeof(CURSORICONDIRENTRY) - 2);
645             lpID->idEntries[i].wResId = i;
646          }
647       *lplpiID = lpiID;
648        return handle;
649      }
650   }
651   /* fail */
652
653   HeapFree( GetProcessHeap(), 0, lpiID);
654   return 0;
655 }
656
657 /*************************************************************************
658  *                      InternalExtractIcon             [SHELL.39]
659  *
660  * This abortion is called directly by Progman
661  */
662 HGLOBAL16 WINAPI InternalExtractIcon16(HINSTANCE16 hInstance,
663                                      LPCSTR lpszExeFileName, UINT16 nIconIndex, WORD n )
664 {       HGLOBAL16       hRet = 0;
665         HGLOBAL16*      RetPtr = NULL;
666         LPBYTE          pData;
667         OFSTRUCT        ofs;
668         DWORD           sig;
669         HFILE           hFile = OpenFile( lpszExeFileName, &ofs, OF_READ );
670         UINT16          iconDirCount = 0,iconCount = 0;
671         LPBYTE          peimage;
672         HANDLE  fmapping;
673         
674         TRACE("(%04x,file %s,start %d,extract %d\n", 
675                        hInstance, lpszExeFileName, nIconIndex, n);
676
677         if( hFile == HFILE_ERROR || !n )
678           return 0;
679
680         hRet = GlobalAlloc16( GMEM_FIXED | GMEM_ZEROINIT, sizeof(HICON16)*n);
681         RetPtr = (HICON16*)GlobalLock16(hRet);
682
683         *RetPtr = (n == 0xFFFF)? 0: 1;  /* error return values */
684
685         sig = SHELL_GetResourceTable(hFile,&pData);
686
687         if( sig==IMAGE_OS2_SIGNATURE || sig==1 ) /* .ICO file */
688         { HICON16        hIcon = 0;
689           NE_TYPEINFO* pTInfo = (NE_TYPEINFO*)(pData + 2);
690           NE_NAMEINFO* pIconStorage = NULL;
691           NE_NAMEINFO* pIconDir = NULL;
692           LPicoICONDIR lpiID = NULL;
693  
694           if( pData == (BYTE*)-1 )
695           { hIcon = ICO_GetIconDirectory(hInstance, hFile, &lpiID);     /* check for .ICO file */
696             if( hIcon ) 
697             { iconDirCount = 1; iconCount = lpiID->idCount; 
698             }
699           }
700           else while( pTInfo->type_id && !(pIconStorage && pIconDir) )
701           { if( pTInfo->type_id == NE_RSCTYPE_GROUP_ICON )      /* find icon directory and icon repository */
702             { iconDirCount = pTInfo->count;
703               pIconDir = ((NE_NAMEINFO*)(pTInfo + 1));
704               TRACE("\tfound directory - %i icon families\n", iconDirCount);
705             }
706             if( pTInfo->type_id == NE_RSCTYPE_ICON ) 
707             { iconCount = pTInfo->count;
708               pIconStorage = ((NE_NAMEINFO*)(pTInfo + 1));
709               TRACE("\ttotal icons - %i\n", iconCount);
710             }
711             pTInfo = (NE_TYPEINFO *)((char*)(pTInfo+1)+pTInfo->count*sizeof(NE_NAMEINFO));
712           }
713
714           /* load resources and create icons */
715
716           if( (pIconStorage && pIconDir) || lpiID )
717           { if( nIconIndex == (UINT16)-1 )
718             { RetPtr[0] = iconDirCount;
719             }
720             else if( nIconIndex < iconDirCount )
721             { UINT16   i, icon;
722               if( n > iconDirCount - nIconIndex ) 
723                 n = iconDirCount - nIconIndex;
724
725               for( i = nIconIndex; i < nIconIndex + n; i++ ) 
726               { /* .ICO files have only one icon directory */
727
728                 if( lpiID == NULL )
729                   hIcon = SHELL_LoadResource( hInstance, hFile, pIconDir + i, *(WORD*)pData );
730                 RetPtr[i-nIconIndex] = GetIconID16( hIcon, 3 );
731                 GlobalFree16(hIcon); 
732               }
733
734               for( icon = nIconIndex; icon < nIconIndex + n; icon++ )
735               { hIcon = 0;
736                 if( lpiID )
737                 { hIcon = ICO_LoadIcon( hInstance, hFile, lpiID->idEntries + RetPtr[icon-nIconIndex]);
738                 }
739                 else
740                 { for( i = 0; i < iconCount; i++ )
741                   { if( pIconStorage[i].id == (RetPtr[icon-nIconIndex] | 0x8000) )
742                     { hIcon = SHELL_LoadResource( hInstance, hFile, pIconStorage + i,*(WORD*)pData );
743                     }
744                   }
745                 }
746                 if( hIcon )
747                 { RetPtr[icon-nIconIndex] = LoadIconHandler16( hIcon, TRUE ); 
748                   FarSetOwner16( RetPtr[icon-nIconIndex], GetExePtr(hInstance) );
749                 }
750                 else
751                 { RetPtr[icon-nIconIndex] = 0;
752                 }
753               }
754             }
755           }
756           if( lpiID ) 
757             HeapFree( GetProcessHeap(), 0, lpiID);
758           else 
759             HeapFree( GetProcessHeap(), 0, pData);
760         } 
761
762         if( sig == IMAGE_NT_SIGNATURE)
763         { LPBYTE                idata,igdata;
764           PIMAGE_DOS_HEADER     dheader;
765           PIMAGE_NT_HEADERS     pe_header;
766           PIMAGE_SECTION_HEADER pe_sections;
767           PIMAGE_RESOURCE_DIRECTORY     rootresdir,iconresdir,icongroupresdir;
768           PIMAGE_RESOURCE_DATA_ENTRY    idataent,igdataent;
769           int                   i,j;
770           PIMAGE_RESOURCE_DIRECTORY_ENTRY       xresent;
771           CURSORICONDIR         **cids;
772         
773           fmapping = CreateFileMappingA(hFile,NULL,PAGE_READONLY|SEC_COMMIT,0,0,NULL);
774           if (fmapping == 0) 
775           { /* FIXME, INVALID_HANDLE_VALUE? */
776             WARN("failed to create filemap.\n");
777             hRet = 0;
778             goto end_2; /* failure */
779           }
780           peimage = MapViewOfFile(fmapping,FILE_MAP_READ,0,0,0);
781           if (!peimage) 
782           { WARN("failed to mmap filemap.\n");
783             hRet = 0;
784             goto end_2; /* failure */
785           }
786           dheader = (PIMAGE_DOS_HEADER)peimage;
787
788           /* it is a pe header, SHELL_GetResourceTable checked that */
789           pe_header = (PIMAGE_NT_HEADERS)(peimage+dheader->e_lfanew);
790
791           /* probably makes problems with short PE headers... but I haven't seen 
792           * one yet... 
793           */
794           pe_sections = (PIMAGE_SECTION_HEADER)(((char*)pe_header)+sizeof(*pe_header));
795           rootresdir = NULL;
796
797           for (i=0;i<pe_header->FileHeader.NumberOfSections;i++) 
798           { if (pe_sections[i].Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
799               continue;
800             /* FIXME: doesn't work when the resources are not in a seperate section */
801             if (pe_sections[i].VirtualAddress == pe_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress) 
802             { rootresdir = (PIMAGE_RESOURCE_DIRECTORY)((char*)peimage+pe_sections[i].PointerToRawData);
803               break;
804             }
805           }
806
807           if (!rootresdir) 
808           { WARN("haven't found section for resource directory.\n");
809             goto end_4; /* failure */
810           }
811
812           icongroupresdir = GetResDirEntryW(rootresdir,RT_GROUP_ICONW, (DWORD)rootresdir,FALSE);
813
814           if (!icongroupresdir) 
815           { WARN("No Icongroupresourcedirectory!\n");
816             goto end_4; /* failure */
817           }
818
819           iconDirCount = icongroupresdir->NumberOfNamedEntries+icongroupresdir->NumberOfIdEntries;
820
821           if( nIconIndex == (UINT16)-1 ) 
822           { RetPtr[0] = iconDirCount;
823             goto end_3; /* success */
824           }
825
826           if (nIconIndex >= iconDirCount) 
827           { WARN("nIconIndex %d is larger than iconDirCount %d\n",nIconIndex,iconDirCount);
828             GlobalFree16(hRet);
829             goto end_4; /* failure */
830           }
831
832           cids = (CURSORICONDIR**)HeapAlloc(GetProcessHeap(),0,n*sizeof(CURSORICONDIR*));
833                 
834           /* caller just wanted the number of entries */
835           xresent = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(icongroupresdir+1);
836
837           /* assure we don't get too much ... */
838           if( n > iconDirCount - nIconIndex ) 
839           { n = iconDirCount - nIconIndex;
840           }
841
842           /* starting from specified index ... */
843           xresent = xresent+nIconIndex;
844
845           for (i=0;i<n;i++,xresent++) 
846           { CURSORICONDIR       *cid;
847             PIMAGE_RESOURCE_DIRECTORY   resdir;
848
849             /* go down this resource entry, name */
850             resdir = (PIMAGE_RESOURCE_DIRECTORY)((DWORD)rootresdir+(xresent->u2.s.OffsetToDirectory));
851
852             /* default language (0) */
853             resdir = GetResDirEntryW(resdir,(LPWSTR)0,(DWORD)rootresdir,TRUE);
854             igdataent = (PIMAGE_RESOURCE_DATA_ENTRY)resdir;
855
856             /* lookup address in mapped image for virtual address */
857             igdata = NULL;
858
859             for (j=0;j<pe_header->FileHeader.NumberOfSections;j++) 
860             { if (igdataent->OffsetToData < pe_sections[j].VirtualAddress)
861                 continue;
862               if (igdataent->OffsetToData+igdataent->Size > pe_sections[j].VirtualAddress+pe_sections[j].SizeOfRawData)
863                 continue;
864               igdata = peimage+(igdataent->OffsetToData-pe_sections[j].VirtualAddress+pe_sections[j].PointerToRawData);
865             }
866
867             if (!igdata) 
868             { WARN("no matching real address for icongroup!\n");
869               goto end_4;       /* failure */
870             }
871             /* found */
872             cid = (CURSORICONDIR*)igdata;
873             cids[i] = cid;
874             RetPtr[i] = LookupIconIdFromDirectoryEx(igdata,TRUE,GetSystemMetrics(SM_CXICON),GetSystemMetrics(SM_CYICON),0);
875           }
876
877           iconresdir=GetResDirEntryW(rootresdir,RT_ICONW,(DWORD)rootresdir,FALSE);
878
879           if (!iconresdir) 
880           { WARN("No Iconresourcedirectory!\n");
881             goto end_4; /* failure */
882           }
883
884           for (i=0;i<n;i++) 
885           { PIMAGE_RESOURCE_DIRECTORY   xresdir;
886             xresdir = GetResDirEntryW(iconresdir,(LPWSTR)(DWORD)RetPtr[i],(DWORD)rootresdir,FALSE);
887             xresdir = GetResDirEntryW(xresdir,(LPWSTR)0,(DWORD)rootresdir,TRUE);
888             idataent = (PIMAGE_RESOURCE_DATA_ENTRY)xresdir;
889             idata = NULL;
890
891             /* map virtual to address in image */
892             for (j=0;j<pe_header->FileHeader.NumberOfSections;j++) 
893             { if (idataent->OffsetToData < pe_sections[j].VirtualAddress)
894                 continue;
895               if (idataent->OffsetToData+idataent->Size > pe_sections[j].VirtualAddress+pe_sections[j].SizeOfRawData)
896                 continue;
897               idata = peimage+(idataent->OffsetToData-pe_sections[j].VirtualAddress+pe_sections[j].PointerToRawData);
898             }
899             if (!idata) 
900             { WARN("no matching real address found for icondata!\n");
901               RetPtr[i]=0;
902               continue;
903             }
904             RetPtr[i] = CreateIconFromResourceEx(idata,idataent->Size,TRUE,0x00030000,GetSystemMetrics(SM_CXICON),GetSystemMetrics(SM_CYICON),0);
905           }
906           goto end_3;   /* sucess */
907         }
908         goto end_1;     /* return array with icon handles */
909
910 /* cleaning up (try & catch would be nicer) */
911 end_4:  hRet = 0;       /* failure */
912 end_3:  UnmapViewOfFile(peimage);       /* success */
913 end_2:  CloseHandle(fmapping);
914 end_1:  _lclose( hFile);
915         return hRet;
916 }
917
918 /*************************************************************************
919  *             ExtractIcon16   (SHELL.34)
920  */
921 HICON16 WINAPI ExtractIcon16( HINSTANCE16 hInstance, LPCSTR lpszExeFileName,
922         UINT16 nIconIndex )
923 {   TRACE("\n");
924     return ExtractIconA( hInstance, lpszExeFileName, nIconIndex );
925 }
926
927 /*************************************************************************
928  *             ExtractIconEx16   (SHELL.40)
929  */
930 HICON16 WINAPI ExtractIconEx16(
931         LPCSTR lpszFile, INT16 nIconIndex, HICON16 *phiconLarge,
932         HICON16 *phiconSmall, UINT16 nIcons
933 ) {
934     HICON       *ilarge,*ismall;
935     UINT16      ret;
936     int         i;
937
938     if (phiconLarge)
939         ilarge = (HICON*)HeapAlloc(GetProcessHeap(),0,nIcons*sizeof(HICON));
940     else
941         ilarge = NULL;
942     if (phiconSmall)
943         ismall = (HICON*)HeapAlloc(GetProcessHeap(),0,nIcons*sizeof(HICON));
944     else
945         ismall = NULL;
946     ret = ExtractIconExA(lpszFile,nIconIndex,ilarge,ismall,nIcons);
947     if (ilarge) {
948         for (i=0;i<nIcons;i++)
949             phiconLarge[i]=ilarge[i];
950         HeapFree(GetProcessHeap(),0,ilarge);
951     }
952     if (ismall) {
953         for (i=0;i<nIcons;i++)
954             phiconSmall[i]=ismall[i];
955         HeapFree(GetProcessHeap(),0,ismall);
956     }
957     return ret;
958 }
959
960 /*************************************************************************
961  *                              ExtractAssociatedIconA
962  * 
963  * Return icon for given file (either from file itself or from associated
964  * executable) and patch parameters if needed.
965  */
966 HICON WINAPI ExtractAssociatedIconA(HINSTANCE hInst, LPSTR lpIconPath, LPWORD lpiIcon)
967 {       TRACE("\n");
968         return ExtractAssociatedIcon16(hInst,lpIconPath,lpiIcon);
969 }
970
971 /*************************************************************************
972  *                              ExtractAssociatedIcon   [SHELL.36]
973  * 
974  * Return icon for given file (either from file itself or from associated
975  * executable) and patch parameters if needed.
976  */
977 HICON16 WINAPI ExtractAssociatedIcon16(HINSTANCE16 hInst, LPSTR lpIconPath, LPWORD lpiIcon)
978 {       HICON16 hIcon;
979         WORD wDummyIcon = 0;
980
981         TRACE("\n");
982
983         if(lpiIcon == NULL)
984             lpiIcon = &wDummyIcon;
985
986         hIcon = ExtractIcon16(hInst, lpIconPath, *lpiIcon);
987
988         if( hIcon < 2 )
989         { if( hIcon == 1 ) /* no icons found in given file */
990           { char  tempPath[0x80];
991             UINT16  uRet = FindExecutable16(lpIconPath,NULL,tempPath);
992
993             if( uRet > 32 && tempPath[0] )
994             { strcpy(lpIconPath,tempPath);
995               hIcon = ExtractIcon16(hInst, lpIconPath, *lpiIcon);
996               if( hIcon > 2 ) 
997                 return hIcon;
998             }
999             else hIcon = 0;
1000           }
1001
1002           if( hIcon == 1 ) 
1003             *lpiIcon = 2;   /* MSDOS icon - we found .exe but no icons in it */
1004           else
1005             *lpiIcon = 6;   /* generic icon - found nothing */
1006
1007           GetModuleFileName16(hInst, lpIconPath, 0x80);
1008           hIcon = LoadIcon16( hInst, MAKEINTRESOURCE16(*lpiIcon));
1009         }
1010         return hIcon;
1011 }
1012
1013 /*************************************************************************
1014  *                              FindEnvironmentString   [SHELL.38]
1015  *
1016  * Returns a pointer into the DOS environment... Ugh.
1017  */
1018 LPSTR SHELL_FindString(LPSTR lpEnv, LPCSTR entry)
1019 { UINT16 l;
1020
1021   TRACE("\n");
1022
1023   l = strlen(entry); 
1024   for( ; *lpEnv ; lpEnv+=strlen(lpEnv)+1 )
1025   { if( lstrncmpiA(lpEnv, entry, l) ) 
1026       continue;
1027         if( !*(lpEnv+l) )
1028             return (lpEnv + l);                 /* empty entry */
1029         else if ( *(lpEnv+l)== '=' )
1030             return (lpEnv + l + 1);
1031     }
1032     return NULL;
1033 }
1034
1035 SEGPTR WINAPI FindEnvironmentString16(LPSTR str)
1036 { SEGPTR  spEnv;
1037   LPSTR lpEnv,lpString;
1038   TRACE("\n");
1039     
1040   spEnv = GetDOSEnvironment16();
1041
1042   lpEnv = (LPSTR)PTR_SEG_TO_LIN(spEnv);
1043   lpString = (spEnv)?SHELL_FindString(lpEnv, str):NULL; 
1044
1045     if( lpString )              /*  offset should be small enough */
1046         return spEnv + (lpString - lpEnv);
1047     return (SEGPTR)NULL;
1048 }
1049
1050 /*************************************************************************
1051  *                              DoEnvironmentSubst      [SHELL.37]
1052  *
1053  * Replace %KEYWORD% in the str with the value of variable KEYWORD
1054  * from "DOS" environment.
1055  */
1056 DWORD WINAPI DoEnvironmentSubst16(LPSTR str,WORD length)
1057 {
1058   LPSTR   lpEnv = (LPSTR)PTR_SEG_TO_LIN(GetDOSEnvironment16());
1059   LPSTR   lpBuffer = (LPSTR)HeapAlloc( GetProcessHeap(), 0, length);
1060   LPSTR   lpstr = str;
1061   LPSTR   lpbstr = lpBuffer;
1062
1063   CharToOemA(str,str);
1064
1065   TRACE("accept %s\n", str);
1066
1067   while( *lpstr && lpbstr - lpBuffer < length )
1068    {
1069      LPSTR lpend = lpstr;
1070
1071      if( *lpstr == '%' )
1072        {
1073           do { lpend++; } while( *lpend && *lpend != '%' );
1074           if( *lpend == '%' && lpend - lpstr > 1 )      /* found key */
1075             {
1076                LPSTR lpKey;
1077               *lpend = '\0';  
1078                lpKey = SHELL_FindString(lpEnv, lpstr+1);
1079                if( lpKey )                              /* found key value */
1080                  {
1081                    int l = strlen(lpKey);
1082
1083                    if( l > length - (lpbstr - lpBuffer) - 1 )
1084                      {
1085            WARN("-- Env subst aborted - string too short\n");
1086                       *lpend = '%';
1087                        break;
1088                      }
1089                    strcpy(lpbstr, lpKey);
1090                    lpbstr += l;
1091                  }
1092                else break;
1093               *lpend = '%';
1094                lpstr = lpend + 1;
1095             }
1096           else break;                                   /* back off and whine */
1097
1098           continue;
1099        } 
1100
1101      *lpbstr++ = *lpstr++;
1102    }
1103
1104  *lpbstr = '\0';
1105   if( lpstr - str == strlen(str) )
1106     {
1107       strncpy(str, lpBuffer, length);
1108       length = 1;
1109     }
1110   else
1111       length = 0;
1112
1113   TRACE("-- return %s\n", str);
1114
1115   OemToCharA(str,str);
1116   HeapFree( GetProcessHeap(), 0, lpBuffer);
1117
1118   /*  Return str length in the LOWORD
1119    *  and 1 in HIWORD if subst was successful.
1120    */
1121  return (DWORD)MAKELONG(strlen(str), length);
1122 }
1123
1124 /*************************************************************************
1125  *                              ShellHookProc           [SHELL.103]
1126  * System-wide WH_SHELL hook.
1127  */
1128 LRESULT WINAPI ShellHookProc16(INT16 code, WPARAM16 wParam, LPARAM lParam)
1129 {
1130     TRACE("%i, %04x, %08x\n", code, wParam, 
1131                                                       (unsigned)lParam );
1132     if( SHELL_hHook && SHELL_hWnd )
1133     {
1134         UINT16  uMsg = 0;
1135         switch( code )
1136         {
1137             case HSHELL_WINDOWCREATED:          uMsg = uMsgWndCreated;   break;
1138             case HSHELL_WINDOWDESTROYED:        uMsg = uMsgWndDestroyed; break;
1139             case HSHELL_ACTIVATESHELLWINDOW:    uMsg = uMsgShellActivate;
1140         }
1141         PostMessage16( SHELL_hWnd, uMsg, wParam, 0 );
1142     }
1143     return CallNextHookEx16( WH_SHELL, code, wParam, lParam );
1144 }
1145
1146 /*************************************************************************
1147  *                              RegisterShellHook       [SHELL.102]
1148  */
1149 BOOL WINAPI RegisterShellHook16(HWND16 hWnd, UINT16 uAction)
1150
1151     TRACE("%04x [%u]\n", hWnd, uAction );
1152
1153     switch( uAction )
1154     { 
1155     case 2:  /* register hWnd as a shell window */
1156         if( !SHELL_hHook )
1157         { 
1158             HMODULE16 hShell = GetModuleHandle16( "SHELL" );
1159             HOOKPROC16 hookProc = (HOOKPROC16)NE_GetEntryPoint( hShell, 103 );
1160             SHELL_hHook = SetWindowsHookEx16( WH_SHELL, hookProc, hShell, 0 );
1161             if ( SHELL_hHook )
1162             { 
1163                 uMsgWndCreated = RegisterWindowMessageA( lpstrMsgWndCreated );
1164                 uMsgWndDestroyed = RegisterWindowMessageA( lpstrMsgWndDestroyed );
1165                 uMsgShellActivate = RegisterWindowMessageA( lpstrMsgShellActivate );
1166             } 
1167             else 
1168                 WARN("-- unable to install ShellHookProc()!\n");
1169         }
1170
1171         if ( SHELL_hHook )
1172             return ((SHELL_hWnd = hWnd) != 0);
1173         break;
1174
1175     default:
1176         WARN("-- unknown code %i\n", uAction );
1177         SHELL_hWnd = 0; /* just in case */
1178     }
1179     return FALSE;
1180 }