Перейти к содержанию

Переопределение ввода/вывода консоли


Рекомендуемые сообщения

Есть консольное приложение - TurboProlog.

Хочу написать виндовый гуй для него. Так как отдельного комипилятора, интерпретатора TurboProlog'а нету, то думаю сделать так - использовать prolog.exe таким образом, чтобы вывод с консоли происходил, например в Memo на моей форме, а, например, по нажатию кнопки на форме, текст из Memo вставлялся в консоль, начиналась бы компияция, а результат опять из консоли в мое приложение.

Это вообще реально?

Ссылка на комментарий

Классиеская модель Front-End Back-End. Основные функции приложения - Back, и множество пользовательских GUI - Front. На ней построенно большенство приложений в Linux.

Как перенаправить? Можно попробовать API функции ReadFile/WriteFile и читать из stdin/stdout (соответствущие макросы для них есть), потом вставлять в Memo.

А вот если погуглить, то можно найти и более изящное решение ;)

Ссылка на комментарий
Классиеская модель Front-End Back-End. Основные функции приложения - Back, и множество пользовательских GUI - Front. На ней построенно большенство приложений в Linux.

Как перенаправить? Можно попробовать API функции ReadFile/WriteFile и читать из stdin/stdout (соответствущие макросы для них есть), потом вставлять в Memo.

А вот если погуглить, то можно найти и более изящное решение ;)

да гуглил я полночи. про переназначение ввода/вывода консоли есть, но очень мало. ссылки три всего нашел, где действительно это обсуждается. нашел 2 примера. один ваще на дельфях. переделал на С++ - не работает. второй пример работает только с cmd. там в обоих примерах пайпы какието.. я первый раз про них слышу поэтому не очень понимаю чоэт такое.

кароч тоесть вообще можно сделать так, чтоб сохранялась полная функциональность консольного приложения такого, как TurboProlog или TurboC?

читаю щас в справке про stdin/stdout, нашел тему Creating a Child Process with Redirected Input and Output. нашел как используется

ReadFile(hStdin, chBuf, BUFSIZE, &dwRead, NULL);

читаю. но всеравно пока ничо непонятно. слава богу хоть на английском справка, а то как-то раз пришлось искать ответ на французском форуме)

Добавлено спустя 24 минуты 46 секунд:

по справке смог сделать только это:

#include 
#include
#include

#pragma hdrstop

#pragma argsused

#define BUFSIZE 4096

HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup,
hInputFile, hSaveStdin, hSaveStdout;

BOOL CreateChildProcess(VOID);
unsigned long __stdcall WriteToPipe(VOID *p);
unsigned long __stdcall ReadFromPipe(VOID *p);
VOID ErrorExit(LPTSTR);
VOID ErrMsg(LPTSTR, BOOL);

int main(int argc, char* argv[])
{
SECURITY_ATTRIBUTES saAttr;
BOOL fSuccess;

// Set the bInheritHandle flag so pipe handles are inherited.

saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;

// The steps for redirecting child process's STDOUT:
// 1. Save current STDOUT, to be restored later.
// 2. Create anonymous pipe to be STDOUT for child process.
// 3. Set STDOUT of the parent process to be write handle of
// the pipe, so it is inherited by the child process.
// 4. Create a noninheritable duplicate of the read handle and

// close the inheritable read handle.

// Save the handle to the current STDOUT.

hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);

// Create a pipe for the child process's STDOUT.

if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
ErrorExit("Stdout pipe creation failed\n");

// Set a write handle to the pipe to be STDOUT.

if (! SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr))
ErrorExit("Redirecting STDOUT failed");

// Create noninheritable read handle and close the inheritable read
// handle.

fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
GetCurrentProcess(), &hChildStdoutRdDup , 0,
FALSE,
DUPLICATE_SAME_ACCESS);
if( !fSuccess )
ErrorExit("DuplicateHandle failed");
CloseHandle(hChildStdoutRd);

// The steps for redirecting child process's STDIN:
// 1. Save current STDIN, to be restored later.

// 2. Create anonymous pipe to be STDIN for child process.
// 3. Set STDIN of the parent to be the read handle of the
// pipe, so it is inherited by the child process.
// 4. Create a noninheritable duplicate of the write handle,
// and close the inheritable write handle.

// Save the handle to the current STDIN.

hSaveStdin = GetStdHandle(STD_INPUT_HANDLE);

// Create a pipe for the child process's STDIN.

if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))
ErrorExit("Stdin pipe creation failed\n");

// Set a read handle to the pipe to be STDIN.

if (! SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd))
ErrorExit("Redirecting Stdin failed");

// Duplicate the write handle to the pipe so it is not inherited.

fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
GetCurrentProcess(), &hChildStdinWrDup, 0,
FALSE, // not inherited

