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