Работа с консоль-приложением из Windows оболочки
От: genesys http://oyee.narod.ru
Дата: 12.09.01 09:35
Оценка:
Я хочу после запуска консольного приложения из Windows оболочки через CreateProcess получить с экрана данные которые выводит это приложение в свою оболочку и у меня не получается. Как же это сделать?
Re: Работа с консоль-приложением из Windows оболочки
От: Roman_M rgmroman.narod.ru
Дата: 12.09.01 10:13
Оценка:
Здравствуйте genesys, вы писали:

G>Я хочу после запуска консольного приложения из Windows оболочки через CreateProcess получить с экрана данные которые выводит это приложение в свою оболочку и у меня не получается. Как же это сделать?


Вообще-то это лучше было бы спросить в WINAPI, но тем не менее...

Создается анонимный канал(pipe), затем инициализируется в STARTUPINFO hStdOutput хэндлом записи созданного канала и
dwFlags |= STARTF_USESTDHANDLES, а затем делается CreateProcess c bInheritHandles=TRUE, ну и ReadFile из хэндла чтения канала.
Re[2]: Работа с консоль-приложением из Windows оболочки
От: Alexche  
Дата: 24.09.01 04:36
Оценка:
Здравствуйте Roman_M, вы писали:

RM>Создается анонимный канал(pipe), затем инициализируется в STARTUPINFO hStdOutput хэндлом записи созданного канала и

RM>dwFlags |= STARTF_USESTDHANDLES, а затем делается CreateProcess c bInheritHandles=TRUE, ну и ReadFile из хэндла чтения канала.

Работает, но очень медленно: программа уже завершила работу, а выведенные сообщения еще долго читаются, а после того, как все прочитается программа зависает на ReadFile
(ни один MessageBox не срабатывает)
Что здесь неправильно?

do
{
fSuccess = ReadFile( hReadPipe, &bReadBuffer, 1, &cbReadBuffer, NULL);
if( !fSuccess )
if( GetLastError() == ERROR_BROKEN_PIPE )
{
MessageBox("Child has died");
break; // child has died
}
else MessageBox("Pipe error");

{
...
}
} while( fSuccess && cbReadBuffer );
Alexche
Чтение вывода дочерней консоли через pipe
От: Alex Fedotov США  
Дата: 24.09.01 05:10
Оценка: 5 (1)
#Имя: FAQ.winapi.readconsoleoutput.pipe
Здравствуйте Alexche, вы писали:

RM>>Создается анонимный канал(pipe), затем инициализируется в STARTUPINFO hStdOutput хэндлом записи созданного канала и

RM>>dwFlags |= STARTF_USESTDHANDLES, а затем делается CreateProcess c bInheritHandles=TRUE, ну и ReadFile из хэндла чтения канала.

A>Работает, но очень медленно: программа уже завершила работу, а выведенные сообщения еще долго читаются, а после того, как все прочитается программа зависает на ReadFile

A>(ни один MessageBox не срабатывает)
A>Что здесь неправильно?

A>do

A>{
A> fSuccess = ReadFile( hReadPipe, &bReadBuffer, 1, &cbReadBuffer, NULL);
A> if( !fSuccess )
A> if( GetLastError() == ERROR_BROKEN_PIPE )
A> {
A> MessageBox("Child has died");
A> break; // child has died
A> }
A> else MessageBox("Pipe error");

A> {

A> ...
A> }
A>} while( fSuccess && cbReadBuffer );

Медленно наверное потому, что читается по одному байту. Я бы сделал буфер
побольше.

А виснет скорее всего потому, что ты не закрываешь хэндл того конца пайпа,
который отдаешь дочернему процессу. В результате для этого конца пайпа
получается два хэндла: один в дочернем процессе и один в твоем процессе.
Когда дочерний завершается, его хэндл закрывается, но поскольку есть еще
один незакрытый хэндл, пайп остается живым с обоих сторон и для ReadFile
нет никакой причины возвращать управление.

Другими словами, надо делать так:

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

HANDLE hRead, hWrite;
if (!CreatePipe(&hRead, &hWrite, &sa, 0))
    ...

STARTUPINFO si;
memset(&si, 0, sizeof(si));

si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = ...;
si.hStdOutput = hWrite;
si.hStdError = ...;

if (!CreateProcess(NULL, pszCmdLine, NULL, NULL,
    TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi))
    ...

CloseHandle(hWrite);  // <--

while (ReadFile(hRead, ...))
{
     ...
}
-- Alex Fedotov
Re[4]: Работа с консоль-приложением из Windows оболочки
От: Alexche  
Дата: 26.09.01 01:10
Оценка:
Здравствуйте Alex Fedotov, вы писали:


AF>Медленно наверное потому, что читается по одному байту. Я бы сделал буфер

AF>побольше.

AF>А виснет скорее всего потому, что ты не закрываешь хэндл того конца пайпа,

AF>который отдаешь дочернему процессу. В результате для этого конца пайпа
AF>получается два хэндла: один в дочернем процессе и один в твоем процессе.
AF>Когда дочерний завершается, его хэндл закрывается, но поскольку есть еще
AF>один незакрытый хэндл, пайп остается живым с обоих сторон и для ReadFile
AF>нет никакой причины возвращать управление.

Спасибо, все заработало! Сделал буфер 80 байт — стало быстрее.
Теперь возникла другая проблема — прочитанные байты я отправляю в CEdit,
но текст появляется только после завершения дочернего процесса.
Пробовал в цикле добавить:

