From da8acbc47d6e14ca6a1336dfdb9847356c71cc32 Mon Sep 17 00:00:00 2001
From: Alexandre Julliard <julliard@winehq.org>
Date: Thu, 20 Aug 2009 20:05:21 +0200
Subject: [PATCH] server: Define an architecture-independent structure for
 process startup info.

---
 dlls/kernel32/process.c        | 218 +++++++++++++++++++--------------
 dlls/ntdll/thread.c            | 129 +++++++++++--------
 include/wine/server_protocol.h |  50 ++++++--
 server/process.c               |  72 +++++++----
 server/protocol.def            |  48 ++++++--
 server/request.h               |  18 ++-
 server/trace.c                 |  93 +++++---------
 7 files changed, 371 insertions(+), 257 deletions(-)

diff --git a/dlls/kernel32/process.c b/dlls/kernel32/process.c
index 92eb01a022c..ddc93f28fc6 100644
--- a/dlls/kernel32/process.c
+++ b/dlls/kernel32/process.c
@@ -1430,85 +1430,127 @@ static int fork_and_exec( const char *filename, const WCHAR *cmdline, const WCHA
 }
 
 
+static inline DWORD append_string( void **ptr, const WCHAR *str )
+{
+    DWORD len = strlenW( str );
+    memcpy( *ptr, str, len * sizeof(WCHAR) );
+    *ptr = (WCHAR *)*ptr + len;
+    return len * sizeof(WCHAR);
+}
+
 /***********************************************************************
- *           create_user_params
+ *           create_startup_info
  */
-static RTL_USER_PROCESS_PARAMETERS *create_user_params( LPCWSTR filename, LPCWSTR cmdline,
-                                                        LPCWSTR cur_dir, LPWSTR env, DWORD flags,
-                                                        const STARTUPINFOW *startup )
+static startup_info_t *create_startup_info( LPCWSTR filename, LPCWSTR cmdline,
+                                            LPCWSTR cur_dir, LPWSTR env, DWORD flags,
+                                            const STARTUPINFOW *startup, DWORD *info_size )
 {
-    RTL_USER_PROCESS_PARAMETERS *params;
-    UNICODE_STRING image_str, cmdline_str, curdir_str, desktop, title, runtime, newdir;
-    NTSTATUS status;
-    WCHAR buffer[MAX_PATH];
+    const RTL_USER_PROCESS_PARAMETERS *cur_params;
+    startup_info_t *info;
+    DWORD size;
+    void *ptr;
+    UNICODE_STRING newdir;
+    WCHAR imagepath[MAX_PATH];
+    HANDLE hstdin, hstdout, hstderr;
+
+    if(!GetLongPathNameW( filename, imagepath, MAX_PATH ))
+        lstrcpynW( imagepath, filename, MAX_PATH );
+    if(!GetFullPathNameW( imagepath, MAX_PATH, imagepath, NULL ))
+        lstrcpynW( imagepath, filename, MAX_PATH );
 
-    if(!GetLongPathNameW( filename, buffer, MAX_PATH ))
-        lstrcpynW( buffer, filename, MAX_PATH );
-    if(!GetFullPathNameW( buffer, MAX_PATH, buffer, NULL ))
-        lstrcpynW( buffer, filename, MAX_PATH );
-    RtlInitUnicodeString( &image_str, buffer );
+    cur_params = NtCurrentTeb()->Peb->ProcessParameters;
 
-    RtlInitUnicodeString( &cmdline_str, cmdline );
     newdir.Buffer = NULL;
     if (cur_dir)
     {
         if (RtlDosPathNameToNtPathName_U( cur_dir, &newdir, NULL, NULL ))
-        {
-            /* skip \??\ prefix */
-            curdir_str.Buffer = newdir.Buffer + 4;
-            curdir_str.Length = newdir.Length - 4 * sizeof(WCHAR);
-            curdir_str.MaximumLength = newdir.MaximumLength - 4 * sizeof(WCHAR);
-        }
-        else cur_dir = NULL;
+            cur_dir = newdir.Buffer + 4;  /* skip \??\ prefix */
+        else
+            cur_dir = NULL;
     }
-    if (startup->lpDesktop) RtlInitUnicodeString( &desktop, startup->lpDesktop );
-    if (startup->lpTitle) RtlInitUnicodeString( &title, startup->lpTitle );
-    if (startup->lpReserved2 && startup->cbReserved2)
+    if (!cur_dir)
     {
-        runtime.Length = 0;
-        runtime.MaximumLength = startup->cbReserved2;
-        runtime.Buffer = (WCHAR*)startup->lpReserved2;
+        if (NtCurrentTeb()->Tib.SubSystemTib)  /* FIXME: hack */
+            cur_dir = ((WIN16_SUBSYSTEM_TIB *)NtCurrentTeb()->Tib.SubSystemTib)->curdir.DosPath.Buffer;
+        else
+            cur_dir = cur_params->CurrentDirectory.DosPath.Buffer;
     }
 
-    status = RtlCreateProcessParameters( &params, &image_str, NULL,
-                                         cur_dir ? &curdir_str : NULL,
-                                         &cmdline_str, env,
-                                         startup->lpTitle ? &title : NULL,
-                                         startup->lpDesktop ? &desktop : NULL,
-                                         NULL, 
-                                         (startup->lpReserved2 && startup->cbReserved2) ? &runtime : NULL );
-    RtlFreeUnicodeString( &newdir );
-    if (status != STATUS_SUCCESS)
-    {
-        SetLastError( RtlNtStatusToDosError(status) );
-        return NULL;
-    }
+    size = sizeof(*info);
+    size += strlenW( cur_dir ) * sizeof(WCHAR);
+    size += cur_params->DllPath.Length;
+    size += strlenW( imagepath ) * sizeof(WCHAR);
+    size += strlenW( cmdline ) * sizeof(WCHAR);
+    if (startup->lpTitle) size += strlenW( startup->lpTitle ) * sizeof(WCHAR);
+    if (startup->lpDesktop) size += strlenW( startup->lpDesktop ) * sizeof(WCHAR);
+    /* FIXME: shellinfo */
+    if (startup->lpReserved2 && startup->cbReserved2) size += startup->cbReserved2;
+    size = (size + 1) & ~1;
+    *info_size = size;
 
-    if (flags & CREATE_NEW_PROCESS_GROUP) params->ConsoleFlags = 1;
-    if (flags & CREATE_NEW_CONSOLE) params->ConsoleHandle = (HANDLE)1;  /* FIXME: cf. kernel_main.c */
+    if (!(info = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ))) goto done;
+
+    info->console_flags = cur_params->ConsoleFlags;
+    if (flags & CREATE_NEW_PROCESS_GROUP) info->console_flags = 1;
+    if (flags & CREATE_NEW_CONSOLE) info->console = (obj_handle_t)1;  /* FIXME: cf. kernel_main.c */
 
     if (startup->dwFlags & STARTF_USESTDHANDLES)
     {
-        params->hStdInput  = startup->hStdInput;
-        params->hStdOutput = startup->hStdOutput;
-        params->hStdError  = startup->hStdError;
+        hstdin  = startup->hStdInput;
+        hstdout = startup->hStdOutput;
+        hstderr = startup->hStdError;
     }
     else
     {
-        params->hStdInput  = GetStdHandle( STD_INPUT_HANDLE );
-        params->hStdOutput = GetStdHandle( STD_OUTPUT_HANDLE );
-        params->hStdError  = GetStdHandle( STD_ERROR_HANDLE );
+        hstdin  = GetStdHandle( STD_INPUT_HANDLE );
+        hstdout = GetStdHandle( STD_OUTPUT_HANDLE );
+        hstderr = GetStdHandle( STD_ERROR_HANDLE );
     }
