OSDN Git Service

* autoload.cc (SetParent): Add new import.
authorcgf <cgf>
Sat, 4 Jul 2009 23:51:09 +0000 (23:51 +0000)
committercgf <cgf>
Sat, 4 Jul 2009 23:51:09 +0000 (23:51 +0000)
* fhandler.h (fhandler_console::create_invisible_console): Declare new
function.
(create_invisible_console_workaround): Ditto.
* fhandler_console.cc (fhandler_console::create_invisible_console): Define new
function.
(create_invisible_console_workaround): Ditto.  Add too much code to deal with
broken Windows 7.  Use a helper app to start an invisible console window.
(fhandler_console::need_invisible): Reorganize to use helper functions to
create invisible console.
* spawn.cc (spawn_guts): Avoid zeroing already zeroed fields in si.

winsup/cygwin/ChangeLog
winsup/cygwin/autoload.cc
winsup/cygwin/fhandler.h
winsup/cygwin/fhandler_console.cc
winsup/cygwin/spawn.cc

index b582456..6fa0057 100644 (file)
@@ -1,3 +1,19 @@
+2009-07-04  Christopher Faylor  <me+cygwin@cgf.cx>
+
+       * autoload.cc (SetParent): Add new import.
+       * fhandler.h (fhandler_console::create_invisible_console): Declare new
+       function.
+       (create_invisible_console_workaround): Ditto.
+       * fhandler_console.cc (fhandler_console::create_invisible_console):
+       Define new function.
+       (create_invisible_console_workaround): Ditto.  Add too much code to
+       deal with broken Windows 7.  Use a helper app to start an invisible
+       console window.
+       (fhandler_console::need_invisible): Reorganize to use helper functions
+       to create invisible console.
+
+       * spawn.cc (spawn_guts): Avoid zeroing already zeroed fields in si.
+
 2009-07-04  Dave Korn  <dave.korn.cygwin@gmail.com>
 
        * autoload.cc (AttachConsole):  Correct size of args.
index bf6c2f1..82b6c3a 100644 (file)
@@ -362,6 +362,7 @@ LoadDLLfunc (RegisterClassA, 4, user32)
 LoadDLLfunc (RegisterClipboardFormatA, 4, user32)
 LoadDLLfunc (SendMessageA, 16, user32)
 LoadDLLfunc (SetClipboardData, 8, user32)
+LoadDLLfunc (SetParent, 8, user32)
 LoadDLLfunc (SetThreadDesktop, 4, user32)
 LoadDLLfunc (SetProcessWindowStation, 4, user32)
 LoadDLLfuncEx (ShowWindowAsync, 8, user32, 1)
index db55e2c..4659241 100644 (file)
@@ -950,6 +950,8 @@ class fhandler_console: public fhandler_termios
   int igncr_enabled ();
   int input_tcsetattr (int a, const struct termios *t);
   void set_cursor_maybe ();
+  static bool create_invisible_console (HWINSTA);
+  static bool create_invisible_console_workaround ();
 
  public:
   fhandler_console ();
index f8a930c..9ab1496 100644 (file)
@@ -1935,7 +1935,126 @@ fhandler_console::fixup_after_fork_exec (bool execing)
 bool NO_COPY fhandler_console::invisible_console;
 
 // #define WINSTA_ACCESS (WINSTA_READATTRIBUTES | STANDARD_RIGHTS_READ | STANDARD_RIGHTS_WRITE | WINSTA_CREATEDESKTOP | WINSTA_EXITWINDOWS)