MSG Msg;
GetMessage(&Msg, NULL, 0, 0);
DispatchMessage(&Msg);

Обновляется, но если я скрываю CEdit, изменяя размеры окна,
то программу приходится "подгонять" мышью (move, click),
иначе программа как-бы подвисает.
А как сделать правильно?
Alexche
Re[5]: Работа с консоль-приложением из Windows оболочки
От: Alex Fedotov США  
Дата: 26.09.01 05:01
Оценка:
Здравствуйте Alexche, вы писали:

A>Спасибо, все заработало! Сделал буфер 80 байт — стало быстрее.

A>Теперь возникла другая проблема — прочитанные байты я отправляю в CEdit,
A>но текст появляется только после завершения дочернего процесса.
A>Пробовал в цикле добавить:

A> MSG Msg;

A> GetMessage(&Msg, NULL, 0, 0);
A> DispatchMessage(&Msg);

A>Обновляется, но если я скрываю CEdit, изменяя размеры окна,

A>то программу приходится "подгонять" мышью (move, click),
A>иначе программа как-бы подвисает.
A>А как сделать правильно?

Совсем правильно будет запустить чтение из канала в отдельном потоке,
чтобы не блокировать UI-поток.

А в качестве заплатки — добавить вот это в цикл чтения из канала:

MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}
-- Alex Fedotov
Re: Работа с консоль-приложением из Windows оболочки
От: &reY Украина http://www.livejournal.com/~1000turov/
Дата: 10.05.02 08:18
Оценка:
Здравствуйте genesys, Вы писали:

G>Я хочу после запуска консольного приложения из Windows оболочки через CreateProcess получить с экрана данные которые выводит это приложение в свою оболочку и у меня не получается. Как же это сделать?


может это тоже самое
http://www.rsdn.ru/forum/?mid=52850
Автор: Oxy
Дата: 09.05.02
Re[2]: Работа с консоль-приложением из Windows оболочки
От: mein Молдова http://people.overclockers.ru/mein
Дата: 20.04.06 08:21
Оценка:
Извиняюсь что поднял древнюю тему. Выше приведённый код у меня работает несколько неправильно. Сначала приведу:

void CQueue::OnBnClickedButton1()
{
    SECURITY_ATTRIBUTES sa;
    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = TRUE;

    HANDLE hRead, hWrite;
    if (!CreatePipe(&hRead, &hWrite, &sa, 0))
        MessageBox(L"error create pipe");

    STARTUPINFO si;
    memset(&si, 0, sizeof(si));

    si.cb = sizeof(si);
    si.dwFlags = STARTF_USESTDHANDLES;
    si.hStdInput = NULL;
    si.hStdOutput = NULL;
    si.hStdError = hWrite;

    PROCESS_INFORMATION pi; 
    memset(&pi, 0, sizeof(pi));

    if (!CreateProcess(L"xxx.exe",L" --xxx", NULL, NULL,
        TRUE, CREATE_NO_WINDOW | IDLE_PRIORITY_CLASS, NULL, NULL, &si, &pi))
        MessageBox(L"error create process");

    CloseHandle(hWrite);  // <--

    char xxx[1000];
    DWORD dd;
    while(ReadFile(hRead,xxx,400,&dd,0))
    {
        printbuffer(IDC_LIST1,xxx,dd);
    }    
    CloseHandle( pi.hProcess ); //  А надо ли? 
      CloseHandle( pi.hThread );  //  А надо ли?
}

void CQueue::printbuffer(int nID, char buf[], int kol){ // выводит построчно в ListBox данные
    wchar_t str[1000];
    CString edit;
    char tempbuf[1000]; 
      int pos=0;
    if(strlen(tbuf)){ // если с предыдущего буфера осталась незаконченая строка, то продолжим
        strcpy(tempbuf,tbuf);
        pos = (int)strlen(tempbuf);
    }
    for(int i=0;i<kol;i++){
        if(buf[i]==13){ // последняя найденная строка
            MultiByteToWideChar(CP_ACP,0,tempbuf,pos,str,1000);
            str[pos]=0; 
            pos=0; 
            ((CListBox*)GetDlgItem(nID))->AddString(str);
        }else{
            if(buf[i]==10)continue;
            tempbuf[pos++] = buf[i];
        }
    }
    if(pos>0){ // если буфер закончился, а строка не закончена, то запомним остатки до следующего буфера
        strncpy(tbuf,tempbuf,pos);
        tbuf[pos]=0;
    }
}

В общем проблема такая:
Иногда в листбокс попадают строки с различными добавками(слева) причём добавки берутся из предыдущих строк. Вот пример как должно быть
"12345 abcdfhgg"
"xxxxx 1/100"
"xxxxx 2/100"
А вот пример как получается:
"12345 abcdfhgg"
"123xxxxx 1/100"
"123xxxxx 2/100"
Причём добавки бывают разной длинны и необязательно ровно с предыдущей строки — бывает и с более ранних. На конфигурацию добавок напрямую влияет размер буфера (у меня в примере 400) — чем он больше тем меньше ошибочных строк, но из-за специфики программы я не могу его делать слишком большим. Ну а самое интересное то что если перед чтением из пайпа, поставить задержку(чтобы дождатся когда процесс завершится), то всё нормально читается и выводится. Эта особенность не даёт отладкой найти глюк — при отладке всё проходит шеколадно. Где я накосячил?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.