-    params->dwX             = startup->dwX;
-    params->dwY             = startup->dwY;
-    params->dwXSize         = startup->dwXSize;
-    params->dwYSize         = startup->dwYSize;
-    params->dwXCountChars   = startup->dwXCountChars;
-    params->dwYCountChars   = startup->dwYCountChars;
-    params->dwFillAttribute = startup->dwFillAttribute;
-    params->dwFlags         = startup->dwFlags;
-    params->wShowWindow     = startup->wShowWindow;
-    return params;
+    info->hstdin  = wine_server_obj_handle( hstdin );
+    info->hstdout = wine_server_obj_handle( hstdout );
+    info->hstderr = wine_server_obj_handle( hstderr );
+    if ((flags & (CREATE_NEW_CONSOLE | DETACHED_PROCESS)) != 0)
+    {
+        /* this is temporary (for console handles). We have no way to control that the handle is invalid in child process otherwise */
+        if (is_console_handle(hstdin))  info->hstdin  = wine_server_obj_handle( INVALID_HANDLE_VALUE );
+        if (is_console_handle(hstdout)) info->hstdout = wine_server_obj_handle( INVALID_HANDLE_VALUE );
+        if (is_console_handle(hstderr)) info->hstderr = wine_server_obj_handle( INVALID_HANDLE_VALUE );
+    }
+    else
+    {
+        if (is_console_handle(hstdin))  info->hstdin  = console_handle_unmap(hstdin);
+        if (is_console_handle(hstdout)) info->hstdout = console_handle_unmap(hstdout);
+        if (is_console_handle(hstderr)) info->hstderr = console_handle_unmap(hstderr);
+    }
+
+    info->x         = startup->dwX;
+    info->y         = startup->dwY;
+    info->xsize     = startup->dwXSize;
+    info->ysize     = startup->dwYSize;
+    info->xchars    = startup->dwXCountChars;
+    info->ychars    = startup->dwYCountChars;
+    info->attribute = startup->dwFillAttribute;
+    info->flags     = startup->dwFlags;
+    info->show      = startup->wShowWindow;
+
+    ptr = info + 1;
+    info->curdir_len = append_string( &ptr, cur_dir );
+    info->dllpath_len = cur_params->DllPath.Length;
+    memcpy( ptr, cur_params->DllPath.Buffer, cur_params->DllPath.Length );
+    ptr = (char *)ptr + cur_params->DllPath.Length;
+    info->imagepath_len = append_string( &ptr, imagepath );
+    info->cmdline_len = append_string( &ptr, cmdline );
+    if (startup->lpTitle) info->title_len = append_string( &ptr, startup->lpTitle );
+    if (startup->lpDesktop) info->desktop_len = append_string( &ptr, startup->lpDesktop );
+    if (startup->lpReserved2 && startup->cbReserved2)
+    {
+        info->runtime_len = startup->cbReserved2;
+        memcpy( ptr, startup->lpReserved2, startup->cbReserved2 );
+    }
+
+done:
+    RtlFreeUnicodeString( &newdir );
+    return info;
 }
 
 
