INT21_GetFreeDiskSpace(): The drive parameter is found in the DL
[wine] / dlls / kernel / process.c
1 /*
2  * Win32 processes
3  *
4  * Copyright 1996, 1998 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <assert.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <locale.h>
28 #include <signal.h>
29 #include <stdio.h>
30
31 #include "wine/winbase16.h"
32 #include "wine/winuser16.h"
33 #include "ntstatus.h"
34 #include "thread.h"
35 #include "drive.h"
36 #include "file.h"
37 #include "module.h"
38 #include "options.h"
39 #include "kernel_private.h"
40 #include "wine/server.h"
41 #include "wine/unicode.h"
42 #include "wine/debug.h"
43
44 WINE_DEFAULT_DEBUG_CHANNEL(process);
45 WINE_DECLARE_DEBUG_CHANNEL(server);
46 WINE_DECLARE_DEBUG_CHANNEL(relay);
47
48 typedef struct
49 {
50     LPSTR lpEnvAddress;
51     LPSTR lpCmdLine;
52     LPSTR lpCmdShow;
53     DWORD dwReserved;
54 } LOADPARMS32;
55
56 static UINT process_error_mode;
57
58 static HANDLE main_exe_file;
59 static DWORD shutdown_flags = 0;
60 static DWORD shutdown_priority = 0x280;
61 static DWORD process_dword;
62 static BOOL oem_file_apis;
63
64 static unsigned int server_startticks;
65 int main_create_flags = 0;
66
67 /* Process flags */
68 #define PDB32_DEBUGGED      0x0001  /* Process is being debugged */
69 #define PDB32_WIN16_PROC    0x0008  /* Win16 process */
70 #define PDB32_DOS_PROC      0x0010  /* Dos process */
71 #define PDB32_CONSOLE_PROC  0x0020  /* Console process */
72 #define PDB32_FILE_APIS_OEM 0x0040  /* File APIs are OEM */
73 #define PDB32_WIN32S_PROC   0x8000  /* Win32s process */
74
75 static const WCHAR comW[] = {'.','c','o','m',0};
76 static const WCHAR batW[] = {'.','b','a','t',0};
77 static const WCHAR winevdmW[] = {'w','i','n','e','v','d','m','.','e','x','e',0};
78
79 extern void SHELL_LoadRegistry(void);
80 extern void VERSION_Init( const WCHAR *appname );
81 extern void MODULE_InitLoadPath(void);
82 extern void LOCALE_Init(void);
83
84 /***********************************************************************
85  *           contains_path
86  */
87 inline static int contains_path( LPCWSTR name )
88 {
89     return ((*name && (name[1] == ':')) || strchrW(name, '/') || strchrW(name, '\\'));
90 }
91
92
93 /***************************************************************************
94  *      get_builtin_path
95  *
96  * Get the path of a builtin module when the native file does not exist.
97  */
98 static BOOL get_builtin_path( const WCHAR *libname, const WCHAR *ext, WCHAR *filename, UINT size )
99 {
100     WCHAR *file_part;
101     WCHAR sysdir[MAX_PATH];
102     UINT len = GetSystemDirectoryW( sysdir, MAX_PATH );
103
104     if (contains_path( libname ))
105     {
106         if (RtlGetFullPathName_U( libname, size * sizeof(WCHAR),
107                                   filename, &file_part ) > size * sizeof(WCHAR))
108             return FALSE;  /* too long */
109
110         if (strncmpiW( filename, sysdir, len ) || filename[len] != '\\')
111             return FALSE;
112         while (filename[len] == '\\') len++;
113         if (filename + len != file_part) return FALSE;
114     }
115     else
116     {
117         if (strlenW(libname) + len + 2 >= size) return FALSE;  /* too long */
118         memcpy( filename, sysdir, len * sizeof(WCHAR) );
119         file_part = filename + len;
120         if (file_part > filename && file_part[-1] != '\\') *file_part++ = '\\';
121         strcpyW( file_part, libname );
122     }
123     if (ext && !strchrW( file_part, '.' ))
124     {
125         if (file_part + strlenW(file_part) + strlenW(ext) + 1 > filename + size)
126             return FALSE;  /* too long */
127         strcatW( file_part, ext );
128     }
129     return TRUE;
130 }
131
132
133 /***********************************************************************
134  *           open_builtin_exe_file
135  *
136  * Open an exe file for a builtin exe.
137  */
138 static void *open_builtin_exe_file( const WCHAR *name, char *error, int error_size,
139                                     int test_only, int *file_exists )
140 {
141     char exename[MAX_PATH];
142     WCHAR *p;
143     UINT i, len;
144
145     if ((p = strrchrW( name, '/' ))) name = p + 1;
146     if ((p = strrchrW( name, '\\' ))) name = p + 1;
147
148     /* we don't want to depend on the current codepage here */
149     len = strlenW( name ) + 1;
150     if (len >= sizeof(exename)) return NULL;
151     for (i = 0; i < len; i++)
152     {
153         if (name[i] > 127) return NULL;
154         exename[i] = (char)name[i];
155         if (exename[i] >= 'A' && exename[i] <= 'Z') exename[i] += 'a' - 'A';
156     }
157     return wine_dll_load_main_exe( exename, error, error_size, test_only, file_exists );
158 }
159
160
161 /***********************************************************************
162  *           open_exe_file
163  *
164  * Open a specific exe file, taking load order into account.
165  * Returns the file handle or 0 for a builtin exe.
166  */
167 static HANDLE open_exe_file( const WCHAR *name )
168 {
169     enum loadorder_type loadorder[LOADORDER_NTYPES];
170     WCHAR buffer[MAX_PATH];
171     HANDLE handle;
172     int i, file_exists;
173
174     TRACE("looking for %s\n", debugstr_w(name) );
175
176     if ((handle = CreateFileW( name, GENERIC_READ, FILE_SHARE_READ,
177                                NULL, OPEN_EXISTING, 0, 0 )) == INVALID_HANDLE_VALUE)
178     {
179         /* file doesn't exist, check for builtin */
180         if (!contains_path( name )) goto error;
181         if (!get_builtin_path( name, NULL, buffer, sizeof(buffer) )) goto error;
182         name = buffer;
183     }
184
185     MODULE_GetLoadOrderW( loadorder, NULL, name );
186
187     for(i = 0; i < LOADORDER_NTYPES; i++)
188     {
189         if (loadorder[i] == LOADORDER_INVALID) break;
190         switch(loadorder[i])
191         {
192         case LOADORDER_DLL:
193             TRACE( "Trying native exe %s\n", debugstr_w(name) );
194             if (handle != INVALID_HANDLE_VALUE) return handle;
195             break;
196         case LOADORDER_BI:
197             TRACE( "Trying built-in exe %s\n", debugstr_w(name) );
198             open_builtin_exe_file( name, NULL, 0, 1, &file_exists );
199             if (file_exists)
200             {
201                 if (handle != INVALID_HANDLE_VALUE) CloseHandle(handle);
202                 return 0;
203             }
204         default:
205             break;
206         }
207     }
208     if (handle != INVALID_HANDLE_VALUE) CloseHandle(handle);
209
210  error:
211     SetLastError( ERROR_FILE_NOT_FOUND );
212     return INVALID_HANDLE_VALUE;
213 }
214
215
216 /***********************************************************************
217  *           find_exe_file
218  *
219  * Open an exe file, and return the full name and file handle.
220  * Returns FALSE if file could not be found.
221  * If file exists but cannot be opened, returns TRUE and set handle to INVALID_HANDLE_VALUE.
222  * If file is a builtin exe, returns TRUE and sets handle to 0.
223  */
224 static BOOL find_exe_file( const WCHAR *name, WCHAR *buffer, int buflen, HANDLE *handle )
225 {
226     static const WCHAR exeW[] = {'.','e','x','e',0};
227
228     enum loadorder_type loadorder[LOADORDER_NTYPES];
229     int i, file_exists;
230
231     TRACE("looking for %s\n", debugstr_w(name) );
232
233     if (!SearchPathW( NULL, name, exeW, buflen, buffer, NULL ) &&
234         !get_builtin_path( name, exeW, buffer, buflen ))
235     {
236         /* no builtin found, try native without extension in case it is a Unix app */
237
238         if (SearchPathW( NULL, name, NULL, buflen, buffer, NULL ))
239         {
240             TRACE( "Trying native/Unix binary %s\n", debugstr_w(buffer) );
241             if ((*handle = CreateFileW( buffer, GENERIC_READ, FILE_SHARE_READ,
242                                         NULL, OPEN_EXISTING, 0, 0 )) != INVALID_HANDLE_VALUE)
243                 return TRUE;
244         }
245         return FALSE;
246     }
247
248     MODULE_GetLoadOrderW( loadorder, NULL, buffer );
249
250     for(i = 0; i < LOADORDER_NTYPES; i++)
251     {
252         if (loadorder[i] == LOADORDER_INVALID) break;
253         switch(loadorder[i])
254         {
255         case LOADORDER_DLL:
256             TRACE( "Trying native exe %s\n", debugstr_w(buffer) );
257             if ((*handle = CreateFileW( buffer, GENERIC_READ, FILE_SHARE_READ,
258                                         NULL, OPEN_EXISTING, 0, 0 )) != INVALID_HANDLE_VALUE)
259                 return TRUE;
260             if (GetLastError() != ERROR_FILE_NOT_FOUND) return TRUE;
261             break;
262         case LOADORDER_BI:
263             TRACE( "Trying built-in exe %s\n", debugstr_w(buffer) );
264             open_builtin_exe_file( buffer, NULL, 0, 1, &file_exists );
265             if (file_exists)
266             {
267                 *handle = 0;
268                 return TRUE;
269             }
270             break;
271         default:
272             break;
273         }
274     }
275     SetLastError( ERROR_FILE_NOT_FOUND );
276     return FALSE;
277 }
278
279
280 /**********************************************************************
281  *           load_pe_exe
282  *
283  * Load a PE format EXE file.
284  */
285 static HMODULE load_pe_exe( const WCHAR *name, HANDLE file )
286 {
287     IMAGE_NT_HEADERS *nt;
288     HANDLE mapping;
289     void *module;
290     OBJECT_ATTRIBUTES attr;
291     LARGE_INTEGER size;
292     DWORD len = 0;
293     UINT drive_type;
294
295     attr.Length                   = sizeof(attr);
296     attr.RootDirectory            = 0;
297     attr.ObjectName               = NULL;
298     attr.Attributes               = 0;
299     attr.SecurityDescriptor       = NULL;
300     attr.SecurityQualityOfService = NULL;
301     size.QuadPart = 0;
302
303     if (NtCreateSection( &mapping, STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ,
304                          &attr, &size, 0, SEC_IMAGE, file ) != STATUS_SUCCESS)
305         return NULL;
306
307     module = NULL;
308     if (NtMapViewOfSection( mapping, GetCurrentProcess(), &module, 0, 0, &size, &len,
309                             ViewShare, 0, PAGE_READONLY ) != STATUS_SUCCESS)
310         return NULL;
311
312     NtClose( mapping );
313
314     /* virus check */
315     nt = RtlImageNtHeader( module );
316     if (nt->OptionalHeader.AddressOfEntryPoint)
317     {
318         if (!RtlImageRvaToSection( nt, module, nt->OptionalHeader.AddressOfEntryPoint ))
319             MESSAGE("VIRUS WARNING: PE module %s has an invalid entrypoint (0x%08lx) "
320                     "outside all sections (possibly infected by Tchernobyl/SpaceFiller virus)!\n",
321                     debugstr_w(name), nt->OptionalHeader.AddressOfEntryPoint );
322     }
323
324     drive_type = GetDriveTypeW( name );
325     /* don't keep the file handle open on removable media */
326     if (drive_type == DRIVE_REMOVABLE || drive_type == DRIVE_CDROM)
327     {
328         CloseHandle( main_exe_file );
329         main_exe_file = 0;
330     }
331
332     return module;
333 }
334
335 /***********************************************************************
336  *           build_initial_environment
337  *
338  * Build the Win32 environment from the Unix environment
339  */
340 static BOOL build_initial_environment( char **environ )
341 {
342     ULONG size = 1;
343     char **e;
344     WCHAR *p, *endptr;
345     void *ptr;
346
347     /* Compute the total size of the Unix environment */
348     for (e = environ; *e; e++)
349     {
350         if (!memcmp(*e, "PATH=", 5)) continue;
351         size += MultiByteToWideChar( CP_UNIXCP, 0, *e, -1, NULL, 0 );
352     }
353     size *= sizeof(WCHAR);
354
355     /* Now allocate the environment */
356     if (NtAllocateVirtualMemory(NtCurrentProcess(), &ptr, 0, &size,
357                                 MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE) != STATUS_SUCCESS)
358         return FALSE;
359
360     NtCurrentTeb()->Peb->ProcessParameters->Environment = p = ptr;
361     endptr = p + size / sizeof(WCHAR);
362
363     /* And fill it with the Unix environment */
364     for (e = environ; *e; e++)
365     {
366         char *str = *e;
367         /* skip Unix PATH and store WINEPATH as PATH */
368         if (!memcmp(str, "PATH=", 5)) continue;
369         if (!memcmp(str, "WINEPATH=", 9 )) str += 4;
370         MultiByteToWideChar( CP_UNIXCP, 0, str, -1, p, endptr - p );
371         p += strlenW(p) + 1;
372     }
373     *p = 0;
374     return TRUE;
375 }
376
377
378 /***********************************************************************
379  *              set_library_wargv
380  *
381  * Set the Wine library Unicode argv global variables.
382  */
383 static void set_library_wargv( char **argv )
384 {
385     int argc;
386     WCHAR *p;
387     WCHAR **wargv;
388     DWORD total = 0;
389
390     for (argc = 0; argv[argc]; argc++)
391         total += MultiByteToWideChar( CP_UNIXCP, 0, argv[argc], -1, NULL, 0 );
392
393     wargv = RtlAllocateHeap( GetProcessHeap(), 0,
394                              total * sizeof(WCHAR) + (argc + 1) * sizeof(*wargv) );
395     p = (WCHAR *)(wargv + argc + 1);
396     for (argc = 0; argv[argc]; argc++)
397     {
398         DWORD reslen = MultiByteToWideChar( CP_UNIXCP, 0, argv[argc], -1, p, total );
399         wargv[argc] = p;
400         p += reslen;
401         total -= reslen;
402     }
403     wargv[argc] = NULL;
404     __wine_main_wargv = wargv;
405 }
406
407
408 /***********************************************************************
409  *           build_command_line
410  *
411  * Build the command line of a process from the argv array.
412  *
413  * Note that it does NOT necessarily include the file name.
414  * Sometimes we don't even have any command line options at all.
415  *
416  * We must quote and escape characters so that the argv array can be rebuilt
417  * from the command line:
418  * - spaces and tabs must be quoted
419  *   'a b'   -> '"a b"'
420  * - quotes must be escaped
421  *   '"'     -> '\"'
422  * - if '\'s are followed by a '"', they must be doubled and followed by '\"',
423  *   resulting in an odd number of '\' followed by a '"'
424  *   '\"'    -> '\\\"'
425  *   '\\"'   -> '\\\\\"'
426  * - '\'s that are not followed by a '"' can be left as is
427  *   'a\b'   == 'a\b'
428  *   'a\\b'  == 'a\\b'
429  */
430 static BOOL build_command_line( WCHAR **argv )
431 {
432     int len;
433     WCHAR **arg;
434     LPWSTR p;
435     RTL_USER_PROCESS_PARAMETERS* rupp = NtCurrentTeb()->Peb->ProcessParameters;
436
437     if (rupp->CommandLine.Buffer) return TRUE; /* already got it from the server */
438
439     len = 0;
440     for (arg = argv; *arg; arg++)
441     {
442         int has_space,bcount;
443         WCHAR* a;
444
445         has_space=0;
446         bcount=0;
447         a=*arg;
448         if( !*a ) has_space=1;
449         while (*a!='\0') {
450             if (*a=='\\') {
451                 bcount++;
452             } else {
453                 if (*a==' ' || *a=='\t') {
454                     has_space=1;
455                 } else if (*a=='"') {
456                     /* doubling of '\' preceeding a '"',
457                      * plus escaping of said '"'
458                      */
459                     len+=2*bcount+1;
460                 }
461                 bcount=0;
462             }
463             a++;
464         }
465         len+=(a-*arg)+1 /* for the separating space */;
466         if (has_space)
467             len+=2; /* for the quotes */
468     }
469
470     if (!(rupp->CommandLine.Buffer = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR))))
471         return FALSE;
472
473     p = rupp->CommandLine.Buffer;
474     rupp->CommandLine.Length = (len - 1) * sizeof(WCHAR);
475     rupp->CommandLine.MaximumLength = len * sizeof(WCHAR);
476     for (arg = argv; *arg; arg++)
477     {
478         int has_space,has_quote;
479         WCHAR* a;
480
481         /* Check for quotes and spaces in this argument */
482         has_space=has_quote=0;
483         a=*arg;
484         if( !*a ) has_space=1;
485         while (*a!='\0') {
486             if (*a==' ' || *a=='\t') {
487                 has_space=1;
488                 if (has_quote)
489                     break;
490             } else if (*a=='"') {
491                 has_quote=1;
492                 if (has_space)
493                     break;
494             }
495             a++;
496         }
497
498         /* Now transfer it to the command line */
499         if (has_space)
500             *p++='"';
501         if (has_quote) {
502             int bcount;
503             WCHAR* a;
504
505             bcount=0;
506             a=*arg;
507             while (*a!='\0') {
508                 if (*a=='\\') {
509                     *p++=*a;
510                     bcount++;
511                 } else {
512                     if (*a=='"') {
513                         int i;
514
515                         /* Double all the '\\' preceeding this '"', plus one */
516                         for (i=0;i<=bcount;i++)
517                             *p++='\\';
518                         *p++='"';
519                     } else {
520                         *p++=*a;
521                     }
522                     bcount=0;
523                 }
524                 a++;
525             }
526         } else {
527             WCHAR* x = *arg;
528             while ((*p=*x++)) p++;
529         }
530         if (has_space)
531             *p++='"';
532         *p++=' ';
533     }
534     if (p > rupp->CommandLine.Buffer)
535         p--;  /* remove last space */
536     *p = '\0';
537
538     return TRUE;
539 }
540
541
542 /* make sure the unicode string doesn't point beyond the end pointer */
543 static inline void fix_unicode_string( UNICODE_STRING *str, char *end_ptr )
544 {
545     if ((char *)str->Buffer >= end_ptr)
546     {
547         str->Length = str->MaximumLength = 0;
548         str->Buffer = NULL;
549         return;
550     }
551     if ((char *)str->Buffer + str->MaximumLength > end_ptr)
552     {
553         str->MaximumLength = (end_ptr - (char *)str->Buffer) & ~(sizeof(WCHAR) - 1);
554     }
555     if (str->Length >= str->MaximumLength)
556     {
557         if (str->MaximumLength >= sizeof(WCHAR))
558             str->Length = str->MaximumLength - sizeof(WCHAR);
559         else
560             str->Length = str->MaximumLength = 0;
561     }
562 }
563
564
565 /***********************************************************************
566  *           init_user_process_params
567  *
568  * Fill the RTL_USER_PROCESS_PARAMETERS structure from the server.
569  */
570 static RTL_USER_PROCESS_PARAMETERS *init_user_process_params( size_t info_size )
571 {
572     void *ptr;
573     DWORD size;
574     NTSTATUS status;
575     RTL_USER_PROCESS_PARAMETERS *params;
576
577     size = info_size;
578     if ((status = NtAllocateVirtualMemory( NtCurrentProcess(), &ptr, NULL, &size,
579                                            MEM_COMMIT, PAGE_READWRITE )) != STATUS_SUCCESS)
580         return NULL;
581
582     SERVER_START_REQ( get_startup_info )
583     {
584         wine_server_set_reply( req, ptr, info_size );
585         wine_server_call( req );
586         info_size = wine_server_reply_size( reply );
587     }
588     SERVER_END_REQ;
589
590     params = ptr;
591     params->Size = info_size;
592     params->AllocationSize = size;
593
594     /* make sure the strings are valid */
595     fix_unicode_string( &params->CurrentDirectoryName, (char *)info_size );
596     fix_unicode_string( &params->DllPath, (char *)info_size );
597     fix_unicode_string( &params->ImagePathName, (char *)info_size );
598     fix_unicode_string( &params->CommandLine, (char *)info_size );
599     fix_unicode_string( &params->WindowTitle, (char *)info_size );
600     fix_unicode_string( &params->Desktop, (char *)info_size );
601     fix_unicode_string( &params->ShellInfo, (char *)info_size );
602     fix_unicode_string( &params->RuntimeInfo, (char *)info_size );
603
604     return RtlNormalizeProcessParams( params );
605 }
606
607
608 /***********************************************************************
609  *           process_init
610  *
611  * Main process initialisation code
612  */
613 static BOOL process_init( char *argv[], char **environ )
614 {
615     BOOL ret;
616     size_t info_size = 0;
617     RTL_USER_PROCESS_PARAMETERS *params;
618     PEB *peb = NtCurrentTeb()->Peb;
619     HANDLE hstdin, hstdout, hstderr;
620
621     setbuf(stdout,NULL);
622     setbuf(stderr,NULL);
623     setlocale(LC_CTYPE,"");
624
625     /* Retrieve startup info from the server */
626     SERVER_START_REQ( init_process )
627     {
628         req->peb      = peb;
629         req->ldt_copy = &wine_ldt_copy;
630         if ((ret = !wine_server_call_err( req )))
631         {
632             main_exe_file     = reply->exe_file;
633             main_create_flags = reply->create_flags;
634             info_size         = reply->info_size;
635             server_startticks = reply->server_start;
636             hstdin            = reply->hstdin;
637             hstdout           = reply->hstdout;
638             hstderr           = reply->hstderr;
639         }
640     }
641     SERVER_END_REQ;
642     if (!ret) return FALSE;
643
644     if (info_size == 0)
645     {
646         params = peb->ProcessParameters;
647
648         /* This is wine specific: we have no parent (we're started from unix)
649          * so, create a simple console with bare handles to unix stdio 
650          * input & output streams (aka simple console)
651          */
652         wine_server_fd_to_handle( 0, GENERIC_READ|SYNCHRONIZE,  TRUE, &params->hStdInput );
653         wine_server_fd_to_handle( 1, GENERIC_WRITE|SYNCHRONIZE, TRUE, &params->hStdOutput );
654         wine_server_fd_to_handle( 2, GENERIC_WRITE|SYNCHRONIZE, TRUE, &params->hStdError );
655
656         /* <hack: to be changed later on> */
657         params->CurrentDirectoryName.Length = 3 * sizeof(WCHAR);
658         params->CurrentDirectoryName.MaximumLength = RtlGetLongestNtPathLength() * sizeof(WCHAR);
659         params->CurrentDirectoryName.Buffer = RtlAllocateHeap( GetProcessHeap(), 0, params->CurrentDirectoryName.MaximumLength);
660         params->CurrentDirectoryName.Buffer[0] = 'C';
661         params->CurrentDirectoryName.Buffer[1] = ':';
662         params->CurrentDirectoryName.Buffer[2] = '\\';
663         params->CurrentDirectoryName.Buffer[3] = '\0';
664         /* </hack: to be changed later on> */
665     }
666     else
667     {
668         if (!(params = init_user_process_params( info_size ))) return FALSE;
669         peb->ProcessParameters = params;
670
671         /* convert value from server:
672          * + 0 => INVALID_HANDLE_VALUE
673          * + console handle need to be mapped
674          */
675         if (!hstdin)
676             hstdin = INVALID_HANDLE_VALUE;
677         else if (VerifyConsoleIoHandle(console_handle_map(hstdin)))
678             hstdin = console_handle_map(hstdin);
679
680         if (!hstdout)
681             hstdout = INVALID_HANDLE_VALUE;
682         else if (VerifyConsoleIoHandle(console_handle_map(hstdout)))
683             hstdout = console_handle_map(hstdout);
684
685         if (!hstderr)
686             hstderr = INVALID_HANDLE_VALUE;
687         else if (VerifyConsoleIoHandle(console_handle_map(hstderr)))
688             hstderr = console_handle_map(hstderr);
689
690         params->hStdInput  = hstdin;
691         params->hStdOutput = hstdout;
692         params->hStdError  = hstderr;
693     }
694
695     LOCALE_Init();
696
697     /* Copy the parent environment */
698     if (!build_initial_environment( environ )) return FALSE;
699
700     /* Parse command line arguments */
701     OPTIONS_ParseOptions( !info_size ? argv : NULL );
702
703     /* initialise DOS drives */
704     if (!DRIVE_Init()) return FALSE;
705
706     /* initialise DOS directories */
707     if (!DIR_Init()) return FALSE;
708
709     /* registry initialisation */
710     SHELL_LoadRegistry();
711
712     /* global boot finished, the rest is process-local */
713     SERVER_START_REQ( boot_done )
714     {
715         req->debug_level = TRACE_ON(server);
716         wine_server_call( req );
717     }
718     SERVER_END_REQ;
719
720     return TRUE;
721 }
722
723
724 /***********************************************************************
725  *           start_process
726  *
727  * Startup routine of a new process. Runs on the new process stack.
728  */
729 static void start_process( void *arg )
730 {
731     __TRY
732     {
733         PEB *peb = NtCurrentTeb()->Peb;
734         IMAGE_NT_HEADERS *nt;
735         LPTHREAD_START_ROUTINE entry;
736
737         LdrInitializeThunk( main_exe_file, CreateFileW, 0, 0 );
738
739         nt = RtlImageNtHeader( peb->ImageBaseAddress );
740         entry = (LPTHREAD_START_ROUTINE)((char *)peb->ImageBaseAddress +
741                                          nt->OptionalHeader.AddressOfEntryPoint);
742
743         if (TRACE_ON(relay))
744             DPRINTF( "%04lx:Starting process %s (entryproc=%p)\n", GetCurrentThreadId(),
745                      debugstr_w(peb->ProcessParameters->ImagePathName.Buffer), entry );
746
747         SetLastError( 0 );  /* clear error code */
748         if (peb->BeingDebugged) DbgBreakPoint();
749         ExitProcess( entry( peb ) );
750     }
751     __EXCEPT(UnhandledExceptionFilter)
752     {
753         TerminateThread( GetCurrentThread(), GetExceptionCode() );
754     }
755     __ENDTRY
756 }
757
758
759 /***********************************************************************
760  *           __wine_kernel_init
761  *
762  * Wine initialisation: load and start the main exe file.
763  */
764 void __wine_kernel_init(void)
765 {
766     WCHAR *main_exe_name, *p;
767     char error[1024];
768     DWORD stack_size = 0;
769     int file_exists;
770     PEB *peb = NtCurrentTeb()->Peb;
771
772     /* Initialize everything */
773     if (!process_init( __wine_main_argv, __wine_main_environ )) exit(1);
774     /* update argc in case options have been removed */
775     for (__wine_main_argc = 0; __wine_main_argv[__wine_main_argc]; __wine_main_argc++) /*nothing*/;
776
777     __wine_main_argv++;  /* remove argv[0] (wine itself) */
778     __wine_main_argc--;
779
780     if (!(main_exe_name = peb->ProcessParameters->ImagePathName.Buffer))
781     {
782         WCHAR buffer[MAX_PATH];
783         WCHAR exe_nameW[MAX_PATH];
784
785         if (!__wine_main_argv[0]) OPTIONS_Usage();
786
787         MultiByteToWideChar( CP_UNIXCP, 0, __wine_main_argv[0], -1, exe_nameW, MAX_PATH );
788         if (!find_exe_file( exe_nameW, buffer, MAX_PATH, &main_exe_file ))
789         {
790             MESSAGE( "wine: cannot find '%s'\n", __wine_main_argv[0] );
791             ExitProcess(1);
792         }
793         if (main_exe_file == INVALID_HANDLE_VALUE)
794         {
795             MESSAGE( "wine: cannot open %s\n", debugstr_w(main_exe_name) );
796             ExitProcess(1);
797         }
798         RtlCreateUnicodeString( &peb->ProcessParameters->ImagePathName, buffer );
799         main_exe_name = peb->ProcessParameters->ImagePathName.Buffer;
800     }
801
802     TRACE( "starting process name=%s file=%p argv[0]=%s\n",
803            debugstr_w(main_exe_name), main_exe_file, debugstr_a(__wine_main_argv[0]) );
804
805     MODULE_InitLoadPath();
806     VERSION_Init( main_exe_name );
807
808     if (!main_exe_file)  /* no file handle -> Winelib app */
809     {
810         TRACE( "starting Winelib app %s\n", debugstr_w(main_exe_name) );
811         if (open_builtin_exe_file( main_exe_name, error, sizeof(error), 0, &file_exists ))
812             goto found;
813         MESSAGE( "wine: cannot open builtin library for %s: %s\n",
814                  debugstr_w(main_exe_name), error );
815         ExitProcess(1);
816     }
817
818     switch( MODULE_GetBinaryType( main_exe_file ))
819     {
820     case BINARY_PE_EXE:
821         TRACE( "starting Win32 binary %s\n", debugstr_w(main_exe_name) );
822         if ((peb->ImageBaseAddress = load_pe_exe( main_exe_name, main_exe_file )))
823             goto found;
824         MESSAGE( "wine: could not load %s as Win32 binary\n", debugstr_w(main_exe_name) );
825         ExitProcess(1);
826     case BINARY_PE_DLL:
827         MESSAGE( "wine: %s is a DLL, not an executable\n", debugstr_w(main_exe_name) );
828         ExitProcess(1);
829     case BINARY_UNKNOWN:
830         /* check for .com extension */
831         if (!(p = strrchrW( main_exe_name, '.' )) || strcmpiW( p, comW ))
832         {
833             MESSAGE( "wine: cannot determine executable type for %s\n",
834                      debugstr_w(main_exe_name) );
835             ExitProcess(1);
836         }
837         /* fall through */
838     case BINARY_WIN16:
839     case BINARY_DOS:
840         TRACE( "starting Win16/DOS binary %s\n", debugstr_w(main_exe_name) );
841         CloseHandle( main_exe_file );
842         main_exe_file = 0;
843         __wine_main_argv--;
844         __wine_main_argc++;
845         __wine_main_argv[0] = "winevdm.exe";
846         if (open_builtin_exe_file( winevdmW, error, sizeof(error), 0, &file_exists ))
847             goto found;
848         MESSAGE( "wine: trying to run %s, cannot open builtin library for 'winevdm.exe': %s\n",
849                  debugstr_w(main_exe_name), error );
850         ExitProcess(1);
851     case BINARY_OS216:
852         MESSAGE( "wine: %s is an OS/2 binary, not supported\n", debugstr_w(main_exe_name) );
853         ExitProcess(1);
854     case BINARY_UNIX_EXE:
855         MESSAGE( "wine: %s is a Unix binary, not supported\n", debugstr_w(main_exe_name) );
856         ExitProcess(1);
857     case BINARY_UNIX_LIB:
858         {
859             DOS_FULL_NAME full_name;
860
861             TRACE( "starting Winelib app %s\n", debugstr_w(main_exe_name) );
862             CloseHandle( main_exe_file );
863             main_exe_file = 0;
864             if (DOSFS_GetFullName( main_exe_name, TRUE, &full_name ) &&
865                 wine_dlopen( full_name.long_name, RTLD_NOW, error, sizeof(error) ))
866             {
867                 static const WCHAR soW[] = {'.','s','o',0};
868                 if ((p = strrchrW( main_exe_name, '.' )) && !strcmpW( p, soW ))
869                 {
870                     *p = 0;
871                     /* update the unicode string */
872                     RtlInitUnicodeString( &peb->ProcessParameters->ImagePathName, main_exe_name );
873                 }
874                 goto found;
875             }
876             MESSAGE( "wine: could not load %s: %s\n", debugstr_w(main_exe_name), error );
877             ExitProcess(1);
878         }
879     }
880
881  found:
882     wine_free_pe_load_area();  /* the main binary is loaded, we don't need this anymore */
883
884     /* build command line */
885     set_library_wargv( __wine_main_argv );
886     if (!build_command_line( __wine_main_wargv )) goto error;
887
888     stack_size = RtlImageNtHeader(peb->ImageBaseAddress)->OptionalHeader.SizeOfStackReserve;
889
890     /* allocate main thread stack */
891     if (!THREAD_InitStack( NtCurrentTeb(), stack_size )) goto error;
892
893     /* switch to the new stack */
894     wine_switch_to_stack( start_process, NULL, NtCurrentTeb()->Tib.StackBase );
895
896  error:
897     ExitProcess( GetLastError() );
898 }
899
900
901 /***********************************************************************
902  *           build_argv
903  *
904  * Build an argv array from a command-line.
905  * 'reserved' is the number of args to reserve before the first one.
906  */
907 static char **build_argv( const WCHAR *cmdlineW, int reserved )
908 {
909     int argc;
910     char** argv;
911     char *arg,*s,*d,*cmdline;
912     int in_quotes,bcount,len;
913
914     len = WideCharToMultiByte( CP_UNIXCP, 0, cmdlineW, -1, NULL, 0, NULL, NULL );
915     if (!(cmdline = malloc(len))) return NULL;
916     WideCharToMultiByte( CP_UNIXCP, 0, cmdlineW, -1, cmdline, len, NULL, NULL );
917
918     argc=reserved+1;
919     bcount=0;
920     in_quotes=0;
921     s=cmdline;
922     while (1) {
923         if (*s=='\0' || ((*s==' ' || *s=='\t') && !in_quotes)) {
924             /* space */
925             argc++;
926             /* skip the remaining spaces */
927             while (*s==' ' || *s=='\t') {
928                 s++;
929             }
930             if (*s=='\0')
931                 break;
932             bcount=0;
933             continue;
934         } else if (*s=='\\') {
935             /* '\', count them */
936             bcount++;
937         } else if ((*s=='"') && ((bcount & 1)==0)) {
938             /* unescaped '"' */
939             in_quotes=!in_quotes;
940             bcount=0;
941         } else {
942             /* a regular character */
943             bcount=0;
944         }
945         s++;
946     }
947     argv=malloc(argc*sizeof(*argv));
948     if (!argv)
949         return NULL;
950
951     arg=d=s=cmdline;
952     bcount=0;
953     in_quotes=0;
954     argc=reserved;
955     while (*s) {
956         if ((*s==' ' || *s=='\t') && !in_quotes) {
957             /* Close the argument and copy it */
958             *d=0;
959             argv[argc++]=arg;
960
961             /* skip the remaining spaces */
962             do {
963                 s++;
964             } while (*s==' ' || *s=='\t');
965
966             /* Start with a new argument */
967             arg=d=s;
968             bcount=0;
969         } else if (*s=='\\') {
970             /* '\\' */
971             *d++=*s++;
972             bcount++;
973         } else if (*s=='"') {
974             /* '"' */
975             if ((bcount & 1)==0) {
976                 /* Preceeded by an even number of '\', this is half that
977                  * number of '\', plus a '"' which we discard.
978                  */
979                 d-=bcount/2;
980                 s++;
981                 in_quotes=!in_quotes;
982             } else {
983                 /* Preceeded by an odd number of '\', this is half that
984                  * number of '\' followed by a '"'
985                  */
986                 d=d-bcount/2-1;
987                 *d++='"';
988                 s++;
989             }
990             bcount=0;
991         } else {
992             /* a regular character */
993             *d++=*s++;
994             bcount=0;
995         }
996     }
997     if (*arg) {
998         *d='\0';
999         argv[argc++]=arg;
1000     }
1001     argv[argc]=NULL;
1002
1003     return argv;
1004 }
1005
1006
1007 /***********************************************************************
1008  *           alloc_env_string
1009  *
1010  * Allocate an environment string; helper for build_envp
1011  */
1012 static char *alloc_env_string( const char *name, const char *value )
1013 {
1014     char *ret = malloc( strlen(name) + strlen(value) + 1 );
1015     strcpy( ret, name );
1016     strcat( ret, value );
1017     return ret;
1018 }
1019
1020 /***********************************************************************
1021  *           build_envp
1022  *
1023  * Build the environment of a new child process.
1024  */
1025 static char **build_envp( const WCHAR *envW, const WCHAR *extra_envW )
1026 {
1027     const WCHAR *p;
1028     char **envp;
1029     char *env, *extra_env = NULL;
1030     int count = 0, length;
1031
1032     if (extra_envW)
1033     {
1034         for (p = extra_envW; *p; count++) p += strlenW(p) + 1;
1035         p++;
1036         length = WideCharToMultiByte( CP_UNIXCP, 0, extra_envW, p - extra_envW,
1037                                       NULL, 0, NULL, NULL );
1038         if ((extra_env = malloc( length )))
1039             WideCharToMultiByte( CP_UNIXCP, 0, extra_envW, p - extra_envW,
1040                                  extra_env, length, NULL, NULL );
1041     }
1042     for (p = envW; *p; count++) p += strlenW(p) + 1;
1043     p++;
1044     length = WideCharToMultiByte( CP_UNIXCP, 0, envW, p - envW, NULL, 0, NULL, NULL );
1045     if (!(env = malloc( length ))) return NULL;
1046     WideCharToMultiByte( CP_UNIXCP, 0, envW, p - envW, env, length, NULL, NULL );
1047
1048     count += 4;
1049
1050     if ((envp = malloc( count * sizeof(*envp) )))
1051     {
1052         char **envptr = envp;
1053         char *p;
1054
1055         /* first the extra strings */
1056         if (extra_env) for (p = extra_env; *p; p += strlen(p) + 1) *envptr++ = p;
1057         /* then put PATH, HOME and WINEPREFIX from the unix env */
1058         if ((p = getenv("PATH"))) *envptr++ = alloc_env_string( "PATH=", p );
1059         if ((p = getenv("HOME"))) *envptr++ = alloc_env_string( "HOME=", p );
1060         if ((p = getenv("WINEPREFIX"))) *envptr++ = alloc_env_string( "WINEPREFIX=", p );
1061         /* now put the Windows environment strings */
1062         for (p = env; *p; p += strlen(p) + 1)
1063         {
1064             if (!memcmp( p, "PATH=", 5 ))  /* store PATH as WINEPATH */
1065                 *envptr++ = alloc_env_string( "WINEPATH=", p + 5 );
1066             else if (memcmp( p, "HOME=", 5 ) &&
1067                      memcmp( p, "WINEPATH=", 9 ) &&
1068                      memcmp( p, "WINEPREFIX=", 11 )) *envptr++ = p;
1069         }
1070         *envptr = 0;
1071     }
1072     return envp;
1073 }
1074
1075
1076 /***********************************************************************
1077  *           fork_and_exec
1078  *
1079  * Fork and exec a new Unix binary, checking for errors.
1080  */
1081 static int fork_and_exec( const char *filename, const WCHAR *cmdline,
1082                           const WCHAR *env, const char *newdir )
1083 {
1084     int fd[2];
1085     int pid, err;
1086
1087     if (!env) env = GetEnvironmentStringsW();
1088
1089     if (pipe(fd) == -1)
1090     {
1091         FILE_SetDosError();
1092         return -1;
1093     }
1094     fcntl( fd[1], F_SETFD, 1 );  /* set close on exec */
1095     if (!(pid = fork()))  /* child */
1096     {
1097         char **argv = build_argv( cmdline, 0 );
1098         char **envp = build_envp( env, NULL );
1099         close( fd[0] );
1100
1101         /* Reset signals that we previously set to SIG_IGN */
1102         signal( SIGPIPE, SIG_DFL );
1103         signal( SIGCHLD, SIG_DFL );
1104
1105         if (newdir) chdir(newdir);
1106
1107         if (argv && envp) execve( filename, argv, envp );
1108         err = errno;
1109         write( fd[1], &err, sizeof(err) );
1110         _exit(1);
1111     }
1112     close( fd[1] );
1113     if ((pid != -1) && (read( fd[0], &err, sizeof(err) ) > 0))  /* exec failed */
1114     {
1115         errno = err;
1116         pid = -1;
1117     }
1118     if (pid == -1) FILE_SetDosError();
1119     close( fd[0] );
1120     return pid;
1121 }
1122
1123
1124 /***********************************************************************
1125  *           create_user_params
1126  */
1127 static RTL_USER_PROCESS_PARAMETERS *create_user_params( LPCWSTR filename, LPCWSTR cmdline,
1128                                                         const STARTUPINFOW *startup )
1129 {
1130     RTL_USER_PROCESS_PARAMETERS *params;
1131     UNICODE_STRING image_str, cmdline_str, desktop, title;
1132     NTSTATUS status;
1133     WCHAR buffer[MAX_PATH];
1134
1135     if (GetLongPathNameW( filename, buffer, MAX_PATH ))
1136         RtlInitUnicodeString( &image_str, buffer );
1137     else
1138         RtlInitUnicodeString( &image_str, filename );
1139
1140     RtlInitUnicodeString( &cmdline_str, cmdline );
1141     if (startup->lpDesktop) RtlInitUnicodeString( &desktop, startup->lpDesktop );
1142     if (startup->lpTitle) RtlInitUnicodeString( &title, startup->lpTitle );
1143
1144     status = RtlCreateProcessParameters( &params, &image_str, NULL, NULL, &cmdline_str, NULL,
1145                                          startup->lpTitle ? &title : NULL,
1146                                          startup->lpDesktop ? &desktop : NULL,
1147                                          NULL, NULL );
1148     if (status != STATUS_SUCCESS)
1149     {
1150         SetLastError( RtlNtStatusToDosError(status) );
1151         return NULL;
1152     }
1153
1154     params->Environment     = NULL;  /* we pass it through the Unix environment */
1155     params->hStdInput       = startup->hStdInput;
1156     params->hStdOutput      = startup->hStdOutput;
1157     params->hStdError       = startup->hStdError;
1158     params->dwX             = startup->dwX;
1159     params->dwY             = startup->dwY;
1160     params->dwXSize         = startup->dwXSize;
1161     params->dwYSize         = startup->dwYSize;
1162     params->dwXCountChars   = startup->dwXCountChars;
1163     params->dwYCountChars   = startup->dwYCountChars;
1164     params->dwFillAttribute = startup->dwFillAttribute;
1165     params->dwFlags         = startup->dwFlags;
1166     params->wShowWindow     = startup->wShowWindow;
1167     return params;
1168 }
1169
1170
1171 /***********************************************************************
1172  *           create_process
1173  *
1174  * Create a new process. If hFile is a valid handle we have an exe
1175  * file, otherwise it is a Winelib app.
1176  */
1177 static BOOL create_process( HANDLE hFile, LPCWSTR filename, LPWSTR cmd_line, LPWSTR env,
1178                             LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
1179                             BOOL inherit, DWORD flags, LPSTARTUPINFOW startup,
1180                             LPPROCESS_INFORMATION info, LPCSTR unixdir )
1181 {
1182     BOOL ret, success = FALSE;
1183     HANDLE process_info;
1184     RTL_USER_PROCESS_PARAMETERS *params;
1185     WCHAR *extra_env = NULL;
1186     int startfd[2];
1187     int execfd[2];
1188     pid_t pid;
1189     int err;
1190     char dummy = 0;
1191
1192     if (!env)
1193     {
1194         env = GetEnvironmentStringsW();
1195         extra_env = DRIVE_BuildEnv();
1196     }
1197
1198     if (!(params = create_user_params( filename, cmd_line, startup )))
1199     {
1200         if (extra_env) HeapFree( GetProcessHeap(), 0, extra_env );
1201         return FALSE;
1202     }
1203
1204     /* create the synchronization pipes */
1205
1206     if (pipe( startfd ) == -1)
1207     {
1208         FILE_SetDosError();
1209         RtlDestroyProcessParameters( params );
1210         if (extra_env) HeapFree( GetProcessHeap(), 0, extra_env );
1211         return FALSE;
1212     }
1213     if (pipe( execfd ) == -1)
1214     {
1215         FILE_SetDosError();
1216         close( startfd[0] );
1217         close( startfd[1] );
1218         RtlDestroyProcessParameters( params );
1219         if (extra_env) HeapFree( GetProcessHeap(), 0, extra_env );
1220         return FALSE;
1221     }
1222     fcntl( execfd[1], F_SETFD, 1 );  /* set close on exec */
1223
1224     /* create the child process */
1225
1226     if (!(pid = fork()))  /* child */
1227     {
1228         char **argv = build_argv( cmd_line, 1 );
1229         char **envp = build_envp( env, extra_env );
1230
1231         close( startfd[1] );
1232         close( execfd[0] );
1233
1234         /* wait for parent to tell us to start */
1235         if (read( startfd[0], &dummy, 1 ) != 1) _exit(1);
1236
1237         close( startfd[0] );
1238         /* Reset signals that we previously set to SIG_IGN */
1239         signal( SIGPIPE, SIG_DFL );
1240         signal( SIGCHLD, SIG_DFL );
1241
1242         if (unixdir) chdir(unixdir);
1243
1244         if (argv && envp)
1245         {
1246             /* first, try for a WINELOADER environment variable */
1247             argv[0] = getenv("WINELOADER");
1248             if (argv[0]) execve( argv[0], argv, envp );
1249             /* now use the standard search strategy */
1250             wine_exec_wine_binary( NULL, argv, envp );
1251         }
1252         err = errno;
1253         write( execfd[1], &err, sizeof(err) );
1254         _exit(1);
1255     }
1256
1257     /* this is the parent */
1258
1259     close( startfd[0] );
1260     close( execfd[1] );
1261     if (extra_env) HeapFree( GetProcessHeap(), 0, extra_env );
1262     if (pid == -1)
1263     {
1264         close( startfd[1] );
1265         close( execfd[0] );
1266         FILE_SetDosError();
1267         RtlDestroyProcessParameters( params );
1268         return FALSE;
1269     }
1270
1271     /* create the process on the server side */
1272
1273     SERVER_START_REQ( new_process )
1274     {
1275         req->inherit_all  = inherit;
1276         req->create_flags = flags;
1277         req->unix_pid     = pid;
1278         req->exe_file     = hFile;
1279         if (startup->dwFlags & STARTF_USESTDHANDLES)
1280         {
1281             req->hstdin  = startup->hStdInput;
1282             req->hstdout = startup->hStdOutput;
1283             req->hstderr = startup->hStdError;
1284         }
1285         else
1286         {
1287             req->hstdin  = GetStdHandle( STD_INPUT_HANDLE );
1288             req->hstdout = GetStdHandle( STD_OUTPUT_HANDLE );
1289             req->hstderr = GetStdHandle( STD_ERROR_HANDLE );
1290         }
1291
1292         if ((flags & (CREATE_NEW_CONSOLE | DETACHED_PROCESS)) != 0)
1293         {
1294             /* this is temporary (for console handles). We have no way to control that the handle is invalid in child process otherwise */
1295             if (is_console_handle(req->hstdin))  req->hstdin  = INVALID_HANDLE_VALUE;
1296             if (is_console_handle(req->hstdout)) req->hstdout = INVALID_HANDLE_VALUE;
1297             if (is_console_handle(req->hstderr)) req->hstderr = INVALID_HANDLE_VALUE;
1298         }
1299         else
1300         {
1301             if (is_console_handle(req->hstdin))  req->hstdin  = console_handle_unmap(req->hstdin);
1302             if (is_console_handle(req->hstdout)) req->hstdout = console_handle_unmap(req->hstdout);
1303             if (is_console_handle(req->hstderr)) req->hstderr = console_handle_unmap(req->hstderr);
1304         }
1305
1306         wine_server_add_data( req, params, params->Size );
1307         ret = !wine_server_call_err( req );
1308         process_info = reply->info;
1309     }
1310     SERVER_END_REQ;
1311
1312     RtlDestroyProcessParameters( params );
1313     if (!ret)
1314     {
1315         close( startfd[1] );
1316         close( execfd[0] );
1317         return FALSE;
1318     }
1319
1320     /* tell child to start and wait for it to exec */
1321
1322     write( startfd[1], &dummy, 1 );
1323     close( startfd[1] );
1324
1325     if (read( execfd[0], &err, sizeof(err) ) > 0) /* exec failed */
1326     {
1327         errno = err;
1328         FILE_SetDosError();
1329         close( execfd[0] );
1330         CloseHandle( process_info );
1331         return FALSE;
1332     }
1333
1334     /* wait for the new process info to be ready */
1335
1336     WaitForSingleObject( process_info, INFINITE );
1337     SERVER_START_REQ( get_new_process_info )
1338     {
1339         req->info     = process_info;
1340         req->pinherit = (psa && (psa->nLength >= sizeof(*psa)) && psa->bInheritHandle);
1341         req->tinherit = (tsa && (tsa->nLength >= sizeof(*tsa)) && tsa->bInheritHandle);
1342         if ((ret = !wine_server_call_err( req )))
1343         {
1344             info->dwProcessId = (DWORD)reply->pid;
1345             info->dwThreadId  = (DWORD)reply->tid;
1346             info->hProcess    = reply->phandle;
1347             info->hThread     = reply->thandle;
1348             success           = reply->success;
1349         }
1350     }
1351     SERVER_END_REQ;
1352
1353     if (ret && !success)  /* new process failed to start */
1354     {
1355         DWORD exitcode;
1356         if (GetExitCodeProcess( info->hProcess, &exitcode )) SetLastError( exitcode );
1357         CloseHandle( info->hThread );
1358         CloseHandle( info->hProcess );
1359         ret = FALSE;
1360     }
1361     CloseHandle( process_info );
1362     return ret;
1363 }
1364
1365
1366 /***********************************************************************
1367  *           create_vdm_process
1368  *
1369  * Create a new VDM process for a 16-bit or DOS application.
1370  */
1371 static BOOL create_vdm_process( LPCWSTR filename, LPWSTR cmd_line, LPWSTR env,
1372                                 LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
1373                                 BOOL inherit, DWORD flags, LPSTARTUPINFOW startup,
1374                                 LPPROCESS_INFORMATION info, LPCSTR unixdir )
1375 {
1376     static const WCHAR argsW[] = {'%','s',' ','-','-','a','p','p','-','n','a','m','e',' ','"','%','s','"',' ','%','s',0};
1377
1378     BOOL ret;
1379     LPWSTR new_cmd_line = HeapAlloc( GetProcessHeap(), 0,
1380                                      (strlenW(filename) + strlenW(cmd_line) + 30) * sizeof(WCHAR) );
1381
1382     if (!new_cmd_line)
1383     {
1384         SetLastError( ERROR_OUTOFMEMORY );
1385         return FALSE;
1386     }
1387     sprintfW( new_cmd_line, argsW, winevdmW, filename, cmd_line );
1388     ret = create_process( 0, winevdmW, new_cmd_line, env, psa, tsa, inherit,
1389                           flags, startup, info, unixdir );
1390     HeapFree( GetProcessHeap(), 0, new_cmd_line );
1391     return ret;
1392 }
1393
1394
1395 /***********************************************************************
1396  *           create_cmd_process
1397  *
1398  * Create a new cmd shell process for a .BAT file.
1399  */
1400 static BOOL create_cmd_process( LPCWSTR filename, LPWSTR cmd_line, LPVOID env,
1401                                 LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
1402                                 BOOL inherit, DWORD flags, LPSTARTUPINFOW startup,
1403                                 LPPROCESS_INFORMATION info, LPCWSTR cur_dir )
1404
1405 {
1406     static const WCHAR comspecW[] = {'C','O','M','S','P','E','C',0};
1407     static const WCHAR slashcW[] = {' ','/','c',' ',0};
1408     WCHAR comspec[MAX_PATH];
1409     WCHAR *newcmdline;
1410     BOOL ret;
1411
1412     if (!GetEnvironmentVariableW( comspecW, comspec, sizeof(comspec)/sizeof(WCHAR) ))
1413         return FALSE;
1414     if (!(newcmdline = HeapAlloc( GetProcessHeap(), 0,
1415                                   (strlenW(comspec) + 4 + strlenW(cmd_line) + 1) * sizeof(WCHAR))))
1416         return FALSE;
1417
1418     strcpyW( newcmdline, comspec );
1419     strcatW( newcmdline, slashcW );
1420     strcatW( newcmdline, cmd_line );
1421     ret = CreateProcessW( comspec, newcmdline, psa, tsa, inherit,
1422                           flags, env, cur_dir, startup, info );
1423     HeapFree( GetProcessHeap(), 0, newcmdline );
1424     return ret;
1425 }
1426
1427
1428 /*************************************************************************
1429  *               get_file_name
1430  *
1431  * Helper for CreateProcess: retrieve the file name to load from the
1432  * app name and command line. Store the file name in buffer, and
1433  * return a possibly modified command line.
1434  * Also returns a handle to the opened file if it's a Windows binary.
1435  */
1436 static LPWSTR get_file_name( LPCWSTR appname, LPWSTR cmdline, LPWSTR buffer,
1437                              int buflen, HANDLE *handle )
1438 {
1439     static const WCHAR quotesW[] = {'"','%','s','"',0};
1440
1441     WCHAR *name, *pos, *ret = NULL;
1442     const WCHAR *p;
1443
1444     /* if we have an app name, everything is easy */
1445
1446     if (appname)
1447     {
1448         /* use the unmodified app name as file name */
1449         lstrcpynW( buffer, appname, buflen );
1450         *handle = open_exe_file( buffer );
1451         if (!(ret = cmdline) || !cmdline[0])
1452         {
1453             /* no command-line, create one */
1454             if ((ret = HeapAlloc( GetProcessHeap(), 0, (strlenW(appname) + 3) * sizeof(WCHAR) )))
1455                 sprintfW( ret, quotesW, appname );
1456         }
1457         return ret;
1458     }
1459
1460     if (!cmdline)
1461     {
1462         SetLastError( ERROR_INVALID_PARAMETER );
1463         return NULL;
1464     }
1465
1466     /* first check for a quoted file name */
1467
1468     if ((cmdline[0] == '"') && ((p = strchrW( cmdline + 1, '"' ))))
1469     {
1470         int len = p - cmdline - 1;
1471         /* extract the quoted portion as file name */
1472         if (!(name = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ))) return NULL;
1473         memcpy( name, cmdline + 1, len * sizeof(WCHAR) );
1474         name[len] = 0;
1475
1476         if (find_exe_file( name, buffer, buflen, handle ))
1477             ret = cmdline;  /* no change necessary */
1478         goto done;
1479     }
1480
1481     /* now try the command-line word by word */
1482
1483     if (!(name = HeapAlloc( GetProcessHeap(), 0, (strlenW(cmdline) + 1) * sizeof(WCHAR) )))
1484         return NULL;
1485     pos = name;
1486     p = cmdline;
1487
1488     while (*p)
1489     {
1490         do *pos++ = *p++; while (*p && *p != ' ');
1491         *pos = 0;
1492         if (find_exe_file( name, buffer, buflen, handle ))
1493         {
1494             ret = cmdline;
1495             break;
1496         }
1497     }
1498
1499     if (!ret || !strchrW( name, ' ' )) goto done;  /* no change necessary */
1500
1501     /* now build a new command-line with quotes */
1502
1503     if (!(ret = HeapAlloc( GetProcessHeap(), 0, (strlenW(cmdline) + 3) * sizeof(WCHAR) )))
1504         goto done;
1505     sprintfW( ret, quotesW, name );
1506     strcatW( ret, p );
1507
1508  done:
1509     HeapFree( GetProcessHeap(), 0, name );
1510     return ret;
1511 }
1512
1513
1514 /**********************************************************************
1515  *       CreateProcessA          (KERNEL32.@)
1516  */
1517 BOOL WINAPI CreateProcessA( LPCSTR app_name, LPSTR cmd_line, LPSECURITY_ATTRIBUTES process_attr,
1518                             LPSECURITY_ATTRIBUTES thread_attr, BOOL inherit,
1519                             DWORD flags, LPVOID env, LPCSTR cur_dir,
1520                             LPSTARTUPINFOA startup_info, LPPROCESS_INFORMATION info )
1521 {
1522     BOOL ret;
1523     UNICODE_STRING app_nameW, cmd_lineW, cur_dirW, desktopW, titleW;
1524     STARTUPINFOW infoW;
1525
1526     if (app_name) RtlCreateUnicodeStringFromAsciiz( &app_nameW, app_name );
1527     else app_nameW.Buffer = NULL;
1528     if (cmd_line) RtlCreateUnicodeStringFromAsciiz( &cmd_lineW, cmd_line );
1529     else cmd_lineW.Buffer = NULL;
1530     if (cur_dir) RtlCreateUnicodeStringFromAsciiz( &cur_dirW, cur_dir );
1531     else cur_dirW.Buffer = NULL;
1532     if (startup_info->lpDesktop) RtlCreateUnicodeStringFromAsciiz( &desktopW, startup_info->lpDesktop );
1533     else desktopW.Buffer = NULL;
1534     if (startup_info->lpTitle) RtlCreateUnicodeStringFromAsciiz( &titleW, startup_info->lpTitle );
1535     else titleW.Buffer = NULL;
1536
1537     memcpy( &infoW, startup_info, sizeof(infoW) );
1538     infoW.lpDesktop = desktopW.Buffer;
1539     infoW.lpTitle = titleW.Buffer;
1540
1541     if (startup_info->lpReserved)
1542       FIXME("StartupInfo.lpReserved is used, please report (%s)\n",
1543             debugstr_a(startup_info->lpReserved));
1544
1545     ret = CreateProcessW( app_nameW.Buffer,  cmd_lineW.Buffer, process_attr, thread_attr,
1546                           inherit, flags, env, cur_dirW.Buffer, &infoW, info );
1547
1548     RtlFreeUnicodeString( &app_nameW );
1549     RtlFreeUnicodeString( &cmd_lineW );
1550     RtlFreeUnicodeString( &cur_dirW );
1551     RtlFreeUnicodeString( &desktopW );
1552     RtlFreeUnicodeString( &titleW );
1553     return ret;
1554 }
1555
1556
1557 /**********************************************************************
1558  *       CreateProcessW          (KERNEL32.@)
1559  */
1560 BOOL WINAPI CreateProcessW( LPCWSTR app_name, LPWSTR cmd_line, LPSECURITY_ATTRIBUTES process_attr,
1561                             LPSECURITY_ATTRIBUTES thread_attr, BOOL inherit, DWORD flags,
1562                             LPVOID env, LPCWSTR cur_dir, LPSTARTUPINFOW startup_info,
1563                             LPPROCESS_INFORMATION info )
1564 {
1565     BOOL retv = FALSE;
1566     HANDLE hFile = 0;
1567     const char *unixdir = NULL;
1568     DOS_FULL_NAME full_dir;
1569     WCHAR name[MAX_PATH];
1570     WCHAR *tidy_cmdline, *p, *envW = env;
1571
1572     /* Process the AppName and/or CmdLine to get module name and path */
1573
1574     TRACE("app %s cmdline %s\n", debugstr_w(app_name), debugstr_w(cmd_line) );
1575
1576     if (!(tidy_cmdline = get_file_name( app_name, cmd_line, name, sizeof(name), &hFile )))
1577         return FALSE;
1578     if (hFile == INVALID_HANDLE_VALUE) goto done;
1579
1580     /* Warn if unsupported features are used */
1581
1582     if (flags & (IDLE_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | REALTIME_PRIORITY_CLASS |
1583                  CREATE_NEW_PROCESS_GROUP | CREATE_SEPARATE_WOW_VDM | CREATE_SHARED_WOW_VDM |
1584                  CREATE_DEFAULT_ERROR_MODE | CREATE_NO_WINDOW |
1585                  PROFILE_USER | PROFILE_KERNEL | PROFILE_SERVER))
1586         WARN("(%s,...): ignoring some flags in %lx\n", debugstr_w(name), flags);
1587
1588     if (cur_dir)
1589     {
1590         if (DOSFS_GetFullName( cur_dir, TRUE, &full_dir )) unixdir = full_dir.long_name;
1591     }
1592     else
1593     {
1594         WCHAR buf[MAX_PATH];
1595         if (GetCurrentDirectoryW(MAX_PATH, buf))
1596         {
1597             if (DOSFS_GetFullName( buf, TRUE, &full_dir )) unixdir = full_dir.long_name;
1598         }
1599     }
1600
1601     if (env && !(flags & CREATE_UNICODE_ENVIRONMENT))  /* convert environment to unicode */
1602     {
1603         char *p = env;
1604         DWORD lenW;
1605
1606         while (*p) p += strlen(p) + 1;
1607         p++;  /* final null */
1608         lenW = MultiByteToWideChar( CP_ACP, 0, env, p - (char*)env, NULL, 0 );
1609         envW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) );
1610         MultiByteToWideChar( CP_ACP, 0, env, p - (char*)env, envW, lenW );
1611         flags |= CREATE_UNICODE_ENVIRONMENT;
1612     }
1613
1614     info->hThread = info->hProcess = 0;
1615     info->dwProcessId = info->dwThreadId = 0;
1616
1617     /* Determine executable type */
1618
1619     if (!hFile)  /* builtin exe */
1620     {
1621         TRACE( "starting %s as Winelib app\n", debugstr_w(name) );
1622         retv = create_process( 0, name, tidy_cmdline, envW, process_attr, thread_attr,
1623                                inherit, flags, startup_info, info, unixdir );
1624         goto done;
1625     }
1626
1627     switch( MODULE_GetBinaryType( hFile ))
1628     {
1629     case BINARY_PE_EXE:
1630         TRACE( "starting %s as Win32 binary\n", debugstr_w(name) );
1631         retv = create_process( hFile, name, tidy_cmdline, envW, process_attr, thread_attr,
1632                                inherit, flags, startup_info, info, unixdir );
1633         break;
1634     case BINARY_WIN16:
1635     case BINARY_DOS:
1636         TRACE( "starting %s as Win16/DOS binary\n", debugstr_w(name) );
1637         retv = create_vdm_process( name, tidy_cmdline, envW, process_attr, thread_attr,
1638                                    inherit, flags, startup_info, info, unixdir );
1639         break;
1640     case BINARY_OS216:
1641         FIXME( "%s is OS/2 binary, not supported\n", debugstr_w(name) );
1642         SetLastError( ERROR_BAD_EXE_FORMAT );
1643         break;
1644     case BINARY_PE_DLL:
1645         TRACE( "not starting %s since it is a dll\n", debugstr_w(name) );
1646         SetLastError( ERROR_BAD_EXE_FORMAT );
1647         break;
1648     case BINARY_UNIX_LIB:
1649         TRACE( "%s is a Unix library, starting as Winelib app\n", debugstr_w(name) );
1650         retv = create_process( hFile, name, tidy_cmdline, envW, process_attr, thread_attr,
1651                                inherit, flags, startup_info, info, unixdir );
1652         break;
1653     case BINARY_UNKNOWN:
1654         /* check for .com or .bat extension */
1655         if ((p = strrchrW( name, '.' )))
1656         {
1657             if (!strcmpiW( p, comW ))
1658             {
1659                 TRACE( "starting %s as DOS binary\n", debugstr_w(name) );
1660                 retv = create_vdm_process( name, tidy_cmdline, envW, process_attr, thread_attr,
1661                                            inherit, flags, startup_info, info, unixdir );
1662                 break;
1663             }
1664             if (!strcmpiW( p, batW ))
1665             {
1666                 TRACE( "starting %s as batch binary\n", debugstr_w(name) );
1667                 retv = create_cmd_process( name, tidy_cmdline, envW, process_attr, thread_attr,
1668                                            inherit, flags, startup_info, info, cur_dir );
1669                 break;
1670             }
1671         }
1672         /* fall through */
1673     case BINARY_UNIX_EXE:
1674         {
1675             /* unknown file, try as unix executable */
1676             DOS_FULL_NAME full_name;
1677
1678             TRACE( "starting %s as Unix binary\n", debugstr_w(name) );
1679
1680             if (DOSFS_GetFullName( name, TRUE, &full_name ))
1681                 retv = (fork_and_exec( full_name.long_name, tidy_cmdline, envW, unixdir ) != -1);
1682         }
1683         break;
1684     }
1685     CloseHandle( hFile );
1686
1687  done:
1688     if (tidy_cmdline != cmd_line) HeapFree( GetProcessHeap(), 0, tidy_cmdline );
1689     if (envW != env) HeapFree( GetProcessHeap(), 0, envW );
1690     return retv;
1691 }
1692
1693
1694 /***********************************************************************
1695  *           wait_input_idle
1696  *
1697  * Wrapper to call WaitForInputIdle USER function
1698  */
1699 typedef DWORD (WINAPI *WaitForInputIdle_ptr)( HANDLE hProcess, DWORD dwTimeOut );
1700
1701 static DWORD wait_input_idle( HANDLE process, DWORD timeout )
1702 {
1703     HMODULE mod = GetModuleHandleA( "user32.dll" );
1704     if (mod)
1705     {
1706         WaitForInputIdle_ptr ptr = (WaitForInputIdle_ptr)GetProcAddress( mod, "WaitForInputIdle" );
1707         if (ptr) return ptr( process, timeout );
1708     }
1709     return 0;
1710 }
1711
1712
1713 /***********************************************************************
1714  *           WinExec   (KERNEL32.@)
1715  */
1716 UINT WINAPI WinExec( LPCSTR lpCmdLine, UINT nCmdShow )
1717 {
1718     PROCESS_INFORMATION info;
1719     STARTUPINFOA startup;
1720     char *cmdline;
1721     UINT ret;
1722
1723     memset( &startup, 0, sizeof(startup) );
1724     startup.cb = sizeof(startup);
1725     startup.dwFlags = STARTF_USESHOWWINDOW;
1726     startup.wShowWindow = nCmdShow;
1727
1728     /* cmdline needs to be writeable for CreateProcess */
1729     if (!(cmdline = HeapAlloc( GetProcessHeap(), 0, strlen(lpCmdLine)+1 ))) return 0;
1730     strcpy( cmdline, lpCmdLine );
1731
1732     if (CreateProcessA( NULL, cmdline, NULL, NULL, FALSE,
1733                         0, NULL, NULL, &startup, &info ))
1734     {
1735         /* Give 30 seconds to the app to come up */
1736         if (wait_input_idle( info.hProcess, 30000 ) == WAIT_FAILED)
1737             WARN("WaitForInputIdle failed: Error %ld\n", GetLastError() );
1738         ret = 33;
1739         /* Close off the handles */
1740         CloseHandle( info.hThread );
1741         CloseHandle( info.hProcess );
1742     }
1743     else if ((ret = GetLastError()) >= 32)
1744     {
1745         FIXME("Strange error set by CreateProcess: %d\n", ret );
1746         ret = 11;
1747     }
1748     HeapFree( GetProcessHeap(), 0, cmdline );
1749     return ret;
1750 }
1751
1752
1753 /**********************************************************************
1754  *          LoadModule    (KERNEL32.@)
1755  */
1756 HINSTANCE WINAPI LoadModule( LPCSTR name, LPVOID paramBlock )
1757 {
1758     LOADPARMS32 *params = paramBlock;
1759     PROCESS_INFORMATION info;
1760     STARTUPINFOA startup;
1761     HINSTANCE hInstance;
1762     LPSTR cmdline, p;
1763     char filename[MAX_PATH];
1764     BYTE len;
1765
1766     if (!name) return (HINSTANCE)ERROR_FILE_NOT_FOUND;
1767
1768     if (!SearchPathA( NULL, name, ".exe", sizeof(filename), filename, NULL ) &&
1769         !SearchPathA( NULL, name, NULL, sizeof(filename), filename, NULL ))
1770         return (HINSTANCE)GetLastError();
1771
1772     len = (BYTE)params->lpCmdLine[0];
1773     if (!(cmdline = HeapAlloc( GetProcessHeap(), 0, strlen(filename) + len + 2 )))
1774         return (HINSTANCE)ERROR_NOT_ENOUGH_MEMORY;
1775
1776     strcpy( cmdline, filename );
1777     p = cmdline + strlen(cmdline);
1778     *p++ = ' ';
1779     memcpy( p, params->lpCmdLine + 1, len );
1780     p[len] = 0;
1781
1782     memset( &startup, 0, sizeof(startup) );
1783     startup.cb = sizeof(startup);
1784     if (params->lpCmdShow)
1785     {
1786         startup.dwFlags = STARTF_USESHOWWINDOW;
1787         startup.wShowWindow = ((WORD *)params->lpCmdShow)[1];
1788     }
1789
1790     if (CreateProcessA( filename, cmdline, NULL, NULL, FALSE, 0,
1791                         params->lpEnvAddress, NULL, &startup, &info ))
1792     {
1793         /* Give 30 seconds to the app to come up */
1794         if (wait_input_idle( info.hProcess, 30000 ) == WAIT_FAILED)
1795             WARN("WaitForInputIdle failed: Error %ld\n", GetLastError() );
1796         hInstance = (HINSTANCE)33;
1797         /* Close off the handles */
1798         CloseHandle( info.hThread );
1799         CloseHandle( info.hProcess );
1800     }
1801     else if ((hInstance = (HINSTANCE)GetLastError()) >= (HINSTANCE)32)
1802     {
1803         FIXME("Strange error set by CreateProcess: %p\n", hInstance );
1804         hInstance = (HINSTANCE)11;
1805     }
1806
1807     HeapFree( GetProcessHeap(), 0, cmdline );
1808     return hInstance;
1809 }
1810
1811
1812 /******************************************************************************
1813  *           TerminateProcess   (KERNEL32.@)
1814  */
1815 BOOL WINAPI TerminateProcess( HANDLE handle, DWORD exit_code )
1816 {
1817     NTSTATUS status = NtTerminateProcess( handle, exit_code );
1818     if (status) SetLastError( RtlNtStatusToDosError(status) );
1819     return !status;
1820 }
1821
1822
1823 /***********************************************************************
1824  *           ExitProcess   (KERNEL32.@)
1825  */
1826 void WINAPI ExitProcess( DWORD status )
1827 {
1828     LdrShutdownProcess();
1829     SERVER_START_REQ( terminate_process )
1830     {
1831         /* send the exit code to the server */
1832         req->handle    = GetCurrentProcess();
1833         req->exit_code = status;
1834         wine_server_call( req );
1835     }
1836     SERVER_END_REQ;
1837     exit( status );
1838 }
1839
1840
1841 /***********************************************************************
1842  * GetExitCodeProcess [KERNEL32.@]
1843  *
1844  * Gets termination status of specified process
1845  *
1846  * RETURNS
1847  *   Success: TRUE
1848  *   Failure: FALSE
1849  */
1850 BOOL WINAPI GetExitCodeProcess(
1851     HANDLE hProcess,    /* [in] handle to the process */
1852     LPDWORD lpExitCode) /* [out] address to receive termination status */
1853 {
1854     BOOL ret;
1855     SERVER_START_REQ( get_process_info )
1856     {
1857         req->handle = hProcess;
1858         ret = !wine_server_call_err( req );
1859         if (ret && lpExitCode) *lpExitCode = reply->exit_code;
1860     }
1861     SERVER_END_REQ;
1862     return ret;
1863 }
1864
1865
1866 /***********************************************************************
1867  *           SetErrorMode   (KERNEL32.@)
1868  */
1869 UINT WINAPI SetErrorMode( UINT mode )
1870 {
1871     UINT old = process_error_mode;
1872     process_error_mode = mode;
1873     return old;
1874 }
1875
1876
1877 /**********************************************************************
1878  * TlsAlloc [KERNEL32.@]  Allocates a TLS index.
1879  *
1880  * Allocates a thread local storage index
1881  *
1882  * RETURNS
1883  *    Success: TLS Index
1884  *    Failure: 0xFFFFFFFF
1885  */
1886 DWORD WINAPI TlsAlloc( void )
1887 {
1888     DWORD index;
1889
1890     RtlAcquirePebLock();
1891     index = RtlFindClearBitsAndSet( NtCurrentTeb()->Peb->TlsBitmap, 1, 0 );
1892     if (index != ~0UL) NtCurrentTeb()->TlsSlots[index] = 0; /* clear the value */
1893     else SetLastError( ERROR_NO_MORE_ITEMS );
1894     RtlReleasePebLock();
1895     return index;
1896 }
1897
1898
1899 /**********************************************************************
1900  * TlsFree [KERNEL32.@]  Releases a TLS index.
1901  *
1902  * Releases a thread local storage index, making it available for reuse
1903  *
1904  * RETURNS
1905  *    Success: TRUE
1906  *    Failure: FALSE
1907  */
1908 BOOL WINAPI TlsFree(
1909     DWORD index) /* [in] TLS Index to free */
1910 {
1911     BOOL ret;
1912
1913     RtlAcquirePebLock();
1914     ret = RtlAreBitsSet( NtCurrentTeb()->Peb->TlsBitmap, index, 1 );
1915     if (ret)
1916     {
1917         RtlClearBits( NtCurrentTeb()->Peb->TlsBitmap, index, 1 );
1918         NtSetInformationThread( GetCurrentThread(), ThreadZeroTlsCell, &index, sizeof(index) );
1919     }
1920     else SetLastError( ERROR_INVALID_PARAMETER );
1921     RtlReleasePebLock();
1922     return TRUE;
1923 }
1924
1925
1926 /**********************************************************************
1927  * TlsGetValue [KERNEL32.@]  Gets value in a thread's TLS slot
1928  *
1929  * RETURNS
1930  *    Success: Value stored in calling thread's TLS slot for index
1931  *    Failure: 0 and GetLastError returns NO_ERROR
1932  */
1933 LPVOID WINAPI TlsGetValue(
1934     DWORD index) /* [in] TLS index to retrieve value for */
1935 {
1936     if (index >= NtCurrentTeb()->Peb->TlsBitmap->SizeOfBitMap)
1937     {
1938         SetLastError( ERROR_INVALID_PARAMETER );
1939         return NULL;
1940     }
1941     SetLastError( ERROR_SUCCESS );
1942     return NtCurrentTeb()->TlsSlots[index];
1943 }
1944
1945
1946 /**********************************************************************
1947  * TlsSetValue [KERNEL32.@]  Stores a value in the thread's TLS slot.
1948  *
1949  * RETURNS
1950  *    Success: TRUE
1951  *    Failure: FALSE
1952  */
1953 BOOL WINAPI TlsSetValue(
1954     DWORD index,  /* [in] TLS index to set value for */
1955     LPVOID value) /* [in] Value to be stored */
1956 {
1957     if (index >= NtCurrentTeb()->Peb->TlsBitmap->SizeOfBitMap)
1958     {
1959         SetLastError( ERROR_INVALID_PARAMETER );
1960         return FALSE;
1961     }
1962     NtCurrentTeb()->TlsSlots[index] = value;
1963     return TRUE;
1964 }
1965
1966
1967 /***********************************************************************
1968  *           GetProcessFlags    (KERNEL32.@)
1969  */
1970 DWORD WINAPI GetProcessFlags( DWORD processid )
1971 {
1972     IMAGE_NT_HEADERS *nt;
1973     DWORD flags = 0;
1974
1975     if (processid && processid != GetCurrentProcessId()) return 0;
1976
1977     if ((nt = RtlImageNtHeader( NtCurrentTeb()->Peb->ImageBaseAddress )))
1978     {
1979         if (nt->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI)
1980             flags |= PDB32_CONSOLE_PROC;
1981     }
1982     if (!AreFileApisANSI()) flags |= PDB32_FILE_APIS_OEM;
1983     if (IsDebuggerPresent()) flags |= PDB32_DEBUGGED;
1984     return flags;
1985 }
1986
1987
1988 /***********************************************************************
1989  *           GetProcessDword    (KERNEL.485)
1990  *           GetProcessDword    (KERNEL32.18)
1991  * 'Of course you cannot directly access Windows internal structures'
1992  */
1993 DWORD WINAPI GetProcessDword( DWORD dwProcessID, INT offset )
1994 {
1995     DWORD               x, y;
1996     STARTUPINFOW        siw;
1997
1998     TRACE("(%ld, %d)\n", dwProcessID, offset );
1999
2000     if (dwProcessID && dwProcessID != GetCurrentProcessId())
2001     {
2002         ERR("%d: process %lx not accessible\n", offset, dwProcessID);
2003         return 0;
2004     }
2005
2006     switch ( offset )
2007     {
2008     case GPD_APP_COMPAT_FLAGS:
2009         return GetAppCompatFlags16(0);
2010     case GPD_LOAD_DONE_EVENT:
2011         return 0;
2012     case GPD_HINSTANCE16:
2013         return GetTaskDS16();
2014     case GPD_WINDOWS_VERSION:
2015         return GetExeVersion16();
2016     case GPD_THDB:
2017         return (DWORD)NtCurrentTeb() - 0x10 /* FIXME */;
2018     case GPD_PDB:
2019         return (DWORD)NtCurrentTeb()->Peb;
2020     case GPD_STARTF_SHELLDATA: /* return stdoutput handle from startupinfo ??? */
2021         GetStartupInfoW(&siw);
2022         return (DWORD)siw.hStdOutput;
2023     case GPD_STARTF_HOTKEY: /* return stdinput handle from startupinfo ??? */
2024         GetStartupInfoW(&siw);
2025         return (DWORD)siw.hStdInput;
2026     case GPD_STARTF_SHOWWINDOW:
2027         GetStartupInfoW(&siw);
2028         return siw.wShowWindow;
2029     case GPD_STARTF_SIZE:
2030         GetStartupInfoW(&siw);
2031         x = siw.dwXSize;
2032         if ( (INT)x == CW_USEDEFAULT ) x = CW_USEDEFAULT16;
2033         y = siw.dwYSize;
2034         if ( (INT)y == CW_USEDEFAULT ) y = CW_USEDEFAULT16;
2035         return MAKELONG( x, y );
2036     case GPD_STARTF_POSITION:
2037         GetStartupInfoW(&siw);
2038         x = siw.dwX;
2039         if ( (INT)x == CW_USEDEFAULT ) x = CW_USEDEFAULT16;
2040         y = siw.dwY;
2041         if ( (INT)y == CW_USEDEFAULT ) y = CW_USEDEFAULT16;
2042         return MAKELONG( x, y );
2043     case GPD_STARTF_FLAGS:
2044         GetStartupInfoW(&siw);
2045         return siw.dwFlags;
2046     case GPD_PARENT:
2047         return 0;
2048     case GPD_FLAGS:
2049         return GetProcessFlags(0);
2050     case GPD_USERDATA:
2051         return process_dword;
2052     default:
2053         ERR("Unknown offset %d\n", offset );
2054         return 0;
2055     }
2056 }
2057
2058 /***********************************************************************
2059  *           SetProcessDword    (KERNEL.484)
2060  * 'Of course you cannot directly access Windows internal structures'
2061  */
2062 void WINAPI SetProcessDword( DWORD dwProcessID, INT offset, DWORD value )
2063 {
2064     TRACE("(%ld, %d)\n", dwProcessID, offset );
2065
2066     if (dwProcessID && dwProcessID != GetCurrentProcessId())
2067     {
2068         ERR("%d: process %lx not accessible\n", offset, dwProcessID);
2069         return;
2070     }
2071
2072     switch ( offset )
2073     {
2074     case GPD_APP_COMPAT_FLAGS:
2075     case GPD_LOAD_DONE_EVENT:
2076     case GPD_HINSTANCE16:
2077     case GPD_WINDOWS_VERSION:
2078     case GPD_THDB:
2079     case GPD_PDB:
2080     case GPD_STARTF_SHELLDATA:
2081     case GPD_STARTF_HOTKEY:
2082     case GPD_STARTF_SHOWWINDOW:
2083     case GPD_STARTF_SIZE:
2084     case GPD_STARTF_POSITION:
2085     case GPD_STARTF_FLAGS:
2086     case GPD_PARENT:
2087     case GPD_FLAGS:
2088         ERR("Not allowed to modify offset %d\n", offset );
2089         break;
2090     case GPD_USERDATA:
2091         process_dword = value;
2092         break;
2093     default:
2094         ERR("Unknown offset %d\n", offset );
2095         break;
2096     }
2097 }
2098
2099
2100 /***********************************************************************
2101  *           ExitProcess   (KERNEL.466)
2102  */
2103 void WINAPI ExitProcess16( WORD status )
2104 {
2105     DWORD count;
2106     ReleaseThunkLock( &count );
2107     ExitProcess( status );
2108 }
2109
2110
2111 /*********************************************************************
2112  *           OpenProcess   (KERNEL32.@)
2113  */
2114 HANDLE WINAPI OpenProcess( DWORD access, BOOL inherit, DWORD id )
2115 {
2116     HANDLE ret = 0;
2117     SERVER_START_REQ( open_process )
2118     {
2119         req->pid     = id;
2120         req->access  = access;
2121         req->inherit = inherit;
2122         if (!wine_server_call_err( req )) ret = reply->handle;
2123     }
2124     SERVER_END_REQ;
2125     return ret;
2126 }
2127
2128
2129 /*********************************************************************
2130  *           MapProcessHandle   (KERNEL.483)
2131  */
2132 DWORD WINAPI MapProcessHandle( HANDLE handle )
2133 {
2134     DWORD ret = 0;
2135     SERVER_START_REQ( get_process_info )
2136     {
2137         req->handle = handle;
2138         if (!wine_server_call_err( req )) ret = reply->pid;
2139     }
2140     SERVER_END_REQ;
2141     return ret;
2142 }
2143
2144
2145 /*********************************************************************
2146  *           CloseW32Handle (KERNEL.474)
2147  *           CloseHandle    (KERNEL32.@)
2148  */
2149 BOOL WINAPI CloseHandle( HANDLE handle )
2150 {
2151     NTSTATUS status;
2152
2153     /* stdio handles need special treatment */
2154     if ((handle == (HANDLE)STD_INPUT_HANDLE) ||
2155         (handle == (HANDLE)STD_OUTPUT_HANDLE) ||
2156         (handle == (HANDLE)STD_ERROR_HANDLE))
2157         handle = GetStdHandle( (DWORD)handle );
2158
2159     if (is_console_handle(handle))
2160         return CloseConsoleHandle(handle);
2161
2162     status = NtClose( handle );
2163     if (status) SetLastError( RtlNtStatusToDosError(status) );
2164     return !status;
2165 }
2166
2167
2168 /*********************************************************************
2169  *           GetHandleInformation   (KERNEL32.@)
2170  */
2171 BOOL WINAPI GetHandleInformation( HANDLE handle, LPDWORD flags )
2172 {
2173     BOOL ret;
2174     SERVER_START_REQ( set_handle_info )
2175     {
2176         req->handle = handle;
2177         req->flags  = 0;
2178         req->mask   = 0;
2179         req->fd     = -1;
2180         ret = !wine_server_call_err( req );
2181         if (ret && flags) *flags = reply->old_flags;
2182     }
2183     SERVER_END_REQ;
2184     return ret;
2185 }
2186
2187
2188 /*********************************************************************
2189  *           SetHandleInformation   (KERNEL32.@)
2190  */
2191 BOOL WINAPI SetHandleInformation( HANDLE handle, DWORD mask, DWORD flags )
2192 {
2193     BOOL ret;
2194     SERVER_START_REQ( set_handle_info )
2195     {
2196         req->handle = handle;
2197         req->flags  = flags;
2198         req->mask   = mask;
2199         req->fd     = -1;
2200         ret = !wine_server_call_err( req );
2201     }
2202     SERVER_END_REQ;
2203     return ret;
2204 }
2205
2206
2207 /*********************************************************************
2208  *           DuplicateHandle   (KERNEL32.@)
2209  */
2210 BOOL WINAPI DuplicateHandle( HANDLE source_process, HANDLE source,
2211                              HANDLE dest_process, HANDLE *dest,
2212                              DWORD access, BOOL inherit, DWORD options )
2213 {
2214     NTSTATUS status;
2215
2216     if (is_console_handle(source))
2217     {
2218         /* FIXME: this test is not sufficient, we need to test process ids, not handles */
2219         if (source_process != dest_process ||
2220             source_process != GetCurrentProcess())
2221         {
2222             SetLastError(ERROR_INVALID_PARAMETER);
2223             return FALSE;
2224         }
2225         *dest = DuplicateConsoleHandle( source, access, inherit, options );
2226         return (*dest != INVALID_HANDLE_VALUE);
2227     }
2228     status = NtDuplicateObject( source_process, source, dest_process, dest,
2229                                 access, inherit ? OBJ_INHERIT : 0, options );
2230     if (status) SetLastError( RtlNtStatusToDosError(status) );
2231     return !status;
2232 }
2233
2234
2235 /***********************************************************************
2236  *           ConvertToGlobalHandle   (KERNEL.476)
2237  *           ConvertToGlobalHandle  (KERNEL32.@)
2238  */
2239 HANDLE WINAPI ConvertToGlobalHandle(HANDLE hSrc)
2240 {
2241     HANDLE ret = INVALID_HANDLE_VALUE;
2242     DuplicateHandle( GetCurrentProcess(), hSrc, GetCurrentProcess(), &ret, 0, FALSE,
2243                      DUP_HANDLE_MAKE_GLOBAL | DUP_HANDLE_SAME_ACCESS | DUP_HANDLE_CLOSE_SOURCE );
2244     return ret;
2245 }
2246
2247
2248 /***********************************************************************
2249  *           SetHandleContext   (KERNEL32.@)
2250  */
2251 BOOL WINAPI SetHandleContext(HANDLE hnd,DWORD context)
2252 {
2253     FIXME("(%p,%ld), stub. In case this got called by WSOCK32/WS2_32: "
2254           "the external WINSOCK DLLs won't work with WINE, don't use them.\n",hnd,context);
2255     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2256     return FALSE;
2257 }
2258
2259
2260 /***********************************************************************
2261  *           GetHandleContext   (KERNEL32.@)
2262  */
2263 DWORD WINAPI GetHandleContext(HANDLE hnd)
2264 {
2265     FIXME("(%p), stub. In case this got called by WSOCK32/WS2_32: "
2266           "the external WINSOCK DLLs won't work with WINE, don't use them.\n",hnd);
2267     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2268     return 0;
2269 }
2270
2271
2272 /***********************************************************************
2273  *           CreateSocketHandle   (KERNEL32.@)
2274  */
2275 HANDLE WINAPI CreateSocketHandle(void)
2276 {
2277     FIXME("(), stub. In case this got called by WSOCK32/WS2_32: "
2278           "the external WINSOCK DLLs won't work with WINE, don't use them.\n");
2279     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2280     return INVALID_HANDLE_VALUE;
2281 }
2282
2283
2284 /***********************************************************************
2285  *           SetPriorityClass   (KERNEL32.@)
2286  */
2287 BOOL WINAPI SetPriorityClass( HANDLE hprocess, DWORD priorityclass )
2288 {
2289     BOOL ret;
2290     SERVER_START_REQ( set_process_info )
2291     {
2292         req->handle   = hprocess;
2293         req->priority = priorityclass;
2294         req->mask     = SET_PROCESS_INFO_PRIORITY;
2295         ret = !wine_server_call_err( req );
2296     }
2297     SERVER_END_REQ;
2298     return ret;
2299 }
2300
2301
2302 /***********************************************************************
2303  *           GetPriorityClass   (KERNEL32.@)
2304  */
2305 DWORD WINAPI GetPriorityClass(HANDLE hprocess)
2306 {
2307     DWORD ret = 0;
2308     SERVER_START_REQ( get_process_info )
2309     {
2310         req->handle = hprocess;
2311         if (!wine_server_call_err( req )) ret = reply->priority;
2312     }
2313     SERVER_END_REQ;
2314     return ret;
2315 }
2316
2317
2318 /***********************************************************************
2319  *          SetProcessAffinityMask   (KERNEL32.@)
2320  */
2321 BOOL WINAPI SetProcessAffinityMask( HANDLE hProcess, DWORD affmask )
2322 {
2323     BOOL ret;
2324     SERVER_START_REQ( set_process_info )
2325     {
2326         req->handle   = hProcess;
2327         req->affinity = affmask;
2328         req->mask     = SET_PROCESS_INFO_AFFINITY;
2329         ret = !wine_server_call_err( req );
2330     }
2331     SERVER_END_REQ;
2332     return ret;
2333 }
2334
2335
2336 /**********************************************************************
2337  *          GetProcessAffinityMask    (KERNEL32.@)
2338  */
2339 BOOL WINAPI GetProcessAffinityMask( HANDLE hProcess,
2340                                       LPDWORD lpProcessAffinityMask,
2341                                       LPDWORD lpSystemAffinityMask )
2342 {
2343     BOOL ret = FALSE;
2344     SERVER_START_REQ( get_process_info )
2345     {
2346         req->handle = hProcess;
2347         if (!wine_server_call_err( req ))
2348         {
2349             if (lpProcessAffinityMask) *lpProcessAffinityMask = reply->process_affinity;
2350             if (lpSystemAffinityMask) *lpSystemAffinityMask = reply->system_affinity;
2351             ret = TRUE;
2352         }
2353     }
2354     SERVER_END_REQ;
2355     return ret;
2356 }
2357
2358
2359 /***********************************************************************
2360  *           GetProcessVersion    (KERNEL32.@)
2361  */
2362 DWORD WINAPI GetProcessVersion( DWORD processid )
2363 {
2364     IMAGE_NT_HEADERS *nt;
2365
2366     if (processid && processid != GetCurrentProcessId())
2367     {
2368         FIXME("should use ReadProcessMemory\n");
2369         return 0;
2370     }
2371     if ((nt = RtlImageNtHeader( NtCurrentTeb()->Peb->ImageBaseAddress )))
2372         return ((nt->OptionalHeader.MajorSubsystemVersion << 16) |
2373                 nt->OptionalHeader.MinorSubsystemVersion);
2374     return 0;
2375 }
2376
2377
2378 /***********************************************************************
2379  *              SetProcessWorkingSetSize        [KERNEL32.@]
2380  * Sets the min/max working set sizes for a specified process.
2381  *
2382  * PARAMS
2383  *    hProcess [I] Handle to the process of interest
2384  *    minset   [I] Specifies minimum working set size
2385  *    maxset   [I] Specifies maximum working set size
2386  *
2387  * RETURNS  STD
2388  */
2389 BOOL WINAPI SetProcessWorkingSetSize(HANDLE hProcess, SIZE_T minset,
2390                                      SIZE_T maxset)
2391 {
2392     FIXME("(%p,%ld,%ld): stub - harmless\n",hProcess,minset,maxset);
2393     if(( minset == (SIZE_T)-1) && (maxset == (SIZE_T)-1)) {
2394         /* Trim the working set to zero */
2395         /* Swap the process out of physical RAM */
2396     }
2397     return TRUE;
2398 }
2399
2400 /***********************************************************************
2401  *           GetProcessWorkingSetSize    (KERNEL32.@)
2402  */
2403 BOOL WINAPI GetProcessWorkingSetSize(HANDLE hProcess, PSIZE_T minset,
2404                                      PSIZE_T maxset)
2405 {
2406     FIXME("(%p,%p,%p): stub\n",hProcess,minset,maxset);
2407     /* 32 MB working set size */
2408     if (minset) *minset = 32*1024*1024;
2409     if (maxset) *maxset = 32*1024*1024;
2410     return TRUE;
2411 }
2412
2413
2414 /***********************************************************************
2415  *           SetProcessShutdownParameters    (KERNEL32.@)
2416  */
2417 BOOL WINAPI SetProcessShutdownParameters(DWORD level, DWORD flags)
2418 {
2419     FIXME("(%08lx, %08lx): partial stub.\n", level, flags);
2420     shutdown_flags = flags;
2421     shutdown_priority = level;
2422     return TRUE;
2423 }
2424
2425
2426 /***********************************************************************
2427  * GetProcessShutdownParameters                 (KERNEL32.@)
2428  *
2429  */
2430 BOOL WINAPI GetProcessShutdownParameters( LPDWORD lpdwLevel, LPDWORD lpdwFlags )
2431 {
2432     *lpdwLevel = shutdown_priority;
2433     *lpdwFlags = shutdown_flags;
2434     return TRUE;
2435 }
2436
2437
2438 /***********************************************************************
2439  *           GetProcessPriorityBoost    (KERNEL32.@)
2440  */
2441 BOOL WINAPI GetProcessPriorityBoost(HANDLE hprocess,PBOOL pDisablePriorityBoost)
2442 {
2443     FIXME("(%p,%p): semi-stub\n", hprocess, pDisablePriorityBoost);
2444     
2445     /* Report that no boost is present.. */
2446     *pDisablePriorityBoost = FALSE;
2447     
2448     return TRUE;
2449 }
2450
2451 /***********************************************************************
2452  *           SetProcessPriorityBoost    (KERNEL32.@)
2453  */
2454 BOOL WINAPI SetProcessPriorityBoost(HANDLE hprocess,BOOL disableboost)
2455 {
2456     FIXME("(%p,%d): stub\n",hprocess,disableboost);
2457     /* Say we can do it. I doubt the program will notice that we don't. */
2458     return TRUE;
2459 }
2460
2461
2462 /***********************************************************************
2463  *              ReadProcessMemory (KERNEL32.@)
2464  */
2465 BOOL WINAPI ReadProcessMemory( HANDLE process, LPCVOID addr, LPVOID buffer, SIZE_T size,
2466                                SIZE_T *bytes_read )
2467 {
2468     NTSTATUS status = NtReadVirtualMemory( process, addr, buffer, size, bytes_read );
2469     if (status) SetLastError( RtlNtStatusToDosError(status) );
2470     return !status;
2471 }
2472
2473
2474 /***********************************************************************
2475  *           WriteProcessMemory                 (KERNEL32.@)
2476  */
2477 BOOL WINAPI WriteProcessMemory( HANDLE process, LPVOID addr, LPCVOID buffer, SIZE_T size,
2478                                 SIZE_T *bytes_written )
2479 {
2480     NTSTATUS status = NtWriteVirtualMemory( process, addr, buffer, size, bytes_written );
2481     if (status) SetLastError( RtlNtStatusToDosError(status) );
2482     return !status;
2483 }
2484
2485
2486 /****************************************************************************
2487  *              FlushInstructionCache (KERNEL32.@)
2488  */
2489 BOOL WINAPI FlushInstructionCache(HANDLE hProcess, LPCVOID lpBaseAddress, SIZE_T dwSize)
2490 {
2491     if (GetVersion() & 0x80000000) return TRUE; /* not NT, always TRUE */
2492     FIXME("(%p,%p,0x%08lx): stub\n",hProcess, lpBaseAddress, dwSize);
2493     return TRUE;
2494 }
2495
2496
2497 /******************************************************************
2498  *              GetProcessIoCounters (KERNEL32.@)
2499  */
2500 BOOL WINAPI GetProcessIoCounters(HANDLE hProcess, PIO_COUNTERS ioc)
2501 {
2502     NTSTATUS    status;
2503
2504     status = NtQueryInformationProcess(hProcess, ProcessIoCounters, 
2505                                        ioc, sizeof(*ioc), NULL);
2506     if (status) SetLastError( RtlNtStatusToDosError(status) );
2507     return !status;
2508 }
2509
2510 /***********************************************************************
2511  * ProcessIdToSessionId   (KERNEL32.@)
2512  * This function is available on Terminal Server 4SP4 and Windows 2000
2513  */
2514 BOOL WINAPI ProcessIdToSessionId( DWORD procid, DWORD *sessionid_ptr )
2515 {
2516     /* According to MSDN, if the calling process is not in a terminal
2517      * services environment, then the sessionid returned is zero.
2518      */
2519     *sessionid_ptr = 0;
2520     return TRUE;
2521 }
2522
2523
2524 /***********************************************************************
2525  *              RegisterServiceProcess (KERNEL.491)
2526  *              RegisterServiceProcess (KERNEL32.@)
2527  *
2528  * A service process calls this function to ensure that it continues to run
2529  * even after a user logged off.
2530  */
2531 DWORD WINAPI RegisterServiceProcess(DWORD dwProcessId, DWORD dwType)
2532 {
2533     /* I don't think that Wine needs to do anything in that function */
2534     return 1; /* success */
2535 }
2536
2537
2538 /**************************************************************************
2539  *              SetFileApisToOEM   (KERNEL32.@)
2540  */
2541 VOID WINAPI SetFileApisToOEM(void)
2542 {
2543     oem_file_apis = TRUE;
2544 }
2545
2546
2547 /**************************************************************************
2548  *              SetFileApisToANSI   (KERNEL32.@)
2549  */
2550 VOID WINAPI SetFileApisToANSI(void)
2551 {
2552     oem_file_apis = FALSE;
2553 }
2554
2555
2556 /******************************************************************************
2557  * AreFileApisANSI [KERNEL32.@]  Determines if file functions are using ANSI
2558  *
2559  * RETURNS
2560  *    TRUE:  Set of file functions is using ANSI code page
2561  *    FALSE: Set of file functions is using OEM code page
2562  */
2563 BOOL WINAPI AreFileApisANSI(void)
2564 {
2565     return !oem_file_apis;
2566 }
2567
2568
2569 /***********************************************************************
2570  *           GetTickCount       (KERNEL32.@)
2571  *
2572  * Returns the number of milliseconds, modulo 2^32, since the start
2573  * of the wineserver.
2574  */
2575 DWORD WINAPI GetTickCount(void)
2576 {
2577     struct timeval t;
2578     gettimeofday( &t, NULL );
2579     return ((t.tv_sec * 1000) + (t.tv_usec / 1000)) - server_startticks;
2580 }
2581
2582
2583 /***********************************************************************
2584  *           GetCurrentProcess   (KERNEL32.@)
2585  */
2586 #undef GetCurrentProcess
2587 HANDLE WINAPI GetCurrentProcess(void)
2588 {
2589     return (HANDLE)0xffffffff;
2590 }