修复 gspawn-win32-helper 获取参数失败

使用 MinGW 中的 GCC 4.8.1 编译 GLib 2.38.1,执行 valac 编译会报如下错误:

ERROR:gspawn-win32-helper.c:226:WinMain: assertion failed: (__argc >=
ARG_COUNT)

阅读代码 glib/gspawn-win32-helper.c 发现此程序依赖于 __argc 和 __argv 全局变量进行参数访问,但有相关说明这两个全局变量是由 Microsoft 编译和 msvcrt.dll 提供,调试发现使用 MinGW 编译的 gspawn-win32-helper.exe 无论是否在命令行下进行都会导致上述错误,而且打印发现 __argc 永远是 0。

查看 /mingw/include/stdlib.h 会发现还有一组访问参数相关的全局变量 _argc 和 _argv,验证后是可行的。解决问题,下面是补丁文件:

From ebe6e6989b49bd119acc2d29b2f8ad0b6bb03313 Mon Sep 17 00:00:00 2001
From: Heiher <root@heiher.info>
Date: Wed, 30 Oct 2013 22:17:29 +0800
Subject: [PATCH] glib/gspawn-win32-helper.c: Fix get arguments for GCC
 compiler.
 
---
 glib/gspawn-win32-helper.c | 61 ++++++++++++++++++++++++++--------------------
 1 file changed, 34 insertions(+), 27 deletions(-)
 
diff --git a/glib/gspawn-win32-helper.c b/glib/gspawn-win32-helper.c
index 7896941..8d2bcbd 100644
--- a/glib/gspawn-win32-helper.c
+++ b/glib/gspawn-win32-helper.c
@@ -222,28 +222,35 @@ main (int ignored_argc, char **ignored_argv)
   _CrtSetReportMode(_CRT_ASSERT, 0);
 #endif
 
+#ifdef _MSC_VER // for Microsoft compiler
+#define __ARGC __argc
+#define __ARGV __argv
+#else           // for GCC compiler (MinGW)
+#define __ARGC _argc
+#define __ARGV _argv
+#endif
 
-  g_assert (__argc >= ARG_COUNT);
+  g_assert (__ARGC >= ARG_COUNT);
 
   /* Fetch the wide-char argument vector */
   __wgetmainargs (&argc, &wargv, &wenvp, 0, &si);
 
-  /* We still have the system codepage args in __argv. We can look
+  /* We still have the system codepage args in __ARGV. We can look
    * at the first args in which gspawn-win32.c passes us flags and
-   * fd numbers in __argv, as we know those are just ASCII anyway.
+   * fd numbers in __ARGV, as we know those are just ASCII anyway.
    */
-  g_assert (argc == __argc);
+  g_assert (argc == __ARGC);
 
   /* argv[ARG_CHILD_ERR_REPORT] is the file descriptor number onto
    * which write error messages.
    */
-  child_err_report_fd = atoi (__argv[ARG_CHILD_ERR_REPORT]);
+  child_err_report_fd = atoi (__ARGV[ARG_CHILD_ERR_REPORT]);
 
   /* Hack to implement G_SPAWN_FILE_AND_ARGV_ZERO. If
    * argv[ARG_CHILD_ERR_REPORT] is suffixed with a '#' it means we get
    * the program to run and its argv[0] separately.
    */
-  if (__argv[ARG_CHILD_ERR_REPORT][strlen (__argv[ARG_CHILD_ERR_REPORT]) - 1] == '#')
+  if (__ARGV[ARG_CHILD_ERR_REPORT][strlen (__ARGV[ARG_CHILD_ERR_REPORT]) - 1] == '#')
     argv_zero_offset++;
 
   /* argv[ARG_HELPER_SYNC] is the file descriptor number we read a
@@ -252,16 +259,16 @@ main (int ignored_argc, char **ignored_argv)
    * duplicate the process handle we sent it. Duplicating a handle
    * from another process works only if that other process exists.
    */
-  helper_sync_fd = atoi (__argv[ARG_HELPER_SYNC]);
+  helper_sync_fd = atoi (__ARGV[ARG_HELPER_SYNC]);
 
   /* argv[ARG_STDIN..ARG_STDERR] are the file descriptor numbers that
    * should be dup2'd to 0, 1 and 2. '-' if the corresponding fd
    * should be left alone, and 'z' if it should be connected to the
    * bit bucket NUL:.
    */
-  if (__argv[ARG_STDIN][0] == '-')
+  if (__ARGV[ARG_STDIN][0] == '-')
     ; /* Nothing */
-  else if (__argv[ARG_STDIN][0] == 'z')
+  else if (__ARGV[ARG_STDIN][0] == 'z')
     {
       fd = open ("NUL:", O_RDONLY);
       if (fd != 0)
@@ -272,7 +279,7 @@ main (int ignored_argc, char **ignored_argv)
     }
   else
     {
-      fd = atoi (__argv[ARG_STDIN]);
+      fd = atoi (__ARGV[ARG_STDIN]);
       if (fd != 0)
 	{
 	  dup2 (fd, 0);
@@ -280,9 +287,9 @@ main (int ignored_argc, char **ignored_argv)
 	}
     }
 
-  if (__argv[ARG_STDOUT][0] == '-')
+  if (__ARGV[ARG_STDOUT][0] == '-')
     ; /* Nothing */
-  else if (__argv[ARG_STDOUT][0] == 'z')
+  else if (__ARGV[ARG_STDOUT][0] == 'z')
     {
       fd = open ("NUL:", O_WRONLY);
       if (fd != 1)
@@ -293,7 +300,7 @@ main (int ignored_argc, char **ignored_argv)
     }
   else
     {
-      fd = atoi (__argv[ARG_STDOUT]);
+      fd = atoi (__ARGV[ARG_STDOUT]);
       if (fd != 1)
 	{
 	  dup2 (fd, 1);
@@ -301,9 +308,9 @@ main (int ignored_argc, char **ignored_argv)
 	}
     }
 
-  if (__argv[ARG_STDERR][0] == '-')
+  if (__ARGV[ARG_STDERR][0] == '-')
     ; /* Nothing */
-  else if (__argv[ARG_STDERR][0] == 'z')
+  else if (__ARGV[ARG_STDERR][0] == 'z')
     {
       fd = open ("NUL:", O_WRONLY);
       if (fd != 2)
@@ -314,7 +321,7 @@ main (int ignored_argc, char **ignored_argv)
     }
   else
     {
-      fd = atoi (__argv[ARG_STDERR]);
+      fd = atoi (__ARGV[ARG_STDERR]);
       if (fd != 2)
 	{
 	  dup2 (fd, 2);
@@ -322,19 +329,19 @@ main (int ignored_argc, char **ignored_argv)
 	}
     }
 
-  /* __argv[ARG_WORKING_DIRECTORY] is the directory in which to run the
+  /* __ARGV[ARG_WORKING_DIRECTORY] is the directory in which to run the
    * process.  If "-", don't change directory.
    */
-  if (__argv[ARG_WORKING_DIRECTORY][0] == '-' &&
-      __argv[ARG_WORKING_DIRECTORY][1] == 0)
+  if (__ARGV[ARG_WORKING_DIRECTORY][0] == '-' &&
+      __ARGV[ARG_WORKING_DIRECTORY][1] == 0)
     ; /* Nothing */
   else if (_wchdir (wargv[ARG_WORKING_DIRECTORY]) < 0)
     write_err_and_exit (child_err_report_fd, CHILD_CHDIR_FAILED);
 
-  /* __argv[ARG_CLOSE_DESCRIPTORS] is "y" if file descriptors from 3
+  /* __ARGV[ARG_CLOSE_DESCRIPTORS] is "y" if file descriptors from 3
    *  upwards should be closed
    */
-  if (__argv[ARG_CLOSE_DESCRIPTORS][0] == 'y')
+  if (__ARGV[ARG_CLOSE_DESCRIPTORS][0] == 'y')
     for (i = 3; i < 1000; i++)	/* FIXME real limit? */
       if (i != child_err_report_fd && i != helper_sync_fd)
         if (_get_osfhandle (i) != -1)
@@ -360,16 +367,16 @@ main (int ignored_argc, char **ignored_argv)
   child_err_report_fd = dup_noninherited (child_err_report_fd, _O_WRONLY);
   helper_sync_fd = dup_noninherited (helper_sync_fd, _O_RDONLY);
 
-  /* __argv[ARG_WAIT] is "w" to wait for the program to exit */
-  if (__argv[ARG_WAIT][0] == 'w')
+  /* __ARGV[ARG_WAIT] is "w" to wait for the program to exit */
+  if (__ARGV[ARG_WAIT][0] == 'w')
     mode = P_WAIT;
   else
     mode = P_NOWAIT;
 
-  /* __argv[ARG_USE_PATH] is "y" to use PATH, otherwise not */
+  /* __ARGV[ARG_USE_PATH] is "y" to use PATH, otherwise not */
 
-  /* __argv[ARG_PROGRAM] is executable file to run,
-   * __argv[argv_zero_offset]... is its argv. argv_zero_offset equals
+  /* __ARGV[ARG_PROGRAM] is executable file to run,
+   * __ARGV[argv_zero_offset]... is its argv. argv_zero_offset equals
    * ARG_PROGRAM unless G_SPAWN_FILE_AND_ARGV_ZERO was used, in which
    * case we have a separate executable name and argv[0].
    */
@@ -379,7 +386,7 @@ main (int ignored_argc, char **ignored_argv)
    */
   protect_wargv (wargv + argv_zero_offset, &new_wargv);
 
-  if (__argv[ARG_USE_PATH][0] == 'y')
+  if (__ARGV[ARG_USE_PATH][0] == 'y')
     handle = _wspawnvp (mode, wargv[ARG_PROGRAM], (const wchar_t **) new_wargv);
   else
     handle = _wspawnv (mode, wargv[ARG_PROGRAM], (const wchar_t **) new_wargv);
-- 
1.8.4.2

Over!