Как получить вывод консоли на C ++ с помощью программы Windows? - PullRequest
29 голосов
/ 10 октября 2008

Если у меня есть собственная программа на C ++ для Windows (то есть точка входа - WinMain), как мне просмотреть выходные данные консольных функций, таких как std :: cout?

Ответы [ 10 ]

21 голосов
/ 10 октября 2008

Извлечение Добавление консольного ввода-вывода в Win32 GUI-приложение . Это может помочь вам сделать то, что вы хотите.

Если у вас нет или вы не можете изменить код, попробуйте найти советы здесь , чтобы перенаправить вывод консоли в файл.


Редактировать: бит некромантии потока здесь. Я впервые ответил на это 9 лет назад, в первые дни SO, до того, как (хорошая) политика ответов, не связанных только с ссылками, вступила в силу. Я перепишу код из оригинальной статьи в надежде искупить мои прошлые грехи.

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 - Интерфейс для функции перенаправления консоли

#ifndef __GUICON_H__
#define __GUICON_H__
#ifdef _DEBUG

void RedirectIOToConsole();

#endif
#endif

// End of File

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
9 голосов
/ 10 октября 2008

Вы также можете снова открыть потоки 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;
}
4 голосов
/ 05 сентября 2017

Использование комбинации ответа Люка и ответа Роджера здесь работало для меня в моем проекте Windows Desktop Application.

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();

}
3 голосов
/ 01 августа 2017

Если вы отправляете вывод вашей программы в файл или канал, например,

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

или если вы вызываете вашу программу из другой программы и записываете ее вывод по каналу, вам не нужно ничего менять. Это будет просто работать, даже если точка входа будет WinMain.

Однако, если вы запускаете свою программу в консоли или в Visual Studio, выходные данные не будут отображаться в консоли или в окне «Вывод» Visual Studio. Если вы хотите увидеть вывод «вживую», попробуйте один из других ответов.

По сути, это означает, что стандартный вывод работает так же, как с консольными приложениями, но он не подключен к консоли, на которой вы запускаете ваше приложение, и, похоже, нет простого способа сделать это (все другие решения представленные здесь соединяют вывод с новым консольным окном, которое появляется при запуске приложения, даже с другой консоли).

3 голосов
/ 11 января 2009

создавая канал, запустите программу консоли 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
}    

}

2 голосов
/ 10 октября 2008

Не цитируйте меня по этому поводу, но консольный API Win32 может быть тем, что вы ищете. Если вы просто делаете это для целей отладки, вам, возможно, будет более интересно запустить DebugView и вызвать функцию DbgPrint .

Это, конечно, предполагает, что ваше приложение хочет отправлять вывод консоли, а не читать его из другого приложения. В этом случае трубы могут быть вашими друзьями.

1 голос
/ 23 февраля 2017

Перейдите в Проект> Свойства проекта> Компоновщик> Система и на правой панели установите Подсистемы в Консоль (/ SUBSYSTEM: CONSOLE)

Затем скомпилируйте вашу программу и запустите ее из консоли, чтобы увидеть, показывает ли ваша командная строка ваши результаты или нет.

0 голосов
/ 27 апреля 2019

Сегодня провел весь день, пытаясь заставить это работать правильно. Нашли ответы по всему Интернету, которые выглядят как Люк и Сев .

Метод работает, но проблема возникает, когда я выхожу из консоли, либо вызывая FreeConsole, либо просто нормально завершая приложение. В режиме отладки я вижу отладочные утверждения о недопустимых дескрипторах файлов в коде очистки CRT.

Я думаю, что проблема вызвана частично слишком сложным процессом перенаправления, который используют все, и частично потому, что стандартные потоки ввода-вывода не перенаправляются перед выходом.

Я вообще не понимаю необходимости использовать _open_osfhandle и _fdopen в этом процессе.

Это полное решение, которое работает для меня:

Стандарт перенаправления консоли перенаправления:

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

Выпуск консоли:

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)
        return false;

    return true;
}

Изменение размера буфера консоли:

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

Выделение новой консоли:

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

Подключение к родительской консоли:

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

Звонок из WinMain:

Связь с /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
        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 >> i;
        cout << "You entered " << i << endl;

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

        ReleaseConsole();
    }

    return 0;
};
0 голосов
/ 09 октября 2018

На самом деле существует гораздо более простое решение, чем любое из предложенных до сих пор. Ваша программа для Windows будет иметь функцию WinMain, поэтому просто добавьте эту «фиктивную» основную функцию

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

Теперь вы можете компилировать, используя MSVC, как это

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

(может потребоваться добавить дополнительные ссылки на библиотеки)

При запуске программы в командной строке будет записано любое значение printf.

Если вы используете gcc (mingw) для компиляции для Windows, вам не нужна фиктивная основная функция, просто выполните

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

(т. Е. Избегайте использования флага -mwindows, который предотвратит запись в консоль. Этот флаг будет полезен при создании окончательного выпуска GUI). Снова вам может потребоваться указать больше библиотек при использовании большего количества функций Windows)

0 голосов
/ 10 октября 2008

Так как нет окна консоли, это невозможно сложно. (Узнайте что-то новое каждый день - я никогда не знал о функциях консоли!)

Возможно ли заменить исходящие вызовы? Я часто буду использовать TRACE или OutputDebugString для отправки информации в окно вывода Visual Studio.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...