process.c 131 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Win32 processes
 *
 * Copyright 1996, 1998 Alexandre Julliard
 *
 * 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
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 20 21 22 23
 */

#include "config.h"
#include "wine/port.h"

24 25 26 27
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
28
#include <stdarg.h>
29
#include <stdio.h>
30 31 32 33
#include <time.h>
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
34 35 36 37 38 39
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
40 41 42
#ifdef HAVE_SYS_PRCTL_H
# include <sys/prctl.h>
#endif
43
#include <sys/types.h>
44 45 46
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
47 48 49 50 51 52 53
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef __APPLE__
#include <CoreFoundation/CoreFoundation.h>
#include <pthread.h>
#endif
54

55 56
#include "ntstatus.h"
#define WIN32_NO_STATUS
57
#include "winternl.h"
58
#include "kernel_private.h"
59
#include "psapi.h"
60
#include "wine/library.h"
61
#include "wine/server.h"
62
#include "wine/unicode.h"
63 64
#include "wine/debug.h"

65
WINE_DEFAULT_DEBUG_CHANNEL(process);
66
WINE_DECLARE_DEBUG_CHANNEL(file);
67
WINE_DECLARE_DEBUG_CHANNEL(relay);
68

69 70 71
#ifdef __APPLE__
extern char **__wine_get_main_environment(void);
#else
72 73
extern char **__wine_main_environ;
static char **__wine_get_main_environment(void) { return __wine_main_environ; }
74 75
#endif

76 77 78 79 80 81 82 83
typedef struct
{
    LPSTR lpEnvAddress;
    LPSTR lpCmdLine;
    LPSTR lpCmdShow;
    DWORD dwReserved;
} LOADPARMS32;

84 85
static DWORD shutdown_flags = 0;
static DWORD shutdown_priority = 0x280;
86
static BOOL is_wow64;
87
static const BOOL is_win64 = (sizeof(void *) > sizeof(int));
88

89
HMODULE kernel32_handle = 0;
90
SYSTEM_BASIC_INFORMATION system_info = { 0 };
91

92 93
const WCHAR *DIR_Windows = NULL;
const WCHAR *DIR_System = NULL;
94
const WCHAR *DIR_SysWow64 = NULL;
95

96 97 98 99 100 101 102 103
/* 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 */

104
static const WCHAR exeW[] = {'.','e','x','e',0};
105 106
static const WCHAR comW[] = {'.','c','o','m',0};
static const WCHAR batW[] = {'.','b','a','t',0};
107
static const WCHAR cmdW[] = {'.','c','m','d',0};
108
static const WCHAR pifW[] = {'.','p','i','f',0};
109 110
static const WCHAR winevdmW[] = {'w','i','n','e','v','d','m','.','e','x','e',0};

111 112
static void exec_process( LPCWSTR name );

113
extern void SHELL_LoadRegistry(void);
114

115 116

/***********************************************************************
117
 *           contains_path
118
 */
119
static inline BOOL contains_path( LPCWSTR name )
120
{
121 122 123
    return ((*name && (name[1] == ':')) || strchrW(name, '/') || strchrW(name, '\\'));
}

124

125 126 127 128 129 130
/***********************************************************************
 *           is_special_env_var
 *
 * Check if an environment variable needs to be handled specially when
 * passed through the Unix environment (i.e. prefixed with "WINE").
 */
131
static inline BOOL is_special_env_var( const char *var )
132
{
133
    return (!strncmp( var, "PATH=", sizeof("PATH=")-1 ) ||
134
            !strncmp( var, "PWD=", sizeof("PWD=")-1 ) ||
135
            !strncmp( var, "HOME=", sizeof("HOME=")-1 ) ||
136 137
            !strncmp( var, "TEMP=", sizeof("TEMP=")-1 ) ||
            !strncmp( var, "TMP=", sizeof("TMP=")-1 ));
138 139 140
}


141 142 143 144 145 146 147 148 149 150 151 152 153
/***********************************************************************
 *           is_path_prefix
 */
static inline unsigned int is_path_prefix( const WCHAR *prefix, const WCHAR *filename )
{
    unsigned int len = strlenW( prefix );

    if (strncmpiW( filename, prefix, len ) || filename[len] != '\\') return 0;
    while (filename[len] == '\\') len++;
    return len;
}


154 155 156 157 158
/***************************************************************************
 *	get_builtin_path
 *
 * Get the path of a builtin module when the native file does not exist.
 */
159 160
static BOOL get_builtin_path( const WCHAR *libname, const WCHAR *ext, WCHAR *filename,
                              UINT size, struct binary_info *binary_info )
161 162
{
    WCHAR *file_part;
163 164 165 166
    UINT len;
    void *redir_disabled = 0;
    unsigned int flags = (sizeof(void*) > sizeof(int) ? BINARY_FLAG_64BIT : 0);

167 168 169
    /* builtin names cannot be empty or contain spaces */
    if (!libname[0] || strchrW( libname, ' ' ) || strchrW( libname, '\t' )) return FALSE;

170 171
    if (is_wow64 && Wow64DisableWow64FsRedirection( &redir_disabled ))
        Wow64RevertWow64FsRedirection( redir_disabled );
172 173 174 175 176 177 178

    if (contains_path( libname ))
    {
        if (RtlGetFullPathName_U( libname, size * sizeof(WCHAR),
                                  filename, &file_part ) > size * sizeof(WCHAR))
            return FALSE;  /* too long */

179 180 181 182 183 184 185 186 187 188
        if ((len = is_path_prefix( DIR_System, filename )))
        {
            if (is_wow64 && redir_disabled) flags = BINARY_FLAG_64BIT;
        }
        else if (DIR_SysWow64 && (len = is_path_prefix( DIR_SysWow64, filename )))
        {
            flags = 0;
        }
        else return FALSE;

189
        if (filename + len != file_part) return FALSE;
190 191 192
    }
    else
    {
193
        len = strlenW( DIR_System );
194
        if (strlenW(libname) + len + 2 >= size) return FALSE;  /* too long */
195
        memcpy( filename, DIR_System, len * sizeof(WCHAR) );
196 197 198
        file_part = filename + len;
        if (file_part > filename && file_part[-1] != '\\') *file_part++ = '\\';
        strcpyW( file_part, libname );
199
        if (is_wow64 && redir_disabled) flags = BINARY_FLAG_64BIT;
200 201 202 203 204 205 206
    }
    if (ext && !strchrW( file_part, '.' ))
    {
        if (file_part + strlenW(file_part) + strlenW(ext) + 1 > filename + size)
            return FALSE;  /* too long */
        strcatW( file_part, ext );
    }
207 208 209 210
    binary_info->type = BINARY_UNIX_LIB;
    binary_info->flags = flags;
    binary_info->res_start = NULL;
    binary_info->res_end = NULL;
211 212 213 214 215 216 217 218 219 220 221 222
    /* assume current arch */
#if defined(__i386__) || defined(__x86_64__)
    binary_info->arch = (flags & BINARY_FLAG_64BIT) ? IMAGE_FILE_MACHINE_AMD64 : IMAGE_FILE_MACHINE_I386;
#elif defined(__powerpc__)
    binary_info->arch = IMAGE_FILE_MACHINE_POWERPC;
#elif defined(__arm__) && !defined(__ARMEB__)
    binary_info->arch = IMAGE_FILE_MACHINE_ARMNT;
#elif defined(__aarch64__)
    binary_info->arch = IMAGE_FILE_MACHINE_ARM64;
#else
    binary_info->arch = IMAGE_FILE_MACHINE_UNKNOWN;
#endif
223
    return TRUE;
224 225 226 227 228 229 230 231 232
}


/***********************************************************************
 *           open_exe_file
 *
 * Open a specific exe file, taking load order into account.
 * Returns the file handle or 0 for a builtin exe.
 */
233
static HANDLE open_exe_file( const WCHAR *name, struct binary_info *binary_info )
234 235 236
{
    HANDLE handle;

237
    TRACE("looking for %s\n", debugstr_w(name) );
238

239
    if ((handle = CreateFileW( name, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_DELETE,
240 241
                               NULL, OPEN_EXISTING, 0, 0 )) == INVALID_HANDLE_VALUE)
    {
242
        WCHAR buffer[MAX_PATH];
243
        /* file doesn't exist, check for builtin */
244
        if (contains_path( name ) && get_builtin_path( name, NULL, buffer, sizeof(buffer), binary_info ))
245
            handle = 0;
246
    }
247 248
    else MODULE_get_binary_info( handle, binary_info );

249
    return handle;
250 251 252 253 254 255 256 257 258
}


/***********************************************************************
 *           find_exe_file
 *
 * Open an exe file, and return the full name and file handle.
 * Returns FALSE if file could not be found.
 */
259 260
static BOOL find_exe_file( const WCHAR *name, WCHAR *buffer, int buflen,
                           HANDLE *handle, struct binary_info *binary_info )
261
{
262
    TRACE("looking for %s\n", debugstr_w(name) );
263

264
    if (!SearchPathW( NULL, name, exeW, buflen, buffer, NULL ) &&
265
        /* no builtin found, try native without extension in case it is a Unix app */
266
        !SearchPathW( NULL, name, NULL, buflen, buffer, NULL )) return FALSE;
267

268 269 270
    TRACE( "Trying native exe %s\n", debugstr_w(buffer) );
    if ((*handle = CreateFileW( buffer, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_DELETE,
                                NULL, OPEN_EXISTING, 0, 0 )) != INVALID_HANDLE_VALUE)
271 272
    {
        MODULE_get_binary_info( *handle, binary_info );
273
        return TRUE;
274
    }
275 276 277 278
    return FALSE;
}


279
/***********************************************************************
280
 *           build_initial_environment
281 282 283
 *
 * Build the Win32 environment from the Unix environment
 */
284
static BOOL build_initial_environment(void)
285
{
286
    SIZE_T size = 1;
287 288 289
    char **e;
    WCHAR *p, *endptr;
    void *ptr;
290
    char **env = __wine_get_main_environment();
291 292

    /* Compute the total size of the Unix environment */
293
    for (e = env; *e; e++)
294
    {
295
        if (is_special_env_var( *e )) continue;
296 297 298 299 300
        size += MultiByteToWideChar( CP_UNIXCP, 0, *e, -1, NULL, 0 );
    }
    size *= sizeof(WCHAR);

    /* Now allocate the environment */
301
    ptr = NULL;
302 303 304 305 306 307 308 309
    if (NtAllocateVirtualMemory(NtCurrentProcess(), &ptr, 0, &size,
                                MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE) != STATUS_SUCCESS)
        return FALSE;

    NtCurrentTeb()->Peb->ProcessParameters->Environment = p = ptr;
    endptr = p + size / sizeof(WCHAR);

    /* And fill it with the Unix environment */
310
    for (e = env; *e; e++)
311 312
    {
        char *str = *e;
313 314

        /* skip Unix special variables and use the Wine variants instead */
315
        if (!strncmp( str, "WINE", 4 ))
316 317
        {
            if (is_special_env_var( str + 4 )) str += 4;
318
            else if (!strncmp( str, "WINEPRELOADRESERVE=", 19 )) continue;  /* skip it */
319 320 321
        }
        else if (is_special_env_var( str )) continue;  /* skip it */

322 323 324 325 326 327 328 329
        MultiByteToWideChar( CP_UNIXCP, 0, str, -1, p, endptr - p );
        p += strlenW(p) + 1;
    }
    *p = 0;
    return TRUE;
}


330 331 332 333 334
/***********************************************************************
 *           set_registry_variables
 *
 * Set environment variables by enumerating the values of a key;
 * helper for set_registry_environment().
335
 * Note that Windows happily truncates the value if it's too big.
336
 */
337
static void set_registry_variables( HANDLE hkey, ULONG type )
338
{
339 340
    static const WCHAR pathW[] = {'P','A','T','H'};
    static const WCHAR sep[] = {';',0};
341 342 343 344
    UNICODE_STRING env_name, env_value;
    NTSTATUS status;
    DWORD size;
    int index;
345
    char buffer[1024*sizeof(WCHAR) + sizeof(KEY_VALUE_FULL_INFORMATION)];
346 347
    WCHAR tmpbuf[1024];
    UNICODE_STRING tmp;
348 349
    KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer;

350 351 352
    tmp.Buffer = tmpbuf;
    tmp.MaximumLength = sizeof(tmpbuf);

353 354 355 356
    for (index = 0; ; index++)
    {
        status = NtEnumerateValueKey( hkey, index, KeyValueFullInformation,
                                      buffer, sizeof(buffer), &size );
357 358 359 360
        if (status != STATUS_SUCCESS && status != STATUS_BUFFER_OVERFLOW)
            break;
        if (info->Type != type)
            continue;
361 362 363
        env_name.Buffer = info->Name;
        env_name.Length = env_name.MaximumLength = info->NameLength;
        env_value.Buffer = (WCHAR *)(buffer + info->DataOffset);
364 365
        env_value.Length = info->DataLength;
        env_value.MaximumLength = sizeof(buffer) - info->DataOffset;
366
        if (env_value.Length && !env_value.Buffer[env_value.Length/sizeof(WCHAR)-1])
367
            env_value.Length -= sizeof(WCHAR);  /* don't count terminating null if any */
368
        if (!env_value.Length) continue;
369 370
        if (info->Type == REG_EXPAND_SZ)
        {
371 372 373
            status = RtlExpandEnvironmentStrings_U( NULL, &env_value, &tmp, NULL );
            if (status != STATUS_SUCCESS && status != STATUS_BUFFER_OVERFLOW) continue;
            RtlCopyUnicodeString( &env_value, &tmp );
374
        }
375 376 377 378
        /* PATH is magic */
        if (env_name.Length == sizeof(pathW) &&
            !memicmpW( env_name.Buffer, pathW, sizeof(pathW)/sizeof(WCHAR) ) &&
            !RtlQueryEnvironmentVariable_U( NULL, &env_name, &tmp ))
379
        {
380 381 382
            RtlAppendUnicodeToString( &tmp, sep );
            if (RtlAppendUnicodeStringToString( &tmp, &env_value )) continue;
            RtlCopyUnicodeString( &env_value, &tmp );
383
        }
384
        RtlSetEnvironmentVariable( NULL, &env_name, &env_value );
385 386 387 388
    }
}


389 390 391 392
/***********************************************************************
 *           set_registry_environment
 *
 * Set the environment variables specified in the registry.
393 394 395 396 397 398 399
 *
 * Note: Windows handles REG_SZ and REG_EXPAND_SZ in one pass with the
 * consequence that REG_EXPAND_SZ cannot be used reliably as it depends
 * on the order in which the variables are processed. But on Windows it
 * does not really matter since they only use %SystemDrive% and
 * %SystemRoot% which are predefined. But Wine defines these in the
 * registry, so we need two passes.
400
 */
401
static BOOL set_registry_environment( BOOL volatile_only )
402
{
403 404
    static const WCHAR env_keyW[] = {'\\','R','e','g','i','s','t','r','y','\\',
                                     'M','a','c','h','i','n','e','\\',
405 406 407 408 409
                                     'S','y','s','t','e','m','\\',
                                     'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
                                     'C','o','n','t','r','o','l','\\',
                                     'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
                                     'E','n','v','i','r','o','n','m','e','n','t',0};
410
    static const WCHAR envW[] = {'E','n','v','i','r','o','n','m','e','n','t',0};
411
    static const WCHAR volatile_envW[] = {'V','o','l','a','t','i','l','e',' ','E','n','v','i','r','o','n','m','e','n','t',0};
412

413
    OBJECT_ATTRIBUTES attr;
414
    UNICODE_STRING nameW;
415
    HANDLE hkey;
416
    BOOL ret = FALSE;
417 418 419 420 421 422 423

    attr.Length = sizeof(attr);
    attr.RootDirectory = 0;
    attr.ObjectName = &nameW;
    attr.Attributes = 0;
    attr.SecurityDescriptor = NULL;
    attr.SecurityQualityOfService = NULL;
424 425

    /* first the system environment variables */
426
    RtlInitUnicodeString( &nameW, env_keyW );
427
    if (!volatile_only && NtOpenKey( &hkey, KEY_READ, &attr ) == STATUS_SUCCESS)
428
    {
429 430
        set_registry_variables( hkey, REG_SZ );
        set_registry_variables( hkey, REG_EXPAND_SZ );
431
        NtClose( hkey );
432
        ret = TRUE;
433
    }
434

435
    /* then the ones for the current user */
436
    if (RtlOpenCurrentUser( KEY_READ, &attr.RootDirectory ) != STATUS_SUCCESS) return ret;
437
    RtlInitUnicodeString( &nameW, envW );
438
    if (!volatile_only && NtOpenKey( &hkey, KEY_READ, &attr ) == STATUS_SUCCESS)
439
    {
440 441
        set_registry_variables( hkey, REG_SZ );
        set_registry_variables( hkey, REG_EXPAND_SZ );
442
        NtClose( hkey );
443
    }
444 445 446 447 448 449 450 451 452

    RtlInitUnicodeString( &nameW, volatile_envW );
    if (NtOpenKey( &hkey, KEY_READ, &attr ) == STATUS_SUCCESS)
    {
        set_registry_variables( hkey, REG_SZ );
        set_registry_variables( hkey, REG_EXPAND_SZ );
        NtClose( hkey );
    }

453
    NtClose( attr.RootDirectory );
454
    return ret;
455 456
}

457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500

/***********************************************************************
 *           get_reg_value
 */
static WCHAR *get_reg_value( HKEY hkey, const WCHAR *name )
{
    char buffer[1024 * sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
    KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
    DWORD len, size = sizeof(buffer);
    WCHAR *ret = NULL;
    UNICODE_STRING nameW;

    RtlInitUnicodeString( &nameW, name );
    if (NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, buffer, size, &size ))
        return NULL;

    if (size <= FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data )) return NULL;
    len = (size - FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data )) / sizeof(WCHAR);

    if (info->Type == REG_EXPAND_SZ)
    {
        UNICODE_STRING value, expanded;

        value.MaximumLength = len * sizeof(WCHAR);
        value.Buffer = (WCHAR *)info->Data;
        if (!value.Buffer[len - 1]) len--;  /* don't count terminating null if any */
        value.Length = len * sizeof(WCHAR);
        expanded.Length = expanded.MaximumLength = 1024 * sizeof(WCHAR);
        if (!(expanded.Buffer = HeapAlloc( GetProcessHeap(), 0, expanded.MaximumLength ))) return NULL;
        if (!RtlExpandEnvironmentStrings_U( NULL, &value, &expanded, NULL )) ret = expanded.Buffer;
        else RtlFreeUnicodeString( &expanded );
    }
    else if (info->Type == REG_SZ)
    {
        if ((ret = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) )))
        {
            memcpy( ret, info->Data, len * sizeof(WCHAR) );
            ret[len] = 0;
        }
    }
    return ret;
}


501 502 503 504 505 506 507
/***********************************************************************
 *           set_additional_environment
 *
 * Set some additional environment variables not specified in the registry.
 */
