简体   繁体   English

为什么重新打开 stdout 到新分配的控制台偶尔会失败?

[英]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:到目前为止我尝试过的并没有改变行为的事情:

  • Removing the line with #pragma使用#pragma删除行
  • Using CON in place of CONOUT$使用CON代替CONOUT$
  • freopen_s ing stdout to NUL before calling FreeConsole在调用freopen_s之前FreeConsolestdout转换为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.请注意,您确实需要执行stdinstderr以及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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM