Я хочу после запуска консольного приложения из Windows оболочки через CreateProcess получить с экрана данные которые выводит это приложение в свою оболочку и у меня не получается. Как же это сделать?
Re: Работа с консоль-приложением из Windows оболочки
Здравствуйте genesys, вы писали:
G>Я хочу после запуска консольного приложения из Windows оболочки через CreateProcess получить с экрана данные которые выводит это приложение в свою оболочку и у меня не получается. Как же это сделать?
Вообще-то это лучше было бы спросить в WINAPI, но тем не менее...
Создается анонимный канал(pipe), затем инициализируется в STARTUPINFO hStdOutput хэндлом записи созданного канала и
dwFlags |= STARTF_USESTDHANDLES, а затем делается CreateProcess c bInheritHandles=TRUE, ну и ReadFile из хэндла чтения канала.
Re[2]: Работа с консоль-приложением из Windows оболочки
Здравствуйте 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");
Здравствуйте 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
нет никакой причины возвращать управление.
AF>Медленно наверное потому, что читается по одному байту. Я бы сделал буфер AF>побольше.
AF>А виснет скорее всего потому, что ты не закрываешь хэндл того конца пайпа, AF>который отдаешь дочернему процессу. В результате для этого конца пайпа AF>получается два хэндла: один в дочернем процессе и один в твоем процессе. AF>Когда дочерний завершается, его хэндл закрывается, но поскольку есть еще AF>один незакрытый хэндл, пайп остается живым с обоих сторон и для ReadFile AF>нет никакой причины возвращать управление.
Спасибо, все заработало! Сделал буфер 80 байт — стало быстрее.
Теперь возникла другая проблема — прочитанные байты я отправляю в CEdit,
но текст появляется только после завершения дочернего процесса.
Пробовал в цикле добавить:
Обновляется, но если я скрываю CEdit, изменяя размеры окна,
то программу приходится "подгонять" мышью (move, click),
иначе программа как-бы подвисает.
А как сделать правильно?
Alexche
Re[5]: Работа с консоль-приложением из Windows оболочки
Здравствуйте 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-поток.
А в качестве заплатки — добавить вот это в цикл чтения из канала:
Здравствуйте genesys, Вы писали:
G>Я хочу после запуска консольного приложения из Windows оболочки через CreateProcess получить с экрана данные которые выводит это приложение в свою оболочку и у меня не получается. Как же это сделать?
Извиняюсь что поднял древнюю тему. Выше приведённый код у меня работает несколько неправильно. Сначала приведу:
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) — чем он больше тем меньше ошибочных строк, но из-за специфики программы я не могу его делать слишком большим. Ну а самое интересное то что если перед чтением из пайпа, поставить задержку(чтобы дождатся когда процесс завершится), то всё нормально читается и выводится. Эта особенность не даёт отладкой найти глюк — при отладке всё проходит шеколадно. Где я накосячил?