process.c 63.7 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2 3
/*
 * Win32 processes
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
4
 * Copyright 1996, 1998 Alexandre Julliard
5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Alexandre Julliard's avatar
Alexandre Julliard committed
19 20
 */

21 22 23
#include "config.h"
#include "wine/port.h"

Alexandre Julliard's avatar
Alexandre Julliard committed
24
#include <assert.h>
25 26
#include <ctype.h>
#include <errno.h>
27
#include <fcntl.h>
28
#include <signal.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
29
#include <stdlib.h>
30
#include <stdio.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
31
#include <string.h>
32 33 34
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
35
#include "wine/winbase16.h"
36
#include "wine/winuser16.h"
37
#include "wine/exception.h"
38
#include "wine/library.h"
39
#include "drive.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
40
#include "module.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
41
#include "file.h"
42
#include "heap.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
43
#include "thread.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
44
#include "winerror.h"
45
#include "wincon.h"
46
#include "wine/server.h"
47
#include "options.h"
48
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
49

50 51
WINE_DEFAULT_DEBUG_CHANNEL(process);
WINE_DECLARE_DEBUG_CHANNEL(relay);
52
WINE_DECLARE_DEBUG_CHANNEL(snoop);
53
WINE_DECLARE_DEBUG_CHANNEL(win32);
54

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
struct _ENVDB;

/* Win32 process database */
typedef struct _PDB
{
    LONG             header[2];        /* 00 Kernel object header */
    HMODULE          module;           /* 08 Main exe module (NT) */
    void            *event;            /* 0c Pointer to an event object (unused) */
    DWORD            exit_code;        /* 10 Process exit code */
    DWORD            unknown2;         /* 14 Unknown */
    HANDLE           heap;             /* 18 Default process heap */
    HANDLE           mem_context;      /* 1c Process memory context */
    DWORD            flags;            /* 20 Flags */
    void            *pdb16;            /* 24 DOS PSP */
    WORD             PSP_sel;          /* 28 Selector to DOS PSP */
    WORD             imte;             /* 2a IMTE for the process module */
    WORD             threads;          /* 2c Number of threads */
    WORD             running_threads;  /* 2e Number of running threads */
    WORD             free_lib_count;   /* 30 Recursion depth of FreeLibrary calls */
    WORD             ring0_threads;    /* 32 Number of ring 0 threads */
    HANDLE           system_heap;      /* 34 System heap to allocate handles */
    HTASK            task;             /* 38 Win16 task */
    void            *mem_map_files;    /* 3c Pointer to mem-mapped files */
    struct _ENVDB   *env_db;           /* 40 Environment database */
    void            *handle_table;     /* 44 Handle table */
    struct _PDB     *parent;           /* 48 Parent process */
    void            *modref_list;      /* 4c MODREF list */
    void            *thread_list;      /* 50 List of threads */
    void            *debuggee_CB;      /* 54 Debuggee context block */
    void            *local_heap_free;  /* 58 Head of local heap free list */
    DWORD            unknown4;         /* 5c Unknown */
    CRITICAL_SECTION crit_section;     /* 60 Critical section */
    DWORD            unknown5[3];      /* 78 Unknown */
    void            *console;          /* 84 Console */
    DWORD            tls_bits[2];      /* 88 TLS in-use bits */
    DWORD            process_dword;    /* 90 Unknown */
    struct _PDB     *group;            /* 94 Process group */
    void            *exe_modref;       /* 98 MODREF for the process EXE */
    void            *top_filter;       /* 9c Top exception filter */
    DWORD            priority;         /* a0 Priority level */
    HANDLE           heap_list;        /* a4 Head of process heap list */
    void            *heap_handles;     /* a8 Head of heap handles list */
    DWORD            unknown6;         /* ac Unknown */
    void            *console_provider; /* b0 Console provider (??) */
    WORD             env_selector;     /* b4 Selector to process environment */
    WORD             error_mode;       /* b6 Error mode */
    HANDLE           load_done_evt;    /* b8 Event for process loading done */
    void            *UTState;          /* bc Head of Univeral Thunk list */
    DWORD            unknown8;         /* c0 Unknown (NT) */
    LCID             locale;           /* c4 Locale to be queried by GetThreadLocale (NT) */
} PDB;

107
PDB current_process;
Alexandre Julliard's avatar
Alexandre Julliard committed
108

109 110 111 112 113 114 115 116
/* Process flags */
#define PDB32_DEBUGGED      0x0001  /* Process is being debugged */
#define PDB32_WIN16_PROC    0x0008  /* Win16 process */
#define PDB32_DOS_PROC      0x0010  /* Dos process */
#define PDB32_CONSOLE_PROC  0x0020  /* Console process */
#define PDB32_FILE_APIS_OEM 0x0040  /* File APIs are OEM */
#define PDB32_WIN32S_PROC   0x8000  /* Win32s process */

117
static char main_exe_name[MAX_PATH];
118
static char *main_exe_name_ptr = main_exe_name;
119
static HANDLE main_exe_file;
120
static unsigned int server_startticks;
121

122 123
int main_create_flags = 0;

124
/* memory/environ.c */
125 126
extern struct _ENVDB *ENV_InitStartupInfo( size_t info_size, char *main_exe_name,
                                           size_t main_exe_size );
127 128 129
extern BOOL ENV_BuildCommandLine( char **argv );
extern STARTUPINFOA current_startupinfo;

130 131 132
/* scheduler/pthread.c */
extern void PTHREAD_init_done(void);

133
extern void RELAY_InitDebugLists(void);
Alexandre Julliard's avatar
Alexandre Julliard committed
134 135
extern BOOL MAIN_MainInit(void);

136
typedef WORD (WINAPI *pUserSignalProc)( UINT, DWORD, DWORD, HMODULE16 );
137

138 139 140 141 142
/***********************************************************************
 *           PROCESS_CallUserSignalProc
 *
 * FIXME:  Some of the signals aren't sent correctly!
 *
143
 * The exact meaning of the USER signals is undocumented, but this
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
 * should cover the basic idea:
 *
 * USIG_DLL_UNLOAD_WIN16
 *     This is sent when a 16-bit module is unloaded.
 *
 * USIG_DLL_UNLOAD_WIN32
 *     This is sent when a 32-bit module is unloaded.
 *
 * USIG_DLL_UNLOAD_ORPHANS
 *     This is sent after the last Win3.1 module is unloaded,
 *     to allow removal of orphaned menus.
 *
 * USIG_FAULT_DIALOG_PUSH
 * USIG_FAULT_DIALOG_POP
 *     These are called to allow USER to prepare for displaying a
 *     fault dialog, even though the fault might have happened while
 *     inside a USER critical section.
 *
 * USIG_THREAD_INIT
 *     This is called from the context of a new thread, as soon as it
 *     has started to run.
 *
 * USIG_THREAD_EXIT
 *     This is called, still in its context, just before a thread is
 *     about to terminate.
 *
 * USIG_PROCESS_CREATE
 *     This is called, in the parent process context, after a new process
 *     has been created.
 *
 * USIG_PROCESS_INIT
 *     This is called in the new process context, just after the main thread
 *     has started execution (after the main thread's USIG_THREAD_INIT has
 *     been sent).
 *
 * USIG_PROCESS_LOADED
 *     This is called after the executable file has been loaded into the
 *     new process context.
 *
 * USIG_PROCESS_RUNNING
 *     This is called immediately before the main entry point is called.
 *
 * USIG_PROCESS_EXIT
 *     This is called in the context of a process that is about to
 *     terminate (but before the last thread's USIG_THREAD_EXIT has
 *     been sent).
 *
 * USIG_PROCESS_DESTROY
 *     This is called after a process has terminated.
 *
 *
 * The meaning of the dwFlags bits is as follows:
 *
 * USIG_FLAGS_WIN32
 *     Current process is 32-bit.
 *
 * USIG_FLAGS_GUI
 *     Current process is a (Win32) GUI process.
 *
203
 * USIG_FLAGS_FEEDBACK
204 205 206 207 208 209
 *     Current process needs 'feedback' (determined from the STARTUPINFO
 *     flags STARTF_FORCEONFEEDBACK / STARTF_FORCEOFFFEEDBACK).
 *
 * USIG_FLAGS_FAULT
 *     The signal is being sent due to a fault.
 */
210
void PROCESS_CallUserSignalProc( UINT uCode, HMODULE16 hModule )
211
{
212
    DWORD dwFlags = 0;
213 214
    HMODULE user;
    pUserSignalProc proc;
215

216 217
    if (!(user = GetModuleHandleA( "user32.dll" ))) return;
    if (!(proc = (pUserSignalProc)GetProcAddress( user, "UserSignalProc" ))) return;
218

219 220
    /* Determine dwFlags */

221 222
    if ( !(current_process.flags & PDB32_WIN16_PROC) ) dwFlags |= USIG_FLAGS_WIN32;
    if ( !(current_process.flags & PDB32_CONSOLE_PROC) ) dwFlags |= USIG_FLAGS_GUI;
223 224 225 226

    if ( dwFlags & USIG_FLAGS_GUI )
    {
        /* Feedback defaults to ON */
227
        if ( !(current_startupinfo.dwFlags & STARTF_FORCEOFFFEEDBACK) )
228 229 230 231 232
            dwFlags |= USIG_FLAGS_FEEDBACK;
    }
    else
    {
        /* Feedback defaults to OFF */
233
        if (current_startupinfo.dwFlags & STARTF_FORCEONFEEDBACK)
234 235 236 237 238
            dwFlags |= USIG_FLAGS_FEEDBACK;
    }

    /* Call USER signal proc */

239
    if ( uCode == USIG_THREAD_INIT || uCode == USIG_THREAD_EXIT )
240
        proc( uCode, GetCurrentThreadId(), dwFlags, hModule );
241
    else
242
        proc( uCode, GetCurrentProcessId(), dwFlags, hModule );
243 244
}

245 246 247 248 249 250 251 252 253 254 255 256 257 258

