【问题标题】:Where does "printf" write in a Windows Non-console Application?“printf”在 Windows 非控制台应用程序中写在哪里?
【发布时间】:2014-03-05 23:31:17
【问题描述】:

如果我选择创建一个Windows非控制台应用程序,并在代码中实现printf/coutprintf/cout在哪里写?它是否写入stdout 缓冲区?如果是,有什么方法可以从stdout 读取它并将其打印到某个文本文件或对文本执行MessageBox(只是为了验证我是否已正确阅读)?

EDIT:: 只是为了澄清我不想将输出重定向到任何地方。我想知道,printf/cout 写在哪里?如果它写入某个默认缓冲区,有没有办法读取输出,只是为了验证我是否从正确的缓冲区读取了正确的输出。 请不要给我重定向“stdout”的解决方案

【问题讨论】:

  • 技术上它不会在任何地方写入,但您可以使用 freopen("file.txt","w",stdout); 将其重定向到文本文件
  • @MartinR:: 我不想重定向输出。我想知道,如果没有重定向,它会写在哪里,有没有办法读取该位置来验证。
  • 您可以使用nonconsole.exe | tee 读取输出。 tee 包含在“Git for Windows”中。

标签: c++ c winapi visual-c++ stdout


【解决方案1】:

在 Windows 下,stdout 是访问由GetStdHandle(STD_OUTPUT_HANDLE) 返回的句柄的低级函数的包装器。

当通过双击启动非控制台应用程序时(我在 Windows 7 64 位下尝试过),GetStdHandle(STD_OUTPUT_HANDLE) 将返回无效句柄。这意味着printf 和其他命令将什么都不写,但printf 内部调用的低级函数将返回错误代码。

然而,正如已经说过的,即使是非控制台程序也可以通过以下方式启动:

program_name > output_file.txt

在这种情况下,printf 输出将被写入输出文件。

-- 编辑--

如果您希望“捕获”printf() 输出并将其写入MessageBox(),有两种方法可以实现:

第一个运行程序两次,而一个实例的输入是另一个实例的标准输出。该行为可以通过以下命令行来解释:

program_name | program_name /msgbox

第二种可能性在不运行两个程序且不运行程序两次的情况下有效:您可以挂钩文件句柄#1。使用 msvcrt.dll 时,这至少应该是可能的:

HANDLE hRead,hWrite;

CreatePipe(&hRead,&hWrite,NULL,0);
dup2(_open_osfhandle(hWrite,O_WRONLY),1);

// Now printf() output can be read from handle hRead

printf("Hello world 2!\n");

// In a separate thread do:
ReadFile(hRead,...);

【讨论】:

  • +1 到目前为止,您的解决方案看起来最适合我。
【解决方案2】:

由于您的问题似乎只是为了提供信息,因此没有控制台的 Windows 应用程序的标准输出、标准错误处理已关闭。任何试图输出到这些句柄的函数,都会被调用,检查打开的句柄,发现它已关闭,然后返回而不做任何其他事情。

您可能会说,在这种情况下,您的输出最终无处可寻 :)

如果要读取此输出,则需要通过分配控制台或使用此处描述的方法之一来打开句柄。

【讨论】:

    【解决方案3】:

    我正在开发一个可视化引擎,我使用以下两件事来替代您在控制台应用程序中获得的 pc 和用户之间的标准输入/输出。

    1:使用sprintf(int sprintf ( char * str, const char * format, ... ))。它的作用是打印成字符串而不是标准输出(您不必使用临时文件)。在此之后,您可以将MessageBox 与您刚刚打印到的字符串一起使用。

    2:创建一个实际的控制台窗口(同时保留主窗口)并将stdinstdoutstderr 从主窗口重定向到控制台。这是一个构造类:

    ConsoleWindowClass.h:

     #pragma once
     #include <windows.h>
     #include <stdio.h>
     #include <fcntl.h>
     #include <io.h>
     #include <iostream>
     #include <fstream>
    class ConsoleWindowClass
    {
    public:
        ConsoleWindowClass(void);
        ~ConsoleWindowClass(void);
        void Create();
    };
    

    ConsoleWindowClass.cpp:

     #include "ConsoleWindowClass.h"
    
    using namespace std;
    
    // maximum mumber of lines the output console should have
    static const WORD MAX_CONSOLE_LINES = 500;
    
    ConsoleWindowClass::ConsoleWindowClass(void)
    {
        Create();
    }
    
    ConsoleWindowClass::~ConsoleWindowClass(void)
    {
    }
    
    void ConsoleWindowClass::Create()
    {
        int hConHandle;
        long lStdHandle;
        CONSOLE_SCREEN_BUFFER_INFO coninfo;
        FILE *fp;
    
    // allocate a console for this app
        AllocConsole();
    
    // set the screen buffer to be big enough to let us scroll text
        GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),&coninfo);
        coninfo.dwSize.Y = MAX_CONSOLE_LINES;
        SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),coninfo.dwSize);
    
    // redirect unbuffered STDOUT to the console
        lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
        hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
    
        fp = _fdopen( hConHandle, "w" );
    
        *stdout = *fp;
    
        setvbuf( stdout, NULL, _IONBF, 0 );
    
    // redirect unbuffered STDIN to the console
    
        lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
        hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
    
        fp = _fdopen( hConHandle, "r" );
        *stdin = *fp;
        setvbuf( stdin, NULL, _IONBF, 0 );
    
    // redirect unbuffered STDERR to the console
        lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);
        hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
    
        fp = _fdopen( hConHandle, "w" );
    
        *stderr = *fp;
    
        setvbuf( stderr, NULL, _IONBF, 0 );
    
    // make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
    // point to console as well
        ios::sync_with_stdio();
    }
    

    之后,调用printf() 会将字符串打印到控制台。您还可以使用控制台在其中输入字符串,它们将可以在主窗口中使用(使用多线程以便scanf 不会暂停您的主程序)。

    【讨论】:

    • 如果你只想使用 C 而不是 C++,你可以去掉 和 ios::sync_with_stdio()。您还必须在没有课程的情况下进行。这是最后一行的作用:cplusplus.com/reference/ios/ios_base/sync_with_stdio
    • 是的,我写完答案后就明白了这个问题。无论如何,这有望为寻找重定向的人服务:)
    【解决方案4】:

    printfcout 始终打印到 stdout

    您需要从命令行启动程序并将其输出通过管道传输到文本文件以使其可读。

    否则,您需要在代码中生成 tmp 文件的输出流。

    管道可以通过以下方式完成(如果您的应用名称为 foo):

    foo > log.txt
    

    然后您可以读取文件log.txt,如果您导航到它可以找到的目录

    dir
    

    【讨论】:

    • 声明printf or cout always print to stdout是完全错误的
    • 他们正在打印到标准输出,你必须作为调用者重定向
    • @Dieter 真的吗? “对象 cout 控制输出到与对象 stdout 关联的流缓冲区”(27.4.2/3)
    • std::cout.rdbuf(std::cerr.rdbuf());不会影响 C 层,但会影响 C++ 层
    猜你喜欢
    • 2011-08-08
    • 2022-11-11
    • 2012-06-15
    • 1970-01-01
    • 2013-12-06
    • 1970-01-01
    • 1970-01-01
    • 2022-11-20
    • 1970-01-01
    相关资源
    最近更新 更多