Денис дата публикации 21-03-2005 06:58 Перенаправление StdOut в TStream
Статья является продолжением темы о перехвате StdOut'а дочернего
приложения, в частности дополнением к статье Черевко Сергея:
"Перенаправление вывода консольной программы"
В исходной статье имеется недостаток - при добавлении текста в
TString весь считанный блок добавляется как одна строка, что не очень
удобно. Ну и к тому же мне не понравилось что процесс чтения данных из
"трубы" в такой развитой среде как Delphi требует столько
телодвижений, и вызовов функций Win32. Это сподвигло меня искать более
другой способ чтения данных из пайпа. В конце концов я наткнулся на
метод TStrings.LoadFromStream(Stream: TStream), и я подумал вот было
бы неплохо одним махом залить все содержимое "трубы" сразу в Memo (ну
или в любой другой TString-подобный обьект), копая дальше наткнулся на
класс THandleStream, являющийся родителем класса TFileStream, и
потомком TSream. Этот класс предоставляет доступ ко многим ресурсам
(files, sockets, named pipes, mailslots, or other communications
resources), которые предусматривают хендлы (handle).
Но не все так просто, как казалось бы. Дело в том, что дочерний
процесс помещает в "трубу" не все данные, а ровно столько, сколько под
"трубу" выделено, и смиренно ждет пока труба освободится. Поэтому
LoadFromStream помещает в TStrings только часть данных, а добавление
методом LoadFromStream не подразумевается.
И мне все-таки пришлось использовать буфер, а также для слежения
"не пуст ли pipe" функцию Win32 PeekNamedPipe().
И еще есть минус — при чтении блоками, поледняя строка обрывается на
середине, и приходится ее "склеивать" вручную с первой строкой следующего блока.
Но все равно код получился более компактный, и прозрачный, т.к.
основная часть операций с перебросом данных возложена на компоненты
Borland'а (не зря же они их так тщательно разрабатывали ;)
Procedure RunAny(CommandLine: string; Str: TStrings);
var
tRead, cWrite, dwAvail: cardinal;
SA: TSecurityAttributes;
PI: TProcessInformation;
SI: TStartupInfo;
sBuff: THandleStream;
StringBuf: TStringList;
begin
SA.nLength:=SizeOf(SECURITY_ATTRIBUTES);
SA.bInheritHandle:=True;
SA.lpSecurityDescriptor:=nil;
if not CreatePipe(tRead, cWrite, @SA, 0) then Exit;
ZeroMemory(@SI, SizeOf(TStartupInfo));
SI.cb:=SizeOf(TStartupInfo);
SI.dwFlags:=STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
SI.wShowWindow:=SW_HIDE;
SI.hStdOutput:=cWrite;
if CreateProcess(nil, PChar(CommandLine), nil, nil, True, 0, nil, nil, SI, PI)
then begin
Str.Clear();
sBuff := THandleStream.Create(tRead);
StringBuf := TStringList.Create();
repeat
StringBuf.Clear();
StringBuf.LoadFromStream(sBuff);
if StringBuf.Count > 0 then
begin
StringBuf.Strings[0] := Str.Strings[Str.Count-1]+StringBuf.Strings[0];
Str.Delete(Str.Count-1);
end;
Str.AddStrings(StringBuf);
WaitForSingleObject(PI.hProcess, 10);
PeekNamedPipe(tRead, nil, 0, nil, @dwAvail, nil);
until (dwAvail = 0);
CloseHandle(PI.hProcess);
CloseHandle(PI.hThread);
end;
CloseHandle(tRead);
CloseHandle(cWrite);
end;
| |
PS: В WaitForSingleObject магическое число 10, избавиться от него не удалось
с INFINITE ждет до бесконечности, буду рад если кто предложит более
"прямой" вариант.
[Ввод/вывод (StdIn/StdOut)]
Обсуждение материала [ 25-04-2005 11:43 ] 3 сообщения |