如何使用 Windows 程序在 C++ 中获得控制台输出?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/191842/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 13:35:24  来源:igfitidea点击:

How do I get console output in C++ with a Windows program?

c++windowsconsole

提问by Obediah Stane

If I have a native C++ windows program (i.e. the entry point is WinMain) how do I view output from console functions like std::cout?

如果我有一个本机 C++ windows 程序(即入口点是 WinMain),我如何查看 std::cout 等控制台函数的输出?

回答by luke

Check out Adding Console I/O to a Win32 GUI App. This may help you do what you want.

查看将控制台 I/O 添加到 Win32 GUI 应用程序。这可能会帮助您做您想做的事。

If you don't have, or can't modify the code, try the suggestions found hereto redirect console output to a file.

如果您没有或无法修改代码,请尝试此处找到的建议将控制台输出重定向到文件。



Edit:bit of thread necromancy here. I first answered this 9ish years ago, in the early days of SO, before the (good) policy of non-link-only answers came into effect. I'll repost the code from the original article in the hope to atone for my past sins.

编辑:这里有点线程死灵法。我第一次回答这个问题是在 9 年前,在 SO 的早期,在非链接答案的(好的)政策生效之前。我将重新发布原始文章中的代码,希望能弥补我过去的罪过。

guicon.cpp -- A console redirection function

guicon.cpp -- 控制台重定向功能

#include <windows.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <iostream>
#include <fstream>
#ifndef _USE_OLD_IOSTREAMS
using namespace std;
#endif
// maximum mumber of lines the output console should have
static const WORD MAX_CONSOLE_LINES = 500;
#ifdef _DEBUG
void RedirectIOToConsole()
{
    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();
}

#endif
//End of File

guicon.h -- Interface to console redirection function

guicon.h -- 控制台重定向功能的接口

#ifndef __GUICON_H__
#define __GUICON_H__
#ifdef _DEBUG

void RedirectIOToConsole();

#endif
#endif

// End of File

test.cpp -- Demonstrating console redirection

test.cpp -- 演示控制台重定向

#include <windows.h>
#include <iostream>
#include <fstream>
#include <conio.h>
#include <stdio.h>
#ifndef _USE_OLD_OSTREAMS
using namespace std;
#endif
#include "guicon.h"


#include <crtdbg.h>

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    #ifdef _DEBUG
    RedirectIOToConsole();
    #endif
    int iVar;

    // test stdio
    fprintf(stdout, "Test output to stdout\n");
    fprintf(stderr, "Test output to stderr\n");
    fprintf(stdout, "Enter an integer to test stdin: ");
    scanf("%d", &iVar);
    printf("You entered %d\n", iVar);

    //test iostreams
    cout << "Test output to cout" << endl;
    cerr << "Test output to cerr" << endl;
    clog << "Test output to clog" << endl;
    cout << "Enter an integer to test cin: ";
    cin >> iVar;
    cout << "You entered " << iVar << endl;
    #ifndef _USE_OLD_IOSTREAMS

    // test wide iostreams
    wcout << L"Test output to wcout" << endl;
    wcerr << L"Test output to wcerr" << endl;
    wclog << L"Test output to wclog" << endl;
    wcout << L"Enter an integer to test wcin: ";
    wcin >> iVar;
    wcout << L"You entered " << iVar << endl;
    #endif

    // test CrtDbg output
    _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE );
    _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR );
    _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE );
    _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR);
    _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE );
    _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR);
    _RPT0(_CRT_WARN, "This is testing _CRT_WARN output\n");
    _RPT0(_CRT_ERROR, "This is testing _CRT_ERROR output\n");
    _ASSERT( 0 && "testing _ASSERT" );
    _ASSERTE( 0 && "testing _ASSERTE" );
    Sleep(2000);
    return 0;
}

//End of File

回答by workmad3

You can also reopen the cout and cerr streams to output to a file as well. The following should work for this:

您还可以重新打开 cout 和 cerr 流以输出到文件。以下应该为此工作:

#include <iostream>
#include <fstream>

int main ()
{
    std::ofstream file;
    file.open ("cout.txt");
    std::streambuf* sbuf = std::cout.rdbuf();
    std::cout.rdbuf(file.rdbuf());
    //cout is now pointing to a file
    return 0;
}

回答by Florian Winter

If you are sending the output of your program to a file or pipe, e.g.

如果您将程序的输出发送到文件或管道,例如

myprogram.exe > file.txt
myprogram.exe | anotherprogram.exe

or you are invoking your program from another program and capturing its output through a pipe, then you don't need to change anything. It will just work, even if the entry point is WinMain.

或者您正在从另一个程序调用您的程序并通过管道捕获其输出,那么您不需要更改任何内容。即使入口点是WinMain.

However, if you are running your program in a console or in Visual Studio, then the output will not appear in the console or in the Output window of Visual Studio. If you want to see the output "live", then try one of the other answers.

但是,如果您在控制台或 Visual Studio 中运行程序,则输出将不会出现在控制台或 Visual Studio 的“输出”窗口中。如果您想查看“实时”输出,请尝试其他答案之一。

Basically, this means that standard output works just like with console applications, but it isn't connected to a console in which you are running your application, and there seems to be no easy way to do that (all the other solutions presented here connect the output to a new console window that will pop up when you run your application, even from another console).

基本上,这意味着标准输出就像控制台应用程序一样工作,但它没有连接到您运行应用程序的控制台,而且似乎没有简单的方法可以做到这一点(此处提供的所有其他解决方案都连接输出到一个新的控制台窗口,当你运行你的应用程序时会弹出这个窗口,即使是从另一个控制台)。

回答by Sev

Using a combination of luke's answerand Roger's answer hereworked for me in my Windows Desktop Application project.

在我的 Windows 桌面应用程序项目中,在这里结合使用luke 的答案Roger 的答案对我有用

void RedirectIOToConsole() {

    //Create a console for this application
    AllocConsole();

    // Get STDOUT handle
    HANDLE ConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    int SystemOutput = _open_osfhandle(intptr_t(ConsoleOutput), _O_TEXT);
    FILE *COutputHandle = _fdopen(SystemOutput, "w");

    // Get STDERR handle
    HANDLE ConsoleError = GetStdHandle(STD_ERROR_HANDLE);
    int SystemError = _open_osfhandle(intptr_t(ConsoleError), _O_TEXT);
    FILE *CErrorHandle = _fdopen(SystemError, "w");

    // Get STDIN handle
    HANDLE ConsoleInput = GetStdHandle(STD_INPUT_HANDLE);
    int SystemInput = _open_osfhandle(intptr_t(ConsoleInput), _O_TEXT);
    FILE *CInputHandle = _fdopen(SystemInput, "r");

    //make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog point to console as well
    ios::sync_with_stdio(true);

    // Redirect the CRT standard input, output, and error handles to the console
    freopen_s(&CInputHandle, "CONIN$", "r", stdin);
    freopen_s(&COutputHandle, "CONOUT$", "w", stdout);
    freopen_s(&CErrorHandle, "CONOUT$", "w", stderr);

    //Clear the error state for each of the C++ standard stream objects. We need to do this, as
    //attempts to access the standard streams before they refer to a valid target will cause the
    //iostream objects to enter an error state. In versions of Visual Studio after 2005, this seems
    //to always occur during startup regardless of whether anything has been read from or written to
    //the console or not.
    std::wcout.clear();
    std::cout.clear();
    std::wcerr.clear();
    std::cerr.clear();
    std::wcin.clear();
    std::cin.clear();

}

回答by Chris Olsen

The problem with the answers like luke'sand Sev'sis that they unnecessarily create new FILEinstances which are then leaked and can cause debug assertions in the CRT cleanup code.

lukeSev这样的答案的问题在于,它们不必要地创建了新FILE实例,然后这些实例会被泄露,并可能导致 CRT 清理代码中的调试断言。

freopen_sis all that is really needed:

freopen_s真正需要的是:

FILE* fp = nullptr;
freopen_s(&fp, "CONIN$", "r", stdin);
freopen_s(&fp, "CONOUT$", "w", stdout);
freopen_s(&fp, "CONOUT$", "w", stderr);

You'll probably want to do a little error checking and cleanup as well. Below is the complete solution that I currently use.

您可能还想进行一些错误检查和清理。以下是我目前使用的完整解决方案。

Redirecting Console Standard IO:

重定向控制台标准 IO:

bool RedirectConsoleIO()
{
    bool result = true;
    FILE* fp;

    // Redirect STDIN if the console has an input handle
    if (GetStdHandle(STD_INPUT_HANDLE) != INVALID_HANDLE_VALUE)
        if (freopen_s(&fp, "CONIN$", "r", stdin) != 0)
            result = false;
        else
            setvbuf(stdin, NULL, _IONBF, 0);

    // Redirect STDOUT if the console has an output handle
    if (GetStdHandle(STD_OUTPUT_HANDLE) != INVALID_HANDLE_VALUE)
        if (freopen_s(&fp, "CONOUT$", "w", stdout) != 0)
            result = false;
        else
            setvbuf(stdout, NULL, _IONBF, 0);

    // Redirect STDERR if the console has an error handle
    if (GetStdHandle(STD_ERROR_HANDLE) != INVALID_HANDLE_VALUE)
        if (freopen_s(&fp, "CONOUT$", "w", stderr) != 0)
            result = false;
        else
            setvbuf(stderr, NULL, _IONBF, 0);

    // Make C++ standard streams point to console as well.
    ios::sync_with_stdio(true);

    // Clear the error state for each of the C++ standard streams.
    std::wcout.clear();
    std::cout.clear();
    std::wcerr.clear();
    std::cerr.clear();
    std::wcin.clear();
    std::cin.clear();

    return result;
}

Releasing a Console:

释放控制台:

bool ReleaseConsole()
{
    bool result = true;
    FILE* fp;

    // Just to be safe, redirect standard IO to NUL before releasing.

    // Redirect STDIN to NUL
    if (freopen_s(&fp, "NUL:", "r", stdin) != 0)
        result = false;
    else
        setvbuf(stdin, NULL, _IONBF, 0);

    // Redirect STDOUT to NUL
    if (freopen_s(&fp, "NUL:", "w", stdout) != 0)
        result = false;
    else
        setvbuf(stdout, NULL, _IONBF, 0);

    // Redirect STDERR to NUL
    if (freopen_s(&fp, "NUL:", "w", stderr) != 0)
        result = false;
    else
        setvbuf(stderr, NULL, _IONBF, 0);

    // Detach from console
    if (!FreeConsole())
        result = false;

    return result;
}

Resizing Console Buffer:

调整控制台缓冲区的大小:

void AdjustConsoleBuffer(int16_t minLength)
{
    // Set the screen buffer to be big enough to scroll some text
    CONSOLE_SCREEN_BUFFER_INFO conInfo;
    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &conInfo);
    if (conInfo.dwSize.Y < minLength)
        conInfo.dwSize.Y = minLength;
    SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), conInfo.dwSize);
}

Allocating a New Console:

分配一个新的控制台:

bool CreateNewConsole(int16_t minLength)
{
    bool result = false;

    // Release any current console and redirect IO to NUL
    ReleaseConsole();

    // Attempt to create new console
    if (AllocConsole())
    {
        AdjustConsoleBuffer(minLength);
        result = RedirectConsoleIO();
    }

    return result;
}

Attaching to Parent's Console:

附加到家长的控制台:

bool AttachParentConsole(int16_t minLength)
{
    bool result = false;

    // Release any current console and redirect IO to NUL
    ReleaseConsole();

    // Attempt to attach to parent process's console
    if (AttachConsole(ATTACH_PARENT_PROCESS))
    {
        AdjustConsoleBuffer(minLength);
        result = RedirectConsoleIO();
    }

    return result;
}

Calling from WinMain:

从 WinMain 调用:

Link with /SUBSYSTEM:Windows

/SUBSYSTEM:Windows

int APIENTRY WinMain(
    HINSTANCE /*hInstance*/,
    HINSTANCE /*hPrevInstance*/,
    LPTSTR    /*lpCmdLine*/,
    int       /*cmdShow*/)
{
    if (CreateNewConsole(1024))
    {
        int i;

        // test stdio
        fprintf(stdout, "Test output to stdout\n");
        fprintf(stderr, "Test output to stderr\n");
        fprintf(stdout, "Enter an integer to test stdin: ");
        scanf("%d", &i);
        printf("You entered %d\n", i);

        // test iostreams
        std::cout << "Test output to std::cout" << std::endl;
        std::cerr << "Test output to std::cerr" << std::endl;
        std::clog << "Test output to std::clog" << std::endl;
        std::cout << "Enter an integer to test std::cin: ";
        std::cin >> i;
        std::cout << "You entered " << i << std::endl;

        std::cout << endl << "Press any key to continue..." << endl;
        _getch();

        ReleaseConsole();
    }

    return 0;
};

回答by John Blackburn

Actually there is a much simpler solution than any proposed so far. Your Windows program will have a WinMain function so just add this "dummy" main function as well

实际上,有一个比迄今为止提出的任何解决方案都简单得多的解决方案。你的 Windows 程序将有一个 WinMain 函数,所以只需添加这个“虚拟”主函数

int main()
{
   return WinMain(GetModuleHandle(NULL), NULL, GetCommandLineA(), SW_SHOWNORMAL);
}

You can now compile using MSVC like this

您现在可以像这样使用 MSVC 进行编译

cl /nologo /c /EHsc myprog.c
link /nologo /out:myprog.exe /subsystem:console myprog.obj user32.lib gdi32.lib

(you may need to add more library links)

(您可能需要添加更多库链接)

When you run the program any printfwill be written to the command prompt.

当您运行该程序时,任何内容printf都将写入命令提示符。

If you are using gcc (mingw) to compile for Windows you don't need a dummy main function, just do

如果您使用 gcc (mingw) 为 Windows 编译,则不需要虚拟主函数,只需执行

gcc -o myprog.exe myprog.c -luser32 -lgdi32

(ie avoid using the -mwindowsflag which will prevent writing to a console. That flag will be useful when you create the final GUI release) Again you may need to specify more libraries if using more windows features)

(即避免使用-mwindows会阻止写入控制台的标志。当您创建最终的 GUI 版本时,该标志将很有用)如果使用更多的 Windows 功能,您可能需要指定更多的库)

回答by John Blackburn

creating a pipe, execute the program console CreateProcess() and read with ReadFile() or writes in console WriteFile()

创建管道,执行程序控制台 CreateProcess() 并使用 ReadFile() 读取或写入控制台 WriteFile()

    HANDLE hRead ; // ConsoleStdInput
    HANDLE hWrite; // ConsoleStdOutput and ConsoleStdError

    STARTUPINFO           stiConsole;
    SECURITY_ATTRIBUTES   segConsole;
    PROCESS_INFORMATION   priConsole;

    segConsole.nLength = sizeof(segConsole);
    segConsole.lpSecurityDescriptor = NULL;
    segConsole.bInheritHandle = TRUE;

if(CreatePipe(&hRead,&hWrite,&segConsole,0) )
{

    FillMemory(&stiConsole,sizeof(stiConsole),0);
    stiConsole.cb = sizeof(stiConsole);
GetStartupInfo(&stiConsole);
stiConsole.hStdOutput = hWrite;
stiConsole.hStdError  = hWrite;
stiConsole.dwFlags    = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
stiConsole.wShowWindow = SW_HIDE; // execute hide 

    if(CreateProcess(NULL, "c:\teste.exe",NULL,NULL,TRUE,NULL,
      NULL,NULL,&stiConsole,&priConsole) == TRUE)
    {
        //readfile and/or writefile
}    

}

}

回答by Kamran Bigdely

Go to Project>Project Properties>Linker>System and in the right pane, set SubSystemsoption to Console(/SUBSYSTEM:CONSOLE)

转到 Project>Project Properties>Linker>System 并在右侧窗格中,将SubSystems选项设置为Console(/SUBSYSTEM:CONSOLE)

Then compile your program and run it from console to see whether you command prompt shows your outputs or not.

然后编译您的程序并从控制台运行它以查看您的命令提示符是否显示您的输出。

回答by Chris Charabaruk

Don't quote me on this, but the Win32 console APImight be what you're looking for. If you're just doing this for debugging purposes, however, you might be more interested in running DebugViewand calling the DbgPrintfunction.

不要在这方面引用我的话,但 Win32控制台 API可能正是您要找的。但是,如果您只是出于调试目的执行此操作,您可能对运行DebugView和调用DbgPrint函数更感兴趣。

This of course assumes its your application you want sending console output, not reading it from another application. In that case, pipes might be your friend.

这当然假设您的应用程序要发送控制台输出,而不是从另一个应用程序读取它。在这种情况下,管道可能是你的朋友。

回答by Slion

As mentioned thereand therethe easiest solution is to use your Project Property Pages to switch back and forth between CONSOLEand WINDOWSSubSytems to enable or disable console output at will.

如前所述存在最简单的解决方法是使用你的项目属性页之间来回切换CONSOLEWINDOWS随意SubSytems启用或禁用控制台输出。

Project Properties

项目属性

Your program will just need mainand WinMainentry points to make sure both configuration are compiling. The mainfunction simply calling WinMainas shown below for instance:

您的程序只需要mainWinMain入口点来确保两个配置都在编译。该main函数简单地调用WinMain如下所示,例如:

int main()
{
cout << "Output standard\n";
cerr << "Output error\n";

return WinMain(GetModuleHandle(NULL), NULL, GetCommandLineA(), SW_SHOWNORMAL);
}