Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Низкоуровневое программирование > Win Api
Регистрация

Восстановить пароль

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 30.05.2008, 12:58   #1
d0vgan
 
Регистрация: 30.05.2008
Сообщений: 5
По умолчанию Console Output Redirection, PeekNamedPipe

Добрый день.

Сделал сабж, всё работает за исключением одного большого "НО". Бьюсь над этим "НО" уже достаточно долгое время, никак не найду решения.

Итак, стандартная задача: перехват вывода консольной программы.
Решение тоже стандартное: создаются read/write pipes с помощью CreatePipe(), затем вызывается CreateProcess(), которому в STARTUPINFO передаются handles этих pipes.

Чтение из pipe делается примерно так:

Код:
    dwBytesRead = 0;
    if ( PeekNamedPipe(m_hStdOutReadPipe, NULL, 0, NULL, &dwBytesRead, NULL) && dwBytesRead > 0 )
    {
        ZeroMemory(Buf, BUF_SIZE);
        dwBytesRead = 0;
        if ( ReadFile(m_hStdOutReadPipe, Buf, BUF_SIZE-1, &dwBytesRead, NULL) && dwBytesRead > 0 )
        {
            // some data has been read from the Pipe
            ...
        }
    }
То есть сначала PeekNamedPipe() удостоверяется в том, что в pipe есть какие-то данные, а затем их читает.

А теперь - то самое "НО", о котором я упоминал.
Если дочерний процесс (который вызывается с помощью CreateProcess) выполняет много действий или использует что-то вроде Sleep(50) между выводами в консоль, то PeekNamedPipe() в моей программе очень часто возвращает 0 независимо от того, вывел дочерний процесс что-то в консоль или нет. Из-за этого чтение из m_hStdOutReadPipe происходит не каждый раз, как дочерний процесс что-то выводит в консоль, а во много раз реже.

Чтобы не быть голосовным, пусть у нас есть такая консольная программа, вывод которой мы хотим перехватить:

Код:
void timeout(void);

int main(void)
{
    int i = 0;

    for ( ; i < 100; i++ )
    {
        printf("Line %d\n", i);
        timeout();
    }
    
    return 0;
}

void timeout(void)
{
    double f = 0.01;
    int    i = 0;

    for ( ; i < 100000; i++ )
    {
        f += 0.01;
        sin(f);
    }
}
В консоли мы видим появляющиеся через определенный промежуток времени строки

Line 0
Line 1
Line 2
...

А при попытке перехватить вывод этой программы с помощью pipe сначала мы вообще ничего не видим, а в самом конце нам разом вываливаются все строки от

Line 0

до

Line 99

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

Может быть, кто-то уже сталкивался с подобной проблемой и знает, что можно сделать? Люди, поделитесь! Или хоть ссылочку дайте, где искать.

Последний раз редактировалось B_N; 30.05.2008 в 16:45.
d0vgan вне форума Ответить с цитированием
Старый 30.05.2008, 16:47   #2
B_N
Новичок
Джуниор
 
Регистрация: 18.01.2008
Сообщений: 1,720
По умолчанию

Через OVERLAPPED попробуйте читать.
B_N вне форума Ответить с цитированием
Старый 30.05.2008, 19:10   #3
d0vgan
 
Регистрация: 30.05.2008
Сообщений: 5
По умолчанию

Цитата:
Сообщение от B_N Посмотреть сообщение
Через OVERLAPPED попробуйте читать.
То есть нужно использовать ConnectNamedPipe, и под Windows 9x это работать не будет?
d0vgan вне форума Ответить с цитированием
Старый 31.05.2008, 01:18   #4
B_N
Новичок
Джуниор
 
Регистрация: 18.01.2008
Сообщений: 1,720
По умолчанию

Цитата:
Сообщение от d0vgan Посмотреть сообщение
То есть нужно использовать ConnectNamedPipe, и под Windows 9x это работать не будет?
Нет, я предлагал воспользоваться структурой OVERLAPPED (Последний параметр ReadFile) и вообще отказаться от PeekNamedPipe, оставив всю синхронизацию операционной системе. (В MSDN даже есть такой пример). Воспроизодить Вашу проблему с нуля пока не хочется , потому и предлагаю, проверьте так. Здесь явно какие-то сложности с буферизацией, пусть ReadFile сама с ними разбирается. А насчёт 9x - нет, конечно:
Цитата:
Windows Me/98/95: Named pipes cannot be created.
B_N вне форума Ответить с цитированием
Старый 02.06.2008, 17:59   #5
Somebody
Участник клуба
 
