Index: gspawn-win32.c
===================================================================
--- gspawn-win32.c	(revision 6334)
+++ gspawn-win32.c	(working copy)
@@ -2,6 +2,7 @@
  *
  *  Copyright 2000 Red Hat, Inc.
  *  Copyright 2003 Tor Lillqvist
+ *  Copyright 2008 Lieven van der Heide
  *
  * GLib is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License as
@@ -19,33 +20,12 @@
  * Boston, MA 02111-1307, USA.
  */
 
-/*
- * 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 most 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.
- *
- * The structure of the source code in this file is a mess, I know.
- */
-
 /* Define this to get some logging all the time */
 /* #define G_SPAWN_WIN32_DEBUG */
 
 #include "config.h"
 
 #include "glib.h"
-#include "gprintfint.h"
 #include "glibintl.h"
 #include "galias.h"
 
@@ -59,58 +39,70 @@
 #include <io.h>
 #include <process.h>
 #include <direct.h>
+#include <sys/types.h>
+#include <sys/stat.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
-
 #ifdef G_SPAWN_WIN32_DEBUG
   static int debug = 1;
   #define SETUP_DEBUG() /* empty */
 #else
   static int debug = -1;
-  #define SETUP_DEBUG()					\
-    G_STMT_START					\
-      {							\
-	if (debug == -1)				\
-	  {						\
-	    if (getenv ("G_SPAWN_WIN32_DEBUG") != NULL)	\
-	      debug = 1;				\
-	    else					\
-	      debug = 0;				\
-	  }						\
-      }							\
+  #define SETUP_DEBUG()                                 \
+    G_STMT_START                                        \
+      {                                                 \
+        if (debug == -1)                                \
+          {                                             \
+            if (getenv ("G_SPAWN_WIN32_DEBUG") != NULL) \
+              debug = 1;                                \
+            else                                        \
+              debug = 0;                                \
+          }                                             \
+      }                                                 \
     G_STMT_END
 #endif
 
-enum
+typedef struct
 {
-  CHILD_NO_ERROR,
-  CHILD_CHDIR_FAILED,
-  CHILD_SPAWN_FAILED,
-};
+  gint read_pipe;
+  gint write_pipe;
+} Pipe;
 
-enum {
-  ARG_CHILD_ERR_REPORT = 1,
-  ARG_STDIN,
-  ARG_STDOUT,
-  ARG_STDERR,
-  ARG_WORKING_DIRECTORY,
-  ARG_CLOSE_DESCRIPTORS,
-  ARG_USE_PATH,
-  ARG_WAIT,
-  ARG_PROGRAM,
-  ARG_COUNT = ARG_PROGRAM
-};
+static void free_pipe(Pipe *pipe);
+static Pipe* create_pipe(gboolean is_input_pipe,GError **error);
+static gint create_dev_null();
 
-#ifndef GSPAWN_HELPER
+typedef struct
+{
+  PROCESS_INFORMATION proc_info;
 
-#define HELPER_PROCESS "gspawn-win32-helper"
+  Pipe *stdin_pipe;
+  Pipe *stdout_pipe;
+  Pipe *stderr_pipe;
 
+  gint dev_null;
+
+} Process;
+static Process*
+do_spawn_with_pipes ( const gchar          *working_directory,
+                      gchar               **argv,
+                      char                **envp,
+                      GSpawnFlags           flags,
+                      GSpawnChildSetupFunc  child_setup,
+                      gpointer              user_data,
+                      gint                 *standard_input,
+                      gint                 *standard_output,
+                      gint                 *standard_error,
+                      GError              **error);
+static Process* alloc_process();
+static void free_process(Process *proc);
+static gboolean proc_get_exit_status(Process *proc,gint *exit_status,GError **error);
+
+static GHashTable *procs_by_handle = 0;
+static GStaticMutex procs_by_handle_lock = G_STATIC_MUTEX_INIT;
+static void add_handle_proc_mapping(HANDLE handle,Process *proc);
+static Process* get_and_remove_handle_proc_mapping(HANDLE handle);
+static void set_error_win32(GError **error,const gchar *format,DWORD win32_error_code);
+
 static gchar *
 protect_argv_string (const gchar *string)
 {
@@ -121,17 +113,17 @@
   while (*p)
     {
       if (*p == ' ' || *p == '\t')
-	need_dblquotes = TRUE;
+        need_dblquotes = TRUE;
       else if (*p == '"')
-	len++;
+        len++;
       else if (*p == '\\')
-	{
-	  const gchar *pp = p;
-	  while (*pp && *pp == '\\')
-	    pp++;
-	  if (*pp == '"')
-	    len++;
-	}
+        {
+          const gchar *pp = p;
+          while (*pp && *pp == '\\')
+            pp++;
+          if (*pp == '"')
+            len++;
+        }
       len++;
       p++;
     }
@@ -145,15 +137,15 @@
   while (*p)
     {
       if (*p == '"')
-	*q++ = '\\';
+        *q++ = '\\';
       else if (*p == '\\')
-	{
-	  const gchar *pp = p;
-	  while (*pp && *pp == '\\')
-	    pp++;
-	  if (*pp == '"')
-	    *q++ = '\\';
-	}
+        {
+          const gchar *pp = p;
+          while (*pp && *pp == '\\')
+            pp++;
+          if (*pp == '"')
+            *q++ = '\\';
+        }
       *q++ = *p;
       p++;
     }
@@ -165,34 +157,28 @@
   return retval;
 }
 
