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