-#define WINSTA_ACCESS STANDARD_RIGHTS_READ
+#define WINSTA_ACCESS WINSTA_ALL_ACCESS
+
+/* Create a console in an invisible workstation.  This should work
+   in all versions of Windows NT except Windows 7 (so far). */
+bool
+fhandler_console::create_invisible_console (HWINSTA horig)
+{
+  HWINSTA h = CreateWindowStationW (NULL, 0, WINSTA_ACCESS, NULL);
+  termios_printf ("%p = CreateWindowStation(NULL), %E", h);
+  BOOL b;
+  if (h)
+    {
+      b = SetProcessWindowStation (h);
+      termios_printf ("SetProcessWindowStation %d, %E", b);
+    }
+  b = AllocConsole (); /* will cause flashing if CreateWindowStation
+                          failed */
+  if (!h)
+    SetParent (GetConsoleWindow (), HWND_MESSAGE);
+  if (horig && h && h != horig && SetProcessWindowStation (horig))
+    CloseWindowStation (h);
+  termios_printf ("%d = AllocConsole (), %E", b);
+  invisible_console = true;
+  return b;
+}
+
+/* Ugly workaround for Windows 7.
+
+   First try to just attach to any console which may have started this
+   app.  If that works use this as our "invisible console".
+
+   This will fail if not started from the command prompt.  In that case, start
+   a dummy console application in a hidden state so that we can use its console
+   as our invisible console.  This probably works everywhere but process
+   creation is slow and to be avoided if possible so the workstation method
+   is vastly preferred.
+
+   FIXME: This is not completely thread-safe since it creates two inheritable
+   handles which are known only to this function.  If another thread starts
+   a process the new process will inherit these handles.  However, since this
+   function is currently only called at startup and during exec, it shouldn't
+   be a big deal.  */
+bool
+fhandler_console::create_invisible_console_workaround ()
+{
+  if (!AttachConsole (-1))
+    {
+      bool taskbar;
+      DWORD err = GetLastError ();
+      path_conv helper ("/bin/cygwin-console-helper.exe");
+      HANDLE hello = NULL;
+      HANDLE goodbye = NULL;
+      /* If err == ERROR_PROC_FOUND then this method won't work.  But that's
+        ok.  The workstation method should work ok when AttachConsole doesn't
+        work.
+
+        If the helper doesn't exist or we can't create event handles then we
+        can't use this method. */
+      if (err == ERROR_PROC_NOT_FOUND || !helper.exists ()
+         || !(hello = CreateEvent (&sec_none, true, false, NULL))
+         || !(goodbye = CreateEvent (&sec_none, true, false, NULL)))
+       {
+         AllocConsole ();      /* This is just sanity check code.  We should
+                                  never actually hit here unless we're running
+                                  in an environment which lacks the helper
+                                  app. */
+         taskbar = true;
+       }
+      else
+       {
+         STARTUPINFOW si = {};
+         PROCESS_INFORMATION pi;
+         size_t len = helper.get_wide_win32_path_len ();
+         WCHAR cmd[len + (2 * strlen ("0xffffffff")) + 1];
+         WCHAR title[] = L"invisible cygwin console";
+
+         helper.get_wide_win32_path (cmd);
+         __small_swprintf (cmd + len, L" %p %p", hello, goodbye);
+
+         si.cb = sizeof (si);
+         si.dwFlags = STARTF_USESHOWWINDOW;
+         si.wShowWindow = SW_HIDE;
+         si.lpTitle = title;
+
+         /* Create a new hidden process.  Use the two event handles as
+            argv[1] and argv[2]. */
+         BOOL x = CreateProcessW (NULL, cmd, &sec_none_nih, &sec_none_nih, true,
+                                  CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
+         if (x)
+           {
+             CloseHandle (pi.hProcess);        /* Don't need */
+             CloseHandle (pi.hThread);         /*  these.    */
+           }
+         taskbar = false;
+         /* Wait for subprocess to indicate that it is live.  This may not
+            actually be needed but it's hard to say since it is possible that
+            there will be no console for a brief time after the process
+            returns and there is no easy way to determine if/when this happens
+            in Windows.  So play it safe. */
+         if (!x || (WaitForSingleObject (hello, 10000) != WAIT_OBJECT_0)
+             || !AttachConsole (pi.dwProcessId))
+           AllocConsole ();    /* Oh well.  Watch the flash. */
+       }
+
+      if (!taskbar)
+       /* Setting the owner of the console window to HWND_MESSAGE seems to
+          hide it from the taskbar.  Don't know if this method is faster than
+          calling ShowWindowAsync but it should guarantee no taskbar presence
+          for the hidden console. */
+       SetParent (GetConsoleWindow (), HWND_MESSAGE);
+      if (hello)
+       CloseHandle (hello);
+      if (goodbye)
+       {
+         SetEvent (goodbye);   /* Tell helper process it's ok to exit. */
+         CloseHandle (goodbye);
+       }
+    }
+  return invisible_console = true;
+}
 
 bool
 fhandler_console::need_invisible ()
@@ -1945,7 +2064,7 @@ fhandler_console::need_invisible ()
     invisible_console = false;
   else
     {
-      HWINSTA h, horig;
+      HWINSTA h;
       /* The intent here is to allocate an "invisible" console if we have no
         controlling tty or to reuse the existing console if we already have
         a tty.  So, first get the old windows station.  If there is no controlling
@@ -1965,51 +2084,23 @@ fhandler_console::need_invisible ()
         - Non-displaying of characters in rxvt or xemacs if you start a
           process using setsid: bash -lc "setsid rxvt".  */
 
-      h = horig = GetProcessWindowStation ();
+      h = GetProcessWindowStation ();
 
       USEROBJECTFLAGS oi;
       DWORD len;
-      if (!horig
-         || !GetUserObjectInformationW (horig, UOI_FLAGS, &oi, sizeof (oi), &len)
+      if (!h
+         || !GetUserObjectInformationW (h, UOI_FLAGS, &oi, sizeof (oi), &len)
          || !(oi.dwFlags & WSF_VISIBLE))
        {
          b = true;
          debug_printf ("window station is not visible");
+         AllocConsole ();
          invisible_console = true;
        }
-      /* Band-aid for Windows 7.  AllocConsole is broken on W7 in that it
-         doesn't allocate the console in the hidden, active WindowStation,
-        but instead on the WindowStation on which the application has
-        originally been started on.  This effectively disallows to create
-        a hidden console.
-        So what we do now is this.  First we try to attach to an existing
-        console window of the parent process.  If that doesn't work, we
-        skip generating a hidden WindowStation entirely.  After creating
-        the new console, we hide it.  Unfortunately it's still visible in
-        the taskbar.  Hopefully this will be fixed in SP1... */
-      else if (!wincap.has_broken_alloc_console () || !AttachConsole (-1))
-       {
-         if (myself->ctty != TTY_CONSOLE
-             && !wincap.has_broken_alloc_console ())
-           {
-             h = CreateWindowStationW (NULL, 0, WINSTA_ACCESS, NULL);
-             termios_printf ("%p = CreateWindowStation(NULL), %E", h);
-             if (h)
-               {
-                 b = SetProcessWindowStation (h);
-                 termios_printf ("SetProcessWindowStation %d, %E", b);
-               }
-           }
-         b = AllocConsole ();  /* will cause flashing if CreateWindowStation
-                                  failed */
-         if (b && wincap.has_broken_alloc_console ())
-           ShowWindowAsync (GetConsoleWindow (), SW_HIDE);
-         debug_printf ("h %p, horig %p, flags %p", h, horig, oi.dwFlags);
-         if (horig && h && h != horig && SetProcessWindowStation (horig))
-           CloseWindowStation (h);
-         termios_printf ("%d = AllocConsole (), %E", b);
-         invisible_console = true;
-       }
+      else if (wincap.has_broken_alloc_console ())
+       b = create_invisible_console_workaround ();
+      else
+       b = create_invisible_console (h);
     }
 
   debug_printf ("invisible_console %d", invisible_console);
index be3c66a..e292dec 100644 (file)
@@ -425,8 +425,6 @@ spawn_guts (const char *prog_arg, const char *const *argv,
   PROCESS_INFORMATION pi;
   pi.hProcess = pi.hThread = NULL;
   pi.dwProcessId = pi.dwThreadId = 0;
-  si.lpReserved = NULL;
-  si.lpDesktop = NULL;
 
   /* Set up needed handles for stdio */
   si.dwFlags = STARTF_USESTDHANDLES;