For me, fails() lives up to its name in Windows 7, but it doesn't fail in Windows 10. It shouldn't fail in Windows 8, either.
In Windows 8+ the console interface is implemented using a kernel device. Console handles reference virtual files on the ConDrv device, such as Connect, Input, and Output. The example doesn't fail because the I/O handles (Input and Output) are valid even if the main console handle (Connect) has been closed by calling FreeConsole.
OTOH, prior to Windows 8, console I/O handles don't reference kernel objects. You may have noticed that the old API tags the values by setting the lower 2 bits (e.g. 3, 7, 11). This enables redirecting actions on console I/O handles to functions in the console host process (i.e. conhost.exe in Win 7, and csrss.exe in XP/Vista). For example, for a regular kernel object, the DuplicateHandle API is serviced by the system call NtDuplicateObject. But for a console handle, Windows instead sends the request to the console LPC port, to be dispatched to SrvDuplicateHandle in the console. Obviously this can't work after the client has called FreeConsole.
Prior to Windows 8, this invalid-handle error can also be the result of running pythonw.exe from a console application such as python.exe or cmd.exe. See issue 3905. In this case Windows is copying the values of the parent's standard handles into the pythonw.exe process parameters, but they're meaningless values since pythonw.exe doesn't attach to a console automatically. (You could of course manually call AllocConsole or AttachConsole.) The new implementation in Windows 8 and 10 is smart enough to initialize the standard handles to 0 in this case. Thus in Windows 10 Terry's example in msg220218 doesn't raise and exception, and p.stdout.read() == b'32\r\n'.
A workaround for Python 3.4+ on older Windows versions would be to check os.get_handle_inheritable, which calls WinAPI GetHandleInformation [1]:
ERROR_INVALID_HANDLE = 0x0006
if stdin is None:
p2cread = _winapi.GetStdHandle(_winapi.STD_INPUT_HANDLE)
try:
os.get_handle_inheritable(p2cread)
except OSError as e:
if e.winerror != ERROR_INVALID_HANDLE:
raise
p2cread = None
if p2cread is None:
p2cread, _ = _winapi.CreatePipe(None, 0)
p2cread = Handle(p2cread)
_winapi.CloseHandle(_)
For 2.7, GetHandleInformation could be added to _subprocess. But then it may as well be added to Python 3's _winapi as well.
[1]: https://msdn.microsoft.com/en-us/library/ms724329
|