-static gint
-protect_argv (gchar  **argv,
-	      gchar ***new_argv)
+static gchar*
+argv_to_command_line(gchar **argv)
 {
   gint i;
-  gint argc = 0;
-  
-  while (argv[argc])
-    ++argc;
-  *new_argv = g_new (gchar *, argc+1);
+  GString *ret;
+  gchar *cur_protected;
 
-  /* Quote each argv element if necessary, so that it will get
-   * reconstructed correctly in the C runtime startup code.  Note that
-   * the unquoting algorithm in the C runtime is really weird, and
-   * rather different than what Unix shells do. See stdargv.c in the C
-   * runtime sources (in the Platform SDK, in src/crt).
-   *
-   * Note that an new_argv[0] constructed by this function should
-   * *not* be passed as the filename argument to a spawn* or exec*
-   * family function. That argument should be the real file name
-   * without any quoting.
-   */
-  for (i = 0; i < argc; i++)
-    (*new_argv)[i] = protect_argv_string (argv[i]);
+  ret = g_string_new("");
 
-  (*new_argv)[argc] = NULL;
+  for(i = 0;argv[i];i++)
+    {
+      cur_protected = protect_argv_string(argv[i]);
+      g_string_append(ret,cur_protected);
 
-  return argc;
+      /* Only add a separating space, if more arguments come after this one. */
+      if(argv[i + 1])
+        g_string_append(ret," ");
+
+      g_free(cur_protected);
+    }
+
+  return g_string_free(ret,FALSE);
 }
 
 GQuark
@@ -230,11 +216,11 @@
 static void
 close_and_invalidate (gint *fd)
 {
-  if (*fd < 0)
+  gint fd_val = InterlockedExchange(fd,-1);
+  if (fd_val < 0)
     return;
 
-  close (*fd);
-  *fd = -1;
+  close (fd_val);
 }
 
 typedef enum
@@ -278,95 +264,10 @@
 }
 
 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,
-		    gint     report[2],
-		    GError **error)
-{
-  gint bytes = 0;
-  
-  while (bytes < sizeof(gint)*2)
-    {
-      gint chunk;
-
-      if (debug)
-	g_print ("%s:read_helper_report: read %d...\n",
-		 __FILE__,
-		 sizeof(gint)*2 - bytes);
-
-      chunk = read (fd, ((gchar*)report) + bytes,
-		    sizeof(gint)*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)
-        break; /* EOF */
-      else
-	bytes += chunk;
-    }
-
-  if (bytes < sizeof(gint)*2)
-    return FALSE;
-
-  return TRUE;
-}
-
-static void
-set_child_error (gint         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:
-      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 ();
-    }
-}
-
-static gboolean
 utf8_charv_to_wcharv (char     **utf8_charv,
-		      wchar_t ***wcharv,
-		      int       *error_index,
-		      GError   **error)
+                      wchar_t ***wcharv,
+                      int       *error_index,
+                      GError   **error)
 {
   wchar_t **retval = NULL;
 
@@ -376,445 +277,206 @@
       int n = 0, i;
 
       while (utf8_charv[n])
-	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[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)     
+static Process*
+do_spawn_with_pipes ( const gchar          *working_directory,
+                      gchar               **argv,
+                      char                **envp,
+                      GSpawnFlags           flags,
+                      GSpawnChildSetupFunc  child_setup,
+                      gpointer              user_data,
+                      gint                 *standard_input,
+                      gint                 *standard_output,
+                      gint                 *standard_error,
+                      GError              **error)
 {
-  const int mode = (exit_status == NULL) ? P_NOWAIT : P_WAIT;
-  char **new_argv;
-  int 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;
-    }
+  Process *ret = 0;
   
-  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);
+  gchar *app_name = 0;
+  gchar *command_line = 0;
+  static gboolean warned_about_child_setup = FALSE;
+  STARTUPINFO startup_info;
+  BOOL res;
 
