Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wine-winehq
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wine
wine-winehq
Commits
aaf3503e
Commit
aaf3503e
authored
Sep 17, 2003
by
Alexandre Julliard
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Moved almost all remaining process, thread, fiber and exception
functions to dlls/kernel.
parent
1479aebd
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
1963 additions
and
2002 deletions
+1963
-2002
Makefile.in
dlls/kernel/Makefile.in
+3
-1
except.c
dlls/kernel/except.c
+0
-0
fiber.c
dlls/kernel/fiber.c
+0
-0
process.c
dlls/kernel/process.c
+1620
-12
thread.c
dlls/kernel/thread.c
+335
-0
Makefile.in
dlls/ntdll/Makefile.in
+0
-2
loader.c
libs/wine/loader.c
+4
-4
module.c
loader/module.c
+0
-126
process.c
scheduler/process.c
+1
-1485
thread.c
scheduler/thread.c
+0
-372
No files found.
dlls/kernel/Makefile.in
View file @
aaf3503e
EXTRADEFS
=
-D_KERNEL32_
EXTRADEFS
=
-D_KERNEL32_
-DBINDIR
=
"
\"
$(bindir)
\"
"
TOPSRCDIR
=
@top_srcdir@
TOPOBJDIR
=
../..
SRCDIR
=
@srcdir@
...
...
@@ -27,6 +27,8 @@ C_SRCS = \
console.c
\
debugger.c
\
editline.c
\
except.c
\
fiber.c
\
file.c
\
file16.c
\
format_msg.c
\
...
...
win32
/except.c
→
dlls/kernel
/except.c
View file @
aaf3503e
File moved
scheduler
/fiber.c
→
dlls/kernel
/fiber.c
View file @
aaf3503e
File moved
dlls/kernel/process.c
View file @
aaf3503e
...
...
@@ -21,28 +21,1622 @@
#include "config.h"
#include "wine/port.h"
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <locale.h>
#include <signal.h>
#include <stdio.h>
#include "wine/winbase16.h"
#include "wine/winuser16.h"
#include "ntstatus.h"
#include "thread.h"
#include "drive.h"
#include "file.h"
#include "heap.h"
#include "module.h"
#include "options.h"
#include "kernel_private.h"
#include "wine/server.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL
(
process
);
WINE_DEFAULT_DEBUG_CHANNEL
(
process
);
WINE_DECLARE_DEBUG_CHANNEL
(
server
);
WINE_DECLARE_DEBUG_CHANNEL
(
relay
);
WINE_DECLARE_DEBUG_CHANNEL
(
snoop
);
/* Win32 process database */
typedef
struct
_PDB
{
LONG
header
[
2
];
/* 00 Kernel object header */
HMODULE
module
;
/* 08 Main exe module (NT) */
PPEB_LDR_DATA
LdrData
;
/* 0c Pointer to loader information */
RTL_USER_PROCESS_PARAMETERS
*
ProcessParameters
;
/* 10 Process parameters */
DWORD
unknown2
;
/* 14 Unknown */
HANDLE
heap
;
/* 18 Default process heap */
HANDLE
mem_context
;
/* 1c Process memory context */
DWORD
flags
;
/* 20 Flags */
void
*
pdb16
;
/* 24 DOS PSP */
WORD
PSP_sel
;
/* 28 Selector to DOS PSP */
WORD
imte
;
/* 2a IMTE for the process module */
WORD
threads
;
/* 2c Number of threads */
WORD
running_threads
;
/* 2e Number of running threads */
WORD
free_lib_count
;
/* 30 Recursion depth of FreeLibrary calls */
WORD
ring0_threads
;
/* 32 Number of ring 0 threads */
HANDLE
system_heap
;
/* 34 System heap to allocate handles */
HTASK
task
;
/* 38 Win16 task */
void
*
mem_map_files
;
/* 3c Pointer to mem-mapped files */
struct
_ENVDB
*
env_db
;
/* 40 Environment database */
void
*
handle_table
;
/* 44 Handle table */
struct
_PDB
*
parent
;
/* 48 Parent process */
void
*
modref_list
;
/* 4c MODREF list */
void
*
thread_list
;
/* 50 List of threads */
void
*
debuggee_CB
;
/* 54 Debuggee context block */
void
*
local_heap_free
;
/* 58 Head of local heap free list */
DWORD
unknown4
;
/* 5c Unknown */
CRITICAL_SECTION
crit_section
;
/* 60 Critical section */
DWORD
unknown5
[
3
];
/* 78 Unknown */
void
*
console
;
/* 84 Console */
DWORD
tls_bits
[
2
];
/* 88 TLS in-use bits */
DWORD
process_dword
;
/* 90 Unknown */
struct
_PDB
*
group
;
/* 94 Process group */
void
*
exe_modref
;
/* 98 MODREF for the process EXE */
void
*
top_filter
;
/* 9c Top exception filter */
DWORD
priority
;
/* a0 Priority level */
HANDLE
heap_list
;
/* a4 Head of process heap list */
void
*
heap_handles
;
/* a8 Head of heap handles list */
DWORD
unknown6
;
/* ac Unknown */
void
*
console_provider
;
/* b0 Console provider (??) */
WORD
env_selector
;
/* b4 Selector to process environment */
WORD
error_mode
;
/* b6 Error mode */
HANDLE
load_done_evt
;
/* b8 Event for process loading done */
void
*
UTState
;
/* bc Head of Univeral Thunk list */
DWORD
unknown8
;
/* c0 Unknown (NT) */
LCID
locale
;
/* c4 Locale to be queried by GetThreadLocale (NT) */
}
PDB
;
PDB
current_process
;
static
RTL_USER_PROCESS_PARAMETERS
process_pmts
;
static
PEB_LDR_DATA
process_ldr
;
static
char
main_exe_name
[
MAX_PATH
];
static
char
*
main_exe_name_ptr
=
main_exe_name
;
static
HANDLE
main_exe_file
;
static
DWORD
shutdown_flags
=
0
;
static
DWORD
shutdown_priority
=
0x280
;
static
DWORD
process_dword
;
static
BOOL
oem_file_apis
;
extern
unsigned
int
server_startticks
;
int
main_create_flags
=
0
;
/* 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 */
/* dlls/ntdll/env.c */
extern
BOOL
init_user_process_pmts
(
size_t
,
char
*
,
size_t
);
extern
BOOL
build_command_line
(
char
**
argv
);
extern
void
RELAY_InitDebugLists
(
void
);
extern
void
SHELL_LoadRegistry
(
void
);
extern
void
VERSION_Init
(
const
char
*
appname
);
/***********************************************************************
* get_basename
*/
inline
static
const
char
*
get_basename
(
const
char
*
name
)
{
char
*
p
;
if
((
p
=
strrchr
(
name
,
'/'
)))
name
=
p
+
1
;
if
((
p
=
strrchr
(
name
,
'\\'
)))
name
=
p
+
1
;
return
name
;
}
/***********************************************************************
* open_builtin_exe_file
*
* Open an exe file for a builtin exe.
*/
static
void
*
open_builtin_exe_file
(
const
char
*
name
,
char
*
error
,
int
error_size
,
int
test_only
,
int
*
file_exists
)
{
char
exename
[
MAX_PATH
],
*
p
;
const
char
*
basename
=
get_basename
(
name
);
if
(
strlen
(
basename
)
>=
sizeof
(
exename
))
return
NULL
;
strcpy
(
exename
,
basename
);
for
(
p
=
exename
;
*
p
;
p
++
)
*
p
=
FILE_tolower
(
*
p
);
return
wine_dll_load_main_exe
(
exename
,
error
,
error_size
,
test_only
,
file_exists
);
}
/***********************************************************************
* open_exe_file
*
* Open a specific exe file, taking load order into account.
* Returns the file handle or 0 for a builtin exe.
*/
static
HANDLE
open_exe_file
(
const
char
*
name
)
{
enum
loadorder_type
loadorder
[
LOADORDER_NTYPES
];
char
buffer
[
MAX_PATH
];
HANDLE
handle
;
int
i
,
file_exists
;
TRACE
(
"looking for %s
\n
"
,
debugstr_a
(
name
)
);
if
((
handle
=
CreateFileA
(
name
,
GENERIC_READ
,
FILE_SHARE_READ
,
NULL
,
OPEN_EXISTING
,
0
,
0
))
==
INVALID_HANDLE_VALUE
)
{
/* file doesn't exist, check for builtin */
if
(
!
FILE_contains_path
(
name
))
goto
error
;
if
(
!
MODULE_GetBuiltinPath
(
name
,
""
,
buffer
,
sizeof
(
buffer
)
))
goto
error
;
name
=
buffer
;
}
MODULE_GetLoadOrder
(
loadorder
,
name
,
TRUE
);
for
(
i
=
0
;
i
<
LOADORDER_NTYPES
;
i
++
)
{
if
(
loadorder
[
i
]
==
LOADORDER_INVALID
)
break
;
switch
(
loadorder
[
i
])
{
case
LOADORDER_DLL
:
TRACE
(
"Trying native exe %s
\n
"
,
debugstr_a
(
name
)
);
if
(
handle
!=
INVALID_HANDLE_VALUE
)
return
handle
;
break
;
case
LOADORDER_BI
:
TRACE
(
"Trying built-in exe %s
\n
"
,
debugstr_a
(
name
)
);
open_builtin_exe_file
(
name
,
NULL
,
0
,
1
,
&
file_exists
);
if
(
file_exists
)
{
if
(
handle
!=
INVALID_HANDLE_VALUE
)
CloseHandle
(
handle
);
return
0
;
}
default:
break
;
}
}
if
(
handle
!=
INVALID_HANDLE_VALUE
)
CloseHandle
(
handle
);
error:
SetLastError
(
ERROR_FILE_NOT_FOUND
);
return
INVALID_HANDLE_VALUE
;
}
/***********************************************************************
* find_exe_file
*
* Open an exe file, and return the full name and file handle.
* Returns FALSE if file could not be found.
* If file exists but cannot be opened, returns TRUE and set handle to INVALID_HANDLE_VALUE.
* If file is a builtin exe, returns TRUE and sets handle to 0.
*/
static
BOOL
find_exe_file
(
const
char
*
name
,
char
*
buffer
,
int
buflen
,
HANDLE
*
handle
)
{
enum
loadorder_type
loadorder
[
LOADORDER_NTYPES
];
int
i
,
file_exists
;
TRACE
(
"looking for %s
\n
"
,
debugstr_a
(
name
)
);
if
(
!
SearchPathA
(
NULL
,
name
,
".exe"
,
buflen
,
buffer
,
NULL
)
&&
!
MODULE_GetBuiltinPath
(
name
,
".exe"
,
buffer
,
buflen
))
{
/* no builtin found, try native without extension in case it is a Unix app */
if
(
SearchPathA
(
NULL
,
name
,
NULL
,
buflen
,
buffer
,
NULL
))
{
TRACE
(
"Trying native/Unix binary %s
\n
"
,
debugstr_a
(
buffer
)
);
if
((
*
handle
=
CreateFileA
(
buffer
,
GENERIC_READ
,
FILE_SHARE_READ
,
NULL
,
OPEN_EXISTING
,
0
,
0
))
!=
INVALID_HANDLE_VALUE
)
return
TRUE
;
}
return
FALSE
;
}
MODULE_GetLoadOrder
(
loadorder
,
buffer
,
TRUE
);
for
(
i
=
0
;
i
<
LOADORDER_NTYPES
;
i
++
)
{
if
(
loadorder
[
i
]
==
LOADORDER_INVALID
)
break
;
switch
(
loadorder
[
i
])
{
case
LOADORDER_DLL
:
TRACE
(
"Trying native exe %s
\n
"
,
debugstr_a
(
buffer
)
);
if
((
*
handle
=
CreateFileA
(
buffer
,
GENERIC_READ
,
FILE_SHARE_READ
,
NULL
,
OPEN_EXISTING
,
0
,
0
))
!=
INVALID_HANDLE_VALUE
)
return
TRUE
;
if
(
GetLastError
()
!=
ERROR_FILE_NOT_FOUND
)
return
TRUE
;
break
;
case
LOADORDER_BI
:
TRACE
(
"Trying built-in exe %s
\n
"
,
debugstr_a
(
buffer
)
);
open_builtin_exe_file
(
buffer
,
NULL
,
0
,
1
,
&
file_exists
);
if
(
file_exists
)
{
*
handle
=
0
;
return
TRUE
;
}
break
;
default:
break
;
}
}
SetLastError
(
ERROR_FILE_NOT_FOUND
);
return
FALSE
;
}
/***********************************************************************
* process_init
*
* Main process initialisation code
*/
static
BOOL
process_init
(
char
*
argv
[]
)
{
BOOL
ret
;
size_t
info_size
=
0
;
setbuf
(
stdout
,
NULL
);
setbuf
(
stderr
,
NULL
);
setlocale
(
LC_CTYPE
,
""
);
/* store the program name */
argv0
=
argv
[
0
];
/* Fill the initial process structure */
current_process
.
threads
=
1
;
current_process
.
running_threads
=
1
;
current_process
.
ring0_threads
=
1
;
current_process
.
group
=
&
current_process
;
current_process
.
priority
=
8
;
/* Normal */
current_process
.
ProcessParameters
=
&
process_pmts
;
current_process
.
LdrData
=
&
process_ldr
;
InitializeListHead
(
&
process_ldr
.
InLoadOrderModuleList
);
InitializeListHead
(
&
process_ldr
.
InMemoryOrderModuleList
);
InitializeListHead
(
&
process_ldr
.
InInitializationOrderModuleList
);
/* Setup the server connection */
CLIENT_InitServer
();
/* Retrieve startup info from the server */
SERVER_START_REQ
(
init_process
)
{
req
->
ldt_copy
=
&
wine_ldt_copy
;
if
((
ret
=
!
wine_server_call_err
(
req
)))
{
main_exe_file
=
reply
->
exe_file
;
main_create_flags
=
reply
->
create_flags
;
info_size
=
reply
->
info_size
;
server_startticks
=
reply
->
server_start
;
process_pmts
.
hStdInput
=
reply
->
hstdin
;
process_pmts
.
hStdOutput
=
reply
->
hstdout
;
process_pmts
.
hStdError
=
reply
->
hstderr
;
}
}
SERVER_END_REQ
;
if
(
!
ret
)
return
FALSE
;
/* Create the process heap */
current_process
.
heap
=
RtlCreateHeap
(
HEAP_GROWABLE
,
NULL
,
0
,
0
,
NULL
,
NULL
);
if
(
info_size
==
0
)
{
/* This is wine specific: we have no parent (we're started from unix)
* so, create a simple console with bare handles to unix stdio
* input & output streams (aka simple console)
*/
wine_server_fd_to_handle
(
0
,
GENERIC_READ
|
SYNCHRONIZE
,
TRUE
,
&
process_pmts
.
hStdInput
);
wine_server_fd_to_handle
(
1
,
GENERIC_WRITE
|
SYNCHRONIZE
,
TRUE
,
&
process_pmts
.
hStdOutput
);
wine_server_fd_to_handle
(
1
,
GENERIC_WRITE
|
SYNCHRONIZE
,
TRUE
,
&
process_pmts
.
hStdError
);
}
else
{
/* convert value from server:
* + 0 => INVALID_HANDLE_VALUE
* + console handle need to be mapped
*/
if
(
!
process_pmts
.
hStdInput
)
process_pmts
.
hStdInput
=
INVALID_HANDLE_VALUE
;
else
if
(
VerifyConsoleIoHandle
(
console_handle_map
(
process_pmts
.
hStdInput
)))
process_pmts
.
hStdInput
=
console_handle_map
(
process_pmts
.
hStdInput
);
if
(
!
process_pmts
.
hStdOutput
)
process_pmts
.
hStdOutput
=
INVALID_HANDLE_VALUE
;
else
if
(
VerifyConsoleIoHandle
(
console_handle_map
(
process_pmts
.
hStdOutput
)))
process_pmts
.
hStdOutput
=
console_handle_map
(
process_pmts
.
hStdOutput
);
if
(
!
process_pmts
.
hStdError
)
process_pmts
.
hStdError
=
INVALID_HANDLE_VALUE
;
else
if
(
VerifyConsoleIoHandle
(
console_handle_map
(
process_pmts
.
hStdError
)))
process_pmts
.
hStdError
=
console_handle_map
(
process_pmts
.
hStdError
);
}
/* Copy the parent environment */
if
(
!
init_user_process_pmts
(
info_size
,
main_exe_name
,
sizeof
(
main_exe_name
)
))
return
FALSE
;
/* Parse command line arguments */
OPTIONS_ParseOptions
(
!
info_size
?
argv
:
NULL
);
/* <hack: to be changed later on> */
process_pmts
.
CurrentDirectoryName
.
Length
=
3
*
sizeof
(
WCHAR
);
process_pmts
.
CurrentDirectoryName
.
MaximumLength
=
RtlGetLongestNtPathLength
()
*
sizeof
(
WCHAR
);
process_pmts
.
CurrentDirectoryName
.
Buffer
=
RtlAllocateHeap
(
GetProcessHeap
(),
0
,
process_pmts
.
CurrentDirectoryName
.
MaximumLength
);
process_pmts
.
CurrentDirectoryName
.
Buffer
[
0
]
=
'C'
;
process_pmts
.
CurrentDirectoryName
.
Buffer
[
1
]
=
':'
;
process_pmts
.
CurrentDirectoryName
.
Buffer
[
2
]
=
'\\'
;
process_pmts
.
CurrentDirectoryName
.
Buffer
[
3
]
=
'\0'
;
/* </hack: to be changed later on> */
/* initialise DOS drives */
if
(
!
DRIVE_Init
())
return
FALSE
;
/* initialise DOS directories */
if
(
!
DIR_Init
())
return
FALSE
;
/* registry initialisation */
SHELL_LoadRegistry
();
/* global boot finished, the rest is process-local */
CLIENT_BootDone
(
TRACE_ON
(
server
)
);
if
(
TRACE_ON
(
relay
)
||
TRACE_ON
(
snoop
))
RELAY_InitDebugLists
();
return
TRUE
;
}
/***********************************************************************
* start_process
*
* Startup routine of a new process. Runs on the new process stack.
*/
static
void
start_process
(
void
*
arg
)
{
__TRY
{
LPTHREAD_START_ROUTINE
entry
;
HANDLE
main_file
=
main_exe_file
;
IMAGE_NT_HEADERS
*
nt
;
PEB
*
peb
=
NtCurrentTeb
()
->
Peb
;
if
(
main_file
)
{
UINT
drive_type
=
GetDriveTypeA
(
main_exe_name
);
/* don't keep the file handle open on removable media */
if
(
drive_type
==
DRIVE_REMOVABLE
||
drive_type
==
DRIVE_CDROM
)
main_file
=
0
;
}
/* Retrieve entry point address */
nt
=
RtlImageNtHeader
(
peb
->
ImageBaseAddress
);
entry
=
(
LPTHREAD_START_ROUTINE
)((
char
*
)
peb
->
ImageBaseAddress
+
nt
->
OptionalHeader
.
AddressOfEntryPoint
);
/* Install signal handlers; this cannot be done before, since we cannot
* send exceptions to the debugger before the create process event that
* is sent by REQ_INIT_PROCESS_DONE.
* We do need the handlers in place by the time the request is over, so
* we set them up here. If we segfault between here and the server call
* something is very wrong... */
if
(
!
SIGNAL_Init
())
goto
error
;
/* Signal the parent process to continue */
SERVER_START_REQ
(
init_process_done
)
{
req
->
module
=
peb
->
ImageBaseAddress
;
req
->
module_size
=
nt
->
OptionalHeader
.
SizeOfImage
;
req
->
entry
=
entry
;
/* API requires a double indirection */
req
->
name
=
&
main_exe_name_ptr
;
req
->
exe_file
=
main_file
;
req
->
gui
=
(
nt
->
OptionalHeader
.
Subsystem
!=
IMAGE_SUBSYSTEM_WINDOWS_CUI
);
wine_server_add_data
(
req
,
main_exe_name
,
strlen
(
main_exe_name
)
);
wine_server_call
(
req
);
peb
->
BeingDebugged
=
reply
->
debugged
;
}
SERVER_END_REQ
;
/* create the main modref and load dependencies */
if
(
!
PE_CreateModule
(
peb
->
ImageBaseAddress
,
main_exe_name
,
0
,
0
,
FALSE
))
goto
error
;
if
(
main_exe_file
)
CloseHandle
(
main_exe_file
);
/* we no longer need it */
MODULE_DllProcessAttach
(
NULL
,
(
LPVOID
)
1
);
if
(
TRACE_ON
(
relay
))
DPRINTF
(
"%04lx:Starting process %s (entryproc=%p)
\n
"
,
GetCurrentThreadId
(),
main_exe_name
,
entry
);
if
(
peb
->
BeingDebugged
)
DbgBreakPoint
();
SetLastError
(
0
);
/* clear error code */
ExitThread
(
entry
(
NtCurrentTeb
()
->
Peb
)
);
error:
ExitProcess
(
GetLastError
()
);
}
__EXCEPT
(
UnhandledExceptionFilter
)
{
TerminateThread
(
GetCurrentThread
(),
GetExceptionCode
()
);
}
__ENDTRY
}
/***********************************************************************
* __wine_process_init
*
* Wine initialisation: load and start the main exe file.
*/
void
__wine_process_init
(
int
argc
,
char
*
argv
[]
)
{
char
error
[
1024
],
*
p
;
DWORD
stack_size
=
0
;
int
file_exists
;
/* Initialize everything */
if
(
!
process_init
(
argv
))
exit
(
1
);
argv
++
;
/* remove argv[0] (wine itself) */
TRACE
(
"starting process name=%s file=%p argv[0]=%s
\n
"
,
debugstr_a
(
main_exe_name
),
main_exe_file
,
debugstr_a
(
argv
[
0
])
);
if
(
!
main_exe_name
[
0
])
{
if
(
!
argv
[
0
])
OPTIONS_Usage
();
if
(
!
find_exe_file
(
argv
[
0
],
main_exe_name
,
sizeof
(
main_exe_name
),
&
main_exe_file
))
{
MESSAGE
(
"%s: cannot find '%s'
\n
"
,
argv0
,
argv
[
0
]
);
ExitProcess
(
1
);
}
if
(
main_exe_file
==
INVALID_HANDLE_VALUE
)
{
MESSAGE
(
"%s: cannot open '%s'
\n
"
,
argv0
,
main_exe_name
);
ExitProcess
(
1
);
}
}
if
(
!
main_exe_file
)
/* no file handle -> Winelib app */
{
TRACE
(
"starting Winelib app %s
\n
"
,
debugstr_a
(
main_exe_name
)
);
if
(
open_builtin_exe_file
(
main_exe_name
,
error
,
sizeof
(
error
),
0
,
&
file_exists
))
goto
found
;
MESSAGE
(
"%s: cannot open builtin library for '%s': %s
\n
"
,
argv0
,
main_exe_name
,
error
);
ExitProcess
(
1
);
}
VERSION_Init
(
main_exe_name
);
switch
(
MODULE_GetBinaryType
(
main_exe_file
))
{
case
BINARY_PE_EXE
:
TRACE
(
"starting Win32 binary %s
\n
"
,
debugstr_a
(
main_exe_name
)
);
if
((
current_process
.
module
=
PE_LoadImage
(
main_exe_file
,
main_exe_name
,
0
)))
goto
found
;
MESSAGE
(
"%s: could not load '%s' as Win32 binary
\n
"
,
argv0
,
main_exe_name
);
ExitProcess
(
1
);
case
BINARY_PE_DLL
:
MESSAGE
(
"%s: '%s' is a DLL, not an executable
\n
"
,
argv0
,
main_exe_name
);
ExitProcess
(
1
);
case
BINARY_UNKNOWN
:
/* check for .com extension */
if
(
!
(
p
=
strrchr
(
main_exe_name
,
'.'
))
||
FILE_strcasecmp
(
p
,
".com"
))
{
MESSAGE
(
"%s: cannot determine executable type for '%s'
\n
"
,
argv0
,
main_exe_name
);
ExitProcess
(
1
);
}
/* fall through */
case
BINARY_WIN16
:
case
BINARY_DOS
:
TRACE
(
"starting Win16/DOS binary %s
\n
"
,
debugstr_a
(
main_exe_name
)
);
CloseHandle
(
main_exe_file
);
main_exe_file
=
0
;
argv
--
;
argv
[
0
]
=
"winevdm.exe"
;
if
(
open_builtin_exe_file
(
"winevdm.exe"
,
error
,
sizeof
(
error
),
0
,
&
file_exists
))
goto
found
;
MESSAGE
(
"%s: trying to run '%s', cannot open builtin library for 'winevdm.exe': %s
\n
"
,
argv0
,
main_exe_name
,
error
);
ExitProcess
(
1
);
case
BINARY_OS216
:
MESSAGE
(
"%s: '%s' is an OS/2 binary, not supported
\n
"
,
argv0
,
main_exe_name
);
ExitProcess
(
1
);
case
BINARY_UNIX_EXE
:
MESSAGE
(
"%s: '%s' is a Unix binary, not supported
\n
"
,
argv0
,
main_exe_name
);
ExitProcess
(
1
);
case
BINARY_UNIX_LIB
:
{
DOS_FULL_NAME
full_name
;
const
char
*
name
=
main_exe_name
;
UNICODE_STRING
nameW
;
TRACE
(
"starting Winelib app %s
\n
"
,
debugstr_a
(
main_exe_name
)
);
RtlCreateUnicodeStringFromAsciiz
(
&
nameW
,
name
);
if
(
DOSFS_GetFullName
(
nameW
.
Buffer
,
TRUE
,
&
full_name
))
name
=
full_name
.
long_name
;
RtlFreeUnicodeString
(
&
nameW
);
CloseHandle
(
main_exe_file
);
main_exe_file
=
0
;
if
(
wine_dlopen
(
name
,
RTLD_NOW
,
error
,
sizeof
(
error
)
))
{
if
((
p
=
strrchr
(
main_exe_name
,
'.'
))
&&
!
strcmp
(
p
,
".so"
))
*
p
=
0
;
goto
found
;
}
MESSAGE
(
"%s: could not load '%s': %s
\n
"
,
argv0
,
main_exe_name
,
error
);
ExitProcess
(
1
);
}
}
found:
/* build command line */
if
(
!
build_command_line
(
argv
))
goto
error
;
/* create 32-bit module for main exe */
if
(
!
(
current_process
.
module
=
BUILTIN32_LoadExeModule
(
current_process
.
module
)))
goto
error
;
stack_size
=
RtlImageNtHeader
(
current_process
.
module
)
->
OptionalHeader
.
SizeOfStackReserve
;
/* allocate main thread stack */
if
(
!
THREAD_InitStack
(
NtCurrentTeb
(),
stack_size
))
goto
error
;
/* switch to the new stack */
wine_switch_to_stack
(
start_process
,
NULL
,
NtCurrentTeb
()
->
Tib
.
StackBase
);
error:
ExitProcess
(
GetLastError
()
);
}
/***********************************************************************
* build_argv
*
* Build an argv array from a command-line.
* The command-line is modified to insert nulls.
* 'reserved' is the number of args to reserve before the first one.
*/
static
char
**
build_argv
(
char
*
cmdline
,
int
reserved
)
{
int
argc
;
char
**
argv
;
char
*
arg
,
*
s
,
*
d
;
int
in_quotes
,
bcount
;
argc
=
reserved
+
1
;
bcount
=
0
;
in_quotes
=
0
;
s
=
cmdline
;
while
(
1
)
{
if
(
*
s
==
'\0'
||
((
*
s
==
' '
||
*
s
==
'\t'
)
&&
!
in_quotes
))
{
/* space */
argc
++
;
/* skip the remaining spaces */
while
(
*
s
==
' '
||
*
s
==
'\t'
)
{
s
++
;
}
if
(
*
s
==
'\0'
)
break
;
bcount
=
0
;
continue
;
}
else
if
(
*
s
==
'\\'
)
{
/* '\', count them */
bcount
++
;
}
else
if
((
*
s
==
'"'
)
&&
((
bcount
&
1
)
==
0
))
{
/* unescaped '"' */
in_quotes
=!
in_quotes
;
bcount
=
0
;
}
else
{
/* a regular character */
bcount
=
0
;
}
s
++
;
}
argv
=
malloc
(
argc
*
sizeof
(
*
argv
));
if
(
!
argv
)
return
NULL
;
arg
=
d
=
s
=
cmdline
;
bcount
=
0
;
in_quotes
=
0
;
argc
=
reserved
;
while
(
*
s
)
{
if
((
*
s
==
' '
||
*
s
==
'\t'
)
&&
!
in_quotes
)
{
/* Close the argument and copy it */
*
d
=
0
;
argv
[
argc
++
]
=
arg
;
/* skip the remaining spaces */
do
{
s
++
;
}
while
(
*
s
==
' '
||
*
s
==
'\t'
);
/* Start with a new argument */
arg
=
d
=
s
;
bcount
=
0
;
}
else
if
(
*
s
==
'\\'
)
{
/* '\\' */
*
d
++=*
s
++
;
bcount
++
;
}
else
if
(
*
s
==
'"'
)
{
/* '"' */
if
((
bcount
&
1
)
==
0
)
{
/* Preceeded by an even number of '\', this is half that
* number of '\', plus a '"' which we discard.
*/
d
-=
bcount
/
2
;
s
++
;
in_quotes
=!
in_quotes
;
}
else
{
/* Preceeded by an odd number of '\', this is half that
* 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
;
return
argv
;
}
/***********************************************************************
* build_envp
*
* Build the environment of a new child process.
*/
static
char
**
build_envp
(
const
char
*
env
,
const
char
*
extra_env
)
{
const
char
*
p
;
char
**
envp
;
int
count
=
0
;
if
(
extra_env
)
for
(
p
=
extra_env
;
*
p
;
count
++
)
p
+=
strlen
(
p
)
+
1
;
for
(
p
=
env
;
*
p
;
count
++
)
p
+=
strlen
(
p
)
+
1
;
count
+=
3
;
if
((
envp
=
malloc
(
count
*
sizeof
(
*
envp
)
)))
{
extern
char
**
environ
;
char
**
envptr
=
envp
;
char
**
unixptr
=
environ
;
/* first the extra strings */
if
(
extra_env
)
for
(
p
=
extra_env
;
*
p
;
p
+=
strlen
(
p
)
+
1
)
*
envptr
++
=
(
char
*
)
p
;
/* then put PATH, HOME and WINEPREFIX from the unix env */
for
(
unixptr
=
environ
;
unixptr
&&
*
unixptr
;
unixptr
++
)
if
(
!
memcmp
(
*
unixptr
,
"PATH="
,
5
)
||
!
memcmp
(
*
unixptr
,
"HOME="
,
5
)
||
!
memcmp
(
*
unixptr
,
"WINEPREFIX="
,
11
))
*
envptr
++
=
*
unixptr
;
/* now put the Windows environment strings */
for
(
p
=
env
;
*
p
;
p
+=
strlen
(
p
)
+
1
)
{
if
(
!
memcmp
(
p
,
"PATH="
,
5
))
/* store PATH as WINEPATH */
{
char
*
winepath
=
malloc
(
strlen
(
p
)
+
5
);
strcpy
(
winepath
,
"WINE"
);
strcpy
(
winepath
+
4
,
p
);
*
envptr
++
=
winepath
;
}
else
if
(
memcmp
(
p
,
"HOME="
,
5
)
&&
memcmp
(
p
,
"WINEPATH="
,
9
)
&&
memcmp
(
p
,
"WINEPREFIX="
,
11
))
*
envptr
++
=
(
char
*
)
p
;
}
*
envptr
=
0
;
}
return
envp
;
}
/***********************************************************************
* exec_wine_binary
*
* Locate the Wine binary to exec for a new Win32 process.
*/
static
void
exec_wine_binary
(
char
**
argv
,
char
**
envp
)
{
const
char
*
path
,
*
pos
,
*
ptr
;
/* first, try for a WINELOADER environment variable */
argv
[
0
]
=
getenv
(
"WINELOADER"
);
if
(
argv
[
0
])
execve
(
argv
[
0
],
argv
,
envp
);
/* next, try bin directory */
argv
[
0
]
=
BINDIR
"/wine"
;
execve
(
argv
[
0
],
argv
,
envp
);
/* now try the path of argv0 of the current binary */
if
(
!
(
argv
[
0
]
=
malloc
(
strlen
(
full_argv0
)
+
6
)))
return
;
if
((
ptr
=
strrchr
(
full_argv0
,
'/'
)))
{
memcpy
(
argv
[
0
],
full_argv0
,
ptr
-
full_argv0
);
strcpy
(
argv
[
0
]
+
(
ptr
-
full_argv0
),
"/wine"
);
execve
(
argv
[
0
],
argv
,
envp
);
}
free
(
argv
[
0
]
);
/* now search in the Unix path */
if
((
path
=
getenv
(
"PATH"
)))
{
if
(
!
(
argv
[
0
]
=
malloc
(
strlen
(
path
)
+
6
)))
return
;
pos
=
path
;
for
(;;)
{
while
(
*
pos
==
':'
)
pos
++
;
if
(
!*
pos
)
break
;
if
(
!
(
ptr
=
strchr
(
pos
,
':'
)))
ptr
=
pos
+
strlen
(
pos
);
memcpy
(
argv
[
0
],
pos
,
ptr
-
pos
);
strcpy
(
argv
[
0
]
+
(
ptr
-
pos
),
"/wine"
);
execve
(
argv
[
0
],
argv
,
envp
);
pos
=
ptr
;
}
}
free
(
argv
[
0
]
);
}
/***********************************************************************
* fork_and_exec
*
* Fork and exec a new Unix binary, checking for errors.
*/
static
int
fork_and_exec
(
const
char
*
filename
,
char
*
cmdline
,
const
char
*
env
,
const
char
*
newdir
)
{
int
fd
[
2
];
int
pid
,
err
;
if
(
!
env
)
env
=
GetEnvironmentStringsA
();
if
(
pipe
(
fd
)
==
-
1
)
{
FILE_SetDosError
();
return
-
1
;
}
fcntl
(
fd
[
1
],
F_SETFD
,
1
);
/* set close on exec */
if
(
!
(
pid
=
fork
()))
/* child */
{
char
**
argv
=
build_argv
(
cmdline
,
0
);
char
**
envp
=
build_envp
(
env
,
NULL
);
close
(
fd
[
0
]
);
/* Reset signals that we previously set to SIG_IGN */
signal
(
SIGPIPE
,
SIG_DFL
);
signal
(
SIGCHLD
,
SIG_DFL
);
if
(
newdir
)
chdir
(
newdir
);
if
(
argv
&&
envp
)
execve
(
filename
,
argv
,
envp
);
err
=
errno
;
write
(
fd
[
1
],
&
err
,
sizeof
(
err
)
);
_exit
(
1
);
}
close
(
fd
[
1
]
);
if
((
pid
!=
-
1
)
&&
(
read
(
fd
[
0
],
&
err
,
sizeof
(
err
)
)
>
0
))
/* exec failed */
{
errno
=
err
;
pid
=
-
1
;
}
if
(
pid
==
-
1
)
FILE_SetDosError
();
close
(
fd
[
0
]
);
return
pid
;
}
/***********************************************************************
* create_process
*
* Create a new process. If hFile is a valid handle we have an exe
* file, otherwise it is a Winelib app.
*/
static
BOOL
create_process
(
HANDLE
hFile
,
LPCSTR
filename
,
LPSTR
cmd_line
,
LPCSTR
env
,
LPSECURITY_ATTRIBUTES
psa
,
LPSECURITY_ATTRIBUTES
tsa
,
BOOL
inherit
,
DWORD
flags
,
LPSTARTUPINFOA
startup
,
LPPROCESS_INFORMATION
info
,
LPCSTR
unixdir
)
{
BOOL
ret
,
success
=
FALSE
;
HANDLE
process_info
;
startup_info_t
startup_info
;
char
*
extra_env
=
NULL
;
int
startfd
[
2
];
int
execfd
[
2
];
pid_t
pid
;
int
err
;
char
dummy
=
0
;
if
(
!
env
)
{
env
=
GetEnvironmentStringsA
();
extra_env
=
DRIVE_BuildEnv
();
}
/* create the synchronization pipes */
if
(
pipe
(
startfd
)
==
-
1
)
{
FILE_SetDosError
();
return
FALSE
;
}
if
(
pipe
(
execfd
)
==
-
1
)
{
close
(
startfd
[
0
]
);
close
(
startfd
[
1
]
);
FILE_SetDosError
();
return
FALSE
;
}
fcntl
(
execfd
[
1
],
F_SETFD
,
1
);
/* set close on exec */
/* create the child process */
if
(
!
(
pid
=
fork
()))
/* child */
{
char
**
argv
=
build_argv
(
cmd_line
,
1
);
char
**
envp
=
build_envp
(
env
,
extra_env
);
close
(
startfd
[
1
]
);
close
(
execfd
[
0
]
);
/* wait for parent to tell us to start */
if
(
read
(
startfd
[
0
],
&
dummy
,
1
)
!=
1
)
_exit
(
1
);
close
(
startfd
[
0
]
);
/* Reset signals that we previously set to SIG_IGN */
signal
(
SIGPIPE
,
SIG_DFL
);
signal
(
SIGCHLD
,
SIG_DFL
);
if
(
unixdir
)
chdir
(
unixdir
);
if
(
argv
&&
envp
)
exec_wine_binary
(
argv
,
envp
);
err
=
errno
;
write
(
execfd
[
1
],
&
err
,
sizeof
(
err
)
);
_exit
(
1
);
}
/* this is the parent */
close
(
startfd
[
0
]
);
close
(
execfd
[
1
]
);
if
(
extra_env
)
HeapFree
(
GetProcessHeap
(),
0
,
extra_env
);
if
(
pid
==
-
1
)
{
close
(
startfd
[
1
]
);
close
(
execfd
[
0
]
);
FILE_SetDosError
();
return
FALSE
;
}
/* fill the startup info structure */
startup_info
.
size
=
sizeof
(
startup_info
);
/* startup_info.filename_len is set below */
startup_info
.
cmdline_len
=
cmd_line
?
strlen
(
cmd_line
)
:
0
;
startup_info
.
desktop_len
=
startup
->
lpDesktop
?
strlen
(
startup
->
lpDesktop
)
:
0
;
startup_info
.
title_len
=
startup
->
lpTitle
?
strlen
(
startup
->
lpTitle
)
:
0
;
startup_info
.
x
=
startup
->
dwX
;
startup_info
.
y
=
startup
->
dwY
;
startup_info
.
cx
=
startup
->
dwXSize
;
startup_info
.
cy
=
startup
->
dwYSize
;
startup_info
.
x_chars
=
startup
->
dwXCountChars
;
startup_info
.
y_chars
=
startup
->
dwYCountChars
;
startup_info
.
attribute
=
startup
->
dwFillAttribute
;
startup_info
.
cmd_show
=
startup
->
wShowWindow
;
startup_info
.
flags
=
startup
->
dwFlags
;
/* create the process on the server side */
SERVER_START_REQ
(
new_process
)
{
char
buf
[
MAX_PATH
];
LPCSTR
nameptr
;
req
->
inherit_all
=
inherit
;
req
->
create_flags
=
flags
;
req
->
unix_pid
=
pid
;
req
->
exe_file
=
hFile
;
if
(
startup
->
dwFlags
&
STARTF_USESTDHANDLES
)
{
req
->
hstdin
=
startup
->
hStdInput
;
req
->
hstdout
=
startup
->
hStdOutput
;
req
->
hstderr
=
startup
->
hStdError
;
}
else
{
req
->
hstdin
=
GetStdHandle
(
STD_INPUT_HANDLE
);
req
->
hstdout
=
GetStdHandle
(
STD_OUTPUT_HANDLE
);
req
->
hstderr
=
GetStdHandle
(
STD_ERROR_HANDLE
);
}
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
(
req
->
hstdin
))
req
->
hstdin
=
INVALID_HANDLE_VALUE
;
if
(
is_console_handle
(
req
->
hstdout
))
req
->
hstdout
=
INVALID_HANDLE_VALUE
;
if
(
is_console_handle
(
req
->
hstderr
))
req
->
hstderr
=
INVALID_HANDLE_VALUE
;
}
else
{
if
(
is_console_handle
(
req
->
hstdin
))
req
->
hstdin
=
console_handle_unmap
(
req
->
hstdin
);
if
(
is_console_handle
(
req
->
hstdout
))
req
->
hstdout
=
console_handle_unmap
(
req
->
hstdout
);
if
(
is_console_handle
(
req
->
hstderr
))
req
->
hstderr
=
console_handle_unmap
(
req
->
hstderr
);
}
if
(
GetLongPathNameA
(
filename
,
buf
,
MAX_PATH
))
nameptr
=
buf
;
else
nameptr
=
filename
;
startup_info
.
filename_len
=
strlen
(
nameptr
);
wine_server_add_data
(
req
,
&
startup_info
,
sizeof
(
startup_info
)
);
wine_server_add_data
(
req
,
nameptr
,
startup_info
.
filename_len
);
wine_server_add_data
(
req
,
cmd_line
,
startup_info
.
cmdline_len
);
wine_server_add_data
(
req
,
startup
->
lpDesktop
,
startup_info
.
desktop_len
);
wine_server_add_data
(
req
,
startup
->
lpTitle
,
startup_info
.
title_len
);
ret
=
!
wine_server_call_err
(
req
);
process_info
=
reply
->
info
;
}
SERVER_END_REQ
;
if
(
!
ret
)
{
close
(
startfd
[
1
]
);
close
(
execfd
[
0
]
);
return
FALSE
;
}
/* tell child to start and wait for it to exec */
write
(
startfd
[
1
],
&
dummy
,
1
);
close
(
startfd
[
1
]
);
if
(
read
(
execfd
[
0
],
&
err
,
sizeof
(
err
)
)
>
0
)
/* exec failed */
{
errno
=
err
;
FILE_SetDosError
();
close
(
execfd
[
0
]
);
CloseHandle
(
process_info
);
return
FALSE
;
}
/* wait for the new process info to be ready */
WaitForSingleObject
(
process_info
,
INFINITE
);
SERVER_START_REQ
(
get_new_process_info
)
{
req
->
info
=
process_info
;
req
->
pinherit
=
(
psa
&&
(
psa
->
nLength
>=
sizeof
(
*
psa
))
&&
psa
->
bInheritHandle
);
req
->
tinherit
=
(
tsa
&&
(
tsa
->
nLength
>=
sizeof
(
*
tsa
))
&&
tsa
->
bInheritHandle
);
if
((
ret
=
!
wine_server_call_err
(
req
)))
{
info
->
dwProcessId
=
(
DWORD
)
reply
->
pid
;
info
->
dwThreadId
=
(
DWORD
)
reply
->
tid
;
info
->
hProcess
=
reply
->
phandle
;
info
->
hThread
=
reply
->
thandle
;
success
=
reply
->
success
;
}
}
SERVER_END_REQ
;
if
(
ret
&&
!
success
)
/* new process failed to start */
{
DWORD
exitcode
;
if
(
GetExitCodeProcess
(
info
->
hProcess
,
&
exitcode
))
SetLastError
(
exitcode
);
CloseHandle
(
info
->
hThread
);
CloseHandle
(
info
->
hProcess
);
ret
=
FALSE
;
}
CloseHandle
(
process_info
);
return
ret
;
}
/* 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 */
/***********************************************************************
* create_vdm_process
*
* Create a new VDM process for a 16-bit or DOS application.
*/
static
BOOL
create_vdm_process
(
LPCSTR
filename
,
LPSTR
cmd_line
,
LPCSTR
env
,
LPSECURITY_ATTRIBUTES
psa
,
LPSECURITY_ATTRIBUTES
tsa
,
BOOL
inherit
,
DWORD
flags
,
LPSTARTUPINFOA
startup
,
LPPROCESS_INFORMATION
info
,
LPCSTR
unixdir
)
{
BOOL
ret
;
LPSTR
new_cmd_line
=
HeapAlloc
(
GetProcessHeap
(),
0
,
strlen
(
filename
)
+
strlen
(
cmd_line
)
+
30
);
if
(
!
new_cmd_line
)
{
SetLastError
(
ERROR_OUTOFMEMORY
);
return
FALSE
;
}
sprintf
(
new_cmd_line
,
"winevdm.exe --app-name
\"
%s
\"
%s"
,
filename
,
cmd_line
);
ret
=
create_process
(
0
,
"winevdm.exe"
,
new_cmd_line
,
env
,
psa
,
tsa
,
inherit
,
flags
,
startup
,
info
,
unixdir
);
HeapFree
(
GetProcessHeap
(),
0
,
new_cmd_line
);
return
ret
;
}
static
DWORD
shutdown_flags
=
0
;
static
DWORD
shutdown_priority
=
0x280
;
static
DWORD
process_dword
;
static
BOOL
oem_file_apis
;
/*************************************************************************
* get_file_name
*
* Helper for CreateProcess: retrieve the file name to load from the
* app name and command line. Store the file name in buffer, and
* return a possibly modified command line.
* Also returns a handle to the opened file if it's a Windows binary.
*/
static
LPSTR
get_file_name
(
LPCSTR
appname
,
LPSTR
cmdline
,
LPSTR
buffer
,
int
buflen
,
HANDLE
*
handle
)
{
char
*
name
,
*
pos
,
*
ret
=
NULL
;
const
char
*
p
;
/* if we have an app name, everything is easy */
if
(
appname
)
{
/* use the unmodified app name as file name */
lstrcpynA
(
buffer
,
appname
,
buflen
);
*
handle
=
open_exe_file
(
buffer
);
if
(
!
(
ret
=
cmdline
)
||
!
cmdline
[
0
])
{
/* no command-line, create one */
if
((
ret
=
HeapAlloc
(
GetProcessHeap
(),
0
,
strlen
(
appname
)
+
3
)))
sprintf
(
ret
,
"
\"
%s
\"
"
,
appname
);
}
return
ret
;
}
if
(
!
cmdline
)
{
SetLastError
(
ERROR_INVALID_PARAMETER
);
return
NULL
;
}
/* first check for a quoted file name */
if
((
cmdline
[
0
]
==
'"'
)
&&
((
p
=
strchr
(
cmdline
+
1
,
'"'
))))
{
int
len
=
p
-
cmdline
-
1
;
/* extract the quoted portion as file name */
if
(
!
(
name
=
HeapAlloc
(
GetProcessHeap
(),
0
,
len
+
1
)))
return
NULL
;
memcpy
(
name
,
cmdline
+
1
,
len
);
name
[
len
]
=
0
;
if
(
find_exe_file
(
name
,
buffer
,
buflen
,
handle
))
ret
=
cmdline
;
/* no change necessary */
goto
done
;
}
/* now try the command-line word by word */
if
(
!
(
name
=
HeapAlloc
(
GetProcessHeap
(),
0
,
strlen
(
cmdline
)
+
1
)))
return
NULL
;
pos
=
name
;
p
=
cmdline
;
while
(
*
p
)
{
do
*
pos
++
=
*
p
++
;
while
(
*
p
&&
*
p
!=
' '
);
*
pos
=
0
;
if
(
find_exe_file
(
name
,
buffer
,
buflen
,
handle
))
{
ret
=
cmdline
;
break
;
}
}
if
(
!
ret
||
!
strchr
(
name
,
' '
))
goto
done
;
/* no change necessary */
/* now build a new command-line with quotes */
if
(
!
(
ret
=
HeapAlloc
(
GetProcessHeap
(),
0
,
strlen
(
cmdline
)
+
3
)))
goto
done
;
sprintf
(
ret
,
"
\"
%s
\"
%s"
,
name
,
p
);
done:
HeapFree
(
GetProcessHeap
(),
0
,
name
);
return
ret
;
}
/**********************************************************************
* CreateProcessA (KERNEL32.@)
*/
BOOL
WINAPI
CreateProcessA
(
LPCSTR
app_name
,
LPSTR
cmd_line
,
LPSECURITY_ATTRIBUTES
process_attr
,
LPSECURITY_ATTRIBUTES
thread_attr
,
BOOL
inherit
,
DWORD
flags
,
LPVOID
env
,
LPCSTR
cur_dir
,
LPSTARTUPINFOA
startup_info
,
LPPROCESS_INFORMATION
info
)
{
BOOL
retv
=
FALSE
;
HANDLE
hFile
=
0
;
const
char
*
unixdir
=
NULL
;
DOS_FULL_NAME
full_dir
;
char
name
[
MAX_PATH
];
LPSTR
tidy_cmdline
;
char
*
p
;
/* Process the AppName and/or CmdLine to get module name and path */
TRACE
(
"app %s cmdline %s
\n
"
,
debugstr_a
(
app_name
),
debugstr_a
(
cmd_line
)
);
if
(
!
(
tidy_cmdline
=
get_file_name
(
app_name
,
cmd_line
,
name
,
sizeof
(
name
),
&
hFile
)))
return
FALSE
;
if
(
hFile
==
INVALID_HANDLE_VALUE
)
goto
done
;
/* Warn if unsupported features are used */
if
(
flags
&
NORMAL_PRIORITY_CLASS
)
FIXME
(
"(%s,...): NORMAL_PRIORITY_CLASS ignored
\n
"
,
name
);
if
(
flags
&
IDLE_PRIORITY_CLASS
)
FIXME
(
"(%s,...): IDLE_PRIORITY_CLASS ignored
\n
"
,
name
);
if
(
flags
&
HIGH_PRIORITY_CLASS
)
FIXME
(
"(%s,...): HIGH_PRIORITY_CLASS ignored
\n
"
,
name
);
if
(
flags
&
REALTIME_PRIORITY_CLASS
)
FIXME
(
"(%s,...): REALTIME_PRIORITY_CLASS ignored
\n
"
,
name
);
if
(
flags
&
CREATE_NEW_PROCESS_GROUP
)
FIXME
(
"(%s,...): CREATE_NEW_PROCESS_GROUP ignored
\n
"
,
name
);
if
(
flags
&
CREATE_UNICODE_ENVIRONMENT
)
FIXME
(
"(%s,...): CREATE_UNICODE_ENVIRONMENT ignored
\n
"
,
name
);
if
(
flags
&
CREATE_SEPARATE_WOW_VDM
)
FIXME
(
"(%s,...): CREATE_SEPARATE_WOW_VDM ignored
\n
"
,
name
);
if
(
flags
&
CREATE_SHARED_WOW_VDM
)
FIXME
(
"(%s,...): CREATE_SHARED_WOW_VDM ignored
\n
"
,
name
);
if
(
flags
&
CREATE_DEFAULT_ERROR_MODE
)
FIXME
(
"(%s,...): CREATE_DEFAULT_ERROR_MODE ignored
\n
"
,
name
);
if
(
flags
&
CREATE_NO_WINDOW
)
FIXME
(
"(%s,...): CREATE_NO_WINDOW ignored
\n
"
,
name
);
if
(
flags
&
PROFILE_USER
)
FIXME
(
"(%s,...): PROFILE_USER ignored
\n
"
,
name
);
if
(
flags
&
PROFILE_KERNEL
)
FIXME
(
"(%s,...): PROFILE_KERNEL ignored
\n
"
,
name
);
if
(
flags
&
PROFILE_SERVER
)
FIXME
(
"(%s,...): PROFILE_SERVER ignored
\n
"
,
name
);
if
(
startup_info
->
lpDesktop
)
FIXME
(
"(%s,...): startup_info->lpDesktop %s ignored
\n
"
,
name
,
debugstr_a
(
startup_info
->
lpDesktop
));
if
(
startup_info
->
dwFlags
&
STARTF_RUNFULLSCREEN
)
FIXME
(
"(%s,...): STARTF_RUNFULLSCREEN ignored
\n
"
,
name
);
if
(
startup_info
->
dwFlags
&
STARTF_FORCEONFEEDBACK
)
FIXME
(
"(%s,...): STARTF_FORCEONFEEDBACK ignored
\n
"
,
name
);
if
(
startup_info
->
dwFlags
&
STARTF_FORCEOFFFEEDBACK
)
FIXME
(
"(%s,...): STARTF_FORCEOFFFEEDBACK ignored
\n
"
,
name
);
if
(
startup_info
->
dwFlags
&
STARTF_USEHOTKEY
)
FIXME
(
"(%s,...): STARTF_USEHOTKEY ignored
\n
"
,
name
);
if
(
cur_dir
)
{
UNICODE_STRING
cur_dirW
;
RtlCreateUnicodeStringFromAsciiz
(
&
cur_dirW
,
cur_dir
);
if
(
DOSFS_GetFullName
(
cur_dirW
.
Buffer
,
TRUE
,
&
full_dir
))
unixdir
=
full_dir
.
long_name
;
RtlFreeUnicodeString
(
&
cur_dirW
);
}
else
{
WCHAR
buf
[
MAX_PATH
];
if
(
GetCurrentDirectoryW
(
MAX_PATH
,
buf
))
{
if
(
DOSFS_GetFullName
(
buf
,
TRUE
,
&
full_dir
))
unixdir
=
full_dir
.
long_name
;
}
}
info
->
hThread
=
info
->
hProcess
=
0
;
info
->
dwProcessId
=
info
->
dwThreadId
=
0
;
/* Determine executable type */
if
(
!
hFile
)
/* builtin exe */
{
TRACE
(
"starting %s as Winelib app
\n
"
,
debugstr_a
(
name
)
);
retv
=
create_process
(
0
,
name
,
tidy_cmdline
,
env
,
process_attr
,
thread_attr
,
inherit
,
flags
,
startup_info
,
info
,
unixdir
);
goto
done
;
}
switch
(
MODULE_GetBinaryType
(
hFile
))
{
case
BINARY_PE_EXE
:
TRACE
(
"starting %s as Win32 binary
\n
"
,
debugstr_a
(
name
)
);
retv
=
create_process
(
hFile
,
name
,
tidy_cmdline
,
env
,
process_attr
,
thread_attr
,
inherit
,
flags
,
startup_info
,
info
,
unixdir
);
break
;
case
BINARY_WIN16
:
case
BINARY_DOS
:
TRACE
(
"starting %s as Win16/DOS binary
\n
"
,
debugstr_a
(
name
)
);
retv
=
create_vdm_process
(
name
,
tidy_cmdline
,
env
,
process_attr
,
thread_attr
,
inherit
,
flags
,
startup_info
,
info
,
unixdir
);
break
;
case
BINARY_OS216
:
FIXME
(
"%s is OS/2 binary, not supported
\n
"
,
debugstr_a
(
name
)
);
SetLastError
(
ERROR_BAD_EXE_FORMAT
);
break
;
case
BINARY_PE_DLL
:
TRACE
(
"not starting %s since it is a dll
\n
"
,
debugstr_a
(
name
)
);
SetLastError
(
ERROR_BAD_EXE_FORMAT
);
break
;
case
BINARY_UNIX_LIB
:
TRACE
(
"%s is a Unix library, starting as Winelib app
\n
"
,
debugstr_a
(
name
)
);
retv
=
create_process
(
hFile
,
name
,
tidy_cmdline
,
env
,
process_attr
,
thread_attr
,
inherit
,
flags
,
startup_info
,
info
,
unixdir
);
break
;
case
BINARY_UNKNOWN
:
/* check for .com or .bat extension */
if
((
p
=
strrchr
(
name
,
'.'
)))
{
if
(
!
FILE_strcasecmp
(
p
,
".com"
))
{
TRACE
(
"starting %s as DOS binary
\n
"
,
debugstr_a
(
name
)
);
retv
=
create_vdm_process
(
name
,
tidy_cmdline
,
env
,
process_attr
,
thread_attr
,
inherit
,
flags
,
startup_info
,
info
,
unixdir
);
break
;
}
if
(
!
FILE_strcasecmp
(
p
,
".bat"
))
{
char
comspec
[
MAX_PATH
];
if
(
GetEnvironmentVariableA
(
"COMSPEC"
,
comspec
,
sizeof
(
comspec
)))
{
char
*
newcmdline
;
if
((
newcmdline
=
HeapAlloc
(
GetProcessHeap
(),
0
,
strlen
(
comspec
)
+
4
+
strlen
(
tidy_cmdline
)
+
1
)))
{
sprintf
(
newcmdline
,
"%s /c %s"
,
comspec
,
tidy_cmdline
);
TRACE
(
"starting %s as batch binary: %s
\n
"
,
debugstr_a
(
name
),
debugstr_a
(
newcmdline
)
);
retv
=
CreateProcessA
(
comspec
,
newcmdline
,
process_attr
,
thread_attr
,
inherit
,
flags
,
env
,
cur_dir
,
startup_info
,
info
);
HeapFree
(
GetProcessHeap
(),
0
,
newcmdline
);
break
;
}
}
}
}
/* fall through */
case
BINARY_UNIX_EXE
:
{
/* unknown file, try as unix executable */
UNICODE_STRING
nameW
;
DOS_FULL_NAME
full_name
;
const
char
*
unixfilename
=
name
;
TRACE
(
"starting %s as Unix binary
\n
"
,
debugstr_a
(
name
)
);
RtlCreateUnicodeStringFromAsciiz
(
&
nameW
,
name
);
if
(
DOSFS_GetFullName
(
nameW
.
Buffer
,
TRUE
,
&
full_name
))
unixfilename
=
full_name
.
long_name
;
RtlFreeUnicodeString
(
&
nameW
);
retv
=
(
fork_and_exec
(
unixfilename
,
tidy_cmdline
,
env
,
unixdir
)
!=
-
1
);
}
break
;
}
CloseHandle
(
hFile
);
done:
if
(
tidy_cmdline
!=
cmd_line
)
HeapFree
(
GetProcessHeap
(),
0
,
tidy_cmdline
);
return
retv
;
}
/**********************************************************************
* CreateProcessW (KERNEL32.@)
* NOTES
* lpReserved is not converted
*/
BOOL
WINAPI
CreateProcessW
(
LPCWSTR
app_name
,
LPWSTR
cmd_line
,
LPSECURITY_ATTRIBUTES
process_attr
,
LPSECURITY_ATTRIBUTES
thread_attr
,
BOOL
inherit
,
DWORD
flags
,
LPVOID
env
,
LPCWSTR
cur_dir
,
LPSTARTUPINFOW
startup_info
,
LPPROCESS_INFORMATION
info
)
{
BOOL
ret
;
STARTUPINFOA
StartupInfoA
;
LPSTR
app_nameA
=
HEAP_strdupWtoA
(
GetProcessHeap
(),
0
,
app_name
);
LPSTR
cmd_lineA
=
HEAP_strdupWtoA
(
GetProcessHeap
(),
0
,
cmd_line
);
LPSTR
cur_dirA
=
HEAP_strdupWtoA
(
GetProcessHeap
(),
0
,
cur_dir
);
memcpy
(
&
StartupInfoA
,
startup_info
,
sizeof
(
STARTUPINFOA
));
StartupInfoA
.
lpDesktop
=
HEAP_strdupWtoA
(
GetProcessHeap
(),
0
,
startup_info
->
lpDesktop
);
StartupInfoA
.
lpTitle
=
HEAP_strdupWtoA
(
GetProcessHeap
(),
0
,
startup_info
->
lpTitle
);
TRACE
(
"(%s,%s,...)
\n
"
,
debugstr_w
(
app_name
),
debugstr_w
(
cmd_line
));
if
(
startup_info
->
lpReserved
)
FIXME
(
"StartupInfo.lpReserved is used, please report (%s)
\n
"
,
debugstr_w
(
startup_info
->
lpReserved
));
ret
=
CreateProcessA
(
app_nameA
,
cmd_lineA
,
process_attr
,
thread_attr
,
inherit
,
flags
,
env
,
cur_dirA
,
&
StartupInfoA
,
info
);
HeapFree
(
GetProcessHeap
(),
0
,
cur_dirA
);
HeapFree
(
GetProcessHeap
(),
0
,
cmd_lineA
);
HeapFree
(
GetProcessHeap
(),
0
,
StartupInfoA
.
lpDesktop
);
HeapFree
(
GetProcessHeap
(),
0
,
StartupInfoA
.
lpTitle
);
return
ret
;
}
/***********************************************************************
* 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.@)
*/
UINT
WINAPI
WinExec
(
LPCSTR
lpCmdLine
,
UINT
nCmdShow
)
{
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
;
/* cmdline needs to be writeable for CreateProcess */
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 */
if
(
wait_input_idle
(
info
.
hProcess
,
30000
)
==
0xFFFFFFFF
)
WARN
(
"WaitForInputIdle failed: Error %ld
\n
"
,
GetLastError
()
);
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.@)
*/
HINSTANCE
WINAPI
LoadModule
(
LPCSTR
name
,
LPVOID
paramBlock
)
{
LOADPARAMS
*
params
=
(
LOADPARAMS
*
)
paramBlock
;
PROCESS_INFORMATION
info
;
STARTUPINFOA
startup
;
HINSTANCE
hInstance
;
LPSTR
cmdline
,
p
;
char
filename
[
MAX_PATH
];
BYTE
len
;
if
(
!
name
)
return
(
HINSTANCE
)
ERROR_FILE_NOT_FOUND
;
if
(
!
SearchPathA
(
NULL
,
name
,
".exe"
,
sizeof
(
filename
),
filename
,
NULL
)
&&
!
SearchPathA
(
NULL
,
name
,
NULL
,
sizeof
(
filename
),
filename
,
NULL
))
return
(
HINSTANCE
)
GetLastError
();
len
=
(
BYTE
)
params
->
lpCmdLine
[
0
];
if
(
!
(
cmdline
=
HeapAlloc
(
GetProcessHeap
(),
0
,
strlen
(
filename
)
+
len
+
2
)))
return
(
HINSTANCE
)
ERROR_NOT_ENOUGH_MEMORY
;
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
;
startup
.
wShowWindow
=
params
->
lpCmdShow
[
1
];
}
if
(
CreateProcessA
(
filename
,
cmdline
,
NULL
,
NULL
,
FALSE
,
0
,
params
->
lpEnvAddress
,
NULL
,
&
startup
,
&
info
))
{
/* Give 30 seconds to the app to come up */
if
(
wait_input_idle
(
info
.
hProcess
,
30000
)
==
0xFFFFFFFF
)
WARN
(
"WaitForInputIdle failed: Error %ld
\n
"
,
GetLastError
()
);
hInstance
=
(
HINSTANCE
)
33
;
/* Close off the handles */
CloseHandle
(
info
.
hThread
);
CloseHandle
(
info
.
hProcess
);
}
else
if
((
hInstance
=
(
HINSTANCE
)
GetLastError
())
>=
(
HINSTANCE
)
32
)
{
FIXME
(
"Strange error set by CreateProcess: %p
\n
"
,
hInstance
);
hInstance
=
(
HINSTANCE
)
11
;
}
HeapFree
(
GetProcessHeap
(),
0
,
cmdline
);
return
hInstance
;
}
/******************************************************************************
* TerminateProcess (KERNEL32.@)
*/
BOOL
WINAPI
TerminateProcess
(
HANDLE
handle
,
DWORD
exit_code
)
{
NTSTATUS
status
=
NtTerminateProcess
(
handle
,
exit_code
);
if
(
status
)
SetLastError
(
RtlNtStatusToDosError
(
status
)
);
return
!
status
;
}
/***********************************************************************
* GetExitCodeProcess [KERNEL32.@]
*
* Gets termination status of specified process
*
* RETURNS
* Success: TRUE
* Failure: FALSE
*/
BOOL
WINAPI
GetExitCodeProcess
(
HANDLE
hProcess
,
/* [in] handle to the process */
LPDWORD
lpExitCode
)
/* [out] address to receive termination status */
{
BOOL
ret
;
SERVER_START_REQ
(
get_process_info
)
{
req
->
handle
=
hProcess
;
ret
=
!
wine_server_call_err
(
req
);
if
(
ret
&&
lpExitCode
)
*
lpExitCode
=
reply
->
exit_code
;
}
SERVER_END_REQ
;
return
ret
;
}
/***********************************************************************
* SetErrorMode (KERNEL32.@)
*/
UINT
WINAPI
SetErrorMode
(
UINT
mode
)
{
UINT
old
=
current_process
.
error_mode
;
current_process
.
error_mode
=
mode
;
return
old
;
}
/**********************************************************************
* TlsAlloc [KERNEL32.@] Allocates a TLS index.
*
* Allocates a thread local storage index
*
* RETURNS
* Success: TLS Index
* Failure: 0xFFFFFFFF
*/
DWORD
WINAPI
TlsAlloc
(
void
)
{
DWORD
i
,
mask
,
ret
=
0
;
DWORD
*
bits
=
current_process
.
tls_bits
;
RtlAcquirePebLock
();
if
(
*
bits
==
0xffffffff
)
{
bits
++
;
ret
=
32
;
if
(
*
bits
==
0xffffffff
)
{
RtlReleasePebLock
();
SetLastError
(
ERROR_NO_MORE_ITEMS
);
return
0xffffffff
;
}
}
for
(
i
=
0
,
mask
=
1
;
i
<
32
;
i
++
,
mask
<<=
1
)
if
(
!
(
*
bits
&
mask
))
break
;
*
bits
|=
mask
;
RtlReleasePebLock
();
NtCurrentTeb
()
->
TlsSlots
[
ret
+
i
]
=
0
;
/* clear the value */
return
ret
+
i
;
}
/**********************************************************************
* TlsFree [KERNEL32.@] Releases a TLS index.
*
* Releases a thread local storage index, making it available for reuse
*
* RETURNS
* Success: TRUE
* Failure: FALSE
*/
BOOL
WINAPI
TlsFree
(
DWORD
index
)
/* [in] TLS Index to free */
{
DWORD
mask
=
(
1
<<
(
index
&
31
));
DWORD
*
bits
=
current_process
.
tls_bits
;
if
(
index
>=
64
)
{
SetLastError
(
ERROR_INVALID_PARAMETER
);
return
FALSE
;
}
if
(
index
>=
32
)
bits
++
;
RtlAcquirePebLock
();
if
(
!
(
*
bits
&
mask
))
/* already free? */
{
RtlReleasePebLock
();
SetLastError
(
ERROR_INVALID_PARAMETER
);
return
FALSE
;
}
*
bits
&=
~
mask
;
NtSetInformationThread
(
GetCurrentThread
(),
ThreadZeroTlsCell
,
&
index
,
sizeof
(
index
)
);
RtlReleasePebLock
();
return
TRUE
;
}
/**********************************************************************
* TlsGetValue [KERNEL32.@] Gets value in a thread's TLS slot
*
* RETURNS
* Success: Value stored in calling thread's TLS slot for index
* Failure: 0 and GetLastError returns NO_ERROR
*/
LPVOID
WINAPI
TlsGetValue
(
DWORD
index
)
/* [in] TLS index to retrieve value for */
{
if
(
index
>=
64
)
{
SetLastError
(
ERROR_INVALID_PARAMETER
);
return
NULL
;
}
SetLastError
(
ERROR_SUCCESS
);
return
NtCurrentTeb
()
->
TlsSlots
[
index
];
}
/**********************************************************************
* TlsSetValue [KERNEL32.@] Stores a value in the thread's TLS slot.
*
* RETURNS
* Success: TRUE
* Failure: FALSE
*/
BOOL
WINAPI
TlsSetValue
(
DWORD
index
,
/* [in] TLS index to set value for */
LPVOID
value
)
/* [in] Value to be stored */
{
if
(
index
>=
64
)
{
SetLastError
(
ERROR_INVALID_PARAMETER
);
return
FALSE
;
}
NtCurrentTeb
()
->
TlsSlots
[
index
]
=
value
;
return
TRUE
;
}
/***********************************************************************
...
...
@@ -426,6 +2020,20 @@ BOOL WINAPI WriteProcessMemory( HANDLE process, LPVOID addr, LPCVOID buffer, SIZ
/***********************************************************************
* ProcessIdToSessionId (KERNEL32.@)
* This function is available on Terminal Server 4SP4 and Windows 2000
*/
BOOL
WINAPI
ProcessIdToSessionId
(
DWORD
procid
,
DWORD
*
sessionid_ptr
)
{
/* According to MSDN, if the calling process is not in a terminal
* services environment, then the sessionid returned is zero.
*/
*
sessionid_ptr
=
0
;
return
TRUE
;
}
/***********************************************************************
* RegisterServiceProcess (KERNEL.491)
* RegisterServiceProcess (KERNEL32.@)
*
...
...
dlls/kernel/thread.c
View file @
aaf3503e
...
...
@@ -37,11 +37,346 @@
#include "winbase.h"
#include "winerror.h"
#include "winnls.h"
#include "module.h"
#include "thread.h"
#include "wine/winbase16.h"
#include "wine/library.h"
#include "wine/server.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL
(
thread
);
WINE_DECLARE_DEBUG_CHANNEL
(
relay
);
/* TEB of the initial thread */
static
TEB
initial_teb
;
extern
struct
_PDB
current_process
;
/***********************************************************************
* THREAD_InitTEB
*
* Initialization of a newly created TEB.
*/
static
BOOL
THREAD_InitTEB
(
TEB
*
teb
)
{
teb
->
Tib
.
ExceptionList
=
(
void
*
)
~
0UL
;
teb
->
Tib
.
StackBase
=
(
void
*
)
~
0UL
;
teb
->
Tib
.
Self
=
&
teb
->
Tib
;
teb
->
tibflags
=
TEBF_WIN32
;
teb
->
exit_code
=
STILL_ACTIVE
;
teb
->
request_fd
=
-
1
;
teb
->
reply_fd
=
-
1
;
teb
->
wait_fd
[
0
]
=
-
1
;
teb
->
wait_fd
[
1
]
=
-
1
;
teb
->
StaticUnicodeString
.
MaximumLength
=
sizeof
(
teb
->
StaticUnicodeBuffer
);
teb
->
StaticUnicodeString
.
Buffer
=
(
PWSTR
)
teb
->
StaticUnicodeBuffer
;
InitializeListHead
(
&
teb
->
TlsLinks
);
teb
->
teb_sel
=
wine_ldt_alloc_fs
();
return
(
teb
->
teb_sel
!=
0
);
}
/***********************************************************************
* THREAD_InitStack
*
* Allocate the stack of a thread.
*/
TEB
*
THREAD_InitStack
(
TEB
*
teb
,
DWORD
stack_size
)
{
DWORD
old_prot
,
total_size
;
DWORD
page_size
=
getpagesize
();
void
*
base
;
/* Allocate the stack */
/* if size is smaller than default, get stack size from parent */
if
(
stack_size
<
1024
*
1024
)
{
if
(
teb
)
stack_size
=
1024
*
1024
;
/* no parent */
else
stack_size
=
((
char
*
)
NtCurrentTeb
()
->
Tib
.
StackBase
-
(
char
*
)
NtCurrentTeb
()
->
DeallocationStack
-
SIGNAL_STACK_SIZE
-
3
*
page_size
);
}
/* FIXME: some Wine functions use a lot of stack, so we add 64Kb here */
stack_size
+=
64
*
1024
;
/* Memory layout in allocated block:
*
* size contents
* 1 page NOACCESS guard page
* SIGNAL_STACK_SIZE signal stack
* 1 page NOACCESS guard page
* 1 page PAGE_GUARD guard page
* stack_size normal stack
* 1 page TEB (except for initial thread)
*/
stack_size
=
(
stack_size
+
(
page_size
-
1
))
&
~
(
page_size
-
1
);
total_size
=
stack_size
+
SIGNAL_STACK_SIZE
+
3
*
page_size
;
if
(
!
teb
)
total_size
+=
page_size
;
if
(
!
(
base
=
VirtualAlloc
(
NULL
,
total_size
,
MEM_COMMIT
,
PAGE_EXECUTE_READWRITE
)))
return
NULL
;
if
(
!
teb
)
{
teb
=
(
TEB
*
)((
char
*
)
base
+
total_size
-
page_size
);
if
(
!
THREAD_InitTEB
(
teb
))
{
VirtualFree
(
base
,
0
,
MEM_RELEASE
);
return
NULL
;
}
}
teb
->
DeallocationStack
=
base
;
teb
->
signal_stack
=
(
char
*
)
base
+
page_size
;
teb
->
Tib
.
StackBase
=
(
char
*
)
base
+
3
*
page_size
+
SIGNAL_STACK_SIZE
+
stack_size
;
teb
->
Tib
.
StackLimit
=
base
;
/* note: limit is lower than base since the stack grows down */
/* Setup guard pages */
VirtualProtect
(
base
,
1
,
PAGE_NOACCESS
,
&
old_prot
);
VirtualProtect
(
(
char
*
)
teb
->
signal_stack
+
SIGNAL_STACK_SIZE
,
1
,
PAGE_NOACCESS
,
&
old_prot
);
VirtualProtect
(
(
char
*
)
teb
->
signal_stack
+
SIGNAL_STACK_SIZE
+
page_size
,
1
,
PAGE_EXECUTE_READWRITE
|
PAGE_GUARD
,
&
old_prot
);
return
teb
;
}
/***********************************************************************
* THREAD_Init
*
* Setup the initial thread.
*
* NOTES: The first allocated TEB on NT is at 0x7ffde000.
*/
void
THREAD_Init
(
void
)
{
static
struct
debug_info
info
;
/* debug info for initial thread */
if
(
!
initial_teb
.
Tib
.
Self
)
/* do it only once */
{
THREAD_InitTEB
(
&
initial_teb
);
assert
(
initial_teb
.
teb_sel
);
info
.
str_pos
=
info
.
strings
;
info
.
out_pos
=
info
.
output
;
initial_teb
.
debug_info
=
&
info
;
initial_teb
.
Peb
=
(
PEB
*
)
&
current_process
;
/* FIXME */
SYSDEPS_SetCurThread
(
&
initial_teb
);
}
}
DECL_GLOBAL_CONSTRUCTOR
(
thread_init
)
{
THREAD_Init
();
}
/***********************************************************************
* THREAD_Start
*
* Start execution of a newly created thread. Does not return.
*/
static
void
THREAD_Start
(
TEB
*
teb
)
{
LPTHREAD_START_ROUTINE
func
=
(
LPTHREAD_START_ROUTINE
)
teb
->
entry_point
;
struct
debug_info
info
;
info
.
str_pos
=
info
.
strings
;
info
.
out_pos
=
info
.
output
;
teb
->
debug_info
=
&
info
;
SYSDEPS_SetCurThread
(
teb
);
SIGNAL_Init
();
CLIENT_InitThread
();
if
(
TRACE_ON
(
relay
))
DPRINTF
(
"%04lx:Starting thread (entryproc=%p)
\n
"
,
GetCurrentThreadId
(),
func
);
__TRY
{
MODULE_DllThreadAttach
(
NULL
);
ExitThread
(
func
(
NtCurrentTeb
()
->
entry_arg
)
);
}
__EXCEPT
(
UnhandledExceptionFilter
)
{
TerminateThread
(
GetCurrentThread
(),
GetExceptionCode
()
);
}
__ENDTRY
}
/***********************************************************************
* CreateThread (KERNEL32.@)
*/
HANDLE
WINAPI
CreateThread
(
SECURITY_ATTRIBUTES
*
sa
,
SIZE_T
stack
,
LPTHREAD_START_ROUTINE
start
,
LPVOID
param
,
DWORD
flags
,
LPDWORD
id
)
{
HANDLE
handle
=
0
;
TEB
*
teb
;
DWORD
tid
=
0
;
int
request_pipe
[
2
];
if
(
pipe
(
request_pipe
)
==
-
1
)
{
SetLastError
(
ERROR_TOO_MANY_OPEN_FILES
);
return
0
;
}
fcntl
(
request_pipe
[
1
],
F_SETFD
,
1
);
/* set close on exec flag */
wine_server_send_fd
(
request_pipe
[
0
]
);
SERVER_START_REQ
(
new_thread
)
{
req
->
suspend
=
((
flags
&
CREATE_SUSPENDED
)
!=
0
);
req
->
inherit
=
(
sa
&&
(
sa
->
nLength
>=
sizeof
(
*
sa
))
&&
sa
->
bInheritHandle
);
req
->
request_fd
=
request_pipe
[
0
];
if
(
!
wine_server_call_err
(
req
))
{
handle
=
reply
->
handle
;
tid
=
reply
->
tid
;
}
close
(
request_pipe
[
0
]
);
}
SERVER_END_REQ
;
if
(
!
handle
||
!
(
teb
=
THREAD_InitStack
(
NULL
,
stack
)))
{
close
(
request_pipe
[
1
]
);
return
0
;
}
teb
->
Peb
=
NtCurrentTeb
()
->
Peb
;
teb
->
ClientId
.
UniqueThread
=
(
HANDLE
)
tid
;
teb
->
request_fd
=
request_pipe
[
1
];
teb
->
entry_point
=
start
;
teb
->
entry_arg
=
param
;
teb
->
htask16
=
GetCurrentTask
();
RtlAcquirePebLock
();
InsertHeadList
(
&
NtCurrentTeb
()
->
TlsLinks
,
&
teb
->
TlsLinks
);
RtlReleasePebLock
();
if
(
id
)
*
id
=
tid
;
if
(
SYSDEPS_SpawnThread
(
THREAD_Start
,
teb
)
==
-
1
)
{
CloseHandle
(
handle
);
close
(
request_pipe
[
1
]
);
RtlAcquirePebLock
();
RemoveEntryList
(
&
teb
->
TlsLinks
);
RtlReleasePebLock
();
wine_ldt_free_fs
(
teb
->
teb_sel
);
VirtualFree
(
teb
->
DeallocationStack
,
0
,
MEM_RELEASE
);
return
0
;
}
return
handle
;
}
/***********************************************************************
* OpenThread [KERNEL32.@] Retrieves a handle to a thread from its thread id
*/
HANDLE
WINAPI
OpenThread
(
DWORD
dwDesiredAccess
,
BOOL
bInheritHandle
,
DWORD
dwThreadId
)
{
HANDLE
ret
=
0
;
SERVER_START_REQ
(
open_thread
)
{
req
->
tid
=
dwThreadId
;
req
->
access
=
dwDesiredAccess
;
req
->
inherit
=
bInheritHandle
;
if
(
!
wine_server_call_err
(
req
))
ret
=
reply
->
handle
;
}
SERVER_END_REQ
;
return
ret
;
}
/***********************************************************************
* ExitThread [KERNEL32.@] Ends a thread
*
* RETURNS
* None
*/
void
WINAPI
ExitThread
(
DWORD
code
)
/* [in] Exit code for this thread */
{
BOOL
last
;
SERVER_START_REQ
(
terminate_thread
)
{
/* send the exit code to the server */
req
->
handle
=
GetCurrentThread
();
req
->
exit_code
=
code
;
wine_server_call
(
req
);
last
=
reply
->
last
;
}
SERVER_END_REQ
;
if
(
last
)
{
LdrShutdownProcess
();
exit
(
code
);
}
else
{
LdrShutdownThread
();
RtlAcquirePebLock
();
RemoveEntryList
(
&
NtCurrentTeb
()
->
TlsLinks
);
RtlReleasePebLock
();
SYSDEPS_ExitThread
(
code
);
}
}
/**********************************************************************
* TerminateThread [KERNEL32.@] Terminates a thread
*
* RETURNS
* Success: TRUE
* Failure: FALSE
*/
BOOL
WINAPI
TerminateThread
(
HANDLE
handle
,
/* [in] Handle to thread */
DWORD
exit_code
)
/* [in] Exit code for thread */
{
NTSTATUS
status
=
NtTerminateThread
(
handle
,
exit_code
);
if
(
status
)
SetLastError
(
RtlNtStatusToDosError
(
status
)
);
return
!
status
;
}
/***********************************************************************
* FreeLibraryAndExitThread (KERNEL32.@)
*/
void
WINAPI
FreeLibraryAndExitThread
(
HINSTANCE
hLibModule
,
DWORD
dwExitCode
)
{
FreeLibrary
(
hLibModule
);
ExitThread
(
dwExitCode
);
}
/**********************************************************************
* GetExitCodeThread (KERNEL32.@)
*
* Gets termination status of thread.
*
* RETURNS
* Success: TRUE
* Failure: FALSE
*/
BOOL
WINAPI
GetExitCodeThread
(
HANDLE
hthread
,
/* [in] Handle to thread */
LPDWORD
exitcode
)
/* [out] Address to receive termination status */
{
THREAD_BASIC_INFORMATION
info
;
NTSTATUS
status
=
NtQueryInformationThread
(
hthread
,
ThreadBasicInformation
,
&
info
,
sizeof
(
info
),
NULL
);
if
(
status
)
{
SetLastError
(
RtlNtStatusToDosError
(
status
)
);
return
FALSE
;
}
if
(
exitcode
)
*
exitcode
=
info
.
ExitStatus
;
return
TRUE
;
}
/***********************************************************************
...
...
dlls/ntdll/Makefile.in
View file @
aaf3503e
...
...
@@ -37,7 +37,6 @@ C_SRCS = \
$(TOPOBJDIR)
/relay32/relay386.c
\
$(TOPOBJDIR)
/relay32/snoop.c
\
$(TOPOBJDIR)
/scheduler/client.c
\
$(TOPOBJDIR)
/scheduler/fiber.c
\
$(TOPOBJDIR)
/scheduler/handle.c
\
$(TOPOBJDIR)
/scheduler/process.c
\
$(TOPOBJDIR)
/scheduler/pthread.c
\
...
...
@@ -45,7 +44,6 @@ C_SRCS = \
$(TOPOBJDIR)
/scheduler/syslevel.c
\
$(TOPOBJDIR)
/scheduler/thread.c
\
$(TOPOBJDIR)
/win32/device.c
\
$(TOPOBJDIR)
/win32/except.c
\
$(TOPOBJDIR)
/win32/newfns.c
\
cdrom.c
\
critsection.c
\
...
...
libs/wine/loader.c
View file @
aaf3503e
...
...
@@ -418,13 +418,13 @@ void *wine_dll_load_main_exe( const char *name, char *error, int errorsize,
void
wine_init
(
int
argc
,
char
*
argv
[],
char
*
error
,
int
error_size
)
{
int
file_exists
;
void
*
ntdl
l
;
void
*
kerne
l
;
void
(
*
init_func
)(
int
,
char
**
);
if
(
!
(
ntdll
=
dlopen_dll
(
"ntdll.dll"
,
error
,
error_size
,
0
,
&
file_exists
)
))
return
;
if
(
!
dlopen_dll
(
"ntdll.dll"
,
error
,
error_size
,
0
,
&
file_exists
))
return
;
/* make sure kernel32 is loaded too */
if
(
!
dlopen_dll
(
"kernel32.dll"
,
error
,
error_size
,
0
,
&
file_exists
))
return
;
if
(
!
(
init_func
=
wine_dlsym
(
ntdl
l
,
"__wine_process_init"
,
error
,
error_size
)))
return
;
if
(
!
(
kernel
=
dlopen_dll
(
"kernel32.dll"
,
error
,
error_size
,
0
,
&
file_exists
)
))
return
;
if
(
!
(
init_func
=
wine_dlsym
(
kerne
l
,
"__wine_process_init"
,
error
,
error_size
)))
return
;
init_func
(
argc
,
argv
);
}
...
...
loader/module.c
View file @
aaf3503e
...
...
@@ -52,25 +52,6 @@ WINE_DECLARE_DEBUG_CHANNEL(win32);
WINE_DECLARE_DEBUG_CHANNEL
(
loaddll
);
/***********************************************************************
* 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
;
}
/****************************************************************************
* DisableThreadLibraryCalls (KERNEL32.@)
*
...
...
@@ -388,104 +369,6 @@ BOOL WINAPI GetBinaryTypeW( LPCWSTR lpApplicationName, LPDWORD lpBinaryType )
/***********************************************************************
* WinExec (KERNEL32.@)
*/
UINT
WINAPI
WinExec
(
LPCSTR
lpCmdLine
,
UINT
nCmdShow
)
{
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
;
/* cmdline needs to be writeable for CreateProcess */
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 */
if
(
wait_input_idle
(
info
.
hProcess
,
30000
)
==
0xFFFFFFFF
)
WARN
(
"WaitForInputIdle failed: Error %ld
\n
"
,
GetLastError
()
);
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.@)
*/
HINSTANCE
WINAPI
LoadModule
(
LPCSTR
name
,
LPVOID
paramBlock
)
{
LOADPARAMS
*
params
=
(
LOADPARAMS
*
)
paramBlock
;
PROCESS_INFORMATION
info
;
STARTUPINFOA
startup
;
HINSTANCE
hInstance
;
LPSTR
cmdline
,
p
;
char
filename
[
MAX_PATH
];
BYTE
len
;
if
(
!
name
)
return
(
HINSTANCE
)
ERROR_FILE_NOT_FOUND
;
if
(
!
SearchPathA
(
NULL
,
name
,
".exe"
,
sizeof
(
filename
),
filename
,
NULL
)
&&
!
SearchPathA
(
NULL
,
name
,
NULL
,
sizeof
(
filename
),
filename
,
NULL
))
return
(
HINSTANCE
)
GetLastError
();
len
=
(
BYTE
)
params
->
lpCmdLine
[
0
];
if
(
!
(
cmdline
=
HeapAlloc
(
GetProcessHeap
(),
0
,
strlen
(
filename
)
+
len
+
2
)))
return
(
HINSTANCE
)
ERROR_NOT_ENOUGH_MEMORY
;
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
;
startup
.
wShowWindow
=
params
->
lpCmdShow
[
1
];
}
if
(
CreateProcessA
(
filename
,
cmdline
,
NULL
,
NULL
,
FALSE
,
0
,
params
->
lpEnvAddress
,
NULL
,
&
startup
,
&
info
))
{
/* Give 30 seconds to the app to come up */
if
(
wait_input_idle
(
info
.
hProcess
,
30000
)
==
0xFFFFFFFF
)
WARN
(
"WaitForInputIdle failed: Error %ld
\n
"
,
GetLastError
()
);
hInstance
=
(
HINSTANCE
)
33
;
/* Close off the handles */
CloseHandle
(
info
.
hThread
);
CloseHandle
(
info
.
hProcess
);
}
else
if
((
hInstance
=
(
HINSTANCE
)
GetLastError
())
>=
(
HINSTANCE
)
32
)
{
FIXME
(
"Strange error set by CreateProcess: %p
\n
"
,
hInstance
);
hInstance
=
(
HINSTANCE
)
11
;
}
HeapFree
(
GetProcessHeap
(),
0
,
cmdline
);
return
hInstance
;
}
/***********************************************************************
* GetModuleHandleA (KERNEL32.@)
* GetModuleHandle32 (KERNEL.488)
*/
...
...
@@ -779,15 +662,6 @@ BOOL WINAPI FreeLibrary(HINSTANCE hLibModule)
}
/***********************************************************************
* FreeLibraryAndExitThread (KERNEL32.@)
*/
VOID
WINAPI
FreeLibraryAndExitThread
(
HINSTANCE
hLibModule
,
DWORD
dwExitCode
)
{
FreeLibrary
(
hLibModule
);
ExitThread
(
dwExitCode
);
}
/***********************************************************************
* GetProcAddress (KERNEL32.@)
*/
FARPROC
WINAPI
GetProcAddress
(
HMODULE
hModule
,
LPCSTR
function
)
...
...
scheduler/process.c
View file @
aaf3503e
...
...
@@ -21,1347 +21,17 @@
#include "config.h"
#include "wine/port.h"
#include <assert.h>
#include <ctype.h>
#include <locale.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include "wine/winbase16.h"
#include "wine/winuser16.h"
#include "wine/exception.h"
#include "wine/library.h"
#include "drive.h"
#include "module.h"
#include "file.h"
#include "heap.h"
#include "thread.h"
#include "winerror.h"
#include "wincon.h"
#include "wine/server.h"
#include "options.h"
#include "wine/debug.h"
#include "../kernel/kernel_private.h"
WINE_DEFAULT_DEBUG_CHANNEL
(
process
);
WINE_DECLARE_DEBUG_CHANNEL
(
server
);
WINE_DECLARE_DEBUG_CHANNEL
(
relay
);
WINE_DECLARE_DEBUG_CHANNEL
(
snoop
);
WINE_DECLARE_DEBUG_CHANNEL
(
win32
);
/* Win32 process database */
typedef
struct
_PDB
{
LONG
header
[
2
];
/* 00 Kernel object header */
HMODULE
module
;
/* 08 Main exe module (NT) */
PPEB_LDR_DATA
LdrData
;
/* 0c Pointer to loader information */
RTL_USER_PROCESS_PARAMETERS
*
ProcessParameters
;
/* 10 Process parameters */
DWORD
unknown2
;
/* 14 Unknown */
HANDLE
heap
;
/* 18 Default process heap */
HANDLE
mem_context
;
/* 1c Process memory context */
DWORD
flags
;
/* 20 Flags */
void
*
pdb16
;
/* 24 DOS PSP */
WORD
PSP_sel
;
/* 28 Selector to DOS PSP */
WORD
imte
;
/* 2a IMTE for the process module */
WORD
threads
;
/* 2c Number of threads */
WORD
running_threads
;
/* 2e Number of running threads */
WORD
free_lib_count
;
/* 30 Recursion depth of FreeLibrary calls */
WORD
ring0_threads
;
/* 32 Number of ring 0 threads */
HANDLE
system_heap
;
/* 34 System heap to allocate handles */
HTASK
task
;
/* 38 Win16 task */
void
*
mem_map_files
;
/* 3c Pointer to mem-mapped files */
struct
_ENVDB
*
env_db
;
/* 40 Environment database */
void
*
handle_table
;
/* 44 Handle table */
struct
_PDB
*
parent
;
/* 48 Parent process */
void
*
modref_list
;
/* 4c MODREF list */
void
*
thread_list
;
/* 50 List of threads */
void
*
debuggee_CB
;
/* 54 Debuggee context block */
void
*
local_heap_free
;
/* 58 Head of local heap free list */
DWORD
unknown4
;
/* 5c Unknown */
CRITICAL_SECTION
crit_section
;
/* 60 Critical section */
DWORD
unknown5
[
3
];
/* 78 Unknown */
void
*
console
;
/* 84 Console */
DWORD
tls_bits
[
2
];
/* 88 TLS in-use bits */
DWORD
process_dword
;
/* 90 Unknown */
struct
_PDB
*
group
;
/* 94 Process group */
void
*
exe_modref
;
/* 98 MODREF for the process EXE */
void
*
top_filter
;
/* 9c Top exception filter */
DWORD
priority
;
/* a0 Priority level */
HANDLE
heap_list
;
/* a4 Head of process heap list */
void
*
heap_handles
;
/* a8 Head of heap handles list */
DWORD
unknown6
;
/* ac Unknown */
void
*
console_provider
;
/* b0 Console provider (??) */
WORD
env_selector
;
/* b4 Selector to process environment */
WORD
error_mode
;
/* b6 Error mode */
HANDLE
load_done_evt
;
/* b8 Event for process loading done */
void
*
UTState
;
/* bc Head of Univeral Thunk list */
DWORD
unknown8
;
/* c0 Unknown (NT) */
LCID
locale
;
/* c4 Locale to be queried by GetThreadLocale (NT) */
}
PDB
;
PDB
current_process
;
static
RTL_USER_PROCESS_PARAMETERS
process_pmts
;
static
PEB_LDR_DATA
process_ldr
;
static
char
main_exe_name
[
MAX_PATH
];
static
char
*
main_exe_name_ptr
=
main_exe_name
;
static
HANDLE
main_exe_file
;
static
unsigned
int
server_startticks
;
int
main_create_flags
=
0
;
/* dlls/ntdll/env.c */
extern
BOOL
init_user_process_pmts
(
size_t
,
char
*
,
size_t
);
extern
BOOL
build_command_line
(
char
**
argv
);
extern
void
RELAY_InitDebugLists
(
void
);
extern
void
SHELL_LoadRegistry
(
void
);
extern
void
VERSION_Init
(
const
char
*
appname
);
/***********************************************************************
* get_basename
*/
inline
static
const
char
*
get_basename
(
const
char
*
name
)
{
char
*
p
;
if
((
p
=
strrchr
(
name
,
'/'
)))
name
=
p
+
1
;
if
((
p
=
strrchr
(
name
,
'\\'
)))
name
=
p
+
1
;
return
name
;
}
/***********************************************************************
* open_builtin_exe_file
*
* Open an exe file for a builtin exe.
*/
static
void
*
open_builtin_exe_file
(
const
char
*
name
,
char
*
error
,
int
error_size
,
int
test_only
,
int
*
file_exists
)
{
char
exename
[
MAX_PATH
],
*
p
;
const
char
*
basename
=
get_basename
(
name
);
if
(
strlen
(
basename
)
>=
sizeof
(
exename
))
return
NULL
;
strcpy
(
exename
,
basename
);
for
(
p
=
exename
;
*
p
;
p
++
)
*
p
=
FILE_tolower
(
*
p
);
return
wine_dll_load_main_exe
(
exename
,
error
,
error_size
,
test_only
,
file_exists
);
}
/***********************************************************************
* open_exe_file
*
* Open a specific exe file, taking load order into account.
* Returns the file handle or 0 for a builtin exe.
*/
static
HANDLE
open_exe_file
(
const
char
*
name
)
{
enum
loadorder_type
loadorder
[
LOADORDER_NTYPES
];
char
buffer
[
MAX_PATH
];
HANDLE
handle
;
int
i
,
file_exists
;
TRACE
(
"looking for %s
\n
"
,
debugstr_a
(
name
)
);
if
((
handle
=
CreateFileA
(
name
,
GENERIC_READ
,
FILE_SHARE_READ
,
NULL
,
OPEN_EXISTING
,
0
,
0
))
==
INVALID_HANDLE_VALUE
)
{
/* file doesn't exist, check for builtin */
if
(
!
FILE_contains_path
(
name
))
goto
error
;
if
(
!
MODULE_GetBuiltinPath
(
name
,
""
,
buffer
,
sizeof
(
buffer
)
))
goto
error
;
name
=
buffer
;
}
MODULE_GetLoadOrder
(
loadorder
,
name
,
TRUE
);
for
(
i
=
0
;
i
<
LOADORDER_NTYPES
;
i
++
)
{
if
(
loadorder
[
i
]
==
LOADORDER_INVALID
)
break
;
switch
(
loadorder
[
i
])
{
case
LOADORDER_DLL
:
TRACE
(
"Trying native exe %s
\n
"
,
debugstr_a
(
name
)
);
if
(
handle
!=
INVALID_HANDLE_VALUE
)
return
handle
;
break
;
case
LOADORDER_BI
:
TRACE
(
"Trying built-in exe %s
\n
"
,
debugstr_a
(
name
)
);
open_builtin_exe_file
(
name
,
NULL
,
0
,
1
,
&
file_exists
);
if
(
file_exists
)
{
if
(
handle
!=
INVALID_HANDLE_VALUE
)
CloseHandle
(
handle
);
return
0
;
}
default:
break
;
}
}
if
(
handle
!=
INVALID_HANDLE_VALUE
)
CloseHandle
(
handle
);
error:
SetLastError
(
ERROR_FILE_NOT_FOUND
);
return
INVALID_HANDLE_VALUE
;
}
/***********************************************************************
* find_exe_file
*
* Open an exe file, and return the full name and file handle.
* Returns FALSE if file could not be found.
* If file exists but cannot be opened, returns TRUE and set handle to INVALID_HANDLE_VALUE.
* If file is a builtin exe, returns TRUE and sets handle to 0.
*/
static
BOOL
find_exe_file
(
const
char
*
name
,
char
*
buffer
,
int
buflen
,
HANDLE
*
handle
)
{
enum
loadorder_type
loadorder
[
LOADORDER_NTYPES
];
int
i
,
file_exists
;
TRACE
(
"looking for %s
\n
"
,
debugstr_a
(
name
)
);
if
(
!
SearchPathA
(
NULL
,
name
,
".exe"
,
buflen
,
buffer
,
NULL
)
&&
!
MODULE_GetBuiltinPath
(
name
,
".exe"
,
buffer
,
buflen
))
{
/* no builtin found, try native without extension in case it is a Unix app */
if
(
SearchPathA
(
NULL
,
name
,
NULL
,
buflen
,
buffer
,
NULL
))
{
TRACE
(
"Trying native/Unix binary %s
\n
"
,
debugstr_a
(
buffer
)
);
if
((
*
handle
=
CreateFileA
(
buffer
,
GENERIC_READ
,
FILE_SHARE_READ
,
NULL
,
OPEN_EXISTING
,
0
,
0
))
!=
INVALID_HANDLE_VALUE
)
return
TRUE
;
}
return
FALSE
;
}
MODULE_GetLoadOrder
(
loadorder
,
buffer
,
TRUE
);
for
(
i
=
0
;
i
<
LOADORDER_NTYPES
;
i
++
)
{
if
(
loadorder
[
i
]
==
LOADORDER_INVALID
)
break
;
switch
(
loadorder
[
i
])
{
case
LOADORDER_DLL
:
TRACE
(
"Trying native exe %s
\n
"
,
debugstr_a
(
buffer
)
);
if
((
*
handle
=
CreateFileA
(
buffer
,
GENERIC_READ
,
FILE_SHARE_READ
,
NULL
,
OPEN_EXISTING
,
0
,
0
))
!=
INVALID_HANDLE_VALUE
)
return
TRUE
;
if
(
GetLastError
()
!=
ERROR_FILE_NOT_FOUND
)
return
TRUE
;
break
;
case
LOADORDER_BI
:
TRACE
(
"Trying built-in exe %s
\n
"
,
debugstr_a
(
buffer
)
);
open_builtin_exe_file
(
buffer
,
NULL
,
0
,
1
,
&
file_exists
);
if
(
file_exists
)
{
*
handle
=
0
;
return
TRUE
;
}
break
;
default:
break
;
}
}
SetLastError
(
ERROR_FILE_NOT_FOUND
);
return
FALSE
;
}
/***********************************************************************
* process_init
*
* Main process initialisation code
*/
static
BOOL
process_init
(
char
*
argv
[]
)
{
BOOL
ret
;
size_t
info_size
=
0
;
setbuf
(
stdout
,
NULL
);
setbuf
(
stderr
,
NULL
);
setlocale
(
LC_CTYPE
,
""
);
/* store the program name */
argv0
=
argv
[
0
];
/* Fill the initial process structure */
current_process
.
threads
=
1
;
current_process
.
running_threads
=
1
;
current_process
.
ring0_threads
=
1
;
current_process
.
group
=
&
current_process
;
current_process
.
priority
=
8
;
/* Normal */
current_process
.
ProcessParameters
=
&
process_pmts
;
current_process
.
LdrData
=
&
process_ldr
;
InitializeListHead
(
&
process_ldr
.
InLoadOrderModuleList
);
InitializeListHead
(
&
process_ldr
.
InMemoryOrderModuleList
);
InitializeListHead
(
&
process_ldr
.
InInitializationOrderModuleList
);
/* Setup the server connection */
CLIENT_InitServer
();
/* Retrieve startup info from the server */
SERVER_START_REQ
(
init_process
)
{
req
->
ldt_copy
=
&
wine_ldt_copy
;
if
((
ret
=
!
wine_server_call_err
(
req
)))
{
main_exe_file
=
reply
->
exe_file
;
main_create_flags
=
reply
->
create_flags
;
info_size
=
reply
->
info_size
;
server_startticks
=
reply
->
server_start
;
process_pmts
.
hStdInput
=
reply
->
hstdin
;
process_pmts
.
hStdOutput
=
reply
->
hstdout
;
process_pmts
.
hStdError
=
reply
->
hstderr
;
}
}
SERVER_END_REQ
;
if
(
!
ret
)
return
FALSE
;
/* Create the process heap */
current_process
.
heap
=
RtlCreateHeap
(
HEAP_GROWABLE
,
NULL
,
0
,
0
,
NULL
,
NULL
);
if
(
info_size
==
0
)
{
/* This is wine specific: we have no parent (we're started from unix)
* so, create a simple console with bare handles to unix stdio
* input & output streams (aka simple console)
*/
wine_server_fd_to_handle
(
0
,
GENERIC_READ
|
SYNCHRONIZE
,
TRUE
,
&
process_pmts
.
hStdInput
);
wine_server_fd_to_handle
(
1
,
GENERIC_WRITE
|
SYNCHRONIZE
,
TRUE
,
&
process_pmts
.
hStdOutput
);
wine_server_fd_to_handle
(
1
,
GENERIC_WRITE
|
SYNCHRONIZE
,
TRUE
,
&
process_pmts
.
hStdError
);
}
else
{
/* convert value from server:
* + 0 => INVALID_HANDLE_VALUE
* + console handle need to be mapped
*/
if
(
!
process_pmts
.
hStdInput
)
process_pmts
.
hStdInput
=
INVALID_HANDLE_VALUE
;
else
if
(
VerifyConsoleIoHandle
(
console_handle_map
(
process_pmts
.
hStdInput
)))
process_pmts
.
hStdInput
=
console_handle_map
(
process_pmts
.
hStdInput
);
if
(
!
process_pmts
.
hStdOutput
)
process_pmts
.
hStdOutput
=
INVALID_HANDLE_VALUE
;
else
if
(
VerifyConsoleIoHandle
(
console_handle_map
(
process_pmts
.
hStdOutput
)))
process_pmts
.
hStdOutput
=
console_handle_map
(
process_pmts
.
hStdOutput
);
if
(
!
process_pmts
.
hStdError
)
process_pmts
.
hStdError
=
INVALID_HANDLE_VALUE
;
else
if
(
VerifyConsoleIoHandle
(
console_handle_map
(
process_pmts
.
hStdError
)))
process_pmts
.
hStdError
=
console_handle_map
(
process_pmts
.
hStdError
);
}
/* Copy the parent environment */
if
(
!
init_user_process_pmts
(
info_size
,
main_exe_name
,
sizeof
(
main_exe_name
)
))
return
FALSE
;
/* Parse command line arguments */
OPTIONS_ParseOptions
(
!
info_size
?
argv
:
NULL
);
/* <hack: to be changed later on> */
process_pmts
.
CurrentDirectoryName
.
Length
=
3
*
sizeof
(
WCHAR
);
process_pmts
.
CurrentDirectoryName
.
MaximumLength
=
RtlGetLongestNtPathLength
()
*
sizeof
(
WCHAR
);
process_pmts
.
CurrentDirectoryName
.
Buffer
=
RtlAllocateHeap
(
GetProcessHeap
(),
0
,
process_pmts
.
CurrentDirectoryName
.
MaximumLength
);
process_pmts
.
CurrentDirectoryName
.
Buffer
[
0
]
=
'C'
;
process_pmts
.
CurrentDirectoryName
.
Buffer
[
1
]
=
':'
;
process_pmts
.
CurrentDirectoryName
.
Buffer
[
2
]
=
'\\'
;
process_pmts
.
CurrentDirectoryName
.
Buffer
[
3
]
=
'\0'
;
/* </hack: to be changed later on> */
/* initialise DOS drives */
if
(
!
DRIVE_Init
())
return
FALSE
;
/* initialise DOS directories */
if
(
!
DIR_Init
())
return
FALSE
;
/* registry initialisation */
SHELL_LoadRegistry
();
/* global boot finished, the rest is process-local */
CLIENT_BootDone
(
TRACE_ON
(
server
)
);
if
(
TRACE_ON
(
relay
)
||
TRACE_ON
(
snoop
))
RELAY_InitDebugLists
();
return
TRUE
;
}
/***********************************************************************
* start_process
*
* Startup routine of a new process. Runs on the new process stack.
*/
static
void
start_process
(
void
*
arg
)
{
__TRY
{
LPTHREAD_START_ROUTINE
entry
;
HANDLE
main_file
=
main_exe_file
;
IMAGE_NT_HEADERS
*
nt
;
PEB
*
peb
=
NtCurrentTeb
()
->
Peb
;
if
(
main_file
)
{
UINT
drive_type
=
GetDriveTypeA
(
main_exe_name
);
/* don't keep the file handle open on removable media */
if
(
drive_type
==
DRIVE_REMOVABLE
||
drive_type
==
DRIVE_CDROM
)
main_file
=
0
;
}
/* Retrieve entry point address */
nt
=
RtlImageNtHeader
(
peb
->
ImageBaseAddress
);
entry
=
(
LPTHREAD_START_ROUTINE
)((
char
*
)
peb
->
ImageBaseAddress
+
nt
->
OptionalHeader
.
AddressOfEntryPoint
);
/* Install signal handlers; this cannot be done before, since we cannot
* send exceptions to the debugger before the create process event that
* is sent by REQ_INIT_PROCESS_DONE.
* We do need the handlers in place by the time the request is over, so
* we set them up here. If we segfault between here and the server call
* something is very wrong... */
if
(
!
SIGNAL_Init
())
goto
error
;
/* Signal the parent process to continue */
SERVER_START_REQ
(
init_process_done
)
{
req
->
module
=
peb
->
ImageBaseAddress
;
req
->
module_size
=
nt
->
OptionalHeader
.
SizeOfImage
;
req
->
entry
=
entry
;
/* API requires a double indirection */
req
->
name
=
&
main_exe_name_ptr
;
req
->
exe_file
=
main_file
;
req
->
gui
=
(
nt
->
OptionalHeader
.
Subsystem
!=
IMAGE_SUBSYSTEM_WINDOWS_CUI
);
wine_server_add_data
(
req
,
main_exe_name
,
strlen
(
main_exe_name
)
);
wine_server_call
(
req
);
peb
->
BeingDebugged
=
reply
->
debugged
;
}
SERVER_END_REQ
;
/* create the main modref and load dependencies */
if
(
!
PE_CreateModule
(
peb
->
ImageBaseAddress
,
main_exe_name
,
0
,
0
,
FALSE
))
goto
error
;
if
(
main_exe_file
)
CloseHandle
(
main_exe_file
);
/* we no longer need it */
MODULE_DllProcessAttach
(
NULL
,
(
LPVOID
)
1
);
if
(
TRACE_ON
(
relay
))
DPRINTF
(
"%04lx:Starting process %s (entryproc=%p)
\n
"
,
GetCurrentThreadId
(),
main_exe_name
,
entry
);
if
(
peb
->
BeingDebugged
)
DbgBreakPoint
();
SetLastError
(
0
);
/* clear error code */
ExitThread
(
entry
(
NtCurrentTeb
()
->
Peb
)
);
error:
ExitProcess
(
GetLastError
()
);
}
__EXCEPT
(
UnhandledExceptionFilter
)
{
TerminateThread
(
GetCurrentThread
(),
GetExceptionCode
()
);
}
__ENDTRY
}
/***********************************************************************
* __wine_process_init
*
* Wine initialisation: load and start the main exe file.
*/
void
__wine_process_init
(
int
argc
,
char
*
argv
[]
)
{
char
error
[
1024
],
*
p
;
DWORD
stack_size
=
0
;
int
file_exists
;
/* Initialize everything */
if
(
!
process_init
(
argv
))
exit
(
1
);
argv
++
;
/* remove argv[0] (wine itself) */
TRACE
(
"starting process name=%s file=%p argv[0]=%s
\n
"
,
debugstr_a
(
main_exe_name
),
main_exe_file
,
debugstr_a
(
argv
[
0
])
);
if
(
!
main_exe_name
[
0
])
{
if
(
!
argv
[
0
])
OPTIONS_Usage
();
if
(
!
find_exe_file
(
argv
[
0
],
main_exe_name
,
sizeof
(
main_exe_name
),
&
main_exe_file
))
{
MESSAGE
(
"%s: cannot find '%s'
\n
"
,
argv0
,
argv
[
0
]
);
ExitProcess
(
1
);
}
if
(
main_exe_file
==
INVALID_HANDLE_VALUE
)
{
MESSAGE
(
"%s: cannot open '%s'
\n
"
,
argv0
,
main_exe_name
);
ExitProcess
(
1
);
}
}
if
(
!
main_exe_file
)
/* no file handle -> Winelib app */
{
TRACE
(
"starting Winelib app %s
\n
"
,
debugstr_a
(
main_exe_name
)
);
if
(
open_builtin_exe_file
(
main_exe_name
,
error
,
sizeof
(
error
),
0
,
&
file_exists
))
goto
found
;
MESSAGE
(
"%s: cannot open builtin library for '%s': %s
\n
"
,
argv0
,
main_exe_name
,
error
);
ExitProcess
(
1
);
}
VERSION_Init
(
main_exe_name
);
switch
(
MODULE_GetBinaryType
(
main_exe_file
))
{
case
BINARY_PE_EXE
:
TRACE
(
"starting Win32 binary %s
\n
"
,
debugstr_a
(
main_exe_name
)
);
if
((
current_process
.
module
=
PE_LoadImage
(
main_exe_file
,
main_exe_name
,
0
)))
goto
found
;
MESSAGE
(
"%s: could not load '%s' as Win32 binary
\n
"
,
argv0
,
main_exe_name
);
ExitProcess
(
1
);
case
BINARY_PE_DLL
:
MESSAGE
(
"%s: '%s' is a DLL, not an executable
\n
"
,
argv0
,
main_exe_name
);
ExitProcess
(
1
);
case
BINARY_UNKNOWN
:
/* check for .com extension */
if
(
!
(
p
=
strrchr
(
main_exe_name
,
'.'
))
||
FILE_strcasecmp
(
p
,
".com"
))
{
MESSAGE
(
"%s: cannot determine executable type for '%s'
\n
"
,
argv0
,
main_exe_name
);
ExitProcess
(
1
);
}
/* fall through */
case
BINARY_WIN16
:
case
BINARY_DOS
:
TRACE
(
"starting Win16/DOS binary %s
\n
"
,
debugstr_a
(
main_exe_name
)
);
CloseHandle
(
main_exe_file
);
main_exe_file
=
0
;
argv
--
;
argv
[
0
]
=
"winevdm.exe"
;
if
(
open_builtin_exe_file
(
"winevdm.exe"
,
error
,
sizeof
(
error
),
0
,
&
file_exists
))
goto
found
;
MESSAGE
(
"%s: trying to run '%s', cannot open builtin library for 'winevdm.exe': %s
\n
"
,
argv0
,
main_exe_name
,
error
);
ExitProcess
(
1
);
case
BINARY_OS216
:
MESSAGE
(
"%s: '%s' is an OS/2 binary, not supported
\n
"
,
argv0
,
main_exe_name
);
ExitProcess
(
1
);
case
BINARY_UNIX_EXE
:
MESSAGE
(
"%s: '%s' is a Unix binary, not supported
\n
"
,
argv0
,
main_exe_name
);
ExitProcess
(
1
);
case
BINARY_UNIX_LIB
:
{
DOS_FULL_NAME
full_name
;
const
char
*
name
=
main_exe_name
;
UNICODE_STRING
nameW
;
TRACE
(
"starting Winelib app %s
\n
"
,
debugstr_a
(
main_exe_name
)
);
RtlCreateUnicodeStringFromAsciiz
(
&
nameW
,
name
);
if
(
DOSFS_GetFullName
(
nameW
.
Buffer
,
TRUE
,
&
full_name
))
name
=
full_name
.
long_name
;
RtlFreeUnicodeString
(
&
nameW
);
CloseHandle
(
main_exe_file
);
main_exe_file
=
0
;
if
(
wine_dlopen
(
name
,
RTLD_NOW
,
error
,
sizeof
(
error
)
))
{
if
((
p
=
strrchr
(
main_exe_name
,
'.'
))
&&
!
strcmp
(
p
,
".so"
))
*
p
=
0
;
goto
found
;
}
MESSAGE
(
"%s: could not load '%s': %s
\n
"
,
argv0
,
main_exe_name
,
error
);
ExitProcess
(
1
);
}
}
found:
/* build command line */
if
(
!
build_command_line
(
argv
))
goto
error
;
/* create 32-bit module for main exe */
if
(
!
(
current_process
.
module
=
BUILTIN32_LoadExeModule
(
current_process
.
module
)))
goto
error
;
stack_size
=
RtlImageNtHeader
(
current_process
.
module
)
->
OptionalHeader
.
SizeOfStackReserve
;
/* allocate main thread stack */
if
(
!
THREAD_InitStack
(
NtCurrentTeb
(),
stack_size
))
goto
error
;
/* switch to the new stack */
wine_switch_to_stack
(
start_process
,
NULL
,
NtCurrentTeb
()
->
Tib
.
StackBase
);
error:
ExitProcess
(
GetLastError
()
);
}
/***********************************************************************
* build_argv
*
* Build an argv array from a command-line.
* The command-line is modified to insert nulls.
* 'reserved' is the number of args to reserve before the first one.
*/
static
char
**
build_argv
(
char
*
cmdline
,
int
reserved
)
{
int
argc
;
char
**
argv
;
char
*
arg
,
*
s
,
*
d
;
int
in_quotes
,
bcount
;
argc
=
reserved
+
1
;
bcount
=
0
;
in_quotes
=
0
;
s
=
cmdline
;
while
(
1
)
{
if
(
*
s
==
'\0'
||
((
*
s
==
' '
||
*
s
==
'\t'
)
&&
!
in_quotes
))
{
/* space */
argc
++
;
/* skip the remaining spaces */
while
(
*
s
==
' '
||
*
s
==
'\t'
)
{
s
++
;
}
if
(
*
s
==
'\0'
)
break
;
bcount
=
0
;
continue
;
}
else
if
(
*
s
==
'\\'
)
{
/* '\', count them */
bcount
++
;
}
else
if
((
*
s
==
'"'
)
&&
((
bcount
&
1
)
==
0
))
{
/* unescaped '"' */
in_quotes
=!
in_quotes
;
bcount
=
0
;
}
else
{
/* a regular character */
bcount
=
0
;
}
s
++
;
}
argv
=
malloc
(
argc
*
sizeof
(
*
argv
));
if
(
!
argv
)
return
NULL
;
arg
=
d
=
s
=
cmdline
;
bcount
=
0
;
in_quotes
=
0
;
argc
=
reserved
;
while
(
*
s
)
{
if
((
*
s
==
' '
||
*
s
==
'\t'
)
&&
!
in_quotes
)
{
/* Close the argument and copy it */
*
d
=
0
;
argv
[
argc
++
]
=
arg
;
/* skip the remaining spaces */
do
{
s
++
;
}
while
(
*
s
==
' '
||
*
s
==
'\t'
);
/* Start with a new argument */
arg
=
d
=
s
;
bcount
=
0
;
}
else
if
(
*
s
==
'\\'
)
{
/* '\\' */
*
d
++=*
s
++
;
bcount
++
;
}
else
if
(
*
s
==
'"'
)
{
/* '"' */
if
((
bcount
&
1
)
==
0
)
{
/* Preceeded by an even number of '\', this is half that
* number of '\', plus a '"' which we discard.
*/
d
-=
bcount
/
2
;
s
++
;
in_quotes
=!
in_quotes
;
}
else
{
/* Preceeded by an odd number of '\', this is half that
* 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
;
return
argv
;
}
/***********************************************************************
* build_envp
*
* Build the environment of a new child process.
*/
static
char
**
build_envp
(
const
char
*
env
,
const
char
*
extra_env
)
{
const
char
*
p
;
char
**
envp
;
int
count
=
0
;
if
(
extra_env
)
for
(
p
=
extra_env
;
*
p
;
count
++
)
p
+=
strlen
(
p
)
+
1
;
for
(
p
=
env
;
*
p
;
count
++
)
p
+=
strlen
(
p
)
+
1
;
count
+=
3
;
if
((
envp
=
malloc
(
count
*
sizeof
(
*
envp
)
)))
{
extern
char
**
environ
;
char
**
envptr
=
envp
;
char
**
unixptr
=
environ
;
/* first the extra strings */
if
(
extra_env
)
for
(
p
=
extra_env
;
*
p
;
p
+=
strlen
(
p
)
+
1
)
*
envptr
++
=
(
char
*
)
p
;
/* then put PATH, HOME and WINEPREFIX from the unix env */
for
(
unixptr
=
environ
;
unixptr
&&
*
unixptr
;
unixptr
++
)
if
(
!
memcmp
(
*
unixptr
,
"PATH="
,
5
)
||
!
memcmp
(
*
unixptr
,
"HOME="
,
5
)
||
!
memcmp
(
*
unixptr
,
"WINEPREFIX="
,
11
))
*
envptr
++
=
*
unixptr
;
/* now put the Windows environment strings */
for
(
p
=
env
;
*
p
;
p
+=
strlen
(
p
)
+
1
)
{
if
(
!
memcmp
(
p
,
"PATH="
,
5
))
/* store PATH as WINEPATH */
{
char
*
winepath
=
malloc
(
strlen
(
p
)
+
5
);
strcpy
(
winepath
,
"WINE"
);
strcpy
(
winepath
+
4
,
p
);
*
envptr
++
=
winepath
;
}
else
if
(
memcmp
(
p
,
"HOME="
,
5
)
&&
memcmp
(
p
,
"WINEPATH="
,
9
)
&&
memcmp
(
p
,
"WINEPREFIX="
,
11
))
*
envptr
++
=
(
char
*
)
p
;
}
*
envptr
=
0
;
}
return
envp
;
}
/***********************************************************************
* exec_wine_binary
*
* Locate the Wine binary to exec for a new Win32 process.
*/
static
void
exec_wine_binary
(
char
**
argv
,
char
**
envp
)
{
const
char
*
path
,
*
pos
,
*
ptr
;
/* first, try for a WINELOADER environment variable */
argv
[
0
]
=
getenv
(
"WINELOADER"
);
if
(
argv
[
0
])
execve
(
argv
[
0
],
argv
,
envp
);
/* next, try bin directory */
argv
[
0
]
=
BINDIR
"/wine"
;
execve
(
argv
[
0
],
argv
,
envp
);
/* now try the path of argv0 of the current binary */
if
(
!
(
argv
[
0
]
=
malloc
(
strlen
(
full_argv0
)
+
6
)))
return
;
if
((
ptr
=
strrchr
(
full_argv0
,
'/'
)))
{
memcpy
(
argv
[
0
],
full_argv0
,
ptr
-
full_argv0
);
strcpy
(
argv
[
0
]
+
(
ptr
-
full_argv0
),
"/wine"
);
execve
(
argv
[
0
],
argv
,
envp
);
}
free
(
argv
[
0
]
);
/* now search in the Unix path */
if
((
path
=
getenv
(
"PATH"
)))
{
if
(
!
(
argv
[
0
]
=
malloc
(
strlen
(
path
)
+
6
)))
return
;
pos
=
path
;
for
(;;)
{
while
(
*
pos
==
':'
)
pos
++
;
if
(
!*
pos
)
break
;
if
(
!
(
ptr
=
strchr
(
pos
,
':'
)))
ptr
=
pos
+
strlen
(
pos
);
memcpy
(
argv
[
0
],
pos
,
ptr
-
pos
);
strcpy
(
argv
[
0
]
+
(
ptr
-
pos
),
"/wine"
);
execve
(
argv
[
0
],
argv
,
envp
);
pos
=
ptr
;
}
}
free
(
argv
[
0
]
);
}
/***********************************************************************
* fork_and_exec
*
* Fork and exec a new Unix binary, checking for errors.
*/
static
int
fork_and_exec
(
const
char
*
filename
,
char
*
cmdline
,
const
char
*
env
,
const
char
*
newdir
)
{
int
fd
[
2
];
int
pid
,
err
;
if
(
!
env
)
env
=
GetEnvironmentStringsA
();
if
(
pipe
(
fd
)
==
-
1
)
{
FILE_SetDosError
();
return
-
1
;
}
fcntl
(
fd
[
1
],
F_SETFD
,
1
);
/* set close on exec */
if
(
!
(
pid
=
fork
()))
/* child */
{
char
**
argv
=
build_argv
(
cmdline
,
0
);
char
**
envp
=
build_envp
(
env
,
NULL
);
close
(
fd
[
0
]
);
/* Reset signals that we previously set to SIG_IGN */
signal
(
SIGPIPE
,
SIG_DFL
);
signal
(
SIGCHLD
,
SIG_DFL
);
if
(
newdir
)
chdir
(
newdir
);
if
(
argv
&&
envp
)
execve
(
filename
,
argv
,
envp
);
err
=
errno
;
write
(
fd
[
1
],
&
err
,
sizeof
(
err
)
);
_exit
(
1
);
}
close
(
fd
[
1
]
);
if
((
pid
!=
-
1
)
&&
(
read
(
fd
[
0
],
&
err
,
sizeof
(
err
)
)
>
0
))
/* exec failed */
{
errno
=
err
;
pid
=
-
1
;
}
if
(
pid
==
-
1
)
FILE_SetDosError
();
close
(
fd
[
0
]
);
return
pid
;
}
/***********************************************************************
* create_process
*
* Create a new process. If hFile is a valid handle we have an exe
* file, otherwise it is a Winelib app.
*/
static
BOOL
create_process
(
HANDLE
hFile
,
LPCSTR
filename
,
LPSTR
cmd_line
,
LPCSTR
env
,
LPSECURITY_ATTRIBUTES
psa
,
LPSECURITY_ATTRIBUTES
tsa
,
BOOL
inherit
,
DWORD
flags
,
LPSTARTUPINFOA
startup
,
LPPROCESS_INFORMATION
info
,
LPCSTR
unixdir
)
{
BOOL
ret
,
success
=
FALSE
;
HANDLE
process_info
;
startup_info_t
startup_info
;
char
*
extra_env
=
NULL
;
int
startfd
[
2
];
int
execfd
[
2
];
pid_t
pid
;
int
err
;
char
dummy
=
0
;
if
(
!
env
)
{
env
=
GetEnvironmentStringsA
();
extra_env
=
DRIVE_BuildEnv
();
}
/* create the synchronization pipes */
if
(
pipe
(
startfd
)
==
-
1
)
{
FILE_SetDosError
();
return
FALSE
;
}
if
(
pipe
(
execfd
)
==
-
1
)
{
close
(
startfd
[
0
]
);
close
(
startfd
[
1
]
);
FILE_SetDosError
();
return
FALSE
;
}
fcntl
(
execfd
[
1
],
F_SETFD
,
1
);
/* set close on exec */
/* create the child process */
if
(
!
(
pid
=
fork
()))
/* child */
{
char
**
argv
=
build_argv
(
cmd_line
,
1
);
char
**
envp
=
build_envp
(
env
,
extra_env
);
close
(
startfd
[
1
]
);
close
(
execfd
[
0
]
);
/* wait for parent to tell us to start */
if
(
read
(
startfd
[
0
],
&
dummy
,
1
)
!=
1
)
_exit
(
1
);
close
(
startfd
[
0
]
);
/* Reset signals that we previously set to SIG_IGN */
signal
(
SIGPIPE
,
SIG_DFL
);
signal
(
SIGCHLD
,
SIG_DFL
);
if
(
unixdir
)
chdir
(
unixdir
);
if
(
argv
&&
envp
)
exec_wine_binary
(
argv
,
envp
);
err
=
errno
;
write
(
execfd
[
1
],
&
err
,
sizeof
(
err
)
);
_exit
(
1
);
}
/* this is the parent */
close
(
startfd
[
0
]
);
close
(
execfd
[
1
]
);
if
(
extra_env
)
HeapFree
(
GetProcessHeap
(),
0
,
extra_env
);
if
(
pid
==
-
1
)
{
close
(
startfd
[
1
]
);
close
(
execfd
[
0
]
);
FILE_SetDosError
();
return
FALSE
;
}
/* fill the startup info structure */
startup_info
.
size
=
sizeof
(
startup_info
);
/* startup_info.filename_len is set below */
startup_info
.
cmdline_len
=
cmd_line
?
strlen
(
cmd_line
)
:
0
;
startup_info
.
desktop_len
=
startup
->
lpDesktop
?
strlen
(
startup
->
lpDesktop
)
:
0
;
startup_info
.
title_len
=
startup
->
lpTitle
?
strlen
(
startup
->
lpTitle
)
:
0
;
startup_info
.
x
=
startup
->
dwX
;
startup_info
.
y
=
startup
->
dwY
;
startup_info
.
cx
=
startup
->
dwXSize
;
startup_info
.
cy
=
startup
->
dwYSize
;
startup_info
.
x_chars
=
startup
->
dwXCountChars
;
startup_info
.
y_chars
=
startup
->
dwYCountChars
;
startup_info
.
attribute
=
startup
->
dwFillAttribute
;
startup_info
.
cmd_show
=
startup
->
wShowWindow
;
startup_info
.
flags
=
startup
->
dwFlags
;
/* create the process on the server side */
SERVER_START_REQ
(
new_process
)
{
char
buf
[
MAX_PATH
];
LPCSTR
nameptr
;
req
->
inherit_all
=
inherit
;
req
->
create_flags
=
flags
;
req
->
unix_pid
=
pid
;
req
->
exe_file
=
hFile
;
if
(
startup
->
dwFlags
&
STARTF_USESTDHANDLES
)
{
req
->
hstdin
=
startup
->
hStdInput
;
req
->
hstdout
=
startup
->
hStdOutput
;
req
->
hstderr
=
startup
->
hStdError
;
}
else
{
req
->
hstdin
=
GetStdHandle
(
STD_INPUT_HANDLE
);
req
->
hstdout
=
GetStdHandle
(
STD_OUTPUT_HANDLE
);
req
->
hstderr
=
GetStdHandle
(
STD_ERROR_HANDLE
);
}
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
(
req
->
hstdin
))
req
->
hstdin
=
INVALID_HANDLE_VALUE
;
if
(
is_console_handle
(
req
->
hstdout
))
req
->
hstdout
=
INVALID_HANDLE_VALUE
;
if
(
is_console_handle
(
req
->
hstderr
))
req
->
hstderr
=
INVALID_HANDLE_VALUE
;
}
else
{
if
(
is_console_handle
(
req
->
hstdin
))
req
->
hstdin
=
console_handle_unmap
(
req
->
hstdin
);
if
(
is_console_handle
(
req
->
hstdout
))
req
->
hstdout
=
console_handle_unmap
(
req
->
hstdout
);
if
(
is_console_handle
(
req
->
hstderr
))
req
->
hstderr
=
console_handle_unmap
(
req
->
hstderr
);
}
if
(
GetLongPathNameA
(
filename
,
buf
,
MAX_PATH
))
nameptr
=
buf
;
else
nameptr
=
filename
;
startup_info
.
filename_len
=
strlen
(
nameptr
);
wine_server_add_data
(
req
,
&
startup_info
,
sizeof
(
startup_info
)
);
wine_server_add_data
(
req
,
nameptr
,
startup_info
.
filename_len
);
wine_server_add_data
(
req
,
cmd_line
,
startup_info
.
cmdline_len
);
wine_server_add_data
(
req
,
startup
->
lpDesktop
,
startup_info
.
desktop_len
);
wine_server_add_data
(
req
,
startup
->
lpTitle
,
startup_info
.
title_len
);
ret
=
!
wine_server_call_err
(
req
);
process_info
=
reply
->
info
;
}
SERVER_END_REQ
;
if
(
!
ret
)
{
close
(
startfd
[
1
]
);
close
(
execfd
[
0
]
);
return
FALSE
;
}
/* tell child to start and wait for it to exec */
write
(
startfd
[
1
],
&
dummy
,
1
);
close
(
startfd
[
1
]
);
if
(
read
(
execfd
[
0
],
&
err
,
sizeof
(
err
)
)
>
0
)
/* exec failed */
{
errno
=
err
;
FILE_SetDosError
();
close
(
execfd
[
0
]
);
CloseHandle
(
process_info
);
return
FALSE
;
}
/* wait for the new process info to be ready */
WaitForSingleObject
(
process_info
,
INFINITE
);
SERVER_START_REQ
(
get_new_process_info
)
{
req
->
info
=
process_info
;
req
->
pinherit
=
(
psa
&&
(
psa
->
nLength
>=
sizeof
(
*
psa
))
&&
psa
->
bInheritHandle
);
req
->
tinherit
=
(
tsa
&&
(
tsa
->
nLength
>=
sizeof
(
*
tsa
))
&&
tsa
->
bInheritHandle
);
if
((
ret
=
!
wine_server_call_err
(
req
)))
{
info
->
dwProcessId
=
(
DWORD
)
reply
->
pid
;
info
->
dwThreadId
=
(
DWORD
)
reply
->
tid
;
info
->
hProcess
=
reply
->
phandle
;
info
->
hThread
=
reply
->
thandle
;
success
=
reply
->
success
;
}
}
SERVER_END_REQ
;
if
(
ret
&&
!
success
)
/* new process failed to start */
{
DWORD
exitcode
;
if
(
GetExitCodeProcess
(
info
->
hProcess
,
&
exitcode
))
SetLastError
(
exitcode
);
CloseHandle
(
info
->
hThread
);
CloseHandle
(
info
->
hProcess
);
ret
=
FALSE
;
}
CloseHandle
(
process_info
);
return
ret
;
}
/***********************************************************************
* create_vdm_process
*
* Create a new VDM process for a 16-bit or DOS application.
*/
static
BOOL
create_vdm_process
(
LPCSTR
filename
,
LPSTR
cmd_line
,
LPCSTR
env
,
LPSECURITY_ATTRIBUTES
psa
,
LPSECURITY_ATTRIBUTES
tsa
,
BOOL
inherit
,
DWORD
flags
,
LPSTARTUPINFOA
startup
,
LPPROCESS_INFORMATION
info
,
LPCSTR
unixdir
)
{
BOOL
ret
;
LPSTR
new_cmd_line
=
HeapAlloc
(
GetProcessHeap
(),
0
,
strlen
(
filename
)
+
strlen
(
cmd_line
)
+
30
);
if
(
!
new_cmd_line
)
{
SetLastError
(
ERROR_OUTOFMEMORY
);
return
FALSE
;
}
sprintf
(
new_cmd_line
,
"winevdm.exe --app-name
\"
%s
\"
%s"
,
filename
,
cmd_line
);
ret
=
create_process
(
0
,
"winevdm.exe"
,
new_cmd_line
,
env
,
psa
,
tsa
,
inherit
,
flags
,
startup
,
info
,
unixdir
);
HeapFree
(
GetProcessHeap
(),
0
,
new_cmd_line
);
return
ret
;
}
/*************************************************************************
* get_file_name
*
* Helper for CreateProcess: retrieve the file name to load from the
* app name and command line. Store the file name in buffer, and
* return a possibly modified command line.
* Also returns a handle to the opened file if it's a Windows binary.
*/
static
LPSTR
get_file_name
(
LPCSTR
appname
,
LPSTR
cmdline
,
LPSTR
buffer
,
int
buflen
,
HANDLE
*
handle
)
{
char
*
name
,
*
pos
,
*
ret
=
NULL
;
const
char
*
p
;
/* if we have an app name, everything is easy */
if
(
appname
)
{
/* use the unmodified app name as file name */
lstrcpynA
(
buffer
,
appname
,
buflen
);
*
handle
=
open_exe_file
(
buffer
);
if
(
!
(
ret
=
cmdline
)
||
!
cmdline
[
0
])
{
/* no command-line, create one */
if
((
ret
=
HeapAlloc
(
GetProcessHeap
(),
0
,
strlen
(
appname
)
+
3
)))
sprintf
(
ret
,
"
\"
%s
\"
"
,
appname
);
}
return
ret
;
}
if
(
!
cmdline
)
{
SetLastError
(
ERROR_INVALID_PARAMETER
);
return
NULL
;
}
/* first check for a quoted file name */
if
((
cmdline
[
0
]
==
'"'
)
&&
((
p
=
strchr
(
cmdline
+
1
,
'"'
))))
{
int
len
=
p
-
cmdline
-
1
;
/* extract the quoted portion as file name */
if
(
!
(
name
=
HeapAlloc
(
GetProcessHeap
(),
0
,
len
+
1
)))
return
NULL
;
memcpy
(
name
,
cmdline
+
1
,
len
);
name
[
len
]
=
0
;
if
(
find_exe_file
(
name
,
buffer
,
buflen
,
handle
))
ret
=
cmdline
;
/* no change necessary */
goto
done
;
}
/* now try the command-line word by word */
if
(
!
(
name
=
HeapAlloc
(
GetProcessHeap
(),
0
,
strlen
(
cmdline
)
+
1
)))
return
NULL
;
pos
=
name
;
p
=
cmdline
;
while
(
*
p
)
{
do
*
pos
++
=
*
p
++
;
while
(
*
p
&&
*
p
!=
' '
);
*
pos
=
0
;
if
(
find_exe_file
(
name
,
buffer
,
buflen
,
handle
))
{
ret
=
cmdline
;
break
;
}
}
if
(
!
ret
||
!
strchr
(
name
,
' '
))
goto
done
;
/* no change necessary */
/* now build a new command-line with quotes */
if
(
!
(
ret
=
HeapAlloc
(
GetProcessHeap
(),
0
,
strlen
(
cmdline
)
+
3
)))
goto
done
;
sprintf
(
ret
,
"
\"
%s
\"
%s"
,
name
,
p
);
done:
HeapFree
(
GetProcessHeap
(),
0
,
name
);
return
ret
;
}
/**********************************************************************
* CreateProcessA (KERNEL32.@)
*/
BOOL
WINAPI
CreateProcessA
(
LPCSTR
app_name
,
LPSTR
cmd_line
,
LPSECURITY_ATTRIBUTES
process_attr
,
LPSECURITY_ATTRIBUTES
thread_attr
,
BOOL
inherit
,
DWORD
flags
,
LPVOID
env
,
LPCSTR
cur_dir
,
LPSTARTUPINFOA
startup_info
,
LPPROCESS_INFORMATION
info
)
{
BOOL
retv
=
FALSE
;
HANDLE
hFile
=
0
;
const
char
*
unixdir
=
NULL
;
DOS_FULL_NAME
full_dir
;
char
name
[
MAX_PATH
];
LPSTR
tidy_cmdline
;
char
*
p
;
/* Process the AppName and/or CmdLine to get module name and path */
TRACE
(
"app %s cmdline %s
\n
"
,
debugstr_a
(
app_name
),
debugstr_a
(
cmd_line
)
);
if
(
!
(
tidy_cmdline
=
get_file_name
(
app_name
,
cmd_line
,
name
,
sizeof
(
name
),
&
hFile
)))
return
FALSE
;
if
(
hFile
==
INVALID_HANDLE_VALUE
)
goto
done
;
/* Warn if unsupported features are used */
if
(
flags
&
NORMAL_PRIORITY_CLASS
)
FIXME
(
"(%s,...): NORMAL_PRIORITY_CLASS ignored
\n
"
,
name
);
if
(
flags
&
IDLE_PRIORITY_CLASS
)
FIXME
(
"(%s,...): IDLE_PRIORITY_CLASS ignored
\n
"
,
name
);
if
(
flags
&
HIGH_PRIORITY_CLASS
)
FIXME
(
"(%s,...): HIGH_PRIORITY_CLASS ignored
\n
"
,
name
);
if
(
flags
&
REALTIME_PRIORITY_CLASS
)
FIXME
(
"(%s,...): REALTIME_PRIORITY_CLASS ignored
\n
"
,
name
);
if
(
flags
&
CREATE_NEW_PROCESS_GROUP
)
FIXME
(
"(%s,...): CREATE_NEW_PROCESS_GROUP ignored
\n
"
,
name
);
if
(
flags
&
CREATE_UNICODE_ENVIRONMENT
)
FIXME
(
"(%s,...): CREATE_UNICODE_ENVIRONMENT ignored
\n
"
,
name
);
if
(
flags
&
CREATE_SEPARATE_WOW_VDM
)
FIXME
(
"(%s,...): CREATE_SEPARATE_WOW_VDM ignored
\n
"
,
name
);
if
(
flags
&
CREATE_SHARED_WOW_VDM
)
FIXME
(
"(%s,...): CREATE_SHARED_WOW_VDM ignored
\n
"
,
name
);
if
(
flags
&
CREATE_DEFAULT_ERROR_MODE
)
FIXME
(
"(%s,...): CREATE_DEFAULT_ERROR_MODE ignored
\n
"
,
name
);
if
(
flags
&
CREATE_NO_WINDOW
)
FIXME
(
"(%s,...): CREATE_NO_WINDOW ignored
\n
"
,
name
);
if
(
flags
&
PROFILE_USER
)
FIXME
(
"(%s,...): PROFILE_USER ignored
\n
"
,
name
);
if
(
flags
&
PROFILE_KERNEL
)
FIXME
(
"(%s,...): PROFILE_KERNEL ignored
\n
"
,
name
);
if
(
flags
&
PROFILE_SERVER
)
FIXME
(
"(%s,...): PROFILE_SERVER ignored
\n
"
,
name
);
if
(
startup_info
->
lpDesktop
)
FIXME
(
"(%s,...): startup_info->lpDesktop %s ignored
\n
"
,
name
,
debugstr_a
(
startup_info
->
lpDesktop
));
if
(
startup_info
->
dwFlags
&
STARTF_RUNFULLSCREEN
)
FIXME
(
"(%s,...): STARTF_RUNFULLSCREEN ignored
\n
"
,
name
);
if
(
startup_info
->
dwFlags
&
STARTF_FORCEONFEEDBACK
)
FIXME
(
"(%s,...): STARTF_FORCEONFEEDBACK ignored
\n
"
,
name
);
if
(
startup_info
->
dwFlags
&
STARTF_FORCEOFFFEEDBACK
)
FIXME
(
"(%s,...): STARTF_FORCEOFFFEEDBACK ignored
\n
"
,
name
);
if
(
startup_info
->
dwFlags
&
STARTF_USEHOTKEY
)
FIXME
(
"(%s,...): STARTF_USEHOTKEY ignored
\n
"
,
name
);
if
(
cur_dir
)
{
UNICODE_STRING
cur_dirW
;
RtlCreateUnicodeStringFromAsciiz
(
&
cur_dirW
,
cur_dir
);
if
(
DOSFS_GetFullName
(
cur_dirW
.
Buffer
,
TRUE
,
&
full_dir
))
unixdir
=
full_dir
.
long_name
;
RtlFreeUnicodeString
(
&
cur_dirW
);
}
else
{
WCHAR
buf
[
MAX_PATH
];
if
(
GetCurrentDirectoryW
(
MAX_PATH
,
buf
))
{
if
(
DOSFS_GetFullName
(
buf
,
TRUE
,
&
full_dir
))
unixdir
=
full_dir
.
long_name
;
}
}
info
->
hThread
=
info
->
hProcess
=
0
;
info
->
dwProcessId
=
info
->
dwThreadId
=
0
;
/* Determine executable type */
if
(
!
hFile
)
/* builtin exe */
{
TRACE
(
"starting %s as Winelib app
\n
"
,
debugstr_a
(
name
)
);
retv
=
create_process
(
0
,
name
,
tidy_cmdline
,
env
,
process_attr
,
thread_attr
,
inherit
,
flags
,
startup_info
,
info
,
unixdir
);
goto
done
;
}
switch
(
MODULE_GetBinaryType
(
hFile
))
{
case
BINARY_PE_EXE
:
TRACE
(
"starting %s as Win32 binary
\n
"
,
debugstr_a
(
name
)
);
retv
=
create_process
(
hFile
,
name
,
tidy_cmdline
,
env
,
process_attr
,
thread_attr
,
inherit
,
flags
,
startup_info
,
info
,
unixdir
);
break
;
case
BINARY_WIN16
:
case
BINARY_DOS
:
TRACE
(
"starting %s as Win16/DOS binary
\n
"
,
debugstr_a
(
name
)
);
retv
=
create_vdm_process
(
name
,
tidy_cmdline
,
env
,
process_attr
,
thread_attr
,
inherit
,
flags
,
startup_info
,
info
,
unixdir
);
break
;
case
BINARY_OS216
:
FIXME
(
"%s is OS/2 binary, not supported
\n
"
,
debugstr_a
(
name
)
);
SetLastError
(
ERROR_BAD_EXE_FORMAT
);
break
;
case
BINARY_PE_DLL
:
TRACE
(
"not starting %s since it is a dll
\n
"
,
debugstr_a
(
name
)
);
SetLastError
(
ERROR_BAD_EXE_FORMAT
);
break
;
case
BINARY_UNIX_LIB
:
TRACE
(
"%s is a Unix library, starting as Winelib app
\n
"
,
debugstr_a
(
name
)
);
retv
=
create_process
(
hFile
,
name
,
tidy_cmdline
,
env
,
process_attr
,
thread_attr
,
inherit
,
flags
,
startup_info
,
info
,
unixdir
);
break
;
case
BINARY_UNKNOWN
:
/* check for .com or .bat extension */
if
((
p
=
strrchr
(
name
,
'.'
)))
{
if
(
!
FILE_strcasecmp
(
p
,
".com"
))
{
TRACE
(
"starting %s as DOS binary
\n
"
,
debugstr_a
(
name
)
);
retv
=
create_vdm_process
(
name
,
tidy_cmdline
,
env
,
process_attr
,
thread_attr
,
inherit
,
flags
,
startup_info
,
info
,
unixdir
);
break
;
}
if
(
!
FILE_strcasecmp
(
p
,
".bat"
))
{
char
comspec
[
MAX_PATH
];
if
(
GetEnvironmentVariableA
(
"COMSPEC"
,
comspec
,
sizeof
(
comspec
)))
{
char
*
newcmdline
;
if
((
newcmdline
=
HeapAlloc
(
GetProcessHeap
(),
0
,
strlen
(
comspec
)
+
4
+
strlen
(
tidy_cmdline
)
+
1
)))
{
sprintf
(
newcmdline
,
"%s /c %s"
,
comspec
,
tidy_cmdline
);
TRACE
(
"starting %s as batch binary: %s
\n
"
,
debugstr_a
(
name
),
debugstr_a
(
newcmdline
)
);
retv
=
CreateProcessA
(
comspec
,
newcmdline
,
process_attr
,
thread_attr
,
inherit
,
flags
,
env
,
cur_dir
,
startup_info
,
info
);
HeapFree
(
GetProcessHeap
(),
0
,
newcmdline
);
break
;
}
}
}
}
/* fall through */
case
BINARY_UNIX_EXE
:
{
/* unknown file, try as unix executable */
UNICODE_STRING
nameW
;
DOS_FULL_NAME
full_name
;
const
char
*
unixfilename
=
name
;
TRACE
(
"starting %s as Unix binary
\n
"
,
debugstr_a
(
name
)
);
RtlCreateUnicodeStringFromAsciiz
(
&
nameW
,
name
);
if
(
DOSFS_GetFullName
(
nameW
.
Buffer
,
TRUE
,
&
full_name
))
unixfilename
=
full_name
.
long_name
;
RtlFreeUnicodeString
(
&
nameW
);
retv
=
(
fork_and_exec
(
unixfilename
,
tidy_cmdline
,
env
,
unixdir
)
!=
-
1
);
}
break
;
}
CloseHandle
(
hFile
);
done:
if
(
tidy_cmdline
!=
cmd_line
)
HeapFree
(
GetProcessHeap
(),
0
,
tidy_cmdline
);
return
retv
;
}
/**********************************************************************
* CreateProcessW (KERNEL32.@)
* NOTES
* lpReserved is not converted
*/
BOOL
WINAPI
CreateProcessW
(
LPCWSTR
app_name
,
LPWSTR
cmd_line
,
LPSECURITY_ATTRIBUTES
process_attr
,
LPSECURITY_ATTRIBUTES
thread_attr
,
BOOL
inherit
,
DWORD
flags
,
LPVOID
env
,
LPCWSTR
cur_dir
,
LPSTARTUPINFOW
startup_info
,
LPPROCESS_INFORMATION
info
)
{
BOOL
ret
;
STARTUPINFOA
StartupInfoA
;
LPSTR
app_nameA
=
HEAP_strdupWtoA
(
GetProcessHeap
(),
0
,
app_name
);
LPSTR
cmd_lineA
=
HEAP_strdupWtoA
(
GetProcessHeap
(),
0
,
cmd_line
);
LPSTR
cur_dirA
=
HEAP_strdupWtoA
(
GetProcessHeap
(),
0
,
cur_dir
);
memcpy
(
&
StartupInfoA
,
startup_info
,
sizeof
(
STARTUPINFOA
));
StartupInfoA
.
lpDesktop
=
HEAP_strdupWtoA
(
GetProcessHeap
(),
0
,
startup_info
->
lpDesktop
);
StartupInfoA
.
lpTitle
=
HEAP_strdupWtoA
(
GetProcessHeap
(),
0
,
startup_info
->
lpTitle
);
TRACE_
(
win32
)(
"(%s,%s,...)
\n
"
,
debugstr_w
(
app_name
),
debugstr_w
(
cmd_line
));
if
(
startup_info
->
lpReserved
)
FIXME_
(
win32
)(
"StartupInfo.lpReserved is used, please report (%s)
\n
"
,
debugstr_w
(
startup_info
->
lpReserved
));
ret
=
CreateProcessA
(
app_nameA
,
cmd_lineA
,
process_attr
,
thread_attr
,
inherit
,
flags
,
env
,
cur_dirA
,
&
StartupInfoA
,
info
);
HeapFree
(
GetProcessHeap
(),
0
,
cur_dirA
);
HeapFree
(
GetProcessHeap
(),
0
,
cmd_lineA
);
HeapFree
(
GetProcessHeap
(),
0
,
StartupInfoA
.
lpDesktop
);
HeapFree
(
GetProcessHeap
(),
0
,
StartupInfoA
.
lpTitle
);
return
ret
;
}
unsigned
int
server_startticks
;
/***********************************************************************
* ExitProcess (KERNEL32.@)
...
...
@@ -1380,52 +50,6 @@ void WINAPI ExitProcess( DWORD status )
exit
(
status
);
}
/******************************************************************************
* TerminateProcess (KERNEL32.@)
*/
BOOL
WINAPI
TerminateProcess
(
HANDLE
handle
,
DWORD
exit_code
)
{
NTSTATUS
status
=
NtTerminateProcess
(
handle
,
exit_code
);
if
(
status
)
SetLastError
(
RtlNtStatusToDosError
(
status
)
);
return
!
status
;
}
/***********************************************************************
* GetExitCodeProcess [KERNEL32.@]
*
* Gets termination status of specified process
*
* RETURNS
* Success: TRUE
* Failure: FALSE
*/
BOOL
WINAPI
GetExitCodeProcess
(
HANDLE
hProcess
,
/* [in] handle to the process */
LPDWORD
lpExitCode
)
/* [out] address to receive termination status */
{
BOOL
ret
;
SERVER_START_REQ
(
get_process_info
)
{
req
->
handle
=
hProcess
;
ret
=
!
wine_server_call_err
(
req
);
if
(
ret
&&
lpExitCode
)
*
lpExitCode
=
reply
->
exit_code
;
}
SERVER_END_REQ
;
return
ret
;
}
/***********************************************************************
* SetErrorMode (KERNEL32.@)
*/
UINT
WINAPI
SetErrorMode
(
UINT
mode
)
{
UINT
old
=
current_process
.
error_mode
;
current_process
.
error_mode
=
mode
;
return
old
;
}
/***********************************************************************
* GetTickCount (KERNEL32.@)
...
...
@@ -1439,111 +63,3 @@ DWORD WINAPI GetTickCount(void)
gettimeofday
(
&
t
,
NULL
);
return
((
t
.
tv_sec
*
1000
)
+
(
t
.
tv_usec
/
1000
))
-
server_startticks
;
}
/**********************************************************************
* TlsAlloc [KERNEL32.@] Allocates a TLS index.
*
* Allocates a thread local storage index
*
* RETURNS
* Success: TLS Index
* Failure: 0xFFFFFFFF
*/
DWORD
WINAPI
TlsAlloc
(
void
)
{
DWORD
i
,
mask
,
ret
=
0
;
DWORD
*
bits
=
current_process
.
tls_bits
;
RtlAcquirePebLock
();
if
(
*
bits
==
0xffffffff
)
{
bits
++
;
ret
=
32
;
if
(
*
bits
==
0xffffffff
)
{
RtlReleasePebLock
();
SetLastError
(
ERROR_NO_MORE_ITEMS
);
return
0xffffffff
;
}
}
for
(
i
=
0
,
mask
=
1
;
i
<
32
;
i
++
,
mask
<<=
1
)
if
(
!
(
*
bits
&
mask
))
break
;
*
bits
|=
mask
;
RtlReleasePebLock
();
NtCurrentTeb
()
->
TlsSlots
[
ret
+
i
]
=
0
;
/* clear the value */
return
ret
+
i
;
}
/**********************************************************************
* TlsFree [KERNEL32.@] Releases a TLS index.
*
* Releases a thread local storage index, making it available for reuse
*
* RETURNS
* Success: TRUE
* Failure: FALSE
*/
BOOL
WINAPI
TlsFree
(
DWORD
index
)
/* [in] TLS Index to free */
{
DWORD
mask
=
(
1
<<
(
index
&
31
));
DWORD
*
bits
=
current_process
.
tls_bits
;
if
(
index
>=
64
)
{
SetLastError
(
ERROR_INVALID_PARAMETER
);
return
FALSE
;
}
if
(
index
>=
32
)
bits
++
;
RtlAcquirePebLock
();
if
(
!
(
*
bits
&
mask
))
/* already free? */
{
RtlReleasePebLock
();
SetLastError
(
ERROR_INVALID_PARAMETER
);
return
FALSE
;
}
*
bits
&=
~
mask
;
NtSetInformationThread
(
GetCurrentThread
(),
ThreadZeroTlsCell
,
&
index
,
sizeof
(
index
)
);
RtlReleasePebLock
();
return
TRUE
;
}
/**********************************************************************
* TlsGetValue [KERNEL32.@] Gets value in a thread's TLS slot
*
* RETURNS
* Success: Value stored in calling thread's TLS slot for index
* Failure: 0 and GetLastError returns NO_ERROR
*/
LPVOID
WINAPI
TlsGetValue
(
DWORD
index
)
/* [in] TLS index to retrieve value for */
{
if
(
index
>=
64
)
{
SetLastError
(
ERROR_INVALID_PARAMETER
);
return
NULL
;
}
SetLastError
(
ERROR_SUCCESS
);
return
NtCurrentTeb
()
->
TlsSlots
[
index
];
}
/**********************************************************************
* TlsSetValue [KERNEL32.@] Stores a value in the thread's TLS slot.
*
* RETURNS
* Success: TRUE
* Failure: FALSE
*/
BOOL
WINAPI
TlsSetValue
(
DWORD
index
,
/* [in] TLS index to set value for */
LPVOID
value
)
/* [in] Value to be stored */
{
if
(
index
>=
64
)
{
SetLastError
(
ERROR_INVALID_PARAMETER
);
return
FALSE
;
}
NtCurrentTeb
()
->
TlsSlots
[
index
]
=
value
;
return
TRUE
;
}
scheduler/thread.c
View file @
aaf3503e
...
...
@@ -36,385 +36,13 @@
#include "wine/winbase16.h"
#include "ntstatus.h"
#include "thread.h"
#include "module.h"
#include "winerror.h"
#include "selectors.h"
#include "winnt.h"
#include "wine/server.h"
#include "wine/debug.h"
#include "winnls.h"
WINE_DEFAULT_DEBUG_CHANNEL
(
thread
);
WINE_DECLARE_DEBUG_CHANNEL
(
relay
);
/* TEB of the initial thread */
static
TEB
initial_teb
;
extern
struct
_PDB
current_process
;
/***********************************************************************
* THREAD_InitTEB
*
* Initialization of a newly created TEB.
*/
static
BOOL
THREAD_InitTEB
(
TEB
*
teb
)
{
teb
->
Tib
.
ExceptionList
=
(
void
*
)
~
0UL
;
teb
->
Tib
.
StackBase
=
(
void
*
)
~
0UL
;
teb
->
Tib
.
Self
=
&
teb
->
Tib
;
teb
->
tibflags
=
TEBF_WIN32
;
teb
->
exit_code
=
STILL_ACTIVE
;
teb
->
request_fd
=
-
1
;
teb
->
reply_fd
=
-
1
;
teb
->
wait_fd
[
0
]
=
-
1
;
teb
->
wait_fd
[
1
]
=
-
1
;
teb
->
StaticUnicodeString
.
MaximumLength
=
sizeof
(
teb
->
StaticUnicodeBuffer
);
teb
->
StaticUnicodeString
.
Buffer
=
(
PWSTR
)
teb
->
StaticUnicodeBuffer
;
InitializeListHead
(
&
teb
->
TlsLinks
);
teb
->
teb_sel
=
wine_ldt_alloc_fs
();
return
(
teb
->
teb_sel
!=
0
);
}
/***********************************************************************
* THREAD_FreeTEB
*
* Free data structures associated with a thread.
* Must be called from the context of another thread.
*/
static
void
THREAD_FreeTEB
(
TEB
*
teb
)
{
TRACE
(
"(%p) called
\n
"
,
teb
);
/* Free the associated memory */
wine_ldt_free_fs
(
teb
->
teb_sel
);
VirtualFree
(
teb
->
DeallocationStack
,
0
,
MEM_RELEASE
);
}
/***********************************************************************
* THREAD_InitStack
*
* Allocate the stack of a thread.
*/
TEB
*
THREAD_InitStack
(
TEB
*
teb
,
DWORD
stack_size
)
{
DWORD
old_prot
,
total_size
;
DWORD
page_size
=
getpagesize
();
void
*
base
;
/* Allocate the stack */
if
(
stack_size
>=
16
*
1024
*
1024
)
WARN
(
"Thread stack size is %ld MB.
\n
"
,
stack_size
/
1024
/
1024
);
/* if size is smaller than default, get stack size from parent */
if
(
stack_size
<
1024
*
1024
)
{
if
(
teb
)
stack_size
=
1024
*
1024
;
/* no parent */
else
stack_size
=
((
char
*
)
NtCurrentTeb
()
->
Tib
.
StackBase
-
(
char
*
)
NtCurrentTeb
()
->
DeallocationStack
-
SIGNAL_STACK_SIZE
-
3
*
page_size
);
}
/* FIXME: some Wine functions use a lot of stack, so we add 64Kb here */
stack_size
+=
64
*
1024
;
/* Memory layout in allocated block:
*
* size contents
* 1 page NOACCESS guard page
* SIGNAL_STACK_SIZE signal stack
* 1 page NOACCESS guard page
* 1 page PAGE_GUARD guard page
* stack_size normal stack
* 1 page TEB (except for initial thread)
*/
stack_size
=
(
stack_size
+
(
page_size
-
1
))
&
~
(
page_size
-
1
);
total_size
=
stack_size
+
SIGNAL_STACK_SIZE
+
3
*
page_size
;
if
(
!
teb
)
total_size
+=
page_size
;
if
(
!
(
base
=
VirtualAlloc
(
NULL
,
total_size
,
MEM_COMMIT
,
PAGE_EXECUTE_READWRITE
)))
return
NULL
;
if
(
!
teb
)
{
teb
=
(
TEB
*
)((
char
*
)
base
+
total_size
-
page_size
);
if
(
!
THREAD_InitTEB
(
teb
))
{
VirtualFree
(
base
,
0
,
MEM_RELEASE
);
return
NULL
;
}
}
teb
->
DeallocationStack
=
base
;
teb
->
signal_stack
=
(
char
*
)
base
+
page_size
;
teb
->
Tib
.
StackBase
=
(
char
*
)
base
+
3
*
page_size
+
SIGNAL_STACK_SIZE
+
stack_size
;
teb
->
Tib
.
StackLimit
=
base
;
/* note: limit is lower than base since the stack grows down */
/* Setup guard pages */
VirtualProtect
(
base
,
1
,
PAGE_NOACCESS
,
&
old_prot
);
VirtualProtect
(
(
char
*
)
teb
->
signal_stack
+
SIGNAL_STACK_SIZE
,
1
,
PAGE_NOACCESS
,
&
old_prot
);
VirtualProtect
(
(
char
*
)
teb
->
signal_stack
+
SIGNAL_STACK_SIZE
+
page_size
,
1
,
PAGE_EXECUTE_READWRITE
|
PAGE_GUARD
,
&
old_prot
);
return
teb
;
}
/***********************************************************************
* THREAD_Init
*
* Setup the initial thread.
*
* NOTES: The first allocated TEB on NT is at 0x7ffde000.
*/
void
THREAD_Init
(
void
)
{
static
struct
debug_info
info
;
/* debug info for initial thread */
if
(
!
initial_teb
.
Tib
.
Self
)
/* do it only once */
{
THREAD_InitTEB
(
&
initial_teb
);
assert
(
initial_teb
.
teb_sel
);
info
.
str_pos
=
info
.
strings
;
info
.
out_pos
=
info
.
output
;
initial_teb
.
debug_info
=
&
info
;
initial_teb
.
Peb
=
(
PEB
*
)
&
current_process
;
/* FIXME */
SYSDEPS_SetCurThread
(
&
initial_teb
);
}
}
DECL_GLOBAL_CONSTRUCTOR
(
thread_init
)
{
THREAD_Init
();
}
/***********************************************************************
* THREAD_Start
*
* Start execution of a newly created thread. Does not return.
*/
static
void
THREAD_Start
(
TEB
*
teb
)
{
LPTHREAD_START_ROUTINE
func
=
(
LPTHREAD_START_ROUTINE
)
teb
->
entry_point
;
struct
debug_info
info
;
info
.
str_pos
=
info
.
strings
;
info
.
out_pos
=
info
.
output
;
teb
->
debug_info
=
&
info
;
SYSDEPS_SetCurThread
(
teb
);
SIGNAL_Init
();
CLIENT_InitThread
();
if
(
TRACE_ON
(
relay
))
DPRINTF
(
"%04lx:Starting thread (entryproc=%p)
\n
"
,
GetCurrentThreadId
(),
func
);
__TRY
{
MODULE_DllThreadAttach
(
NULL
);
ExitThread
(
func
(
NtCurrentTeb
()
->
entry_arg
)
);
}
__EXCEPT
(
UnhandledExceptionFilter
)
{
TerminateThread
(
GetCurrentThread
(),
GetExceptionCode
()
);
}
__ENDTRY
}
/***********************************************************************
* CreateThread (KERNEL32.@)
*/
HANDLE
WINAPI
CreateThread
(
SECURITY_ATTRIBUTES
*
sa
,
SIZE_T
stack
,
LPTHREAD_START_ROUTINE
start
,
LPVOID
param
,
DWORD
flags
,
LPDWORD
id
)
{
HANDLE
handle
=
0
;
TEB
*
teb
;
DWORD
tid
=
0
;
int
request_pipe
[
2
];
if
(
pipe
(
request_pipe
)
==
-
1
)
{
SetLastError
(
ERROR_TOO_MANY_OPEN_FILES
);
return
0
;
}
fcntl
(
request_pipe
[
1
],
F_SETFD
,
1
);
/* set close on exec flag */
wine_server_send_fd
(
request_pipe
[
0
]
);
SERVER_START_REQ
(
new_thread
)
{
req
->
suspend
=
((
flags
&
CREATE_SUSPENDED
)
!=
0
);
req
->
inherit
=
(
sa
&&
(
sa
->
nLength
>=
sizeof
(
*
sa
))
&&
sa
->
bInheritHandle
);
req
->
request_fd
=
request_pipe
[
0
];
if
(
!
wine_server_call_err
(
req
))
{
handle
=
reply
->
handle
;
tid
=
reply
->
tid
;
}
close
(
request_pipe
[
0
]
);
}
SERVER_END_REQ
;
if
(
!
handle
||
!
(
teb
=
THREAD_InitStack
(
NULL
,
stack
)))
{
close
(
request_pipe
[
1
]
);
return
0
;
}
teb
->
Peb
=
NtCurrentTeb
()
->
Peb
;
teb
->
ClientId
.
UniqueThread
=
(
HANDLE
)
tid
;
teb
->
request_fd
=
request_pipe
[
1
];
teb
->
entry_point
=
start
;
teb
->
entry_arg
=
param
;
teb
->
htask16
=
GetCurrentTask
();
RtlAcquirePebLock
();
InsertHeadList
(
&
NtCurrentTeb
()
->
TlsLinks
,
&
teb
->
TlsLinks
);
RtlReleasePebLock
();
if
(
id
)
*
id
=
tid
;
if
(
SYSDEPS_SpawnThread
(
THREAD_Start
,
teb
)
==
-
1
)
{
CloseHandle
(
handle
);
close
(
request_pipe
[
1
]
);
RtlAcquirePebLock
();
RemoveEntryList
(
&
teb
->
TlsLinks
);
RtlReleasePebLock
();
THREAD_FreeTEB
(
teb
);
return
0
;
}
return
handle
;
}
/***********************************************************************
* ExitThread [KERNEL32.@] Ends a thread
*
* RETURNS
* None
*/
void
WINAPI
ExitThread
(
DWORD
code
)
/* [in] Exit code for this thread */
{
BOOL
last
;
SERVER_START_REQ
(
terminate_thread
)
{
/* send the exit code to the server */
req
->
handle
=
GetCurrentThread
();
req
->
exit_code
=
code
;
wine_server_call
(
req
);
last
=
reply
->
last
;
}
SERVER_END_REQ
;
if
(
last
)
{
LdrShutdownProcess
();
exit
(
code
);
}
else
{
LdrShutdownThread
();
RtlAcquirePebLock
();
RemoveEntryList
(
&
NtCurrentTeb
()
->
TlsLinks
);
RtlReleasePebLock
();
SYSDEPS_ExitThread
(
code
);
}
}
/***********************************************************************
* OpenThread Retrieves a handle to a thread from its thread id
*
* RETURNS
* None
*/
HANDLE
WINAPI
OpenThread
(
DWORD
dwDesiredAccess
,
BOOL
bInheritHandle
,
DWORD
dwThreadId
)
{
HANDLE
ret
=
0
;
SERVER_START_REQ
(
open_thread
)
{
req
->
tid
=
dwThreadId
;
req
->
access
=
dwDesiredAccess
;
req
->
inherit
=
bInheritHandle
;
if
(
!
wine_server_call_err
(
req
))
ret
=
reply
->
handle
;
}
SERVER_END_REQ
;
return
ret
;
}
/**********************************************************************
* TerminateThread [KERNEL32.@] Terminates a thread
*
* RETURNS
* Success: TRUE
* Failure: FALSE
*/
BOOL
WINAPI
TerminateThread
(
HANDLE
handle
,
/* [in] Handle to thread */
DWORD
exit_code
)
/* [in] Exit code for thread */
{
NTSTATUS
status
=
NtTerminateThread
(
handle
,
exit_code
);
if
(
status
)
SetLastError
(
RtlNtStatusToDosError
(
status
)
);
return
!
status
;
}
/**********************************************************************
* GetExitCodeThread (KERNEL32.@)
*
* Gets termination status of thread.
*
* RETURNS
* Success: TRUE
* Failure: FALSE
*/
BOOL
WINAPI
GetExitCodeThread
(
HANDLE
hthread
,
/* [in] Handle to thread */
LPDWORD
exitcode
)
/* [out] Address to receive termination status */
{
THREAD_BASIC_INFORMATION
info
;
NTSTATUS
status
=
NtQueryInformationThread
(
hthread
,
ThreadBasicInformation
,
&
info
,
sizeof
(
info
),
NULL
);
if
(
status
)
{
SetLastError
(
RtlNtStatusToDosError
(
status
)
);
return
FALSE
;
}
if
(
exitcode
)
*
exitcode
=
info
.
ExitStatus
;
return
TRUE
;
}
/* callback for QueueUserAPC */
static
void
CALLBACK
call_user_apc
(
ULONG_PTR
arg1
,
ULONG_PTR
arg2
,
ULONG_PTR
arg3
)
{
PAPCFUNC
func
=
(
PAPCFUNC
)
arg1
;
func
(
arg2
);
}
/***********************************************************************
* QueueUserAPC (KERNEL32.@)
*/
DWORD
WINAPI
QueueUserAPC
(
PAPCFUNC
func
,
HANDLE
hthread
,
ULONG_PTR
data
)
{
NTSTATUS
status
=
NtQueueApcThread
(
hthread
,
call_user_apc
,
(
ULONG_PTR
)
func
,
data
,
0
);
if
(
status
)
SetLastError
(
RtlNtStatusToDosError
(
status
)
);
return
!
status
;
}
/***********************************************************************
* ProcessIdToSessionId (KERNEL32.@)
* This function is available on Terminal Server 4SP4 and Windows 2000
*/
BOOL
WINAPI
ProcessIdToSessionId
(
DWORD
procid
,
DWORD
*
sessionid_ptr
)
{
/* According to MSDN, if the calling process is not in a terminal
* services environment, then the sessionid returned is zero.
*/
*
sessionid_ptr
=
0
;
return
TRUE
;
}
#ifdef __i386__
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment