Nic*_*son 6 visual-studio-2005 msvcrt windows-8
在Windows 8上,我们遇到了FreeConsole的问题.它似乎关闭了stdio句柄,而没有关闭文件流.
这可能是Windows 8的问题,或者可能是我根本不理解Windows控制台/ GUI应用程序子系统执行操作的方式(完全荒谬).
这是怎么回事?
下面的最小例子.使用静态链接的CRT对编译器进行测试:VS2005,VS2013,VS2017.
#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);
}
Run Code Online (Sandbox Code Playgroud)
小智 5
事先由Windows 8标准(未重定向)控制台处理(由GetStdHandle返回)的事实引起的问题,其中实际的伪句柄,其值不与其他内核对象句柄相关,因此在FreeConsole"关闭"之后写入该伪句柄始终会失败.在Win8 MS中改变了一些内容,因此GetStdHandle返回引用控制台子系统驱动程序对象的正常内核对象句柄(实际上该驱动程序也只出现在Win8中).所以FreeConsole关闭了这个句柄.最有趣的事情是CRT在启动时执行GetStdHandle并在内部保存返回值,并在任何使用称为访问std :: in/out/err的C函数的地方使用.由于FreeConsole关闭了该句柄,并且它不再是一个特殊的伪句柄值 - 任何其他打开的内核对象句柄都可以重用相同的句柄值,如果在这种情况下它不是文件,管道或套接字原因你将会很幸运你的调试输出将去那里:)
在对不同Windows版本上的FreeConsole代码进行反汇编后,我找出了问题的原因。
FreeConsole 是一个非常简单的功能!我确实为您关闭了很多句柄,即使它不“拥有”这些句柄(例如 stdio 函数拥有的句柄)。
而且,Windows 7 和 8 中的行为有所不同,并且在 10 中再次发生变化。
在提出解决方案时,遇到了这样的困境:
close(1)或freopen(stdout)任何您喜欢的方法,但是如果有一个引用控制台的打开文件描述符,如果您想在 FreeConsole 之后将 stdout 切换到新的 NUL 句柄,则将在其上调用 CloseHandle。GetStdHandle(STD_OUTPUT_HANDLE))。而且,如果您首先调用 FreeConsole,则无法修复 stdio 对象而不导致它们对 CloseHandle 进行无效调用。通过排除,我得出的结论是,如果公共函数不起作用,唯一的解决方案是使用未记录的函数。
// The undocumented bit!
extern "C" int __cdecl _free_osfhnd(int const fh);
static HANDLE closeFdButNotHandle(int fd) {
HANDLE h = (HANDLE)_get_osfhandle(fd);
_free_osfhnd(fd); // Prevent CloseHandle happening in close()
close(fd);
return h;
}
static bool valid(HANDLE h) {
SetLastError(0);
return GetFileType(h) != FILE_TYPE_UNKNOWN || GetLastError() == 0;
}
static void openNull(int fd, DWORD flags) {
int newFd;
// Yet another Microsoft bug! (I've reported four in this code...)
// They have confirmed a bug in dup2 in Visual Studio 2013, fixed
// in Visual Studio 2017. If dup2 is called with fd == newFd, the
// CRT lock is corrupted, hence the check here before calling dup2.
if (!_tsopen_s(&newFd, _T("NUL"), flags, _SH_DENYNO, 0) &&
fd != newFd)
dup2(newFd, fd);
if (fd != newFd) close(newFd);
}
void doFreeConsole() {
// stderr, stdin are similar - left to the reader. You probably
// also want to add code (as we have) to detect when the handle
// is FILE_TYPE_DISK/FILE_TYPE_PIPE and leave the stdio FILE
// alone if it's actually pointing to disk/pipe.
HANDLE stdoutHandle = closeFdButNotHandle(fileno(stdout));
FreeConsole(); // error checking left to the reader
// If FreeConsole *didn't* close the handle then do so now.
// Has a race condition, but all of this code does so hey.
if (valid(stdoutHandle)) CloseHandle(stdoutHandle);
openNull(stdoutRestore, _O_BINARY | _O_RDONLY);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2027 次 |
| 最近记录: |