GVariant: use GVariantTypeInfo on internal constructors
[platform/upstream/glib.git] / glib / gspawn-win32.c
index ff42dea..d98aba1 100644 (file)
@@ -30,7 +30,7 @@
  *   before starting the child process. (There might be several threads
  *   running, and the current directory is common for all threads.)
  *
- * Thus, we must in most cases use a helper program to handle closing
+ * 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
 /* Define this to get some logging all the time */
 /* #define G_SPAWN_WIN32_DEBUG */
 
-#include <config.h>
+#include "config.h"
 
 #include "glib.h"
+#include "glib-private.h"
 #include "gprintfint.h"
-#include "galias.h"
+#include "glibintl.h"
+#include "gthread.h"
 
 #include <string.h>
 #include <stdlib.h>
 #include <io.h>
 #include <process.h>
 #include <direct.h>
+#include <wchar.h>
 
-#ifdef __MINGW32__
-/* Mingw doesn't have prototypes for these */
-int _wspawnvpe (int, const wchar_t *, const wchar_t **, const wchar_t **);
-int _wspawnvp (int, const wchar_t *, const wchar_t **);
-int _wspawnve (int, const wchar_t *, const wchar_t **, const wchar_t **);
-int _wspawnv (int, const wchar_t *, const wchar_t **);
-#endif
-
-#include "glibintl.h"
-
+#ifndef GSPAWN_HELPER
 #ifdef G_SPAWN_WIN32_DEBUG
   static int debug = 1;
   #define SETUP_DEBUG() /* empty */
@@ -87,6 +81,7 @@ int _wspawnv (int, const wchar_t *, const wchar_t **);
       }                                                        \
     G_STMT_END
 #endif
