On Windows 8, we have an issue with FreeConsole. It seems to close the stdio handles, without shutting the file streams.
This may be a Windows 8 problem, or it could be that I simply don't understand the (totally absurd) way the Windows console/GUI app subsystem does things.
What's going on?
Minimal example below. Tested with compilers: VS2005, VS2013, VS2017, using statically linked CRT.
#include <windows.h>
#include <io.h>
#include <stdio.h>
static void testHandle(FILE* file) {
HANDLE h = (HANDLE)_get_osfhandle(fileno(file));
DWORD flags;
if (!GetHandleInformation(h, &flags)) {
MessageBoxA(0, "Bogus handle!!", "TITLE", MB_OK);
}
}
int main(int argc, char** argv)
{
freopen("NUL", "wb", stdout); // Demonstrate the issue with NUL
// Leave stderr as it is, to demonstrate the issue with handles
// to the console device.
FreeConsole();
testHandle(stdout);
testHandle(stderr);
}
issue caused by fact that prior Windows 8 standard (not redirected) console handles (that returned by GetStdHandle) where actually pseudohandles, wich values doesnt instersect with other kernel object handles, so writing to that pseudohandle after it being 'closed' by FreeConsole always fails. In Win8 MS changed something inside, so GetStdHandle returns normal kernel object handle that refers to console subsystem driver object (actually that driver also appeared only in Win8). So FreeConsole closes that handle. The most funny thing that CRT does GetStdHandle on startup and saves returned value somewhere inside and uses wherever use called C functions that access std::in/out/err. Since FreeConsole closed that handle, and its not a special pseudohandle value anymore - same handle value can be reused by any other opened kernel object handle, and you will be lucky if it will not be file, pipe, or socket cause in this case all your debug outpout will go there:)
After disassembling the code for FreeConsole on different Windows versions, I worked out the cause of the problem.
FreeConsole is a remarkably unsubtle function! I really does close a load of handles for you, even if it doesn't "own" those handles (eg HANDLEs owned by stdio functions).
And, the behaviour is different in Windows 7 and 8, and changed again in 10.
Here's the dilemma when coming up with a fix:
close(1)
orfreopen(stdout)
or whatever you like, but if there is an open file descriptor that refers to the console, CloseHandle will be called on it, if you want to switch stdout to a new NUL handle after FreeConsole.GetStdHandle(STD_OUTPUT_HANDLE)
). And, if you call FreeConsole first, there's no way to fix up the stdio objects without causing them to do an invalid call to CloseHandle.By elimination, I conclude that the only solution is to use an undocumented function, if the public ones just won't work.