[英]Why does reopening stdout to a newly-allocated console occasionally fail?
Consider this program:考虑这个程序:
#include <windows.h>
#include <stdio.h>
#pragma comment(linker, "/SUBSYSTEM:CONSOLE")
int main(void) {
if (!FreeConsole()) {
MessageBoxA(NULL, "FreeConsole failed", NULL, MB_ICONHAND);
return 1;
}
if (!AllocConsole()) {
MessageBoxA(NULL, "AllocConsole failed", NULL, MB_ICONHAND);
return 1;
}
FILE* newstdout;
if (freopen_s(&newstdout, "CONOUT$", "w", stdout)) {
MessageBoxA(NULL, "freopen_s failed", NULL, MB_ICONHAND);
return 1;
}
if (newstdout != stdout) {
MessageBoxA(NULL, "freopen_s did something wonky", NULL, MB_ICONHAND);
return 1;
}
puts("1");
MessageBoxA(NULL, "Look for 1 then press OK", NULL, 0);
return 0;
}
Steps to reproduce: create a new empty C/C++ project in Visual Studio 2019, go to Project Properties -> Configuration Properties -> Linker -> System and change SubSystem to Not Set, then add the above code to the project in a new file called Source.c
.重现步骤:在Visual Studio 2019中新建一个空的C/C++项目,进入项目属性->配置属性->链接器->系统,将子系统更改为未设置,然后将上述代码添加到新文件中的项目中称为
Source.c
。
Most of the time I run it, it opens a new console with the number 1 in it, like it's supposed to, but sometimes it fails.大多数时候我运行它,它会打开一个带有数字 1 的新控制台,就像它应该的那样,但有时它会失败。 If I used "Start Without Debugging" (Ctrl+F5) to run it, it fails about 1 run out of 30, with my "freopen_s failed" message.
如果我使用“在不调试的情况下启动”(Ctrl+F5)来运行它,它会在 30 次运行中失败大约 1 次,并显示我的“freopen_s failed”消息。 If I used "Start Debugging" (F5) to run it, it also fails about 1 run out of 10, with
_NtClose
throwing an exception that says "An invalid handle was specified", with a stack trace pointing at my call to freopen_s
.如果我使用“开始调试”(F5) 来运行它,它也会失败大约 10 次,
_NtClose
抛出一个异常,上面写着“指定了无效的句柄”,堆栈跟踪指向我对freopen_s
的调用。
Why does this intermittent failure happen?为什么会发生这种间歇性故障? Am I breaking one of the rules of the Windows API, either in the C code or in the project configuration?
我是否在 C 代码或项目配置中违反了 Windows API 的规则之一?
Things I tried so far that didn't change the behavior:到目前为止我尝试过的并没有改变行为的事情:
#pragma
#pragma
删除行CON
in place of CONOUT$
CON
代替CONOUT$
freopen_s
ing stdout
to NUL
before calling FreeConsole
freopen_s
之前FreeConsole
将stdout
转换为NUL
This is messed up, it's not your fault.搞砸了,这不是你的错。
FreeConsole()
proceeds to destroy the old console and take down the handle with it. FreeConsole()
继续销毁旧控制台并用它取下手柄。 This invalidates stdout
asynchronously.这会使
stdout
异步无效。
You really want to do something like this.你真的想做这样的事情。 Note that you really do need to do
stdin
and stderr
as well as stdout
or bad things can happen later.请注意,您确实需要执行
stdin
和stderr
以及stdout
,否则以后可能会发生不好的事情。
// Change the handle of stdout so that we can destroy the old one.
FILE* newstdout;
if (freopen_s(&newstdout, "NUL", "w", stdout)) {
MessageBoxA(NULL, "freopen_s failed", NULL, MB_ICONHAND);
return 1;
}
if (!FreeConsole()) {
MessageBoxA(NULL, "FreeConsole failed", NULL, MB_ICONHAND);
return 1;
}
if (!AllocConsole()) {
MessageBoxA(NULL, "AllocConsole failed", NULL, MB_ICONHAND);
return 1;
}
// Now we can point stdout at it.
if (freopen_s(&newstdout, "CONOUT$", "w", stdout)) {
MessageBoxA(NULL, "freopen_s failed", NULL, MB_ICONHAND);
return 1;
}
if (newstdout != stdout) {
MessageBoxA(NULL, "freopen_s did something wonky", NULL, MB_ICONHAND);
return 1;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.