Как перехватить вывод на консоль дочернего процесса?
От: mDmitriy Россия  
Дата: 03.06.05 11:46
Оценка:
Приветствую, All!

Вопрос в следующем:
Надо запустить консольное приложение, например, rar.exe или компилятор или еще что...
Запуск предполагается через CreateProcess
Надо в реальном времени получать результат процесса, т.е. вариант с перенаправленим в файл и последующей читкой оного не прокатит.
CreateProcess вроде позволяет получать хэндл устройства вывода, но как перехватить идущие туда данные?
В MSDN есть пример похожий с PIPE, но может кто знает попроще?


Дмитрий
Re: Как перехватить вывод на консоль дочернего процесса?
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 03.06.05 12:04
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>Приветствую, All!


D>Вопрос в следующем:

D>Надо запустить консольное приложение, например, rar.exe или компилятор или еще что...
D>Запуск предполагается через CreateProcess
D>Надо в реальном времени получать результат процесса, т.е. вариант с перенаправленим в файл и последующей читкой оного не прокатит.
D>CreateProcess вроде позволяет получать хэндл устройства вывода, но как перехватить идущие туда данные?

В свое время я написал нечно вроде этого. Работает не всегда корректно, но для моих целей хватило. А до товарного вида все баги не пофиксил
... << RSDN@Home 1.1.3 stable >>
Re: Как перехватить вывод на консоль дочернего процесса?
От: Danchik Украина  
Дата: 03.06.05 14:09
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>Приветствую, All!


D>Вопрос в следующем:

D>Надо запустить консольное приложение, например, rar.exe или компилятор или еще что...
D>Запуск предполагается через CreateProcess
D>Надо в реальном времени получать результат процесса, т.е. вариант с перенаправленим в файл и последующей читкой оного не прокатит.
D>CreateProcess вроде позволяет получать хэндл устройства вывода, но как перехватить идущие туда данные?
D>В MSDN есть пример похожий с PIPE, но может кто знает попроще?

D>Дмитрий


Для этих целей я писал вот такую вот процедурень запуска программы. MSDN начитался до опупения, зато работает как часики


type
  TDOutputCallBack = procedure (OuptpuStr : string) of object;
......

function RunProgram(FileName : string; CommandLine : string; WorkDir : string;
    ShowCmd : Integer; OutputCallBack : TDOutputCallBack; WaitFor : Boolean = True): DWORD;

const
  cntReadBufferSize = 1024;
var
  BytesRead: Cardinal;
  Buffer : array [0..cntReadBufferSize] of Char;
  hOutputReadTmp, hOutputRead, hOutputWrite : THandle;
  hInputWriteTmp, hInputRead,  hInputWrite  : THandle;
  hErrorWrite : THandle;

var
  ProcessInfo     : TProcessInformation;
  StartupInfo     : TStartupInfo;
  EnviromentBlock : PChar;
  ExecString      : string;
  Security        : TSecurityAttributes;
begin

  with Security do begin
    nlength              := SizeOf (TSecurityAttributes);
    binherithandle       := True;
    lpsecuritydescriptor := nil;
  end;

  FileName := TString.Trim (FileName);
  if (TString.Char (FileName) <> '"') and (Pos (' ', FileName) > 0) then
    FileName := '"' + StringReplace (FileName, '"', '""', [rfReplaceAll]) + '"';

  hOutputRead := 0;
  if Assigned (OutputCallBack) then begin
    if CreatePipe (hOutputReadTmp, hOutputWrite, @Security, 0) then begin
      DuplicateHandle (GetCurrentProcess, hOutputWrite, GetCurrentProcess, @hErrorWrite, 0, True, DUPLICATE_SAME_ACCESS);
      if CreatePipe (hInputRead, hInputWriteTmp, @Security, 0) then begin
        DuplicateHandle (GetCurrentProcess, hOutputReadTmp, GetCurrentProcess, @hOutputRead, 0, False, DUPLICATE_SAME_ACCESS);
        DuplicateHandle (GetCurrentProcess, hInputWriteTmp, GetCurrentProcess, @hInputWrite, 0, False, DUPLICATE_SAME_ACCESS);
        CloseHandle (hOutputReadTmp);
        CloseHandle (hInputWriteTmp);
      end;
    end;
  end;

  FillChar (StartupInfo, SizeOf (StartupInfo), 0);
  with StartupInfo do begin
    cb := sizeof (StartupInfo);
    lpReserved  := nil;
    lpDesktop   := nil;   {inherit}
    lpTitle     := nil;
    dwFlags     := TDTools.Choose (not Assigned (OutputCallBack), 0, STARTF_USESTDHANDLES) or STARTF_USESHOWWINDOW; 
    hStdOutput  := hOutputWrite;
    hStdInput   := hInputRead;
    hStdError   := hErrorWrite;
    wShowWindow := ShowCmd;
    cbReserved2 := 0;
    lpReserved2 := nil;
  end;

  EnviromentBlock := GetEnvironmentStrings;
  try
    ExecString := FileName + ' ' + CommandLine;
    if not CreateProcess( nil,
                 PChar(ExecString),
                 @Security,
                 @Security,
                 true,
                 0{DEBUG_PROCESS},
                 EnviromentBlock,
                 TDTools.Choose (WorkDir = '', nil, PChar (WorkDir)),
                 StartupInfo,
                 ProcessInfo)
    then RaiseLastWin32Error;

    CloseHandle(hOutputWrite);
    CloseHandle(hInputRead);
    CloseHandle(hErrorWrite);

    CloseHandle (ProcessInfo.hThread);
  finally
    FreeEnvironmentStrings (EnviromentBlock)
  end;

  Result := 0;
  try
    if hOutputRead <> 0 then begin

      BytesRead := 0;
      repeat

        if not ReadFile (hOutputRead, Buffer, cntReadBufferSize, BytesRead, nil) then begin
          Break
        end else begin
          if BytesRead > 0 then begin
            Buffer [BytesRead] := #0;
            if Assigned (OutputCallback) then begin
              OemToChar (@Buffer, @Buffer);
              OutputCallback (string (Buffer));
            end;
          end;
        end;
      until False;
    end;

    if WaitFor then begin

      if Result = 0 then
        Result := WaitForSingleObject(ProcessInfo.hProcess, 2 * 60 * 60 * 1000 {2 hours});

      case Result of
        WAIT_TIMEOUT : raise Exception.Create ('Process timed out.');
        WAIT_FAILED : raise Exception.Create ('Process filed');
      end;

      GetExitCodeProcess (ProcessInfo.hProcess, Result);
    end else
      Result := 0;

    CloseHandle (ProcessInfo.hProcess);
  finally
    if hOutputRead <> 0 then
      CloseHandle (hOutputRead);
  end;