static void set_additional_environment(void)
{
508 509
    static const WCHAR profile_keyW[] = {'\\','R','e','g','i','s','t','r','y','\\',
                                         'M','a','c','h','i','n','e','\\',
510 511 512 513 514 515 516
                                         'S','o','f','t','w','a','r','e','\\',
                                         'M','i','c','r','o','s','o','f','t','\\',
                                         'W','i','n','d','o','w','s',' ','N','T','\\',
                                         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
                                         'P','r','o','f','i','l','e','L','i','s','t',0};
    static const WCHAR profiles_valueW[] = {'P','r','o','f','i','l','e','s','D','i','r','e','c','t','o','r','y',0};
    static const WCHAR all_users_valueW[] = {'A','l','l','U','s','e','r','s','P','r','o','f','i','l','e','\0'};
517
    static const WCHAR computernameW[] = {'C','O','M','P','U','T','E','R','N','A','M','E',0};
518 519 520
    static const WCHAR allusersW[] = {'A','L','L','U','S','E','R','S','P','R','O','F','I','L','E',0};
    OBJECT_ATTRIBUTES attr;
    UNICODE_STRING nameW;
521
    WCHAR *profile_dir = NULL, *all_users_dir = NULL;
522
    WCHAR buf[MAX_COMPUTERNAME_LENGTH+1];
523 524 525
    HANDLE hkey;
    DWORD len;

526 527 528 529 530
    /* ComputerName */
    len = sizeof(buf) / sizeof(WCHAR);
    if (GetComputerNameW( buf, &len ))
        SetEnvironmentVariableW( computernameW, buf );

531
    /* set the ALLUSERSPROFILE variables */
532 533 534 535 536 537 538 539

    attr.Length = sizeof(attr);
    attr.RootDirectory = 0;
    attr.ObjectName = &nameW;
    attr.Attributes = 0;
    attr.SecurityDescriptor = NULL;
    attr.SecurityQualityOfService = NULL;
    RtlInitUnicodeString( &nameW, profile_keyW );
540
    if (!NtOpenKey( &hkey, KEY_READ, &attr ))
541 542 543 544 545 546
    {
        profile_dir = get_reg_value( hkey, profiles_valueW );
        all_users_dir = get_reg_value( hkey, all_users_valueW );
        NtClose( hkey );
    }

547
    if (profile_dir && all_users_dir)
548 549 550
    {
        WCHAR *value, *p;

551
        len = strlenW(profile_dir) + strlenW(all_users_dir) + 2;
552 553 554 555
        value = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
        strcpyW( value, profile_dir );
        p = value + strlenW(value);
        if (p > value && p[-1] != '\\') *p++ = '\\';
556 557
        strcpyW( p, all_users_dir );
        SetEnvironmentVariableW( allusersW, value );
558 559 560 561 562
        HeapFree( GetProcessHeap(), 0, value );
    }

    HeapFree( GetProcessHeap(), 0, all_users_dir );
    HeapFree( GetProcessHeap(), 0, profile_dir );
563
}
564

565 566 567 568 569 570 571 572 573 574
/***********************************************************************
 *           set_wow64_environment
 *
 * Set the environment variables that change across 32/64/Wow64.
 */
static void set_wow64_environment(void)
{
    static const WCHAR archW[]    = {'P','R','O','C','E','S','S','O','R','_','A','R','C','H','I','T','E','C','T','U','R','E',0};
    static const WCHAR arch6432W[] = {'P','R','O','C','E','S','S','O','R','_','A','R','C','H','I','T','E','W','6','4','3','2',0};
    static const WCHAR x86W[] = {'x','8','6',0};
575 576
    static const WCHAR versionW[] = {'\\','R','e','g','i','s','t','r','y','\\',
                                     'M','a','c','h','i','n','e','\\',
577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597
                                     'S','o','f','t','w','a','r','e','\\',
                                     'M','i','c','r','o','s','o','f','t','\\',
                                     'W','i','n','d','o','w','s','\\',
                                     'C','u','r','r','e','n','t','V','e','r','s','i','o','n',0};
    static const WCHAR progdirW[]   = {'P','r','o','g','r','a','m','F','i','l','e','s','D','i','r',0};
    static const WCHAR progdir86W[] = {'P','r','o','g','r','a','m','F','i','l','e','s','D','i','r',' ','(','x','8','6',')',0};
    static const WCHAR progfilesW[] = {'P','r','o','g','r','a','m','F','i','l','e','s',0};
    static const WCHAR progw6432W[] = {'P','r','o','g','r','a','m','W','6','4','3','2',0};
    static const WCHAR commondirW[]   = {'C','o','m','m','o','n','F','i','l','e','s','D','i','r',0};
    static const WCHAR commondir86W[] = {'C','o','m','m','o','n','F','i','l','e','s','D','i','r',' ','(','x','8','6',')',0};
    static const WCHAR commonfilesW[] = {'C','o','m','m','o','n','P','r','o','g','r','a','m','F','i','l','e','s',0};
    static const WCHAR commonw6432W[] = {'C','o','m','m','o','n','P','r','o','g','r','a','m','W','6','4','3','2',0};

    OBJECT_ATTRIBUTES attr;
    UNICODE_STRING nameW;
    WCHAR arch[64];
    WCHAR *value;
    HANDLE hkey;

    /* set the PROCESSOR_ARCHITECTURE variable */

598
    if (GetEnvironmentVariableW( arch6432W, arch, sizeof(arch)/sizeof(WCHAR) ))
599 600 601 602 603 604 605
    {
        if (is_win64)
        {
            SetEnvironmentVariableW( archW, arch );
            SetEnvironmentVariableW( arch6432W, NULL );
        }
    }
606
    else if (GetEnvironmentVariableW( archW, arch, sizeof(arch)/sizeof(WCHAR) ))
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629
    {
        if (is_wow64)
        {
            SetEnvironmentVariableW( arch6432W, arch );
            SetEnvironmentVariableW( archW, x86W );
        }
    }

    attr.Length = sizeof(attr);
    attr.RootDirectory = 0;
    attr.ObjectName = &nameW;
    attr.Attributes = 0;
    attr.SecurityDescriptor = NULL;
    attr.SecurityQualityOfService = NULL;
    RtlInitUnicodeString( &nameW, versionW );
    if (NtOpenKey( &hkey, KEY_READ | KEY_WOW64_64KEY, &attr )) return;

    /* set the ProgramFiles variables */

    if ((value = get_reg_value( hkey, progdirW )))
    {
        if (is_win64 || is_wow64) SetEnvironmentVariableW( progw6432W, value );
        if (is_win64 || !is_wow64) SetEnvironmentVariableW( progfilesW, value );
630
        HeapFree( GetProcessHeap(), 0, value );
631 632
    }
    if (is_wow64 && (value = get_reg_value( hkey, progdir86W )))
633
    {
634
        SetEnvironmentVariableW( progfilesW, value );
635 636
        HeapFree( GetProcessHeap(), 0, value );
    }
637 638 639 640 641 642 643

    /* set the CommonProgramFiles variables */

    if ((value = get_reg_value( hkey, commondirW )))
    {
        if (is_win64 || is_wow64) SetEnvironmentVariableW( commonw6432W, value );
        if (is_win64 || !is_wow64) SetEnvironmentVariableW( commonfilesW, value );
644
        HeapFree( GetProcessHeap(), 0, value );
645 646
    }
    if (is_wow64 && (value = get_reg_value( hkey, commondir86W )))
647
    {
648
        SetEnvironmentVariableW( commonfilesW, value );
649 650
        HeapFree( GetProcessHeap(), 0, value );
    }
651 652 653 654

    NtClose( hkey );
}

655
/***********************************************************************
656
 *              set_library_wargv
657
 *
658
 * Set the Wine library Unicode argv global variables.
659
 */
660
static void set_library_wargv( char **argv )
661 662
{
    int argc;
663
    char *q;
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681
    WCHAR *p;
    WCHAR **wargv;
    DWORD total = 0;

    for (argc = 0; argv[argc]; argc++)
        total += MultiByteToWideChar( CP_UNIXCP, 0, argv[argc], -1, NULL, 0 );

    wargv = RtlAllocateHeap( GetProcessHeap(), 0,
                             total * sizeof(WCHAR) + (argc + 1) * sizeof(*wargv) );
    p = (WCHAR *)(wargv + argc + 1);
    for (argc = 0; argv[argc]; argc++)
    {
        DWORD reslen = MultiByteToWideChar( CP_UNIXCP, 0, argv[argc], -1, p, total );
        wargv[argc] = p;
        p += reslen;
        total -= reslen;
    }
    wargv[argc] = NULL;
682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698

    /* convert argv back from Unicode since it has to be in the Ansi codepage not the Unix one */

    for (argc = 0; wargv[argc]; argc++)
        total += WideCharToMultiByte( CP_ACP, 0, wargv[argc], -1, NULL, 0, NULL, NULL );

    argv = RtlAllocateHeap( GetProcessHeap(), 0, total + (argc + 1) * sizeof(*argv) );
    q = (char *)(argv + argc + 1);
    for (argc = 0; wargv[argc]; argc++)
    {
        DWORD reslen = WideCharToMultiByte( CP_ACP, 0, wargv[argc], -1, q, total, NULL, NULL );
        argv[argc] = q;
        q += reslen;
        total -= reslen;
    }
    argv[argc] = NULL;

699
    __wine_main_argc = argc;
700
    __wine_main_argv = argv;
701 702 703 704
    __wine_main_wargv = wargv;
}


705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728
/***********************************************************************
 *              update_library_argv0
 *
 * Update the argv[0] global variable with the binary we have found.
 */
static void update_library_argv0( const WCHAR *argv0 )
{
    DWORD len = strlenW( argv0 );

    if (len > strlenW( __wine_main_wargv[0] ))
    {
        __wine_main_wargv[0] = RtlAllocateHeap( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) );
    }
    strcpyW( __wine_main_wargv[0], argv0 );

    len = WideCharToMultiByte( CP_ACP, 0, argv0, -1, NULL, 0, NULL, NULL );
    if (len > strlen( __wine_main_argv[0] ) + 1)
    {
        __wine_main_argv[0] = RtlAllocateHeap( GetProcessHeap(), 0, len );
    }
    WideCharToMultiByte( CP_ACP, 0, argv0, -1, __wine_main_argv[0], len, NULL, NULL );
}


729 730 731 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 762
/***********************************************************************
 *           build_command_line
 *
 * Build the command line of a process from the argv array.
 *
 * Note that it does NOT necessarily include the file name.
 * Sometimes we don't even have any command line options at all.
 *
 * We must quote and escape characters so that the argv array can be rebuilt
 * from the command line:
 * - spaces and tabs must be quoted
 *   'a b'   -> '"a b"'
 * - quotes must be escaped
 *   '"'     -> '\"'
 * - if '\'s are followed by a '"', they must be doubled and followed by '\"',
 *   resulting in an odd number of '\' followed by a '"'
 *   '\"'    -> '\\\"'
 *   '\\"'   -> '\\\\\"'
 * - '\'s that are not followed by a '"' can be left as is
 *   'a\b'   == 'a\b'
 *   'a\\b'  == 'a\\b'
 */
static BOOL build_command_line( WCHAR **argv )
{
    int len;
    WCHAR **arg;
    LPWSTR p;
    RTL_USER_PROCESS_PARAMETERS* rupp = NtCurrentTeb()->Peb->ProcessParameters;

    if (rupp->CommandLine.Buffer) return TRUE; /* already got it from the server */

    len = 0;
    for (arg = argv; *arg; arg++)
    {
763 764
        BOOL has_space;
        int bcount;
765 766
        WCHAR* a;

767
        has_space=FALSE;
768 769
        bcount=0;
        a=*arg;
770
        if( !*a ) has_space=TRUE;
771 772 773 774 775
        while (*a!='\0') {
            if (*a=='\\') {
                bcount++;
            } else {
                if (*a==' ' || *a=='\t') {
776
                    has_space=TRUE;
777
                } else if (*a=='"') {
778
                    /* doubling of '\' preceding a '"',
779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799
                     * plus escaping of said '"'
                     */
                    len+=2*bcount+1;
                }
                bcount=0;
            }
            a++;
        }
        len+=(a-*arg)+1 /* for the separating space */;
        if (has_space)
            len+=2; /* for the quotes */
    }

    if (!(rupp->CommandLine.Buffer = RtlAllocateHeap( GetProcessHeap(), 0, len * sizeof(WCHAR))))
        return FALSE;

    p = rupp->CommandLine.Buffer;
    rupp->CommandLine.Length = (len - 1) * sizeof(WCHAR);
    rupp->CommandLine.MaximumLength = len * sizeof(WCHAR);
    for (arg = argv; *arg; arg++)
    {
800
        BOOL has_space,has_quote;
801 802 803
        WCHAR* a;

        /* Check for quotes and spaces in this argument */
804
        has_space=has_quote=FALSE;
805
        a=*arg;
806
        if( !*a ) has_space=TRUE;
807 808
        while (*a!='\0') {
            if (*a==' ' || *a=='\t') {
809
                has_space=TRUE;
810 811 812
                if (has_quote)
                    break;
            } else if (*a=='"') {
813
                has_quote=TRUE;
814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835
                if (has_space)
                    break;
            }
            a++;
        }

        /* Now transfer it to the command line */
        if (has_space)
            *p++='"';
        if (has_quote) {
            int bcount;

            bcount=0;
            a=*arg;
            while (*a!='\0') {
                if (*a=='\\') {
                    *p++=*a;
                    bcount++;
                } else {
                    if (*a=='"') {
                        int i;

836
                        /* Double all the '\\' preceding this '"', plus one */
837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862
                        for (i=0;i<=bcount;i++)
                            *p++='\\';
                        *p++='"';
                    } else {
                        *p++=*a;
                    }
                    bcount=0;
                }
                a++;
            }
        } else {
            WCHAR* x = *arg;
            while ((*p=*x++)) p++;
        }
        if (has_space)
            *p++='"';
        *p++=' ';
    }
    if (p > rupp->CommandLine.Buffer)
        p--;  /* remove last space */
    *p = '\0';

    return TRUE;
}


863 864 865 866 867 868 869 870
/***********************************************************************
 *           init_current_directory
 *
 * Initialize the current directory from the Unix cwd or the parent info.
 */
static void init_current_directory( CURDIR *cur_dir )
{
    UNICODE_STRING dir_str;
871
    const char *pwd;
872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893
    char *cwd;
    int size;

    /* if we received a cur dir from the parent, try this first */

    if (cur_dir->DosPath.Length)
    {
        if (RtlSetCurrentDirectory_U( &cur_dir->DosPath ) == STATUS_SUCCESS) goto done;
    }

    /* now try to get it from the Unix cwd */

    for (size = 256; ; size *= 2)
    {
        if (!(cwd = HeapAlloc( GetProcessHeap(), 0, size ))) break;
        if (getcwd( cwd, size )) break;
        HeapFree( GetProcessHeap(), 0, cwd );
        if (errno == ERANGE) continue;
        cwd = NULL;
        break;
    }

894 895 896
    /* try to use PWD if it is valid, so that we don't resolve symlinks */

    pwd = getenv( "PWD" );
897
    if (cwd)
898 899 900 901 902 903 904 905 906
    {
        struct stat st1, st2;

        if (!pwd || stat( pwd, &st1 ) == -1 ||
            (!stat( cwd, &st2 ) && (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino)))
            pwd = cwd;
    }

    if (pwd)
907
    {
908 909 910 911
        ANSI_STRING unix_name;
        UNICODE_STRING nt_name;
        RtlInitAnsiString( &unix_name, pwd );
        if (!wine_unix_to_nt_file_name( &unix_name, &nt_name ))
912
        {
913 914 915 916 917
            UNICODE_STRING dos_path;
            /* skip the \??\ prefix, nt_name is 0 terminated */
            RtlInitUnicodeString( &dos_path, nt_name.Buffer + 4 );
            RtlSetCurrentDirectory_U( &dos_path );
            RtlFreeUnicodeString( &nt_name );
918 919 920 921 922 923 924 925 926 927
        }
    }

    if (!cur_dir->DosPath.Length)  /* still not initialized */
    {
        MESSAGE("Warning: could not find DOS drive for current working directory '%s', "
                "starting in the Windows directory.\n", cwd ? cwd : "" );
        RtlInitUnicodeString( &dir_str, DIR_Windows );
        RtlSetCurrentDirectory_U( &dir_str );
    }
928
    HeapFree( GetProcessHeap(), 0, cwd );
929 930 931 932 933 934

done:
    TRACE( "starting in %s %p\n", debugstr_w( cur_dir->DosPath.Buffer ), cur_dir->Handle );
}


935 936 937 938 939 940 941
/***********************************************************************
 *           init_windows_dirs
 *
 * Initialize the windows and system directories from the environment.
 */
static void init_windows_dirs(void)
{
942
    extern void CDECL __wine_init_windows_dir( const WCHAR *windir, const WCHAR *sysdir );
943

944 945
    static const WCHAR windirW[] = {'w','i','n','d','i','r',0};
    static const WCHAR winsysdirW[] = {'w','i','n','s','y','s','d','i','r',0};
946
    static const WCHAR default_windirW[] = {'C',':','\\','w','i','n','d','o','w','s',0};
947
    static const WCHAR default_sysdirW[] = {'\\','s','y','s','t','e','m','3','2',0};
948
    static const WCHAR default_syswow64W[] = {'\\','s','y','s','w','o','w','6','4',0};
949 950 951 952 953 954 955 956 957 958

    DWORD len;
    WCHAR *buffer;

    if ((len = GetEnvironmentVariableW( windirW, NULL, 0 )))
    {
        buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
        GetEnvironmentVariableW( windirW, buffer, len );
        DIR_Windows = buffer;
    }
959
    else DIR_Windows = default_windirW;
960 961 962 963 964 965 966 967 968

    if ((len = GetEnvironmentVariableW( winsysdirW, NULL, 0 )))
    {
        buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
        GetEnvironmentVariableW( winsysdirW, buffer, len );
        DIR_System = buffer;
    }
    else
    {
969 970 971 972 973
        len = strlenW( DIR_Windows );
        buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) + sizeof(default_sysdirW) );
        memcpy( buffer, DIR_Windows, len * sizeof(WCHAR) );
        memcpy( buffer + len, default_sysdirW, sizeof(default_sysdirW) );
        DIR_System = buffer;
974 975
    }

976 977 978 979 980 981 982
    if (!CreateDirectoryW( DIR_Windows, NULL ) && GetLastError() != ERROR_ALREADY_EXISTS)
        ERR( "directory %s could not be created, error %u\n",
             debugstr_w(DIR_Windows), GetLastError() );
    if (!CreateDirectoryW( DIR_System, NULL ) && GetLastError() != ERROR_ALREADY_EXISTS)
        ERR( "directory %s could not be created, error %u\n",
             debugstr_w(DIR_System), GetLastError() );

983
    if (is_win64 || is_wow64)   /* SysWow64 is always defined on 64-bit */
984 985 986 987 988 989
    {
        len = strlenW( DIR_Windows );
        buffer = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) + sizeof(default_syswow64W) );
        memcpy( buffer, DIR_Windows, len * sizeof(WCHAR) );
        memcpy( buffer + len, default_syswow64W, sizeof(default_syswow64W) );
        DIR_SysWow64 = buffer;
990 991 992
        if (!CreateDirectoryW( DIR_SysWow64, NULL ) && GetLastError() != ERROR_ALREADY_EXISTS)
            ERR( "directory %s could not be created, error %u\n",
                 debugstr_w(DIR_SysWow64), GetLastError() );
993 994
    }

995 996
    TRACE_(file)( "WindowsDir = %s\n", debugstr_w(DIR_Windows) );
    TRACE_(file)( "SystemDir  = %s\n", debugstr_w(DIR_System) );
997 998 999

    /* set the directories in ntdll too */
    __wine_init_windows_dir( DIR_Windows, DIR_System );
1000 1001 1002
}


1003 1004 1005
/***********************************************************************
 *           start_wineboot
 *
1006
 * Start the wineboot process if necessary. Return the handles to wait on.
1007
 */
