Никогда этим не занималься, мож направит кто-нить на путь истинный.....
Проблема такая — есть функции, (например в dll), которые исспользуют консольный вывод (типа printf).
Есть также ГУИ приложение, запускающее эти функции. Так вот хотелось бы весь консольный вывод направлять в некое окошко, в EDIT контрол, например.
AllocCOnsole — вроде не катит — ибо оно открывает новую черную ужасную консоленцию.... А хотелось бы чего-то в стиле вывода статуса процесса компиляции у MSVS....
Здравствуйте Sasparella, Вы писали:
S>Проблема такая — есть функции, (например в dll), которые исспользуют консольный вывод (типа printf).
S>Есть также ГУИ приложение, запускающее эти функции. Так вот хотелось бы весь консольный вывод направлять в некое окошко, в EDIT контрол, например.
Сделать SetStdHandle и перенаправить стандартный вывод в pipe. В отдельном потоке из pipe читать, а там уж хочешь в EDIT, хочешь еще куда передавай.
Здравствуйте Sasparella, Вы писали:
S>Есть также ГУИ приложение, запускающее эти функции. Так вот хотелось бы весь консольный вывод направлять в некое окошко, в EDIT контрол, например.
S>AllocCOnsole — вроде не катит — ибо оно открывает новую черную ужасную консоленцию.... А хотелось бы чего-то в стиле вывода статуса процесса компиляции у MSVS....
S>Подскажите, плиз, куда копать?
Дополняя Alex Fedotov.
Сегодня в форуме С++ http://www.rsdn.ru/forum/message.asp?mid=13138
Здравствуйте Sasparella, Вы писали:
S>Есть также ГУИ приложение, запускающее эти функции. Так вот хотелось бы весь консольный вывод направлять в некое окошко, в EDIT контрол, например.
S>Подскажите, плиз, куда копать?
Здравствуйте Sashko, Вы писали:
S>Здравствуйте Sasparella, Вы писали:
S>>Есть также ГУИ приложение, запускающее эти функции. Так вот хотелось бы весь консольный вывод направлять в некое окошко, в EDIT контрол, например.
S>>Подскажите, плиз, куда копать?
S>Creating a Child Process with Redirected Input and Output (поищи в MSDN Library) или жми сюда S>http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/prothred_4uus.asp
S>Успехов.
Спасибо всем ОГРОМНОЕ! Буду разбираться .... Key Idea получена
Никак не могу понять —
в SDI приложении я пишу следующее (cм внизу)
Причем странно 3 вещи.
1. Когда я до махинаций с StdHandle вызываю GetSTDHandle(STD_OUTPUT_HANDLE)- он чтото возвращает — НЕ INVALID_HANDLE_HANDLE. Какой, спрашивается STD Handle у НЕКОНСОЛЬНОГО приложения?
2. printf("printf2"); НЕ срабатывает. А WriteFile во второй коней пайпа работает. Значит, как я понимаю, не установился STDHandle. А почему тогда не вернул ошибку?
3. Не слишком ли жестоко так организовывать цикл чтения из пайпа? Это же жестоко будет грузить прцессор.. Нет ли какой функции типа WaitForSingleObject? (они вроде к пайпу неприменимы?)
Спасибо...
HANDLE hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);
BOOL btest = (hSaveStdout == INVALID_HANDLE_VALUE);
// bTest тут == FALSE
SECURITY_ATTRIBUTES saAttr;
BOOL fSuccess;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
AfxMessageBox("Stdout pipe creation failed\n");
if (! SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr))
AfxMessageBox("Redirecting STDOUT failed");
fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
GetCurrentProcess(), &hChildStdoutRdDup , 0,
FALSE,
DUPLICATE_SAME_ACCESS);
if( !fSuccess )
AfxMessageBox("DuplicateHandle failed");
CloseHandle(hChildStdoutRd);
printf("printf1");
AfxBeginThread(ReadFromPipe,0);
printf("printf2");// Это не отрабатывает
Sleep(200);
DWORD dwWritten;
char chBuf[] ="kuku";
WriteFile(hChildStdoutWr, chBuf, 4, &dwWritten, NULL) ; // а это - работает
}
#define BUFSIZE 4096
UINT ReadFromPipe(LPVOID pParam)
{
DWORD dwRead;
CHAR chBuf[BUFSIZE];
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
for (;;)
{
if(!ReadFile( hChildStdoutRdDup, chBuf, BUFSIZE, &dwRead, NULL))
DisplayErrorMessage(""); Это тоже не вызывается if(dwRead != 0)
{
chBuf[dwRead]=0;
AfxMessageBox(chBuf); А тут - вываливает только "kuku"
}
}
return 0;
}
S> HANDLE hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);
S> BOOL btest = (hSaveStdout == INVALID_HANDLE_VALUE);
S>// bTest тут == FALSE
S> SECURITY_ATTRIBUTES saAttr;
S> BOOL fSuccess;
S>
S> saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
S> saAttr.bInheritHandle = TRUE;
S> saAttr.lpSecurityDescriptor = NULL;
S> if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
S> AfxMessageBox("Stdout pipe creation failed\n");
S>
S> if (! SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr))
S> AfxMessageBox("Redirecting STDOUT failed");
S>
S> fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
S> GetCurrentProcess(), &hChildStdoutRdDup , 0,
S> FALSE,
S> DUPLICATE_SAME_ACCESS);
S> if( !fSuccess )
S> AfxMessageBox("DuplicateHandle failed");
S> CloseHandle(hChildStdoutRd);
Когда ты используешь второй коней pipe в том же процессе,
то дуплицировать его ни к чему. Поэтому вызовы DuplicateHandle
и CloseHandle можно безболезненно убрать, и далее пользоваться
hChildStdoutRd.
S> printf("printf1");
S> AfxBeginThread(ReadFromPipe,0);
S> printf("printf2");// Это не отрабатывает
Здесь бы я для начала попробовал printf("printf2\n");,
чтобы убедиться что ты сражаешься не с буферизацией в
стандартной библиотеке.
S>
S> Sleep(200);
S> DWORD dwWritten;
S> char chBuf[] ="kuku";
S> WriteFile(hChildStdoutWr, chBuf, 4, &dwWritten, NULL) ; // а это - работает
S>}
S>#define BUFSIZE 4096
S>UINT ReadFromPipe(LPVOID pParam)
S>{
S>
S> DWORD dwRead;
S> CHAR chBuf[BUFSIZE];
S> HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
S>
S> for (;;)
S> {
S> if(!ReadFile( hChildStdoutRdDup, chBuf, BUFSIZE, &dwRead, NULL))
S> DisplayErrorMessage(""); Это тоже не вызывается
Этот вызов сработает, когда другой конец pipe закроют. В ситуации с двумя
процессами это происходит автоматически, когда дочерний процесс завершается,
но в твоем случае все происходит в одном процессе и ты сам ответственнен за
закрытие pipe.
S>
S> if(dwRead != 0)
S> {
S> chBuf[dwRead]=0;
S> AfxMessageBox(chBuf); А тут - вываливает только "kuku"
S> }
S> }
S> return 0;
S>}
S>
Большое спасибо за быстрый ответ,
AF>Комментарии прямо в коде.
AF>[ccode]
S>> HANDLE hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE); S>> BOOL btest = (hSaveStdout == INVALID_HANDLE_VALUE); S>>// bTest тут == FALSE
Все-таки что это он может возвращать в SDI приложении абсолютно неясно...
Самое интересное что printf(чтото) (до всяких SetSTD...)пишут "в никуда" — и ошибок никаких не генерируется...
S>> printf("printf1"); S>> AfxBeginThread(ReadFromPipe,0); S>> printf("printf2"); // Это не отрабатывает
AF>Здесь бы я для начала попробовал printf("printf2\n");, AF>чтобы убедиться что ты сражаешься не с буферизацией в AF>стандартной библиотеке.
Не помогло Даже fflush(stdout); пробовал...
S>> S>> Sleep(200); S>> DWORD dwWritten; S>> char chBuf[] ="kuku"; S>> WriteFile(hChildStdoutWr, chBuf, 4, &dwWritten, NULL) ; // а это — работает
S>>}
....
S>> if(!ReadFile( hChildStdoutRdDup, chBuf, BUFSIZE, &dwRead, NULL)) S>> DisplayErrorMessage(""); Это тоже не вызывается
AF>Этот вызов сработает, когда другой конец pipe закроют. В ситуации с двумя AF>процессами это происходит автоматически, когда дочерний процесс завершается, AF>но в твоем случае все происходит в одном процессе и ты сам ответственнен за AF>закрытие pipe.
А почему этот вызов ReadFile не ждет входа? ТО есть он при каждом вызове постоянно возвращает dwRead ==0. А чтение с com-порта когдто вроде ждало....
Как лучше огрганизовать чтение? Хотя может он начнет ждать когда проблема с STDHandle пропадет....
Это вот настораживает, а то можно было подумать что просто стандартная библиотеке закештровала гдето хэндлы и ведать не ведает что ктото их подменил......
S>> S>> if(dwRead != 0) S>> { S>> chBuf[dwRead]=0; S>> AfxMessageBox(chBuf); А тут — вываливает только "kuku"
S>> } S>> } S>> return 0; S>>}
Здравствуйте Sasparella, Вы писали:
S>Это вот настораживает, а то можно было подумать что просто стандартная библиотеке закештровала гдето хэндлы и ведать не ведает что ктото их подменил......
Кстати, хорошая мысль. Возможно это так и есть. Если тебя не пугают внутренности printf, попробуй пройти по ним в отладчике, посмотри, куда на самом деле она выводит.
Здравствуйте Alex Fedotov, Вы писали:
AF>Здравствуйте Sasparella, Вы писали:
S>>Это вот настораживает, а то можно было подумать что просто стандартная библиотеке закештровала гдето хэндлы и ведать не ведает что ктото их подменил......
AF>Кстати, хорошая мысль. Возможно это так и есть. Если тебя не пугают внутренности printf, попробуй пройти по ним в отладчике, посмотри, куда на самом деле она выводит.
Как раз сейчас это и делал )).. Заблудился , дорогу домой еле нашел.... Уж очень там монстроидально......
Где-то когда то я обрывочно слышал, что в таких случаях нужно пересоздавать какието буферы для stdin и stdout. Мож быть вы в курсе, что это могло быть?
Но одно меня бесит, у меня же НЕ КОНСОЛЬНОЕ Windows приложение!!!!!!! Ну зачем мне все эти буферы в нем нужны? Ну, понятно, станд. библ. чтото творит, но как то непонятно она это делает...
Раз ничего в пайп не попадает, значит SetStdHandle не достаточно для перенастройки stdout.
А что нужно сделат ьчтобы было достаточно?
Попытка переоткрыть CONOUT$ проваливается с Access Denied
if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
AfxMessageBox("Stdout pipe creation failed\n");
// Set a write handle to the pipe to be STDOUT. if (! SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr))
AfxMessageBox("Redirecting STDOUT failed");
if (!freopen("CONOUT$","a",stdout)) // вместо "a" можно "wt", один черт.
DisplayErrorMessage(""); // Tут Access Denied
Вопрос, почему?
Неужели никто с таким не сталкивался?
Саша.
P.S. Оставшийся открытым вопрос — что возвращает GetStdHandle(STD_OUTPUT_HANDLE) в SDI приложении (до махинаций с нэндлами)?
Здравствуйте Sasparella, Вы писали:
S>Где-то когда то я обрывочно слышал, что в таких случаях нужно пересоздавать какието буферы для stdin и stdout. Мож быть вы в курсе, что это могло быть?
S>Но одно меня бесит, у меня же НЕ КОНСОЛЬНОЕ Windows приложение!!!!!!! Ну зачем мне все эти буферы в нем нужны? Ну, понятно, станд. библ. чтото творит, но как то непонятно она это делает...
Дело в том, что stdout вполне существует и у приложения, совсем не имеющего консоли. Вот пример (вломы было WriteFile делать, так я printf использовал) :
// cl /DWINDOWS out.cpp#include <windows.h>
#include <stdio.h>
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
printf("Вот такие дела...");
return 0;
}
Если теперь запустить эту программу (у которой нет ни окна, ни консоли) с редиректором >1, то на диске появится файл "1" с выводимой строкой.
PS: так ты видел KB HOWTO: Spawn Console Processes with Redirected Standard Handles (Q190351) или нет?
Здравствуйте retalik, Вы писали:
R>Если теперь запустить эту программу (у которой нет ни окна, ни консоли) с редиректором >1, то на диске появится файл "1" с выводимой строкой.
Ясно, спасибо — не знал
R>PS: так ты видел KB HOWTO: Spawn Console Processes with Redirected Standard Handles (Q190351) или нет?
Угу. С нее и начал копать. Там все работает потому что вначале настраиваютя хендлы а потом в контекста новых хендлов создается дочерний процесс. И в нем при инициализация стандарной библиотеки используюся уже нужные наследованые хэндлы.. Поэтому все путем. А тут — я пытаюсь (как я понимаю) перенастроить вывод, оставаясь в своем родном процессе. Это то и не получается.
То есть pipe созается и работает на раз — если писать в него WriteFile-ом, то все работает, а вот SetStdHandle(его-же) + printf — никак не хочет....
Наверное можно запускать мой процесс из некого временного, в котором готовить именованые каналы, все настраивать — и в своем их открывать и исспользовать- но это ЧЕРЕЗВЫЧАЙНО криво, и потому даже пробовать не хочу. И еще не факт что получится.
Тут явно что то простое, чего я не знаю — КАК ПЕРЕИНИЦИАЛИЗИРОВАТЬ _все_что_связано_с_выводом_в_стандартной_библиотеке.
Народ, откликнитесь — а то уже три дня буксую
Саша.
Здравствуйте Alex Fedotov, Вы писали:
AF>Здравствуйте Sasparella, Вы писали:
S>>Это вот настораживает, а то можно было подумать что просто стандартная библиотеке закештровала гдето хэндлы и ведать не ведает что ктото их подменил......
AF>Кстати, хорошая мысль. Возможно это так и есть. Если тебя не пугают внутренности printf, попробуй пройти по ним в отладчике, посмотри, куда на самом деле она выводит.
Здравствуйте Sasparella, Вы писали:
S>Проделал я такой эксперимент: S>[...] S>Как я понимаю, этот факт потдверждает что проблема связана с переинициализацией библиотечного кеша/буферов/черт_знает_еще_чего.
S>Как же из этого выкрутится?
Ты сам ответил на свой вопрос — загружать DLL после того, как вывод перенаправлен. В противном случае общего решения добиться не удастся, поскольку оно будет зависеть от версии компилятора, версии стандартной библиотеки и способа, которым эта самая библиотека прилинкована.
AF>Ты сам ответил на свой вопрос — загружать DLL после того, как вывод перенаправлен. В противном случае общего решения добиться не удастся, поскольку оно будет зависеть от версии компилятора, версии стандартной библиотеки и способа, которым эта самая библиотека прилинкована.
Угу. Тут тоже опять застрял...
Оказалось,. что если загрузить длл, printf там чтото и выгрузить — то сообщения приходят. А если не выгружать — то не приходят. Приходят все вместе только при выгрузке длл.
Здравствуйте Sashko, Вы писали:
S>Здравствуйте Sasparella, Вы писали:
S>...
S>Вопрос следующий. Зачем ловить вывод printf'а в это же процессе. Какова задача.
Задача в следующем — есть рассчетные модули, написаные в основном на ФОТРРАН и оформленные как dll, — они читают файлы, пишут файлы, и сообщают о статусе. (это просто число, передаваемое по ссылке). Есть еще exe -ГУИ, осуществляющее управление данными пользователя и запуском рассчетных функций.
Тк вот иногда нужно сообщить о некой редкой и нертривиальной ошибке в процессе рассчета — отдельный механизм передачи сообщений вводить не хочется, дабы не захламлять dll-ки — а вот перехват консолььных сообщений — как раз в самы й раз — ибо эти длл раньще как правило отлаживаются и работают как condole app, и все механизмы уведомления пользователя через консоль там уже запрограммированы .
Кстати, из ФОРТРАН модулей перехват вывода на консоль работает просто чудесно — это я только что заметил до этого тестировал сишной длл-кой .
Более того, cout << "Test Out"; из сей тоже работает!
А вот printf("чтото \n"), если после него не вызывать fflush(stdout), не хочет.
Причем у него что, буффер бездонный — я там строк 1000 типа "Kuku from DLL" в цикле вывел — а он все буфферизировал и буфферизировал и ничего в pipe не попадало...
А потом fflush- ем они хором выплюнулись.
Так что свою задачу я в общем-то решил (с фотрана то все ок), СПАСИБО ВСЕМ ОГРОМНОЕ за поддержку, только вот осталось разобраться с буфферизацией printf-a. — а вдруг кто-то (в общем то я сам захочет на С длл сделать
Здравствуйте Sasparella, Вы писали:
S>Угу. С нее и начал копать. Там все работает потому что вначале настраиваютя хендлы а потом в контекста новых хендлов создается дочерний процесс. И в нем при инициализация стандарной библиотеки используюся уже нужные наследованые хэндлы.. Поэтому все путем. А тут — я пытаюсь (как я понимаю) перенастроить вывод, оставаясь в своем родном процессе. Это то и не получается.
S>То есть pipe созается и работает на раз — если писать в него WriteFile-ом, то все работает, а вот SetStdHandle(его-же) + printf — никак не хочет....
S>Наверное можно запускать мой процесс из некого временного, в котором готовить именованые каналы, все настраивать — и в своем их открывать и исспользовать- но это ЧЕРЕЗВЫЧАЙНО криво, и потому даже пробовать не хочу. И еще не факт что получится.
S>Тут явно что то простое, чего я не знаю — КАК ПЕРЕИНИЦИАЛИЗИРОВАТЬ _все_что_связано_с_выводом_в_стандартной_библиотеке.
S>Народ, откликнитесь — а то уже три дня буксую S>Саша.
Ну, есть еще решение, работало еще с досовских (и юниксовых) времен: старые добрые dup и dup2.
В MSDN для них даже пример есть: перенаправляется собственный вывод в файл. Для пайпов, наверное, тоже сойдет.