end;


Удачи!
Re[2]: Как перехватить вывод на консоль дочернего процесса?
От: s.ts  
Дата: 03.06.05 21:01
Оценка:
Здравствуйте, Danchik, Вы писали:


D>Для этих целей я писал вот такую вот процедурень запуска программы. MSDN начитался до опупения, зато работает как часики


неименованные пайпы — самое простое.
Как мне кажется, все проще, чем написано.
Никаких DuplicateHandle не нужно.
просто создаешь пайп — 2 хэндла. (1 штуку, а то все норовят 2 пайпа создать и поиметь 4 хэндла соответственно)
записываешь во входящий нпайп чего надо.
Присваиваешь нелбходимые значения для ShellExecuteEx
Запускаешь
Там читают чего ты записал
Дальше по пайпам пишешь/читаешь (для справки — ReadFile ждет пока в пайп запишут)
Можно ждать одновременно пайп и завершение приложения (WaitFor)
Потом оба хэндла нужно закрыть.
В общем, если в объекты не обертывать, то я в пол дюжины строк (чисто на служебные нужды без особенностей протокола обмена) укладывался всегда.
Re[3]: Как перехватить вывод на консоль дочернего процесса?
От: Danchik Украина  
Дата: 04.06.05 13:43
Оценка:
Здравствуйте, s.ts, Вы писали:

ST>Здравствуйте, Danchik, Вы писали:



D>>Для этих целей я писал вот такую вот процедурень запуска программы. MSDN начитался до опупения, зато работает как часики


ST>неименованные пайпы — самое простое.

ST>Как мне кажется, все проще, чем написано.
Ты пробовал? у меня иначе все висло
ST>Никаких DuplicateHandle не нужно.
Выдрано и портировано с MSDN, один в один
ST>просто создаешь пайп — 2 хэндла. (1 штуку, а то все норовят 2 пайпа создать и поиметь 4 хэндла соответственно)
ST>записываешь во входящий нпайп чего надо.
ST>Присваиваешь нелбходимые значения для ShellExecuteEx
ST>Запускаешь
ST>Там читают чего ты записал
ST>Дальше по пайпам пишешь/читаешь (для справки — ReadFile ждет пока в пайп запишут)
Итак понятно
ST>Можно ждать одновременно пайп и завершение приложения (WaitFor)
согласен
ST>Потом оба хэндла нужно закрыть.
че то не закрыл?, тыкни в место
ST>В общем, если в объекты не обертывать, то я в пол дюжины строк (чисто на служебные нужды без особенностей протокола обмена) укладывался всегда.

Кидай код, только если он еа CreateProcess основан
Re: Всем спасибо!!! Работает!!!
От: mDmitriy Россия  
Дата: 06.06.05 10:25
Оценка:
Здравствуйте, mDmitriy, Вы писали:

D>Приветствую, All!


D>Вопрос в следующем:

D>Надо запустить консольное приложение, например, rar.exe или компилятор или еще что...
D>Запуск предполагается через CreateProcess
D>Надо в реальном времени получать результат процесса, т.е. вариант с перенаправленим в файл и последующей читкой оного не прокатит.
D>CreateProcess вроде позволяет получать хэндл устройства вывода, но как перехватить идущие туда данные?
D>В MSDN есть пример похожий с PIPE, но может кто знает попроще?


Что интересно — работает без дубликации хэндлов

интересующимся — сюды (много исходников по теме):
http://delphiworld.narod.ru/_os_.html


Дмитрий
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.