* 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.
This commit is contained in:
Christopher Faylor 2009-07-04 23:51:10 +00:00
parent 38a58dd13c
commit 01d8a2dfd6
5 changed files with 148 additions and 40 deletions

View 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.

View 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)

View 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 ();

View 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);

View 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;