/***********************************************************************
 *           get_basename
 */
inline static const char *get_basename( const char *name )
{
    char *p;

    if ((p = strrchr( name, '/' ))) name = p + 1;
    if ((p = strrchr( name, '\\' ))) name = p + 1;
    return name;
}


259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
/***********************************************************************
 *           open_builtin_exe_file
 *
 * Open an exe file for a builtin exe.
 */
static void *open_builtin_exe_file( const char *name, char *error, int error_size, int test_only )
{
    char exename[MAX_PATH], *p;
    const char *basename = get_basename(name);

    if (strlen(basename) >= sizeof(exename)) return NULL;
    strcpy( exename, basename );
    for (p = exename; *p; p++) *p = FILE_tolower(*p);
    return wine_dll_load_main_exe( exename, error, error_size, test_only );
}


276 277 278
/***********************************************************************
 *           open_exe_file
 *
279
 * Open a specific exe file, taking load order into account.
280 281 282 283 284
 * Returns the file handle or 0 for a builtin exe.
 */
static HANDLE open_exe_file( const char *name )
{
    enum loadorder_type loadorder[LOADORDER_NTYPES];
285
    char buffer[MAX_PATH];
286 287 288
    HANDLE handle;
    int i;

289 290 291 292 293 294 295 296 297 298 299
    TRACE("looking for %s\n", debugstr_a(name) );

    if ((handle = CreateFileA( name, GENERIC_READ, FILE_SHARE_READ,
                               NULL, OPEN_EXISTING, 0, 0 )) == INVALID_HANDLE_VALUE)
    {
        /* file doesn't exist, check for builtin */
        if (!FILE_contains_path( name )) goto error;
        if (!MODULE_GetBuiltinPath( name, "", buffer, sizeof(buffer) )) goto error;
        name = buffer;
    }

300 301 302 303 304 305 306 307 308
    MODULE_GetLoadOrder( loadorder, name, TRUE );

    for(i = 0; i < LOADORDER_NTYPES; i++)
    {
        if (loadorder[i] == LOADORDER_INVALID) break;
        switch(loadorder[i])
        {
        case LOADORDER_DLL:
            TRACE( "Trying native exe %s\n", debugstr_a(name) );
309
            if (handle != INVALID_HANDLE_VALUE) return handle;
310 311 312
            break;
        case LOADORDER_BI:
            TRACE( "Trying built-in exe %s\n", debugstr_a(name) );
313 314 315 316 317
            if (open_builtin_exe_file( name, NULL, 0, 1 ))
            {
                if (handle != INVALID_HANDLE_VALUE) CloseHandle(handle);
                return 0;
            }
318 319 320 321
        default:
            break;
        }
    }
322 323 324 325
    if (handle != INVALID_HANDLE_VALUE) CloseHandle(handle);

 error:
    SetLastError( ERROR_FILE_NOT_FOUND );
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
    return INVALID_HANDLE_VALUE;
}


/***********************************************************************
 *           find_exe_file
 *
 * Open an exe file, and return the full name and file handle.
 * Returns FALSE if file could not be found.
 * If file exists but cannot be opened, returns TRUE and set handle to INVALID_HANDLE_VALUE.
 * If file is a builtin exe, returns TRUE and sets handle to 0.
 */
