--- gspawn-win32-orig.c	2008-09-17 22:31:23.000000000 -0700
+++ gspawn-win32-new.c	2008-09-17 22:59:38.000000000 -0700
@@ -2,6 +2,7 @@
  *
  *  Copyright 2000 Red Hat, Inc.
  *  Copyright 2003 Tor Lillqvist
+ *  Copyright 2008 Marcelo Vanzin
  *
  * GLib is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
@@ -22,19 +23,12 @@
 /*
  * Implementation details on Win32.
  *
- * - There is no way to set the no-inherit flag for
- *   a "file descriptor" in the MS C runtime. The flag is there,
- *   and the dospawn() function uses it, but unfortunately
- *   this flag can only be set when opening the file.
  * - As there is no fork(), we cannot reliably change directory
  *   before starting the child process. (There might be several threads
  *   running, and the current directory is common for all threads.)
- *
- * Thus, we must in many cases use a helper program to handle closing
- * of (inherited) file descriptors and changing of directory. The
- * helper process is also needed if the standard input, standard
- * output, or standard error of the process to be run are supposed to
- * be redirected somewhere.
+ * - Some G_SPAWN_* flags don't really map well to how the Win32 API
+ *   handles child processes. See the various comments in
+ *   do_spawn_with_pipes() for details.
  *
  * The structure of the source code in this file is a mess, I know.
  */
@@ -80,47 +74,6 @@
     G_STMT_END
 #endif
 
-enum
-{
-  CHILD_NO_ERROR,
-  CHILD_CHDIR_FAILED,
-  CHILD_SPAWN_FAILED,
-};
-
-enum {
-  ARG_CHILD_ERR_REPORT = 1,
-  ARG_HELPER_SYNC,
-  ARG_STDIN,
-  ARG_STDOUT,
-  ARG_STDERR,
-  ARG_WORKING_DIRECTORY,
-  ARG_CLOSE_DESCRIPTORS,
-  ARG_USE_PATH,
-  ARG_WAIT,
-  ARG_PROGRAM,
-  ARG_COUNT = ARG_PROGRAM
-};
-
-static int
-dup_noninherited (int fd,
-		  int mode)
-{
-  HANDLE filehandle;
-
-  DuplicateHandle (GetCurrentProcess (), (LPHANDLE) _get_osfhandle (fd),
-		   GetCurrentProcess (), &filehandle,
-		   0, FALSE, DUPLICATE_SAME_ACCESS);
-  close (fd);
-  return _open_osfhandle ((gintptr) filehandle, mode | _O_NOINHERIT);
-}
-
-#ifndef GSPAWN_HELPER
-
-#ifdef _WIN64
-#define HELPER_PROCESS "gspawn-win64-helper"
-#else
-#define HELPER_PROCESS "gspawn-win32-helper"
-#endif
 
 static gchar *
 protect_argv_string (const gchar *string)
@@ -288,234 +241,66 @@
     return READ_OK;
 }
 
-static gboolean
-make_pipe (gint     p[2],
-           GError **error)
-{
-  if (_pipe (p, 4096, _O_BINARY) < 0)
-    {
-      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
-                   _("Failed to create pipe for communicating with child process (%s)"),
-                   g_strerror (errno));
-      return FALSE;
-    }
-  else
-    return TRUE;
-}
-
-/* The helper process writes a status report back to us, through a
- * pipe, consisting of two ints.
- */
-static gboolean
-read_helper_report (int      fd,
-		    gintptr  report[2],
-		    GError **error)
-{
-  gint bytes = 0;
-  
-  while (bytes < sizeof(gintptr)*2)
-    {
-      gint chunk;
-
-      if (debug)
-	g_print ("%s:read_helper_report: read %" G_GSIZE_FORMAT "...\n",
-		 __FILE__,
-		 sizeof(gintptr)*2 - bytes);
-
-      chunk = read (fd, ((gchar*)report) + bytes,
-		    sizeof(gintptr)*2 - bytes);
-
-      if (debug)
-	g_print ("...got %d bytes\n", chunk);
-          
-      if (chunk < 0)
-        {
-          /* Some weird shit happened, bail out */
-              
-          g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
-                       _("Failed to read from child pipe (%s)"),
-                       g_strerror (errno));
-
-          return FALSE;
-        }
-      else if (chunk == 0)
-	{
-	  g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
-		       _("Failed to read from child pipe (%s)"),
-		       "EOF");
-	  break; /* EOF */
-	}
-      else
-	bytes += chunk;
-    }
-
-  if (bytes < sizeof(gintptr)*2)
-    return FALSE;
-
-  return TRUE;
-}
 
 static void
-set_child_error (gintptr      report[2],
-		 const gchar *working_directory,
-		 GError     **error)
-{
-  switch (report[0])
-    {
-    case CHILD_CHDIR_FAILED:
-      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR,
-		   _("Failed to change to directory '%s' (%s)"),
-		   working_directory,
-		   g_strerror (report[1]));
-      break;
-    case CHILD_SPAWN_FAILED:
+set_spawn_error (gint     errorcode,
+		 GError **error)
+{
+      gchar *msg = g_win32_error_message (GetLastError ());
       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
-		   _("Failed to execute child process (%s)"),
-		   g_strerror (report[1]));
-      break;
-    default:
-      g_assert_not_reached ();
-    }
+		   _("Failed to execute child process: %s"),
+		   msg);
+      g_free (msg);
 }
 
-static gboolean
-utf8_charv_to_wcharv (char     **utf8_charv,
-		      wchar_t ***wcharv,
-		      int       *error_index,
-		      GError   **error)
-{
-  wchar_t **retval = NULL;
-
-  *wcharv = NULL;
-  if (utf8_charv != NULL)
-    {
-      int n = 0, i;
-
-      while (utf8_charv[n])
-	n++;
-      retval = g_new (wchar_t *, n + 1);
-
-      for (i = 0; i < n; i++)
-	{
-	  retval[i] = g_utf8_to_utf16 (utf8_charv[i], -1, NULL, NULL, error);
-	  if (retval[i] == NULL)
-	    {
-	      if (error_index)
-		*error_index = i;
-	      while (i)
-		g_free (retval[--i]);
-	      g_free (retval);
-	      return FALSE;
-	    }
-	}
-	    
-      retval[n] = NULL;
-    }
-  *wcharv = retval;
-  return TRUE;
-}
 
 static gboolean
-do_spawn_directly (gint                 *exit_status,
-		   gboolean		 do_return_handle,
-		   GSpawnFlags           flags,
-		   gchar               **argv,
-		   char                **envp,
-		   char                **protected_argv,
-		   GSpawnChildSetupFunc  child_setup,
-		   gpointer              user_data,
-		   GPid                 *child_handle,
-		   GError              **error)     
-{
-  const int mode = (exit_status == NULL) ? P_NOWAIT : P_WAIT;
-  char **new_argv;
-  gintptr rc = -1;
-  int saved_errno;
-  GError *conv_error = NULL;
-  gint conv_error_index;
-  wchar_t *wargv0, **wargv, **wenvp;
-
-  new_argv = (flags & G_SPAWN_FILE_AND_ARGV_ZERO) ? protected_argv + 1 : protected_argv;
-      
-  wargv0 = g_utf8_to_utf16 (argv[0], -1, NULL, NULL, &conv_error);
-  if (wargv0 == NULL)
-    {
-      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
-		   _("Invalid program name: %s"),
-		   conv_error->message);
-      g_error_free (conv_error);
-      
-      return FALSE;
+create_child_fd (HANDLE   *readpipe,
+		 HANDLE   *writepipe,
+		 gint     *readfd,
+		 gint     *writefd,
+		 gboolean  isnull)
+{
+  HANDLE read = NULL;
+  HANDLE write = NULL;
+  SECURITY_ATTRIBUTES secattrs;
+
+  secattrs.nLength = sizeof secattrs;
+  secattrs.lpSecurityDescriptor = NULL;
+  secattrs.bInheritHandle = TRUE;
+
+  if (isnull)
+    {
+      /* Only redirect the write pipe to the NULL device. */
+      g_assert (writepipe != NULL);
+
+      write = CreateFileW (L"\\\\.\\NUL", GENERIC_WRITE, 0, &secattrs,
+			  OPEN_EXISTING, 0, NULL);
+      if (write == INVALID_HANDLE_VALUE)
+	return FALSE;
     }
-  
-  if (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error))
-    {
-      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
-		   _("Invalid string in argument vector at %d: %s"),
-		   conv_error_index, conv_error->message);
-      g_error_free (conv_error);
-      g_free (wargv0);
-
-      return FALSE;
-    }
-
-  if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error))
+  else
     {
-      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
-		   _("Invalid string in environment: %s"),
-		   conv_error->message);
-      g_error_free (conv_error);
-      g_free (wargv0);
-      g_strfreev ((gchar **) wargv);
-
-      return FALSE;
+      g_assert (writepipe != NULL);
+      g_assert (readpipe != NULL);
+      if (!CreatePipe (&read, &write, &secattrs, 0))
+      	return FALSE;
     }
 
-  if (child_setup)
-    (* child_setup) (user_data);
+  if (read)
+    *readpipe = read;
+  *writepipe = write;
 
-  if (flags & G_SPAWN_SEARCH_PATH)
-    if (wenvp != NULL)
-      rc = _wspawnvpe (mode, wargv0, (const wchar_t **) wargv, (const wchar_t **) wenvp);
-    else
-      rc = _wspawnvp (mode, wargv0, (const wchar_t **) wargv);
-  else
-    if (wenvp != NULL)
-      rc = _wspawnve (mode, wargv0, (const wchar_t **) wargv, (const wchar_t **) wenvp);
-    else
-      rc = _wspawnv (mode, wargv0, (const wchar_t **) wargv);
-
-  g_free (wargv0);
-  g_strfreev ((gchar **) wargv);
-  g_strfreev ((gchar **) wenvp);
+  if (read && readfd)
+    *readfd =  _open_osfhandle ((intptr_t) read, 0);
 
-  saved_errno = errno;
-
-  if (rc == -1 && saved_errno != 0)
-    {
-      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
-		   _("Failed to execute child process (%s)"),
-		   g_strerror (saved_errno));
-      return FALSE;
-    }
-
-  if (exit_status == NULL)
-    {
-      if (child_handle && do_return_handle)
-	*child_handle = (GPid) rc;
-      else
-	{
-	  CloseHandle ((HANDLE) rc);
-	  if (child_handle)
-	    *child_handle = 0;
-	}
-    }
-  else
-    *exit_status = rc;
+  if (writefd)
+    *writefd =  _open_osfhandle ((intptr_t) write, 0);
 
   return TRUE;
 }
 
