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

发布于 2025-02-04 19:25:49 字数 1452 浏览 4 评论 0原文

考虑以下程序:

#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;
}

复制步骤:在Visual Studio 2019中创建一个新的空C/C ++项目,转到Project Properties -&GT;配置属性 - &gt;链接器 - &gt;系统并将子系统更改为未设置,然后在名为source.c的新文件中添加上述代码。

大多数时候我运行它,它会像预定的那样打开一个带有数字1的新控制台,但有时会失败。如果我使用“启动不调试”(CTRL+F5)运行它,则它的“ Freopen_s失败”消息大约在30中失败。如果我使用“开始调试”(F5)运行它,那么它也会失败10中的1个,而_ntclose抛出一个例外,上面说“指定了无效的句柄”,带有堆栈跟踪指向我呼叫freopen_s

为什么这种间歇性失败会发生?我是否在C代码或项目配置中打破Windows API的规则之一?

到目前为止,我尝试过没有改变行为的事情:

  • 使用#pragma
  • 使用con代替conout $
  • freopen_s ing stdout to nul在调用freeconsole之前

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
quot;, "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.

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. 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. 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.

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?

Things I tried so far that didn't change the behavior:

  • Removing the line with #pragma
  • Using CON in place of CONOUT$
  • freopen_sing stdout to NUL before calling FreeConsole

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

有木有妳兜一样 2025-02-11 19:25:49

这搞砸了,这不是你的错。 freeconsole()继续销毁旧控制台并用它拆下手柄。这无效stdout异步。

您真的想做这样的事情。请注意,您确实需要做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;
    }

This is messed up, it's not your fault. FreeConsole() proceeds to destroy the old console and take down the handle with it. This invalidates stdout asynchronously.

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.

    // 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
quot;, "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;
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文