1008
static void start_wineboot( HANDLE handles[2] )
1009 1010 1011
{
    static const WCHAR wineboot_eventW[] = {'_','_','w','i','n','e','b','o','o','t','_','e','v','e','n','t',0};

1012 1013
    handles[1] = 0;
    if (!(handles[0] = CreateEventW( NULL, TRUE, FALSE, wineboot_eventW )))
1014 1015
    {
        ERR( "failed to create wineboot event, expect trouble\n" );
1016
        return;
1017 1018 1019
    }
    if (GetLastError() != ERROR_ALREADY_EXISTS)  /* we created it */
    {
1020 1021
        static const WCHAR wineboot[] = {'\\','w','i','n','e','b','o','o','t','.','e','x','e',0};
        static const WCHAR args[] = {' ','-','-','i','n','i','t',0};
1022 1023
        STARTUPINFOW si;
        PROCESS_INFORMATION pi;
1024
        void *redir;
1025
        WCHAR app[MAX_PATH];
1026
        WCHAR cmdline[MAX_PATH + (sizeof(wineboot) + sizeof(args)) / sizeof(WCHAR)];
1027 1028 1029

        memset( &si, 0, sizeof(si) );
        si.cb = sizeof(si);
1030 1031 1032 1033 1034
        si.dwFlags = STARTF_USESTDHANDLES;
        si.hStdInput  = 0;
        si.hStdOutput = 0;
        si.hStdError  = GetStdHandle( STD_ERROR_HANDLE );

1035 1036
        GetSystemDirectoryW( app, MAX_PATH - sizeof(wineboot)/sizeof(WCHAR) );
        lstrcatW( app, wineboot );
1037 1038

        Wow64DisableWow64FsRedirection( &redir );
1039 1040 1041
        strcpyW( cmdline, app );
        strcatW( cmdline, args );
        if (CreateProcessW( app, cmdline, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi ))
1042 1043 1044
        {
            TRACE( "started wineboot pid %04x tid %04x\n", pi.dwProcessId, pi.dwThreadId );
            CloseHandle( pi.hThread );
1045 1046 1047 1048 1049 1050 1051
            handles[1] = pi.hProcess;
        }
        else
        {
            ERR( "failed to start wineboot, err %u\n", GetLastError() );
            CloseHandle( handles[0] );
            handles[0] = 0;
1052
        }
1053
        Wow64RevertWow64FsRedirection( redir );
1054 1055 1056 1057
    }
}


1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079
#ifdef __i386__
extern DWORD call_process_entry( PEB *peb, LPTHREAD_START_ROUTINE entry );
__ASM_GLOBAL_FUNC( call_process_entry,
                    "pushl %ebp\n\t"
                    __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
                    __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
                    "movl %esp,%ebp\n\t"
                    __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
                    "subl $12,%esp\n\t"  /* deliberately mis-align the stack by 8, Doom 3 needs this */
                    "pushl 8(%ebp)\n\t"
                    "call *12(%ebp)\n\t"
                    "leave\n\t"
                    __ASM_CFI(".cfi_def_cfa %esp,4\n\t")
                    __ASM_CFI(".cfi_same_value %ebp\n\t")
                    "ret" )
#else
static inline DWORD call_process_entry( PEB *peb, LPTHREAD_START_ROUTINE entry )
{
    return entry( peb );
}
#endif

1080 1081 1082 1083 1084
/***********************************************************************
 *           start_process
 *
 * Startup routine of a new process. Runs on the new process stack.
 */
1085
static DWORD WINAPI start_process( PEB *peb )
1086
{
1087 1088
    IMAGE_NT_HEADERS *nt;
    LPTHREAD_START_ROUTINE entry;
1089

1090 1091 1092
    nt = RtlImageNtHeader( peb->ImageBaseAddress );
    entry = (LPTHREAD_START_ROUTINE)((char *)peb->ImageBaseAddress +
                                     nt->OptionalHeader.AddressOfEntryPoint);
1093

1094
    if (!nt->OptionalHeader.AddressOfEntryPoint)
1095
    {
1096 1097 1098
        ERR( "%s doesn't have an entry point, it cannot be executed\n",
             debugstr_w(peb->ProcessParameters->ImagePathName.Buffer) );
        ExitThread( 1 );
1099
    }
1100 1101 1102 1103 1104 1105 1106

    if (TRACE_ON(relay))
        DPRINTF( "%04x:Starting process %s (entryproc=%p)\n", GetCurrentThreadId(),
                 debugstr_w(peb->ProcessParameters->ImagePathName.Buffer), entry );

    SetLastError( 0 );  /* clear error code */
    if (peb->BeingDebugged) DbgBreakPoint();
1107
    return call_process_entry( peb, entry );
1108 1109 1110
}


1111 1112 1113 1114 1115
/***********************************************************************
 *           set_process_name
 *
 * Change the process name in the ps output.
 */
1116
static void set_process_name( int argc, char *argv[] )
1117
{
1118 1119 1120 1121
#ifdef HAVE_SETPROCTITLE
    setproctitle("-%s", argv[1]);
#endif

1122 1123
#ifdef HAVE_PRCTL
    int i, offset;
1124 1125
    char *p, *prctl_name = argv[1];
    char *end = argv[argc-1] + strlen(argv[argc-1]) + 1;
1126 1127 1128 1129 1130

#ifndef PR_SET_NAME
# define PR_SET_NAME 15
#endif

1131 1132
    if ((p = strrchr( prctl_name, '\\' ))) prctl_name = p + 1;
    if ((p = strrchr( prctl_name, '/' ))) prctl_name = p + 1;
1133

1134
    if (prctl( PR_SET_NAME, prctl_name ) != -1)
1135
    {
1136 1137 1138 1139 1140
        offset = argv[1] - argv[0];
        memmove( argv[1] - offset, argv[1], end - argv[1] );
        memset( end - offset, 0, offset );
        for (i = 1; i < argc; i++) argv[i-1] = argv[i] - offset;
        argv[i-1] = NULL;
1141 1142
    }
    else
1143
#endif  /* HAVE_PRCTL */
1144
    {
1145 1146
        /* remove argv[0] */
        memmove( argv, argv + 1, argc * sizeof(argv[0]) );
1147 1148 1149 1150
    }
}


1151
/***********************************************************************
1152
 *           __wine_kernel_init
1153 1154 1155
 *
 * Wine initialisation: load and start the main exe file.
 */
1156
void CDECL __wine_kernel_init(void)
1157
{
1158
    static const WCHAR kernel32W[] = {'k','e','r','n','e','l','3','2',0};
1159 1160
    static const WCHAR dotW[] = {'.',0};

1161
    WCHAR *p, main_exe_name[MAX_PATH+1];
1162
    PEB *peb = NtCurrentTeb()->Peb;
1163
    RTL_USER_PROCESS_PARAMETERS *params = peb->ProcessParameters;
1164
    HANDLE boot_events[2];
1165
    BOOL got_environment = TRUE;
1166 1167

    /* Initialize everything */
1168 1169 1170 1171

    setbuf(stdout,NULL);
    setbuf(stderr,NULL);
    kernel32_handle = GetModuleHandleW(kernel32W);
1172
    IsWow64Process( GetCurrentProcess(), &is_wow64 );
1173 1174 1175 1176 1177 1178

    LOCALE_Init();

    if (!params->Environment)
    {
        /* Copy the parent environment */
1179
        if (!build_initial_environment()) exit(1);
1180 1181 1182 1183

        /* convert old configuration to new format */
        convert_old_config();

1184
        got_environment = set_registry_environment( FALSE );
1185 1186 1187 1188 1189 1190
        set_additional_environment();
    }

    init_windows_dirs();
    init_current_directory( &params->CurrentDirectory );

1191
    set_process_name( __wine_main_argc, __wine_main_argv );
1192
    set_library_wargv( __wine_main_argv );
1193
    boot_events[0] = boot_events[1] = 0;
1194

1195 1196 1197 1198 1199
    if (peb->ProcessParameters->ImagePathName.Buffer)
    {
        strcpyW( main_exe_name, peb->ProcessParameters->ImagePathName.Buffer );
    }
    else
1200
    {
1201 1202
        struct binary_info binary_info;

1203
        if (!SearchPathW( NULL, __wine_main_wargv[0], exeW, MAX_PATH, main_exe_name, NULL ) &&
1204
            !get_builtin_path( __wine_main_wargv[0], exeW, main_exe_name, MAX_PATH, &binary_info ))
1205
        {
1206
            MESSAGE( "wine: cannot find '%s'\n", __wine_main_argv[0] );
1207
            ExitProcess( GetLastError() );
1208
        }
1209
        update_library_argv0( main_exe_name );
1210
        if (!build_command_line( __wine_main_wargv )) goto error;
1211
        start_wineboot( boot_events );
1212 1213
    }

1214 1215 1216 1217
    /* if there's no extension, append a dot to prevent LoadLibrary from appending .dll */
    p = strrchrW( main_exe_name, '.' );
    if (!p || strchrW( p, '/' ) || strchrW( p, '\\' )) strcatW( main_exe_name, dotW );

1218
    TRACE( "starting process name=%s argv[0]=%s\n",
1219
           debugstr_w(main_exe_name), debugstr_w(__wine_main_wargv[0]) );
1220

1221
    RtlInitUnicodeString( &NtCurrentTeb()->Peb->ProcessParameters->DllPath,
1222
                          MODULE_get_dll_load_path(main_exe_name) );
1223

1224
    if (boot_events[0])
1225
    {
1226
        DWORD timeout = 2 * 60 * 1000, count = 1;
1227

1228
        if (boot_events[1]) count++;
1229
        if (!got_environment) timeout = 5 * 60 * 1000;  /* initial prefix creation can take longer */
1230
        if (WaitForMultipleObjects( count, boot_events, FALSE, timeout ) == WAIT_TIMEOUT)
1231 1232 1233
            ERR( "boot event wait timed out\n" );
        CloseHandle( boot_events[0] );
        if (boot_events[1]) CloseHandle( boot_events[1] );
1234 1235 1236
        /* reload environment now that wineboot has run */
        set_registry_environment( got_environment );
        set_additional_environment();
1237
    }
1238
    set_wow64_environment();
1239

1240
    if (!(peb->ImageBaseAddress = LoadLibraryExW( main_exe_name, 0, DONT_RESOLVE_DLL_REFERENCES )))
1241
    {
1242 1243
        DWORD_PTR args[1];
        WCHAR msgW[1024];
1244 1245 1246 1247
        char msg[1024];
        DWORD error = GetLastError();

        /* if Win16/DOS format, or unavailable address, exec a new process with the proper setup */
1248 1249 1250
        if (error == ERROR_BAD_EXE_FORMAT ||
            error == ERROR_INVALID_ADDRESS ||
            error == ERROR_NOT_ENOUGH_MEMORY)
1251
        {
1252 1253
            if (!getenv("WINEPRELOADRESERVE")) exec_process( main_exe_name );
            /* if we get back here, it failed */
1254
        }
1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265
        else if (error == ERROR_MOD_NOT_FOUND)
        {
            if ((p = strrchrW( main_exe_name, '\\' ))) p++;
            else p = main_exe_name;
            if (!strcmpiW( p, winevdmW ) && __wine_main_argc > 3)
            {
                /* args 1 and 2 are --app-name full_path */
                MESSAGE( "wine: could not run %s: 16-bit/DOS support missing\n",
                         debugstr_w(__wine_main_wargv[3]) );
                ExitProcess( ERROR_BAD_EXE_FORMAT );
            }
1266 1267
            MESSAGE( "wine: cannot find %s\n", debugstr_w(main_exe_name) );
            ExitProcess( ERROR_FILE_NOT_FOUND );
1268
        }
1269 1270 1271
        args[0] = (DWORD_PTR)main_exe_name;
        FormatMessageW( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY,
                        NULL, error, 0, msgW, sizeof(msgW)/sizeof(WCHAR), (__ms_va_list *)args );
1272
        WideCharToMultiByte( CP_UNIXCP, 0, msgW, -1, msg, sizeof(msg), NULL, NULL );
1273
        MESSAGE( "wine: %s", msg );
1274
        ExitProcess( error );
1275 1276
    }

1277 1278
    if (!params->CurrentDirectory.Handle) chdir("/"); /* avoid locking removable devices */

1279
    LdrInitializeThunk( start_process, 0, 0, 0 );
1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291

 error:
    ExitProcess( GetLastError() );
}


/***********************************************************************
 *           build_argv
 *
 * Build an argv array from a command-line.
 * 'reserved' is the number of args to reserve before the first one.
 */
1292
static char **build_argv( const WCHAR *cmdlineW, int reserved )
1293 1294 1295
{
    int argc;
    char** argv;
1296 1297 1298 1299
    char *arg,*s,*d,*cmdline;
    int in_quotes,bcount,len;

    len = WideCharToMultiByte( CP_UNIXCP, 0, cmdlineW, -1, NULL, 0, NULL, NULL );
1300
    if (!(cmdline = HeapAlloc( GetProcessHeap(), 0, len ))) return NULL;
1301
    WideCharToMultiByte( CP_UNIXCP, 0, cmdlineW, -1, cmdline, len, NULL, NULL );
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

    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;
        }
        s++;
    }
1332 1333 1334
    if (!(argv = HeapAlloc( GetProcessHeap(), 0, argc*sizeof(*argv) + len )))
    {
        HeapFree( GetProcessHeap(), 0, cmdline );
1335
        return NULL;
1336
    }
1337

1338 1339
    arg = d = s = (char *)(argv + argc);
    memcpy( d, cmdline, len );
1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363
    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) {
1364
                /* Preceded by an even number of '\', this is half that
1365 1366 1367 1368 1369 1370
                 * number of '\', plus a '"' which we discard.
                 */
                d-=bcount/2;
                s++;
                in_quotes=!in_quotes;
            } else {
1371
                /* Preceded by an odd number of '\', this is half that
1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390
                 * number of '\' followed by a '"'
                 */
                d=d-bcount/2-1;
                *d++='"';
                s++;
            }
            bcount=0;
        } else {
            /* a regular character */
            *d++=*s++;
            bcount=0;
        }
    }
    if (*arg) {
        *d='\0';
        argv[argc++]=arg;
    }
    argv[argc]=NULL;

1391
    HeapFree( GetProcessHeap(), 0, cmdline );
1392 1393 1394 1395 1396 1397 1398 1399 1400
    return argv;
}


/***********************************************************************
 *           build_envp
 *
 * Build the environment of a new child process.
 */
1401
static char **build_envp( const WCHAR *envW )
1402
{
1403 1404
    static const char * const unix_vars[] = { "PATH", "TEMP", "TMP", "HOME" };

1405
    const WCHAR *end;
1406
    char **envp;
1407
    char *env, *p;
1408
    int count = 1, length;
1409
    unsigned int i;
1410

1411 1412 1413
    for (end = envW; *end; count++) end += strlenW(end) + 1;
    end++;
    length = WideCharToMultiByte( CP_UNIXCP, 0, envW, end - envW, NULL, 0, NULL, NULL );
1414
    if (!(env = HeapAlloc( GetProcessHeap(), 0, length ))) return NULL;
1415
    WideCharToMultiByte( CP_UNIXCP, 0, envW, end - envW, env, length, NULL, NULL );
1416

1417 1418
    for (p = env; *p; p += strlen(p) + 1)
        if (is_special_env_var( p )) length += 4; /* prefix it with "WINE" */
1419

1420 1421 1422 1423 1424 1425 1426 1427
    for (i = 0; i < sizeof(unix_vars)/sizeof(unix_vars[0]); i++)
    {
        if (!(p = getenv(unix_vars[i]))) continue;
        length += strlen(unix_vars[i]) + strlen(p) + 2;
        count++;
    }

    if ((envp = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*envp) + length )))
1428 1429
    {
        char **envptr = envp;
1430
        char *dst = (char *)(envp + count);
1431

1432
        /* some variables must not be modified, so we get them directly from the unix env */
1433 1434 1435 1436 1437 1438 1439 1440 1441
        for (i = 0; i < sizeof(unix_vars)/sizeof(unix_vars[0]); i++)
        {
            if (!(p = getenv(unix_vars[i]))) continue;
            *envptr++ = strcpy( dst, unix_vars[i] );
            strcat( dst, "=" );
            strcat( dst, p );
            dst += strlen(dst) + 1;
        }

1442 1443 1444
        /* now put the Windows environment strings */
        for (p = env; *p; p += strlen(p) + 1)
        {
1445 1446
            if (*p == '=') continue;  /* skip drive curdirs, this crashes some unix apps */
            if (!strncmp( p, "WINEPRELOADRESERVE=", sizeof("WINEPRELOADRESERVE=")-1 )) continue;
1447
            if (!strncmp( p, "WINELOADERNOEXEC=", sizeof("WINELOADERNOEXEC=")-1 )) continue;
1448
            if (!strncmp( p, "WINESERVERSOCKET=", sizeof("WINESERVERSOCKET=")-1 )) continue;
1449
            if (is_special_env_var( p ))  /* prefix it with "WINE" */
1450 1451 1452 1453
            {
                *envptr++ = strcpy( dst, "WINE" );
                strcat( dst, p );
            }
1454
            else
1455 1456 1457 1458
            {
                *envptr++ = strcpy( dst, p );
            }
            dst += strlen(dst) + 1;
1459 1460 1461
        }
        *envptr = 0;
    }
1462
    HeapFree( GetProcessHeap(), 0, env );
1463 1464 1465 1466 1467 1468 1469 1470 1471
    return envp;
}


/***********************************************************************
 *           fork_and_exec
 *
 * Fork and exec a new Unix binary, checking for errors.
 */
1472 1473
static int fork_and_exec( const char *filename, const WCHAR *cmdline, const WCHAR *env,
                          const char *newdir, DWORD flags, STARTUPINFOW *startup )
1474
{
1475
    int fd[2], stdin_fd = -1, stdout_fd = -1, stderr_fd = -1;
1476
    int pid, err;
1477
    char **argv, **envp;
1478

1479
    if (!env) env = GetEnvironmentStringsW();
1480

1481 1482 1483
#ifdef HAVE_PIPE2
    if (pipe2( fd, O_CLOEXEC ) == -1)
#endif
1484
    {
1485 1486 1487 1488 1489 1490 1491
        if (pipe(fd) == -1)
        {
            SetLastError( ERROR_TOO_MANY_OPEN_FILES );
            return -1;
        }
        fcntl( fd[0], F_SETFD, FD_CLOEXEC );
        fcntl( fd[1], F_SETFD, FD_CLOEXEC );
1492
    }
1493 1494 1495

    if (!(flags & (CREATE_NEW_PROCESS_GROUP | CREATE_NEW_CONSOLE | DETACHED_PROCESS)))
    {
1496
        HANDLE hstdin, hstdout, hstderr;
1497 1498 1499 1500 1501

        if (startup->dwFlags & STARTF_USESTDHANDLES)
        {
            hstdin = startup->hStdInput;
            hstdout = startup->hStdOutput;
1502
            hstderr = startup->hStdError;
1503 1504 1505 1506 1507
        }
        else
        {
            hstdin = GetStdHandle(STD_INPUT_HANDLE);
            hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
1508
            hstderr = GetStdHandle(STD_ERROR_HANDLE);
1509 1510
        }

1511 1512 1513 1514
        if (is_console_handle( hstdin ))
            hstdin = wine_server_ptr_handle( console_handle_unmap( hstdin ));
        if (is_console_handle( hstdout ))
            hstdout = wine_server_ptr_handle( console_handle_unmap( hstdout ));
1515 1516
        if (is_console_handle( hstderr ))
            hstderr = wine_server_ptr_handle( console_handle_unmap( hstderr ));
1517 1518
        wine_server_handle_to_fd( hstdin, FILE_READ_DATA, &stdin_fd, NULL );
        wine_server_handle_to_fd( hstdout, FILE_WRITE_DATA, &stdout_fd, NULL );
1519
        wine_server_handle_to_fd( hstderr, FILE_WRITE_DATA, &stderr_fd, NULL );
1520 1521
    }

1522
    argv = build_argv( cmdline, 0 );
1523
    envp = build_envp( env );
1524

1525 1526
    if (!(pid = fork()))  /* child */
    {
1527
        if (!(pid = fork()))  /* grandchild */
1528
        {
1529 1530 1531
            close( fd[0] );

            if (flags & (CREATE_NEW_PROCESS_GROUP | CREATE_NEW_CONSOLE | DETACHED_PROCESS))
1532
            {
1533
                int nullfd = open( "/dev/null", O_RDWR );
1534 1535
                setsid();
                /* close stdin and stdout */
1536
                if (nullfd != -1)
1537
                {
1538 1539 1540
                    dup2( nullfd, 0 );
                    dup2( nullfd, 1 );
                    close( nullfd );
1541 1542
                }
            }
1543
            else
1544
            {
1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559
                if (stdin_fd != -1)
                {
                    dup2( stdin_fd, 0 );
                    close( stdin_fd );
                }
                if (stdout_fd != -1)
                {
                    dup2( stdout_fd, 1 );
                    close( stdout_fd );
                }
                if (stderr_fd != -1)
                {
                    dup2( stderr_fd, 2 );
                    close( stderr_fd );
                }
1560
            }
1561

1562 1563
            /* Reset signals that we previously set to SIG_IGN */
            signal( SIGPIPE, SIG_DFL );
1564

1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575
            if (newdir) chdir(newdir);

            if (argv && envp) execve( filename, argv, envp );
        }

        if (pid <= 0)  /* grandchild if exec failed or child if fork failed */
        {
            err = errno;
            write( fd[1], &err, sizeof(err) );
            _exit(1);
        }
1576

1577
        _exit(0); /* child if fork succeeded */
1578
    }
1579
    HeapFree( GetProcessHeap(), 0, argv );
1580
    HeapFree( GetProcessHeap(), 0, envp );
1581 1582
    if (stdin_fd != -1) close( stdin_fd );
    if (stdout_fd != -1) close( stdout_fd );
1583
    if (stderr_fd != -1) close( stderr_fd );
1584
    close( fd[1] );
1585
    if (pid != -1)
1586
    {
1587 1588 1589 1590 1591 1592 1593 1594 1595 1596
        /* reap child */
        do {
            err = waitpid(pid, NULL, 0);
        } while (err < 0 && errno == EINTR);

        if (read( fd[0], &err, sizeof(err) ) > 0)  /* exec or second fork failed */
        {
            errno = err;
            pid = -1;
        }
1597 1598 1599 1600 1601 1602 1603
    }
    if (pid == -1) FILE_SetDosError();
    close( fd[0] );
    return pid;
}


1604 1605 1606 1607 1608 1609 1610 1611
static inline DWORD append_string( void **ptr, const WCHAR *str )
{
    DWORD len = strlenW( str );
    memcpy( *ptr, str, len * sizeof(WCHAR) );
    *ptr = (WCHAR *)*ptr + len;
    return len * sizeof(WCHAR);
}

1612
/***********************************************************************
1613
 *           create_startup_info
1614
 */
1615 1616 1617
static startup_info_t *create_startup_info( LPCWSTR filename, LPCWSTR cmdline,
                                            LPCWSTR cur_dir, LPWSTR env, DWORD flags,
                                            const STARTUPINFOW *startup, DWORD *info_size )
1618
{
1619
    const RTL_USER_PROCESS_PARAMETERS *cur_params;
1620
    const WCHAR *title;
1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631
    startup_info_t *info;
    DWORD size;
    void *ptr;
    UNICODE_STRING newdir;
    WCHAR imagepath[MAX_PATH];
    HANDLE hstdin, hstdout, hstderr;

    if(!GetLongPathNameW( filename, imagepath, MAX_PATH ))
        lstrcpynW( imagepath, filename, MAX_PATH );
    if(!GetFullPathNameW( imagepath, MAX_PATH, imagepath, NULL ))
        lstrcpynW( imagepath, filename, MAX_PATH );
1632

1633
    cur_params = NtCurrentTeb()->Peb->ProcessParameters;
1634

1635 1636 1637 1638
    newdir.Buffer = NULL;
    if (cur_dir)
    {
        if (RtlDosPathNameToNtPathName_U( cur_dir, &newdir, NULL, NULL ))
1639 1640 1641
            cur_dir = newdir.Buffer + 4;  /* skip \??\ prefix */
        else
            cur_dir = NULL;
1642
    }
1643
    if (!cur_dir)
1644
    {
1645 1646 1647 1648
        if (NtCurrentTeb()->Tib.SubSystemTib)  /* FIXME: hack */
            cur_dir = ((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir.DosPath.Buffer;
        else
            cur_dir = cur_params->CurrentDirectory.DosPath.Buffer;
1649
    }
1650
    title = startup->lpTitle ? startup->lpTitle : imagepath;
1651

1652 1653 1654 1655 1656
    size = sizeof(*info);
    size += strlenW( cur_dir ) * sizeof(WCHAR);
    size += cur_params->DllPath.Length;
    size += strlenW( imagepath ) * sizeof(WCHAR);
    size += strlenW( cmdline ) * sizeof(WCHAR);
1657
    size += strlenW( title ) * sizeof(WCHAR);
1658 1659 1660 1661 1662 1663 1664 1665 1666 1667
    if (startup->lpDesktop) size += strlenW( startup->lpDesktop ) * sizeof(WCHAR);
    /* FIXME: shellinfo */
    if (startup->lpReserved2 && startup->cbReserved2) size += startup->cbReserved2;
    size = (size + 1) & ~1;
    *info_size = size;

    if (!(info = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ))) goto done;

    info->console_flags = cur_params->ConsoleFlags;
    if (flags & CREATE_NEW_PROCESS_GROUP) info->console_flags = 1;
1668
    if (flags & CREATE_NEW_CONSOLE) info->console = wine_server_obj_handle(KERNEL32_CONSOLE_ALLOC);
1669

1670 1671
    if (startup->dwFlags & STARTF_USESTDHANDLES)
    {
1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690
        hstdin  = startup->hStdInput;
        hstdout = startup->hStdOutput;
        hstderr = startup->hStdError;
    }
    else
    {
        hstdin  = GetStdHandle( STD_INPUT_HANDLE );
        hstdout = GetStdHandle( STD_OUTPUT_HANDLE );
        hstderr = GetStdHandle( STD_ERROR_HANDLE );
    }
    info->hstdin  = wine_server_obj_handle( hstdin );
    info->hstdout = wine_server_obj_handle( hstdout );
    info->hstderr = wine_server_obj_handle( hstderr );
    if ((flags & (CREATE_NEW_CONSOLE | DETACHED_PROCESS)) != 0)
    {
        /* this is temporary (for console handles). We have no way to control that the handle is invalid in child process otherwise */
        if (is_console_handle(hstdin))  info->hstdin  = wine_server_obj_handle( INVALID_HANDLE_VALUE );
        if (is_console_handle(hstdout)) info->hstdout = wine_server_obj_handle( INVALID_HANDLE_VALUE );
        if (is_console_handle(hstderr)) info->hstderr = wine_server_obj_handle( INVALID_HANDLE_VALUE );
1691 1692 1693
    }
    else
    {
1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715
        if (is_console_handle(hstdin))  info->hstdin  = console_handle_unmap(hstdin);
        if (is_console_handle(hstdout)) info->hstdout = console_handle_unmap(hstdout);
        if (is_console_handle(hstderr)) info->hstderr = console_handle_unmap(hstderr);
    }

    info->x         = startup->dwX;
    info->y         = startup->dwY;
    info->xsize     = startup->dwXSize;
    info->ysize     = startup->dwYSize;
    info->xchars    = startup->dwXCountChars;
    info->ychars    = startup->dwYCountChars;
    info->attribute = startup->dwFillAttribute;
    info->flags     = startup->dwFlags;
    info->show      = startup->wShowWindow;

    ptr = info + 1;
    info->curdir_len = append_string( &ptr, cur_dir );
    info->dllpath_len = cur_params->DllPath.Length;
    memcpy( ptr, cur_params->DllPath.Buffer, cur_params->DllPath.Length );
    ptr = (char *)ptr + cur_params->DllPath.Length;
    info->imagepath_len = append_string( &ptr, imagepath );
    info->cmdline_len = append_string( &ptr, cmdline );
1716
    info->title_len = append_string( &ptr, title );
1717 1718 1719 1720 1721 1722 1723 1724 1725 1726
    if (startup->lpDesktop) info->desktop_len = append_string( &ptr, startup->lpDesktop );
    if (startup->lpReserved2 && startup->cbReserved2)
    {
        info->runtime_len = startup->cbReserved2;
        memcpy( ptr, startup->lpReserved2, startup->cbReserved2 );
    }

done:
    RtlFreeUnicodeString( &newdir );
    return info;
1727 1728
}

1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741
/***********************************************************************
 *           get_alternate_loader
 *
 * Get the name of the alternate (32 or 64 bit) Wine loader.
 */
static const char *get_alternate_loader( char **ret_env )
{
    char *env;
    const char *loader = NULL;
    const char *loader_env = getenv( "WINELOADER" );

    *ret_env = NULL;

1742
    if (wine_get_build_dir()) loader = is_win64 ? "loader/wine" : "server/../loader/wine64";
1743 1744 1745 1746

    if (loader_env)
    {
        int len = strlen( loader_env );
1747
        if (!is_win64)
1748 1749 1750 1751
        {
            if (!(env = HeapAlloc( GetProcessHeap(), 0, sizeof("WINELOADER=") + len + 2 ))) return NULL;
            strcpy( env, "WINELOADER=" );
            strcat( env, loader_env );
1752
            strcat( env, "64" );
1753 1754 1755 1756 1757 1758 1759
        }
        else
        {
            if (!(env = HeapAlloc( GetProcessHeap(), 0, sizeof("WINELOADER=") + len ))) return NULL;
            strcpy( env, "WINELOADER=" );
            strcat( env, loader_env );
            len += sizeof("WINELOADER=") - 1;
1760
            if (!strcmp( env + len - 2, "64" )) env[len - 2] = 0;
1761 1762 1763 1764 1765 1766
        }
        if (!loader)
        {
            if ((loader = strrchr( env, '/' ))) loader++;
            else loader = env;
        }
1767
        *ret_env = env;
1768
    }
1769
    if (!loader) loader = is_win64 ? "wine" : "wine64";
1770 1771
    return loader;
}
1772

1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820
#ifdef __APPLE__
/***********************************************************************
 *           terminate_main_thread
 *
 * On some versions of Mac OS X, the execve system call fails with
 * ENOTSUP if the process has multiple threads.  Wine is always multi-
 * threaded on Mac OS X because it specifically reserves the main thread
 * for use by the system frameworks (see apple_main_thread() in
 * libs/wine/loader.c).  So, when we need to exec without first forking,
 * we need to terminate the main thread first.  We do this by installing
 * a custom run loop source onto the main run loop and signaling it.
 * The source's "perform" callback is pthread_exit and it will be
 * executed on the main thread, terminating it.
 *
 * Returns TRUE if there's still hope the main thread has terminated or
 * will soon.  Return FALSE if we've given up.
 */
static BOOL terminate_main_thread(void)
{
    static int delayms;

    if (!delayms)
    {
        CFRunLoopSourceContext source_context = { 0 };
        CFRunLoopSourceRef source;

        source_context.perform = pthread_exit;
        if (!(source = CFRunLoopSourceCreate( NULL, 0, &source_context )))
            return FALSE;

        CFRunLoopAddSource( CFRunLoopGetMain(), source, kCFRunLoopCommonModes );
        CFRunLoopSourceSignal( source );
        CFRunLoopWakeUp( CFRunLoopGetMain() );
        CFRelease( source );

        delayms = 20;
    }

    if (delayms > 1000)
        return FALSE;

    usleep(delayms * 1000);
    delayms *= 2;

    return TRUE;
}
#endif

1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839
/***********************************************************************
 *           get_process_cpu
 */
static int get_process_cpu( const WCHAR *filename, const struct binary_info *binary_info )
{
    switch (binary_info->arch)
    {
    case IMAGE_FILE_MACHINE_I386:    return CPU_x86;
    case IMAGE_FILE_MACHINE_AMD64:   return CPU_x86_64;
    case IMAGE_FILE_MACHINE_POWERPC: return CPU_POWERPC;
    case IMAGE_FILE_MACHINE_ARM:
    case IMAGE_FILE_MACHINE_THUMB:
    case IMAGE_FILE_MACHINE_ARMNT:   return CPU_ARM;
    case IMAGE_FILE_MACHINE_ARM64:   return CPU_ARM64;
    }
    ERR( "%s uses unsupported architecture (%04x)\n", debugstr_w(filename), binary_info->arch );
    return -1;
}

1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858
/***********************************************************************
 *           exec_loader
 */
static pid_t exec_loader( LPCWSTR cmd_line, unsigned int flags, int socketfd,
                          int stdin_fd, int stdout_fd, const char *unixdir, char *winedebug,
                          const struct binary_info *binary_info, int exec_only )
{
    pid_t pid;
    char *wineloader = NULL;
    const char *loader = NULL;
    char **argv;

    argv = build_argv( cmd_line, 1 );

    if (!is_win64 ^ !(binary_info->flags & BINARY_FLAG_64BIT))
        loader = get_alternate_loader( &wineloader );

    if (exec_only || !(pid = fork()))  /* child */
    {
1859
        if (exec_only || !(pid = fork()))  /* grandchild */
1860
        {
1861 1862 1863
            char preloader_reserve[64], socket_env[64];

            if (flags & (CREATE_NEW_PROCESS_GROUP | CREATE_NEW_CONSOLE | DETACHED_PROCESS))
1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874
            {
                int fd = open( "/dev/null", O_RDWR );
                setsid();
                /* close stdin and stdout */
                if (fd != -1)
                {
                    dup2( fd, 0 );
                    dup2( fd, 1 );
                    close( fd );
                }
            }
1875 1876 1877 1878 1879
            else
            {
                if (stdin_fd != -1) dup2( stdin_fd, 0 );
                if (stdout_fd != -1) dup2( stdout_fd, 1 );
            }
1880

1881 1882
            if (stdin_fd != -1) close( stdin_fd );
            if (stdout_fd != -1) close( stdout_fd );
1883

1884 1885
            /* Reset signals that we previously set to SIG_IGN */
            signal( SIGPIPE, SIG_DFL );
1886

1887 1888 1889
            sprintf( socket_env, "WINESERVERSOCKET=%u", socketfd );
            sprintf( preloader_reserve, "WINEPRELOADRESERVE=%lx-%lx",
                     (unsigned long)binary_info->res_start, (unsigned long)binary_info->res_end );
1890

1891 1892 1893 1894 1895
            putenv( preloader_reserve );
            putenv( socket_env );
            if (winedebug) putenv( winedebug );
            if (wineloader) putenv( wineloader );
            if (unixdir) chdir(unixdir);
1896

1897
            if (argv)
1898
            {
1899 1900 1901 1902
                do
                {
                    wine_exec_wine_binary( loader, argv, getenv("WINELOADER") );
                }
1903
#ifdef __APPLE__
1904
                while (errno == ENOTSUP && exec_only && terminate_main_thread());
1905
#else
1906
                while (0);
1907
#endif
1908 1909
            }
            _exit(1);
1910
        }
1911 1912

        _exit(pid == -1);
1913
    }
1914 1915 1916 1917 1918 1919 1920 1921 1922 1923

    if (pid != -1)
    {
        /* reap child */
        pid_t wret;
        do {
            wret = waitpid(pid, NULL, 0);
        } while (wret < 0 && errno == EINTR);
    }

1924 1925 1926 1927 1928
    HeapFree( GetProcessHeap(), 0, wineloader );
    HeapFree( GetProcessHeap(), 0, argv );
    return pid;
}

1929 1930 1931 1932 1933 1934
/***********************************************************************
 *           create_process
 *
 * Create a new process. If hFile is a valid handle we have an exe
 * file, otherwise it is a Winelib app.
 */
1935
static BOOL create_process( HANDLE hFile, LPCWSTR filename, LPWSTR cmd_line, LPWSTR env,
1936
                            LPCWSTR cur_dir, LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
1937
                            BOOL inherit, DWORD flags, LPSTARTUPINFOW startup,
1938
                            LPPROCESS_INFORMATION info, LPCSTR unixdir,
1939
                            const struct binary_info *binary_info, int exec_only )
1940
{
1941 1942 1943
    static const char *cpu_names[] = { "x86", "x86_64", "PowerPC", "ARM", "ARM64" };
    NTSTATUS status;
    BOOL success = FALSE;
1944
    HANDLE process_info;
1945
    WCHAR *env_end;
1946
    char *winedebug = NULL;
1947 1948
    startup_info_t *startup_info;
    DWORD startup_info_size;
1949
    int socketfd[2], stdin_fd = -1, stdout_fd = -1;
1950
    pid_t pid;
1951
    int err, cpu;
1952

1953
    if ((cpu = get_process_cpu( filename, binary_info )) == -1)
1954 1955 1956 1957 1958
    {
        SetLastError( ERROR_BAD_EXE_FORMAT );
        return FALSE;
    }

1959 1960 1961 1962 1963 1964 1965
    /* create the socket for the new process */

    if (socketpair( PF_UNIX, SOCK_STREAM, 0, socketfd ) == -1)
    {
        SetLastError( ERROR_TOO_MANY_OPEN_FILES );
        return FALSE;
    }
1966 1967 1968 1969 1970 1971 1972
#ifdef SO_PASSCRED
    else
    {
        int enable = 1;
        setsockopt( socketfd[0], SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable) );
    }
#endif
1973 1974 1975 1976 1977 1978 1979 1980 1981 1982

    if (exec_only)  /* things are much simpler in this case */
    {
        wine_server_send_fd( socketfd[1] );
        close( socketfd[1] );
        SERVER_START_REQ( new_process )
        {
            req->create_flags   = flags;
            req->socket_fd      = socketfd[1];
            req->exe_file       = wine_server_obj_handle( hFile );
1983 1984
            req->cpu            = cpu;
            status = wine_server_call( req );
1985 1986 1987
        }
        SERVER_END_REQ;

1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000
        switch (status)
        {
        case STATUS_INVALID_IMAGE_WIN_64:
            ERR( "64-bit application %s not supported in 32-bit prefix\n", debugstr_w(filename) );
            break;
        case STATUS_INVALID_IMAGE_FORMAT:
            ERR( "%s not supported on this installation (%s binary)\n",
                 debugstr_w(filename), cpu_names[cpu] );
            break;
        case STATUS_SUCCESS:
            exec_loader( cmd_line, flags, socketfd[0], stdin_fd, stdout_fd, unixdir,
                         winedebug, binary_info, TRUE );
        }
2001
        close( socketfd[0] );
2002
        SetLastError( RtlNtStatusToDosError( status ));
2003 2004 2005
        return FALSE;
    }

2006
    RtlAcquirePebLock();
2007

2008 2009
    if (!(startup_info = create_startup_info( filename, cmd_line, cur_dir, env, flags, startup,
                                              &startup_info_size )))
2010
    {
2011
        RtlReleasePebLock();
2012 2013
        close( socketfd[0] );
        close( socketfd[1] );
2014
        return FALSE;
2015
    }
2016 2017
    if (!env) env = NtCurrentTeb()->Peb->ProcessParameters->Environment;
    env_end = env;
2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028
    while (*env_end)
    {
        static const WCHAR WINEDEBUG[] = {'W','I','N','E','D','E','B','U','G','=',0};
        if (!winedebug && !strncmpW( env_end, WINEDEBUG, sizeof(WINEDEBUG)/sizeof(WCHAR) - 1 ))
        {
            DWORD len = WideCharToMultiByte( CP_UNIXCP, 0, env_end, -1, NULL, 0, NULL, NULL );
            if ((winedebug = HeapAlloc( GetProcessHeap(), 0, len )))
                WideCharToMultiByte( CP_UNIXCP, 0, env_end, -1, winedebug, len, NULL, NULL );
        }
        env_end += strlenW(env_end) + 1;
    }
2029
    env_end++;
2030

2031 2032
    wine_server_send_fd( socketfd[1] );
    close( socketfd[1] );
2033 2034 2035 2036 2037

    /* create the process on the server side */

    SERVER_START_REQ( new_process )
    {
2038 2039 2040
        req->inherit_all    = inherit;
        req->create_flags   = flags;
        req->socket_fd      = socketfd[1];
2041
        req->exe_file       = wine_server_obj_handle( hFile );
2042 2043 2044 2045
        req->process_access = PROCESS_ALL_ACCESS;
        req->process_attr   = (psa && (psa->nLength >= sizeof(*psa)) && psa->bInheritHandle) ? OBJ_INHERIT : 0;
        req->thread_access  = THREAD_ALL_ACCESS;
        req->thread_attr    = (tsa && (tsa->nLength >= sizeof(*tsa)) && tsa->bInheritHandle) ? OBJ_INHERIT : 0;
2046
        req->cpu            = cpu;
2047
        req->info_size      = startup_info_size;
2048

2049 2050
        wine_server_add_data( req, startup_info, startup_info_size );
        wine_server_add_data( req, env, (env_end - env) * sizeof(WCHAR) );
2051
        if (!(status = wine_server_call( req )))
2052 2053 2054
        {
            info->dwProcessId = (DWORD)reply->pid;
            info->dwThreadId  = (DWORD)reply->tid;
2055 2056
            info->hProcess    = wine_server_ptr_handle( reply->phandle );
            info->hThread     = wine_server_ptr_handle( reply->thandle );
2057
        }
2058
        process_info = wine_server_ptr_handle( reply->info );
2059 2060
    }
    SERVER_END_REQ;
2061

2062
    RtlReleasePebLock();
2063
    if (status)
2064
    {
2065 2066 2067 2068 2069 2070 2071 2072 2073 2074
        switch (status)
        {
        case STATUS_INVALID_IMAGE_WIN_64:
            ERR( "64-bit application %s not supported in 32-bit prefix\n", debugstr_w(filename) );
            break;
        case STATUS_INVALID_IMAGE_FORMAT:
            ERR( "%s not supported on this installation (%s binary)\n",
                 debugstr_w(filename), cpu_names[cpu] );
            break;
        }
2075
        close( socketfd[0] );
2076
        HeapFree( GetProcessHeap(), 0, startup_info );
2077
        HeapFree( GetProcessHeap(), 0, winedebug );
2078
        SetLastError( RtlNtStatusToDosError( status ));
2079 2080 2081
        return FALSE;
    }

2082 2083 2084 2085 2086 2087 2088 2089 2090 2091
    if (!(flags & (CREATE_NEW_CONSOLE | DETACHED_PROCESS)))
    {
        if (startup_info->hstdin)
            wine_server_handle_to_fd( wine_server_ptr_handle(startup_info->hstdin),
                                      FILE_READ_DATA, &stdin_fd, NULL );
        if (startup_info->hstdout)
            wine_server_handle_to_fd( wine_server_ptr_handle(startup_info->hstdout),
                                      FILE_WRITE_DATA, &stdout_fd, NULL );
    }
    HeapFree( GetProcessHeap(), 0, startup_info );
2092

2093 2094
    /* create the child process */

2095 2096
    pid = exec_loader( cmd_line, flags, socketfd[0], stdin_fd, stdout_fd, unixdir,
                       winedebug, binary_info, FALSE );
2097

2098 2099
    if (stdin_fd != -1) close( stdin_fd );
    if (stdout_fd != -1) close( stdout_fd );
2100 2101 2102
    close( socketfd[0] );
    HeapFree( GetProcessHeap(), 0, winedebug );
    if (pid == -1)
2103 2104
    {
        FILE_SetDosError();
2105
        goto error;
2106 2107 2108 2109 2110 2111 2112
    }

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

    WaitForSingleObject( process_info, INFINITE );
    SERVER_START_REQ( get_new_process_info )
    {
2113
        req->info = wine_server_obj_handle( process_info );
2114 2115 2116
        wine_server_call( req );
        success = reply->success;
        err = reply->exit_code;
2117 2118 2119
    }
    SERVER_END_REQ;

2120
    if (!success)
2121
    {
2122 2123
        SetLastError( err ? err : ERROR_INTERNAL_ERROR );
        goto error;
2124 2125
    }
    CloseHandle( process_info );
2126 2127 2128 2129 2130 2131 2132 2133 2134
    return success;

error:
    CloseHandle( process_info );
    CloseHandle( info->hProcess );
    CloseHandle( info->hThread );
    info->hProcess = info->hThread = 0;
    info->dwProcessId = info->dwThreadId = 0;
    return FALSE;
2135 2136 2137 2138 2139 2140 2141 2142
}


/***********************************************************************
 *           create_vdm_process
 *
 * Create a new VDM process for a 16-bit or DOS application.
 */
2143
static BOOL create_vdm_process( LPCWSTR filename, LPWSTR cmd_line, LPWSTR env, LPCWSTR cur_dir,
2144
                                LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
2145
                                BOOL inherit, DWORD flags, LPSTARTUPINFOW startup,
2146
                                LPPROCESS_INFORMATION info, LPCSTR unixdir,
2147
                                const struct binary_info *binary_info, int exec_only )
2148
{
2149 2150
    static const WCHAR argsW[] = {'%','s',' ','-','-','a','p','p','-','n','a','m','e',' ','"','%','s','"',' ','%','s',0};

2151
    BOOL ret;
2152 2153
    LPWSTR new_cmd_line = HeapAlloc( GetProcessHeap(), 0,
                                     (strlenW(filename) + strlenW(cmd_line) + 30) * sizeof(WCHAR) );
2154 2155 2156 2157 2158 2159

    if (!new_cmd_line)
    {
        SetLastError( ERROR_OUTOFMEMORY );
        return FALSE;
    }
2160
    sprintfW( new_cmd_line, argsW, winevdmW, filename, cmd_line );
2161
    ret = create_process( 0, winevdmW, new_cmd_line, env, cur_dir, psa, tsa, inherit,
2162
                          flags, startup, info, unixdir, binary_info, exec_only );
2163 2164 2165 2166 2167
    HeapFree( GetProcessHeap(), 0, new_cmd_line );
    return ret;
}


2168 2169 2170 2171 2172
/***********************************************************************
 *           create_cmd_process
 *
 * Create a new cmd shell process for a .BAT file.
 */
2173
static BOOL create_cmd_process( LPCWSTR filename, LPWSTR cmd_line, LPVOID env, LPCWSTR cur_dir,
2174 2175
                                LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
                                BOOL inherit, DWORD flags, LPSTARTUPINFOW startup,
2176
                                LPPROCESS_INFORMATION info )
2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200

{
    static const WCHAR comspecW[] = {'C','O','M','S','P','E','C',0};
    static const WCHAR slashcW[] = {' ','/','c',' ',0};
    WCHAR comspec[MAX_PATH];
    WCHAR *newcmdline;
    BOOL ret;

    if (!GetEnvironmentVariableW( comspecW, comspec, sizeof(comspec)/sizeof(WCHAR) ))
        return FALSE;
    if (!(newcmdline = HeapAlloc( GetProcessHeap(), 0,
                                  (strlenW(comspec) + 4 + strlenW(cmd_line) + 1) * sizeof(WCHAR))))
        return FALSE;

    strcpyW( newcmdline, comspec );
    strcatW( newcmdline, slashcW );
    strcatW( newcmdline, cmd_line );
    ret = CreateProcessW( comspec, newcmdline, psa, tsa, inherit,
                          flags, env, cur_dir, startup, info );
    HeapFree( GetProcessHeap(), 0, newcmdline );
    return ret;
}


2201 2202 2203 2204 2205 2206 2207 2208
/*************************************************************************
 *               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.
 */
2209
static LPWSTR get_file_name( LPCWSTR appname, LPWSTR cmdline, LPWSTR buffer,
2210
                             int buflen, HANDLE *handle, struct binary_info *binary_info )
2211
{
2212 2213
    static const WCHAR quotesW[] = {'"','%','s','"',0};

2214
    WCHAR *name, *pos, *first_space, *ret = NULL;
2215
    const WCHAR *p;
2216 2217 2218 2219 2220 2221

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

    if (appname)
    {
        /* use the unmodified app name as file name */
2222
        lstrcpynW( buffer, appname, buflen );
2223
        *handle = open_exe_file( buffer, binary_info );
2224 2225 2226
        if (!(ret = cmdline) || !cmdline[0])
        {
            /* no command-line, create one */
2227
            if ((ret = HeapAlloc( GetProcessHeap(), 0, (strlenW(appname) + 3) * sizeof(WCHAR) )))
2228
                sprintfW( ret, quotesW, appname );
2229 2230 2231 2232 2233 2234
        }
        return ret;
    }

    /* first check for a quoted file name */

2235
    if ((cmdline[0] == '"') && ((p = strchrW( cmdline + 1, '"' ))))
2236 2237 2238
    {
        int len = p - cmdline - 1;
        /* extract the quoted portion as file name */
2239 2240
        if (!(name = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ))) return NULL;
        memcpy( name, cmdline + 1, len * sizeof(WCHAR) );
2241 2242
        name[len] = 0;

2243
        if (!find_exe_file( name, buffer, buflen, handle, binary_info )) goto done;
2244
        ret = cmdline;  /* no change necessary */
2245 2246 2247 2248 2249
        goto done;
    }

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

2250 2251
    if (!(name = HeapAlloc( GetProcessHeap(), 0, (strlenW(cmdline) + 1) * sizeof(WCHAR) )))
        return NULL;
2252 2253
    pos = name;
    p = cmdline;
2254
    first_space = NULL;
2255

2256
    for (;;)
2257
    {
2258
        while (*p && *p != ' ' && *p != '\t') *pos++ = *p++;
2259
        *pos = 0;
2260
        if (find_exe_file( name, buffer, buflen, handle, binary_info ))
2261 2262 2263 2264
        {
            ret = cmdline;
            break;
        }
2265
        if (!first_space) first_space = pos;
2266
        if (!(*pos++ = *p++)) break;
2267 2268
    }

2269 2270
    if (!ret)
    {
2271
        SetLastError( ERROR_FILE_NOT_FOUND );
2272 2273
    }
    else if (first_space)  /* build a new command-line with quotes */
2274 2275 2276 2277 2278 2279
    {
        if (!(ret = HeapAlloc( GetProcessHeap(), 0, (strlenW(cmdline) + 3) * sizeof(WCHAR) )))
            goto done;
        sprintfW( ret, quotesW, name );
        strcatW( ret, p );
    }
2280 2281 2282 2283 2284 2285 2286

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


2287 2288 2289 2290 2291
/* Steam hotpatches CreateProcessA and W, so to prevent it from crashing use an internal function */
static BOOL create_process_impl( 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 )
2292 2293 2294
{
    BOOL retv = FALSE;
    HANDLE hFile = 0;
2295
    char *unixdir = NULL;
2296 2297
    WCHAR name[MAX_PATH];
    WCHAR *tidy_cmdline, *p, *envW = env;
2298
    struct binary_info binary_info;
2299 2300 2301

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

2302
    TRACE("app %s cmdline %s\n", debugstr_w(app_name), debugstr_w(cmd_line) );
2303

2304 2305
    if (!(tidy_cmdline = get_file_name( app_name, cmd_line, name, sizeof(name)/sizeof(WCHAR),
                                        &hFile, &binary_info )))
2306 2307 2308 2309 2310
        return FALSE;
    if (hFile == INVALID_HANDLE_VALUE) goto done;

    /* Warn if unsupported features are used */

2311 2312 2313 2314
    if (flags & (IDLE_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | REALTIME_PRIORITY_CLASS |
                 CREATE_NEW_PROCESS_GROUP | CREATE_SEPARATE_WOW_VDM | CREATE_SHARED_WOW_VDM |
                 CREATE_DEFAULT_ERROR_MODE | CREATE_NO_WINDOW |
                 PROFILE_USER | PROFILE_KERNEL | PROFILE_SERVER))
2315
        WARN("(%s,...): ignoring some flags in %x\n", debugstr_w(name), flags);
2316 2317 2318

    if (cur_dir)
    {
2319 2320 2321 2322 2323
        if (!(unixdir = wine_get_unix_file_name( cur_dir )))
        {
            SetLastError(ERROR_DIRECTORY);
            goto done;
        }
2324 2325 2326 2327
    }
    else
    {
        WCHAR buf[MAX_PATH];
2328
        if (GetCurrentDirectoryW(MAX_PATH, buf)) unixdir = wine_get_unix_file_name( buf );
2329 2330
    }

2331 2332
    if (env && !(flags & CREATE_UNICODE_ENVIRONMENT))  /* convert environment to unicode */
    {
2333
        char *e = env;
2334 2335
        DWORD lenW;

2336 2337 2338
        while (*e) e += strlen(e) + 1;
        e++;  /* final null */
        lenW = MultiByteToWideChar( CP_ACP, 0, env, e - (char*)env, NULL, 0 );
2339
        envW = HeapAlloc( GetProcessHeap(), 0, lenW * sizeof(WCHAR) );
2340
        MultiByteToWideChar( CP_ACP, 0, env, e - (char*)env, envW, lenW );
2341 2342 2343
        flags |= CREATE_UNICODE_ENVIRONMENT;
    }

2344 2345 2346
    info->hThread = info->hProcess = 0;
    info->dwProcessId = info->dwThreadId = 0;

2347
    if (binary_info.flags & BINARY_FLAG_DLL)
2348
    {
2349 2350 2351
        TRACE( "not starting %s since it is a dll\n", debugstr_w(name) );
        SetLastError( ERROR_BAD_EXE_FORMAT );
    }
2352
    else switch (binary_info.type)
2353 2354
    {
    case BINARY_PE:
2355
        TRACE( "starting %s as Win%d binary (%p-%p, arch %04x%s)\n",
2356
               debugstr_w(name), (binary_info.flags & BINARY_FLAG_64BIT) ? 64 : 32,
2357 2358
               binary_info.res_start, binary_info.res_end, binary_info.arch,
               (binary_info.flags & BINARY_FLAG_FAKEDLL) ? ", fakedll" : "" );
2359
        retv = create_process( hFile, name, tidy_cmdline, envW, cur_dir, process_attr, thread_attr,
2360
                               inherit, flags, startup_info, info, unixdir, &binary_info, FALSE );
2361
        break;
2362
    case BINARY_OS216:
2363 2364
    case BINARY_WIN16:
    case BINARY_DOS:
2365
        TRACE( "starting %s as Win16/DOS binary\n", debugstr_w(name) );
2366
        retv = create_vdm_process( name, tidy_cmdline, envW, cur_dir, process_attr, thread_attr,
2367
                                   inherit, flags, startup_info, info, unixdir, &binary_info, FALSE );
2368 2369
        break;
    case BINARY_UNIX_LIB:
2370 2371
        TRACE( "starting %s as %d-bit Winelib app\n",
               debugstr_w(name), (binary_info.flags & BINARY_FLAG_64BIT) ? 64 : 32 );
2372
        retv = create_process( hFile, name, tidy_cmdline, envW, cur_dir, process_attr, thread_attr,
2373
                               inherit, flags, startup_info, info, unixdir, &binary_info, FALSE );
2374 2375 2376
        break;
    case BINARY_UNKNOWN:
        /* check for .com or .bat extension */
2377
        if ((p = strrchrW( name, '.' )))
2378
        {
2379
            if (!strcmpiW( p, comW ) || !strcmpiW( p, pifW ))
2380
            {
2381
                TRACE( "starting %s as DOS binary\n", debugstr_w(name) );
2382 2383
                binary_info.type = BINARY_DOS;
                binary_info.arch = IMAGE_FILE_MACHINE_I386;
2384
                retv = create_vdm_process( name, tidy_cmdline, envW, cur_dir, process_attr, thread_attr,
2385
                                           inherit, flags, startup_info, info, unixdir,
2386
                                           &binary_info, FALSE );
2387 2388
                break;
            }
2389
            if (!strcmpiW( p, batW ) || !strcmpiW( p, cmdW ) )
2390
            {
2391
                TRACE( "starting %s as batch binary\n", debugstr_w(name) );
2392 2393
                retv = create_cmd_process( name, tidy_cmdline, envW, cur_dir, process_attr, thread_attr,
                                           inherit, flags, startup_info, info );
2394
                break;
2395 2396 2397 2398 2399 2400
            }
        }
        /* fall through */
    case BINARY_UNIX_EXE:
        {
            /* unknown file, try as unix executable */
2401
            char *unix_name;
2402

2403
            TRACE( "starting %s as Unix binary\n", debugstr_w(name) );
2404

2405 2406
            if ((unix_name = wine_get_unix_file_name( name )))
            {
2407
                retv = (fork_and_exec( unix_name, tidy_cmdline, envW, unixdir, flags, startup_info ) != -1);
2408 2409
                HeapFree( GetProcessHeap(), 0, unix_name );
            }
2410 2411 2412
        }
        break;
    }
2413
    if (hFile) CloseHandle( hFile );
2414 2415 2416

 done:
    if (tidy_cmdline != cmd_line) HeapFree( GetProcessHeap(), 0, tidy_cmdline );
2417
    if (envW != env) HeapFree( GetProcessHeap(), 0, envW );
2418
    HeapFree( GetProcessHeap(), 0, unixdir );
2419 2420
    if (retv)
        TRACE( "started process pid %04x tid %04x\n", info->dwProcessId, info->dwThreadId );
2421 2422 2423 2424
    return retv;
}


2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479
/**********************************************************************
 *       CreateProcessA          (KERNEL32.@)
 */
BOOL WINAPI DECLSPEC_HOTPATCH 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 ret = FALSE;
    WCHAR *app_nameW = NULL, *cmd_lineW = NULL, *cur_dirW = NULL;
    UNICODE_STRING desktopW, titleW;
    STARTUPINFOW infoW;

    desktopW.Buffer = NULL;
    titleW.Buffer = NULL;
    if (app_name && !(app_nameW = FILE_name_AtoW( app_name, TRUE ))) goto done;
    if (cmd_line && !(cmd_lineW = FILE_name_AtoW( cmd_line, TRUE ))) goto done;
    if (cur_dir && !(cur_dirW = FILE_name_AtoW( cur_dir, TRUE ))) goto done;

    if (startup_info->lpDesktop) RtlCreateUnicodeStringFromAsciiz( &desktopW, startup_info->lpDesktop );
    if (startup_info->lpTitle) RtlCreateUnicodeStringFromAsciiz( &titleW, startup_info->lpTitle );

    memcpy( &infoW, startup_info, sizeof(infoW) );
    infoW.lpDesktop = desktopW.Buffer;
    infoW.lpTitle = titleW.Buffer;

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

    ret = create_process_impl( app_nameW, cmd_lineW, process_attr, thread_attr,
                               inherit, flags, env, cur_dirW, &infoW, info );
done:
    HeapFree( GetProcessHeap(), 0, app_nameW );
    HeapFree( GetProcessHeap(), 0, cmd_lineW );
    HeapFree( GetProcessHeap(), 0, cur_dirW );
    RtlFreeUnicodeString( &desktopW );
    RtlFreeUnicodeString( &titleW );
    return ret;
}


/**********************************************************************
 *       CreateProcessW          (KERNEL32.@)
 */
BOOL WINAPI DECLSPEC_HOTPATCH 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 )
{
    return create_process_impl( app_name, cmd_line, process_attr, thread_attr,
                                inherit, flags, env, cur_dir, startup_info, info);
}


2480 2481 2482 2483 2484 2485 2486 2487 2488
/**********************************************************************
 *       exec_process
 */
static void exec_process( LPCWSTR name )
{
    HANDLE hFile;
    WCHAR *p;
    STARTUPINFOW startup_info;
    PROCESS_INFORMATION info;
2489
    struct binary_info binary_info;
2490

2491
    hFile = open_exe_file( name, &binary_info );
2492 2493 2494 2495 2496 2497 2498
    if (!hFile || hFile == INVALID_HANDLE_VALUE) return;

    memset( &startup_info, 0, sizeof(startup_info) );
    startup_info.cb = sizeof(startup_info);

    /* Determine executable type */

2499 2500 2501 2502 2503 2504
    if (binary_info.flags & BINARY_FLAG_DLL)
    {
        CloseHandle( hFile );
        return;
    }

2505
    switch (binary_info.type)
2506
    {
2507
    case BINARY_PE:
2508
        TRACE( "starting %s as Win%d binary (%p-%p, arch %04x)\n",
2509
               debugstr_w(name), (binary_info.flags & BINARY_FLAG_64BIT) ? 64 : 32,
2510
               binary_info.res_start, binary_info.res_end, binary_info.arch );
2511
        create_process( hFile, name, GetCommandLineW(), NULL, NULL, NULL, NULL,
2512
                        FALSE, 0, &startup_info, &info, NULL, &binary_info, TRUE );
2513 2514 2515 2516
        break;
    case BINARY_UNIX_LIB:
        TRACE( "%s is a Unix library, starting as Winelib app\n", debugstr_w(name) );
        create_process( hFile, name, GetCommandLineW(), NULL, NULL, NULL, NULL,
2517
                        FALSE, 0, &startup_info, &info, NULL, &binary_info, TRUE );
2518 2519 2520 2521 2522
        break;
    case BINARY_UNKNOWN:
        /* check for .com or .pif extension */
        if (!(p = strrchrW( name, '.' ))) break;
        if (strcmpiW( p, comW ) && strcmpiW( p, pifW )) break;
2523 2524
        binary_info.type = BINARY_DOS;
        binary_info.arch = IMAGE_FILE_MACHINE_I386;
2525 2526 2527 2528 2529 2530
        /* fall through */
    case BINARY_OS216:
    case BINARY_WIN16:
    case BINARY_DOS:
        TRACE( "starting %s as Win16/DOS binary\n", debugstr_w(name) );
        create_vdm_process( name, GetCommandLineW(), NULL, NULL, NULL, NULL,
2531
                            FALSE, 0, &startup_info, &info, NULL, &binary_info, TRUE );
2532 2533 2534 2535 2536 2537 2538 2539
        break;
    default:
        break;
    }
    CloseHandle( hFile );
}


2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561
/***********************************************************************
 *           wait_input_idle
 *
 * Wrapper to call WaitForInputIdle USER function
 */
typedef DWORD (WINAPI *WaitForInputIdle_ptr)( HANDLE hProcess, DWORD dwTimeOut );

static DWORD wait_input_idle( HANDLE process, DWORD timeout )
{
    HMODULE mod = GetModuleHandleA( "user32.dll" );
    if (mod)
    {
        WaitForInputIdle_ptr ptr = (WaitForInputIdle_ptr)GetProcAddress( mod, "WaitForInputIdle" );
        if (ptr) return ptr( process, timeout );
    }
    return 0;
}


/***********************************************************************
 *           WinExec   (KERNEL32.@)
 */
2562
UINT WINAPI DECLSPEC_HOTPATCH WinExec( LPCSTR lpCmdLine, UINT nCmdShow )
2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573
{
    PROCESS_INFORMATION info;
    STARTUPINFOA startup;
    char *cmdline;
    UINT ret;

    memset( &startup, 0, sizeof(startup) );
    startup.cb = sizeof(startup);
    startup.dwFlags = STARTF_USESHOWWINDOW;
    startup.wShowWindow = nCmdShow;

2574
    /* cmdline needs to be writable for CreateProcess */
2575 2576 2577 2578 2579 2580 2581
    if (!(cmdline = HeapAlloc( GetProcessHeap(), 0, strlen(lpCmdLine)+1 ))) return 0;
    strcpy( cmdline, lpCmdLine );

    if (CreateProcessA( NULL, cmdline, NULL, NULL, FALSE,
                        0, NULL, NULL, &startup, &info ))
    {
        /* Give 30 seconds to the app to come up */
2582
        if (wait_input_idle( info.hProcess, 30000 ) == WAIT_FAILED)
2583
            WARN("WaitForInputIdle failed: Error %d\n", GetLastError() );
2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601
        ret = 33;
        /* Close off the handles */
        CloseHandle( info.hThread );
        CloseHandle( info.hProcess );
    }
    else if ((ret = GetLastError()) >= 32)
    {
        FIXME("Strange error set by CreateProcess: %d\n", ret );
        ret = 11;
    }
    HeapFree( GetProcessHeap(), 0, cmdline );
    return ret;
}


/**********************************************************************
 *	    LoadModule    (KERNEL32.@)
 */
2602
DWORD WINAPI LoadModule( LPCSTR name, LPVOID paramBlock )
2603
{
2604
    LOADPARMS32 *params = paramBlock;
2605 2606
    PROCESS_INFORMATION info;
    STARTUPINFOA startup;
2607
    DWORD ret;
2608 2609 2610 2611
    LPSTR cmdline, p;
    char filename[MAX_PATH];
    BYTE len;

2612
    if (!name) return ERROR_FILE_NOT_FOUND;
2613 2614 2615

    if (!SearchPathA( NULL, name, ".exe", sizeof(filename), filename, NULL ) &&
        !SearchPathA( NULL, name, NULL, sizeof(filename), filename, NULL ))
2616
        return GetLastError();
2617 2618 2619

    len = (BYTE)params->lpCmdLine[0];
    if (!(cmdline = HeapAlloc( GetProcessHeap(), 0, strlen(filename) + len + 2 )))
2620
        return ERROR_NOT_ENOUGH_MEMORY;
2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632

    strcpy( cmdline, filename );
    p = cmdline + strlen(cmdline);
    *p++ = ' ';
    memcpy( p, params->lpCmdLine + 1, len );
    p[len] = 0;

    memset( &startup, 0, sizeof(startup) );
    startup.cb = sizeof(startup);
    if (params->lpCmdShow)
    {
        startup.dwFlags = STARTF_USESHOWWINDOW;
2633
        startup.wShowWindow = ((WORD *)params->lpCmdShow)[1];
2634 2635 2636 2637 2638 2639
    }

    if (CreateProcessA( filename, cmdline, NULL, NULL, FALSE, 0,
                        params->lpEnvAddress, NULL, &startup, &info ))
    {
        /* Give 30 seconds to the app to come up */
2640
        if (wait_input_idle( info.hProcess, 30000 ) == WAIT_FAILED)
2641
            WARN("WaitForInputIdle failed: Error %d\n", GetLastError() );
2642
        ret = 33;
2643 2644 2645 2646
        /* Close off the handles */
        CloseHandle( info.hThread );
        CloseHandle( info.hProcess );
    }
2647
    else if ((ret = GetLastError()) >= 32)
2648
    {
2649 2650
        FIXME("Strange error set by CreateProcess: %u\n", ret );
        ret = 11;
2651 2652 2653
    }

    HeapFree( GetProcessHeap(), 0, cmdline );
2654
    return ret;
2655 2656 2657 2658 2659
}


/******************************************************************************
 *           TerminateProcess   (KERNEL32.@)
2660 2661 2662 2663 2664 2665 2666 2667 2668 2669
 *
 * Terminates a process.
 *
 * PARAMS
 *  handle    [I] Process to terminate.
 *  exit_code [I] Exit code.
 *
 * RETURNS
 *  Success: TRUE.
 *  Failure: FALSE, check GetLastError().
2670 2671 2672
 */
BOOL WINAPI TerminateProcess( HANDLE handle, DWORD exit_code )
{
2673 2674 2675 2676 2677 2678 2679 2680 2681
    NTSTATUS status;

    if (!handle)
    {
        SetLastError( ERROR_INVALID_HANDLE );
        return FALSE;
    }

    status = NtTerminateProcess( handle, exit_code );
2682 2683 2684 2685
    if (status) SetLastError( RtlNtStatusToDosError(status) );
    return !status;
}

2686 2687
/***********************************************************************
 *           ExitProcess   (KERNEL32.@)
2688 2689 2690 2691 2692 2693 2694 2695
 *
 * Exits the current process.
 *
 * PARAMS
 *  status [I] Status code to exit with.
 *
 * RETURNS
 *  Nothing.
2696
 */
2697
#ifdef __i386__
2698
__ASM_STDCALL_FUNC( ExitProcess, 4, /* Shrinker depend on this particular ExitProcess implementation */
2699 2700 2701 2702 2703
                   "pushl %ebp\n\t"
                   ".byte 0x8B, 0xEC\n\t" /* movl %esp, %ebp */
                   ".byte 0x6A, 0x00\n\t" /* pushl $0 */
                   ".byte 0x68, 0x00, 0x00, 0x00, 0x00\n\t" /* pushl $0 - 4 bytes immediate */
                   "pushl 8(%ebp)\n\t"
2704
                   "call " __ASM_NAME("RtlExitUserProcess") __ASM_STDCALL(4) "\n\t"
2705 2706 2707 2708
                   "leave\n\t"
                   "ret $4" )
#else

2709 2710
void WINAPI ExitProcess( DWORD status )
{
2711
    RtlExitUserProcess( status );
2712 2713
}

2714
#endif
2715

2716
/***********************************************************************
2717
 * GetExitCodeProcess           [KERNEL32.@]
2718
 *
2719 2720 2721 2722 2723
 * Gets termination status of specified process.
 *
 * PARAMS
 *   hProcess   [in]  Handle to the process.
 *   lpExitCode [out] Address to receive termination status.
2724 2725 2726 2727 2728
 *
 * RETURNS
 *   Success: TRUE
 *   Failure: FALSE
 */
2729
BOOL WINAPI GetExitCodeProcess( HANDLE hProcess, LPDWORD lpExitCode )
2730
{
2731 2732 2733 2734 2735 2736
    NTSTATUS status;
    PROCESS_BASIC_INFORMATION pbi;

    status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi,
                                       sizeof(pbi), NULL);
    if (status == STATUS_SUCCESS)
2737
    {
2738 2739
        if (lpExitCode) *lpExitCode = pbi.ExitStatus;
        return TRUE;
2740
    }
2741 2742
    SetLastError( RtlNtStatusToDosError(status) );
    return FALSE;
2743 2744 2745 2746 2747 2748 2749 2750
}


/***********************************************************************
 *           SetErrorMode   (KERNEL32.@)
 */
UINT WINAPI SetErrorMode( UINT mode )
{
2751 2752 2753 2754 2755 2756
    UINT old;

    NtQueryInformationProcess( GetCurrentProcess(), ProcessDefaultHardErrorMode,
                               &old, sizeof(old), NULL );
    NtSetInformationProcess( GetCurrentProcess(), ProcessDefaultHardErrorMode,
                             &mode, sizeof(mode) );
2757 2758 2759
    return old;
}

2760 2761 2762 2763 2764
/***********************************************************************
 *           GetErrorMode   (KERNEL32.@)
 */
UINT WINAPI GetErrorMode( void )
{
2765 2766 2767 2768 2769
    UINT mode;

    NtQueryInformationProcess( GetCurrentProcess(), ProcessDefaultHardErrorMode,
                               &mode, sizeof(mode), NULL );
    return mode;
2770
}
2771 2772

/**********************************************************************
2773
 * TlsAlloc             [KERNEL32.@]
2774
 *
2775
 * Allocates a thread local storage index.
2776 2777
 *
 * RETURNS
2778
 *    Success: TLS index.
2779 2780 2781 2782
 *    Failure: 0xFFFFFFFF
 */
DWORD WINAPI TlsAlloc( void )
{
2783
    DWORD index;
2784
    PEB * const peb = NtCurrentTeb()->Peb;
2785

2786
    RtlAcquirePebLock();
2787
    index = RtlFindClearBitsAndSet( peb->TlsBitmap, 1, 1 );
2788
    if (index != ~0U) NtCurrentTeb()->TlsSlots[index] = 0; /* clear the value */
2789 2790 2791
    else
    {
        index = RtlFindClearBitsAndSet( peb->TlsExpansionBitmap, 1, 0 );
2792
        if (index != ~0U)
2793 2794 2795 2796 2797 2798
        {
            if (!NtCurrentTeb()->TlsExpansionSlots &&
                !(NtCurrentTeb()->TlsExpansionSlots = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
                                         8 * sizeof(peb->TlsExpansionBitmapBits) * sizeof(void*) )))
            {
                RtlClearBits( peb->TlsExpansionBitmap, index, 1 );
2799
                index = ~0U;
2800 2801 2802 2803 2804 2805 2806 2807 2808 2809
                SetLastError( ERROR_NOT_ENOUGH_MEMORY );
            }
            else
            {
                NtCurrentTeb()->TlsExpansionSlots[index] = 0; /* clear the value */
                index += TLS_MINIMUM_AVAILABLE;
            }
        }
        else SetLastError( ERROR_NO_MORE_ITEMS );
    }
2810
    RtlReleasePebLock();
2811
    return index;
2812 2813 2814 2815
}


/**********************************************************************
2816 2817 2818
 * TlsFree              [KERNEL32.@]
 *
 * Releases a thread local storage index, making it available for reuse.
2819
 *
2820 2821
 * PARAMS
 *    index [in] TLS index to free.
2822 2823 2824 2825 2826
 *
 * RETURNS
 *    Success: TRUE
 *    Failure: FALSE
 */
2827
BOOL WINAPI TlsFree( DWORD index )
2828
{
2829 2830
    BOOL ret;

2831
    RtlAcquirePebLock();
2832 2833 2834 2835 2836 2837
    if (index >= TLS_MINIMUM_AVAILABLE)
    {
        ret = RtlAreBitsSet( NtCurrentTeb()->Peb->TlsExpansionBitmap, index - TLS_MINIMUM_AVAILABLE, 1 );
        if (ret) RtlClearBits( NtCurrentTeb()->Peb->TlsExpansionBitmap, index - TLS_MINIMUM_AVAILABLE, 1 );
    }
    else
2838
    {
2839 2840
        ret = RtlAreBitsSet( NtCurrentTeb()->Peb->TlsBitmap, index, 1 );
        if (ret) RtlClearBits( NtCurrentTeb()->Peb->TlsBitmap, index, 1 );
2841
    }
2842
    if (ret) NtSetInformationThread( GetCurrentThread(), ThreadZeroTlsCell, &index, sizeof(index) );
2843
    else SetLastError( ERROR_INVALID_PARAMETER );
2844
    RtlReleasePebLock();
2845
    return ret;
2846 2847 2848 2849
}


/**********************************************************************
2850 2851 2852 2853 2854 2855
 * TlsGetValue          [KERNEL32.@]
 *
 * Gets value in a thread's TLS slot.
 *
 * PARAMS
 *    index [in] TLS index to retrieve value for.
2856 2857
 *
 * RETURNS
2858 2859
 *    Success: Value stored in calling thread's TLS slot for index.
 *    Failure: 0 and GetLastError() returns NO_ERROR.
2860
 */
2861
LPVOID WINAPI TlsGetValue( DWORD index )
2862
{
2863 2864 2865
    LPVOID ret;

    if (index < TLS_MINIMUM_AVAILABLE)
2866
    {
2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878
        ret = NtCurrentTeb()->TlsSlots[index];
    }
    else
    {
        index -= TLS_MINIMUM_AVAILABLE;
        if (index >= 8 * sizeof(NtCurrentTeb()->Peb->TlsExpansionBitmapBits))
        {
            SetLastError( ERROR_INVALID_PARAMETER );
            return NULL;
        }
        if (!NtCurrentTeb()->TlsExpansionSlots) ret = NULL;
        else ret = NtCurrentTeb()->TlsExpansionSlots[index];
2879 2880
    }
    SetLastError( ERROR_SUCCESS );
2881
    return ret;
2882 2883 2884 2885
}


/**********************************************************************
2886 2887 2888 2889 2890 2891 2892
 * TlsSetValue          [KERNEL32.@]
 *
 * Stores a value in the thread's TLS slot.
 *
 * PARAMS
 *    index [in] TLS index to set value for.
 *    value [in] Value to be stored.
2893 2894 2895 2896 2897
 *
 * RETURNS
 *    Success: TRUE
 *    Failure: FALSE
 */
2898
BOOL WINAPI TlsSetValue( DWORD index, LPVOID value )
2899
{
2900
    if (index < TLS_MINIMUM_AVAILABLE)
2901
    {
2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919
        NtCurrentTeb()->TlsSlots[index] = value;
    }
    else
    {
        index -= TLS_MINIMUM_AVAILABLE;
        if (index >= 8 * sizeof(NtCurrentTeb()->Peb->TlsExpansionBitmapBits))
        {
            SetLastError( ERROR_INVALID_PARAMETER );
            return FALSE;
        }
        if (!NtCurrentTeb()->TlsExpansionSlots &&
            !(NtCurrentTeb()->TlsExpansionSlots = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
                         8 * sizeof(NtCurrentTeb()->Peb->TlsExpansionBitmapBits) * sizeof(void*) )))
        {
            SetLastError( ERROR_NOT_ENOUGH_MEMORY );
            return FALSE;
        }
        NtCurrentTeb()->TlsExpansionSlots[index] = value;
2920 2921 2922
    }
    return TRUE;
}
2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947


/***********************************************************************
 *           GetProcessFlags    (KERNEL32.@)
 */
DWORD WINAPI GetProcessFlags( DWORD processid )
{
    IMAGE_NT_HEADERS *nt;
    DWORD flags = 0;

    if (processid && processid != GetCurrentProcessId()) return 0;

    if ((nt = RtlImageNtHeader( NtCurrentTeb()->Peb->ImageBaseAddress )))
    {
        if (nt->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI)
            flags |= PDB32_CONSOLE_PROC;
    }
    if (!AreFileApisANSI()) flags |= PDB32_FILE_APIS_OEM;
    if (IsDebuggerPresent()) flags |= PDB32_DEBUGGED;
    return flags;
}


/*********************************************************************
 *           OpenProcess   (KERNEL32.@)
2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958
 *
 * Opens a handle to a process.
 *
 * PARAMS
 *  access  [I] Desired access rights assigned to the returned handle.
 *  inherit [I] Determines whether or not child processes will inherit the handle.
 *  id      [I] Process identifier of the process to get a handle to.
 *
 * RETURNS
 *  Success: Valid handle to the specified process.
 *  Failure: NULL, check GetLastError().
2959 2960 2961
 */
HANDLE WINAPI OpenProcess( DWORD access, BOOL inherit, DWORD id )
{
2962 2963 2964 2965 2966
    NTSTATUS            status;
    HANDLE              handle;
    OBJECT_ATTRIBUTES   attr;
    CLIENT_ID           cid;

2967
    cid.UniqueProcess = ULongToHandle(id);
2968 2969 2970 2971 2972 2973 2974 2975 2976
    cid.UniqueThread = 0; /* FIXME ? */

    attr.Length = sizeof(OBJECT_ATTRIBUTES);
    attr.RootDirectory = NULL;
    attr.Attributes = inherit ? OBJ_INHERIT : 0;
    attr.SecurityDescriptor = NULL;
    attr.SecurityQualityOfService = NULL;
    attr.ObjectName = NULL;

2977 2978
    if (GetVersion() & 0x80000000) access = PROCESS_ALL_ACCESS;

2979 2980
    status = NtOpenProcess(&handle, access, &attr, &cid);
    if (status != STATUS_SUCCESS)
2981
    {
2982 2983
        SetLastError( RtlNtStatusToDosError(status) );
        return NULL;
2984
    }
2985
    return handle;
2986 2987 2988 2989
}


/*********************************************************************
Robert Shearman's avatar
Robert Shearman committed
2990
 *           GetProcessId       (KERNEL32.@)
2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004
 *
 * Gets the a unique identifier of a process.
 *
 * PARAMS
 *  hProcess [I] Handle to the process.
 *
 * RETURNS
 *  Success: TRUE.
 *  Failure: FALSE, check GetLastError().
 *
 * NOTES
 *
 * The identifier is unique only on the machine and only until the process
 * exits (including system shutdown).
3005
 */
Robert Shearman's avatar
Robert Shearman committed
3006
DWORD WINAPI GetProcessId( HANDLE hProcess )
3007
{
3008 3009 3010 3011 3012 3013 3014 3015
    NTSTATUS status;
    PROCESS_BASIC_INFORMATION pbi;

    status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi,
                                       sizeof(pbi), NULL);
    if (status == STATUS_SUCCESS) return pbi.UniqueProcessId;
    SetLastError( RtlNtStatusToDosError(status) );
    return 0;
3016 3017 3018
}


3019 3020
/*********************************************************************
 *           CloseHandle    (KERNEL32.@)
3021 3022 3023 3024 3025 3026 3027 3028 3029
 *
 * Closes a handle.
 *
 * PARAMS
 *  handle [I] Handle to close.
 *
 * RETURNS
 *  Success: TRUE.
 *  Failure: FALSE, check GetLastError().
3030 3031 3032 3033 3034 3035
 */
BOOL WINAPI CloseHandle( HANDLE handle )
{
    NTSTATUS status;

    /* stdio handles need special treatment */
3036 3037 3038 3039 3040 3041
    if (handle == (HANDLE)STD_INPUT_HANDLE)
        handle = InterlockedExchangePointer( &NtCurrentTeb()->Peb->ProcessParameters->hStdInput, 0 );
    else if (handle == (HANDLE)STD_OUTPUT_HANDLE)
        handle = InterlockedExchangePointer( &NtCurrentTeb()->Peb->ProcessParameters->hStdOutput, 0 );
    else if (handle == (HANDLE)STD_ERROR_HANDLE)
        handle = InterlockedExchangePointer( &NtCurrentTeb()->Peb->ProcessParameters->hStdError, 0 );
3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056

    if (is_console_handle(handle))
        return CloseConsoleHandle(handle);

    status = NtClose( handle );
    if (status) SetLastError( RtlNtStatusToDosError(status) );
    return !status;
}


/*********************************************************************
 *           GetHandleInformation   (KERNEL32.@)
 */
BOOL WINAPI GetHandleInformation( HANDLE handle, LPDWORD flags )
{
3057 3058 3059 3060 3061
    OBJECT_DATA_INFORMATION info;
    NTSTATUS status = NtQueryObject( handle, ObjectDataInformation, &info, sizeof(info), NULL );

    if (status) SetLastError( RtlNtStatusToDosError(status) );
    else if (flags)
3062
    {
3063 3064 3065
        *flags = 0;
        if (info.InheritHandle) *flags |= HANDLE_FLAG_INHERIT;
        if (info.ProtectFromClose) *flags |= HANDLE_FLAG_PROTECT_FROM_CLOSE;
3066
    }
3067
    return !status;
3068 3069 3070 3071 3072 3073 3074 3075
}


/*********************************************************************
 *           SetHandleInformation   (KERNEL32.@)
 */
BOOL WINAPI SetHandleInformation( HANDLE handle, DWORD mask, DWORD flags )
{
3076 3077 3078 3079 3080 3081
    OBJECT_DATA_INFORMATION info;
    NTSTATUS status;

    /* if not setting both fields, retrieve current value first */
    if ((mask & (HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE)) !=
        (HANDLE_FLAG_INHERIT | HANDLE_FLAG_PROTECT_FROM_CLOSE))
3082
    {
3083 3084 3085 3086 3087
        if ((status = NtQueryObject( handle, ObjectDataInformation, &info, sizeof(info), NULL )))
        {
            SetLastError( RtlNtStatusToDosError(status) );
            return FALSE;
        }
3088
    }
3089 3090 3091 3092 3093 3094 3095 3096
    if (mask & HANDLE_FLAG_INHERIT)
        info.InheritHandle = (flags & HANDLE_FLAG_INHERIT) != 0;
    if (mask & HANDLE_FLAG_PROTECT_FROM_CLOSE)
        info.ProtectFromClose = (flags & HANDLE_FLAG_PROTECT_FROM_CLOSE) != 0;

    status = NtSetInformationObject( handle, ObjectDataInformation, &info, sizeof(info) );
    if (status) SetLastError( RtlNtStatusToDosError(status) );
    return !status;
3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144
}


/*********************************************************************
 *           DuplicateHandle   (KERNEL32.@)
 */
BOOL WINAPI DuplicateHandle( HANDLE source_process, HANDLE source,
                             HANDLE dest_process, HANDLE *dest,
                             DWORD access, BOOL inherit, DWORD options )
{
    NTSTATUS status;

    if (is_console_handle(source))
    {
        /* FIXME: this test is not sufficient, we need to test process ids, not handles */
        if (source_process != dest_process ||
            source_process != GetCurrentProcess())
        {
            SetLastError(ERROR_INVALID_PARAMETER);
            return FALSE;
        }
        *dest = DuplicateConsoleHandle( source, access, inherit, options );
        return (*dest != INVALID_HANDLE_VALUE);
    }
    status = NtDuplicateObject( source_process, source, dest_process, dest,
                                access, inherit ? OBJ_INHERIT : 0, options );
    if (status) SetLastError( RtlNtStatusToDosError(status) );
    return !status;
}


/***********************************************************************
 *           ConvertToGlobalHandle  (KERNEL32.@)
 */
HANDLE WINAPI ConvertToGlobalHandle(HANDLE hSrc)
{
    HANDLE ret = INVALID_HANDLE_VALUE;
    DuplicateHandle( GetCurrentProcess(), hSrc, GetCurrentProcess(), &ret, 0, FALSE,
                     DUP_HANDLE_MAKE_GLOBAL | DUP_HANDLE_SAME_ACCESS | DUP_HANDLE_CLOSE_SOURCE );
    return ret;
}


/***********************************************************************
 *           SetHandleContext   (KERNEL32.@)
 */
BOOL WINAPI SetHandleContext(HANDLE hnd,DWORD context)
{
3145
    FIXME("(%p,%d), stub. In case this got called by WSOCK32/WS2_32: "
3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175
          "the external WINSOCK DLLs won't work with WINE, don't use them.\n",hnd,context);
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return FALSE;
}


/***********************************************************************
 *           GetHandleContext   (KERNEL32.@)
 */
DWORD WINAPI GetHandleContext(HANDLE hnd)
{
    FIXME("(%p), stub. In case this got called by WSOCK32/WS2_32: "
          "the external WINSOCK DLLs won't work with WINE, don't use them.\n",hnd);
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return 0;
}


/***********************************************************************
 *           CreateSocketHandle   (KERNEL32.@)
 */
HANDLE WINAPI CreateSocketHandle(void)
{
    FIXME("(), stub. In case this got called by WSOCK32/WS2_32: "
          "the external WINSOCK DLLs won't work with WINE, don't use them.\n");
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return INVALID_HANDLE_VALUE;
}


3176 3177 3178 3179 3180
/***********************************************************************
 *           SetPriorityClass   (KERNEL32.@)
 */
BOOL WINAPI SetPriorityClass( HANDLE hprocess, DWORD priorityclass )
{
3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207
    NTSTATUS                    status;
    PROCESS_PRIORITY_CLASS      ppc;

    ppc.Foreground = FALSE;
    switch (priorityclass)
    {
    case IDLE_PRIORITY_CLASS:
        ppc.PriorityClass = PROCESS_PRIOCLASS_IDLE; break;
    case BELOW_NORMAL_PRIORITY_CLASS:
        ppc.PriorityClass = PROCESS_PRIOCLASS_BELOW_NORMAL; break;
    case NORMAL_PRIORITY_CLASS:
        ppc.PriorityClass = PROCESS_PRIOCLASS_NORMAL; break;
    case ABOVE_NORMAL_PRIORITY_CLASS:
        ppc.PriorityClass = PROCESS_PRIOCLASS_ABOVE_NORMAL; break;
    case HIGH_PRIORITY_CLASS:
        ppc.PriorityClass = PROCESS_PRIOCLASS_HIGH; break;
    case REALTIME_PRIORITY_CLASS:
        ppc.PriorityClass = PROCESS_PRIOCLASS_REALTIME; break;
    default:
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    status = NtSetInformationProcess(hprocess, ProcessPriorityClass,
                                     &ppc, sizeof(ppc));

    if (status != STATUS_SUCCESS)
3208
    {
3209 3210
        SetLastError( RtlNtStatusToDosError(status) );
        return FALSE;
3211
    }
3212
    return TRUE;
3213 3214 3215 3216 3217 3218
}


/***********************************************************************
 *           GetPriorityClass   (KERNEL32.@)
 */
3219
DWORD WINAPI GetPriorityClass(HANDLE hProcess)
3220
{
3221 3222 3223 3224 3225
    NTSTATUS status;
    PROCESS_BASIC_INFORMATION pbi;

    status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi,
                                       sizeof(pbi), NULL);
3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240
    if (status != STATUS_SUCCESS)
    {
        SetLastError( RtlNtStatusToDosError(status) );
        return 0;
    }
    switch (pbi.BasePriority)
    {
    case PROCESS_PRIOCLASS_IDLE: return IDLE_PRIORITY_CLASS;
    case PROCESS_PRIOCLASS_BELOW_NORMAL: return BELOW_NORMAL_PRIORITY_CLASS;
    case PROCESS_PRIOCLASS_NORMAL: return NORMAL_PRIORITY_CLASS;
    case PROCESS_PRIOCLASS_ABOVE_NORMAL: return ABOVE_NORMAL_PRIORITY_CLASS;
    case PROCESS_PRIOCLASS_HIGH: return HIGH_PRIORITY_CLASS;
    case PROCESS_PRIOCLASS_REALTIME: return REALTIME_PRIORITY_CLASS;
    }
    SetLastError( ERROR_INVALID_PARAMETER );
3241
    return 0;
3242 3243 3244 3245 3246 3247
}


/***********************************************************************
 *          SetProcessAffinityMask   (KERNEL32.@)
 */
3248
BOOL WINAPI SetProcessAffinityMask( HANDLE hProcess, DWORD_PTR affmask )
3249
{
3250 3251 3252 3253
    NTSTATUS status;

    status = NtSetInformationProcess(hProcess, ProcessAffinityMask,
                                     &affmask, sizeof(DWORD_PTR));
3254
    if (status)
3255
    {
3256 3257
        SetLastError( RtlNtStatusToDosError(status) );
        return FALSE;
3258
    }
3259
    return TRUE;
3260 3261 3262 3263 3264 3265
}


/**********************************************************************
 *          GetProcessAffinityMask    (KERNEL32.@)
 */
3266
BOOL WINAPI GetProcessAffinityMask( HANDLE hProcess, PDWORD_PTR process_mask, PDWORD_PTR system_mask )
3267
{
3268
    NTSTATUS status = STATUS_SUCCESS;
3269

3270
    if (process_mask)
3271
    {
3272 3273 3274
        if ((status = NtQueryInformationProcess( hProcess, ProcessAffinityMask,
                                                 process_mask, sizeof(*process_mask), NULL )))
            SetLastError( RtlNtStatusToDosError(status) );
3275
    }
3276 3277 3278 3279 3280 3281 3282 3283 3284
    if (system_mask && status == STATUS_SUCCESS)
    {
        SYSTEM_BASIC_INFORMATION info;

        if ((status = NtQuerySystemInformation( SystemBasicInformation, &info, sizeof(info), NULL )))
            SetLastError( RtlNtStatusToDosError(status) );
        else
            *system_mask = info.ActiveProcessorsAffinityMask;
    }
3285
    return !status;
3286 3287 3288 3289 3290 3291
}


/***********************************************************************
 *           GetProcessVersion    (KERNEL32.@)
 */
3292
DWORD WINAPI GetProcessVersion( DWORD pid )
3293
{
3294 3295 3296 3297 3298 3299 3300 3301
    HANDLE process;
    NTSTATUS status;
    PROCESS_BASIC_INFORMATION pbi;
    SIZE_T count;
    PEB peb;
    IMAGE_DOS_HEADER dos;
    IMAGE_NT_HEADERS nt;
    DWORD ver = 0;
3302

3303
    if (!pid || pid == GetCurrentProcessId())
3304
    {
3305
        IMAGE_NT_HEADERS *pnt;
3306

3307 3308 3309
        if ((pnt = RtlImageNtHeader( NtCurrentTeb()->Peb->ImageBaseAddress )))
            return ((pnt->OptionalHeader.MajorSubsystemVersion << 16) |
                    pnt->OptionalHeader.MinorSubsystemVersion);
3310 3311
        return 0;
    }
3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340

    process = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, pid);
    if (!process) return 0;

    status = NtQueryInformationProcess(process, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
    if (status) goto err;

    status = NtReadVirtualMemory(process, pbi.PebBaseAddress, &peb, sizeof(peb), &count);
    if (status || count != sizeof(peb)) goto err;

    memset(&dos, 0, sizeof(dos));
    status = NtReadVirtualMemory(process, peb.ImageBaseAddress, &dos, sizeof(dos), &count);
    if (status || count != sizeof(dos)) goto err;
    if (dos.e_magic != IMAGE_DOS_SIGNATURE) goto err;

    memset(&nt, 0, sizeof(nt));
    status = NtReadVirtualMemory(process, (char *)peb.ImageBaseAddress + dos.e_lfanew, &nt, sizeof(nt), &count);
    if (status || count != sizeof(nt)) goto err;
    if (nt.Signature != IMAGE_NT_SIGNATURE) goto err;

    ver = MAKELONG(nt.OptionalHeader.MinorSubsystemVersion, nt.OptionalHeader.MajorSubsystemVersion);

err:
    CloseHandle(process);

    if (status != STATUS_SUCCESS)
        SetLastError(RtlNtStatusToDosError(status));

    return ver;
3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352
}


/***********************************************************************
 *		SetProcessWorkingSetSize	[KERNEL32.@]
 * 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
 *
Jon Griffiths's avatar
Jon Griffiths committed
3353 3354 3355
 * RETURNS
 *  Success: TRUE
 *  Failure: FALSE
3356 3357 3358 3359
 */
BOOL WINAPI SetProcessWorkingSetSize(HANDLE hProcess, SIZE_T minset,
                                     SIZE_T maxset)
{
3360
    WARN("(%p,%ld,%ld): stub - harmless\n",hProcess,minset,maxset);
3361 3362 3363 3364 3365 3366 3367
    if(( minset == (SIZE_T)-1) && (maxset == (SIZE_T)-1)) {
        /* Trim the working set to zero */
        /* Swap the process out of physical RAM */
    }
    return TRUE;
}

3368 3369 3370 3371 3372 3373 3374 3375
/***********************************************************************
 *           K32EmptyWorkingSet (KERNEL32.@)
 */
BOOL WINAPI K32EmptyWorkingSet(HANDLE hProcess)
{
    return SetProcessWorkingSetSize(hProcess, (SIZE_T)-1, (SIZE_T)-1);
}

3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394
/***********************************************************************
 *           GetProcessWorkingSetSize    (KERNEL32.@)
 */
BOOL WINAPI GetProcessWorkingSetSize(HANDLE hProcess, PSIZE_T minset,
                                     PSIZE_T maxset)
{
    FIXME("(%p,%p,%p): stub\n",hProcess,minset,maxset);
    /* 32 MB working set size */
    if (minset) *minset = 32*1024*1024;
    if (maxset) *maxset = 32*1024*1024;
    return TRUE;
}


/***********************************************************************
 *           SetProcessShutdownParameters    (KERNEL32.@)
 */
BOOL WINAPI SetProcessShutdownParameters(DWORD level, DWORD flags)
{
3395
    FIXME("(%08x, %08x): partial stub.\n", level, flags);
3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443
    shutdown_flags = flags;
    shutdown_priority = level;
    return TRUE;
}


/***********************************************************************
 * GetProcessShutdownParameters                 (KERNEL32.@)
 *
 */
BOOL WINAPI GetProcessShutdownParameters( LPDWORD lpdwLevel, LPDWORD lpdwFlags )
{
    *lpdwLevel = shutdown_priority;
    *lpdwFlags = shutdown_flags;
    return TRUE;
}


/***********************************************************************
 *           GetProcessPriorityBoost    (KERNEL32.@)
 */
BOOL WINAPI GetProcessPriorityBoost(HANDLE hprocess,PBOOL pDisablePriorityBoost)
{
    FIXME("(%p,%p): semi-stub\n", hprocess, pDisablePriorityBoost);
    
    /* Report that no boost is present.. */
    *pDisablePriorityBoost = FALSE;
    
    return TRUE;
}

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


/***********************************************************************
 *		ReadProcessMemory (KERNEL32.@)
 */
BOOL WINAPI ReadProcessMemory( HANDLE process, LPCVOID addr, LPVOID buffer, SIZE_T size,
                               SIZE_T *bytes_read )
{
3444 3445 3446
    NTSTATUS status = NtReadVirtualMemory( process, addr, buffer, size, bytes_read );
    if (status) SetLastError( RtlNtStatusToDosError(status) );
    return !status;
3447 3448 3449 3450 3451 3452 3453 3454 3455
}


/***********************************************************************
 *           WriteProcessMemory    		(KERNEL32.@)
 */
BOOL WINAPI WriteProcessMemory( HANDLE process, LPVOID addr, LPCVOID buffer, SIZE_T size,
                                SIZE_T *bytes_written )
{
3456 3457 3458
    NTSTATUS status = NtWriteVirtualMemory( process, addr, buffer, size, bytes_written );
    if (status) SetLastError( RtlNtStatusToDosError(status) );
    return !status;
3459 3460
}

3461 3462 3463 3464 3465 3466

/****************************************************************************
 *		FlushInstructionCache (KERNEL32.@)
 */
BOOL WINAPI FlushInstructionCache(HANDLE hProcess, LPCVOID lpBaseAddress, SIZE_T dwSize)
{
3467 3468 3469 3470
    NTSTATUS status;
    status = NtFlushInstructionCache( hProcess, lpBaseAddress, dwSize );
    if (status) SetLastError( RtlNtStatusToDosError(status) );
    return !status;
3471 3472 3473
}


3474 3475 3476 3477 3478 3479 3480 3481 3482 3483
/******************************************************************
 *		GetProcessIoCounters (KERNEL32.@)
 */
BOOL WINAPI GetProcessIoCounters(HANDLE hProcess, PIO_COUNTERS ioc)
{
    NTSTATUS    status;

    status = NtQueryInformationProcess(hProcess, ProcessIoCounters, 
                                       ioc, sizeof(*ioc), NULL);
    if (status) SetLastError( RtlNtStatusToDosError(status) );
3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496
    return !status;
}

/******************************************************************
 *		GetProcessHandleCount (KERNEL32.@)
 */
BOOL WINAPI GetProcessHandleCount(HANDLE hProcess, DWORD *cnt)
{
    NTSTATUS status;

    status = NtQueryInformationProcess(hProcess, ProcessHandleCount,
                                       cnt, sizeof(*cnt), NULL);
    if (status) SetLastError( RtlNtStatusToDosError(status) );
3497 3498
    return !status;
}
3499

3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520
/******************************************************************
 *		QueryFullProcessImageNameA (KERNEL32.@)
 */
BOOL WINAPI QueryFullProcessImageNameA(HANDLE hProcess, DWORD dwFlags, LPSTR lpExeName, PDWORD pdwSize)
{
    BOOL retval;
    DWORD pdwSizeW = *pdwSize;
    LPWSTR lpExeNameW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *pdwSize * sizeof(WCHAR));

    retval = QueryFullProcessImageNameW(hProcess, dwFlags, lpExeNameW, &pdwSizeW);

    if(retval)
        retval = (0 != WideCharToMultiByte(CP_ACP, 0, lpExeNameW, -1,
                               lpExeName, *pdwSize, NULL, NULL));
    if(retval)
        *pdwSize = strlen(lpExeName);

    HeapFree(GetProcessHeap(), 0, lpExeNameW);
    return retval;
}

3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531
/******************************************************************
 *		QueryFullProcessImageNameW (KERNEL32.@)
 */
BOOL WINAPI QueryFullProcessImageNameW(HANDLE hProcess, DWORD dwFlags, LPWSTR lpExeName, PDWORD pdwSize)
{
    BYTE buffer[sizeof(UNICODE_STRING) + MAX_PATH*sizeof(WCHAR)];  /* this buffer should be enough */
    UNICODE_STRING *dynamic_buffer = NULL;
    UNICODE_STRING *result = NULL;
    NTSTATUS status;
    DWORD needed;

3532 3533
    /* FIXME: On Windows, ProcessImageFileName return an NT path. In Wine it
     * is a DOS path and we depend on this. */
3534 3535
    status = NtQueryInformationProcess(hProcess, ProcessImageFileName, buffer,
                                       sizeof(buffer) - sizeof(WCHAR), &needed);
3536 3537
    if (status == STATUS_INFO_LENGTH_MISMATCH)
    {
3538
        dynamic_buffer = HeapAlloc(GetProcessHeap(), 0, needed + sizeof(WCHAR));
3539 3540 3541 3542 3543 3544 3545 3546 3547 3548
        status = NtQueryInformationProcess(hProcess, ProcessImageFileName, (LPBYTE)dynamic_buffer, needed, &needed);
        result = dynamic_buffer;
    }
    else
        result = (PUNICODE_STRING)buffer;

    if (status) goto cleanup;

    if (dwFlags & PROCESS_NAME_NATIVE)
    {
3549 3550 3551 3552 3553
        WCHAR drive[3];
        WCHAR device[1024];
        DWORD ntlen, devlen;

        if (result->Buffer[1] != ':' || result->Buffer[0] < 'A' || result->Buffer[0] > 'Z')
3554
        {
3555 3556
            /* We cannot convert it to an NT device path so fail */
            status = STATUS_NO_SUCH_DEVICE;
3557 3558 3559
            goto cleanup;
        }

3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573
        /* Find this drive's NT device path */
        drive[0] = result->Buffer[0];
        drive[1] = ':';
        drive[2] = 0;
        if (!QueryDosDeviceW(drive, device, sizeof(device)/sizeof(*device)))
        {
            status = STATUS_NO_SUCH_DEVICE;
            goto cleanup;
        }

        devlen = lstrlenW(device);
        ntlen = devlen + (result->Length/sizeof(WCHAR) - 2);
        if (ntlen + 1 > *pdwSize)
        {
3574 3575
            status = STATUS_BUFFER_TOO_SMALL;
            goto cleanup;
3576 3577 3578 3579 3580 3581 3582
        }
        *pdwSize = ntlen;

        memcpy(lpExeName, device, devlen * sizeof(*device));
        memcpy(lpExeName + devlen, result->Buffer + 2, result->Length - 2 * sizeof(WCHAR));
        lpExeName[*pdwSize] = 0;
        TRACE("NT path: %s\n", debugstr_w(lpExeName));
3583
    }
3584 3585 3586 3587 3588 3589 3590
    else
    {
        if (result->Length/sizeof(WCHAR) + 1 > *pdwSize)
        {
            status = STATUS_BUFFER_TOO_SMALL;
            goto cleanup;
        }
3591

3592 3593 3594 3595
        *pdwSize = result->Length/sizeof(WCHAR);
        memcpy( lpExeName, result->Buffer, result->Length );
        lpExeName[*pdwSize] = 0;
    }
3596 3597 3598 3599 3600 3601 3602

cleanup:
    HeapFree(GetProcessHeap(), 0, dynamic_buffer);
    if (status) SetLastError( RtlNtStatusToDosError(status) );
    return !status;
}

3603 3604 3605 3606 3607
/***********************************************************************
 *           K32GetProcessImageFileNameA (KERNEL32.@)
 */
DWORD WINAPI K32GetProcessImageFileNameA( HANDLE process, LPSTR file, DWORD size )
{
3608
    return QueryFullProcessImageNameA(process, PROCESS_NAME_NATIVE, file, &size) ? size : 0;
3609 3610 3611 3612 3613 3614 3615 3616 3617 3618
}

/***********************************************************************
 *           K32GetProcessImageFileNameW (KERNEL32.@)
 */
DWORD WINAPI K32GetProcessImageFileNameW( HANDLE process, LPWSTR file, DWORD size )
{
    return QueryFullProcessImageNameW(process, PROCESS_NAME_NATIVE, file, &size) ? size : 0;
}

3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662
/***********************************************************************
 *           K32EnumProcesses (KERNEL32.@)
 */
BOOL WINAPI K32EnumProcesses(DWORD *lpdwProcessIDs, DWORD cb, DWORD *lpcbUsed)
{
    SYSTEM_PROCESS_INFORMATION *spi;
    ULONG size = 0x4000;
    void *buf = NULL;
    NTSTATUS status;

    do {
        size *= 2;
        HeapFree(GetProcessHeap(), 0, buf);
        buf = HeapAlloc(GetProcessHeap(), 0, size);
        if (!buf)
            return FALSE;

        status = NtQuerySystemInformation(SystemProcessInformation, buf, size, NULL);
    } while(status == STATUS_INFO_LENGTH_MISMATCH);

    if (status != STATUS_SUCCESS)
    {
        HeapFree(GetProcessHeap(), 0, buf);
        SetLastError(RtlNtStatusToDosError(status));
        return FALSE;
    }

    spi = buf;

    for (*lpcbUsed = 0; cb >= sizeof(DWORD); cb -= sizeof(DWORD))
    {
        *lpdwProcessIDs++ = HandleToUlong(spi->UniqueProcessId);
        *lpcbUsed += sizeof(DWORD);

        if (spi->NextEntryOffset == 0)
            break;

        spi = (SYSTEM_PROCESS_INFORMATION *)(((PCHAR)spi) + spi->NextEntryOffset);
    }

    HeapFree(GetProcessHeap(), 0, buf);
    return TRUE;
}

3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700
/***********************************************************************
 *           K32QueryWorkingSet (KERNEL32.@)
 */
BOOL WINAPI K32QueryWorkingSet( HANDLE process, LPVOID buffer, DWORD size )
{
    NTSTATUS status;

    TRACE( "(%p, %p, %d)\n", process, buffer, size );

    status = NtQueryVirtualMemory( process, NULL, MemoryWorkingSetList, buffer, size, NULL );

    if (status)
    {
        SetLastError( RtlNtStatusToDosError( status ) );
        return FALSE;
    }
    return TRUE;
}

/***********************************************************************
 *           K32QueryWorkingSetEx (KERNEL32.@)
 */
BOOL WINAPI K32QueryWorkingSetEx( HANDLE process, LPVOID buffer, DWORD size )
{
    NTSTATUS status;

    TRACE( "(%p, %p, %d)\n", process, buffer, size );

    status = NtQueryVirtualMemory( process, NULL, MemoryWorkingSetList, buffer,  size, NULL );

    if (status)
    {
        SetLastError( RtlNtStatusToDosError( status ) );
        return FALSE;
    }
    return TRUE;
}

3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741
/***********************************************************************
 *           K32GetProcessMemoryInfo (KERNEL32.@)
 *
 * Retrieve memory usage information for a given process
 *
 */
BOOL WINAPI K32GetProcessMemoryInfo(HANDLE process,
                                    PPROCESS_MEMORY_COUNTERS pmc, DWORD cb)
{
    NTSTATUS status;
    VM_COUNTERS vmc;

    if (cb < sizeof(PROCESS_MEMORY_COUNTERS))
    {
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        return FALSE;
    }

    status = NtQueryInformationProcess(process, ProcessVmCounters,
                                       &vmc, sizeof(vmc), NULL);

    if (status)
    {
        SetLastError(RtlNtStatusToDosError(status));
        return FALSE;
    }

    pmc->cb = sizeof(PROCESS_MEMORY_COUNTERS);
    pmc->PageFaultCount = vmc.PageFaultCount;
    pmc->PeakWorkingSetSize = vmc.PeakWorkingSetSize;
    pmc->WorkingSetSize = vmc.WorkingSetSize;
    pmc->QuotaPeakPagedPoolUsage = vmc.QuotaPeakPagedPoolUsage;
    pmc->QuotaPagedPoolUsage = vmc.QuotaPagedPoolUsage;
    pmc->QuotaPeakNonPagedPoolUsage = vmc.QuotaPeakNonPagedPoolUsage;
    pmc->QuotaNonPagedPoolUsage = vmc.QuotaNonPagedPoolUsage;
    pmc->PagefileUsage = vmc.PagefileUsage;
    pmc->PeakPagefileUsage = vmc.PeakPagefileUsage;

    return TRUE;
}

3742 3743 3744 3745 3746 3747
/***********************************************************************
 * ProcessIdToSessionId   (KERNEL32.@)
 * This function is available on Terminal Server 4SP4 and Windows 2000
 */
BOOL WINAPI ProcessIdToSessionId( DWORD procid, DWORD *sessionid_ptr )
{
3748 3749 3750 3751
    if (procid != GetCurrentProcessId())
        FIXME("Unsupported for other processes.\n");

    *sessionid_ptr = NtCurrentTeb()->Peb->SessionId;
3752 3753 3754 3755
    return TRUE;
}


3756 3757 3758 3759 3760 3761 3762 3763
/***********************************************************************
 *		RegisterServiceProcess (KERNEL32.@)
 *
 * A service process calls this function to ensure that it continues to run
 * even after a user logged off.
 */
DWORD WINAPI RegisterServiceProcess(DWORD dwProcessId, DWORD dwType)
{
Jon Griffiths's avatar
Jon Griffiths committed
3764
    /* I don't think that Wine needs to do anything in this function */
3765 3766 3767 3768
    return 1; /* success */
}


3769 3770 3771 3772 3773
/**********************************************************************
 *           IsWow64Process         (KERNEL32.@)
 */
BOOL WINAPI IsWow64Process(HANDLE hProcess, PBOOL Wow64Process)
{
3774
    ULONG_PTR pbi;
3775 3776 3777 3778 3779 3780 3781 3782 3783 3784
    NTSTATUS status;

    status = NtQueryInformationProcess( hProcess, ProcessWow64Information, &pbi, sizeof(pbi), NULL );

    if (status != STATUS_SUCCESS)
    {
        SetLastError( RtlNtStatusToDosError( status ) );
        return FALSE;
    }
    *Wow64Process = (pbi != 0);
3785 3786 3787 3788
    return TRUE;
}


3789 3790
/***********************************************************************
 *           GetCurrentProcess   (KERNEL32.@)
Jon Griffiths's avatar
Jon Griffiths committed
3791 3792 3793 3794 3795 3796 3797 3798
 *
 * Get a handle to the current process.
 *
 * PARAMS
 *  None.
 *
 * RETURNS
 *  A handle representing the current process.
3799 3800 3801 3802
 */
#undef GetCurrentProcess
HANDLE WINAPI GetCurrentProcess(void)
{
3803
    return (HANDLE)~(ULONG_PTR)0;
3804
}
3805

3806 3807 3808 3809 3810
/***********************************************************************
 *           GetLogicalProcessorInformation     (KERNEL32.@)
 */
BOOL WINAPI GetLogicalProcessorInformation(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer, PDWORD pBufLen)
{
3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833
    NTSTATUS status;

    TRACE("(%p,%p)\n", buffer, pBufLen);

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

    status = NtQuerySystemInformation( SystemLogicalProcessorInformation, buffer, *pBufLen, pBufLen);

    if (status == STATUS_INFO_LENGTH_MISMATCH)
    {
        SetLastError( ERROR_INSUFFICIENT_BUFFER );
        return FALSE;
    }
    if (status != STATUS_SUCCESS)
    {
        SetLastError( RtlNtStatusToDosError( status ) );
        return FALSE;
    }
    return TRUE;
3834 3835 3836 3837 3838
}

/***********************************************************************
 *           GetLogicalProcessorInformationEx   (KERNEL32.@)
 */
3839
BOOL WINAPI GetLogicalProcessorInformationEx(LOGICAL_PROCESSOR_RELATIONSHIP relationship, SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *buffer, DWORD *len)
3840
{
3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858
    NTSTATUS status;

    TRACE("(%u,%p,%p)\n", relationship, buffer, len);

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

    status = NtQuerySystemInformationEx( SystemLogicalProcessorInformationEx, &relationship, sizeof(relationship),
        buffer, *len, len );
    if (status != STATUS_SUCCESS)
    {
        SetLastError( RtlNtStatusToDosError( status ) );
        return FALSE;
    }
    return TRUE;
3859 3860
}

3861 3862 3863
/***********************************************************************
 *           CmdBatNotification   (KERNEL32.@)
 *
3864 3865 3866 3867 3868 3869 3870 3871
 * Notifies the system that a batch file has started or finished.
 *
 * PARAMS
 *  bBatchRunning [I]  TRUE if a batch file has started or 
 *                     FALSE if a batch file has finished executing.
 *
 * RETURNS
 *  Unknown.
3872 3873 3874 3875 3876 3877
 */
BOOL WINAPI CmdBatNotification( BOOL bBatchRunning )
{
    FIXME("%d\n", bBatchRunning);
    return FALSE;
}
3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888


/***********************************************************************
 *           RegisterApplicationRestart       (KERNEL32.@)
 */
HRESULT WINAPI RegisterApplicationRestart(PCWSTR pwzCommandLine, DWORD dwFlags)
{
    FIXME("(%s,%d)\n", debugstr_w(pwzCommandLine), dwFlags);

    return S_OK;
}
3889 3890 3891 3892 3893 3894

/**********************************************************************
 *           WTSGetActiveConsoleSessionId     (KERNEL32.@)
 */
DWORD WINAPI WTSGetActiveConsoleSessionId(void)
{
3895 3896
    static int once;
    if (!once++) FIXME("stub\n");
3897 3898
    /* Return current session id. */
    return NtCurrentTeb()->Peb->SessionId;
3899
}
3900 3901 3902 3903 3904 3905 3906 3907 3908

/**********************************************************************
 *           GetSystemDEPPolicy     (KERNEL32.@)
 */
DEP_SYSTEM_POLICY_TYPE WINAPI GetSystemDEPPolicy(void)
{
    FIXME("stub\n");
    return OptIn;
}
3909

3910 3911 3912 3913 3914 3915 3916 3917 3918
/**********************************************************************
 *           SetProcessDEPPolicy     (KERNEL32.@)
 */
BOOL WINAPI SetProcessDEPPolicy(DWORD newDEP)
{
    FIXME("(%d): stub\n", newDEP);
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return FALSE;
}
3919 3920 3921 3922 3923 3924 3925 3926 3927

/**********************************************************************
 *           ApplicationRecoveryFinished     (KERNEL32.@)
 */
VOID WINAPI ApplicationRecoveryFinished(BOOL success)
{
    FIXME(": stub\n");
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
}
3928 3929 3930 3931 3932 3933 3934 3935 3936 3937

/**********************************************************************
 *           ApplicationRecoveryInProgress     (KERNEL32.@)
 */
HRESULT WINAPI ApplicationRecoveryInProgress(PBOOL canceled)
{
    FIXME(":%p stub\n", canceled);
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return E_FAIL;
}
3938 3939 3940 3941 3942 3943 3944 3945 3946 3947

/**********************************************************************
 *           RegisterApplicationRecoveryCallback     (KERNEL32.@)
 */
HRESULT WINAPI RegisterApplicationRecoveryCallback(APPLICATION_RECOVERY_CALLBACK callback, PVOID param, DWORD pingint, DWORD flags)
{
    FIXME("%p, %p, %d, %d: stub\n", callback, param, pingint, flags);
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return E_FAIL;
}
3948 3949 3950 3951 3952 3953

/**********************************************************************
 *           GetNumaHighestNodeNumber     (KERNEL32.@)
 */
BOOL WINAPI GetNumaHighestNodeNumber(PULONG highestnode)
{
3954 3955 3956
    *highestnode = 0;
    FIXME("(%p): semi-stub\n", highestnode);
    return TRUE;
3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977
}

/**********************************************************************
 *           GetNumaNodeProcessorMask     (KERNEL32.@)
 */
BOOL WINAPI GetNumaNodeProcessorMask(UCHAR node, PULONGLONG mask)
{
    FIXME("(%c %p): stub\n", node, mask);
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return FALSE;
}

/**********************************************************************
 *           GetNumaAvailableMemoryNode     (KERNEL32.@)
 */
BOOL WINAPI GetNumaAvailableMemoryNode(UCHAR node, PULONGLONG available_bytes)
{
    FIXME("(%c %p): stub\n", node, available_bytes);
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return FALSE;
}
3978

3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999
/***********************************************************************
 *           GetNumaProcessorNode (KERNEL32.@)
 */
BOOL WINAPI GetNumaProcessorNode(UCHAR processor, PUCHAR node)
{
    SYSTEM_INFO si;

    TRACE("(%d, %p)\n", processor, node);

    GetSystemInfo( &si );
    if (processor < si.dwNumberOfProcessors)
    {
        *node = 0;
        return TRUE;
    }

    *node = 0xFF;
    SetLastError(ERROR_INVALID_PARAMETER);
    return FALSE;
}

4000 4001 4002 4003 4004
/**********************************************************************
 *           GetProcessDEPPolicy     (KERNEL32.@)
 */
BOOL WINAPI GetProcessDEPPolicy(HANDLE process, LPDWORD flags, PBOOL permanent)
{
4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029
    NTSTATUS status;
    ULONG dep_flags;

    TRACE("(%p %p %p)\n", process, flags, permanent);

    status = NtQueryInformationProcess( GetCurrentProcess(), ProcessExecuteFlags,
                                        &dep_flags, sizeof(dep_flags), NULL );
    if (!status)
    {

        if (flags)
        {
            *flags = 0;
            if (dep_flags & MEM_EXECUTE_OPTION_DISABLE)
                *flags |= PROCESS_DEP_ENABLE;
            if (dep_flags & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)
                *flags |= PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION;
        }

        if (permanent)
            *permanent = (dep_flags & MEM_EXECUTE_OPTION_PERMANENT) != 0;

    }
    if (status) SetLastError( RtlNtStatusToDosError(status) );
    return !status;
4030
}
4031

4032 4033 4034 4035 4036
/**********************************************************************
 *           FlushProcessWriteBuffers     (KERNEL32.@)
 */
VOID WINAPI FlushProcessWriteBuffers(void)
{
4037 4038 4039 4040
    static int once = 0;

    if (!once++)
        FIXME(": stub\n");
4041
}
4042 4043 4044 4045 4046 4047 4048 4049 4050 4051

/***********************************************************************
 *           UnregisterApplicationRestart       (KERNEL32.@)
 */
HRESULT WINAPI UnregisterApplicationRestart(void)
{
    FIXME(": stub\n");
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return S_OK;
}
4052 4053 4054 4055 4056 4057 4058 4059 4060 4061

/***********************************************************************
 *           GetSystemFirmwareTable       (KERNEL32.@)
 */
UINT WINAPI GetSystemFirmwareTable(DWORD provider, DWORD id, PVOID buffer, DWORD size)
{
    FIXME("(%d %d %p %d):stub\n", provider, id, buffer, size);
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
    return 0;
}