DUPLICATE_SAME_ACCESS);
if (! fSuccess)
ErrorExit("DuplicateHandle failed");

CloseHandle(hChildStdinWr);

// Now create the child process.

if (! CreateChildProcess())
ErrorExit("Create process failed");

// After process creation, restore the saved STDIN and STDOUT.

if (! SetStdHandle(STD_INPUT_HANDLE, hSaveStdin))
ErrorExit("Re-redirecting Stdin failed\n");

if (! SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout))

ErrorExit("Re-redirecting Stdout failed\n");

// Get a handle to the parent's input file.

if (argc > 1)
hInputFile = CreateFile(argv[1], GENERIC_READ, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
else
hInputFile = hSaveStdin;

if (hInputFile == INVALID_HANDLE_VALUE)
ErrorExit("no input file\n");

// Write to pipe that is the standard input for a child process.

//WriteToPipe();
CreateThread( 0, 0, WriteToPipe, 0, 0, 0 );

// Read from pipe that is the standard output for child process.

//ReadFromPipe();
CreateThread( 0, 0, ReadFromPipe, 0, 0, 0 );

return 0;
}

BOOL CreateChildProcess()
{
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;

// Set up members of STARTUPINFO structure.

ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);

// Create the child process.

return CreateProcess(NULL,
"c:\\prolog\\prolog.exe", // command line
NULL, // process security attributes
NULL, // primary thread security attributes

TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
}

unsigned long __stdcall WriteToPipe(void *p)
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];

// Read from a file and write its contents to a pipe.

for (;
{
if (! ReadFile(hInputFile, chBuf, BUFSIZE, &dwRead, NULL) ||

dwRead == 0) break;
if (! WriteFile(hChildStdinWrDup, chBuf, dwRead,
&dwWritten, NULL)) break;
}

// Close the pipe handle so the child process stops reading.

if (! CloseHandle(hChildStdinWrDup))
ErrorExit("Close pipe failed\n");
}

unsigned long __stdcall ReadFromPipe(VOID *p)
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

// Close the write end of the pipe before reading from the

// read end of the pipe.

if (!CloseHandle(hChildStdoutWr))
ErrorExit("Closing handle failed");

// Read output from the child process, and write to parent's STDOUT.

for (;
{
if( !ReadFile( hChildStdoutRdDup, chBuf, BUFSIZE, &dwRead,
NULL) || dwRead == 0) break;
if (! WriteFile(hSaveStdout, chBuf, dwRead, &dwWritten, NULL))
break;
}
}

VOID ErrorExit (LPTSTR lpszMessage)
{
fprintf(stderr, "%s\n", lpszMessage);

ExitProcess(0);
}

//---------------------------------------------------------------------------

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

как к VCL-то это дело прикрутить? там нету этого.

Ссылка на комментарий

Великий и ужасный виндовый АПИ *24

Pipe - эта такая сущность для межпроцессорного взаимодействия, действует как двунаправленная труба (по принципу FIFO), читать/писать как из обычного файла. (подробнее - Основы операционных систем)

А работает сей код так:

1. Запоминаем дескрипторы стандартного I/O

2. Делаем пайпы. Перенапавляем стандартный I/O в них

3. Клонируем нужные дескрипторы для предотвращения наследования их дочерним процессом, ненужные закрываем.

4. Пораждаем процесс. Здесь основная фишка: теперь вместо консоли он пишет/читает в пайп, а мы можем писать/читать из них. hChildStdoutRdDup - дескрипотр для чтения с STDOUT предка, hChildStdinWrDup - для записи в STDIN предка.

5. Далее создание инфраструктуры для избежания блокировок - это потоки исполнения (Thread)

Вот и вся канитель. ReadFromPipe/WriteToPipe дописываем/обворачиваем как нам надо, пишем парсер (если надо) и вперед на ГУИ!

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

Ссылка на комментарий
 HANDLE hChildStdinRd, hChildStdinWr, hChildStdinWrDup,
hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup,
hInputFile, hSaveStdin, hSaveStdout;

BOOL CreateChildProcess(AnsiString command);
unsigned long __stdcall ReadFromPipe(void*p);
const BUFSIZE = 2048;

AnsiString TMainForm :: make_console( AnsiString command )
{
AnsiString ret = "success";

SECURITY_ATTRIBUTES saAttr;
BOOL fSuccess;

saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;

hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);

if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
{
return "Stdout pipe creation failed";
}

if (! SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr))
{
return "Redirecting STDOUT failed";
}

fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd, GetCurrentProcess(), &hChildStdoutRdDup , 0, FALSE, DUPLICATE_SAME_ACCESS);
if( !fSuccess )
{
return "DuplicateHandle failed";
}
CloseHandle(hChildStdoutRd);

hSaveStdin = GetStdHandle(STD_INPUT_HANDLE);

if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))
{
return "Stdin pipe creation failed";
}

if (! SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd))
{
return "Redirecting Stdin failed";
}

fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr, GetCurrentProcess(), &hChildStdinWrDup, 0, FALSE, DUPLICATE_SAME_ACCESS);
if (! fSuccess)
{
return "DuplicateHandle failed";
}

CloseHandle(hChildStdinWr);

if(! CreateChildProcess( command ))
{
return "Create process failed";
}

if (! SetStdHandle(STD_INPUT_HANDLE, hSaveStdin))
{
return "Re-redirecting Stdin failed";
}

if (! SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout))
{
return "Re-redirecting Stdout failed";
}

hInputFile = hSaveStdin;

if (hInputFile == INVALID_HANDLE_VALUE)
{
return "no input file";
}

CreateThread( 0, 0,ReadFromPipe, 0, 0, 0);

return ret;
}

//---------------------------------------------------------------------------

BOOL CreateChildProcess( AnsiString command )
{
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;

ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.dwFlags = STARTF_USESHOWWINDOW; //??? ???!
siStartInfo.wShowWindow = SW_HIDE; //??? ???!

return CreateProcess(NULL,
//command.c_str(), // command line
"cmd",
NULL, // process security attributes
NULL, // primary thread security attributes

TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION

}

//---------------------------------------------------------------------------

unsigned long __stdcall ReadFromPipe(void*p)
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

if (!CloseHandle(hChildStdoutWr))
return 0;
//return "Closing handle failed";

for (;
{
if( !ReadFile( hChildStdoutRdDup, chBuf, BUFSIZE, &dwRead,
NULL) || dwRead == 0) break;
if (! WriteFile(hSaveStdout, chBuf, dwRead, &dwWritten, NULL))
break;

MainForm->DialogMemo->Lines->Add( chBuf );
}

}

Ссылка на комментарий
unsigned long __stdcall ReadFromPipe(void*p)

{

DWORD dwRead, dwWritten;

CHAR chBuf[bUFSIZE];

HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

if (!CloseHandle(hChildStdoutWr))

return 0;

//return "Closing handle failed";

for (;;)

{

if( !ReadFile( hChildStdoutRdDup, chBuf, BUFSIZE, &dwRead,

NULL) || dwRead == 0) break;

if (! WriteFile(hSaveStdout, chBuf, dwRead, &dwWritten, NULL))

break;

MainForm->DialogMemo->Lines->Add( chBuf );

}

}

что то забыл?

Ссылка на комментарий

делаю короче так:

 STARTUPINFO si;
char buf[1024],bufin[1024];
DWORD i2,i3;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sa;
HANDLE cstdin, wstdin, rstdout, cstdout;

unsigned long __stdcall DO(void *p)
{

for(;
{
BOOL read=TRUE;
while (read == TRUE)
{
memset(buf,0,1024);
ReadFile(rstdout,buf,1024,&i2,0);
read = (i2 == 1024);

MainForm->DialogMemo->Lines->Add( buf );
}
}

}

//---------------------------------------------------------------------------

AnsiString TMainForm :: make_console( AnsiString command )
{
AnsiString ret = "";

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

ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(STARTUPINFO);
GetStartupInfoA(&si);
CreatePipe(&cstdin, &wstdin, &sa, 0);
CreatePipe(&rstdout, &cstdout, &sa, 0);

si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = FALSE;
si.hStdOutput = cstdout;
si.hStdError = cstdout;
si.hStdInput = cstdin;

CreateProcess( NULL, "cmd", 0, 0, TRUE, CREATE_NEW_CONSOLE, NULL,NULL,&si,π);

CreateThread(0,0,DO,0,0,0);

CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);

DialogMemo->Lines->Add(GetLastError());

return ret;
}

все работает. текст из консоли CMD попадает в DialogMemo. но когда в CreateProcess запускаю не cmd, а, скажем bc.exe (BorlandC), то CreateProcess кажет ошибку 126(The specified module could not be found). Причем если убрать всю эту фигню с подменой потоков, то CreateProcess работает нормально и правильно даже на bc.exe.

В чем дело? может потомуш cmd.exe 32-х разрадный, а bc.exe 16-ти?

Ссылка на комментарий

ВСЕ ПОНЯЛ!

Дело в том, что в некоторых случаях, некоторые 16-битные программы под 9x, 2000 (но не NT) некорректно закрывают консоль. И ReadFile при попытке чтения возвращает ошибку и пустой буфер.

Если вы работает под NT-family, просто вызвайте вашу программу через CMD. Т.е. используйте командную строку типа:

cmd.exe /c myprog.exe

В данном случае, CMD.EXE и поработает тем самым stub'ом, т.е. примет и воспроизведет консольный вывод и корректно все закроет.

Все работает.

Ссылка на комментарий

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...