Захват консольного приложения вывод в мемо
Как сделать вывод в реальном времени?
Код:
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
#define READ_BUFFER_SIZE 1024
String AppName = "E:\\1.exe"; // Консольное приложение
SECURITY_ATTRIBUTES Security;
HANDLE ReadPipe, WritePipe;
STARTUPINFO Start;
TProcessInformation ProcessInfo;
char *Buffer, Data;
DWORD BytesRead, Apprunning;
int Result, DataSize;
Security.nLength = sizeof(TSecurityAttributes);
Security.bInheritHandle = true;
Security.lpSecurityDescriptor = NULL;
if (CreatePipe(&ReadPipe, &WritePipe, &Security, 0))
{
Buffer = new char[READ_BUFFER_SIZE + 1];
memset(&Start, 0, sizeof(Start));
Start.cb = sizeof(Start) ;
Start.hStdOutput = WritePipe;
Start.hStdInput = ReadPipe;
Start.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
Start.wShowWindow = SW_HIDE;
if (CreateProcess(NULL, AppName.c_str(), &Security, &Security, true, NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE, NULL, NULL, &Start, &ProcessInfo))
{
do
{
Apprunning = WaitForSingleObject(ProcessInfo.hProcess, 100);
Application->ProcessMessages();
// вычитываем данные работающей консоли
do
{
Result = PeekNamedPipe(ReadPipe, NULL, 0, NULL, (LPDWORD) &DataSize, NULL);
if ((Result) && (DataSize))
{
if (DataSize > READ_BUFFER_SIZE) DataSize = READ_BUFFER_SIZE;
ReadFile(ReadPipe, Buffer, DataSize, &BytesRead, NULL);
Buffer[BytesRead] = 0;
OemToAnsi(Buffer, Buffer);
Memo->Text = Memo->Text + (AnsiString) Buffer;
}
}
while ((Result) && (DataSize));
}
while (Apprunning == WAIT_TIMEOUT);
}
delete [] Buffer;
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
CloseHandle(ReadPipe);
CloseHandle(WritePipe);
}
}
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
#define READ_BUFFER_SIZE 1024
String AppName = "E:\\1.exe"; // Консольное приложение
SECURITY_ATTRIBUTES Security;
HANDLE ReadPipe, WritePipe;
STARTUPINFO Start;
TProcessInformation ProcessInfo;
char *Buffer, Data;
DWORD BytesRead, Apprunning;
int Result, DataSize;
Security.nLength = sizeof(TSecurityAttributes);
Security.bInheritHandle = true;
Security.lpSecurityDescriptor = NULL;
if (CreatePipe(&ReadPipe, &WritePipe, &Security, 0))
{
Buffer = new char[READ_BUFFER_SIZE + 1];
memset(&Start, 0, sizeof(Start));
Start.cb = sizeof(Start) ;
Start.hStdOutput = WritePipe;
Start.hStdInput = ReadPipe;
Start.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
Start.wShowWindow = SW_HIDE;
if (CreateProcess(NULL, AppName.c_str(), &Security, &Security, true, NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE, NULL, NULL, &Start, &ProcessInfo))
{
do
{
Apprunning = WaitForSingleObject(ProcessInfo.hProcess, 100);
Application->ProcessMessages();
// вычитываем данные работающей консоли
do
{
Result = PeekNamedPipe(ReadPipe, NULL, 0, NULL, (LPDWORD) &DataSize, NULL);
if ((Result) && (DataSize))
{
if (DataSize > READ_BUFFER_SIZE) DataSize = READ_BUFFER_SIZE;
ReadFile(ReadPipe, Buffer, DataSize, &BytesRead, NULL);
Buffer[BytesRead] = 0;
OemToAnsi(Buffer, Buffer);
Memo->Text = Memo->Text + (AnsiString) Buffer;
}
}
while ((Result) && (DataSize));
}
while (Apprunning == WAIT_TIMEOUT);
}
delete [] Buffer;
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
CloseHandle(ReadPipe);
CloseHandle(WritePipe);
}
}
А код приложения есть?
Кода нету, приложение сторонние.
Заменить внутренний цикл на таймер. По ходу мемо не перерисовывается в таком цикле.
С memo все норм, проблема актуальна.
Чтобы было в реальном времени, стороннее приложение должно постоянно сбрасывать output-буфер fflush(stdout) или вообще отключить выходной буфер, если в консольном приложение этого не предусмотрел разработчик, значит получать данные будешь только в конце работы.
Пример реализации можно?
Код:
//---------------------------------------------------------------------------
#define bzero(a) memset(a,0,sizeof(a))
#include <string>
typedef std::string xStr;
//---------------------------------------------------------------------------
class ExecOutProc {
private:
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR sd;//структура security для пайпов
HANDLE newstdin,
newstdout,
read_stdout,
write_stdin;//дескрипторы пайпов
char *buffer,
*cmdline;
unsigned long bread,//кол-во прочитанных байт
avail;//кол-во доступных байт
public:
ExecOutProc(char*);
~ExecOutProc(void);
void StartProc(void);
xStr ReadOut(void);
};
//------------------------------------
ExecOutProc::ExecOutProc(char* cmd){
cmdline = new char [512];
strcpy(cmdline,cmd);
ZeroMemory(&si,sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi,sizeof(pi));
buffer = new char [1024];//|1024|2048|4096|8192|16384|32768|
sa.lpSecurityDescriptor = NULL;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = true;//разрешаем наследование дескрипторов
CreatePipe(&newstdin,&write_stdin,&sa,0);//создаем пайп для stdin
CreatePipe(&read_stdout,&newstdout,&sa,0);//создаем пайп для stdout
GetStartupInfo(&si);//создаем startupinfo для дочернего процесса
si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;//SW_HIDE;
si.hStdOutput = newstdout;
si.hStdError = newstdout;//подменяем дескрипторы для
si.hStdInput = newstdin;// дочернего процесса
}
//------------------------------------
ExecOutProc::~ExecOutProc(void){
delete [] buffer;
delete [] cmdline;
// Закроем дескрипторы процесса и потока.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(newstdin);//небольшая уборка за собой
CloseHandle(newstdout);
CloseHandle(read_stdout);
CloseHandle(write_stdin);
}
//------------------------------------
void ExecOutProc::StartProc(void){
if(!CreateProcess(0,cmdline,0,0,1,0,0,0,&si,&pi )){
CloseHandle(newstdin);
CloseHandle(newstdout);
CloseHandle(read_stdout);
CloseHandle(write_stdin);
}
}
//------------------------------------
xStr ExecOutProc::ReadOut(void){
xStr result;
// Ждать до тех пор, пока дочерний процесс не выйдет из работы.
WaitForSingleObject(pi.hProcess,50);// 10 INFINITE
//WaitForInputIdle(pi.hProcess,50);//INFINITE
bzero(buffer);
PeekNamedPipe(read_stdout,buffer,1023,&bread,&avail,NULL);
//Проверяем, есть ли данные для чтения в stdout
if(bread != 0){
bzero(buffer);
if(avail > 1023){
while (bread >= 1023){
ReadFile(read_stdout,buffer,1023,&bread,NULL);//читаем из пайпа stdout
result += buffer;
bzero(buffer);
}
} else {
ReadFile(read_stdout,buffer,1023,&bread,NULL);
result += buffer;
bzero(buffer);
}
}
return result;
}
//---------------------------------------------------------------------------
ExecOutProc *ep;
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
//Получение и печать сообщения
AnsiString str = AnsiString(ep->ReadOut().c_str());
if(str.Length())
Memo1->Lines->Add(str);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ToolButton2Click(TObject *Sender)
{
//Создаем экземпляр и запускаем приложение
ep = new ExecOutProc("test.exe");
ep->StartProc();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ToolButton3Click(TObject *Sender)
{
//Пуск/Останов чтения пайпа
Timer1->Enabled = !Timer1->Enabled;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
if(ep) delete ep;
}
//---------------------------------------------------------------------------
#define bzero(a) memset(a,0,sizeof(a))
#include <string>
typedef std::string xStr;
//---------------------------------------------------------------------------
class ExecOutProc {
private:
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR sd;//структура security для пайпов
HANDLE newstdin,
newstdout,
read_stdout,
write_stdin;//дескрипторы пайпов
char *buffer,
*cmdline;
unsigned long bread,//кол-во прочитанных байт
avail;//кол-во доступных байт
public:
ExecOutProc(char*);
~ExecOutProc(void);
void StartProc(void);
xStr ReadOut(void);
};
//------------------------------------
ExecOutProc::ExecOutProc(char* cmd){
cmdline = new char [512];
strcpy(cmdline,cmd);
ZeroMemory(&si,sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi,sizeof(pi));
buffer = new char [1024];//|1024|2048|4096|8192|16384|32768|
sa.lpSecurityDescriptor = NULL;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = true;//разрешаем наследование дескрипторов
CreatePipe(&newstdin,&write_stdin,&sa,0);//создаем пайп для stdin
CreatePipe(&read_stdout,&newstdout,&sa,0);//создаем пайп для stdout
GetStartupInfo(&si);//создаем startupinfo для дочернего процесса
si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;//SW_HIDE;
si.hStdOutput = newstdout;
si.hStdError = newstdout;//подменяем дескрипторы для
si.hStdInput = newstdin;// дочернего процесса
}
//------------------------------------
ExecOutProc::~ExecOutProc(void){
delete [] buffer;
delete [] cmdline;
// Закроем дескрипторы процесса и потока.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
CloseHandle(newstdin);//небольшая уборка за собой
CloseHandle(newstdout);
CloseHandle(read_stdout);
CloseHandle(write_stdin);
}
//------------------------------------
void ExecOutProc::StartProc(void){
if(!CreateProcess(0,cmdline,0,0,1,0,0,0,&si,&pi )){
CloseHandle(newstdin);
CloseHandle(newstdout);
CloseHandle(read_stdout);
CloseHandle(write_stdin);
}
}
//------------------------------------
xStr ExecOutProc::ReadOut(void){
xStr result;
// Ждать до тех пор, пока дочерний процесс не выйдет из работы.
WaitForSingleObject(pi.hProcess,50);// 10 INFINITE
//WaitForInputIdle(pi.hProcess,50);//INFINITE
bzero(buffer);
PeekNamedPipe(read_stdout,buffer,1023,&bread,&avail,NULL);
//Проверяем, есть ли данные для чтения в stdout
if(bread != 0){
bzero(buffer);
if(avail > 1023){
while (bread >= 1023){
ReadFile(read_stdout,buffer,1023,&bread,NULL);//читаем из пайпа stdout
result += buffer;
bzero(buffer);
}
} else {
ReadFile(read_stdout,buffer,1023,&bread,NULL);
result += buffer;
bzero(buffer);
}
}
return result;
}
//---------------------------------------------------------------------------
ExecOutProc *ep;
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
//Получение и печать сообщения
AnsiString str = AnsiString(ep->ReadOut().c_str());
if(str.Length())
Memo1->Lines->Add(str);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ToolButton2Click(TObject *Sender)
{
//Создаем экземпляр и запускаем приложение
ep = new ExecOutProc("test.exe");
ep->StartProc();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ToolButton3Click(TObject *Sender)
{
//Пуск/Останов чтения пайпа
Timer1->Enabled = !Timer1->Enabled;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
if(ep) delete ep;
}
//---------------------------------------------------------------------------
Код:
#include <iostream>
#include <clocale>
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
//------------------------------------------------------------------
int main(int argc, char *argv[]) {
setlocale(LC_ALL,"Rus");
std::cout << "Русский текст.\n\n";
for(int i=0;i<250;i++){
std::cout << i << "\n";
Sleep(50);
fflush(stdout);
}
system("pause");
return 0;
}
//------------------------------------------------------------------
#include <clocale>
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
//------------------------------------------------------------------
int main(int argc, char *argv[]) {
setlocale(LC_ALL,"Rus");
std::cout << "Русский текст.\n\n";
for(int i=0;i<250;i++){
std::cout << i << "\n";
Sleep(50);
fflush(stdout);
}
system("pause");
return 0;
}
//------------------------------------------------------------------
Спасибо, за помощь! Того чего я хочу в природе не существует.
Цитата: xAtom
отключить выходной буфер, если в консольном приложение этого не предусмотрел разработчик, значит получать данные будешь только в конце работы.
Можно инжектнуться в приложение и отключить его изнутри.
Цитата: @pixo $oft
Цитата: xAtom
отключить выходной буфер, если в консольном приложение этого не предусмотрел разработчик, значит получать данные будешь только в конце работы.
Можно инжектнуться в приложение и отключить его изнутри.
Каким образом куда копать?
А ещё не оверквоть сообщения — выглядит просто жутчайше, причём чем дальше, тем хуже.