-      return FALSE;
-    }
+  SETUP_DEBUG();
 
-  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);
+  ret = alloc_process();
 
-      return FALSE;
-    }
+  if(standard_input)
+    *standard_input = -1;
+  if(standard_output)
+    *standard_output = -1;
+  if(standard_error)
+    *standard_error = -1;
 
-  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);
-
-  g_free (wargv0);
-  g_strfreev ((gchar **) wargv);
-  g_strfreev ((gchar **) wenvp);
-
-  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;
-
-  return TRUE;
-}
-
-static gboolean
-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		  *err_report,
-		     GError              **error)     
-{
-  char **protected_argv;
-  char args[ARG_COUNT][10];
-  char **new_argv;
-  int i;
-  int 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];
-  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;
-  
-  SETUP_DEBUG();
-
   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");
     }
 
-  argc = protect_argv (argv, &protected_argv);
-
-  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))
+  if(flags & G_SPAWN_SEARCH_PATH)
     {
-      /* 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;
-    }
+      gchar *tmp;
+      
+      tmp = g_find_program_in_path(argv[0]);
+      if(!tmp)
+        {
+          g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED,"Executable not found: %s",argv[0]);
+          goto error_handler;
+        }
 
-  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;
-  
-  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";
-  new_argv[0] = 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];
-  
-  if (flags & G_SPAWN_FILE_AND_ARGV_ZERO)
-    {
-      /* Overload ARG_CHILD_ERR_REPORT to also encode the
-       * G_SPAWN_FILE_AND_ARGV_ZERO functionality.
-       */
-      strcat (args[ARG_CHILD_ERR_REPORT], "#");
+      app_name = protect_argv_string(tmp);
+      g_free(tmp);
     }
-  
-  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";
-    }
+    app_name = protect_argv_string(argv[0]);
+
+  if(flags & G_SPAWN_FILE_AND_ARGV_ZERO)
+    command_line = argv_to_command_line(argv + 1);
   else
+    command_line = argv_to_command_line(argv);
+
+  memset(&startup_info,0,sizeof(STARTUPINFO));
+  startup_info.cb = sizeof(STARTUPINFO);
+
+  startup_info.dwFlags |= STARTF_USESTDHANDLES;
+  if(flags & G_SPAWN_CHILD_INHERITS_STDIN)
     {
-      new_argv[ARG_STDOUT] = "-";
+      g_assert(!standard_input);
+      startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
     }
-  
-  if (standard_error)
-    {
-      _g_sprintf (args[ARG_STDERR], "%d", stderr_pipe[1]);
-      new_argv[ARG_STDERR] = args[ARG_STDERR];
-    }
-  else if (flags & G_SPAWN_STDERR_TO_DEV_NULL)
-    {
-      new_argv[ARG_STDERR] = "z";
-    }
   else
     {
-      new_argv[ARG_STDERR] = "-";
-    }
-  
-  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] = "-";
+      /* For input from /dev/null, we just create a new pipe, and set the 
+       * end of file flag of that file, so any reads from it will just give
+       * give an end of file (as /dev/null does). */
 
-  if (flags & G_SPAWN_SEARCH_PATH)
-    new_argv[ARG_USE_PATH] = "y";
-  else
-    new_argv[ARG_USE_PATH] = "-";
+      ret->stdin_pipe = create_pipe(TRUE,error);
+      if(!ret->stdin_pipe)
+        goto error_handler;
 
-  if (exit_status == NULL)
-    new_argv[ARG_WAIT] = "-";
-  else
-    new_argv[ARG_WAIT] = "w";
+      startup_info.hStdInput = (HANDLE)_get_osfhandle(ret->stdin_pipe->read_pipe);
 