@@ -1525,11 +1567,12 @@ static BOOL create_process( HANDLE hFile, LPCWSTR filename, LPWSTR cmd_line, LPW
                             void *res_start, void *res_end, DWORD binary_type, int exec_only )
 {
     BOOL ret, success = FALSE;
-    HANDLE process_info, hstdin, hstdout;
+    HANDLE process_info;
     WCHAR *env_end;
     char *winedebug = NULL;
     char **argv;
-    RTL_USER_PROCESS_PARAMETERS *params;
+    startup_info_t *startup_info;
+    DWORD startup_info_size;
     int socketfd[2], stdin_fd = -1, stdout_fd = -1;
     pid_t pid;
     int err;
@@ -1541,14 +1584,16 @@ static BOOL create_process( HANDLE hFile, LPCWSTR filename, LPWSTR cmd_line, LPW
         return FALSE;
     }
 
-    if (!env) RtlAcquirePebLock();
+    RtlAcquirePebLock();
 
-    if (!(params = create_user_params( filename, cmd_line, cur_dir, env, flags, startup )))
+    if (!(startup_info = create_startup_info( filename, cmd_line, cur_dir, env, flags, startup,
+                                              &startup_info_size )))
     {
-        if (!env) RtlReleasePebLock();
+        RtlReleasePebLock();
         return FALSE;
     }
-    env_end = params->Environment;
+    if (!env) env = NtCurrentTeb()->Peb->ProcessParameters->Environment;
+    env_end = env;
     while (*env_end)
     {
         static const WCHAR WINEDEBUG[] = {'W','I','N','E','D','E','B','U','G','=',0};
@@ -1566,9 +1611,9 @@ static BOOL create_process( HANDLE hFile, LPCWSTR filename, LPWSTR cmd_line, LPW
 
     if (socketpair( PF_UNIX, SOCK_STREAM, 0, socketfd ) == -1)
     {
-        if (!env) RtlReleasePebLock();
+        RtlReleasePebLock();
         HeapFree( GetProcessHeap(), 0, winedebug );
-        RtlDestroyProcessParameters( params );
+        HeapFree( GetProcessHeap(), 0, startup_info );
         SetLastError( ERROR_TOO_MANY_OPEN_FILES );
         return FALSE;
     }
@@ -1587,29 +1632,10 @@ static BOOL create_process( HANDLE hFile, LPCWSTR filename, LPWSTR cmd_line, LPW
         req->process_attr   = (psa && (psa->nLength >= sizeof(*psa)) && psa->bInheritHandle) ? OBJ_INHERIT : 0;
         req->thread_access  = THREAD_ALL_ACCESS;
         req->thread_attr    = (tsa && (tsa->nLength >= sizeof(*tsa)) && tsa->bInheritHandle) ? OBJ_INHERIT : 0;
-        req->hstdin         = wine_server_obj_handle( params->hStdInput );
-        req->hstdout        = wine_server_obj_handle( params->hStdOutput );
-        req->hstderr        = wine_server_obj_handle( params->hStdError );
+        req->info_size      = startup_info_size;
 
-        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(params->hStdInput))  req->hstdin  = wine_server_obj_handle( INVALID_HANDLE_VALUE );
-            if (is_console_handle(params->hStdOutput)) req->hstdout = wine_server_obj_handle( INVALID_HANDLE_VALUE );
-            if (is_console_handle(params->hStdError))  req->hstderr = wine_server_obj_handle( INVALID_HANDLE_VALUE );
-            hstdin = hstdout = 0;
-        }
-        else
-        {
-            if (is_console_handle(params->hStdInput))  req->hstdin  = console_handle_unmap(params->hStdInput);
-            if (is_console_handle(params->hStdOutput)) req->hstdout = console_handle_unmap(params->hStdOutput);
-            if (is_console_handle(params->hStdError))  req->hstderr = console_handle_unmap(params->hStdError);
-            hstdin  = wine_server_ptr_handle( req->hstdin );
-            hstdout = wine_server_ptr_handle( req->hstdout );
-        }
-
-        wine_server_add_data( req, params, params->Size );
-        wine_server_add_data( req, params->Environment, (env_end-params->Environment)*sizeof(WCHAR) );
+        wine_server_add_data( req, startup_info, startup_info_size );
+        wine_server_add_data( req, env, (env_end - env) * sizeof(WCHAR) );
         if ((ret = !wine_server_call_err( req )))
         {
             info->dwProcessId = (DWORD)reply->pid;
@@ -1621,17 +1647,25 @@ static BOOL create_process( HANDLE hFile, LPCWSTR filename, LPWSTR cmd_line, LPW
     }
     SERVER_END_REQ;
 
-    if (!env) RtlReleasePebLock();
-    RtlDestroyProcessParameters( params );
+    RtlReleasePebLock();
     if (!ret)
     {
         close( socketfd[0] );
+        HeapFree( GetProcessHeap(), 0, startup_info );
         HeapFree( GetProcessHeap(), 0, winedebug );
         return FALSE;
     }
 
-    if (hstdin) wine_server_handle_to_fd( hstdin, FILE_READ_DATA, &stdin_fd, NULL );
-    if (hstdout) wine_server_handle_to_fd( hstdout, FILE_WRITE_DATA, &stdout_fd, NULL );
+    if (!(flags & (CREATE_NEW_CONSOLE | DETACHED_PROCESS)))
+    {
+        if (startup_info->hstdin)
+            wine_server_handle_to_fd( wine_server_ptr_handle(startup_info->hstdin),
+                                      FILE_READ_DATA, &stdin_fd, NULL );
+        if (startup_info->hstdout)
+            wine_server_handle_to_fd( wine_server_ptr_handle(startup_info->hstdout),
+                                      FILE_WRITE_DATA, &stdout_fd, NULL );
+    }
+    HeapFree( GetProcessHeap(), 0, startup_info );
 
     /* create the child process */
     argv = build_argv( cmd_line, 1 );
diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c
index c2d839c0813..f44bd6083ff 100644
--- a/dlls/ntdll/thread.c
+++ b/dlls/ntdll/thread.c
@@ -126,89 +126,118 @@ static inline NTSTATUS init_teb( TEB *teb )
 
 
 /***********************************************************************
- *           fix_unicode_string
+ *           get_unicode_string
  *
- * Make sure the unicode string doesn't point beyond the end pointer
+ * Copy a unicode string from the startup info.
  */
-static inline void fix_unicode_string( UNICODE_STRING *str, const char *end_ptr )
+static inline void get_unicode_string( UNICODE_STRING *str, WCHAR **src, WCHAR **dst, UINT len )
 {
-    if ((char *)str->Buffer >= end_ptr)
-    {
-        str->Length = str->MaximumLength = 0;
-        str->Buffer = NULL;
-        return;
-    }
-    if ((char *)str->Buffer + str->MaximumLength > end_ptr)
-    {
-        str->MaximumLength = (end_ptr - (char *)str->Buffer) & ~(sizeof(WCHAR) - 1);
-    }
-    if (str->Length >= str->MaximumLength)
-    {
-        if (str->MaximumLength >= sizeof(WCHAR))
-            str->Length = str->MaximumLength - sizeof(WCHAR);
-        else
-            str->Length = str->MaximumLength = 0;
-    }
+    str->Buffer = *dst;
+    str->Length = len;
+    str->MaximumLength = len + sizeof(WCHAR);
+    memcpy( str->Buffer, *src, len );
+    str->Buffer[len / sizeof(WCHAR)] = 0;
+    *src += len / sizeof(WCHAR);
+    *dst += len / sizeof(WCHAR) + 1;
 }
 
-
 /***********************************************************************
  *           init_user_process_params
  *
  * Fill the RTL_USER_PROCESS_PARAMETERS structure from the server.
  */
-static NTSTATUS init_user_process_params( SIZE_T info_size, HANDLE *exe_file )
+static NTSTATUS init_user_process_params( SIZE_T data_size, HANDLE *exe_file )
 {
     void *ptr;
-    SIZE_T env_size;
+    WCHAR *src, *dst;
+    SIZE_T info_size, env_size, size, alloc_size;
     NTSTATUS status;
+    startup_info_t *info;
     RTL_USER_PROCESS_PARAMETERS *params = NULL;
 
-    status = NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&params, 0, &info_size,
-                                      MEM_COMMIT, PAGE_READWRITE );
-    if (status != STATUS_SUCCESS) return status;
-
-    params->AllocationSize = info_size;
-    NtCurrentTeb()->Peb->ProcessParameters = params;
+    if (!(info = RtlAllocateHeap( GetProcessHeap(), 0, data_size )))
+        return STATUS_NO_MEMORY;
 
     SERVER_START_REQ( get_startup_info )
     {
-        wine_server_set_reply( req, params, info_size );
+        wine_server_set_reply( req, info, data_size );
         if (!(status = wine_server_call( req )))
         {
-            info_size = wine_server_reply_size( reply );
+            data_size = wine_server_reply_size( reply );
+            info_size = reply->info_size;
+            env_size  = data_size - info_size;
             *exe_file = wine_server_ptr_handle( reply->exe_file );
-            params->hStdInput  = wine_server_ptr_handle( reply->hstdin );
-            params->hStdOutput = wine_server_ptr_handle( reply->hstdout );
-            params->hStdError  = wine_server_ptr_handle( reply->hstderr );
         }
     }
     SERVER_END_REQ;
     if (status != STATUS_SUCCESS) return status;
 
-    if (params->Size > info_size) params->Size = info_size;
+    size = sizeof(*params);
+    size += MAX_NT_PATH_LENGTH * sizeof(WCHAR);
+    size += info->dllpath_len + sizeof(WCHAR);
+    size += info->imagepath_len + sizeof(WCHAR);
+    size += info->cmdline_len + sizeof(WCHAR);
+    size += info->title_len + sizeof(WCHAR);
+    size += info->desktop_len + sizeof(WCHAR);
+    size += info->shellinfo_len + sizeof(WCHAR);
+    size += info->runtime_len + sizeof(WCHAR);
+
+    alloc_size = size;
+    status = NtAllocateVirtualMemory( NtCurrentProcess(), (void **)&params, 0, &alloc_size,
+                                      MEM_COMMIT, PAGE_READWRITE );
+    if (status != STATUS_SUCCESS) goto done;
 
-    /* make sure the strings are valid */
-    fix_unicode_string( &params->CurrentDirectory.DosPath, (char *)info_size );
-    fix_unicode_string( &params->DllPath, (char *)info_size );
-    fix_unicode_string( &params->ImagePathName, (char *)info_size );
-    fix_unicode_string( &params->CommandLine, (char *)info_size );
-    fix_unicode_string( &params->WindowTitle, (char *)info_size );
-    fix_unicode_string( &params->Desktop, (char *)info_size );
-    fix_unicode_string( &params->ShellInfo, (char *)info_size );
-    fix_unicode_string( &params->RuntimeInfo, (char *)info_size );
+    NtCurrentTeb()->Peb->ProcessParameters = params;
+    params->AllocationSize  = alloc_size;
+    params->Size            = size;
+    params->Flags           = PROCESS_PARAMS_FLAG_NORMALIZED;
+    params->DebugFlags      = info->debug_flags;
+    params->ConsoleHandle   = wine_server_ptr_handle( info->console );
+    params->ConsoleFlags    = info->console_flags;
+    params->hStdInput       = wine_server_ptr_handle( info->hstdin );
+    params->hStdOutput      = wine_server_ptr_handle( info->hstdout );
+    params->hStdError       = wine_server_ptr_handle( info->hstderr );
+    params->dwX             = info->x;
+    params->dwY             = info->y;
+    params->dwXSize         = info->xsize;
+    params->dwYSize         = info->ysize;
+    params->dwXCountChars   = info->xchars;
+    params->dwYCountChars   = info->ychars;
+    params->dwFillAttribute = info->attribute;
+    params->dwFlags         = info->flags;
+    params->wShowWindow     = info->show;
+
+    src = (WCHAR *)(info + 1);
+    dst = (WCHAR *)(params + 1);
+
+    /* current directory needs more space */
+    get_unicode_string( &params->CurrentDirectory.DosPath, &src, &dst, info->curdir_len );
+    params->CurrentDirectory.DosPath.MaximumLength = MAX_NT_PATH_LENGTH * sizeof(WCHAR);
+    dst = (WCHAR *)(params + 1) + MAX_NT_PATH_LENGTH;
+
+    get_unicode_string( &params->DllPath, &src, &dst, info->dllpath_len );
+    get_unicode_string( &params->ImagePathName, &src, &dst, info->imagepath_len );
+    get_unicode_string( &params->CommandLine, &src, &dst, info->cmdline_len );
+    get_unicode_string( &params->WindowTitle, &src, &dst, info->title_len );
+    get_unicode_string( &params->Desktop, &src, &dst, info->desktop_len );
+    get_unicode_string( &params->ShellInfo, &src, &dst, info->shellinfo_len );
+
+    /* runtime info isn't a real string */
+    params->RuntimeInfo.Buffer = dst;
+    params->RuntimeInfo.Length = params->RuntimeInfo.MaximumLength = info->runtime_len;
+    memcpy( dst, src, info->runtime_len );
 
     /* environment needs to be a separate memory block */
-    env_size = info_size - params->Size;
-    if (!env_size) env_size = 1;
     ptr = NULL;
-    status = NtAllocateVirtualMemory( NtCurrentProcess(), &ptr, 0, &env_size,
+    alloc_size = max( 1, env_size );
+    status = NtAllocateVirtualMemory( NtCurrentProcess(), &ptr, 0, &alloc_size,
                                       MEM_COMMIT, PAGE_READWRITE );
-    if (status != STATUS_SUCCESS) return status;
-    memcpy( ptr, (char *)params + params->Size, info_size - params->Size );
+    if (status != STATUS_SUCCESS) goto done;
+    memcpy( ptr, (char *)info + info_size, env_size );
     params->Environment = ptr;
 
-    RtlNormalizeProcessParams( params );
+done:
+    RtlFreeHeap( GetProcessHeap(), 0, info );
     return status;
 }
 
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index c926f6d8744..6ab1df24bd0 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -211,6 +211,42 @@ typedef __int64 timeout_t;
 #define TIMEOUT_INFINITE (((timeout_t)0x7fffffff) << 32 | 0xffffffff)
 
 
+typedef struct
+{
+    unsigned int debug_flags;
+    unsigned int console_flags;
+    obj_handle_t console;
+    obj_handle_t hstdin;
+    obj_handle_t hstdout;
+    obj_handle_t hstderr;
+    unsigned int x;
+    unsigned int y;
+    unsigned int xsize;
+    unsigned int ysize;
+    unsigned int xchars;
+    unsigned int ychars;
+    unsigned int attribute;
+    unsigned int flags;
+    unsigned int show;
+    data_size_t  curdir_len;
+    data_size_t  dllpath_len;
+    data_size_t  imagepath_len;
+    data_size_t  cmdline_len;
+    data_size_t  title_len;
+    data_size_t  desktop_len;
+    data_size_t  shellinfo_len;
+    data_size_t  runtime_len;
+
+
+
+
+
+
+
+
+} startup_info_t;
+
+
 typedef struct
 {
     atom_t         atom;
@@ -535,14 +571,12 @@ struct new_process_request
     unsigned int create_flags;
     int          socket_fd;
     obj_handle_t exe_file;
-    obj_handle_t hstdin;
-    obj_handle_t hstdout;
-    obj_handle_t hstderr;
     unsigned int process_access;
     unsigned int process_attr;
     unsigned int thread_access;
     unsigned int thread_attr;
-    /* VARARG(info,startup_info); */
+    data_size_t  info_size;
+    /* VARARG(info,startup_info,info_size); */
     /* VARARG(env,unicode_str); */
 };
 struct new_process_reply
@@ -597,10 +631,8 @@ struct get_startup_info_reply
 {
     struct reply_header __header;
     obj_handle_t exe_file;
-    obj_handle_t hstdin;
-    obj_handle_t hstdout;
-    obj_handle_t hstderr;
-    /* VARARG(info,startup_info); */
+    data_size_t  info_size;
+    /* VARARG(info,startup_info,info_size); */
     /* VARARG(env,unicode_str); */
 };
 
@@ -5315,6 +5347,6 @@ union generic_reply
     struct set_window_layered_info_reply set_window_layered_info_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 388
+#define SERVER_PROTOCOL_VERSION 389
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/process.c b/server/process.c
index 2b8d8f6cc64..8df9f394221 100644
--- a/server/process.c
+++ b/server/process.c
@@ -102,13 +102,11 @@ static const struct fd_ops process_fd_ops =
 struct startup_info
 {
     struct object       obj;          /* object header */
-    obj_handle_t        hstdin;       /* handle for stdin */
-    obj_handle_t        hstdout;      /* handle for stdout */
-    obj_handle_t        hstderr;      /* handle for stderr */
     struct file        *exe_file;     /* file handle for main exe */
     struct process     *process;      /* created process */
-    data_size_t         data_size;    /* size of startup data */
-    void               *data;         /* data for startup info */
+    data_size_t         info_size;    /* size of startup info */
+    data_size_t         data_size;    /* size of whole startup data */
+    startup_info_t     *data;         /* data for startup info */
 };
 
 static void startup_info_dump( struct object *obj, int verbose );
@@ -478,7 +476,7 @@ static void startup_info_dump( struct object *obj, int verbose )
     assert( obj->ops == &startup_info_ops );
 
     fprintf( stderr, "Startup info in=%04x out=%04x err=%04x\n",
-             info->hstdin, info->hstdout, info->hstderr );
+             info->data->hstdin, info->data->hstdout, info->data->hstderr );
 }
 
 static int startup_info_signaled( struct object *obj, struct thread *thread )
@@ -891,19 +889,46 @@ DECL_HANDLER(new_process)
 
     /* build the startup info for a new process */
     if (!(info = alloc_object( &startup_info_ops ))) return;
-    info->hstdin       = req->hstdin;
-    info->hstdout      = req->hstdout;
-    info->hstderr      = req->hstderr;
-    info->exe_file     = NULL;
-    info->process      = NULL;
-    info->data_size    = get_req_data_size();
-    info->data         = NULL;
+    info->exe_file = NULL;
+    info->process  = NULL;
+    info->data     = NULL;
 
     if (req->exe_file &&
         !(info->exe_file = get_file_obj( current->process, req->exe_file, FILE_READ_DATA )))
         goto done;
 
-    if (!(info->data = memdup( get_req_data(), info->data_size ))) goto done;
+    info->data_size = get_req_data_size();
+    info->info_size = min( req->info_size, info->data_size );
+
+    if (req->info_size < sizeof(*info->data))
+    {
+        /* make sure we have a full startup_info_t structure */
+        data_size_t env_size = info->data_size - info->info_size;
+        data_size_t info_size = min( req->info_size, FIELD_OFFSET( startup_info_t, curdir_len ));
+
+        if (!(info->data = mem_alloc( sizeof(*info->data) + env_size ))) goto done;
+        memcpy( info->data, get_req_data(), info_size );
+        memset( (char *)info->data + info_size, 0, sizeof(*info->data) - info_size );
+        memcpy( info->data + 1, (const char *)get_req_data() + req->info_size, env_size );
+        info->info_size = sizeof(startup_info_t);
+        info->data_size = info->info_size + env_size;
+    }
+    else
+    {
+        data_size_t pos = sizeof(*info->data);
+
+        if (!(info->data = memdup( get_req_data(), info->data_size ))) goto done;
+#define FIXUP_LEN(len) do { (len) = min( (len), info->info_size - pos ); pos += (len); } while(0)
+        FIXUP_LEN( info->data->curdir_len );
+        FIXUP_LEN( info->data->dllpath_len );
+        FIXUP_LEN( info->data->imagepath_len );
+        FIXUP_LEN( info->data->cmdline_len );
+        FIXUP_LEN( info->data->title_len );
+        FIXUP_LEN( info->data->desktop_len );
+        FIXUP_LEN( info->data->shellinfo_len );
+        FIXUP_LEN( info->data->runtime_len );
+#undef FIXUP_LEN
+    }
 
     if (!(thread = create_process( socket_fd, current, req->inherit_all ))) goto done;
     process = thread->process;
@@ -923,17 +948,17 @@ DECL_HANDLER(new_process)
          * like if hConOut and hConIn are console handles, then they should be on the same
          * physical console
          */
-        inherit_console( current, process, req->inherit_all ? req->hstdin : 0 );
+        inherit_console( current, process, req->inherit_all ? info->data->hstdin : 0 );
     }
 
     if (!req->inherit_all && !(req->create_flags & CREATE_NEW_CONSOLE))
     {
-        info->hstdin  = duplicate_handle( parent, req->hstdin, process,
-                                          0, OBJ_INHERIT, DUPLICATE_SAME_ACCESS );
-        info->hstdout = duplicate_handle( parent, req->hstdout, process,
-                                          0, OBJ_INHERIT, DUPLICATE_SAME_ACCESS );
-        info->hstderr = duplicate_handle( parent, req->hstderr, process,
-                                          0, OBJ_INHERIT, DUPLICATE_SAME_ACCESS );
+        info->data->hstdin  = duplicate_handle( parent, info->data->hstdin, process,
+                                                0, OBJ_INHERIT, DUPLICATE_SAME_ACCESS );
+        info->data->hstdout = duplicate_handle( parent, info->data->hstdout, process,
+                                                0, OBJ_INHERIT, DUPLICATE_SAME_ACCESS );
+        info->data->hstderr = duplicate_handle( parent, info->data->hstderr, process,
+                                                0, OBJ_INHERIT, DUPLICATE_SAME_ACCESS );
         /* some handles above may have been invalid; this is not an error */
         if (get_error() == STATUS_INVALID_HANDLE ||
             get_error() == STATUS_OBJECT_TYPE_MISMATCH) clear_error();
@@ -985,11 +1010,8 @@ DECL_HANDLER(get_startup_info)
     if (info->exe_file &&
         !(reply->exe_file = alloc_handle( process, info->exe_file, GENERIC_READ, 0 ))) return;
 
-    reply->hstdin  = info->hstdin;
-    reply->hstdout = info->hstdout;
-    reply->hstderr = info->hstderr;
-
     /* we return the data directly without making a copy so this can only be called once */
+    reply->info_size = info->info_size;
     size = info->data_size;
     if (size > get_reply_max_size()) size = get_reply_max_size();
     set_reply_data_ptr( info->data, size );
diff --git a/server/protocol.def b/server/protocol.def
index 1dcc04248d0..d6b282a19d8 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -226,6 +226,42 @@ struct wake_up_reply
 typedef __int64 timeout_t;
 #define TIMEOUT_INFINITE (((timeout_t)0x7fffffff) << 32 | 0xffffffff)
 
+/* structure for process startup info */
+typedef struct
+{
+    unsigned int debug_flags;
+    unsigned int console_flags;
+    obj_handle_t console;
+    obj_handle_t hstdin;
+    obj_handle_t hstdout;
+    obj_handle_t hstderr;
+    unsigned int x;
+    unsigned int y;
+    unsigned int xsize;
+    unsigned int ysize;
+    unsigned int xchars;
+    unsigned int ychars;
+    unsigned int attribute;
+    unsigned int flags;
+    unsigned int show;
+    data_size_t  curdir_len;
+    data_size_t  dllpath_len;
+    data_size_t  imagepath_len;
+    data_size_t  cmdline_len;
+    data_size_t  title_len;
+    data_size_t  desktop_len;
+    data_size_t  shellinfo_len;
+    data_size_t  runtime_len;
+    /* VARARG(curdir,unicode_str,curdir_len); */
+    /* VARARG(dllpath,unicode_str,dllpath_len); */
+    /* VARARG(imagepath,unicode_str,imagepath_len); */
+    /* VARARG(cmdline,unicode_str,cmdline_len); */
+    /* VARARG(title,unicode_str,title_len); */
+    /* VARARG(desktop,unicode_str,desktop_len); */
+    /* VARARG(shellinfo,unicode_str,shellinfo_len); */
+    /* VARARG(runtime,unicode_str,runtime_len); */
+} startup_info_t;
+
 /* structure returned in the list of window properties */
 typedef struct
 {
@@ -549,14 +585,12 @@ typedef union
     unsigned int create_flags;   /* creation flags */
     int          socket_fd;      /* file descriptor for process socket */
     obj_handle_t exe_file;       /* file handle for main exe */
-    obj_handle_t hstdin;         /* handle for stdin */
-    obj_handle_t hstdout;        /* handle for stdout */
-    obj_handle_t hstderr;        /* handle for stderr */
     unsigned int process_access; /* access rights for process object */
     unsigned int process_attr;   /* attributes for process object */
     unsigned int thread_access;  /* access rights for thread object */
     unsigned int thread_attr;    /* attributes for thread object */
-    VARARG(info,startup_info);   /* startup information */
+    data_size_t  info_size;      /* size of startup info */
+    VARARG(info,startup_info,info_size); /* startup information */
     VARARG(env,unicode_str);     /* environment for new process */
 @REPLY
     obj_handle_t info;           /* new process info handle */
@@ -592,10 +626,8 @@ typedef union
 @REQ(get_startup_info)
 @REPLY
     obj_handle_t exe_file;     /* file handle for main exe */
-    obj_handle_t hstdin;       /* handle for stdin */
-    obj_handle_t hstdout;      /* handle for stdout */
-    obj_handle_t hstderr;      /* handle for stderr */
-    VARARG(info,startup_info); /* startup information */
+    data_size_t  info_size;    /* size of startup info */
+    VARARG(info,startup_info,info_size); /* startup information */
     VARARG(env,unicode_str);   /* environment */
 @END
 
diff --git a/server/request.h b/server/request.h
index a0efd2d0b0e..c726130443a 100644
--- a/server/request.h
+++ b/server/request.h
@@ -627,13 +627,11 @@ C_ASSERT( FIELD_OFFSET(struct new_process_request, inherit_all) == 12 );
 C_ASSERT( FIELD_OFFSET(struct new_process_request, create_flags) == 16 );
 C_ASSERT( FIELD_OFFSET(struct new_process_request, socket_fd) == 20 );
 C_ASSERT( FIELD_OFFSET(struct new_process_request, exe_file) == 24 );
-C_ASSERT( FIELD_OFFSET(struct new_process_request, hstdin) == 28 );
-C_ASSERT( FIELD_OFFSET(struct new_process_request, hstdout) == 32 );
-C_ASSERT( FIELD_OFFSET(struct new_process_request, hstderr) == 36 );
-C_ASSERT( FIELD_OFFSET(struct new_process_request, process_access) == 40 );
-C_ASSERT( FIELD_OFFSET(struct new_process_request, process_attr) == 44 );
-C_ASSERT( FIELD_OFFSET(struct new_process_request, thread_access) == 48 );
-C_ASSERT( FIELD_OFFSET(struct new_process_request, thread_attr) == 52 );
+C_ASSERT( FIELD_OFFSET(struct new_process_request, process_access) == 28 );
+C_ASSERT( FIELD_OFFSET(struct new_process_request, process_attr) == 32 );
+C_ASSERT( FIELD_OFFSET(struct new_process_request, thread_access) == 36 );
+C_ASSERT( FIELD_OFFSET(struct new_process_request, thread_attr) == 40 );
+C_ASSERT( FIELD_OFFSET(struct new_process_request, info_size) == 44 );
 C_ASSERT( FIELD_OFFSET(struct new_process_reply, info) == 8 );
 C_ASSERT( FIELD_OFFSET(struct new_process_reply, pid) == 12 );
 C_ASSERT( FIELD_OFFSET(struct new_process_reply, phandle) == 16 );
@@ -652,10 +650,8 @@ C_ASSERT( FIELD_OFFSET(struct new_thread_reply, tid) == 8 );
 C_ASSERT( FIELD_OFFSET(struct new_thread_reply, handle) == 12 );
 C_ASSERT( sizeof(struct new_thread_reply) == 16 );
 C_ASSERT( FIELD_OFFSET(struct get_startup_info_reply, exe_file) == 8 );
-C_ASSERT( FIELD_OFFSET(struct get_startup_info_reply, hstdin) == 12 );
-C_ASSERT( FIELD_OFFSET(struct get_startup_info_reply, hstdout) == 16 );
-C_ASSERT( FIELD_OFFSET(struct get_startup_info_reply, hstderr) == 20 );
-C_ASSERT( sizeof(struct get_startup_info_reply) == 24 );
+C_ASSERT( FIELD_OFFSET(struct get_startup_info_reply, info_size) == 12 );
+C_ASSERT( sizeof(struct get_startup_info_reply) == 16 );
 C_ASSERT( FIELD_OFFSET(struct init_process_done_request, gui) == 12 );
 C_ASSERT( FIELD_OFFSET(struct init_process_done_request, module) == 16 );
 C_ASSERT( FIELD_OFFSET(struct init_process_done_request, ldt_copy) == 24 );
diff --git a/server/trace.c b/server/trace.c
index be3f1c68f23..38a20b764b8 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -675,65 +675,38 @@ static void dump_varargs_debug_event( const char *prefix, data_size_t size )
 }
 
 /* dump a unicode string contained in a buffer; helper for dump_varargs_startup_info */
-static void dump_inline_unicode_string( const UNICODE_STRING *str, const void *data, data_size_t size )
+static data_size_t dump_inline_unicode_string( const char *prefix, data_size_t pos, data_size_t len, data_size_t total_size )
 {
-    size_t length = str->Length;
-    size_t offset = (size_t)str->Buffer;
-
-    if (offset >= size) return;
-    if (offset + length > size) length = size - offset;
-    dump_strW( (const WCHAR *)data + offset/sizeof(WCHAR), length/sizeof(WCHAR), stderr, "\"\"" );
+    fputs( prefix, stderr );
+    if (pos >= total_size) return pos;
+    if (len > total_size - pos) len = total_size - pos;
+    len /= sizeof(WCHAR);
+    dump_strW( (const WCHAR *)cur_data + pos/sizeof(WCHAR), len, stderr, "\"\"" );
+    return pos + len * sizeof(WCHAR);
 }
 
 static void dump_varargs_startup_info( const char *prefix, data_size_t size )
 {
-    const RTL_USER_PROCESS_PARAMETERS *ptr = cur_data;
-    RTL_USER_PROCESS_PARAMETERS params;
-
-    if (size < sizeof(params.Size))
-    {
-        fprintf( stderr, "%s{}", prefix );
-        return;
-    }
-    if (size > ptr->Size) size = ptr->Size;
-    memset( &params, 0, sizeof(params) );
-    memcpy( &params, ptr, min( size, sizeof(params) ));
-
-    fprintf( stderr, "%s{AllocationSize=%x,", prefix, params.AllocationSize );
-    fprintf( stderr, "Size=%x,", params.Size );
-    fprintf( stderr, "Flags=%x,", params.Flags );
-    fprintf( stderr, "DebugFlags=%x,", params.DebugFlags );
-    fprintf( stderr, "ConsoleHandle=%p,", params.ConsoleHandle );
-    fprintf( stderr, "ConsoleFlags=%x,", params.ConsoleFlags );
-    fprintf( stderr, "hStdInput=%p,", params.hStdInput );
-    fprintf( stderr, "hStdOutput=%p,", params.hStdOutput );
-    fprintf( stderr, "hStdError=%p,", params.hStdError );
-    fprintf( stderr, "CurrentDirectory.Handle=%p,", params.CurrentDirectory.Handle );
-    fprintf( stderr, "dwX=%d,", params.dwX );
-    fprintf( stderr, "dwY=%d,", params.dwY );
-    fprintf( stderr, "dwXSize=%d,", params.dwXSize );
-    fprintf( stderr, "dwYSize=%d,", params.dwYSize );
-    fprintf( stderr, "dwXCountChars=%d,", params.dwXCountChars );
-    fprintf( stderr, "dwYCountChars=%d,", params.dwYCountChars );
-    fprintf( stderr, "dwFillAttribute=%x,", params.dwFillAttribute );
-    fprintf( stderr, "dwFlags=%x,", params.dwFlags );
-    fprintf( stderr, "wShowWindow=%x,", params.wShowWindow );
-    fprintf( stderr, "CurrentDirectory.DosPath=L\"" );
-    dump_inline_unicode_string( &params.CurrentDirectory.DosPath, cur_data, size );
-    fprintf( stderr, "\",DllPath=L\"" );
-    dump_inline_unicode_string( &params.DllPath, cur_data, size );
-    fprintf( stderr, "\",ImagePathName=L\"" );
-    dump_inline_unicode_string( &params.ImagePathName, cur_data, size );
-    fprintf( stderr, "\",CommandLine=L\"" );
-    dump_inline_unicode_string( &params.CommandLine, cur_data, size );
-    fprintf( stderr, "\",WindowTitle=L\"" );
-    dump_inline_unicode_string( &params.WindowTitle, cur_data, size );
-    fprintf( stderr, "\",Desktop=L\"" );
-    dump_inline_unicode_string( &params.Desktop, cur_data, size );
-    fprintf( stderr, "\",ShellInfo=L\"" );
-    dump_inline_unicode_string( &params.ShellInfo, cur_data, size );
-    fprintf( stderr, "\",RuntimeInfo=L\"" );
-    dump_inline_unicode_string( &params.RuntimeInfo, cur_data, size );
+    startup_info_t info;
+    data_size_t pos = sizeof(info);
+
+    memset( &info, 0, sizeof(info) );
+    memcpy( &info, cur_data, min( size, sizeof(info) ));
+
+    fprintf( stderr,
+             "%s{debug_flags=%x,console_flags=%x,console=%04x,hstdin=%04x,hstdout=%04x,hstderr=%04x,"
+             "x=%u,y=%u,xsize=%u,ysize=%u,xchars=%u,ychars=%u,attribute=%02x,flags=%x,show=%u",
+             prefix, info.debug_flags, info.console_flags, info.console,
+             info.hstdin, info.hstdout, info.hstderr, info.x, info.y, info.xsize, info.ysize,
+             info.xchars, info.ychars, info.attribute, info.flags, info.show );
+    pos = dump_inline_unicode_string( ",curdir=L\"", pos, info.curdir_len, size );
+    pos = dump_inline_unicode_string( "\",dllpath=L\"", pos, info.dllpath_len, size );
+    pos = dump_inline_unicode_string( "\",imagepath=L\"", pos, info.imagepath_len, size );
+    pos = dump_inline_unicode_string( "\",cmdline=L\"", pos, info.cmdline_len, size );
+    pos = dump_inline_unicode_string( "\",title=L\"", pos, info.title_len, size );
+    pos = dump_inline_unicode_string( "\",desktop=L\"", pos, info.desktop_len, size );
+    pos = dump_inline_unicode_string( "\",shellinfo=L\"", pos, info.shellinfo_len, size );
+    pos = dump_inline_unicode_string( "\",runtime=L\"", pos, info.runtime_len, size );
     fprintf( stderr, "\"}" );
     remove_data( size );
 }
@@ -1025,14 +998,12 @@ static void dump_new_process_request( const struct new_process_request *req )
     fprintf( stderr, ", create_flags=%08x", req->create_flags );
     fprintf( stderr, ", socket_fd=%d", req->socket_fd );
     fprintf( stderr, ", exe_file=%04x", req->exe_file );
-    fprintf( stderr, ", hstdin=%04x", req->hstdin );
-    fprintf( stderr, ", hstdout=%04x", req->hstdout );
-    fprintf( stderr, ", hstderr=%04x", req->hstderr );
     fprintf( stderr, ", process_access=%08x", req->process_access );
     fprintf( stderr, ", process_attr=%08x", req->process_attr );
     fprintf( stderr, ", thread_access=%08x", req->thread_access );
     fprintf( stderr, ", thread_attr=%08x", req->thread_attr );
-    dump_varargs_startup_info( ", info=", cur_size );
+    fprintf( stderr, ", info_size=%u", req->info_size );
+    dump_varargs_startup_info( ", info=", min(cur_size,req->info_size) );
     dump_varargs_unicode_str( ", env=", cur_size );
 }
 
@@ -1077,10 +1048,8 @@ static void dump_get_startup_info_request( const struct get_startup_info_request
 static void dump_get_startup_info_reply( const struct get_startup_info_reply *req )
 {
     fprintf( stderr, " exe_file=%04x", req->exe_file );
-    fprintf( stderr, ", hstdin=%04x", req->hstdin );
-    fprintf( stderr, ", hstdout=%04x", req->hstdout );
-    fprintf( stderr, ", hstderr=%04x", req->hstderr );
-    dump_varargs_startup_info( ", info=", cur_size );
+    fprintf( stderr, ", info_size=%u", req->info_size );
+    dump_varargs_startup_info( ", info=", min(cur_size,req->info_size) );
     dump_varargs_unicode_str( ", env=", cur_size );
 }
 
-- 
GitLab