Текущий архив: 2008.01.13;
Скачать: CL | DM;
Вниз
Как правильно сделать вывод из консоли? Найти похожие ветки
← →
Kolan © (2007-12-11 15:38) [0]Роясь в нете сдела такую процедуру:
procedure TReplicationController.RunApplicationAndWaitTillFinish(
RunString: string);
var
StartupInfo: TStartupInfo;
SecurityAttributes: TSecurityAttributes;
ProcessInfo: TProcessInformation;
ExitCode: Cardinal;
StdOutPipeRead, StdOutPipeWrite: THandle;
CreateResult, ReadResult: Boolean;
Buffer: array[0𠢇] of Char;
BytesRead: Cardinal;
S: string;
FOverlappedRead: TOverlapped;
LastError: Cardinal;
TimeOut: Cardinal;
FCommStat: TComStat;
Errors: Cardinal;
WaitResult: Cardinal;
begin
DoInfoEvent(RunString);
with SecurityAttributes do
begin
nLength := SizeOf(SecurityAttributes);
bInheritHandle := True;
lpSecurityDescriptor := nil;
end;
try
// создаём пайп для перенаправления стандартного вывода
CreatePipe(StdOutPipeRead, // дескриптор чтения
StdOutPipeWrite, // дескриптор записи
@SecurityAttributes, // аттрибуты безопасности
0 // количество байт принятых для пайпа — 0 по умолчанию
);
with StartupInfo do
begin
FillChar(StartupInfo, SizeOf(StartupInfo), 0);
cb := SizeOf(StartupInfo);
dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
wShowWindow := SW_HIDE;
hStdInput := GetStdHandle(STD_INPUT_HANDLE); // стандартный ввод не перенаправляем
hStdOutput := StdOutPipeWrite;
hStdError := StdOutPipeWrite;
end;
CreateResult := CreateProcess(nil, PAnsiChar(RunString), nil, nil, True,
0, nil, nil, StartupInfo, ProcessInfo);
if CreateResult then
begin
try
S := "";
repeat
Application.ProcessMessages;
WaitResult := WaitForSingleObject (ProcessInfo.hProcess, 10);
ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
Buffer[BytesRead] := #0;
OemToAnsi(Buffer, Buffer);
S := S + Buffer;
until (WaitResult <> WAIT_TIMEOUT);
DoInfoEvent(S);
finally
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
end;
end
else
raise EReplicationException.Create(SysErrorMessage(GetLastError));
finally
CloseHandle(StdOutPipeRead);
end;
end;
Кое что конечно перенаправляется, но не все, почему? И вообще как правильно это сделать?
← →
Slym © (2007-12-12 04:35) [1]потому что мало из пайпа вычитываешь...
while true do
begin
PeekNamedPipe(tdOutPipeRead,nil,0,nil,@bytesRead,nil);
if bytesRead=0 then break;
if not ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil) then RaiseLastOSError;
Buffer[BytesRead] := #0;
OemToAnsi(Buffer, Buffer);
S := S + Buffer;
end;
← →
Kolan © (2007-12-12 10:14) [2]> потому что мало из пайпа вычитываешь…
Шас попробую. Неужели остальной код правильный?
← →
Kolan © (2007-12-12 10:34) [3]Получилось. Но не доконца.
Если просто использовать ваш код, то происходит выход поBytesReadFromPipe = 0
, так как видимо сразу начинается чтение, а данных еще нет.
Добавил ожидание завершения процесса (WaitForSingleObject), в этом случае работает, но так я получаю все данные одним куском, а хотелось бы по мере их появления.WaitForSingleObject (ProcessInfo.hProcess, INFINITE);
{}
while True do
begin
PeekNamedPipe(StdOutPipeRead, nil, 0, nil, @BytesReadFromPipe, nil);
if BytesReadFromPipe = 0 then
Break;
if not ReadFile(StdOutPipeRead, Buffer, 255, BytesReadFromFile, nil) then
RaiseLastOSError;
Buffer[BytesReadFromFile] := #0;
OemToAnsi(Buffer, Buffer);
S := Buffer;
DoInfoEvent(S, clGray, False);
end;
← →
Kolan © (2007-12-12 11:13) [4]Вот получилось так:
repeat
WaitResult := WaitForSingleObject(ProcessInfo.hProcess, 10);
PeekNamedPipe(StdOutPipeRead, nil, 0, nil, @BytesReadFromPipe, nil);
if BytesReadFromPipe > 0 then
begin
SetLength(Buffer, BytesReadFromPipe);
if not ReadFile(StdOutPipeRead, Buffer[0], BytesReadFromPipe, BytesReadFromFile, nil) then
RaiseLastOSError;
OemToAnsi(@Buffer[0], @Buffer[0]);
S := "";
for I := Low(Buffer) to High(Buffer) do
S := S + Buffer[I];
DoInfoEvent(S, clGray, False);
end;
until (WaitResult <> WAIT_TIMEOUT) and (BytesReadFromPipe = 0);
Не попортил ли я где память?
Buffer объявлен так:Buffer: array of Char;
Можно ли обойтись без цикла:for I := Low(Buffer) to High(Buffer) do
S := S + Buffer[I];
?
← →
Leonid Troyanovsky © (2007-12-13 18:57) [5]
> Kolan © (12.12.07 11:13) [4]
> Можно ли обойтись без цикла:
> for I := Low(Buffer) to High(Buffer) do
> S := S + Buffer[I];
Не надо забывать и SetString.
--
Regards, LVT.
← →
Slym © (2007-12-14 04:23) [6]
repeat
WaitResult := WaitForSingleObject(ProcessInfo.hProcess, 10);
PeekNamedPipe(StdOutPipeRead, nil, 0, nil, @BytesReadFromPipe, nil);
if BytesReadFromPipe > 0 then
begin
SetLength(S, BytesReadFromPipe);
if not ReadFile(StdOutPipeRead, PChar(S)^, BytesReadFromPipe, BytesReadFromFile, nil) then
RaiseLastOSError;
SetLength(S, BytesReadFromFile);
OemToAnsi(PChar(S),PChar(S));
DoInfoEvent(S, clGray, False);
end;
until (WaitResult <> WAIT_TIMEOUT) and (BytesReadFromPipe = 0);
← →
Kolan © (2007-12-14 09:48) [7]> [6] Slym © (14.12.07 04:23)
О, благодарю — так работает правильно, а то я видно где-то память портил. Слетала программа, даже не пикала.
Окончтельный вариант:procedure RunApplicationAndWaitTillFinish(RunString: string;
ConsoleViewVisitor: IConsoleViewVisitor);
var
StartupInfo: TStartupInfo;
SecurityAttributes: TSecurityAttributes;
ProcessInfo: TProcessInformation;
StdOutPipeRead, StdOutPipeWrite: THandle;
CreateResult: Boolean;
S: string;
BytesReadFromFile: Cardinal;
BytesReadFromPipe: Cardinal;
WaitResult: Cardinal;
begin
with SecurityAttributes do
begin
nLength := SizeOf(SecurityAttributes);
bInheritHandle := True;
lpSecurityDescriptor := nil;
end;
{Создать пайп для перенаправления стандартного вывода.}
if not CreatePipe(
StdOutPipeRead, // дескриптор чтения.
StdOutPipeWrite, // дескриптор записи.
@SecurityAttributes, // аттрибуты безопасности.
0) // количество байт принятых для пайпа — 0 по умолчанию.
then
RaiseLastOSError;
try
with StartupInfo do
begin
FillChar(StartupInfo, SizeOf(StartupInfo), 0);
cb := SizeOf(StartupInfo);
dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
wShowWindow := SW_HIDE;
hStdInput := GetStdHandle(STD_INPUT_HANDLE); // стандартный ввод не перенаправляем
hStdOutput := StdOutPipeWrite;
hStdError := StdOutPipeWrite;
end;
ZeroMemory(@ProcessInfo, SizeOf(@ProcessInfo));
CreateResult := CreateProcess(nil, PAnsiChar(RunString), nil, nil, True,
0, nil, nil, StartupInfo, ProcessInfo);
if CreateResult then
begin
try
repeat
Application.ProcessMessages;
{Ждать процесс 100 мс.}
WaitResult := WaitForSingleObject(ProcessInfo.hProcess, 100);
Application.ProcessMessages;
{Узнать сколько в пайпе байт}
PeekNamedPipe(StdOutPipeRead, nil, 0, nil, @BytesReadFromPipe, nil);
{Есл байт больше 0…}
if BytesReadFromPipe > 0 then
begin
{… выделить память для строки.}
SetLength(S, BytesReadFromPipe);
{Прочесть байты.}
if not ReadFile(StdOutPipeRead, PChar(S)^, BytesReadFromPipe, BytesReadFromFile, nil) then
RaiseLastOSError;
{Отрезать лишние.}
SetLength(S, BytesReadFromFile);
{Перекодировать из дос кодировки.}
OemToAnsi(PChar(S), PChar(S));
{Отправить полученые из консоли данные посетителю.}
if Assigned(ConsoleViewVisitor) then
ConsoleViewVisitor.ViewConsoleMessage(S);
end;
{Выйти если процесс завершился и нечего читать.}
until (WaitResult <> WAIT_TIMEOUT) and (BytesReadFromPipe = 0);
finally
{Закрыть хендлы процесса.}
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
end;
end
else
RaiseLastOSError;
finally
{Закрыть хендлы пайпа.}
CloseHandle(StdOutPipeRead);
CloseHandle(StdOutPipeWrite);
end;
end;
Так правильно? Нет ошибок?
Страницы: 1 вся ветка
Текущий архив: 2008.01.13;
Скачать: CL | DM;
Память: 0.51 MB
Время: 0.019 c