Регистрация: 08.10.2007
Сообщений: 1,185
По умолчанию

Я потестил это в Delphi и вот к чему пришёл: проблема в том, что при использовании Write (printf) в консоль выводится всё потоком, но при появлении пайпа (похоже, что и в Delphi, и в C++) откуда-то появляется буферизация. Если вместо Write (printf) использовать WriteFile, то всё выводится без задержек, потоком.
Во вложении исходники: Reader - читает вывод запущенного им процесса, Writer - пишет строки с помощью Write, Writer2 - пишет строки с помощью WriteFile.
Вложения
Тип файла: rar Pipes.rar (801 байт, 68 просмотров)
Somebody вне форума Ответить с цитированием
Старый 03.06.2008, 22:45   #6
d0vgan
 
Регистрация: 30.05.2008
Сообщений: 5
По умолчанию

По-моему, операции вывода в принципе являются буферизированными, причем буферизация эта управляется самой системой. Поэтому и существуют функции типа flush, fflush, заставляющие весь этот буфер слиться в место назначения.
Но это со стороны дочернего процесса, вывод которого мы хотим перехватить.
В данном случае вопрос в том, как прочесть из нашей программы, которая запускает дочерний процесс, данные этого дочернего процесса, которые он вроде бы вывел, но которые еще не попали в pipe.
С overlapped пока что не получилось, но похоже, что это единственный возможный вариант решения. Или начать пляску с бубном вокруг размера памяти для pipe - вдруг какое-то "магическое" значение решит проблему с буферизацией...
d0vgan вне форума Ответить с цитированием
Старый 04.06.2008, 15:10   #7
Somebody
Участник клуба
 
Регистрация: 08.10.2007
Сообщений: 1,185
По умолчанию

Цитата:
Сообщение от d0vgan Посмотреть сообщение
По-моему, операции вывода в принципе являются буферизированными, причем буферизация эта управляется самой системой. Поэтому и существуют функции типа flush, fflush, заставляющие весь этот буфер слиться в место назначения.
Стандартные Delphi (про C++ не знаю) функции вроде работают со своей собственной буферизацией (SetTextBuf - установка буфера; по умолчанию - что-то в переменных Text такое есть, то ли буфер, то ли указатель).
Не думаю, что со стороны, читающей через пайп, можно что-то ещё сделать; наверное, это всё. Остаётся только по возможности использовать в дочернем процессе (если своя прога) апишные функции.
Somebody вне форума Ответить с цитированием
Старый 05.09.2021, 13:57   #8
lvvas
 
Регистрация: 22.08.2018
Сообщений: 8
По умолчанию

Цитата:
Сообщение от d0vgan Посмотреть сообщение
В данном случае вопрос в том, как прочесть из нашей программы, которая запускает дочерний процесс, данные этого дочернего процесса, которые он вроде бы вывел, но которые еще не попали в pipe.
С overlapped пока что не получилось, но похоже, что это единственный возможный вариант решения. Или начать пляску с бубном вокруг размера памяти для pipe - вдруг какое-то "магическое" значение решит проблему с буферизацией...
Ну, так Вы решили указанную проблему? Подскажите как. Я тоже с этим столкнулся, и не могу справиться (в неименованных pipe).
Проблема легко решается либо остановкой дочернего процесса, либо закрытием "ручки pipe". Но мне это не подходит, поскольку дочерний процесс зациклен и выводит данные постепенно (порциями), котрые и считывать нужно родительским процессом порциями, по мере поступления. Это в принципе возможно?
Пока конець pipe не закрыт ReadFile "висит", а PeekNamedPipe (которым пробовал заменить ReadFile) тоже ничего не читает без окончания процесса или закрытия pipe.
lvvas вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Вопрос по ввыводу данных в output.txt C++ Jugger Общие вопросы C/C++ 2 31.01.2008 16:14
Открытие документов Microsofrt Common Console через delphi )Игнат( Общие вопросы Delphi 6 24.01.2008 17:09
Выполнение хранимой процедуры с output параметром Иванчо БД в Delphi 5 26.10.2007 14:59