static BOOL find_exe_file( const char *name, char *buffer, int buflen, HANDLE *handle )
{
    enum loadorder_type loadorder[LOADORDER_NTYPES];
    int i;

    TRACE("looking for %s\n", debugstr_a(name) );

345 346
    if (!SearchPathA( NULL, name, ".exe", buflen, buffer, NULL ) &&
        !MODULE_GetBuiltinPath( name, ".exe", buffer, buflen ))
347
    {
348
        /* no builtin found, try native without extension in case it is a Unix app */
349

350 351 352 353 354 355 356 357
        if (SearchPathA( NULL, name, NULL, buflen, buffer, NULL ))
        {
            TRACE( "Trying native/Unix binary %s\n", debugstr_a(buffer) );
            if ((*handle = CreateFileA( buffer, GENERIC_READ, FILE_SHARE_READ,
                                        NULL, OPEN_EXISTING, 0, 0 )) != INVALID_HANDLE_VALUE)
                return TRUE;
        }
        return FALSE;
358 359 360
    }

    MODULE_GetLoadOrder( loadorder, buffer, TRUE );
361 362

    for(i = 0; i < LOADORDER_NTYPES; i++)
363
    {
364 365
        if (loadorder[i] == LOADORDER_INVALID) break;
        switch(loadorder[i])
366
        {
367 368 369 370 371 372 373 374
        case LOADORDER_DLL:
            TRACE( "Trying native exe %s\n", debugstr_a(buffer) );
            if ((*handle = CreateFileA( buffer, GENERIC_READ, FILE_SHARE_READ,
                                        NULL, OPEN_EXISTING, 0, 0 )) != INVALID_HANDLE_VALUE)
                return TRUE;
            if (GetLastError() != ERROR_FILE_NOT_FOUND) return TRUE;
            break;
        case LOADORDER_BI:
375
            TRACE( "Trying built-in exe %s\n", debugstr_a(buffer) );
376
            if (open_builtin_exe_file( buffer, NULL, 0, 1 ))
377 378 379 380 381
            {
                *handle = 0;
                return TRUE;
            }
            break;
382 383
        default:
            break;
384 385
        }
    }
386
    SetLastError( ERROR_FILE_NOT_FOUND );
387 388 389 390
    return FALSE;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
391
/***********************************************************************
392 393 394
 *           process_init
 *
 * Main process initialisation code
Alexandre Julliard's avatar
Alexandre Julliard committed
395
 */
396
static BOOL process_init( char *argv[] )
Alexandre Julliard's avatar
Alexandre Julliard committed
397
{
398
    BOOL ret;
399
    size_t info_size = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
400

401 402 403
    /* store the program name */
    argv0 = argv[0];

404
    /* Fill the initial process structure */
405 406 407 408 409 410
    current_process.exit_code       = STILL_ACTIVE;
    current_process.threads         = 1;
    current_process.running_threads = 1;
    current_process.ring0_threads   = 1;
    current_process.group           = &current_process;
    current_process.priority        = 8;  /* Normal */
411 412

    /* Setup the server connection */
413
    CLIENT_InitServer();
414

415
    /* Retrieve startup info from the server */
416
    SERVER_START_REQ( init_process )
417
    {
418
        req->ldt_copy  = &wine_ldt_copy;
419
        req->ppid      = getppid();
420
        if ((ret = !wine_server_call_err( req )))
421
        {
422 423 424 425
            main_exe_file     = reply->exe_file;
            main_create_flags = reply->create_flags;
            info_size         = reply->info_size;
            server_startticks = reply->server_start;
426 427 428
            current_startupinfo.hStdInput   = reply->hstdin;
            current_startupinfo.hStdOutput  = reply->hstdout;
            current_startupinfo.hStdError   = reply->hstderr;
429 430
        }
    }
431
    SERVER_END_REQ;
432
    if (!ret) return FALSE;
433

434
    /* Create the process heap */
435
    current_process.heap = HeapCreate( HEAP_GROWABLE, 0, 0 );
436

437
    if (main_create_flags == 0 &&
438
	current_startupinfo.hStdInput  == 0 &&
439
	current_startupinfo.hStdOutput == 0 &&
440 441 442 443 444
	current_startupinfo.hStdError  == 0)
    {
	/* no parent, and no new console requested, create a simple console with bare handles to
	 * unix stdio input & output streams (aka simple console)
	 */
445 446 447 448 449 450 451
        HANDLE handle;
        wine_server_fd_to_handle( 0, GENERIC_READ|SYNCHRONIZE, TRUE, &handle );
        SetStdHandle( STD_INPUT_HANDLE, handle );
        wine_server_fd_to_handle( 1, GENERIC_WRITE|SYNCHRONIZE, TRUE, &handle );
        SetStdHandle( STD_OUTPUT_HANDLE, handle );
        wine_server_fd_to_handle( 1, GENERIC_WRITE|SYNCHRONIZE, TRUE, &handle );
        SetStdHandle( STD_ERROR_HANDLE, handle );
452
    }
453
    else if (!(main_create_flags & (DETACHED_PROCESS|CREATE_NEW_CONSOLE)))
454 455 456 457 458 459
    {
	SetStdHandle( STD_INPUT_HANDLE,  current_startupinfo.hStdInput  );
	SetStdHandle( STD_OUTPUT_HANDLE, current_startupinfo.hStdOutput );
	SetStdHandle( STD_ERROR_HANDLE,  current_startupinfo.hStdError  );
    }

460 461 462
    /* Now we can use the pthreads routines */
    PTHREAD_init_done();

463
    /* Copy the parent environment */
464 465
    if (!(current_process.env_db = ENV_InitStartupInfo( info_size, main_exe_name,
                                                        sizeof(main_exe_name) )))
466
        return FALSE;
467

468
    /* Parse command line arguments */
469
    OPTIONS_ParseOptions( !info_size ? argv : NULL );
470

471
    ret = MAIN_MainInit();
472
    if (TRACE_ON(relay) || TRACE_ON(snoop)) RELAY_InitDebugLists();
473 474

    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
475 476 477
}


478
/***********************************************************************
479
 *           start_process
480
 *
481
 * Startup routine of a new process. Runs on the new process stack.
482
 */
483
static void start_process(void)
484
{
485 486
    int debugged, console_app;
    LPTHREAD_START_ROUTINE entry;
487
    WINE_MODREF *wm;
488
    HANDLE main_file = main_exe_file;
489
    IMAGE_NT_HEADERS *nt;
490

491 492 493 494 495 496
    /* use original argv[0] as name for the main module */
    if (!main_exe_name[0])
    {
        if (!GetLongPathNameA( full_argv0, main_exe_name, sizeof(main_exe_name) ))
            lstrcpynA( main_exe_name, full_argv0, sizeof(main_exe_name) );
    }
497

498 499 500 501 502 503 504
    if (main_file)
    {
        UINT drive_type = GetDriveTypeA( main_exe_name );
        /* don't keep the file handle open on removable media */
        if (drive_type == DRIVE_REMOVABLE || drive_type == DRIVE_CDROM) main_file = 0;
    }

505
    /* Retrieve entry point address */
506
    nt = RtlImageNtHeader( current_process.module );
507
    entry = (LPTHREAD_START_ROUTINE)((char*)current_process.module +
508 509
                                     nt->OptionalHeader.AddressOfEntryPoint);
    console_app = (nt->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI);
510
    if (console_app) current_process.flags |= PDB32_CONSOLE_PROC;
511 512

    /* Signal the parent process to continue */
513
    SERVER_START_REQ( init_process_done )
514
    {
515
        req->module      = (void *)current_process.module;
516
        req->module_size = nt->OptionalHeader.SizeOfImage;
517
        req->entry    = entry;
518 519
        /* API requires a double indirection */
        req->name     = &main_exe_name_ptr;
520
        req->exe_file = main_file;
521
        req->gui      = !console_app;
522
        wine_server_add_data( req, main_exe_name, strlen(main_exe_name) );
523 524
        wine_server_call( req );
        debugged = reply->debugged;
525 526
    }
    SERVER_END_REQ;
527 528 529 530 531 532

    /* Install signal handlers; this cannot be done before, since we cannot
     * send exceptions to the debugger before the create process event that
     * is sent by REQ_INIT_PROCESS_DONE */
    if (!SIGNAL_Init()) goto error;

533
    /* create the main modref and load dependencies */
534
    if (!(wm = PE_CreateModule( current_process.module, main_exe_name, 0, 0, FALSE )))
535 536 537
        goto error;
    wm->refCount++;

538 539
    if (main_exe_file) CloseHandle( main_exe_file ); /* we no longer need it */

540
    MODULE_DllProcessAttach( NULL, (LPVOID)1 );
541

542 543 544 545
    /* Note: The USIG_PROCESS_CREATE signal is supposed to be sent in the
     *       context of the parent process.  Actually, the USER signal proc
     *       doesn't really care about that, but it *does* require that the
     *       startup parameters are correctly set up, so that GetProcessDword
546
     *       works.  Furthermore, before calling the USER signal proc the
547 548 549 550 551 552 553
     *       16-bit stack must be set up, which it is only after TASK_Create
     *       in the case of a 16-bit process. Thus, we send the signal here.
     */
    PROCESS_CallUserSignalProc( USIG_PROCESS_CREATE, 0 );
    PROCESS_CallUserSignalProc( USIG_THREAD_INIT, 0 );
    PROCESS_CallUserSignalProc( USIG_PROCESS_INIT, 0 );
    PROCESS_CallUserSignalProc( USIG_PROCESS_LOADED, 0 );
554 555 556
    /* Call UserSignalProc ( USIG_PROCESS_RUNNING ... ) only for non-GUI win32 apps */
    if (console_app) PROCESS_CallUserSignalProc( USIG_PROCESS_RUNNING, 0 );

557 558 559
    if (TRACE_ON(relay))
        DPRINTF( "%08lx:Starting process %s (entryproc=%p)\n",
                 GetCurrentThreadId(), main_exe_name, entry );
560 561 562
    if (debugged) DbgBreakPoint();
    /* FIXME: should use _PEB as parameter for NT 3.5 programs !
     * Dunno about other OSs */
563
    SetLastError(0);  /* clear error code */
564 565 566 567
    ExitThread( entry(NULL) );

 error:
    ExitProcess( GetLastError() );
568 569 570 571 572 573 574 575
}


/***********************************************************************
 *           PROCESS_InitWine
 *
 * Wine initialisation: load and start the main exe file.
 */
576
void PROCESS_InitWine( int argc, char *argv[], LPSTR win16_exe_name, HANDLE *win16_exe_file )
577
{
578
    char error[100], *p;
579
    DWORD stack_size = 0;
580 581

    /* Initialize everything */
582
    if (!process_init( argv )) exit(1);
583

584
    argv++;  /* remove argv[0] (wine itself) */
585

586
    TRACE( "starting process name=%s file=%p argv[0]=%s\n",
587 588
           debugstr_a(main_exe_name), main_exe_file, debugstr_a(argv[0]) );

589
    if (!main_exe_name[0])
590
    {
591
        if (!argv[0]) OPTIONS_Usage();
592

593
        if (!find_exe_file( argv[0], main_exe_name, sizeof(main_exe_name), &main_exe_file ))
594
        {
595
            MESSAGE( "%s: cannot find '%s'\n", argv0, argv[0] );
596
            ExitProcess(1);
597
        }
598
        if (main_exe_file == INVALID_HANDLE_VALUE)
599 600
        {
            MESSAGE( "%s: cannot open '%s'\n", argv0, main_exe_name );
601
            ExitProcess(1);
602 603 604
        }
    }

605
    if (!main_exe_file)  /* no file handle -> Winelib app */
606
    {
607
        TRACE( "starting Winelib app %s\n", debugstr_a(main_exe_name) );
608
        if (open_builtin_exe_file( main_exe_name, error, sizeof(error), 0 ))
609 610 611
            goto found;
        MESSAGE( "%s: cannot open builtin library for '%s': %s\n", argv0, main_exe_name, error );
        ExitProcess(1);
612
    }
613

614 615 616 617 618 619 620 621 622 623
    switch( MODULE_GetBinaryType( main_exe_file ))
    {
    case BINARY_PE_EXE:
        TRACE( "starting Win32 binary %s\n", debugstr_a(main_exe_name) );
        if ((current_process.module = PE_LoadImage( main_exe_file, main_exe_name, 0 ))) goto found;
        MESSAGE( "%s: could not load '%s' as Win32 binary\n", argv0, main_exe_name );
        ExitProcess(1);
    case BINARY_PE_DLL:
        MESSAGE( "%s: '%s' is a DLL, not an executable\n", argv0, main_exe_name );
        ExitProcess(1);
624 625 626 627 628 629 630 631
    case BINARY_UNKNOWN:
        /* check for .com extension */
        if (!(p = strrchr( main_exe_name, '.' )) || FILE_strcasecmp( p, ".com" ))
        {
            MESSAGE( "%s: cannot determine executable type for '%s'\n", argv0, main_exe_name );
            ExitProcess(1);
        }
        /* fall through */
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652
    case BINARY_WIN16:
    case BINARY_DOS:
        TRACE( "starting Win16/DOS binary %s\n", debugstr_a(main_exe_name) );
        NtCurrentTeb()->tibflags &= ~TEBF_WIN32;
        current_process.flags |= PDB32_WIN16_PROC;
        strcpy( win16_exe_name, main_exe_name );
        main_exe_name[0] = 0;
        *win16_exe_file = main_exe_file;
        main_exe_file = 0;
        _EnterWin16Lock();
        goto found;
    case BINARY_OS216:
        MESSAGE( "%s: '%s' is an OS/2 binary, not supported\n", argv0, main_exe_name );
        ExitProcess(1);
    case BINARY_UNIX_EXE:
        MESSAGE( "%s: '%s' is a Unix binary, not supported\n", argv0, main_exe_name );
        ExitProcess(1);
    case BINARY_UNIX_LIB:
        {
            DOS_FULL_NAME full_name;
            const char *name = main_exe_name;
653
            UNICODE_STRING nameW;
654 655

            TRACE( "starting Winelib app %s\n", debugstr_a(main_exe_name) );
656 657 658
            RtlCreateUnicodeStringFromAsciiz(&nameW, name);
            if (DOSFS_GetFullName( nameW.Buffer, TRUE, &full_name )) name = full_name.long_name;
            RtlFreeUnicodeString(&nameW);
659 660
            CloseHandle( main_exe_file );
            main_exe_file = 0;
661 662 663 664 665
            if (wine_dlopen( name, RTLD_NOW, error, sizeof(error) ))
            {
                if ((p = strrchr( main_exe_name, '.' )) && !strcmp( p, ".so" )) *p = 0;
                goto found;
            }
666 667 668 669
            MESSAGE( "%s: could not load '%s': %s\n", argv0, main_exe_name, error );
            ExitProcess(1);
        }
    }
670 671

 found:
672
    /* build command line */
673
    if (!ENV_BuildCommandLine( argv )) goto error;
674 675 676

    /* create 32-bit module for main exe */
    if (!(current_process.module = BUILTIN32_LoadExeModule( current_process.module ))) goto error;
677
    stack_size = RtlImageNtHeader(current_process.module)->OptionalHeader.SizeOfStackReserve;
678

679
    /* allocate main thread stack */
680
    if (!THREAD_InitStack( NtCurrentTeb(), stack_size )) goto error;
681 682 683

    /* switch to the new stack */
    SYSDEPS_SwitchToThreadStack( start_process );
684

685 686 687 688 689
 error:
    ExitProcess( GetLastError() );
}


690 691 692 693 694
/***********************************************************************
 *           build_argv
 *
 * Build an argv array from a command-line.
 * The command-line is modified to insert nulls.
695
 * 'reserved' is the number of args to reserve before the first one.
696
 */
697
static char **build_argv( char *cmdline, int reserved )
698
{
699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729
    int argc;
    char** argv;
    char *arg,*s,*d;
    int in_quotes,bcount;

    argc=reserved+1;
    bcount=0;
    in_quotes=0;
    s=cmdline;
    while (1) {
        if (*s=='\0' || ((*s==' ' || *s=='\t') && !in_quotes)) {
            /* space */
            argc++;
            /* skip the remaining spaces */
            while (*s==' ' || *s=='\t') {
                s++;
            }
            if (*s=='\0')
                break;
            bcount=0;
            continue;
        } else if (*s=='\\') {
            /* '\', count them */
            bcount++;
        } else if ((*s=='"') && ((bcount & 1)==0)) {
            /* unescaped '"' */
            in_quotes=!in_quotes;
            bcount=0;
        } else {
            /* a regular character */
            bcount=0;
730
        }
731
        s++;
732
    }
733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761
    argv=malloc(argc*sizeof(*argv));
    if (!argv)
        return NULL;

    arg=d=s=cmdline;
    bcount=0;
    in_quotes=0;
    argc=reserved;
    while (*s) {
        if ((*s==' ' || *s=='\t') && !in_quotes) {
            /* Close the argument and copy it */
            *d=0;
            argv[argc++]=arg;

            /* skip the remaining spaces */
            do {
                s++;
            } while (*s==' ' || *s=='\t');

            /* Start with a new argument */
            arg=d=s;
            bcount=0;
        } else if (*s=='\\') {
            /* '\\' */
            *d++=*s++;
            bcount++;
        } else if (*s=='"') {
            /* '"' */
            if ((bcount & 1)==0) {
762
                /* Preceeded by an even number of '\', this is half that
763 764 765 766 767 768
                 * number of '\', plus a '"' which we discard.
                 */
                d-=bcount/2;
                s++;
                in_quotes=!in_quotes;
            } else {
769
                /* Preceeded by an odd number of '\', this is half that
770 771 772 773 774
                 * number of '\' followed by a '"'
                 */
                d=d-bcount/2-1;
                *d++='"';
                s++;
775
            }
776 777 778 779 780
            bcount=0;
        } else {
            /* a regular character */
            *d++=*s++;
            bcount=0;
781
        }
782
    }
783 784 785 786 787 788
    if (*arg) {
        *d='\0';
        argv[argc++]=arg;
    }
    argv[argc]=NULL;

789 790 791 792 793 794 795 796 797
    return argv;
}


/***********************************************************************
 *           build_envp
 *
 * Build the environment of a new child process.
 */
798
static char **build_envp( const char *env, const char *extra_env )
799 800 801
{
    const char *p;
    char **envp;
802
    int count = 0;
803

804 805
    if (extra_env) for (p = extra_env; *p; count++) p += strlen(p) + 1;
    for (p = env; *p; count++) p += strlen(p) + 1;
806
    count += 3;
807

808 809 810 811 812
    if ((envp = malloc( count * sizeof(*envp) )))
    {
        extern char **environ;
        char **envptr = envp;
        char **unixptr = environ;
813 814 815
        /* first the extra strings */
        if (extra_env) for (p = extra_env; *p; p += strlen(p) + 1) *envptr++ = (char *)p;
        /* then put PATH, HOME and WINEPREFIX from the unix env */
816 817 818 819 820 821 822
        for (unixptr = environ; unixptr && *unixptr; unixptr++)
            if (!memcmp( *unixptr, "PATH=", 5 ) ||
                !memcmp( *unixptr, "HOME=", 5 ) ||
                !memcmp( *unixptr, "WINEPREFIX=", 11 )) *envptr++ = *unixptr;
        /* now put the Windows environment strings */
        for (p = env; *p; p += strlen(p) + 1)
        {
823 824 825 826 827 828 829 830 831 832
            if (!memcmp( p, "PATH=", 5 ))  /* store PATH as WINEPATH */
            {
                char *winepath = malloc( strlen(p) + 5 );
                strcpy( winepath, "WINE" );
                strcpy( winepath + 4, p );
                *envptr++ = winepath;
            }
            else if (memcmp( p, "HOME=", 5 ) &&
                     memcmp( p, "WINEPATH=", 9 ) &&
                     memcmp( p, "WINEPREFIX=", 11 )) *envptr++ = (char *)p;
833 834 835 836 837 838 839 840
        }
        *envptr = 0;
    }
    return envp;
}


/***********************************************************************
841
 *           exec_wine_binary
842 843 844 845 846 847 848
 *
 * Locate the Wine binary to exec for a new Win32 process.
 */
static void exec_wine_binary( char **argv, char **envp )
{
    const char *path, *pos, *ptr;

849 850 851 852 853 854
    /* first, try for a WINELOADER environment variable */
    argv[0] = getenv("WINELOADER");
    if (argv[0])
        execve( argv[0], argv, envp );

    /* next, try bin directory */
855 856 857 858
    argv[0] = BINDIR "/wine";
    execve( argv[0], argv, envp );

    /* now try the path of argv0 of the current binary */
859 860
    if (!(argv[0] = malloc( strlen(full_argv0) + 6 ))) return;
    if ((ptr = strrchr( full_argv0, '/' )))
861
    {
862 863
        memcpy( argv[0], full_argv0, ptr - full_argv0 );
        strcpy( argv[0] + (ptr - full_argv0), "/wine" );
864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892
        execve( argv[0], argv, envp );
    }
    free( argv[0] );

    /* now search in the Unix path */
    if ((path = getenv( "PATH" )))
    {
        if (!(argv[0] = malloc( strlen(path) + 6 ))) return;
        pos = path;
        for (;;)
        {
            while (*pos == ':') pos++;
            if (!*pos) break;
            if (!(ptr = strchr( pos, ':' ))) ptr = pos + strlen(pos);
            memcpy( argv[0], pos, ptr - pos );
            strcpy( argv[0] + (ptr - pos), "/wine" );
            execve( argv[0], argv, envp );
            pos = ptr;
        }
    }
    free( argv[0] );
}


/***********************************************************************
 *           fork_and_exec
 *
 * Fork and exec a new Unix process, checking for errors.
 */
893 894
static int fork_and_exec( const char *filename, char *cmdline,
                          const char *env, const char *newdir )
895 896 897
{
    int fd[2];
    int pid, err;
898 899 900 901 902 903 904
    char *extra_env = NULL;

    if (!env)
    {
        env = GetEnvironmentStringsA();
        extra_env = DRIVE_BuildEnv();
    }
905 906 907 908 909 910 911 912 913

    if (pipe(fd) == -1)
    {
        FILE_SetDosError();
        return -1;
    }
    fcntl( fd[1], F_SETFD, 1 );  /* set close on exec */
    if (!(pid = fork()))  /* child */
    {
914
        char **argv = build_argv( cmdline, filename ? 0 : 1 );
915
        char **envp = build_envp( env, extra_env );
916
        close( fd[0] );
917

918 919 920 921
        /* Reset signals that we previously set to SIG_IGN */
        signal( SIGPIPE, SIG_DFL );
        signal( SIGCHLD, SIG_DFL );

922
        if (newdir) chdir(newdir);
923

924 925
        if (argv && envp)
        {
926
            if (!filename) exec_wine_binary( argv, envp );
927 928
            else execve( filename, argv, envp );
        }
929 930 931 932 933 934 935 936 937 938 939 940
        err = errno;
        write( fd[1], &err, sizeof(err) );
        _exit(1);
    }
    close( fd[1] );
    if ((pid != -1) && (read( fd[0], &err, sizeof(err) ) > 0))  /* exec failed */
    {
        errno = err;
        pid = -1;
    }
    if (pid == -1) FILE_SetDosError();
    close( fd[0] );
941
    if (extra_env) HeapFree( GetProcessHeap(), 0, extra_env );
942 943 944 945 946
    return pid;
}


/***********************************************************************
947
 *           create_process
948 949
 *
 * Create a new process. If hFile is a valid handle we have an exe
950
 * file, otherwise it is a Winelib app.
951
 */
952 953 954 955
static BOOL create_process( HANDLE hFile, LPCSTR filename, LPSTR cmd_line, LPCSTR env,
                            LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
                            BOOL inherit, DWORD flags, LPSTARTUPINFOA startup,
                            LPPROCESS_INFORMATION info, LPCSTR unixdir )
956
{
957
    BOOL ret, success = FALSE;
958
    HANDLE process_info;
959
    startup_info_t startup_info;
960

961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977
    /* fill the startup info structure */

    startup_info.size        = sizeof(startup_info);
    /* startup_info.filename_len is set below */
    startup_info.cmdline_len = cmd_line ? strlen(cmd_line) : 0;
    startup_info.desktop_len = startup->lpDesktop ? strlen(startup->lpDesktop) : 0;
    startup_info.title_len   = startup->lpTitle ? strlen(startup->lpTitle) : 0;
    startup_info.x           = startup->dwX;
    startup_info.y           = startup->dwY;
    startup_info.cx          = startup->dwXSize;
    startup_info.cy          = startup->dwYSize;
    startup_info.x_chars     = startup->dwXCountChars;
    startup_info.y_chars     = startup->dwYCountChars;
    startup_info.attribute   = startup->dwFillAttribute;
    startup_info.cmd_show    = startup->wShowWindow;
    startup_info.flags       = startup->dwFlags;

978
    /* create the process on the server side */
979

980
    SERVER_START_REQ( new_process )
981
    {
982
        char buf[MAX_PATH];
983
        LPCSTR nameptr;
984

985 986
        req->inherit_all  = inherit;
        req->create_flags = flags;
987
        req->use_handles  = (startup->dwFlags & STARTF_USESTDHANDLES) != 0;
988 989 990 991 992 993 994 995 996 997 998 999 1000 1001
        req->exe_file     = hFile;
        if (startup->dwFlags & STARTF_USESTDHANDLES)
        {
            req->hstdin  = startup->hStdInput;
            req->hstdout = startup->hStdOutput;
            req->hstderr = startup->hStdError;
        }
        else
        {
            req->hstdin  = GetStdHandle( STD_INPUT_HANDLE );
            req->hstdout = GetStdHandle( STD_OUTPUT_HANDLE );
            req->hstderr = GetStdHandle( STD_ERROR_HANDLE );
        }

1002 1003 1004 1005 1006
        if (GetLongPathNameA( filename, buf, MAX_PATH ))
            nameptr = buf;
        else
            nameptr = filename;

1007 1008 1009 1010 1011 1012 1013
        startup_info.filename_len = strlen(nameptr);
        wine_server_add_data( req, &startup_info, sizeof(startup_info) );
        wine_server_add_data( req, nameptr, startup_info.filename_len );
        wine_server_add_data( req, cmd_line, startup_info.cmdline_len );
        wine_server_add_data( req, startup->lpDesktop, startup_info.desktop_len );
        wine_server_add_data( req, startup->lpTitle, startup_info.title_len );

1014 1015
        ret = !wine_server_call_err( req );
        process_info = reply->info;
1016
    }
1017
    SERVER_END_REQ;
1018
    if (!ret) return FALSE;
1019 1020 1021

    /* fork and execute */

1022
    if (fork_and_exec( NULL, cmd_line, env, unixdir ) == -1)
1023 1024 1025 1026
    {
        CloseHandle( process_info );
        return FALSE;
    }
1027

1028 1029
    /* wait for the new process info to be ready */

1030 1031
    WaitForSingleObject( process_info, INFINITE );
    SERVER_START_REQ( get_new_process_info )
1032
    {
1033 1034 1035 1036
        req->info     = process_info;
        req->pinherit = (psa && (psa->nLength >= sizeof(*psa)) && psa->bInheritHandle);
        req->tinherit = (tsa && (tsa->nLength >= sizeof(*tsa)) && tsa->bInheritHandle);
        if ((ret = !wine_server_call_err( req )))
1037
        {
1038 1039 1040 1041 1042
            info->dwProcessId = (DWORD)reply->pid;
            info->dwThreadId  = (DWORD)reply->tid;
            info->hProcess    = reply->phandle;
            info->hThread     = reply->thandle;
            success           = reply->success;
1043 1044
        }
    }
1045
    SERVER_END_REQ;
1046

1047
    if (ret && !success)  /* new process failed to start */
1048
    {
1049 1050 1051 1052 1053
        DWORD exitcode;
        if (GetExitCodeProcess( info->hProcess, &exitcode )) SetLastError( exitcode );
        CloseHandle( info->hThread );
        CloseHandle( info->hProcess );
        ret = FALSE;
1054
    }
1055 1056
    CloseHandle( process_info );
    return ret;
1057 1058 1059
}


1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080
/*************************************************************************
 *               get_file_name
 *
 * Helper for CreateProcess: retrieve the file name to load from the
 * app name and command line. Store the file name in buffer, and
 * return a possibly modified command line.
 * Also returns a handle to the opened file if it's a Windows binary.
 */
static LPSTR get_file_name( LPCSTR appname, LPSTR cmdline, LPSTR buffer,
                            int buflen, HANDLE *handle )
{
    char *name, *pos, *ret = NULL;
    const char *p;

    /* if we have an app name, everything is easy */

    if (appname)
    {
        /* use the unmodified app name as file name */
        lstrcpynA( buffer, appname, buflen );
        *handle = open_exe_file( buffer );
1081
        if (!(ret = cmdline) || !cmdline[0])
1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154
        {
            /* no command-line, create one */
            if ((ret = HeapAlloc( GetProcessHeap(), 0, strlen(appname) + 3 )))
                sprintf( ret, "\"%s\"", appname );
        }
        return ret;
    }

    if (!cmdline)
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return NULL;
    }

    /* first check for a quoted file name */

    if ((cmdline[0] == '"') && ((p = strchr( cmdline + 1, '"' ))))
    {
        int len = p - cmdline - 1;
        /* extract the quoted portion as file name */
        if (!(name = HeapAlloc( GetProcessHeap(), 0, len + 1 ))) return NULL;
        memcpy( name, cmdline + 1, len );
        name[len] = 0;

        if (find_exe_file( name, buffer, buflen, handle ))
            ret = cmdline;  /* no change necessary */
        goto done;
    }

    /* now try the command-line word by word */

    if (!(name = HeapAlloc( GetProcessHeap(), 0, strlen(cmdline) + 1 ))) return NULL;
    pos = name;
    p = cmdline;

    while (*p)
    {
        do *pos++ = *p++; while (*p && *p != ' ');
        *pos = 0;
        if (find_exe_file( name, buffer, buflen, handle ))
        {
            ret = cmdline;
            break;
        }
    }

    if (!ret || !strchr( name, ' ' )) goto done;  /* no change necessary */

    /* now build a new command-line with quotes */

    if (!(ret = HeapAlloc( GetProcessHeap(), 0, strlen(cmdline) + 3 ))) goto done;
    sprintf( ret, "\"%s\"%s", name, p );

 done:
    HeapFree( GetProcessHeap(), 0, name );
    return ret;
}


/**********************************************************************
 *       CreateProcessA          (KERNEL32.@)
 */
BOOL WINAPI CreateProcessA( LPCSTR app_name, LPSTR cmd_line, LPSECURITY_ATTRIBUTES process_attr,
                            LPSECURITY_ATTRIBUTES thread_attr, BOOL inherit,
                            DWORD flags, LPVOID env, LPCSTR cur_dir,
                            LPSTARTUPINFOA startup_info, LPPROCESS_INFORMATION info )
{
    BOOL retv = FALSE;
    HANDLE hFile = 0;
    const char *unixdir = NULL;
    DOS_FULL_NAME full_dir;
    char name[MAX_PATH];
    LPSTR tidy_cmdline;
1155
    char *p;
1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206

    /* Process the AppName and/or CmdLine to get module name and path */

    TRACE("app %s cmdline %s\n", debugstr_a(app_name), debugstr_a(cmd_line) );

    if (!(tidy_cmdline = get_file_name( app_name, cmd_line, name, sizeof(name), &hFile )))
        return FALSE;
    if (hFile == INVALID_HANDLE_VALUE) goto done;

    /* Warn if unsupported features are used */

    if (flags & NORMAL_PRIORITY_CLASS)
        FIXME("(%s,...): NORMAL_PRIORITY_CLASS ignored\n", name);
    if (flags & IDLE_PRIORITY_CLASS)
        FIXME("(%s,...): IDLE_PRIORITY_CLASS ignored\n", name);
    if (flags & HIGH_PRIORITY_CLASS)
        FIXME("(%s,...): HIGH_PRIORITY_CLASS ignored\n", name);
    if (flags & REALTIME_PRIORITY_CLASS)
        FIXME("(%s,...): REALTIME_PRIORITY_CLASS ignored\n", name);
    if (flags & CREATE_NEW_PROCESS_GROUP)
        FIXME("(%s,...): CREATE_NEW_PROCESS_GROUP ignored\n", name);
    if (flags & CREATE_UNICODE_ENVIRONMENT)
        FIXME("(%s,...): CREATE_UNICODE_ENVIRONMENT ignored\n", name);
    if (flags & CREATE_SEPARATE_WOW_VDM)
        FIXME("(%s,...): CREATE_SEPARATE_WOW_VDM ignored\n", name);
    if (flags & CREATE_SHARED_WOW_VDM)
        FIXME("(%s,...): CREATE_SHARED_WOW_VDM ignored\n", name);
    if (flags & CREATE_DEFAULT_ERROR_MODE)
        FIXME("(%s,...): CREATE_DEFAULT_ERROR_MODE ignored\n", name);
    if (flags & CREATE_NO_WINDOW)
        FIXME("(%s,...): CREATE_NO_WINDOW ignored\n", name);
    if (flags & PROFILE_USER)
        FIXME("(%s,...): PROFILE_USER ignored\n", name);
    if (flags & PROFILE_KERNEL)
        FIXME("(%s,...): PROFILE_KERNEL ignored\n", name);
    if (flags & PROFILE_SERVER)
        FIXME("(%s,...): PROFILE_SERVER ignored\n", name);
    if (startup_info->lpDesktop)
        FIXME("(%s,...): startup_info->lpDesktop %s ignored\n",
              name, debugstr_a(startup_info->lpDesktop));
    if (startup_info->dwFlags & STARTF_RUNFULLSCREEN)
        FIXME("(%s,...): STARTF_RUNFULLSCREEN ignored\n", name);
    if (startup_info->dwFlags & STARTF_FORCEONFEEDBACK)
        FIXME("(%s,...): STARTF_FORCEONFEEDBACK ignored\n", name);
    if (startup_info->dwFlags & STARTF_FORCEOFFFEEDBACK)
        FIXME("(%s,...): STARTF_FORCEOFFFEEDBACK ignored\n", name);
    if (startup_info->dwFlags & STARTF_USEHOTKEY)
        FIXME("(%s,...): STARTF_USEHOTKEY ignored\n", name);

    if (cur_dir)
    {
1207 1208 1209
        UNICODE_STRING cur_dirW;
        RtlCreateUnicodeStringFromAsciiz(&cur_dirW, cur_dir);
        if (DOSFS_GetFullName( cur_dirW.Buffer, TRUE, &full_dir ))
1210
            unixdir = full_dir.long_name;
1211
        RtlFreeUnicodeString(&cur_dirW);
1212 1213 1214
    }
    else
    {
1215 1216
        WCHAR buf[MAX_PATH];
        if (GetCurrentDirectoryW(MAX_PATH, buf))
1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257
        {
            if (DOSFS_GetFullName( buf, TRUE, &full_dir )) unixdir = full_dir.long_name;
        }
    }

    info->hThread = info->hProcess = 0;
    info->dwProcessId = info->dwThreadId = 0;

    /* Determine executable type */

    if (!hFile)  /* builtin exe */
    {
        TRACE( "starting %s as Winelib app\n", debugstr_a(name) );
        retv = create_process( 0, name, tidy_cmdline, env, process_attr, thread_attr,
                               inherit, flags, startup_info, info, unixdir );
        goto done;
    }

    switch( MODULE_GetBinaryType( hFile ))
    {
    case BINARY_PE_EXE:
    case BINARY_WIN16:
    case BINARY_DOS:
        TRACE( "starting %s as Windows binary\n", debugstr_a(name) );
        retv = create_process( hFile, name, tidy_cmdline, env, process_attr, thread_attr,
                               inherit, flags, startup_info, info, unixdir );
        break;
    case BINARY_OS216:
        FIXME( "%s is OS/2 binary, not supported\n", debugstr_a(name) );
        SetLastError( ERROR_BAD_EXE_FORMAT );
        break;
    case BINARY_PE_DLL:
        TRACE( "not starting %s since it is a dll\n", debugstr_a(name) );
        SetLastError( ERROR_BAD_EXE_FORMAT );
        break;
    case BINARY_UNIX_LIB:
        TRACE( "%s is a Unix library, starting as Winelib app\n", debugstr_a(name) );
        retv = create_process( hFile, name, tidy_cmdline, env, process_attr, thread_attr,
                               inherit, flags, startup_info, info, unixdir );
        break;
    case BINARY_UNKNOWN:
1258 1259
        /* check for .com or .bat extension */
        if ((p = strrchr( name, '.' )))
1260
        {
1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287
            if (!FILE_strcasecmp( p, ".com" ))
            {
                TRACE( "starting %s as DOS binary\n", debugstr_a(name) );
                retv = create_process( hFile, name, tidy_cmdline, env, process_attr, thread_attr,
                                       inherit, flags, startup_info, info, unixdir );
                break;
            }
            if (!FILE_strcasecmp( p, ".bat" ))
            {
                char comspec[MAX_PATH];
                if (GetEnvironmentVariableA("COMSPEC", comspec, sizeof(comspec)))
                {
                    char *newcmdline, *q = strchr(cmd_line, ' ');
                    if (!q) q = "";
                    if ((newcmdline = HeapAlloc( GetProcessHeap(), 0,
                                                 strlen(comspec) + strlen(name) + strlen(q) + 8)))
                    {
                        sprintf( newcmdline, "%s /c %s%s", comspec, name, q );
                        TRACE( "starting %s as batch binary: %s\n",
                               debugstr_a(name), debugstr_a(newcmdline) );
                        retv = CreateProcessA( comspec, newcmdline, process_attr, thread_attr,
                                               inherit, flags, env, cur_dir, startup_info, info );
                        HeapFree( GetProcessHeap(), 0, newcmdline );
                        break;
                    }
                }
            }
1288 1289 1290
        }
        /* fall through */
    case BINARY_UNIX_EXE:
1291
        {
1292 1293
            /* unknown file, try as unix executable */
            UNICODE_STRING nameW;
1294 1295 1296 1297
            DOS_FULL_NAME full_name;
            const char *unixfilename = name;

            TRACE( "starting %s as Unix binary\n", debugstr_a(name) );
1298 1299 1300 1301

            RtlCreateUnicodeStringFromAsciiz(&nameW, name);
            if (DOSFS_GetFullName( nameW.Buffer, TRUE, &full_name )) unixfilename = full_name.long_name;
            RtlFreeUnicodeString(&nameW);
1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352
            retv = (fork_and_exec( unixfilename, tidy_cmdline, env, unixdir ) != -1);
        }
        break;
    }
    CloseHandle( hFile );

 done:
    if (tidy_cmdline != cmd_line) HeapFree( GetProcessHeap(), 0, tidy_cmdline );
    return retv;
}


/**********************************************************************
 *       CreateProcessW          (KERNEL32.@)
 * NOTES
 *  lpReserved is not converted
 */
BOOL WINAPI CreateProcessW( LPCWSTR app_name, LPWSTR cmd_line, LPSECURITY_ATTRIBUTES process_attr,
                            LPSECURITY_ATTRIBUTES thread_attr, BOOL inherit, DWORD flags,
                            LPVOID env, LPCWSTR cur_dir, LPSTARTUPINFOW startup_info,
                            LPPROCESS_INFORMATION info )
{
    BOOL ret;
    STARTUPINFOA StartupInfoA;

    LPSTR app_nameA = HEAP_strdupWtoA (GetProcessHeap(),0,app_name);
    LPSTR cmd_lineA = HEAP_strdupWtoA (GetProcessHeap(),0,cmd_line);
    LPSTR cur_dirA = HEAP_strdupWtoA (GetProcessHeap(),0,cur_dir);

    memcpy (&StartupInfoA, startup_info, sizeof(STARTUPINFOA));
    StartupInfoA.lpDesktop = HEAP_strdupWtoA (GetProcessHeap(),0,startup_info->lpDesktop);
    StartupInfoA.lpTitle = HEAP_strdupWtoA (GetProcessHeap(),0,startup_info->lpTitle);

    TRACE_(win32)("(%s,%s,...)\n", debugstr_w(app_name), debugstr_w(cmd_line));

    if (startup_info->lpReserved)
      FIXME_(win32)("StartupInfo.lpReserved is used, please report (%s)\n",
                    debugstr_w(startup_info->lpReserved));

    ret = CreateProcessA( app_nameA,  cmd_lineA, process_attr, thread_attr,
                          inherit, flags, env, cur_dirA, &StartupInfoA, info );

    HeapFree( GetProcessHeap(), 0, cur_dirA );
    HeapFree( GetProcessHeap(), 0, cmd_lineA );
    HeapFree( GetProcessHeap(), 0, StartupInfoA.lpDesktop );
    HeapFree( GetProcessHeap(), 0, StartupInfoA.lpTitle );

    return ret;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1353
/***********************************************************************
1354
 *           ExitProcess   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1355
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1356
void WINAPI ExitProcess( DWORD status )
Alexandre Julliard's avatar
Alexandre Julliard committed
1357
{
1358
    MODULE_DllProcessDetach( TRUE, (LPVOID)1 );
1359
    SERVER_START_REQ( terminate_process )
1360 1361 1362 1363
    {
        /* send the exit code to the server */
        req->handle    = GetCurrentProcess();
        req->exit_code = status;
1364
        wine_server_call( req );
1365 1366
    }
    SERVER_END_REQ;
1367
    exit( status );
Alexandre Julliard's avatar
Alexandre Julliard committed
1368 1369
}

1370
/***********************************************************************
1371
 *           ExitProcess   (KERNEL.466)
1372 1373 1374
 */
void WINAPI ExitProcess16( WORD status )
{
1375 1376
    DWORD count;
    ReleaseThunkLock( &count );
1377 1378
    ExitProcess( status );
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1379

Alexandre Julliard's avatar
Alexandre Julliard committed
1380
/******************************************************************************
1381
 *           TerminateProcess   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1382
 */
1383
BOOL WINAPI TerminateProcess( HANDLE handle, DWORD exit_code )
Alexandre Julliard's avatar
Alexandre Julliard committed
1384
{
1385 1386 1387
    NTSTATUS status = NtTerminateProcess( handle, exit_code );
    if (status) SetLastError( RtlNtStatusToDosError(status) );
    return !status;
Alexandre Julliard's avatar
Alexandre Julliard committed
1388 1389
}

1390 1391

/***********************************************************************
1392 1393
 *           GetProcessDword    (KERNEL.485)
 *           GetProcessDword    (KERNEL32.18)
1394 1395 1396 1397 1398 1399
 * 'Of course you cannot directly access Windows internal structures'
 */
DWORD WINAPI GetProcessDword( DWORD dwProcessID, INT offset )
{
    DWORD x, y;

1400
    TRACE_(win32)("(%ld, %d)\n", dwProcessID, offset );
1401 1402

    if (dwProcessID && dwProcessID != GetCurrentProcessId())
1403 1404 1405 1406
    {
        ERR("%d: process %lx not accessible\n", offset, dwProcessID);
        return 0;
    }
1407

1408
    switch ( offset )
1409 1410
    {
    case GPD_APP_COMPAT_FLAGS:
1411
        return GetAppCompatFlags16(0);
1412 1413

    case GPD_LOAD_DONE_EVENT:
1414
        return (DWORD)current_process.load_done_evt;
1415 1416

    case GPD_HINSTANCE16:
1417
        return GetTaskDS16();
1418 1419

    case GPD_WINDOWS_VERSION:
1420
        return GetExeVersion16();
1421 1422

    case GPD_THDB:
1423
        return (DWORD)NtCurrentTeb() - 0x10 /* FIXME */;
1424 1425

    case GPD_PDB:
1426
        return (DWORD)&current_process;
1427 1428

    case GPD_STARTF_SHELLDATA: /* return stdoutput handle from startupinfo ??? */
1429
        return (DWORD)current_startupinfo.hStdOutput;
1430 1431

    case GPD_STARTF_HOTKEY: /* return stdinput handle from startupinfo ??? */
1432
        return (DWORD)current_startupinfo.hStdInput;
1433 1434

    case GPD_STARTF_SHOWWINDOW:
1435
        return current_startupinfo.wShowWindow;
1436 1437

    case GPD_STARTF_SIZE:
1438
        x = current_startupinfo.dwXSize;
1439
        if ( (INT)x == CW_USEDEFAULT ) x = CW_USEDEFAULT16;
1440
        y = current_startupinfo.dwYSize;
1441
        if ( (INT)y == CW_USEDEFAULT ) y = CW_USEDEFAULT16;
1442 1443 1444
        return MAKELONG( x, y );

    case GPD_STARTF_POSITION:
1445
        x = current_startupinfo.dwX;
1446
        if ( (INT)x == CW_USEDEFAULT ) x = CW_USEDEFAULT16;
1447
        y = current_startupinfo.dwY;
1448
        if ( (INT)y == CW_USEDEFAULT ) y = CW_USEDEFAULT16;
1449 1450 1451
        return MAKELONG( x, y );

    case GPD_STARTF_FLAGS:
1452
        return current_startupinfo.dwFlags;
1453

1454
    case GPD_PARENT:
1455
        return 0;
1456 1457

    case GPD_FLAGS:
1458
        return current_process.flags;
1459 1460

    case GPD_USERDATA:
1461
        return current_process.process_dword;
1462 1463

    default:
1464
        ERR_(win32)("Unknown offset %d\n", offset );
1465 1466 1467 1468 1469 1470 1471 1472 1473 1474
        return 0;
    }
}

/***********************************************************************
 *           SetProcessDword    (KERNEL.484)
 * 'Of course you cannot directly access Windows internal structures'
 */
void WINAPI SetProcessDword( DWORD dwProcessID, INT offset, DWORD value )
{
1475
    TRACE_(win32)("(%ld, %d)\n", dwProcessID, offset );
1476 1477

    if (dwProcessID && dwProcessID != GetCurrentProcessId())
1478 1479 1480 1481
    {
        ERR("%d: process %lx not accessible\n", offset, dwProcessID);
        return;
    }
1482

1483
    switch ( offset )
1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496
    {
    case GPD_APP_COMPAT_FLAGS:
    case GPD_LOAD_DONE_EVENT:
    case GPD_HINSTANCE16:
    case GPD_WINDOWS_VERSION:
    case GPD_THDB:
    case GPD_PDB:
    case GPD_STARTF_SHELLDATA:
    case GPD_STARTF_HOTKEY:
    case GPD_STARTF_SHOWWINDOW:
    case GPD_STARTF_SIZE:
    case GPD_STARTF_POSITION:
    case GPD_STARTF_FLAGS:
1497
    case GPD_PARENT:
1498
    case GPD_FLAGS:
1499
        ERR_(win32)("Not allowed to modify offset %d\n", offset );
1500 1501 1502
        break;

    case GPD_USERDATA:
1503
        current_process.process_dword = value;
1504 1505 1506
        break;

    default:
1507
        ERR_(win32)("Unknown offset %d\n", offset );
1508 1509 1510 1511 1512
        break;
    }
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1513
/*********************************************************************
1514
 *           OpenProcess   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1515
 */
1516
HANDLE WINAPI OpenProcess( DWORD access, BOOL inherit, DWORD id )
Alexandre Julliard's avatar
Alexandre Julliard committed
1517
{
1518
    HANDLE ret = 0;
1519
    SERVER_START_REQ( open_process )
1520
    {
1521
        req->pid     = id;
1522 1523
        req->access  = access;
        req->inherit = inherit;
1524
        if (!wine_server_call_err( req )) ret = reply->handle;
1525 1526
    }
    SERVER_END_REQ;
1527
    return ret;
1528
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1529

1530 1531 1532 1533 1534
/*********************************************************************
 *           MapProcessHandle   (KERNEL.483)
 */
DWORD WINAPI MapProcessHandle( HANDLE handle )
{
1535
    DWORD ret = 0;
1536
    SERVER_START_REQ( get_process_info )
1537 1538
    {
        req->handle = handle;
1539
        if (!wine_server_call_err( req )) ret = (DWORD)reply->pid;
1540 1541
    }
    SERVER_END_REQ;
1542
    return ret;
1543
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1544

Alexandre Julliard's avatar
Alexandre Julliard committed
1545
/***********************************************************************
1546
 *           SetPriorityClass   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1547
 */
1548
BOOL WINAPI SetPriorityClass( HANDLE hprocess, DWORD priorityclass )
Alexandre Julliard's avatar
Alexandre Julliard committed
1549
{
1550
    BOOL ret;
1551
    SERVER_START_REQ( set_process_info )
1552 1553 1554 1555
    {
        req->handle   = hprocess;
        req->priority = priorityclass;
        req->mask     = SET_PROCESS_INFO_PRIORITY;
1556
        ret = !wine_server_call_err( req );
1557 1558 1559
    }
    SERVER_END_REQ;
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1560 1561 1562 1563
}


/***********************************************************************
1564
 *           GetPriorityClass   (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1565
 */
1566
DWORD WINAPI GetPriorityClass(HANDLE hprocess)
Alexandre Julliard's avatar
Alexandre Julliard committed
1567
{
1568
    DWORD ret = 0;
1569
    SERVER_START_REQ( get_process_info )
1570 1571
    {
        req->handle = hprocess;
1572
        if (!wine_server_call_err( req )) ret = reply->priority;
1573 1574
    }
    SERVER_END_REQ;
1575
    return ret;
1576 1577 1578 1579
}


/***********************************************************************
1580
 *          SetProcessAffinityMask   (KERNEL32.@)
1581
 */
1582
BOOL WINAPI SetProcessAffinityMask( HANDLE hProcess, DWORD affmask )
1583
{
1584
    BOOL ret;
1585
    SERVER_START_REQ( set_process_info )
1586 1587 1588 1589
    {
        req->handle   = hProcess;
        req->affinity = affmask;
        req->mask     = SET_PROCESS_INFO_AFFINITY;
1590
        ret = !wine_server_call_err( req );
1591 1592 1593
    }
    SERVER_END_REQ;
    return ret;
1594 1595 1596
}

/**********************************************************************
1597
 *          GetProcessAffinityMask    (KERNEL32.@)
1598
 */
1599
BOOL WINAPI GetProcessAffinityMask( HANDLE hProcess,
1600 1601 1602
                                      LPDWORD lpProcessAffinityMask,
                                      LPDWORD lpSystemAffinityMask )
{
1603
    BOOL ret = FALSE;
1604
    SERVER_START_REQ( get_process_info )
1605
    {
1606
        req->handle = hProcess;
1607
        if (!wine_server_call_err( req ))
1608
        {
1609 1610
            if (lpProcessAffinityMask) *lpProcessAffinityMask = reply->process_affinity;
            if (lpSystemAffinityMask) *lpSystemAffinityMask = reply->system_affinity;
1611 1612
            ret = TRUE;
        }
1613
    }
1614
    SERVER_END_REQ;
1615
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1616 1617 1618
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1619
/***********************************************************************
1620
 *           GetProcessVersion    (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1621
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1622
DWORD WINAPI GetProcessVersion( DWORD processid )
Alexandre Julliard's avatar
Alexandre Julliard committed
1623
{
1624 1625 1626
    IMAGE_NT_HEADERS *nt;

    if (processid && processid != GetCurrentProcessId())
1627 1628 1629 1630
    {
	FIXME("should use ReadProcessMemory\n");
        return 0;
    }
1631 1632 1633 1634
    if ((nt = RtlImageNtHeader( current_process.module )))
        return ((nt->OptionalHeader.MajorSubsystemVersion << 16) |
                nt->OptionalHeader.MinorSubsystemVersion);
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1635
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1636

Alexandre Julliard's avatar
Alexandre Julliard committed
1637
/***********************************************************************
1638
 *           GetProcessFlags    (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1639
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1640
DWORD WINAPI GetProcessFlags( DWORD processid )
Alexandre Julliard's avatar
Alexandre Julliard committed
1641
{
1642 1643
    if (processid && processid != GetCurrentProcessId()) return 0;
    return current_process.flags;
Alexandre Julliard's avatar
Alexandre Julliard committed
1644 1645
}

1646

Alexandre Julliard's avatar
Alexandre Julliard committed
1647
/***********************************************************************
1648
 *		SetProcessWorkingSetSize	[KERNEL32.@]
Alexandre Julliard's avatar
Alexandre Julliard committed
1649 1650 1651 1652 1653 1654 1655 1656
 * Sets the min/max working set sizes for a specified process.
 *
 * PARAMS
 *    hProcess [I] Handle to the process of interest
 *    minset   [I] Specifies minimum working set size
 *    maxset   [I] Specifies maximum working set size
 *
 * RETURNS  STD
Alexandre Julliard's avatar
Alexandre Julliard committed
1657
 */
1658 1659
BOOL WINAPI SetProcessWorkingSetSize(HANDLE hProcess, SIZE_T minset,
                                     SIZE_T maxset)
Alexandre Julliard's avatar
Alexandre Julliard committed
1660
{
Patrik Stridvall's avatar
Patrik Stridvall committed
1661
    FIXME("(%p,%ld,%ld): stub - harmless\n",hProcess,minset,maxset);
1662
    if(( minset == (SIZE_T)-1) && (maxset == (SIZE_T)-1)) {
Alexandre Julliard's avatar
Alexandre Julliard committed
1663 1664 1665
        /* Trim the working set to zero */
        /* Swap the process out of physical RAM */
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1666
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1667 1668 1669
}

/***********************************************************************
1670
 *           GetProcessWorkingSetSize    (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1671
 */
1672 1673
BOOL WINAPI GetProcessWorkingSetSize(HANDLE hProcess, PSIZE_T minset,
                                     PSIZE_T maxset)
Alexandre Julliard's avatar
Alexandre Julliard committed
1674
{
Patrik Stridvall's avatar
Patrik Stridvall committed
1675
	FIXME("(%p,%p,%p): stub\n",hProcess,minset,maxset);
Alexandre Julliard's avatar
Alexandre Julliard committed
1676 1677 1678 1679 1680 1681 1682
	/* 32 MB working set size */
	if (minset) *minset = 32*1024*1024;
	if (maxset) *maxset = 32*1024*1024;
	return TRUE;
}

/***********************************************************************
1683
 *           SetProcessShutdownParameters    (KERNEL32.@)
1684 1685 1686 1687
 *
 * CHANGED - James Sutherland (JamesSutherland@gmx.de)
 * Now tracks changes made (but does not act on these changes)
 */
1688 1689 1690 1691
static DWORD shutdown_flags = 0;
static DWORD shutdown_priority = 0x280;

BOOL WINAPI SetProcessShutdownParameters(DWORD level, DWORD flags)
Alexandre Julliard's avatar
Alexandre Julliard committed
1692
{
1693 1694 1695
    FIXME("(%08lx, %08lx): partial stub.\n", level, flags);
    shutdown_flags = flags;
    shutdown_priority = level;
Alexandre Julliard's avatar
Alexandre Julliard committed
1696
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1697 1698
}

1699 1700

/***********************************************************************
1701
 * GetProcessShutdownParameters                 (KERNEL32.@)
1702 1703
 *
 */
1704
BOOL WINAPI GetProcessShutdownParameters( LPDWORD lpdwLevel, LPDWORD lpdwFlags )
1705
{
1706 1707 1708
    *lpdwLevel = shutdown_priority;
    *lpdwFlags = shutdown_flags;
    return TRUE;
1709
}
1710 1711


1712
/***********************************************************************
1713
 *           SetProcessPriorityBoost    (KERNEL32.@)
1714
 */
1715
BOOL WINAPI SetProcessPriorityBoost(HANDLE hprocess,BOOL disableboost)
1716
{
1717
    FIXME("(%p,%d): stub\n",hprocess,disableboost);
1718 1719 1720 1721
    /* Say we can do it. I doubt the program will notice that we don't. */
    return TRUE;
}

1722

Alexandre Julliard's avatar
Alexandre Julliard committed
1723
/***********************************************************************
1724
 *		ReadProcessMemory (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1725
 */
1726 1727
BOOL WINAPI ReadProcessMemory( HANDLE process, LPCVOID addr, LPVOID buffer, SIZE_T size,
                               SIZE_T *bytes_read )
Alexandre Julliard's avatar
Alexandre Julliard committed
1728
{
1729
    DWORD res;
1730

1731
    SERVER_START_REQ( read_process_memory )
1732
    {
1733 1734 1735 1736
        req->handle = process;
        req->addr   = (void *)addr;
        wine_server_set_reply( req, buffer, size );
        if ((res = wine_server_call_err( req ))) size = 0;
1737
    }
1738 1739 1740
    SERVER_END_REQ;
    if (bytes_read) *bytes_read = size;
    return !res;
Alexandre Julliard's avatar
Alexandre Julliard committed
1741 1742
}

1743

Alexandre Julliard's avatar
Alexandre Julliard committed
1744
/***********************************************************************
1745
 *           WriteProcessMemory    		(KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1746
 */
1747 1748
BOOL WINAPI WriteProcessMemory( HANDLE process, LPVOID addr, LPCVOID buffer, SIZE_T size,
                                SIZE_T *bytes_written )
Alexandre Julliard's avatar
Alexandre Julliard committed
1749
{
1750 1751 1752
    static const int zero;
    unsigned int first_offset, last_offset, first_mask, last_mask;
    DWORD res;
1753 1754 1755 1756 1757 1758 1759 1760

    if (!size)
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return FALSE;
    }

    /* compute the mask for the first int */
1761
    first_mask = ~0;
1762
    first_offset = (unsigned int)addr % sizeof(int);
1763
    memset( &first_mask, 0, first_offset );
1764 1765 1766 1767 1768 1769

    /* compute the mask for the last int */
    last_offset = (size + first_offset) % sizeof(int);
    last_mask = 0;
    memset( &last_mask, 0xff, last_offset ? last_offset : sizeof(int) );

1770
    SERVER_START_REQ( write_process_memory )
1771
    {
1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785
        req->handle     = process;
        req->addr       = (char *)addr - first_offset;
        req->first_mask = first_mask;
        req->last_mask  = last_mask;
        if (first_offset) wine_server_add_data( req, &zero, first_offset );
        wine_server_add_data( req, buffer, size );
        if (last_offset) wine_server_add_data( req, &zero, sizeof(int) - last_offset );

        if ((res = wine_server_call_err( req ))) size = 0;
    }
    SERVER_END_REQ;
    if (bytes_written) *bytes_written = size;
    {
        char dummy[32];
1786
        SIZE_T read;
1787
        ReadProcessMemory( process, addr, dummy, sizeof(dummy), &read );
1788
    }
1789
    return !res;
Alexandre Julliard's avatar
Alexandre Julliard committed
1790 1791
}

1792

Alexandre Julliard's avatar
Alexandre Julliard committed
1793
/***********************************************************************
1794 1795
 *		RegisterServiceProcess (KERNEL.491)
 *		RegisterServiceProcess (KERNEL32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1796 1797 1798 1799
 *
 * A service process calls this function to ensure that it continues to run
 * even after a user logged off.
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
1800
DWORD WINAPI RegisterServiceProcess(DWORD dwProcessId, DWORD dwType)
Alexandre Julliard's avatar
Alexandre Julliard committed
1801 1802 1803 1804
{
	/* I don't think that Wine needs to do anything in that function */
	return 1; /* success */
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1805 1806

/***********************************************************************
1807
 * GetExitCodeProcess [KERNEL32.@]
Alexandre Julliard's avatar
Alexandre Julliard committed
1808 1809
 *
 * Gets termination status of specified process
1810
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1811 1812 1813 1814
 * RETURNS
 *   Success: TRUE
 *   Failure: FALSE
 */
1815
BOOL WINAPI GetExitCodeProcess(
1816 1817
    HANDLE hProcess,    /* [in] handle to the process */
    LPDWORD lpExitCode) /* [out] address to receive termination status */
Alexandre Julliard's avatar
Alexandre Julliard committed
1818
{
1819
    BOOL ret;
1820
    SERVER_START_REQ( get_process_info )
1821
    {
1822
        req->handle = hProcess;
1823 1824
        ret = !wine_server_call_err( req );
        if (ret && lpExitCode) *lpExitCode = reply->exit_code;
1825
    }
1826
    SERVER_END_REQ;
1827
    return ret;
Alexandre Julliard's avatar
Alexandre Julliard committed
1828
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1829

1830

1831
/***********************************************************************
1832
 *           SetErrorMode   (KERNEL32.@)
1833 1834 1835
 */
UINT WINAPI SetErrorMode( UINT mode )
{
1836 1837
    UINT old = current_process.error_mode;
    current_process.error_mode = mode;
1838 1839
    return old;
}
1840

1841

1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872
/**************************************************************************
 *              SetFileApisToOEM   (KERNEL32.@)
 */
VOID WINAPI SetFileApisToOEM(void)
{
    current_process.flags |= PDB32_FILE_APIS_OEM;
}


/**************************************************************************
 *              SetFileApisToANSI   (KERNEL32.@)
 */
VOID WINAPI SetFileApisToANSI(void)
{
    current_process.flags &= ~PDB32_FILE_APIS_OEM;
}


/******************************************************************************
 * AreFileApisANSI [KERNEL32.@]  Determines if file functions are using ANSI
 *
 * RETURNS
 *    TRUE:  Set of file functions is using ANSI code page
 *    FALSE: Set of file functions is using OEM code page
 */
BOOL WINAPI AreFileApisANSI(void)
{
    return !(current_process.flags & PDB32_FILE_APIS_OEM);
}


1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886
/***********************************************************************
 *           GetTickCount       (KERNEL32.@)
 *
 * Returns the number of milliseconds, modulo 2^32, since the start
 * of the wineserver.
 */
DWORD WINAPI GetTickCount(void)
{
    struct timeval t;
    gettimeofday( &t, NULL );
    return ((t.tv_sec * 1000) + (t.tv_usec / 1000)) - server_startticks;
}


1887
/**********************************************************************
1888
 * TlsAlloc [KERNEL32.@]  Allocates a TLS index.
1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914
 *
 * Allocates a thread local storage index
 *
 * RETURNS
 *    Success: TLS Index
 *    Failure: 0xFFFFFFFF
 */
DWORD WINAPI TlsAlloc( void )
{
    DWORD i, mask, ret = 0;
    DWORD *bits = current_process.tls_bits;
    RtlAcquirePebLock();
    if (*bits == 0xffffffff)
    {
        bits++;
        ret = 32;
        if (*bits == 0xffffffff)
        {
            RtlReleasePebLock();
            SetLastError( ERROR_NO_MORE_ITEMS );
            return 0xffffffff;
        }
    }
    for (i = 0, mask = 1; i < 32; i++, mask <<= 1) if (!(*bits & mask)) break;
    *bits |= mask;
    RtlReleasePebLock();
1915
    NtCurrentTeb()->tls_array[ret+i] = 0; /* clear the value */
1916 1917 1918 1919 1920
    return ret + i;
}


/**********************************************************************
1921
 * TlsFree [KERNEL32.@]  Releases a TLS index.
1922 1923
 *
 * Releases a thread local storage index, making it available for reuse
1924
 *
1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955
 * RETURNS
 *    Success: TRUE
 *    Failure: FALSE
 */
BOOL WINAPI TlsFree(
    DWORD index) /* [in] TLS Index to free */
{
    DWORD mask = (1 << (index & 31));
    DWORD *bits = current_process.tls_bits;
    if (index >= 64)
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return FALSE;
    }
    if (index >= 32) bits++;
    RtlAcquirePebLock();
    if (!(*bits & mask))  /* already free? */
    {
        RtlReleasePebLock();
        SetLastError( ERROR_INVALID_PARAMETER );
        return FALSE;
    }
    *bits &= ~mask;
    NtCurrentTeb()->tls_array[index] = 0;
    /* FIXME: should zero all other thread values */
    RtlReleasePebLock();
    return TRUE;
}


/**********************************************************************
1956
 * TlsGetValue [KERNEL32.@]  Gets value in a thread's TLS slot
1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975
 *
 * RETURNS
 *    Success: Value stored in calling thread's TLS slot for index
 *    Failure: 0 and GetLastError returns NO_ERROR
 */
LPVOID WINAPI TlsGetValue(
    DWORD index) /* [in] TLS index to retrieve value for */
{
    if (index >= 64)
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return NULL;
    }
    SetLastError( ERROR_SUCCESS );
    return NtCurrentTeb()->tls_array[index];
}


/**********************************************************************
1976
 * TlsSetValue [KERNEL32.@]  Stores a value in the thread's TLS slot.
1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995
 *
 * RETURNS
 *    Success: TRUE
 *    Failure: FALSE
 */
BOOL WINAPI TlsSetValue(
    DWORD index,  /* [in] TLS index to set value for */
    LPVOID value) /* [in] Value to be stored */
{
    if (index >= 64)
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return FALSE;
    }
    NtCurrentTeb()->tls_array[index] = value;
    return TRUE;
}


1996
/***********************************************************************
1997
 *           GetCurrentProcess   (KERNEL32.@)
1998 1999 2000 2001
 */
#undef GetCurrentProcess
HANDLE WINAPI GetCurrentProcess(void)
{
2002
    return (HANDLE)0xffffffff;
2003
}