+
 static gboolean
 do_spawn_with_pipes (gint                 *exit_status,
 		     gboolean		   do_return_handle,
@@ -529,362 +314,304 @@
 		     gint                 *standard_input,
 		     gint                 *standard_output,
 		     gint                 *standard_error,
-		     gint		  *err_report,
-		     GError              **error)     
+		     GError              **error)
 {
-  char **protected_argv;
-  char args[ARG_COUNT][10];
-  char **new_argv;
-  int i;
-  gintptr rc = -1;
-  int saved_errno;
-  int argc;
-  int stdin_pipe[2] = { -1, -1 };
-  int stdout_pipe[2] = { -1, -1 };
-  int stderr_pipe[2] = { -1, -1 };
-  int child_err_report_pipe[2] = { -1, -1 };
-  int helper_sync_pipe[2] = { -1, -1 };
-  gintptr helper_report[2];
-  static gboolean warned_about_child_setup = FALSE;
-  GError *conv_error = NULL;
-  gint conv_error_index;
-  gchar *helper_process;
-  CONSOLE_CURSOR_INFO cursor_info;
-  wchar_t *whelper, **wargv, **wenvp;
-  extern gchar *_glib_get_installation_directory (void);
-  gchar *glib_top;
-
-  if (child_setup && !warned_about_child_setup)
+  gboolean ret = FALSE;
+  gint argc;
+  gint i;
+  DWORD childflags = 0;
+  GError *err = NULL;
+  STARTUPINFOW childparams;
+  PROCESS_INFORMATION childinfo;
+
+  gchar *childenv = NULL;
+  gchar **protected_argv = NULL;
+  gchar *cmdline = NULL;
+  wchar_t *cmd_utf16 = NULL;
+  wchar_t *cmdline_utf16 = NULL;
+  wchar_t *work_dir_utf16 = NULL;
+
+  HANDLE childstdin = NULL;
+  HANDLE childstdout = NULL;
+  HANDLE childstderr = NULL;
+
+  gint childstdinfd = -1;
+  gint childstdoutfd = -1;
+  gint childstderrfd = -1;
+
+  memset (&childparams, 0, sizeof childparams);
+  memset (&childinfo, 0, sizeof childinfo);
+
+  childparams.cb = sizeof childparams;
+
+  /*
+   * Build the command line based on the given argv. If the user
+   * requested, quote all the given arguments.
+   */
+  argc = protect_argv (argv, &protected_argv);
+  i = (flags & G_SPAWN_FILE_AND_ARGV_ZERO) ? 1 : 0;
+  for (; i < argc; i++)
     {
-      warned_about_child_setup = TRUE;
-      g_warning ("passing a child setup function to the g_spawn functions is pointless and dangerous on Win32");
+      gchar *tmp = cmdline;
+	if (cmdline == NULL)
+	    cmdline = g_strdup_printf ("%s", protected_argv[i]);
+	else
+	    cmdline = g_strdup_printf ("%s %s", cmdline, protected_argv[i]);
+      g_free (tmp);
     }
 
-  argc = protect_argv (argv, &protected_argv);
+  /*
+   * Convert the command to UTF16.
+   */
 
-  if (!standard_input && !standard_output && !standard_error &&
-      (flags & G_SPAWN_CHILD_INHERITS_STDIN) &&
-      !(flags & G_SPAWN_STDOUT_TO_DEV_NULL) &&
-      !(flags & G_SPAWN_STDERR_TO_DEV_NULL) &&
-      (working_directory == NULL || !*working_directory) &&
-      (flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN))
-    {
-      /* We can do without the helper process */
-      gboolean retval =
-	do_spawn_directly (exit_status, do_return_handle, flags,
-			   argv, envp, protected_argv,
-			   child_setup, user_data, child_handle,
-			   error);
-      g_strfreev (protected_argv);
-      return retval;
+  cmd_utf16 = g_utf8_to_utf16 (argv[0], -1, NULL, NULL, &err);
+  if (err != NULL)
+    {
+      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+		   _("Invalid command line: %s"),
+		   err->message);
+      g_clear_error (&err);
+
+      goto exit;
     }
 
-  if (standard_input && !make_pipe (stdin_pipe, error))
-    goto cleanup_and_fail;
-  
-  if (standard_output && !make_pipe (stdout_pipe, error))
-    goto cleanup_and_fail;
-  
-  if (standard_error && !make_pipe (stderr_pipe, error))
-    goto cleanup_and_fail;
-  
-  if (!make_pipe (child_err_report_pipe, error))
-    goto cleanup_and_fail;
-  
-  if (!make_pipe (helper_sync_pipe, error))
-    goto cleanup_and_fail;
-  
-  new_argv = g_new (char *, argc + 1 + ARG_COUNT);
-  if (GetConsoleCursorInfo (GetStdHandle (STD_OUTPUT_HANDLE), &cursor_info))
-    helper_process = HELPER_PROCESS "-console.exe";
-  else
-    helper_process = HELPER_PROCESS ".exe";
-  
-  glib_top = _glib_get_installation_directory ();
-  if (glib_top != NULL)
+  cmdline_utf16 = g_utf8_to_utf16 (cmdline, -1, NULL, NULL, &err);
+  if (err != NULL)
     {
-      helper_process = g_build_filename (glib_top, "bin", helper_process, NULL);
-      g_free (glib_top);
-    }
-  else
-    helper_process = g_strdup (helper_process);
+      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+		   _("Invalid command line: %s"),
+		   err->message);
+      g_clear_error (&err);
 
-  new_argv[0] = protect_argv_string (helper_process);
+      goto exit;
+    }
 
-  _g_sprintf (args[ARG_CHILD_ERR_REPORT], "%d", child_err_report_pipe[1]);
-  new_argv[ARG_CHILD_ERR_REPORT] = args[ARG_CHILD_ERR_REPORT];
-  
-  /* Make the read end of the child error report pipe
-   * noninherited. Otherwise it will needlessly be inherited by the
-   * helper process, and the started actual user process. As such that
-   * shouldn't harm, but it is unnecessary.
+  /*
+   * Setup the child's environment. See the CreateProcess() documentation
+   * for the format expected by the function:
+   *
+   *	http://msdn.microsoft.com/en-us/library/ms682425.aspx
+   *
+   * Note about unicode: the CreateProcess() documentation mentions that
+   * it's possible to pass a "Unicode environment" to the child process
+   * by providing the CREATE_UNICODE_ENVIRONMENT flag. But there's a
+   * side effect to that: if the child process does not expect a Unicode
+   * environment, it will not understand the environment at all, thinking
+   * that the nulls in the Unicode characters are actually string terminators.
+   * So it's probably safer to just use the given environment without
+   * converting to Unicode. This code will at least translate the data
+   * to the current user's locale.
    */
-  child_err_report_pipe[0] = dup_noninherited (child_err_report_pipe[0], _O_RDONLY);
 
-  if (flags & G_SPAWN_FILE_AND_ARGV_ZERO)
+  if (envp != NULL)
     {
-      /* Overload ARG_CHILD_ERR_REPORT to also encode the
-       * G_SPAWN_FILE_AND_ARGV_ZERO functionality.
-       */
-      strcat (args[ARG_CHILD_ERR_REPORT], "#");
-    }
-  
-  _g_sprintf (args[ARG_HELPER_SYNC], "%d", helper_sync_pipe[0]);
-  new_argv[ARG_HELPER_SYNC] = args[ARG_HELPER_SYNC];
-  
-  /* Make the write end of the sync pipe noninherited. Otherwise the
-   * helper process will inherit it, and thus if this process happens
-   * to crash before writing the sync byte to the pipe, the helper
-   * process won't read but won't get any EOF either, as it has the
-   * write end open itself.
+      size_t childenvlen = 0;
+
+      for (i = 0; envp[i] != NULL; i++) {
+	size_t envlen;
+	gchar *env;
+
+	env = g_locale_from_utf8 (envp[i], -1, NULL, NULL, &err);
+	if (err != NULL)
+	  {
+	    g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+			 _("Invalid environment: %s"),
+			 err->message);
+	    g_clear_error (&err);
+	    goto exit;
+	  }
+
+	 envlen = strlen (env) + 1;
+	 childenv = g_realloc (childenv, childenvlen + envlen);
+	 memcpy (childenv + childenvlen, env, envlen);
+	 childenvlen += envlen;
+	 g_free (env);
+      }
+
+      /* Allocate the final NULL terminator. */
+      childenv = g_realloc (childenv, childenvlen + 1);
+      childenv[childenvlen] = '\0';
+    }
+
+  /*
+   * Call the child setup function if defined... although on Win32
+   * it doesn't really run in the child.
    */
-  helper_sync_pipe[1] = dup_noninherited (helper_sync_pipe[1], _O_WRONLY);
 
-  if (standard_input)
-    {
-      _g_sprintf (args[ARG_STDIN], "%d", stdin_pipe[0]);
-      new_argv[ARG_STDIN] = args[ARG_STDIN];
-    }
-  else if (flags & G_SPAWN_CHILD_INHERITS_STDIN)
-    {
-      /* Let stdin be alone */
-      new_argv[ARG_STDIN] = "-";
-    }
-  else
-    {
-      /* Keep process from blocking on a read of stdin */
-      new_argv[ARG_STDIN] = "z";
-    }
-  
-  if (standard_output)
-    {
-      _g_sprintf (args[ARG_STDOUT], "%d", stdout_pipe[1]);
-      new_argv[ARG_STDOUT] = args[ARG_STDOUT];
-    }
-  else if (flags & G_SPAWN_STDOUT_TO_DEV_NULL)
-    {
-      new_argv[ARG_STDOUT] = "z";
-    }
-  else
+  if (child_setup)
+    (* child_setup) (user_data);
+
+  /*
+   * Setup the child I/O streams according to the provided arguments.
+   */
+
+  if (flags & G_SPAWN_CHILD_INHERITS_STDIN)
     {
-      new_argv[ARG_STDOUT] = "-";
+      if (!DuplicateHandle (GetCurrentProcess (),
+			    GetStdHandle (STD_INPUT_HANDLE),
+			    GetCurrentProcess (),
+			    &childstdin,
+			    0,
+			    TRUE,
+			    DUPLICATE_SAME_ACCESS))
+        {
+	  set_spawn_error (GetLastError (), error);
+	  goto exit;
+	}
+	childstdinfd = _open_osfhandle ((intptr_t) childstdin, 0);
     }
-  
-  if (standard_error)
+  else if (standard_input != NULL)
     {
-      _g_sprintf (args[ARG_STDERR], "%d", stderr_pipe[1]);
-      new_argv[ARG_STDERR] = args[ARG_STDERR];
+      if (!create_child_fd (&childparams.hStdInput,
+			    &childstdin,
+			    NULL,
+			    &childstdinfd,
+			    FALSE))
+        {
+	  set_spawn_error (GetLastError (), error);
+	  goto exit;
+	}
     }
-  else if (flags & G_SPAWN_STDERR_TO_DEV_NULL)
+
+  if (standard_output != NULL || (flags & G_SPAWN_STDOUT_TO_DEV_NULL))
     {
-      new_argv[ARG_STDERR] = "z";
+      if (!create_child_fd (&childstdout,
+			    &childparams.hStdOutput,
+			    &childstdoutfd,
+			    NULL,
+			    (flags & G_SPAWN_STDOUT_TO_DEV_NULL)))
+        {
+	  set_spawn_error (GetLastError (), error);
+	  goto exit;
+	}
     }
-  else
+
+  if (standard_error != NULL || (flags & G_SPAWN_STDERR_TO_DEV_NULL))
     {
-      new_argv[ARG_STDERR] = "-";
+      if (!create_child_fd (&childstderr,
+			    &childparams.hStdError,
+			    &childstderrfd,
+			    NULL,
+			    (flags & G_SPAWN_STDERR_TO_DEV_NULL)))
+        {
+	  set_spawn_error (GetLastError (), error);
+	  goto exit;
+	}
     }
-  
-  if (working_directory && *working_directory)
-    new_argv[ARG_WORKING_DIRECTORY] = protect_argv_string (working_directory);
-  else
-    new_argv[ARG_WORKING_DIRECTORY] = g_strdup ("-");
-  
-  if (!(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN))
-    new_argv[ARG_CLOSE_DESCRIPTORS] = "y";
-  else
-    new_argv[ARG_CLOSE_DESCRIPTORS] = "-";
-
-  if (flags & G_SPAWN_SEARCH_PATH)
-    new_argv[ARG_USE_PATH] = "y";
-  else
-    new_argv[ARG_USE_PATH] = "-";
 
-  if (exit_status == NULL)
-    new_argv[ARG_WAIT] = "-";
-  else
-    new_argv[ARG_WAIT] = "w";
+  if (childstderr || childstdin || childstdout)
+      childparams.dwFlags = STARTF_USESTDHANDLES;
 
-  for (i = 0; i <= argc; i++)
-    new_argv[ARG_PROGRAM + i] = protected_argv[i];
+  /*
+   * Sets the parent ends of the pipes to "non-inherit, and call
+   * CreateProcess() with the information we've built.
+   */
 
-  SETUP_DEBUG();
+  if (childstdin)
+    SetHandleInformation (childstdin, HANDLE_FLAG_INHERIT, 0);
+  if (childstdout)
+    SetHandleInformation (childstdout, HANDLE_FLAG_INHERIT, 0);
+  if (childstderr)
+    SetHandleInformation (childstderr, HANDLE_FLAG_INHERIT, 0);
+
+  if (flags & G_SPAWN_WIN32_NO_CMD_WINDOW)
+    childflags |= CREATE_NO_WINDOW;
+  else if (!(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN))
+    childflags |= DETACHED_PROCESS;
+
+  if (!CreateProcessW (cmd_utf16,
+		       cmdline_utf16,
+		       NULL,
+		       NULL,
+		       childstderr || childstdin || childstdout ||
+		       (flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN),
+		       childflags,
+		       childenv,
+		       work_dir_utf16,
+		       &childparams,
+		       &childinfo))
+    {
+      set_spawn_error (GetLastError (), error);
+      goto exit;
+    }
+
+  /*
+   * If requested, return the child handle, otherwise wait for the
+   * process to exit and collect the exit status, if requested.
+   */
 
-  if (debug)
+  CloseHandle (childinfo.hThread);
+  if (!do_return_handle)
     {
-      g_print ("calling %s with argv:\n", helper_process);
-      for (i = 0; i < argc + 1 + ARG_COUNT; i++)
-	g_print ("argv[%d]: %s\n", i, (new_argv[i] ? new_argv[i] : "NULL"));
+      WaitForSingleObject (childinfo.hProcess, INFINITE);
+      if (exit_status)
+        {
+	  DWORD exitcode;
+	  if (!GetExitCodeProcess (childinfo.hProcess, &exitcode))
+	    exitcode = 1;
+	  *exit_status = (gint) exitcode;
+	}
+      CloseHandle (childinfo.hProcess);
     }
+  else if (child_handle)
+    *child_handle = childinfo.hProcess;
+  else
+    CloseHandle (childinfo.hProcess);
 
-  if (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error))
-    {
-      if (conv_error_index == ARG_WORKING_DIRECTORY)
-	g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_CHDIR,
-		     _("Invalid working directory: %s"),
-		     conv_error->message);
-      else
-	g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
-		     _("Invalid string in argument vector at %d: %s"),
-		     conv_error_index - ARG_PROGRAM, conv_error->message);
-      g_error_free (conv_error);
-      g_strfreev (protected_argv);
-      g_free (new_argv[0]);
-      g_free (new_argv[ARG_WORKING_DIRECTORY]);
-      g_free (new_argv);
-      g_free (helper_process);
+  /*
+   * Pass I/O information back to the caller.
+   */
 
-      goto cleanup_and_fail;
+  if (standard_input)
+    {
+      *standard_input = childstdinfd;
+      childstdinfd = -1;
     }
 
-  if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error))
+  if (standard_output)
     {
-      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
-		   _("Invalid string in environment: %s"),
-		   conv_error->message);
-      g_error_free (conv_error);
-      g_strfreev (protected_argv);
-      g_free (new_argv[0]);
-      g_free (new_argv[ARG_WORKING_DIRECTORY]);
-      g_free (new_argv);
-      g_free (helper_process);
-      g_strfreev ((gchar **) wargv);
- 
-      goto cleanup_and_fail;
+      *standard_output = childstdoutfd;
+      childstdoutfd = -1;
     }
 
-  if (child_setup)
-    (* child_setup) (user_data);
-
-  whelper = g_utf8_to_utf16 (helper_process, -1, NULL, NULL, NULL);
-  g_free (helper_process);
-
-  if (wenvp != NULL)
-    rc = _wspawnvpe (P_NOWAIT, whelper, (const wchar_t **) wargv, (const wchar_t **) wenvp);
-  else
-    rc = _wspawnvp (P_NOWAIT, whelper, (const wchar_t **) wargv);
-
-  saved_errno = errno;
-
-  g_free (whelper);
-  g_strfreev ((gchar **) wargv);
-  g_strfreev ((gchar **) wenvp);
+  if (standard_error)
+    {
+      *standard_error = childstderrfd;
+      childstderrfd = -1;
+    }
 
-  /* Close the other process's ends of the pipes in this process,
-   * otherwise the reader will never get EOF.
-   */
-  close_and_invalidate (&child_err_report_pipe[1]);
-  close_and_invalidate (&helper_sync_pipe[0]);
-  close_and_invalidate (&stdin_pipe[0]);
-  close_and_invalidate (&stdout_pipe[1]);
-  close_and_invalidate (&stderr_pipe[1]);
+  ret = TRUE;
 
+exit:
+  g_free (childenv);
+  g_free (cmdline);
+  g_free (cmd_utf16);
+  g_free (cmdline_utf16);
+  g_free (work_dir_utf16);
   g_strfreev (protected_argv);
 
-  g_free (new_argv[0]);
-  g_free (new_argv[ARG_WORKING_DIRECTORY]);
-  g_free (new_argv);
+  if (childparams.hStdInput)
+    CloseHandle (childparams.hStdInput);
 
-  /* Check if gspawn-win32-helper couldn't be run */
-  if (rc == -1 && saved_errno != 0)
-    {
-      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
-		   _("Failed to execute helper program (%s)"),
-		   g_strerror (saved_errno));
-      goto cleanup_and_fail;
-    }
+  if (childparams.hStdOutput)
+    CloseHandle (childparams.hStdOutput);
 
-  if (exit_status != NULL)
-    {
-      /* Synchronous case. Pass helper's report pipe back to caller,
-       * which takes care of reading it after the grandchild has
-       * finished.
-       */
-      g_assert (err_report != NULL);
-      *err_report = child_err_report_pipe[0];
-      write (helper_sync_pipe[1], " ", 1);
-      close_and_invalidate (&helper_sync_pipe[1]);
-    }
-  else
-    {
-      /* Asynchronous case. We read the helper's report right away. */
-      if (!read_helper_report (child_err_report_pipe[0], helper_report, error))
-	goto cleanup_and_fail;
-        
-      close_and_invalidate (&child_err_report_pipe[0]);
+  if (childparams.hStdError)
+    CloseHandle (childparams.hStdError);
 
-      switch (helper_report[0])
-	{
-	case CHILD_NO_ERROR:
-	  if (child_handle && do_return_handle)
-	    {
-	      /* rc is our HANDLE for gspawn-win32-helper. It has
-	       * told us the HANDLE of its child. Duplicate that into
-	       * a HANDLE valid in this process.
-	       */
-	      if (!DuplicateHandle ((HANDLE) rc, (HANDLE) helper_report[1],
-				    GetCurrentProcess (), (LPHANDLE) child_handle,
-				    0, TRUE, DUPLICATE_SAME_ACCESS))
-		{
-		  char *emsg = g_win32_error_message (GetLastError ());
-		  g_print("%s\n", emsg);
-		  *child_handle = 0;
-		}
-	    }
-	  else if (child_handle)
-	    *child_handle = 0;
-	  write (helper_sync_pipe[1], " ", 1);
-	  close_and_invalidate (&helper_sync_pipe[1]);
-	  break;
-	  
-	default:
-	  write (helper_sync_pipe[1], " ", 1);
-	  close_and_invalidate (&helper_sync_pipe[1]);
-	  set_child_error (helper_report, working_directory, error);
-	  goto cleanup_and_fail;
-	}
-    }
-
-  /* Success against all odds! return the information */
-      
-  if (standard_input)
-    *standard_input = stdin_pipe[1];
-  if (standard_output)
-    *standard_output = stdout_pipe[0];
-  if (standard_error)
-    *standard_error = stderr_pipe[0];
-  if (rc != -1)
-    CloseHandle ((HANDLE) rc);
-  
-  return TRUE;
+  if (childstdinfd != -1)
+    _close (childstdinfd);
 
- cleanup_and_fail:
+  if (childstdoutfd != -1)
+    _close (childstdoutfd);
 
-  if (rc != -1)
-    CloseHandle ((HANDLE) rc);
-  if (child_err_report_pipe[0] != -1)
-    close (child_err_report_pipe[0]);
-  if (child_err_report_pipe[1] != -1)
-    close (child_err_report_pipe[1]);
-  if (helper_sync_pipe[0] != -1)
-    close (helper_sync_pipe[0]);
-  if (helper_sync_pipe[1] != -1)
-    close (helper_sync_pipe[1]);
-  if (stdin_pipe[0] != -1)
-    close (stdin_pipe[0]);
-  if (stdin_pipe[1] != -1)
-    close (stdin_pipe[1]);
-  if (stdout_pipe[0] != -1)
-    close (stdout_pipe[0]);
-  if (stdout_pipe[1] != -1)
-    close (stdout_pipe[1]);
-  if (stderr_pipe[0] != -1)
-    close (stderr_pipe[0]);
-  if (stderr_pipe[1] != -1)
-    close (stderr_pipe[1]);
+  if (childstderrfd != -1)
+    _close (childstderrfd);
 
-  return FALSE;
+  return ret;
 }
 
+
 gboolean
 g_spawn_sync_utf8 (const gchar          *working_directory,
 		   gchar               **argv,
@@ -899,7 +626,6 @@
 {
   gint outpipe = -1;
   gint errpipe = -1;
-  gint reportpipe = -1;
   GIOChannel *outchannel = NULL;
   GIOChannel *errchannel = NULL;
   GPollFD outfd, errfd;
@@ -911,6 +637,7 @@
   GString *outstr = NULL;
   GString *errstr = NULL;
   gboolean failed;
+  GPid child = NULL;
   gint status;
   
   g_return_val_if_fail (argv != NULL, FALSE);
@@ -930,18 +657,17 @@
     *standard_error = NULL;
   
   if (!do_spawn_with_pipes (&status,
-			    FALSE,
+			    (standard_output || standard_error),
 			    working_directory,
 			    argv,
 			    envp,
 			    flags,
 			    child_setup,
 			    user_data,
-			    NULL,
+			    &child,
 			    NULL,
 			    standard_output ? &outpipe : NULL,
 			    standard_error ? &errpipe : NULL,
-			    &reportpipe,
 			    error))
     return FALSE;
 
@@ -1061,40 +787,17 @@
         }
     }
 
-  if (reportpipe == -1)
+  /* If we got the child handle back, reap the process. */
+  if (child)
     {
-      /* No helper process, exit status of actual spawned process
-       * already available.
-       */
-      if (exit_status)
-        *exit_status = status;
-    }
-  else
-    {
-      /* Helper process was involved. Read its report now after the
-       * grandchild has finished.
-       */
-      gintptr helper_report[2];
-
-      if (!read_helper_report (reportpipe, helper_report, error))
-	failed = TRUE;
-      else
-	{
-	  switch (helper_report[0])
-	    {
-	    case CHILD_NO_ERROR:
-	      if (exit_status)
-		*exit_status = helper_report[1];
-	      break;
-	    default:
-	      set_child_error (helper_report, working_directory, error);
-	      failed = TRUE;
-	      break;
-	    }
-	}
-      close_and_invalidate (&reportpipe);
+      WaitForSingleObject (child, INFINITE);
+      if (!GetExitCodeProcess (child, (LPDWORD) &status))
+	status = 1;
+      CloseHandle (child);
     }
 
+  if (exit_status)
+    *exit_status = status;
 
   /* These should only be open still if we had an error.  */
   
@@ -1162,7 +865,6 @@
 			      standard_input,
 			      standard_output,
 			      standard_error,
-			      NULL,
 			      error);
 }
 
@@ -1500,7 +1202,5 @@
 
 #endif	/* !_WIN64 */
 
-#endif /* !GSPAWN_HELPER */
-
 #define __G_SPAWN_C__
 #include "galiasdef.c"