-  for (i = 0; i <= argc; i++)
-    new_argv[ARG_PROGRAM + i] = protected_argv[i];
-
-  if (debug)
-    {
-      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(standard_input)
+        {
+          *standard_input = ret->stdin_pipe->write_pipe;
+          /* The fd is owned by standard_input now. */
+          ret->stdin_pipe->write_pipe = -1;
+        }
+      else
+        {
+          /* Set an end of file to our side of the stdin pipe, 
+           * and close it right away. */
+          SetEndOfFile((HANDLE)_get_osfhandle(ret->stdin_pipe->write_pipe));
+          
+          close_and_invalidate(&ret->stdin_pipe->write_pipe);
+        }
     }
 
-  whelper = g_utf8_to_utf16 (helper_process, -1, NULL, NULL, NULL);
-
-  if (!utf8_charv_to_wcharv (new_argv, &wargv, &conv_error_index, &conv_error))
+  if(standard_output)
     {
-      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);
+      ret->stdout_pipe = create_pipe(FALSE,error);
+      if(!ret->stdout_pipe)
+        goto error_handler;
+      
+      startup_info.hStdOutput = (HANDLE)_get_osfhandle(ret->stdout_pipe->write_pipe);
 
-      return FALSE;
+      *standard_output = ret->stdout_pipe->read_pipe;
+      /* The fd is owned by standard_output now. */
+      ret->stdout_pipe->read_pipe = -1;
     }
-
-  if (!utf8_charv_to_wcharv (envp, &wenvp, NULL, &conv_error))
+  else if(flags & G_SPAWN_STDOUT_TO_DEV_NULL)
     {
-      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(!ret->dev_null)
+        ret->dev_null = create_dev_null();
+      startup_info.hStdOutput = (HANDLE)_get_osfhandle(ret->dev_null);
     }
-
-  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);
   else
-    rc = _wspawnvp (P_NOWAIT, whelper, (const wchar_t **) wargv);
+    startup_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);  
 
-  saved_errno = errno;
-
-  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 (&stdin_pipe[0]);
-  close_and_invalidate (&stdout_pipe[1]);
-  close_and_invalidate (&stderr_pipe[1]);
-
-  g_strfreev (protected_argv);
-
-  g_free (new_argv[ARG_WORKING_DIRECTORY]);
-  g_free (new_argv);
-
-  /* Check if gspawn-win32-helper couldn't be run */
-  if (rc == -1 && saved_errno != 0)
+  if(standard_error)
     {
-      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;
-    }
+      ret->stderr_pipe = create_pipe(FALSE,error);
+      if(!ret->stderr_pipe)
+        goto error_handler;
+      
+      startup_info.hStdError = (HANDLE)_get_osfhandle(ret->stderr_pipe->write_pipe);
 
-  if (exit_status != NULL)
+      *standard_error = ret->stderr_pipe->read_pipe;
+      /* The fd is owned by standard_error now. */
+      ret->stderr_pipe->read_pipe = -1;
+    }
+  else if (flags & G_SPAWN_STDERR_TO_DEV_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];
+      if(!ret->dev_null)
+        ret->dev_null = create_dev_null();
+      startup_info.hStdError = (HANDLE)_get_osfhandle(ret->dev_null);
     }
   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 (child_err_report_pipe[0]);
+    startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);  
 
-      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))
-		*child_handle = 0;
-	    }
-	  else if (child_handle)
-	    *child_handle = 0;
-	  break;
-	  
-	default:
-	  set_child_error (helper_report, working_directory, error);
-	  goto cleanup_and_fail;
-	}
+  memset(&ret->proc_info,0,sizeof(PROCESS_INFORMATION));
+  res = CreateProcess(app_name,command_line,
+    NULL,NULL,TRUE,0,envp,working_directory,
+    &startup_info,&ret->proc_info);
+  if(!res)
+    {
+      set_error_win32(error,"CreateProcess failed: %s",GetLastError());
+      goto error_handler;
     }
 
-  /* 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;
+  /* close our versions of the child handles, so that as soon as 
+   * the child closes those handles aswell, an end of file will be appended */
+  if(ret->stdin_pipe)
+    close_and_invalidate(&ret->stdin_pipe->read_pipe);
+  if(ret->stdout_pipe)
+    close_and_invalidate(&ret->stdout_pipe->write_pipe);
+  if(ret->stderr_pipe)
+    close_and_invalidate(&ret->stderr_pipe->write_pipe);
 
- 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 (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]);
+  g_free(app_name);
+  g_free(command_line);
 
-  return FALSE;
+  return ret;
+
+error_handler:
+  g_free(app_name);
+  g_free(command_line);
+
+  if(ret)
+    free_process(ret);
+
+  if(standard_input)
+    close_and_invalidate(standard_input);
+  if(standard_output)
+    close_and_invalidate(standard_output);
+  if(standard_error)
+    close_and_invalidate(standard_error);
+
+  return NULL;
 }
 
 gboolean
@@ -829,235 +491,197 @@
 		   gint                 *exit_status,
 		   GError              **error)     
 {
-  gint outpipe = -1;
-  gint errpipe = -1;
-  gint reportpipe = -1;
-  GIOChannel *outchannel = NULL;
-  GIOChannel *errchannel = NULL;
-  GPollFD outfd, errfd;
+  Process *proc = 0;
+
+  gint stdout_pipe = -1;
+  gint stderr_pipe = -1;
+  GIOChannel *outchannel = 0;
+  GIOChannel *errchannel = 0;
+  GString *outstr = 0;
+  GString *errstr = 0;
+  
+  GPollFD outfd;
   GPollFD fds[2];
   gint nfds;
   gint outindex = -1;
   gint errindex = -1;
   gint ret;
-  GString *outstr = NULL;
-  GString *errstr = NULL;
-  gboolean failed;
-  gint status;
-  
+
+  GPollFD errfd;
+    
   g_return_val_if_fail (argv != NULL, FALSE);
   g_return_val_if_fail (!(flags & G_SPAWN_DO_NOT_REAP_CHILD), FALSE);
   g_return_val_if_fail (standard_output == NULL ||
                         !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
   g_return_val_if_fail (standard_error == NULL ||
                         !(flags & G_SPAWN_STDERR_TO_DEV_NULL), FALSE);
-  
-  /* Just to ensure segfaults if callers try to use
-   * these when an error is reported.
-   */
-  if (standard_output)
-    *standard_output = NULL;
 
-  if (standard_error)
-    *standard_error = NULL;
-  
-  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,
-			    &reportpipe,
-			    error))
-    return FALSE;
+  proc = do_spawn_with_pipes(
+    working_directory,
+    argv,
+    envp,
+    flags,
+    child_setup,
+    user_data,
+    NULL,
+    standard_output ? &stdout_pipe : NULL,
+    standard_error ? &stderr_pipe : NULL,
+    error);
+  if(!proc)
+    goto error_handler;
 
   /* Read data from child. */
   
-  failed = FALSE;
-
-  if (outpipe >= 0)
+  if (stdout_pipe >= 0)
     {
       outstr = g_string_new (NULL);
-      outchannel = g_io_channel_win32_new_fd (outpipe);
+      
+      outchannel = g_io_channel_win32_new_fd (stdout_pipe);
+      outchannel->close_on_unref = 1;
+      if(!outchannel)
+        goto error_handler;
+      stdout_pipe = -1;
+
       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);
+                                      G_IO_IN | G_IO_ERR | G_IO_HUP,
+                                      &outfd);
       if (debug)
-	g_print ("outfd=%x\n", outfd.fd);
+        g_print ("outfd=%x\n", outfd.fd);
     }
       
-  if (errpipe >= 0)
+  if (stderr_pipe >= 0)
     {
       errstr = g_string_new (NULL);
-      errchannel = g_io_channel_win32_new_fd (errpipe);
+      
+      errchannel = g_io_channel_win32_new_fd (stderr_pipe);
+      errchannel->close_on_unref = 1;
+      if(!errchannel)
+        goto error_handler;
+      stderr_pipe = -1;
+
       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);
+                                      G_IO_IN | G_IO_ERR | G_IO_HUP,
+                                      &errfd);
       if (debug)
-	g_print ("errfd=%x\n", errfd.fd);
+        g_print ("errfd=%x\n", errfd.fd);
     }
 
   /* Read data until we get EOF on all pipes. */
-  while (!failed && (outpipe >= 0 || errpipe >= 0))
+  while (outchannel || errchannel)
     {
       nfds = 0;
-      if (outpipe >= 0)
-	{
-	  fds[nfds] = outfd;
-	  outindex = nfds;
-	  nfds++;
-	}
-      if (errpipe >= 0)
-	{
-	  fds[nfds] = errfd;
-	  errindex = nfds;
-	  nfds++;
-	}
+      if (outchannel)
+        {
+          fds[nfds] = outfd;
+          outindex = nfds;
+          nfds++;
+        }
+      if (errchannel)
+        {
+          fds[nfds] = errfd;
+          errindex = nfds;
+          nfds++;
+        }
 
       if (debug)
-	g_print ("g_spawn_sync: calling g_io_channel_win32_poll, nfds=%d\n",
-		 nfds);
+        g_print ("g_spawn_sync: calling g_io_channel_win32_poll, nfds=%d\n",nfds);
 
       ret = g_io_channel_win32_poll (fds, nfds, -1);
 
       if (ret < 0)
         {
-          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"));
-	  
-          break;
+          goto error_handler;
         }
 
-      if (outpipe >= 0 && (fds[outindex].revents & G_IO_IN))
+      if (outchannel && (fds[outindex].revents & G_IO_IN))
         {
           switch (read_data (outstr, outchannel, error))
             {
             case READ_FAILED:
-	      if (debug)
-		g_print ("g_spawn_sync: outchannel: READ_FAILED\n");
-              failed = TRUE;
-              break;
+              if (debug)
+                g_print ("g_spawn_sync: outchannel: READ_FAILED\n");
+              goto error_handler;
+
             case READ_EOF:
-	      if (debug)
-		g_print ("g_spawn_sync: outchannel: READ_EOF\n");
+              if (debug)
+                g_print ("g_spawn_sync: outchannel: READ_EOF\n");
               g_io_channel_unref (outchannel);
-	      outchannel = NULL;
-              close_and_invalidate (&outpipe);
+              outchannel = NULL;
               break;
             default:
-	      if (debug)
-		g_print ("g_spawn_sync: outchannel: OK\n");
+              if (debug)
+                g_print ("g_spawn_sync: outchannel: OK\n");
               break;
             }
-
-          if (failed)
-            break;
         }
 
-      if (errpipe >= 0 && (fds[errindex].revents & G_IO_IN))
+      if (errchannel >= 0 && (fds[errindex].revents & G_IO_IN))
         {
           switch (read_data (errstr, errchannel, error))
             {
             case READ_FAILED:
-	      if (debug)
-		g_print ("g_spawn_sync: errchannel: READ_FAILED\n");
-              failed = TRUE;
-              break;
+              if (debug)
+                g_print ("g_spawn_sync: errchannel: READ_FAILED\n");
+              goto error_handler;
+
             case READ_EOF:
-	      if (debug)
-		g_print ("g_spawn_sync: errchannel: READ_EOF\n");
-	      g_io_channel_unref (errchannel);
-	      errchannel = NULL;
-              close_and_invalidate (&errpipe);
+              if (debug)
+                g_print ("g_spawn_sync: errchannel: READ_EOF\n");
+              g_io_channel_unref (errchannel);
+              errchannel = NULL;
               break;
             default:
-	      if (debug)
-		g_print ("g_spawn_sync: errchannel: OK\n");
+              if (debug)
+                g_print ("g_spawn_sync: errchannel: OK\n");
               break;
             }
-
-          if (failed)
-            break;
         }
     }
 
-  if (reportpipe == -1)
+  /* Wait until the process has actually shut down. */
+  WaitForSingleObject(proc->proc_info.hProcess,INFINITE);
+
+  if (exit_status)
     {
-      /* No helper process, exit status of actual spawned process
-       * already available.
-       */
-      if (exit_status)
-        *exit_status = status;
+      if(!proc_get_exit_status(proc,exit_status,error))
+        goto error_handler;
     }
-  else
-    {
-      /* Helper process was involved. Read its report now after the
-       * grandchild has finished.
-       */
-      gint 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);
-    }
+  if (standard_output)        
+    *standard_output = g_string_free (outstr, FALSE);
 
+  if (standard_error)
+    *standard_error = g_string_free (errstr, FALSE);
 
-  /* These should only be open still if we had an error.  */
+  free_process(proc);
+  g_assert(stdout_pipe == -1);
+  g_assert(stderr_pipe == -1);
+
+  return TRUE;
+
+error_handler:
+  free_process(proc);
   
   if (outchannel != NULL)
     g_io_channel_unref (outchannel);
   if (errchannel != NULL)
     g_io_channel_unref (errchannel);
-  if (outpipe >= 0)
-    close_and_invalidate (&outpipe);
-  if (errpipe >= 0)
-    close_and_invalidate (&errpipe);
-  
-  if (failed)
-    {
-      if (outstr)
-        g_string_free (outstr, TRUE);
-      if (errstr)
-        g_string_free (errstr, TRUE);
+  if (stdout_pipe >= 0)
+    close_and_invalidate (&stdout_pipe);
+  if (stderr_pipe >= 0)
+    close_and_invalidate (&stderr_pipe);
 
-      return FALSE;
-    }
-  else
-    {
-      if (standard_output)        
-        *standard_output = g_string_free (outstr, FALSE);
-
-      if (standard_error)
-        *standard_error = g_string_free (errstr, FALSE);
-
-      return TRUE;
-    }
+  if (outstr)
+    g_string_free (outstr, TRUE);
+  if (errstr)
+    g_string_free (errstr, TRUE);
+  return FALSE;
 }
 
 gboolean
@@ -1073,6 +697,8 @@
 			       gint                 *standard_error,
 			       GError              **error)
 {
+  Process *proc = 0;
+
   g_return_val_if_fail (argv != NULL, FALSE);
   g_return_val_if_fail (standard_output == NULL ||
                         !(flags & G_SPAWN_STDOUT_TO_DEV_NULL), FALSE);
@@ -1081,21 +707,42 @@
   /* can't inherit stdin if we have an input pipe. */
   g_return_val_if_fail (standard_input == NULL ||
                         !(flags & G_SPAWN_CHILD_INHERITS_STDIN), FALSE);
-  
-  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,
-			      error);
+
+  if(child_handle)
+    *child_handle = 0;
+
+  proc = do_spawn_with_pipes(working_directory,
+    argv,
+    envp,
+    flags,
+    child_setup,
+    user_data,
+    standard_input,
+    standard_output,
+    standard_error,
+    error);
+  if(!proc)
+    goto error_handler;
+
+  if(flags & G_SPAWN_DO_NOT_REAP_CHILD && child_handle)
+    {
+      *child_handle = proc->proc_info.hProcess;
+      add_handle_proc_mapping(proc->proc_info.hProcess,proc);
+    }
+  else
+    free_process(proc);
+
+  return TRUE;
+
+error_handler:
+  if(*child_handle)
+    {
+      /* If it is already in the hash table, make sure it's removed. 
+       * (if it's not, this call won't do anything. */
+      get_and_remove_handle_proc_mapping((HANDLE)*child_handle);
+    }
+  free_process(proc);
+  return FALSE;
 }
 
 gboolean
@@ -1160,7 +807,18 @@
 void
 g_spawn_close_pid (GPid pid)
 {
-    CloseHandle (pid);
+  Process *proc;
+
+  proc = get_and_remove_handle_proc_mapping((HANDLE)pid);
+  if(proc)
+  {
+    free_process(proc);
+  }
+  else
+  {
+    g_warning("Couldn't find the process state associated with "
+      "the given handle. This probably results in a memory leak.");
+  }
 }
 
 /* Binary compatibility versions that take system codepage pathnames,
@@ -1428,7 +1086,195 @@
   return retval;
 }
 
-#endif /* !GSPAWN_HELPER */
+static gboolean
+proc_get_exit_status(Process *proc,gint *exit_status,GError **error)
+{
+  DWORD exit_code;
+  DWORD res;
 
+  if(error)
+    *error = 0;
+  
+  res = GetExitCodeProcess(proc->proc_info.hProcess,&exit_code);
+  if(!res)
+    {
+      set_error_win32(error,"GetExitCodeProcess failed: %s",GetLastError());
+      return FALSE;
+    }
+
+  // mangle it into the unix format.
+  *exit_status = exit_code;
+
+  return TRUE;
+}
+
+static Process*
+alloc_process()
+{
+  Process *ret = 0;
+  
+  ret = (Process*)g_malloc(sizeof(Process));
+  memset(ret,0,sizeof(Process));
+
+  ret->dev_null = -1;
+
+  return ret;
+}
+
+static void
+free_process(Process *proc)
+{
+  if(!proc)
+    return;
+
+  CloseHandle(proc->proc_info.hProcess);
+  CloseHandle(proc->proc_info.hThread);
+
+  free_pipe(proc->stdin_pipe);
+  free_pipe(proc->stdout_pipe);
+  free_pipe(proc->stderr_pipe);
+
+  close_and_invalidate(&proc->dev_null);
+
+  g_free(proc);
+}
+
+static Pipe*
+create_pipe(gboolean is_input_pipe,GError **error)
+{
+  Pipe *ret = 0;
+  gint *inheritable_pipe;
+  gint inheritable;
+  
+  ret = g_malloc(sizeof(Pipe));
+  ret->read_pipe = -1;
+  ret->write_pipe = -1;
+
+  if (_pipe ((gint*)&ret->read_pipe, 4096, _O_BINARY|_O_NOINHERIT) < 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));
+      goto error_handler;
+    }
+  
+  if(is_input_pipe)
+    inheritable_pipe = &ret->read_pipe;
+  else
+    inheritable_pipe = &ret->write_pipe;
+    
+  inheritable = dup(*inheritable_pipe);
+  if(inheritable == -1)
+    {
+      g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,
+                   _("Failed to create pipe for communicating with child process (%s)"),
+                   g_strerror (errno));
+      goto error_handler;
+    }
+
+  close(*inheritable_pipe);
+  *inheritable_pipe = inheritable;
+  
+  return ret;
+
+error_handler:
+  free_pipe(ret);
+  return NULL;
+}
+
+static void
+free_pipe(Pipe *pipe)
+{
+  if(!pipe)
+    return;
+
+  close_and_invalidate(&pipe->read_pipe);
+  close_and_invalidate(&pipe->write_pipe);
+  
+  g_free(pipe);
+}
+
+static gint
+create_dev_null()
+{
+  return open("NUL", _O_RDWR);
+}
+
+static guint
+handle_hash_func(gconstpointer key)
+{
+  return (guint)(intptr_t)((HANDLE*)key);
+}
+
+static gboolean
+handle_equal_func(gconstpointer a,gconstpointer b)
+{
+  return (HANDLE*)a == (HANDLE*)b;
+}
+
+static void
+add_handle_proc_mapping(HANDLE handle,Process *proc)
+{
+  g_static_mutex_lock(&procs_by_handle_lock);
+
+  if(!procs_by_handle)
+    {
+      procs_by_handle = g_hash_table_new(handle_hash_func,handle_equal_func);
+    }
+
+  g_hash_table_insert(procs_by_handle,handle,proc);
+
+  g_static_mutex_unlock(&procs_by_handle_lock);
+}
+
+static Process*
+get_and_remove_handle_proc_mapping(HANDLE handle)
+{
+  Process *ret;
+
+  g_static_mutex_lock(&procs_by_handle_lock);
+
+  ret = (Process*)g_hash_table_lookup(procs_by_handle,handle);
+
+  if(ret)
+    {
+      gboolean remove_res = g_hash_table_remove(procs_by_handle,handle);
+      g_assert(remove_res);
+
+      if(g_hash_table_size(procs_by_handle) == 0)
+        {
+          g_hash_table_unref(procs_by_handle);
+          procs_by_handle = NULL;
+        }
+    }
+
+  g_static_mutex_unlock(&procs_by_handle_lock);
+
+  return ret;
+}
+
+static void
+set_error_win32(GError **error,const gchar *format,DWORD win32_error_code)
+{
+  LPWSTR win32_msg = NULL;
+  gchar full_msg[1024];
+
+  FormatMessage(
+    FORMAT_MESSAGE_ALLOCATE_BUFFER | 
+    FORMAT_MESSAGE_FROM_SYSTEM |
+    FORMAT_MESSAGE_IGNORE_INSERTS,
+    NULL,
+    win32_error_code,
+    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+    (LPTSTR) &win32_msg,
+    0, NULL );
+
+  g_snprintf(full_msg,1024,format,win32_msg);
+
+  LocalFree(win32_msg);
+    
+  g_set_error (error, G_SPAWN_ERROR, G_SPAWN_ERROR_FAILED,full_msg);
+}
+
 #define __G_SPAWN_C__
 #include "galiasdef.c"