+#endif
 
 enum
 {
@@ -97,6 +92,7 @@ enum
 
 enum {
   ARG_CHILD_ERR_REPORT = 1,
+  ARG_HELPER_SYNC,
   ARG_STDIN,
   ARG_STDOUT,
   ARG_STDERR,
@@ -108,6 +104,27 @@ enum {
   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)
 {
@@ -192,18 +209,8 @@ protect_argv (gchar  **argv,
   return argc;
 }
 
-#ifndef GSPAWN_HELPER
-
-#define HELPER_PROCESS "gspawn-win32-helper"
-
-GQuark
-g_spawn_error_quark (void)
-{
-  static GQuark quark = 0;
-  if (quark == 0)
-    quark = g_quark_from_static_string ("g-exec-error-quark");
-  return quark;
-}
+G_DEFINE_QUARK (g-exec-error-quark, g_spawn_error)
+G_DEFINE_QUARK (g-spawn-exit-error-quark, g_spawn_exit_error)
 
 gboolean
 g_spawn_async_utf8 (const gchar          *working_directory,
@@ -254,7 +261,7 @@ read_data (GString     *str,
            GError     **error)
 {
   GIOStatus giostatus;
-  gssize bytes;
+  gsize bytes;
   gchar buf[4096];
 
  again:
@@ -272,8 +279,8 @@ read_data (GString     *str,
     goto again;
   else if (giostatus == G_IO_STATUS_ERROR)
     {
-      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_READ,
-                   _("Failed to read data from child process"));
+      g_set_error_literal (error, G_SPAWN_ERROR, G_SPAWN_ERROR_READ,
+                           _("Failed to read data from child process"));
       
       return READ_FAILED;
     }
@@ -285,11 +292,13 @@ static gboolean
 make_pipe (gint     p[2],
            GError **error)
 {
-  if (pipe (p) < 0)
+  if (_pipe (p, 4096, _O_BINARY) < 0)
     {
+      int errsv = errno;
+
       g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
                    _("Failed to create pipe for communicating with child process (%s)"),
-                   g_strerror (errno));
+                   g_strerror (errsv));
       return FALSE;
     }
   else
@@ -301,50 +310,56 @@ make_pipe (gint     p[2],
  */
 static gboolean
 read_helper_report (int      fd,
-                   gint     report[2],
+                   gintptr  report[2],
                    GError **error)
 {
   gint bytes = 0;
   
-  while (bytes < sizeof(gint)*2)
+  while (bytes < sizeof(gintptr)*2)
     {
       gint chunk;
 
       if (debug)
-       g_print ("%s:read_helper_report: read %d...\n",
+       g_print ("%s:read_helper_report: read %" G_GSIZE_FORMAT "...\n",
                 __FILE__,
-                sizeof(gint)*2 - bytes);
+                sizeof(gintptr)*2 - bytes);
 
       chunk = read (fd, ((gchar*)report) + bytes,
-                   sizeof(gint)*2 - bytes);
+                   sizeof(gintptr)*2 - bytes);
 
       if (debug)
        g_print ("...got %d bytes\n", chunk);
           
       if (chunk < 0)
         {
+          int errsv = errno;
+
           /* 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));
+                       g_strerror (errsv));
 
           return FALSE;
         }
       else if (chunk == 0)
-        break; /* EOF */
+       {
+         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(gint)*2)
+  if (bytes < sizeof(gintptr)*2)
     return FALSE;
 
   return TRUE;
 }
 
 static void
-set_child_error (gint         report[2],
+set_child_error (gintptr      report[2],
                 const gchar *working_directory,
                 GError     **error)
 {
@@ -404,175 +419,73 @@ utf8_charv_to_wcharv (char     **utf8_charv,
 }
 
 static gboolean
-utf8_charv_to_cp_charv (char   **utf8_charv,
-                       gchar ***cp_charv,
-                       int     *error_index,
-                       GError **error)
-{
-  char **retval = NULL;
-
-  *cp_charv = NULL;
-  if (utf8_charv != NULL)
-    {
-      int n = 0, i;
-
-      while (utf8_charv[n])
-       n++;
-      retval = g_new (char *, n + 1);
-
-      for (i = 0; i < n; i++)
-       {
-         retval[i] = g_locale_from_utf8 (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;
-    }
-
-  *cp_charv = retval;
-  return TRUE;
-}
-
-static gboolean
-do_spawn_directly (gboolean              dont_wait,
-                  gboolean              dont_return_handle,
+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,
-                  gint                 *exit_status,
                   GError              **error)     
 {
-  int mode = dont_wait ? P_NOWAIT : P_WAIT;
+  const int mode = (exit_status == NULL) ? P_NOWAIT : P_WAIT;
   char **new_argv;
-  int rc = -1;
+  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;
-  if (G_WIN32_HAVE_WIDECHAR_API ())
-    {
-      wchar_t *wargv0, **wargv, **wenvp;
-      
-      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;
-       }
       
-      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))
-       {
-         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;
-       }
-      
-      if (child_setup)
-       (* child_setup) (user_data);
-      
-      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);
+  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);
       
-      g_free (wargv0);
-      g_strfreev ((gchar **) wargv);
-      g_strfreev ((gchar **) wenvp);
+      return FALSE;
     }
-  else
+  
+  if (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error))
     {
-      char *cpargv0, **cpargv, **cpenvp;
-      
-      cpargv0 = g_locale_from_utf8 (argv[0], -1, NULL, NULL, &conv_error);
-      if (cpargv0 == 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;
-       }
-
-      if  (!utf8_charv_to_cp_charv (new_argv, &cpargv, &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 (cpargv0);
+      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;
-       }
+      return FALSE;
+    }
 
-      if (!utf8_charv_to_cp_charv (envp, &cpenvp, NULL, &conv_error))
-       {
-         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 (cpargv0);
-         g_strfreev (cpargv);
+  if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error))
+    {
+      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;
-       }
+      return FALSE;
+    }
 
-      if (child_setup)
-       (* child_setup) (user_data);
+  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);
 
-      if (flags & G_SPAWN_SEARCH_PATH)
-       if (cpenvp != NULL)
-         rc = spawnvpe (mode, cpargv0, (const char **) cpargv, (const char **) cpenvp);
-       else
-         rc = spawnvp (mode, cpargv0, (const char **) cpargv);
-      else
-       if (envp != NULL)
-         rc = spawnve (mode, cpargv0, (const char **) cpargv, (const char **) cpenvp);
-       else
-         rc = spawnv (mode, cpargv0, (const char **) cpargv);
-
-      g_free (cpargv0);
-      g_strfreev (cpargv);
-      g_strfreev (cpenvp);
-    }
+  g_free (wargv0);
+  g_strfreev ((gchar **) wargv);
+  g_strfreev ((gchar **) wenvp);
 
   saved_errno = errno;
 
@@ -584,9 +497,9 @@ do_spawn_directly (gboolean              dont_wait,
       return FALSE;
     }
 
-  if (dont_wait)
+  if (exit_status == NULL)
     {
-      if (child_handle && !dont_return_handle)
+      if (child_handle && do_return_handle)
        *child_handle = (GPid) rc;
       else
        {
@@ -595,26 +508,24 @@ do_spawn_directly (gboolean              dont_wait,
            *child_handle = 0;
        }
     }
-  else if (exit_status)
+  else
     *exit_status = rc;
 
   return TRUE;
 }
 
 static gboolean
-do_spawn_with_pipes (gboolean              dont_wait,
-                    gboolean              dont_return_handle,
+do_spawn_with_pipes (gint                 *exit_status,
+                    gboolean              do_return_handle,
                     const gchar          *working_directory,
                     gchar               **argv,
                     char                **envp,
                     GSpawnFlags           flags,
                     GSpawnChildSetupFunc  child_setup,
-                    gpointer              user_data,
                     GPid                 *child_handle,
                     gint                 *standard_input,
                     gint                 *standard_output,
                     gint                 *standard_error,
-                    gint                 *exit_status,
                     gint                 *err_report,
                     GError              **error)     
 {
@@ -622,24 +533,27 @@ do_spawn_with_pipes (gboolean              dont_wait,
   char args[ARG_COUNT][10];
   char **new_argv;
   int i;
-  int rc = -1;
+  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_report[2];
+  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;
-  
-  SETUP_DEBUG();
+  gchar *helper_process;
+  CONSOLE_CURSOR_INFO cursor_info;
+  wchar_t *whelper, **wargv, **wenvp;
+  gchar *glib_dll_directory;
 
   if (child_setup && !warned_about_child_setup)
     {
       warned_about_child_setup = TRUE;
-      g_warning ("passing a child setup function to the g_spawn functions is pointless and dangerous on Win32");
+      g_warning ("passing a child setup function to the g_spawn functions is pointless on Windows and it is ignored");
     }
 
   argc = protect_argv (argv, &protected_argv);
@@ -653,10 +567,9 @@ do_spawn_with_pipes (gboolean              dont_wait,
     {
       /* We can do without the helper process */
       gboolean retval =
-       do_spawn_directly (dont_wait, dont_return_handle, flags,
+       do_spawn_directly (exit_status, do_return_handle, flags,
                           argv, envp, protected_argv,
-                          child_setup, user_data, child_handle,
-                          exit_status, error);
+                          child_handle, error);
       g_strfreev (protected_argv);
       return retval;
     }
@@ -673,11 +586,36 @@ do_spawn_with_pipes (gboolean              dont_wait,
   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);
-  new_argv[0] = HELPER_PROCESS;
+  if (GetConsoleWindow () != NULL)
+    helper_process = HELPER_PROCESS "-console.exe";
+  else
+    helper_process = HELPER_PROCESS ".exe";
+  
+  glib_dll_directory = _glib_get_dll_directory ();
+  if (glib_dll_directory != NULL)
+    {
+      helper_process = g_build_filename (glib_dll_directory, helper_process, NULL);
+      g_free (glib_dll_directory);
+    }
+  else
+    helper_process = g_strdup (helper_process);
+
+  new_argv[0] = protect_argv_string (helper_process);
+
   _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.
+   */
+  child_err_report_pipe[0] = dup_noninherited (child_err_report_pipe[0], _O_RDONLY);
+
   if (flags & G_SPAWN_FILE_AND_ARGV_ZERO)
     {
       /* Overload ARG_CHILD_ERR_REPORT to also encode the
@@ -686,6 +624,17 @@ do_spawn_with_pipes (gboolean              dont_wait,
       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.
+   */
+  helper_sync_pipe[1] = dup_noninherited (helper_sync_pipe[1], _O_WRONLY);
+
   if (standard_input)
     {
       _g_sprintf (args[ARG_STDIN], "%d", stdin_pipe[0]);
@@ -745,7 +694,7 @@ do_spawn_with_pipes (gboolean              dont_wait,
   else
     new_argv[ARG_USE_PATH] = "-";
 
-  if (dont_wait)
+  if (exit_status == NULL)
     new_argv[ARG_WAIT] = "-";
   else
     new_argv[ARG_WAIT] = "w";
@@ -753,129 +702,77 @@ do_spawn_with_pipes (gboolean              dont_wait,
   for (i = 0; i <= argc; i++)
     new_argv[ARG_PROGRAM + i] = protected_argv[i];
 
+  SETUP_DEBUG();
+
   if (debug)
     {
-      g_print ("calling " HELPER_PROCESS " with argv:\n");
+      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"));
     }
 
-  if (G_WIN32_HAVE_WIDECHAR_API ())
+  if (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error))
     {
-      wchar_t *whelper = g_utf8_to_utf16 (HELPER_PROCESS, -1, NULL, NULL, NULL);
-      wchar_t **wargv, **wenvp;
-
-      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[ARG_WORKING_DIRECTORY]);
-         g_free (new_argv);
-         g_free (whelper);
-         
-         return FALSE;
-       }
-      
-      if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error))
-       {
-         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[ARG_WORKING_DIRECTORY]);
-         g_free (new_argv);
-         g_free (whelper);
-         g_strfreev ((gchar **) wargv);
-         
-         return FALSE;
-       }
-
-      if (child_setup)
-       (* child_setup) (user_data);
-
-      if (wenvp != NULL)
-       /* Let's hope envp hasn't mucked with PATH so that
-        * gspawn-win32-helper.exe isn't found.
-        */
-       rc = _wspawnvpe (P_NOWAIT, whelper, (const wchar_t **) wargv, (const wchar_t **) wenvp);
+      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
-       rc = _wspawnvp (P_NOWAIT, whelper, (const wchar_t **) wargv);
+       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);
 
-      saved_errno = errno;
+      goto cleanup_and_fail;
+    }
 
-      g_free (whelper);
+  if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error))
+    {
+      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);
-      g_strfreev ((gchar **) wenvp);
+      goto cleanup_and_fail;
     }
-  else
-    {
-      char **cpargv, **cpenvp;
 
-      if (!utf8_charv_to_cp_charv (new_argv, &cpargv, &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[ARG_WORKING_DIRECTORY]);
-         g_free (new_argv);
-         
-         return FALSE;
-       }
-       
-      if (!utf8_charv_to_cp_charv (envp, &cpenvp, NULL, &conv_error))
-       {
-         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[ARG_WORKING_DIRECTORY]);
-         g_free (new_argv);
-         g_strfreev (cpargv);
-         
-         return FALSE;
-       }
-
-      if (child_setup)
-       (* child_setup) (user_data);
+  whelper = g_utf8_to_utf16 (helper_process, -1, NULL, NULL, NULL);
+  g_free (helper_process);
 
-      if (cpenvp != NULL)
-       rc = spawnvpe (P_NOWAIT, HELPER_PROCESS, (const char **) cpargv, (const char **) cpenvp);
-      else
-       rc = spawnvp (P_NOWAIT, HELPER_PROCESS, (const char **) cpargv);
+  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;
+  saved_errno = errno;
 
-      g_strfreev (cpargv);
-      g_strfreev (cpenvp);
-    }
+  g_free (whelper);
+  g_strfreev ((gchar **) wargv);
+  g_strfreev ((gchar **) wenvp);
 
   /* 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]);
 
   g_strfreev (protected_argv);
 
+  g_free (new_argv[0]);
   g_free (new_argv[ARG_WORKING_DIRECTORY]);
   g_free (new_argv);
 
@@ -888,7 +785,7 @@ do_spawn_with_pipes (gboolean              dont_wait,
       goto cleanup_and_fail;
     }
 
-  if (!dont_wait)
+  if (exit_status != NULL)
     {
       /* Synchronous case. Pass helper's report pipe back to caller,
        * which takes care of reading it after the grandchild has
@@ -896,6 +793,8 @@ do_spawn_with_pipes (gboolean              dont_wait,
        */
       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
     {
@@ -903,12 +802,12 @@ do_spawn_with_pipes (gboolean              dont_wait,
       if (!read_helper_report (child_err_report_pipe[0], helper_report, error))
        goto cleanup_and_fail;
         
-      close (child_err_report_pipe[0]);
+      close_and_invalidate (&child_err_report_pipe[0]);
 
       switch (helper_report[0])
        {
        case CHILD_NO_ERROR:
-         if (child_handle && dont_wait && !dont_return_handle)
+         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
@@ -917,15 +816,21 @@ do_spawn_with_pipes (gboolean              dont_wait,
              if (!DuplicateHandle ((HANDLE) rc, (HANDLE) helper_report[1],
                                    GetCurrentProcess (), (LPHANDLE) child_handle,
                                    0, TRUE, DUPLICATE_SAME_ACCESS))
-               *child_handle = 0;
+               {
+                 char *emsg = g_win32_error_message (GetLastError ());
+                 g_print("%s\n", emsg);
+                 *child_handle = 0;
+               }
            }
          else if (child_handle)
            *child_handle = 0;
-         if (exit_status)
-           *exit_status = helper_report[1];
+         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;
        }
@@ -945,12 +850,17 @@ do_spawn_with_pipes (gboolean              dont_wait,
   return TRUE;
 
  cleanup_and_fail:
+
   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)
@@ -1011,19 +921,17 @@ g_spawn_sync_utf8 (const gchar          *working_directory,
   if (standard_error)
     *standard_error = NULL;
   
-  if (!do_spawn_with_pipes (FALSE,
-                           TRUE,
+  if (!do_spawn_with_pipes (&status,
+                           FALSE,
                            working_directory,
                            argv,
                            envp,
                            flags,
                            child_setup,
-                           user_data,
                            NULL,
                            NULL,
                            standard_output ? &outpipe : NULL,
                            standard_error ? &errpipe : NULL,
-                           &status,
                            &reportpipe,
                            error))
     return FALSE;
@@ -1037,9 +945,12 @@ g_spawn_sync_utf8 (const gchar          *working_directory,
       outstr = g_string_new (NULL);
       outchannel = g_io_channel_win32_new_fd (outpipe);
       g_io_channel_set_encoding (outchannel, NULL, NULL);
+      g_io_channel_set_buffered (outchannel, FALSE);
       g_io_channel_win32_make_pollfd (outchannel,
                                      G_IO_IN | G_IO_ERR | G_IO_HUP,
                                      &outfd);
+      if (debug)
+       g_print ("outfd=%p\n", (HANDLE) outfd.fd);
     }
       
   if (errpipe >= 0)
@@ -1047,9 +958,12 @@ g_spawn_sync_utf8 (const gchar          *working_directory,
       errstr = g_string_new (NULL);
       errchannel = g_io_channel_win32_new_fd (errpipe);
       g_io_channel_set_encoding (errchannel, NULL, NULL);
+      g_io_channel_set_buffered (errchannel, FALSE);
       g_io_channel_win32_make_pollfd (errchannel,
                                      G_IO_IN | G_IO_ERR | G_IO_HUP,
                                      &errfd);
+      if (debug)
+       g_print ("errfd=%p\n", (HANDLE) errfd.fd);
     }
 
   /* Read data until we get EOF on all pipes. */
@@ -1079,8 +993,8 @@ g_spawn_sync_utf8 (const gchar          *working_directory,
         {
           failed = TRUE;
 
-          g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_READ,
-                       _("Unexpected error in g_io_channel_win32_poll() reading data from a child process"));
+          g_set_error_literal (error, G_SPAWN_ERROR, G_SPAWN_ERROR_READ,
+                               _("Unexpected error in g_io_channel_win32_poll() reading data from a child process"));
          
           break;
         }
@@ -1151,7 +1065,7 @@ g_spawn_sync_utf8 (const gchar          *working_directory,
       /* Helper process was involved. Read its report now after the
        * grandchild has finished.
        */
-      gint helper_report[2];
+      gintptr helper_report[2];
 
       if (!read_helper_report (reportpipe, helper_report, error))
        failed = TRUE;
@@ -1227,20 +1141,18 @@ g_spawn_async_with_pipes_utf8 (const gchar          *working_directory,
   g_return_val_if_fail (standard_input == NULL ||
                         !(flags & G_SPAWN_CHILD_INHERITS_STDIN), FALSE);
   
-  return do_spawn_with_pipes (TRUE,
-                             !(flags & G_SPAWN_DO_NOT_REAP_CHILD),
+  return do_spawn_with_pipes (NULL,
+                             (flags & G_SPAWN_DO_NOT_REAP_CHILD),
                              working_directory,
                              argv,
                              envp,
                              flags,
                              child_setup,
-                             user_data,
                              child_handle,
                              standard_input,
                              standard_output,
                              standard_error,
                              NULL,
-                             NULL,
                              error);
 }
 
@@ -1309,6 +1221,27 @@ g_spawn_close_pid (GPid pid)
     CloseHandle (pid);
 }
 
+gboolean
+g_spawn_check_exit_status (gint      exit_status,
+                          GError  **error)
+{
+  gboolean ret = FALSE;
+
+  if (exit_status != 0)
+    {
+      g_set_error (error, G_SPAWN_EXIT_ERROR, exit_status,
+                  _("Child process exited with code %ld"),
+                  (long) exit_status);
+      goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+#if !defined (_WIN64)
+
 /* Binary compatibility versions that take system codepage pathnames,
  * argument vectors and environments. These get used only by code
  * built against 2.8.1 or earlier. Code built against 2.8.2 or later
@@ -1574,7 +1507,6 @@ g_spawn_command_line_async (const gchar *command_line,
   return retval;
 }
 
-#endif /* !GSPAWN_HELPER */
+#endif /* !_WIN64 */
 
-#define __G_SPAWN_C__
-#include "galiasdef.c"
+#endif /* !GSPAWN_HELPER */