Set default StdError to fd 2 (was on 1).
[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 "heap.h"
38 #include "module.h"
39 #include "options.h"
40 #include "kernel_private.h"
41 #include "wine/server.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 WINE_DECLARE_DEBUG_CHANNEL(snoop);
48
49 /* Win32 process database */
50 typedef struct _PDB
51 {
52     LONG             header[2];        /* 00 Kernel object header */
53     HMODULE          module;           /* 08 Main exe module (NT) */
54     PPEB_LDR_DATA    LdrData;          /* 0c Pointer to loader information */
55     RTL_USER_PROCESS_PARAMETERS *ProcessParameters;  /*  10 Process parameters */
56     DWORD            unknown2;         /* 14 Unknown */
57     HANDLE           heap;             /* 18 Default process heap */
58     HANDLE           mem_context;      /* 1c Process memory context */
59     DWORD            flags;            /* 20 Flags */
60     void            *pdb16;            /* 24 DOS PSP */
61     WORD             PSP_sel;          /* 28 Selector to DOS PSP */
62     WORD             imte;             /* 2a IMTE for the process module */
63     WORD             threads;          /* 2c Number of threads */
64     WORD             running_threads;  /* 2e Number of running threads */
65     WORD             free_lib_count;   /* 30 Recursion depth of FreeLibrary calls */
66     WORD             ring0_threads;    /* 32 Number of ring 0 threads */
67     HANDLE           system_heap;      /* 34 System heap to allocate handles */
68     HTASK            task;             /* 38 Win16 task */
69     void            *mem_map_files;    /* 3c Pointer to mem-mapped files */
70     struct _ENVDB   *env_db;           /* 40 Environment database */
71     void            *handle_table;     /* 44 Handle table */
72     struct _PDB     *parent;           /* 48 Parent process */
73     void            *modref_list;      /* 4c MODREF list */
74     void            *thread_list;      /* 50 List of threads */
75     void            *debuggee_CB;      /* 54 Debuggee context block */
76     void            *local_heap_free;  /* 58 Head of local heap free list */
77     DWORD            unknown4;         /* 5c Unknown */
78     CRITICAL_SECTION crit_section;     /* 60 Critical section */
79     DWORD            unknown5[3];      /* 78 Unknown */
80     void            *console;          /* 84 Console */
81     DWORD            tls_bits[2];      /* 88 TLS in-use bits */
82     DWORD            process_dword;    /* 90 Unknown */
83     struct _PDB     *group;            /* 94 Process group */
84     void            *exe_modref;       /* 98 MODREF for the process EXE */
85     void            *top_filter;       /* 9c Top exception filter */
86     DWORD            priority;         /* a0 Priority level */
87     HANDLE           heap_list;        /* a4 Head of process heap list */
88     void            *heap_handles;     /* a8 Head of heap handles list */
89     DWORD            unknown6;         /* ac Unknown */
90     void            *console_provider; /* b0 Console provider (??) */
91     WORD             env_selector;     /* b4 Selector to process environment */
92     WORD             error_mode;       /* b6 Error mode */
93     HANDLE           load_done_evt;    /* b8 Event for process loading done */
94     void            *UTState;          /* bc Head of Univeral Thunk list */
95     DWORD            unknown8;         /* c0 Unknown (NT) */
96     LCID             locale;           /* c4 Locale to be queried by GetThreadLocale (NT) */
97 } PDB;
98
99 PDB current_process;
100
101 static RTL_USER_PROCESS_PARAMETERS      process_pmts;
102 static PEB_LDR_DATA                     process_ldr;
103
104 static char main_exe_name[MAX_PATH];
105 static char *main_exe_name_ptr = main_exe_name;
106 static HANDLE main_exe_file;
107 static DWORD shutdown_flags = 0;
108 static DWORD shutdown_priority = 0x280;
109 static DWORD process_dword;
110 static BOOL oem_file_apis;
111
112 extern unsigned int server_startticks;
113 int main_create_flags = 0;
114
115 /* Process flags */
116 #define PDB32_DEBUGGED      0x0001  /* Process is being debugged */
117 #define PDB32_WIN16_PROC    0x0008  /* Win16 process */
118 #define PDB32_DOS_PROC      0x0010  /* Dos process */
119 #define PDB32_CONSOLE_PROC  0x0020  /* Console process */
120 #define PDB32_FILE_APIS_OEM 0x0040  /* File APIs are OEM */
121 #define PDB32_WIN32S_PROC   0x8000  /* Win32s process */
122
123 /* dlls/ntdll/env.c */
124 extern BOOL init_user_process_pmts( size_t, char*, size_t );
125 extern BOOL build_command_line( char **argv );
126
127 extern void RELAY_InitDebugLists(void);
128 extern void SHELL_LoadRegistry(void);
129 extern void VERSION_Init( const char *appname );
130
131 /***********************************************************************
132  *           get_basename
133  */
134 inline static const char *get_basename( const char *name )
135 {
136     char *p;
137
138     if ((p = strrchr( name, '/' ))) name = p + 1;
139     if ((p = strrchr( name, '\\' ))) name = p + 1;
140     return name;
141 }
142
143
144 /***********************************************************************
145  *           open_builtin_exe_file
146  *
147  * Open an exe file for a builtin exe.
148  */
149 static void *open_builtin_exe_file( const char *name, char *error, int error_size,
150                                     int test_only, int *file_exists )
151 {
152     char exename[MAX_PATH], *p;
153     const char *basename = get_basename(name);
154
155     if (strlen(basename) >= sizeof(exename)) return NULL;
156     strcpy( exename, basename );
157     for (p = exename; *p; p++) *p = FILE_tolower(*p);
158     return wine_dll_load_main_exe( exename, error, error_size, test_only, file_exists );
159 }
160
161
162 /***********************************************************************
163  *           open_exe_file
164  *
165  * Open a specific exe file, taking load order into account.
166  * Returns the file handle or 0 for a builtin exe.
167  */
168 static HANDLE open_exe_file( const char *name )
169 {
170     enum loadorder_type loadorder[LOADORDER_NTYPES];
171     char buffer[MAX_PATH];
172     HANDLE handle;
173     int i, file_exists;
174
175     TRACE("looking for %s\n", debugstr_a(name) );
176
177     if ((handle = CreateFileA( name, GENERIC_READ, FILE_SHARE_READ,
178                                NULL, OPEN_EXISTING, 0, 0 )) == INVALID_HANDLE_VALUE)
179     {
180         /* file doesn't exist, check for builtin */
181         if (!FILE_contains_path( name )) goto error;
182         if (!MODULE_GetBuiltinPath( name, "", buffer, sizeof(buffer) )) goto error;
183         name = buffer;
184     }
185
186     MODULE_GetLoadOrder( loadorder, name, TRUE );
187
188     for(i = 0; i < LOADORDER_NTYPES; i++)
189     {
190         if (loadorder[i] == LOADORDER_INVALID) break;
191         switch(loadorder[i])
192         {
193         case LOADORDER_DLL:
194             TRACE( "Trying native exe %s\n", debugstr_a(name) );
195             if (handle != INVALID_HANDLE_VALUE) return handle;
196             break;
197         case LOADORDER_BI:
198             TRACE( "Trying built-in exe %s\n", debugstr_a(name) );
199             open_builtin_exe_file( name, NULL, 0, 1, &file_exists );
200             if (file_exists)
201             {
202                 if (handle != INVALID_HANDLE_VALUE) CloseHandle(handle);
203                 return 0;
204             }
205         default:
206             break;
207         }
208     }
209     if (handle != INVALID_HANDLE_VALUE) CloseHandle(handle);
210
211  error:
212     SetLastError( ERROR_FILE_NOT_FOUND );
213     return INVALID_HANDLE_VALUE;
214 }
215
216
217 /***********************************************************************
218  *           find_exe_file
219  *
220  * Open an exe file, and return the full name and file handle.
221  * Returns FALSE if file could not be found.
222  * If file exists but cannot be opened, returns TRUE and set handle to INVALID_HANDLE_VALUE.
223  * If file is a builtin exe, returns TRUE and sets handle to 0.
224  */
225 static BOOL find_exe_file( const char *name, char *buffer, int buflen, HANDLE *handle )
226 {
227     enum loadorder_type loadorder[LOADORDER_NTYPES];
228     int i, file_exists;
229
230     TRACE("looking for %s\n", debugstr_a(name) );
231
232     if (!SearchPathA( NULL, name, ".exe", buflen, buffer, NULL ) &&
233         !MODULE_GetBuiltinPath( name, ".exe", buffer, buflen ))
234     {
235         /* no builtin found, try native without extension in case it is a Unix app */
236
237         if (SearchPathA( NULL, name, NULL, buflen, buffer, NULL ))
238         {
239             TRACE( "Trying native/Unix binary %s\n", debugstr_a(buffer) );
240             if ((*handle = CreateFileA( buffer, GENERIC_READ, FILE_SHARE_READ,
241                                         NULL, OPEN_EXISTING, 0, 0 )) != INVALID_HANDLE_VALUE)
242                 return TRUE;
243         }
244         return FALSE;
245     }
246
247     MODULE_GetLoadOrder( loadorder, buffer, TRUE );
248
249     for(i = 0; i < LOADORDER_NTYPES; i++)
250     {
251         if (loadorder[i] == LOADORDER_INVALID) break;
252         switch(loadorder[i])
253         {
254         case LOADORDER_DLL:
255             TRACE( "Trying native exe %s\n", debugstr_a(buffer) );
256             if ((*handle = CreateFileA( buffer, GENERIC_READ, FILE_SHARE_READ,
257                                         NULL, OPEN_EXISTING, 0, 0 )) != INVALID_HANDLE_VALUE)
258                 return TRUE;
259             if (GetLastError() != ERROR_FILE_NOT_FOUND) return TRUE;
260             break;
261         case LOADORDER_BI:
262             TRACE( "Trying built-in exe %s\n", debugstr_a(buffer) );
263             open_builtin_exe_file( buffer, NULL, 0, 1, &file_exists );
264             if (file_exists)
265             {
266                 *handle = 0;
267                 return TRUE;
268             }
269             break;
270         default:
271             break;
272         }
273     }
274     SetLastError( ERROR_FILE_NOT_FOUND );
275     return FALSE;
276 }
277
278
279 /***********************************************************************
280  *           process_init
281  *
282  * Main process initialisation code
283  */
284 static BOOL process_init( char *argv[] )
285 {
286     BOOL ret;
287     size_t info_size = 0;
288
289     setbuf(stdout,NULL);
290     setbuf(stderr,NULL);
291     setlocale(LC_CTYPE,"");
292
293     /* store the program name */
294     argv0 = argv[0];
295
296     /* Fill the initial process structure */
297     current_process.threads           = 1;
298     current_process.running_threads   = 1;
299     current_process.ring0_threads     = 1;
300     current_process.group             = &current_process;
301     current_process.priority          = 8;  /* Normal */
302     current_process.ProcessParameters = &process_pmts;
303     current_process.LdrData           = &process_ldr;
304     InitializeListHead(&process_ldr.InLoadOrderModuleList);
305     InitializeListHead(&process_ldr.InMemoryOrderModuleList);
306     InitializeListHead(&process_ldr.InInitializationOrderModuleList);
307
308     /* Setup the server connection */
309     CLIENT_InitServer();
310
311     /* Retrieve startup info from the server */
312     SERVER_START_REQ( init_process )
313     {
314         req->ldt_copy  = &wine_ldt_copy;
315         if ((ret = !wine_server_call_err( req )))
316         {
317             main_exe_file     = reply->exe_file;
318             main_create_flags = reply->create_flags;
319             info_size         = reply->info_size;
320             server_startticks = reply->server_start;
321             process_pmts.hStdInput   = reply->hstdin;
322             process_pmts.hStdOutput  = reply->hstdout;
323             process_pmts.hStdError   = reply->hstderr;
324         }
325     }
326     SERVER_END_REQ;
327     if (!ret) return FALSE;
328
329     /* Create the process heap */
330     current_process.heap = RtlCreateHeap( HEAP_GROWABLE, NULL, 0, 0, NULL, NULL );
331
332     if (info_size == 0)
333     {
334         /* This is wine specific: we have no parent (we're started from unix)
335          * so, create a simple console with bare handles to unix stdio 
336          * input & output streams (aka simple console)
337          */
338         wine_server_fd_to_handle( 0, GENERIC_READ|SYNCHRONIZE,  TRUE, &process_pmts.hStdInput );
339         wine_server_fd_to_handle( 1, GENERIC_WRITE|SYNCHRONIZE, TRUE, &process_pmts.hStdOutput );
340         wine_server_fd_to_handle( 2, GENERIC_WRITE|SYNCHRONIZE, TRUE, &process_pmts.hStdError );
341     }
342     else
343     {
344         /* convert value from server:
345          * + 0 => INVALID_HANDLE_VALUE
346          * + console handle need to be mapped
347          */
348         if (!process_pmts.hStdInput)
349             process_pmts.hStdInput = INVALID_HANDLE_VALUE;
350         else if (VerifyConsoleIoHandle(console_handle_map(process_pmts.hStdInput)))
351             process_pmts.hStdInput = console_handle_map(process_pmts.hStdInput);
352         if (!process_pmts.hStdOutput)
353             process_pmts.hStdOutput = INVALID_HANDLE_VALUE;
354         else if (VerifyConsoleIoHandle(console_handle_map(process_pmts.hStdOutput)))
355             process_pmts.hStdOutput = console_handle_map(process_pmts.hStdOutput);
356         if (!process_pmts.hStdError)
357             process_pmts.hStdError = INVALID_HANDLE_VALUE;
358         else if (VerifyConsoleIoHandle(console_handle_map(process_pmts.hStdError)))
359             process_pmts.hStdError = console_handle_map(process_pmts.hStdError);
360     }
361
362     /* Copy the parent environment */
363     if (!init_user_process_pmts( info_size, main_exe_name, sizeof(main_exe_name) ))
364         return FALSE;
365
366     /* Parse command line arguments */
367     OPTIONS_ParseOptions( !info_size ? argv : NULL );
368
369     /* <hack: to be changed later on> */
370     process_pmts.CurrentDirectoryName.Length = 3 * sizeof(WCHAR);
371     process_pmts.CurrentDirectoryName.MaximumLength = RtlGetLongestNtPathLength() * sizeof(WCHAR);
372     process_pmts.CurrentDirectoryName.Buffer = RtlAllocateHeap( GetProcessHeap(), 0, process_pmts.CurrentDirectoryName.MaximumLength);
373     process_pmts.CurrentDirectoryName.Buffer[0] = 'C';
374     process_pmts.CurrentDirectoryName.Buffer[1] = ':';
375     process_pmts.CurrentDirectoryName.Buffer[2] = '\\';
376     process_pmts.CurrentDirectoryName.Buffer[3] = '\0';
377     /* </hack: to be changed later on> */
378
379     /* initialise DOS drives */
380     if (!DRIVE_Init()) return FALSE;
381
382     /* initialise DOS directories */
383     if (!DIR_Init()) return FALSE;
384
385     /* registry initialisation */
386     SHELL_LoadRegistry();
387
388     /* global boot finished, the rest is process-local */
389     CLIENT_BootDone( TRACE_ON(server) );
390     if (TRACE_ON(relay) || TRACE_ON(snoop)) RELAY_InitDebugLists();
391
392     return TRUE;
393 }
394
395
396 /***********************************************************************
397  *           start_process
398  *
399  * Startup routine of a new process. Runs on the new process stack.
400  */
401 static void start_process( void *arg )
402 {
403     __TRY
404     {
405         LPTHREAD_START_ROUTINE entry;
406         HANDLE main_file = main_exe_file;
407         IMAGE_NT_HEADERS *nt;
408         PEB *peb = NtCurrentTeb()->Peb;
409
410         if (main_file)
411         {
412             UINT drive_type = GetDriveTypeA( main_exe_name );
413             /* don't keep the file handle open on removable media */
414             if (drive_type == DRIVE_REMOVABLE || drive_type == DRIVE_CDROM) main_file = 0;
415         }
416
417         /* Retrieve entry point address */
418         nt = RtlImageNtHeader( peb->ImageBaseAddress );
419         entry = (LPTHREAD_START_ROUTINE)((char*)peb->ImageBaseAddress +
420                                          nt->OptionalHeader.AddressOfEntryPoint);
421
422         /* Install signal handlers; this cannot be done before, since we cannot
423          * send exceptions to the debugger before the create process event that
424          * is sent by REQ_INIT_PROCESS_DONE.
425          * We do need the handlers in place by the time the request is over, so
426          * we set them up here. If we segfault between here and the server call
427          * something is very wrong... */
428         if (!SIGNAL_Init()) goto error;
429
430         /* Signal the parent process to continue */
431         SERVER_START_REQ( init_process_done )
432         {
433             req->module      = peb->ImageBaseAddress;
434             req->module_size = nt->OptionalHeader.SizeOfImage;
435             req->entry       = entry;
436             /* API requires a double indirection */
437             req->name        = &main_exe_name_ptr;
438             req->exe_file    = main_file;
439             req->gui         = (nt->OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_WINDOWS_CUI);
440             wine_server_add_data( req, main_exe_name, strlen(main_exe_name) );
441             wine_server_call( req );
442             peb->BeingDebugged = reply->debugged;
443         }
444         SERVER_END_REQ;
445
446         /* create the main modref and load dependencies */
447         if (!PE_CreateModule( peb->ImageBaseAddress, main_exe_name, 0, 0, FALSE )) goto error;
448
449         if (main_exe_file) CloseHandle( main_exe_file ); /* we no longer need it */
450
451         MODULE_DllProcessAttach( NULL, (LPVOID)1 );
452
453         if (TRACE_ON(relay))
454             DPRINTF( "%04lx:Starting process %s (entryproc=%p)\n",
455                      GetCurrentThreadId(), main_exe_name, entry );
456         if (peb->BeingDebugged) DbgBreakPoint();
457         SetLastError(0);  /* clear error code */
458         ExitThread( entry( NtCurrentTeb()->Peb ) );
459
460     error:
461         ExitProcess( GetLastError() );
462     }
463     __EXCEPT(UnhandledExceptionFilter)
464     {
465         TerminateThread( GetCurrentThread(), GetExceptionCode() );
466     }
467     __ENDTRY
468 }
469
470
471 /***********************************************************************
472  *           __wine_process_init
473  *
474  * Wine initialisation: load and start the main exe file.
475  */
476 void __wine_process_init( int argc, char *argv[] )
477 {
478     char error[1024], *p;
479     DWORD stack_size = 0;
480     int file_exists;
481
482     /* Initialize everything */
483     if (!process_init( argv )) exit(1);
484
485     argv++;  /* remove argv[0] (wine itself) */
486
487     TRACE( "starting process name=%s file=%p argv[0]=%s\n",
488            debugstr_a(main_exe_name), main_exe_file, debugstr_a(argv[0]) );
489
490     if (!main_exe_name[0])
491     {
492         if (!argv[0]) OPTIONS_Usage();
493
494         if (!find_exe_file( argv[0], main_exe_name, sizeof(main_exe_name), &main_exe_file ))
495         {
496             MESSAGE( "%s: cannot find '%s'\n", argv0, argv[0] );
497             ExitProcess(1);
498         }
499         if (main_exe_file == INVALID_HANDLE_VALUE)
500         {
501             MESSAGE( "%s: cannot open '%s'\n", argv0, main_exe_name );
502             ExitProcess(1);
503         }
504     }
505
506     if (!main_exe_file)  /* no file handle -> Winelib app */
507     {
508         TRACE( "starting Winelib app %s\n", debugstr_a(main_exe_name) );
509         if (open_builtin_exe_file( main_exe_name, error, sizeof(error), 0, &file_exists ))
510             goto found;
511         MESSAGE( "%s: cannot open builtin library for '%s': %s\n", argv0, main_exe_name, error );
512         ExitProcess(1);
513     }
514     VERSION_Init( main_exe_name );
515
516     switch( MODULE_GetBinaryType( main_exe_file ))
517     {
518     case BINARY_PE_EXE:
519         TRACE( "starting Win32 binary %s\n", debugstr_a(main_exe_name) );
520         if ((current_process.module = PE_LoadImage( main_exe_file, main_exe_name, 0 ))) goto found;
521         MESSAGE( "%s: could not load '%s' as Win32 binary\n", argv0, main_exe_name );
522         ExitProcess(1);
523     case BINARY_PE_DLL:
524         MESSAGE( "%s: '%s' is a DLL, not an executable\n", argv0, main_exe_name );
525         ExitProcess(1);
526     case BINARY_UNKNOWN:
527         /* check for .com extension */
528         if (!(p = strrchr( main_exe_name, '.' )) || FILE_strcasecmp( p, ".com" ))
529         {
530             MESSAGE( "%s: cannot determine executable type for '%s'\n", argv0, main_exe_name );
531             ExitProcess(1);
532         }
533         /* fall through */
534     case BINARY_WIN16:
535     case BINARY_DOS:
536         TRACE( "starting Win16/DOS binary %s\n", debugstr_a(main_exe_name) );
537         CloseHandle( main_exe_file );
538         main_exe_file = 0;
539         argv--;
540         argv[0] = "winevdm.exe";
541         if (open_builtin_exe_file( "winevdm.exe", error, sizeof(error), 0, &file_exists ))
542             goto found;
543         MESSAGE( "%s: trying to run '%s', cannot open builtin library for 'winevdm.exe': %s\n",
544                  argv0, main_exe_name, error );
545         ExitProcess(1);
546     case BINARY_OS216:
547         MESSAGE( "%s: '%s' is an OS/2 binary, not supported\n", argv0, main_exe_name );
548         ExitProcess(1);
549     case BINARY_UNIX_EXE:
550         MESSAGE( "%s: '%s' is a Unix binary, not supported\n", argv0, main_exe_name );
551         ExitProcess(1);
552     case BINARY_UNIX_LIB:
553         {
554             DOS_FULL_NAME full_name;
555             const char *name = main_exe_name;
556             UNICODE_STRING nameW;
557
558             TRACE( "starting Winelib app %s\n", debugstr_a(main_exe_name) );
559             RtlCreateUnicodeStringFromAsciiz(&nameW, name);
560             if (DOSFS_GetFullName( nameW.Buffer, TRUE, &full_name )) name = full_name.long_name;
561             RtlFreeUnicodeString(&nameW);
562             CloseHandle( main_exe_file );
563             main_exe_file = 0;
564             if (wine_dlopen( name, RTLD_NOW, error, sizeof(error) ))
565             {
566                 if ((p = strrchr( main_exe_name, '.' )) && !strcmp( p, ".so" )) *p = 0;
567                 goto found;
568             }
569             MESSAGE( "%s: could not load '%s': %s\n", argv0, main_exe_name, error );
570             ExitProcess(1);
571         }
572     }
573
574  found:
575     /* build command line */
576     if (!build_command_line( argv )) goto error;
577
578     /* create 32-bit module for main exe */
579     if (!(current_process.module = BUILTIN32_LoadExeModule( current_process.module ))) goto error;
580     stack_size = RtlImageNtHeader(current_process.module)->OptionalHeader.SizeOfStackReserve;
581
582     /* allocate main thread stack */
583     if (!THREAD_InitStack( NtCurrentTeb(), stack_size )) goto error;
584
585     /* switch to the new stack */
586     wine_switch_to_stack( start_process, NULL, NtCurrentTeb()->Tib.StackBase );
587
588  error:
589     ExitProcess( GetLastError() );
590 }
591
592
593 /***********************************************************************
594  *           build_argv
595  *
596  * Build an argv array from a command-line.
597  * The command-line is modified to insert nulls.
598  * 'reserved' is the number of args to reserve before the first one.
599  */
600 static char **build_argv( char *cmdline, int reserved )
601 {
602     int argc;
603     char** argv;
604     char *arg,*s,*d;
605     int in_quotes,bcount;
606
607     argc=reserved+1;
608     bcount=0;
609     in_quotes=0;
610     s=cmdline;
611     while (1) {
612         if (*s=='\0' || ((*s==' ' || *s=='\t') && !in_quotes)) {
613             /* space */
614             argc++;
615             /* skip the remaining spaces */
616             while (*s==' ' || *s=='\t') {
617                 s++;
618             }
619             if (*s=='\0')
620                 break;
621             bcount=0;
622             continue;
623         } else if (*s=='\\') {
624             /* '\', count them */
625             bcount++;
626         } else if ((*s=='"') && ((bcount & 1)==0)) {
627             /* unescaped '"' */
628             in_quotes=!in_quotes;
629             bcount=0;
630         } else {
631             /* a regular character */
632             bcount=0;
633         }
634         s++;
635     }
636     argv=malloc(argc*sizeof(*argv));
637     if (!argv)
638         return NULL;
639
640     arg=d=s=cmdline;
641     bcount=0;
642     in_quotes=0;
643     argc=reserved;
644     while (*s) {
645         if ((*s==' ' || *s=='\t') && !in_quotes) {
646             /* Close the argument and copy it */
647             *d=0;
648             argv[argc++]=arg;
649
650             /* skip the remaining spaces */
651             do {
652                 s++;
653             } while (*s==' ' || *s=='\t');
654
655             /* Start with a new argument */
656             arg=d=s;
657             bcount=0;
658         } else if (*s=='\\') {
659             /* '\\' */
660             *d++=*s++;
661             bcount++;
662         } else if (*s=='"') {
663             /* '"' */
664             if ((bcount & 1)==0) {
665                 /* Preceeded by an even number of '\', this is half that
666                  * number of '\', plus a '"' which we discard.
667                  */
668                 d-=bcount/2;
669                 s++;
670                 in_quotes=!in_quotes;
671             } else {
672                 /* Preceeded by an odd number of '\', this is half that
673                  * number of '\' followed by a '"'
674                  */
675                 d=d-bcount/2-1;
676                 *d++='"';
677                 s++;
678             }
679             bcount=0;
680         } else {
681             /* a regular character */
682             *d++=*s++;
683             bcount=0;
684         }
685     }
686     if (*arg) {
687         *d='\0';
688         argv[argc++]=arg;
689     }
690     argv[argc]=NULL;
691
692     return argv;
693 }
694
695
696 /***********************************************************************
697  *           build_envp
698  *
699  * Build the environment of a new child process.
700  */
701 static char **build_envp( const char *env, const char *extra_env )
702 {
703     const char *p;
704     char **envp;
705     int count = 0;
706
707     if (extra_env) for (p = extra_env; *p; count++) p += strlen(p) + 1;
708     for (p = env; *p; count++) p += strlen(p) + 1;
709     count += 3;
710
711     if ((envp = malloc( count * sizeof(*envp) )))
712     {
713         extern char **environ;
714         char **envptr = envp;
715         char **unixptr = environ;
716         /* first the extra strings */
717         if (extra_env) for (p = extra_env; *p; p += strlen(p) + 1) *envptr++ = (char *)p;
718         /* then put PATH, HOME and WINEPREFIX from the unix env */
719         for (unixptr = environ; unixptr && *unixptr; unixptr++)
720             if (!memcmp( *unixptr, "PATH=", 5 ) ||
721                 !memcmp( *unixptr, "HOME=", 5 ) ||
722                 !memcmp( *unixptr, "WINEPREFIX=", 11 )) *envptr++ = *unixptr;
723         /* now put the Windows environment strings */
724         for (p = env; *p; p += strlen(p) + 1)
725         {
726             if (!memcmp( p, "PATH=", 5 ))  /* store PATH as WINEPATH */
727             {
728                 char *winepath = malloc( strlen(p) + 5 );
729                 strcpy( winepath, "WINE" );
730                 strcpy( winepath + 4, p );
731                 *envptr++ = winepath;
732             }
733             else if (memcmp( p, "HOME=", 5 ) &&
734                      memcmp( p, "WINEPATH=", 9 ) &&
735                      memcmp( p, "WINEPREFIX=", 11 )) *envptr++ = (char *)p;
736         }
737         *envptr = 0;
738     }
739     return envp;
740 }
741
742
743 /***********************************************************************
744  *           exec_wine_binary
745  *
746  * Locate the Wine binary to exec for a new Win32 process.
747  */
748 static void exec_wine_binary( char **argv, char **envp )
749 {
750     const char *path, *pos, *ptr;
751
752     /* first, try for a WINELOADER environment variable */
753     argv[0] = getenv("WINELOADER");
754     if (argv[0])
755         execve( argv[0], argv, envp );
756
757     /* next, try bin directory */
758     argv[0] = BINDIR "/wine";
759     execve( argv[0], argv, envp );
760
761     /* now try the path of argv0 of the current binary */
762     if (!(argv[0] = malloc( strlen(full_argv0) + 6 ))) return;
763     if ((ptr = strrchr( full_argv0, '/' )))
764     {
765         memcpy( argv[0], full_argv0, ptr - full_argv0 );
766         strcpy( argv[0] + (ptr - full_argv0), "/wine" );
767         execve( argv[0], argv, envp );
768     }
769     free( argv[0] );
770
771     /* now search in the Unix path */
772     if ((path = getenv( "PATH" )))
773     {
774         if (!(argv[0] = malloc( strlen(path) + 6 ))) return;
775         pos = path;
776         for (;;)
777         {
778             while (*pos == ':') pos++;
779             if (!*pos) break;
780             if (!(ptr = strchr( pos, ':' ))) ptr = pos + strlen(pos);
781             memcpy( argv[0], pos, ptr - pos );
782             strcpy( argv[0] + (ptr - pos), "/wine" );
783             execve( argv[0], argv, envp );
784             pos = ptr;
785         }
786     }
787     free( argv[0] );
788 }
789
790
791 /***********************************************************************
792  *           fork_and_exec
793  *
794  * Fork and exec a new Unix binary, checking for errors.
795  */
796 static int fork_and_exec( const char *filename, char *cmdline,
797                           const char *env, const char *newdir )
798 {
799     int fd[2];
800     int pid, err;
801
802     if (!env) env = GetEnvironmentStringsA();
803
804     if (pipe(fd) == -1)
805     {
806         FILE_SetDosError();
807         return -1;
808     }
809     fcntl( fd[1], F_SETFD, 1 );  /* set close on exec */
810     if (!(pid = fork()))  /* child */
811     {
812         char **argv = build_argv( cmdline, 0 );
813         char **envp = build_envp( env, NULL );
814         close( fd[0] );
815
816         /* Reset signals that we previously set to SIG_IGN */
817         signal( SIGPIPE, SIG_DFL );
818         signal( SIGCHLD, SIG_DFL );
819
820         if (newdir) chdir(newdir);
821
822         if (argv && envp) execve( filename, argv, envp );
823         err = errno;
824         write( fd[1], &err, sizeof(err) );
825         _exit(1);
826     }
827     close( fd[1] );
828     if ((pid != -1) && (read( fd[0], &err, sizeof(err) ) > 0))  /* exec failed */
829     {
830         errno = err;
831         pid = -1;
832     }
833     if (pid == -1) FILE_SetDosError();
834     close( fd[0] );
835     return pid;
836 }
837
838
839 /***********************************************************************
840  *           create_process
841  *
842  * Create a new process. If hFile is a valid handle we have an exe
843  * file, otherwise it is a Winelib app.
844  */
845 static BOOL create_process( HANDLE hFile, LPCSTR filename, LPSTR cmd_line, LPCSTR env,
846                             LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
847                             BOOL inherit, DWORD flags, LPSTARTUPINFOA startup,
848                             LPPROCESS_INFORMATION info, LPCSTR unixdir )
849 {
850     BOOL ret, success = FALSE;
851     HANDLE process_info;
852     startup_info_t startup_info;
853     char *extra_env = NULL;
854     int startfd[2];
855     int execfd[2];
856     pid_t pid;
857     int err;
858     char dummy = 0;
859
860     if (!env)
861     {
862         env = GetEnvironmentStringsA();
863         extra_env = DRIVE_BuildEnv();
864     }
865
866     /* create the synchronization pipes */
867
868     if (pipe( startfd ) == -1)
869     {
870         FILE_SetDosError();
871         return FALSE;
872     }
873     if (pipe( execfd ) == -1)
874     {
875         close( startfd[0] );
876         close( startfd[1] );
877         FILE_SetDosError();
878         return FALSE;
879     }
880     fcntl( execfd[1], F_SETFD, 1 );  /* set close on exec */
881
882     /* create the child process */
883
884     if (!(pid = fork()))  /* child */
885     {
886         char **argv = build_argv( cmd_line, 1 );
887         char **envp = build_envp( env, extra_env );
888
889         close( startfd[1] );
890         close( execfd[0] );
891
892         /* wait for parent to tell us to start */
893         if (read( startfd[0], &dummy, 1 ) != 1) _exit(1);
894
895         close( startfd[0] );
896         /* Reset signals that we previously set to SIG_IGN */
897         signal( SIGPIPE, SIG_DFL );
898         signal( SIGCHLD, SIG_DFL );
899
900         if (unixdir) chdir(unixdir);
901
902         if (argv && envp) exec_wine_binary( argv, envp );
903
904         err = errno;
905         write( execfd[1], &err, sizeof(err) );
906         _exit(1);
907     }
908
909     /* this is the parent */
910
911     close( startfd[0] );
912     close( execfd[1] );
913     if (extra_env) HeapFree( GetProcessHeap(), 0, extra_env );
914     if (pid == -1)
915     {
916         close( startfd[1] );
917         close( execfd[0] );
918         FILE_SetDosError();
919         return FALSE;
920     }
921
922     /* fill the startup info structure */
923
924     startup_info.size        = sizeof(startup_info);
925     /* startup_info.filename_len is set below */
926     startup_info.cmdline_len = cmd_line ? strlen(cmd_line) : 0;
927     startup_info.desktop_len = startup->lpDesktop ? strlen(startup->lpDesktop) : 0;
928     startup_info.title_len   = startup->lpTitle ? strlen(startup->lpTitle) : 0;
929     startup_info.x           = startup->dwX;
930     startup_info.y           = startup->dwY;
931     startup_info.cx          = startup->dwXSize;
932     startup_info.cy          = startup->dwYSize;
933     startup_info.x_chars     = startup->dwXCountChars;
934     startup_info.y_chars     = startup->dwYCountChars;
935     startup_info.attribute   = startup->dwFillAttribute;
936     startup_info.cmd_show    = startup->wShowWindow;
937     startup_info.flags       = startup->dwFlags;
938
939     /* create the process on the server side */
940
941     SERVER_START_REQ( new_process )
942     {
943         char buf[MAX_PATH];
944         LPCSTR nameptr;
945
946         req->inherit_all  = inherit;
947         req->create_flags = flags;
948         req->unix_pid     = pid;
949         req->exe_file     = hFile;
950         if (startup->dwFlags & STARTF_USESTDHANDLES)
951         {
952             req->hstdin  = startup->hStdInput;
953             req->hstdout = startup->hStdOutput;
954             req->hstderr = startup->hStdError;
955         }
956         else
957         {
958             req->hstdin  = GetStdHandle( STD_INPUT_HANDLE );
959             req->hstdout = GetStdHandle( STD_OUTPUT_HANDLE );
960             req->hstderr = GetStdHandle( STD_ERROR_HANDLE );
961         }
962
963         if ((flags & (CREATE_NEW_CONSOLE | DETACHED_PROCESS)) != 0)
964         {
965             /* this is temporary (for console handles). We have no way to control that the handle is invalid in child process otherwise */
966             if (is_console_handle(req->hstdin))  req->hstdin  = INVALID_HANDLE_VALUE;
967             if (is_console_handle(req->hstdout)) req->hstdout = INVALID_HANDLE_VALUE;
968             if (is_console_handle(req->hstderr)) req->hstderr = INVALID_HANDLE_VALUE;
969         }
970         else
971         {
972             if (is_console_handle(req->hstdin))  req->hstdin  = console_handle_unmap(req->hstdin);
973             if (is_console_handle(req->hstdout)) req->hstdout = console_handle_unmap(req->hstdout);
974             if (is_console_handle(req->hstderr)) req->hstderr = console_handle_unmap(req->hstderr);
975         }
976
977         if (GetLongPathNameA( filename, buf, MAX_PATH ))
978             nameptr = buf;
979         else
980             nameptr = filename;
981
982         startup_info.filename_len = strlen(nameptr);
983         wine_server_add_data( req, &startup_info, sizeof(startup_info) );
984         wine_server_add_data( req, nameptr, startup_info.filename_len );
985         wine_server_add_data( req, cmd_line, startup_info.cmdline_len );
986         wine_server_add_data( req, startup->lpDesktop, startup_info.desktop_len );
987         wine_server_add_data( req, startup->lpTitle, startup_info.title_len );
988
989         ret = !wine_server_call_err( req );
990         process_info = reply->info;
991     }
992     SERVER_END_REQ;
993
994     if (!ret)
995     {
996         close( startfd[1] );
997         close( execfd[0] );
998         return FALSE;
999     }
1000
1001     /* tell child to start and wait for it to exec */
1002
1003     write( startfd[1], &dummy, 1 );
1004     close( startfd[1] );
1005
1006     if (read( execfd[0], &err, sizeof(err) ) > 0) /* exec failed */
1007     {
1008         errno = err;
1009         FILE_SetDosError();
1010         close( execfd[0] );
1011         CloseHandle( process_info );
1012         return FALSE;
1013     }
1014
1015     /* wait for the new process info to be ready */
1016
1017     WaitForSingleObject( process_info, INFINITE );
1018     SERVER_START_REQ( get_new_process_info )
1019     {
1020         req->info     = process_info;
1021         req->pinherit = (psa && (psa->nLength >= sizeof(*psa)) && psa->bInheritHandle);
1022         req->tinherit = (tsa && (tsa->nLength >= sizeof(*tsa)) && tsa->bInheritHandle);
1023         if ((ret = !wine_server_call_err( req )))
1024         {
1025             info->dwProcessId = (DWORD)reply->pid;
1026             info->dwThreadId  = (DWORD)reply->tid;
1027             info->hProcess    = reply->phandle;
1028             info->hThread     = reply->thandle;
1029             success           = reply->success;
1030         }
1031     }
1032     SERVER_END_REQ;
1033
1034     if (ret && !success)  /* new process failed to start */
1035     {
1036         DWORD exitcode;
1037         if (GetExitCodeProcess( info->hProcess, &exitcode )) SetLastError( exitcode );
1038         CloseHandle( info->hThread );
1039         CloseHandle( info->hProcess );
1040         ret = FALSE;
1041     }
1042     CloseHandle( process_info );
1043     return ret;
1044 }
1045
1046
1047 /***********************************************************************
1048  *           create_vdm_process
1049  *
1050  * Create a new VDM process for a 16-bit or DOS application.
1051  */
1052 static BOOL create_vdm_process( LPCSTR filename, LPSTR cmd_line, LPCSTR env,
1053                                 LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
1054                                 BOOL inherit, DWORD flags, LPSTARTUPINFOA startup,
1055                                 LPPROCESS_INFORMATION info, LPCSTR unixdir )
1056 {
1057     BOOL ret;
1058     LPSTR new_cmd_line = HeapAlloc( GetProcessHeap(), 0, strlen(filename) + strlen(cmd_line) + 30 );
1059
1060     if (!new_cmd_line)
1061     {
1062         SetLastError( ERROR_OUTOFMEMORY );
1063         return FALSE;
1064     }
1065     sprintf( new_cmd_line, "winevdm.exe --app-name \"%s\" %s", filename, cmd_line );
1066     ret = create_process( 0, "winevdm.exe", new_cmd_line, env, psa, tsa, inherit,
1067                           flags, startup, info, unixdir );
1068     HeapFree( GetProcessHeap(), 0, new_cmd_line );
1069     return ret;
1070 }
1071
1072
1073 /*************************************************************************
1074  *               get_file_name
1075  *
1076  * Helper for CreateProcess: retrieve the file name to load from the
1077  * app name and command line. Store the file name in buffer, and
1078  * return a possibly modified command line.
1079  * Also returns a handle to the opened file if it's a Windows binary.
1080  */
1081 static LPSTR get_file_name( LPCSTR appname, LPSTR cmdline, LPSTR buffer,
1082                             int buflen, HANDLE *handle )
1083 {
1084     char *name, *pos, *ret = NULL;
1085     const char *p;
1086
1087     /* if we have an app name, everything is easy */
1088
1089     if (appname)
1090     {
1091         /* use the unmodified app name as file name */
1092         lstrcpynA( buffer, appname, buflen );
1093         *handle = open_exe_file( buffer );
1094         if (!(ret = cmdline) || !cmdline[0])
1095         {
1096             /* no command-line, create one */
1097             if ((ret = HeapAlloc( GetProcessHeap(), 0, strlen(appname) + 3 )))
1098                 sprintf( ret, "\"%s\"", appname );
1099         }
1100         return ret;
1101     }
1102
1103     if (!cmdline)
1104     {
1105         SetLastError( ERROR_INVALID_PARAMETER );
1106         return NULL;
1107     }
1108
1109     /* first check for a quoted file name */
1110
1111     if ((cmdline[0] == '"') && ((p = strchr( cmdline + 1, '"' ))))
1112     {
1113         int len = p - cmdline - 1;
1114         /* extract the quoted portion as file name */
1115         if (!(name = HeapAlloc( GetProcessHeap(), 0, len + 1 ))) return NULL;
1116         memcpy( name, cmdline + 1, len );
1117         name[len] = 0;
1118
1119         if (find_exe_file( name, buffer, buflen, handle ))
1120             ret = cmdline;  /* no change necessary */
1121         goto done;
1122     }
1123
1124     /* now try the command-line word by word */
1125
1126     if (!(name = HeapAlloc( GetProcessHeap(), 0, strlen(cmdline) + 1 ))) return NULL;
1127     pos = name;
1128     p = cmdline;
1129
1130     while (*p)
1131     {
1132         do *pos++ = *p++; while (*p && *p != ' ');
1133         *pos = 0;
1134         if (find_exe_file( name, buffer, buflen, handle ))
1135         {
1136             ret = cmdline;
1137             break;
1138         }
1139     }
1140
1141     if (!ret || !strchr( name, ' ' )) goto done;  /* no change necessary */
1142
1143     /* now build a new command-line with quotes */
1144
1145     if (!(ret = HeapAlloc( GetProcessHeap(), 0, strlen(cmdline) + 3 ))) goto done;
1146     sprintf( ret, "\"%s\"%s", name, p );
1147
1148  done:
1149     HeapFree( GetProcessHeap(), 0, name );
1150     return ret;
1151 }
1152
1153
1154 /**********************************************************************
1155  *       CreateProcessA          (KERNEL32.@)
1156  */
1157 BOOL WINAPI CreateProcessA( LPCSTR app_name, LPSTR cmd_line, LPSECURITY_ATTRIBUTES process_attr,
1158                             LPSECURITY_ATTRIBUTES thread_attr, BOOL inherit,
1159                             DWORD flags, LPVOID env, LPCSTR cur_dir,
1160                             LPSTARTUPINFOA startup_info, LPPROCESS_INFORMATION info )
1161 {
1162     BOOL retv = FALSE;
1163     HANDLE hFile = 0;
1164     const char *unixdir = NULL;
1165     DOS_FULL_NAME full_dir;
1166     char name[MAX_PATH];
1167     LPSTR tidy_cmdline;
1168     char *p;
1169
1170     /* Process the AppName and/or CmdLine to get module name and path */
1171
1172     TRACE("app %s cmdline %s\n", debugstr_a(app_name), debugstr_a(cmd_line) );
1173
1174     if (!(tidy_cmdline = get_file_name( app_name, cmd_line, name, sizeof(name), &hFile )))
1175         return FALSE;
1176     if (hFile == INVALID_HANDLE_VALUE) goto done;
1177
1178     /* Warn if unsupported features are used */
1179
1180     if (flags & NORMAL_PRIORITY_CLASS)
1181         FIXME("(%s,...): NORMAL_PRIORITY_CLASS ignored\n", name);
1182     if (flags & IDLE_PRIORITY_CLASS)
1183         FIXME("(%s,...): IDLE_PRIORITY_CLASS ignored\n", name);
1184     if (flags & HIGH_PRIORITY_CLASS)
1185         FIXME("(%s,...): HIGH_PRIORITY_CLASS ignored\n", name);
1186     if (flags & REALTIME_PRIORITY_CLASS)
1187         FIXME("(%s,...): REALTIME_PRIORITY_CLASS ignored\n", name);
1188     if (flags & CREATE_NEW_PROCESS_GROUP)
1189         FIXME("(%s,...): CREATE_NEW_PROCESS_GROUP ignored\n", name);
1190     if (flags & CREATE_UNICODE_ENVIRONMENT)
1191         FIXME("(%s,...): CREATE_UNICODE_ENVIRONMENT ignored\n", name);
1192     if (flags & CREATE_SEPARATE_WOW_VDM)
1193         FIXME("(%s,...): CREATE_SEPARATE_WOW_VDM ignored\n", name);
1194     if (flags & CREATE_SHARED_WOW_VDM)
1195         FIXME("(%s,...): CREATE_SHARED_WOW_VDM ignored\n", name);
1196     if (flags & CREATE_DEFAULT_ERROR_MODE)
1197         FIXME("(%s,...): CREATE_DEFAULT_ERROR_MODE ignored\n", name);
1198     if (flags & CREATE_NO_WINDOW)
1199         FIXME("(%s,...): CREATE_NO_WINDOW ignored\n", name);
1200     if (flags & PROFILE_USER)
1201         FIXME("(%s,...): PROFILE_USER ignored\n", name);
1202     if (flags & PROFILE_KERNEL)
1203         FIXME("(%s,...): PROFILE_KERNEL ignored\n", name);
1204     if (flags & PROFILE_SERVER)
1205         FIXME("(%s,...): PROFILE_SERVER ignored\n", name);
1206     if (startup_info->lpDesktop)
1207         FIXME("(%s,...): startup_info->lpDesktop %s ignored\n",
1208               name, debugstr_a(startup_info->lpDesktop));
1209     if (startup_info->dwFlags & STARTF_RUNFULLSCREEN)
1210         FIXME("(%s,...): STARTF_RUNFULLSCREEN ignored\n", name);
1211     if (startup_info->dwFlags & STARTF_FORCEONFEEDBACK)
1212         FIXME("(%s,...): STARTF_FORCEONFEEDBACK ignored\n", name);
1213     if (startup_info->dwFlags & STARTF_FORCEOFFFEEDBACK)
1214         FIXME("(%s,...): STARTF_FORCEOFFFEEDBACK ignored\n", name);
1215     if (startup_info->dwFlags & STARTF_USEHOTKEY)
1216         FIXME("(%s,...): STARTF_USEHOTKEY ignored\n", name);
1217
1218     if (cur_dir)
1219     {
1220         UNICODE_STRING cur_dirW;
1221         RtlCreateUnicodeStringFromAsciiz(&cur_dirW, cur_dir);
1222         if (DOSFS_GetFullName( cur_dirW.Buffer, TRUE, &full_dir ))
1223             unixdir = full_dir.long_name;
1224         RtlFreeUnicodeString(&cur_dirW);
1225     }
1226     else
1227     {
1228         WCHAR buf[MAX_PATH];
1229         if (GetCurrentDirectoryW(MAX_PATH, buf))
1230         {
1231             if (DOSFS_GetFullName( buf, TRUE, &full_dir )) unixdir = full_dir.long_name;
1232         }
1233     }
1234
1235     info->hThread = info->hProcess = 0;
1236     info->dwProcessId = info->dwThreadId = 0;
1237
1238     /* Determine executable type */
1239
1240     if (!hFile)  /* builtin exe */
1241     {
1242         TRACE( "starting %s as Winelib app\n", debugstr_a(name) );
1243         retv = create_process( 0, name, tidy_cmdline, env, process_attr, thread_attr,
1244                                inherit, flags, startup_info, info, unixdir );
1245         goto done;
1246     }
1247
1248     switch( MODULE_GetBinaryType( hFile ))
1249     {
1250     case BINARY_PE_EXE:
1251         TRACE( "starting %s as Win32 binary\n", debugstr_a(name) );
1252         retv = create_process( hFile, name, tidy_cmdline, env, process_attr, thread_attr,
1253                                inherit, flags, startup_info, info, unixdir );
1254         break;
1255     case BINARY_WIN16:
1256     case BINARY_DOS:
1257         TRACE( "starting %s as Win16/DOS binary\n", debugstr_a(name) );
1258         retv = create_vdm_process( name, tidy_cmdline, env, process_attr, thread_attr,
1259                                    inherit, flags, startup_info, info, unixdir );
1260         break;
1261     case BINARY_OS216:
1262         FIXME( "%s is OS/2 binary, not supported\n", debugstr_a(name) );
1263         SetLastError( ERROR_BAD_EXE_FORMAT );
1264         break;
1265     case BINARY_PE_DLL:
1266         TRACE( "not starting %s since it is a dll\n", debugstr_a(name) );
1267         SetLastError( ERROR_BAD_EXE_FORMAT );
1268         break;
1269     case BINARY_UNIX_LIB:
1270         TRACE( "%s is a Unix library, starting as Winelib app\n", debugstr_a(name) );
1271         retv = create_process( hFile, name, tidy_cmdline, env, process_attr, thread_attr,
1272                                inherit, flags, startup_info, info, unixdir );
1273         break;
1274     case BINARY_UNKNOWN:
1275         /* check for .com or .bat extension */
1276         if ((p = strrchr( name, '.' )))
1277         {
1278             if (!FILE_strcasecmp( p, ".com" ))
1279             {
1280                 TRACE( "starting %s as DOS binary\n", debugstr_a(name) );
1281                 retv = create_vdm_process( name, tidy_cmdline, env, process_attr, thread_attr,
1282                                            inherit, flags, startup_info, info, unixdir );
1283                 break;
1284             }
1285             if (!FILE_strcasecmp( p, ".bat" ))
1286             {
1287                 char comspec[MAX_PATH];
1288                 if (GetEnvironmentVariableA("COMSPEC", comspec, sizeof(comspec)))
1289                 {
1290                     char *newcmdline;
1291                     if ((newcmdline = HeapAlloc( GetProcessHeap(), 0,
1292                                                  strlen(comspec) + 4 + strlen(tidy_cmdline) + 1)))
1293                     {
1294                         sprintf( newcmdline, "%s /c %s", comspec,  tidy_cmdline);
1295                         TRACE( "starting %s as batch binary: %s\n",
1296                                debugstr_a(name), debugstr_a(newcmdline) );
1297                         retv = CreateProcessA( comspec, newcmdline, process_attr, thread_attr,
1298                                                inherit, flags, env, cur_dir, startup_info, info );
1299                         HeapFree( GetProcessHeap(), 0, newcmdline );
1300                         break;
1301                     }
1302                 }
1303             }
1304         }
1305         /* fall through */
1306     case BINARY_UNIX_EXE:
1307         {
1308             /* unknown file, try as unix executable */
1309             UNICODE_STRING nameW;
1310             DOS_FULL_NAME full_name;
1311             const char *unixfilename = name;
1312
1313             TRACE( "starting %s as Unix binary\n", debugstr_a(name) );
1314
1315             RtlCreateUnicodeStringFromAsciiz(&nameW, name);
1316             if (DOSFS_GetFullName( nameW.Buffer, TRUE, &full_name )) unixfilename = full_name.long_name;
1317             RtlFreeUnicodeString(&nameW);
1318             retv = (fork_and_exec( unixfilename, tidy_cmdline, env, unixdir ) != -1);
1319         }
1320         break;
1321     }
1322     CloseHandle( hFile );
1323
1324  done:
1325     if (tidy_cmdline != cmd_line) HeapFree( GetProcessHeap(), 0, tidy_cmdline );
1326     return retv;
1327 }
1328
1329
1330 /**********************************************************************
1331  *       CreateProcessW          (KERNEL32.@)
1332  * NOTES
1333  *  lpReserved is not converted
1334  */
1335 BOOL WINAPI CreateProcessW( LPCWSTR app_name, LPWSTR cmd_line, LPSECURITY_ATTRIBUTES process_attr,
1336                             LPSECURITY_ATTRIBUTES thread_attr, BOOL inherit, DWORD flags,
1337                             LPVOID env, LPCWSTR cur_dir, LPSTARTUPINFOW startup_info,
1338                             LPPROCESS_INFORMATION info )
1339 {
1340     BOOL ret;
1341     STARTUPINFOA StartupInfoA;
1342
1343     LPSTR app_nameA = HEAP_strdupWtoA (GetProcessHeap(),0,app_name);
1344     LPSTR cmd_lineA = HEAP_strdupWtoA (GetProcessHeap(),0,cmd_line);
1345     LPSTR cur_dirA = HEAP_strdupWtoA (GetProcessHeap(),0,cur_dir);
1346
1347     memcpy (&StartupInfoA, startup_info, sizeof(STARTUPINFOA));
1348     StartupInfoA.lpDesktop = HEAP_strdupWtoA (GetProcessHeap(),0,startup_info->lpDesktop);
1349     StartupInfoA.lpTitle = HEAP_strdupWtoA (GetProcessHeap(),0,startup_info->lpTitle);
1350
1351     TRACE("(%s,%s,...)\n", debugstr_w(app_name), debugstr_w(cmd_line));
1352
1353     if (startup_info->lpReserved)
1354       FIXME("StartupInfo.lpReserved is used, please report (%s)\n",
1355             debugstr_w(startup_info->lpReserved));
1356
1357     ret = CreateProcessA( app_nameA,  cmd_lineA, process_attr, thread_attr,
1358                           inherit, flags, env, cur_dirA, &StartupInfoA, info );
1359
1360     HeapFree( GetProcessHeap(), 0, cur_dirA );
1361     HeapFree( GetProcessHeap(), 0, cmd_lineA );
1362     HeapFree( GetProcessHeap(), 0, StartupInfoA.lpDesktop );
1363     HeapFree( GetProcessHeap(), 0, StartupInfoA.lpTitle );
1364
1365     return ret;
1366 }
1367
1368
1369 /***********************************************************************
1370  *           wait_input_idle
1371  *
1372  * Wrapper to call WaitForInputIdle USER function
1373  */
1374 typedef DWORD (WINAPI *WaitForInputIdle_ptr)( HANDLE hProcess, DWORD dwTimeOut );
1375
1376 static DWORD wait_input_idle( HANDLE process, DWORD timeout )
1377 {
1378     HMODULE mod = GetModuleHandleA( "user32.dll" );
1379     if (mod)
1380     {
1381         WaitForInputIdle_ptr ptr = (WaitForInputIdle_ptr)GetProcAddress( mod, "WaitForInputIdle" );
1382         if (ptr) return ptr( process, timeout );
1383     }
1384     return 0;
1385 }
1386
1387
1388 /***********************************************************************
1389  *           WinExec   (KERNEL32.@)
1390  */
1391 UINT WINAPI WinExec( LPCSTR lpCmdLine, UINT nCmdShow )
1392 {
1393     PROCESS_INFORMATION info;
1394     STARTUPINFOA startup;
1395     char *cmdline;
1396     UINT ret;
1397
1398     memset( &startup, 0, sizeof(startup) );
1399     startup.cb = sizeof(startup);
1400     startup.dwFlags = STARTF_USESHOWWINDOW;
1401     startup.wShowWindow = nCmdShow;
1402
1403     /* cmdline needs to be writeable for CreateProcess */
1404     if (!(cmdline = HeapAlloc( GetProcessHeap(), 0, strlen(lpCmdLine)+1 ))) return 0;
1405     strcpy( cmdline, lpCmdLine );
1406
1407     if (CreateProcessA( NULL, cmdline, NULL, NULL, FALSE,
1408                         0, NULL, NULL, &startup, &info ))
1409     {
1410         /* Give 30 seconds to the app to come up */
1411         if (wait_input_idle( info.hProcess, 30000 ) == 0xFFFFFFFF)
1412             WARN("WaitForInputIdle failed: Error %ld\n", GetLastError() );
1413         ret = 33;
1414         /* Close off the handles */
1415         CloseHandle( info.hThread );
1416         CloseHandle( info.hProcess );
1417     }
1418     else if ((ret = GetLastError()) >= 32)
1419     {
1420         FIXME("Strange error set by CreateProcess: %d\n", ret );
1421         ret = 11;
1422     }
1423     HeapFree( GetProcessHeap(), 0, cmdline );
1424     return ret;
1425 }
1426
1427
1428 /**********************************************************************
1429  *          LoadModule    (KERNEL32.@)
1430  */
1431 HINSTANCE WINAPI LoadModule( LPCSTR name, LPVOID paramBlock )
1432 {
1433     LOADPARAMS *params = (LOADPARAMS *)paramBlock;
1434     PROCESS_INFORMATION info;
1435     STARTUPINFOA startup;
1436     HINSTANCE hInstance;
1437     LPSTR cmdline, p;
1438     char filename[MAX_PATH];
1439     BYTE len;
1440
1441     if (!name) return (HINSTANCE)ERROR_FILE_NOT_FOUND;
1442
1443     if (!SearchPathA( NULL, name, ".exe", sizeof(filename), filename, NULL ) &&
1444         !SearchPathA( NULL, name, NULL, sizeof(filename), filename, NULL ))
1445         return (HINSTANCE)GetLastError();
1446
1447     len = (BYTE)params->lpCmdLine[0];
1448     if (!(cmdline = HeapAlloc( GetProcessHeap(), 0, strlen(filename) + len + 2 )))
1449         return (HINSTANCE)ERROR_NOT_ENOUGH_MEMORY;
1450
1451     strcpy( cmdline, filename );
1452     p = cmdline + strlen(cmdline);
1453     *p++ = ' ';
1454     memcpy( p, params->lpCmdLine + 1, len );
1455     p[len] = 0;
1456
1457     memset( &startup, 0, sizeof(startup) );
1458     startup.cb = sizeof(startup);
1459     if (params->lpCmdShow)
1460     {
1461         startup.dwFlags = STARTF_USESHOWWINDOW;
1462         startup.wShowWindow = params->lpCmdShow[1];
1463     }
1464
1465     if (CreateProcessA( filename, cmdline, NULL, NULL, FALSE, 0,
1466                         params->lpEnvAddress, NULL, &startup, &info ))
1467     {
1468         /* Give 30 seconds to the app to come up */
1469         if (wait_input_idle( info.hProcess, 30000 ) ==  0xFFFFFFFF )
1470             WARN("WaitForInputIdle failed: Error %ld\n", GetLastError() );
1471         hInstance = (HINSTANCE)33;
1472         /* Close off the handles */
1473         CloseHandle( info.hThread );
1474         CloseHandle( info.hProcess );
1475     }
1476     else if ((hInstance = (HINSTANCE)GetLastError()) >= (HINSTANCE)32)
1477     {
1478         FIXME("Strange error set by CreateProcess: %p\n", hInstance );
1479         hInstance = (HINSTANCE)11;
1480     }
1481
1482     HeapFree( GetProcessHeap(), 0, cmdline );
1483     return hInstance;
1484 }
1485
1486
1487 /******************************************************************************
1488  *           TerminateProcess   (KERNEL32.@)
1489  */
1490 BOOL WINAPI TerminateProcess( HANDLE handle, DWORD exit_code )
1491 {
1492     NTSTATUS status = NtTerminateProcess( handle, exit_code );
1493     if (status) SetLastError( RtlNtStatusToDosError(status) );
1494     return !status;
1495 }
1496
1497
1498 /***********************************************************************
1499  * GetExitCodeProcess [KERNEL32.@]
1500  *
1501  * Gets termination status of specified process
1502  *
1503  * RETURNS
1504  *   Success: TRUE
1505  *   Failure: FALSE
1506  */
1507 BOOL WINAPI GetExitCodeProcess(
1508     HANDLE hProcess,    /* [in] handle to the process */
1509     LPDWORD lpExitCode) /* [out] address to receive termination status */
1510 {
1511     BOOL ret;
1512     SERVER_START_REQ( get_process_info )
1513     {
1514         req->handle = hProcess;
1515         ret = !wine_server_call_err( req );
1516         if (ret && lpExitCode) *lpExitCode = reply->exit_code;
1517     }
1518     SERVER_END_REQ;
1519     return ret;
1520 }
1521
1522
1523 /***********************************************************************
1524  *           SetErrorMode   (KERNEL32.@)
1525  */
1526 UINT WINAPI SetErrorMode( UINT mode )
1527 {
1528     UINT old = current_process.error_mode;
1529     current_process.error_mode = mode;
1530     return old;
1531 }
1532
1533
1534 /**********************************************************************
1535  * TlsAlloc [KERNEL32.@]  Allocates a TLS index.
1536  *
1537  * Allocates a thread local storage index
1538  *
1539  * RETURNS
1540  *    Success: TLS Index
1541  *    Failure: 0xFFFFFFFF
1542  */
1543 DWORD WINAPI TlsAlloc( void )
1544 {
1545     DWORD i, mask, ret = 0;
1546     DWORD *bits = current_process.tls_bits;
1547     RtlAcquirePebLock();
1548     if (*bits == 0xffffffff)
1549     {
1550         bits++;
1551         ret = 32;
1552         if (*bits == 0xffffffff)
1553         {
1554             RtlReleasePebLock();
1555             SetLastError( ERROR_NO_MORE_ITEMS );
1556             return 0xffffffff;
1557         }
1558     }
1559     for (i = 0, mask = 1; i < 32; i++, mask <<= 1) if (!(*bits & mask)) break;
1560     *bits |= mask;
1561     RtlReleasePebLock();
1562     NtCurrentTeb()->TlsSlots[ret+i] = 0; /* clear the value */
1563     return ret + i;
1564 }
1565
1566
1567 /**********************************************************************
1568  * TlsFree [KERNEL32.@]  Releases a TLS index.
1569  *
1570  * Releases a thread local storage index, making it available for reuse
1571  *
1572  * RETURNS
1573  *    Success: TRUE
1574  *    Failure: FALSE
1575  */
1576 BOOL WINAPI TlsFree(
1577     DWORD index) /* [in] TLS Index to free */
1578 {
1579     DWORD mask = (1 << (index & 31));
1580     DWORD *bits = current_process.tls_bits;
1581     if (index >= 64)
1582     {
1583         SetLastError( ERROR_INVALID_PARAMETER );
1584         return FALSE;
1585     }
1586     if (index >= 32) bits++;
1587     RtlAcquirePebLock();
1588     if (!(*bits & mask))  /* already free? */
1589     {
1590         RtlReleasePebLock();
1591         SetLastError( ERROR_INVALID_PARAMETER );
1592         return FALSE;
1593     }
1594     *bits &= ~mask;
1595     NtSetInformationThread( GetCurrentThread(), ThreadZeroTlsCell, &index, sizeof(index) );
1596     RtlReleasePebLock();
1597     return TRUE;
1598 }
1599
1600
1601 /**********************************************************************
1602  * TlsGetValue [KERNEL32.@]  Gets value in a thread's TLS slot
1603  *
1604  * RETURNS
1605  *    Success: Value stored in calling thread's TLS slot for index
1606  *    Failure: 0 and GetLastError returns NO_ERROR
1607  */
1608 LPVOID WINAPI TlsGetValue(
1609     DWORD index) /* [in] TLS index to retrieve value for */
1610 {
1611     if (index >= 64)
1612     {
1613         SetLastError( ERROR_INVALID_PARAMETER );
1614         return NULL;
1615     }
1616     SetLastError( ERROR_SUCCESS );
1617     return NtCurrentTeb()->TlsSlots[index];
1618 }
1619
1620
1621 /**********************************************************************
1622  * TlsSetValue [KERNEL32.@]  Stores a value in the thread's TLS slot.
1623  *
1624  * RETURNS
1625  *    Success: TRUE
1626  *    Failure: FALSE
1627  */
1628 BOOL WINAPI TlsSetValue(
1629     DWORD index,  /* [in] TLS index to set value for */
1630     LPVOID value) /* [in] Value to be stored */
1631 {
1632     if (index >= 64)
1633     {
1634         SetLastError( ERROR_INVALID_PARAMETER );
1635         return FALSE;
1636     }
1637     NtCurrentTeb()->TlsSlots[index] = value;
1638     return TRUE;
1639 }
1640
1641
1642 /***********************************************************************
1643  *           GetProcessFlags    (KERNEL32.@)
1644  */
1645 DWORD WINAPI GetProcessFlags( DWORD processid )
1646 {
1647     IMAGE_NT_HEADERS *nt;
1648     DWORD flags = 0;
1649
1650     if (processid && processid != GetCurrentProcessId()) return 0;
1651
1652     if ((nt = RtlImageNtHeader( NtCurrentTeb()->Peb->ImageBaseAddress )))
1653     {
1654         if (nt->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI)
1655             flags |= PDB32_CONSOLE_PROC;
1656     }
1657     if (!AreFileApisANSI()) flags |= PDB32_FILE_APIS_OEM;
1658     if (IsDebuggerPresent()) flags |= PDB32_DEBUGGED;
1659     return flags;
1660 }
1661
1662
1663 /***********************************************************************
1664  *           GetProcessDword    (KERNEL.485)
1665  *           GetProcessDword    (KERNEL32.18)
1666  * 'Of course you cannot directly access Windows internal structures'
1667  */
1668 DWORD WINAPI GetProcessDword( DWORD dwProcessID, INT offset )
1669 {
1670     DWORD               x, y;
1671     STARTUPINFOW        siw;
1672
1673     TRACE("(%ld, %d)\n", dwProcessID, offset );
1674
1675     if (dwProcessID && dwProcessID != GetCurrentProcessId())
1676     {
1677         ERR("%d: process %lx not accessible\n", offset, dwProcessID);
1678         return 0;
1679     }
1680
1681     switch ( offset )
1682     {
1683     case GPD_APP_COMPAT_FLAGS:
1684         return GetAppCompatFlags16(0);
1685     case GPD_LOAD_DONE_EVENT:
1686         return 0;
1687     case GPD_HINSTANCE16:
1688         return GetTaskDS16();
1689     case GPD_WINDOWS_VERSION:
1690         return GetExeVersion16();
1691     case GPD_THDB:
1692         return (DWORD)NtCurrentTeb() - 0x10 /* FIXME */;
1693     case GPD_PDB:
1694         return (DWORD)NtCurrentTeb()->Peb;
1695     case GPD_STARTF_SHELLDATA: /* return stdoutput handle from startupinfo ??? */
1696         GetStartupInfoW(&siw);
1697         return (DWORD)siw.hStdOutput;
1698     case GPD_STARTF_HOTKEY: /* return stdinput handle from startupinfo ??? */
1699         GetStartupInfoW(&siw);
1700         return (DWORD)siw.hStdInput;
1701     case GPD_STARTF_SHOWWINDOW:
1702         GetStartupInfoW(&siw);
1703         return siw.wShowWindow;
1704     case GPD_STARTF_SIZE:
1705         GetStartupInfoW(&siw);
1706         x = siw.dwXSize;
1707         if ( (INT)x == CW_USEDEFAULT ) x = CW_USEDEFAULT16;
1708         y = siw.dwYSize;
1709         if ( (INT)y == CW_USEDEFAULT ) y = CW_USEDEFAULT16;
1710         return MAKELONG( x, y );
1711     case GPD_STARTF_POSITION:
1712         GetStartupInfoW(&siw);
1713         x = siw.dwX;
1714         if ( (INT)x == CW_USEDEFAULT ) x = CW_USEDEFAULT16;
1715         y = siw.dwY;
1716         if ( (INT)y == CW_USEDEFAULT ) y = CW_USEDEFAULT16;
1717         return MAKELONG( x, y );
1718     case GPD_STARTF_FLAGS:
1719         GetStartupInfoW(&siw);
1720         return siw.dwFlags;
1721     case GPD_PARENT:
1722         return 0;
1723     case GPD_FLAGS:
1724         return GetProcessFlags(0);
1725     case GPD_USERDATA:
1726         return process_dword;
1727     default:
1728         ERR("Unknown offset %d\n", offset );
1729         return 0;
1730     }
1731 }
1732
1733 /***********************************************************************
1734  *           SetProcessDword    (KERNEL.484)
1735  * 'Of course you cannot directly access Windows internal structures'
1736  */
1737 void WINAPI SetProcessDword( DWORD dwProcessID, INT offset, DWORD value )
1738 {
1739     TRACE("(%ld, %d)\n", dwProcessID, offset );
1740
1741     if (dwProcessID && dwProcessID != GetCurrentProcessId())
1742     {
1743         ERR("%d: process %lx not accessible\n", offset, dwProcessID);
1744         return;
1745     }
1746
1747     switch ( offset )
1748     {
1749     case GPD_APP_COMPAT_FLAGS:
1750     case GPD_LOAD_DONE_EVENT:
1751     case GPD_HINSTANCE16:
1752     case GPD_WINDOWS_VERSION:
1753     case GPD_THDB:
1754     case GPD_PDB:
1755     case GPD_STARTF_SHELLDATA:
1756     case GPD_STARTF_HOTKEY:
1757     case GPD_STARTF_SHOWWINDOW:
1758     case GPD_STARTF_SIZE:
1759     case GPD_STARTF_POSITION:
1760     case GPD_STARTF_FLAGS:
1761     case GPD_PARENT:
1762     case GPD_FLAGS:
1763         ERR("Not allowed to modify offset %d\n", offset );
1764         break;
1765     case GPD_USERDATA:
1766         process_dword = value;
1767         break;
1768     default:
1769         ERR("Unknown offset %d\n", offset );
1770         break;
1771     }
1772 }
1773
1774
1775 /***********************************************************************
1776  *           ExitProcess   (KERNEL.466)
1777  */
1778 void WINAPI ExitProcess16( WORD status )
1779 {
1780     DWORD count;
1781     ReleaseThunkLock( &count );
1782     ExitProcess( status );
1783 }
1784
1785
1786 /*********************************************************************
1787  *           OpenProcess   (KERNEL32.@)
1788  */
1789 HANDLE WINAPI OpenProcess( DWORD access, BOOL inherit, DWORD id )
1790 {
1791     HANDLE ret = 0;
1792     SERVER_START_REQ( open_process )
1793     {
1794         req->pid     = id;
1795         req->access  = access;
1796         req->inherit = inherit;
1797         if (!wine_server_call_err( req )) ret = reply->handle;
1798     }
1799     SERVER_END_REQ;
1800     return ret;
1801 }
1802
1803
1804 /*********************************************************************
1805  *           MapProcessHandle   (KERNEL.483)
1806  */
1807 DWORD WINAPI MapProcessHandle( HANDLE handle )
1808 {
1809     DWORD ret = 0;
1810     SERVER_START_REQ( get_process_info )
1811     {
1812         req->handle = handle;
1813         if (!wine_server_call_err( req )) ret = reply->pid;
1814     }
1815     SERVER_END_REQ;
1816     return ret;
1817 }
1818
1819
1820 /***********************************************************************
1821  *           SetPriorityClass   (KERNEL32.@)
1822  */
1823 BOOL WINAPI SetPriorityClass( HANDLE hprocess, DWORD priorityclass )
1824 {
1825     BOOL ret;
1826     SERVER_START_REQ( set_process_info )
1827     {
1828         req->handle   = hprocess;
1829         req->priority = priorityclass;
1830         req->mask     = SET_PROCESS_INFO_PRIORITY;
1831         ret = !wine_server_call_err( req );
1832     }
1833     SERVER_END_REQ;
1834     return ret;
1835 }
1836
1837
1838 /***********************************************************************
1839  *           GetPriorityClass   (KERNEL32.@)
1840  */
1841 DWORD WINAPI GetPriorityClass(HANDLE hprocess)
1842 {
1843     DWORD ret = 0;
1844     SERVER_START_REQ( get_process_info )
1845     {
1846         req->handle = hprocess;
1847         if (!wine_server_call_err( req )) ret = reply->priority;
1848     }
1849     SERVER_END_REQ;
1850     return ret;
1851 }
1852
1853
1854 /***********************************************************************
1855  *          SetProcessAffinityMask   (KERNEL32.@)
1856  */
1857 BOOL WINAPI SetProcessAffinityMask( HANDLE hProcess, DWORD affmask )
1858 {
1859     BOOL ret;
1860     SERVER_START_REQ( set_process_info )
1861     {
1862         req->handle   = hProcess;
1863         req->affinity = affmask;
1864         req->mask     = SET_PROCESS_INFO_AFFINITY;
1865         ret = !wine_server_call_err( req );
1866     }
1867     SERVER_END_REQ;
1868     return ret;
1869 }
1870
1871
1872 /**********************************************************************
1873  *          GetProcessAffinityMask    (KERNEL32.@)
1874  */
1875 BOOL WINAPI GetProcessAffinityMask( HANDLE hProcess,
1876                                       LPDWORD lpProcessAffinityMask,
1877                                       LPDWORD lpSystemAffinityMask )
1878 {
1879     BOOL ret = FALSE;
1880     SERVER_START_REQ( get_process_info )
1881     {
1882         req->handle = hProcess;
1883         if (!wine_server_call_err( req ))
1884         {
1885             if (lpProcessAffinityMask) *lpProcessAffinityMask = reply->process_affinity;
1886             if (lpSystemAffinityMask) *lpSystemAffinityMask = reply->system_affinity;
1887             ret = TRUE;
1888         }
1889     }
1890     SERVER_END_REQ;
1891     return ret;
1892 }
1893
1894
1895 /***********************************************************************
1896  *           GetProcessVersion    (KERNEL32.@)
1897  */
1898 DWORD WINAPI GetProcessVersion( DWORD processid )
1899 {
1900     IMAGE_NT_HEADERS *nt;
1901
1902     if (processid && processid != GetCurrentProcessId())
1903     {
1904         FIXME("should use ReadProcessMemory\n");
1905         return 0;
1906     }
1907     if ((nt = RtlImageNtHeader( NtCurrentTeb()->Peb->ImageBaseAddress )))
1908         return ((nt->OptionalHeader.MajorSubsystemVersion << 16) |
1909                 nt->OptionalHeader.MinorSubsystemVersion);
1910     return 0;
1911 }
1912
1913
1914 /***********************************************************************
1915  *              SetProcessWorkingSetSize        [KERNEL32.@]
1916  * Sets the min/max working set sizes for a specified process.
1917  *
1918  * PARAMS
1919  *    hProcess [I] Handle to the process of interest
1920  *    minset   [I] Specifies minimum working set size
1921  *    maxset   [I] Specifies maximum working set size
1922  *
1923  * RETURNS  STD
1924  */
1925 BOOL WINAPI SetProcessWorkingSetSize(HANDLE hProcess, SIZE_T minset,
1926                                      SIZE_T maxset)
1927 {
1928     FIXME("(%p,%ld,%ld): stub - harmless\n",hProcess,minset,maxset);
1929     if(( minset == (SIZE_T)-1) && (maxset == (SIZE_T)-1)) {
1930         /* Trim the working set to zero */
1931         /* Swap the process out of physical RAM */
1932     }
1933     return TRUE;
1934 }
1935
1936 /***********************************************************************
1937  *           GetProcessWorkingSetSize    (KERNEL32.@)
1938  */
1939 BOOL WINAPI GetProcessWorkingSetSize(HANDLE hProcess, PSIZE_T minset,
1940                                      PSIZE_T maxset)
1941 {
1942     FIXME("(%p,%p,%p): stub\n",hProcess,minset,maxset);
1943     /* 32 MB working set size */
1944     if (minset) *minset = 32*1024*1024;
1945     if (maxset) *maxset = 32*1024*1024;
1946     return TRUE;
1947 }
1948
1949
1950 /***********************************************************************
1951  *           SetProcessShutdownParameters    (KERNEL32.@)
1952  */
1953 BOOL WINAPI SetProcessShutdownParameters(DWORD level, DWORD flags)
1954 {
1955     FIXME("(%08lx, %08lx): partial stub.\n", level, flags);
1956     shutdown_flags = flags;
1957     shutdown_priority = level;
1958     return TRUE;
1959 }
1960
1961
1962 /***********************************************************************
1963  * GetProcessShutdownParameters                 (KERNEL32.@)
1964  *
1965  */
1966 BOOL WINAPI GetProcessShutdownParameters( LPDWORD lpdwLevel, LPDWORD lpdwFlags )
1967 {
1968     *lpdwLevel = shutdown_priority;
1969     *lpdwFlags = shutdown_flags;
1970     return TRUE;
1971 }
1972
1973
1974 /***********************************************************************
1975  *           GetProcessPriorityBoost    (KERNEL32.@)
1976  */
1977 BOOL WINAPI GetProcessPriorityBoost(HANDLE hprocess,PBOOL pDisablePriorityBoost)
1978 {
1979     FIXME("(%p,%p): semi-stub\n", hprocess, pDisablePriorityBoost);
1980     
1981     /* Report that no boost is present.. */
1982     *pDisablePriorityBoost = FALSE;
1983     
1984     return TRUE;
1985 }
1986
1987 /***********************************************************************
1988  *           SetProcessPriorityBoost    (KERNEL32.@)
1989  */
1990 BOOL WINAPI SetProcessPriorityBoost(HANDLE hprocess,BOOL disableboost)
1991 {
1992     FIXME("(%p,%d): stub\n",hprocess,disableboost);
1993     /* Say we can do it. I doubt the program will notice that we don't. */
1994     return TRUE;
1995 }
1996
1997
1998 /***********************************************************************
1999  *              ReadProcessMemory (KERNEL32.@)
2000  */
2001 BOOL WINAPI ReadProcessMemory( HANDLE process, LPCVOID addr, LPVOID buffer, SIZE_T size,
2002                                SIZE_T *bytes_read )
2003 {
2004     NTSTATUS status = NtReadVirtualMemory( process, addr, buffer, size, bytes_read );
2005     if (status) SetLastError( RtlNtStatusToDosError(status) );
2006     return !status;
2007 }
2008
2009
2010 /***********************************************************************
2011  *           WriteProcessMemory                 (KERNEL32.@)
2012  */
2013 BOOL WINAPI WriteProcessMemory( HANDLE process, LPVOID addr, LPCVOID buffer, SIZE_T size,
2014                                 SIZE_T *bytes_written )
2015 {
2016     NTSTATUS status = NtWriteVirtualMemory( process, addr, buffer, size, bytes_written );
2017     if (status) SetLastError( RtlNtStatusToDosError(status) );
2018     return !status;
2019 }
2020
2021
2022 /***********************************************************************
2023  * ProcessIdToSessionId   (KERNEL32.@)
2024  * This function is available on Terminal Server 4SP4 and Windows 2000
2025  */
2026 BOOL WINAPI ProcessIdToSessionId( DWORD procid, DWORD *sessionid_ptr )
2027 {
2028     /* According to MSDN, if the calling process is not in a terminal
2029      * services environment, then the sessionid returned is zero.
2030      */
2031     *sessionid_ptr = 0;
2032     return TRUE;
2033 }
2034
2035
2036 /***********************************************************************
2037  *              RegisterServiceProcess (KERNEL.491)
2038  *              RegisterServiceProcess (KERNEL32.@)
2039  *
2040  * A service process calls this function to ensure that it continues to run
2041  * even after a user logged off.
2042  */
2043 DWORD WINAPI RegisterServiceProcess(DWORD dwProcessId, DWORD dwType)
2044 {
2045     /* I don't think that Wine needs to do anything in that function */
2046     return 1; /* success */
2047 }
2048
2049
2050 /**************************************************************************
2051  *              SetFileApisToOEM   (KERNEL32.@)
2052  */
2053 VOID WINAPI SetFileApisToOEM(void)
2054 {
2055     oem_file_apis = TRUE;
2056 }
2057
2058
2059 /**************************************************************************
2060  *              SetFileApisToANSI   (KERNEL32.@)
2061  */
2062 VOID WINAPI SetFileApisToANSI(void)
2063 {
2064     oem_file_apis = FALSE;
2065 }
2066
2067
2068 /******************************************************************************
2069  * AreFileApisANSI [KERNEL32.@]  Determines if file functions are using ANSI
2070  *
2071  * RETURNS
2072  *    TRUE:  Set of file functions is using ANSI code page
2073  *    FALSE: Set of file functions is using OEM code page
2074  */
2075 BOOL WINAPI AreFileApisANSI(void)
2076 {
2077     return !oem_file_apis;
2078 }
2079
2080
2081 /***********************************************************************
2082  *           GetCurrentProcess   (KERNEL32.@)
2083  */
2084 #undef GetCurrentProcess
2085 HANDLE WINAPI GetCurrentProcess(void)
2086 {
2087     return (HANDLE)0xffffffff;
2088 }