Ответ 1
Вам нужно создать канал (с CreatePipe()), затем присоединить stdout к нему конец записи с SetStdHandle(), вы можете прочитать с конца чтения с помощью ReadFile() и поместить текст вам оттуда куда угодно.
У меня есть доступ к сторонней библиотеке, которая делает "хороший материал". Он выводит сообщения о статусе и прогрессе в стандартный вывод. В приложении "Консоль" я вижу эти сообщения просто отлично. В приложении Windows они просто переходят к ведро бит.
Есть ли простой способ перенаправления stdout и stderr в текстовый элемент управления или другое видимое место. В идеале это не потребует перекомпиляции кода третьей стороны. Это будет просто перехватывать пары на низком уровне. Мне нужно решение, где я просто # включаю заголовок, вызываю функцию инициализации и свяжу библиотеку, как в...
#include "redirectStdFiles.h"
void function(args...)
{
TextControl* text = new TextControl(args...);
initializeRedirectLibrary(text, ...);
printf("Message that will show up in the TextControl\n");
std::cout << "Another message that also shows up in TextControl\n";
}
Еще лучше было бы, если бы он использовал какой-то интерфейс, который я мог бы переопределить, поэтому он не привязан к какой-либо конкретной библиотеке графического интерфейса.
class StdFilesRedirector
{
public:
writeStdout(std::string const& message) = 0;
writeStderr(std::string const& errorMessage) = 0;
readStdin(std::string &putReadStringHere) = 0;
};
Я просто мечтаю? Или кто-нибудь знает что-то, что может сделать что-то подобное?
Редактировать после двух ответов: Я думаю, что использование freopen для перенаправления файлов является хорошим первым шагом. Для полного решения потребуется новый поток, созданный для чтения файла и отображения вывода. Для отладки достаточно сделать "хвост -f" в окне оболочки cygwin. Для более полированного приложения... Вот что я хочу написать... будет некоторая дополнительная работа для создания потока и т.д.
Вам нужно создать канал (с CreatePipe()), затем присоединить stdout к нему конец записи с SetStdHandle(), вы можете прочитать с конца чтения с помощью ReadFile() и поместить текст вам оттуда куда угодно.
Вы можете перенаправить stdout, stderr и stdin с помощью freopen.
Из приведенной выше ссылки:
/* freopen example: redirecting stdout */
#include <stdio.h>
int main ()
{
freopen ("myfile.txt","w",stdout);
printf ("This sentence is redirected to a file.");
fclose (stdout);
return 0;
}
Вы также можете запустить свою программу с помощью командной строки следующим образом:
a.exe > stdout.txt 2> stderr.txt
Вероятно, вы ищете что-то в этих строках:
#define OUT_BUFF_SIZE 512
int main(int argc, char* argv[])
{
printf("1: stdout\n");
StdOutRedirect stdoutRedirect(512);
stdoutRedirect.Start();
printf("2: redirected stdout\n");
stdoutRedirect.Stop();
printf("3: stdout\n");
stdoutRedirect.Start();
printf("4: redirected stdout\n");
stdoutRedirect.Stop();
printf("5: stdout\n");
char szBuffer[OUT_BUFF_SIZE];
int nOutRead = stdoutRedirect.GetBuffer(szBuffer,OUT_BUFF_SIZE);
if(nOutRead)
printf("Redirected outputs: \n%s\n",szBuffer);
return 0;
}
Этот класс сделает это:
#include <windows.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <iostream>
#ifndef _USE_OLD_IOSTREAMS
using namespace std;
#endif
#define READ_FD 0
#define WRITE_FD 1
#define CHECK(a) if ((a)!= 0) return -1;
class StdOutRedirect
{
public:
StdOutRedirect(int bufferSize);
~StdOutRedirect();
int Start();
int Stop();
int GetBuffer(char *buffer, int size);
private:
int fdStdOutPipe[2];
int fdStdOut;
};
StdOutRedirect::~StdOutRedirect()
{
_close(fdStdOut);
_close(fdStdOutPipe[WRITE_FD]);
_close(fdStdOutPipe[READ_FD]);
}
StdOutRedirect::StdOutRedirect(int bufferSize)
{
if (_pipe(fdStdOutPipe, bufferSize, O_TEXT)!=0)
{
//treat error eventually
}
fdStdOut = _dup(_fileno(stdout));
}
int StdOutRedirect::Start()
{
fflush( stdout );
CHECK(_dup2(fdStdOutPipe[WRITE_FD], _fileno(stdout)));
ios::sync_with_stdio();
setvbuf( stdout, NULL, _IONBF, 0 ); // absolutely needed
return 0;
}
int StdOutRedirect::Stop()
{
CHECK(_dup2(fdStdOut, _fileno(stdout)));
ios::sync_with_stdio();
return 0;
}
int StdOutRedirect::GetBuffer(char *buffer, int size)
{
int nOutRead = _read(fdStdOutPipe[READ_FD], buffer, size);
buffer[nOutRead] = '\0';
return nOutRead;
}
Здесь результат:
1: stdout
3: stdout
5: stdout
Redirected outputs:
2: redirected stdout
4: redirected stdout
Когда вы создаете процесс, используя CreateProcess(), вы можете выбрать HANDLE
, на который будут записываться stdout и stderr. Этот HANDLE
может быть файлом, на который вы направляете вывод.
Это позволит вам использовать код без перекомпиляции. Просто выполните его и вместо system()
или еще чего-то используйте CreateProcess()
.
РУЧКА, которую вы передаете CreateProcess()
, также может быть той, что вы создали, и затем вы можете читать из канала и делать что-то еще с данными.
Вы можете сделать что-то подобное с помощью cout или cerr:
// open a file stream
ofstream out("filename");
// save cout stream buffer
streambuf *sb = cout.rdbuf();
// point cout stream buffer to that of the open file
cout.rdbuf(out.rdbuf());
// now you can print to file by writing to cout
cout << "Hello, world!";
// restore cout buffer back
cout.rdbuf(sb);
Или вы можете сделать это с помощью std::stringstream
или другого класса, полученного из std::ostream
.
Чтобы перенаправить stdout, вам нужно будет снова открыть дескриптор файла. Этот поток имеет некоторые идеи такого рода.
Здесь мы установим новую точку входа consoleMain
, которая переопределит ваш собственный.
defaultMain
.Где-то в исходном коде объявляем исходную точку входа (чтобы мы могли ее привязать) и новую точку входа. Оба должны быть объявлены extern "C"
, чтобы предотвратить изменение имени.
extern "C"
{
int defaultMain (void);
int consoleMain (void);
}
Внедрить функцию точки входа.
__declspec(noinline) int consoleMain (void)
{
// __debugbreak(); // Break into the program right at the entry point!
AllocConsole(); // Create a new console
freopen("CON", "w", stdout);
freopen("CON", "w", stderr);
freopen("CON", "r", stdin); // Note: "r", not "w".
return defaultMain();
}
Добавьте свой тестовый код где-нибудь, например. в действии нажатия кнопки.
fwprintf(stdout, L"This is a test to stdout\n");
fwprintf(stderr, L"This is a test to stderr\n");
cout<<"Enter an Integer Number Followed by ENTER to Continue" << endl;
_flushall();
int i = 0;
int Result = wscanf( L"%d", &i);
printf ("Read %d from console. Result = %d\n", i, Result);
consoleMain
как новую точку входа (Project Properties/Linker/Advanced/Entry Point).Благодаря gamedev ссылка в ответ greyfade, я смог написать и протестировать этот простой фрагмент кода
AllocConsole();
*stdout = *_tfdopen(_open_osfhandle((intptr_t) GetStdHandle(STD_OUTPUT_HANDLE), _O_WRONLY), _T("a"));
*stderr = *_tfdopen(_open_osfhandle((intptr_t) GetStdHandle(STD_ERROR_HANDLE), _O_WRONLY), _T("a"));
*stdin = *_tfdopen(_open_osfhandle((intptr_t) GetStdHandle(STD_INPUT_HANDLE), _O_WRONLY), _T("r"));
printf("A printf to stdout\n");
std::cout << "A << to std::cout\n";
std::cerr << "A << to std::cerr\n";
std::string input;
std::cin >> input;
std::cout << "value read from std::cin is " << input << std::endl;
Он работает и подходит для отладки. Получение текста в более привлекательный элемент GUI займет немного больше работы.
Это то, что я сделал бы: