0

В Delphi XE8 под Windows я пытаюсь вызвать внешнее консольное приложение и записать его вывод. Я использую следующий код, как описано в Capture the output from a DOS (command/console) Window, а также Getting output from a shell/dos app into a Delphi app:Delphi XE8: проблемы с запуском внешнего консольного приложения, ожидающего его результатов и получения его результатов

procedure TForm1.Button1Click(Sender: TObject) ; 

    procedure RunDosInMemo(DosApp:String;AMemo:TMemo) ; 
    const 
    ReadBuffer = 2400; 
    var 
    Security : TSecurityAttributes; 
    ReadPipe,WritePipe : THandle; 
    start : TStartUpInfo; 
    ProcessInfo : TProcessInformation; 
    Buffer : Pchar; 
    BytesRead : DWord; 
    Apprunning : DWord; 
    S: String; 
    begin 
    With Security do begin 
     nlength := SizeOf(TSecurityAttributes) ; 
     binherithandle := true; 
     lpsecuritydescriptor := nil; 
    end; 
    if Createpipe (ReadPipe, WritePipe, 
        @Security, 0) then 
    begin 
     Buffer := AllocMem(ReadBuffer + 1) ; 
     FillChar(Start,Sizeof(Start),#0) ; 
     start.cb := SizeOf(start) ; 
     start.hStdOutput := WritePipe; 
     start.hStdInput := ReadPipe; 
     start.dwFlags := STARTF_USESTDHANDLES + STARTF_USESHOWWINDOW; 
     start.wShowWindow := SW_HIDE; 

     S:=UniqueString(DosApp); 
     if CreateProcess(nil, 
       PChar(S), 
       @Security, 
       @Security, 
       true, 
       NORMAL_PRIORITY_CLASS, 
       nil, 
       nil, 
       start, 
       ProcessInfo) then 
     begin 
     repeat 
      Apprunning := WaitForSingleObject 
         (ProcessInfo.hProcess,100) ; 
      Application.ProcessMessages; 
     until (Apprunning <> WAIT_TIMEOUT) ; 
     Repeat 
      BytesRead := 0; 
      ReadFile(ReadPipe,Buffer[0], ReadBuffer,BytesRead,nil) ; 
      Buffer[BytesRead]:= #0; 
      OemToAnsi(Buffer,Buffer) ; 
      AMemo.Text := AMemo.text + String(Buffer) ; 
     until (BytesRead < ReadBuffer) ; 
     end; 
     FreeMem(Buffer) ; 
     CloseHandle(ProcessInfo.hProcess) ; 
     CloseHandle(ProcessInfo.hThread) ; 
     CloseHandle(ReadPipe) ; 
     CloseHandle(WritePipe) ; 
    end; 
    end; 

begin {button 1 code} 
    RunDosInMemo('cmd.exe /c dir',Memo1) ; //<-- this works 
    RunDosInMemo('"c:\consoleapp.exe" "/parameter"',Memo1) //<-- this hangs in the repeat until (Apprunning <> WAIT_TIMEOUT) ; 
end; 

Он работает для DOS команды, но он не работает для консольного приложения. Приложение консоли запускается и выполняется правильно, но оно зависает в цикле repeat until (Apprunning <> WAIT_TIMEOUT). Что я могу попытаться решить проблему?

спасибо!

+1

Совершенно очевидно, сколько разных сломанных вариантов этой программы можно найти. Я думаю, что мой код здесь работает: http://stackoverflow.com/questions/25723807/execute-dos-program-and-get-output-dynamically –

+0

Дэвид, спасибо вам за помощь. Я пробовал ваш код, и он работает для нескольких консольных приложений, кроме тех, которые мне нужны. Приложение console выполнено, FileSize кажется правильным, но AnsiBuffer содержит только бессмысленную информацию. К сожалению, из-за лицензий и конфиденциальности я не могу назвать или загрузить консольное приложение. Консольное приложение отлично работает в Wondows Commad Prompt. Что я могу попытаться сделать для дальнейшего анализа или решения проблемы? Благодарю. – user3384674

+0

Не знаю. Если вы не можете рассказать нам о приложении, это будет немного сложно. –

ответ

4

Программа, в которой вы работаете, либо ожидает ввода (например, с клавиатуры), либо производит больше выходных данных, чем подходит в буфере трубы. В любом случае эта программа зависает в ожидании дальнейших операций ввода-вывода, но ваша родительская программа ждет, пока этот ребенок завершит работу перед обработкой любого вывода и никогда не будет вводить какие-либо данные.

Вам необходимо обработать выходной канал во время работы программы. В противном случае вы рискуете заполнить буфер, и ребенок будет блокироваться, пока не появится больше свободного места. Аналогичным образом, если вы не планируете вводить какие-либо данные для другого процесса, вы, вероятно, не должны давать ему действительный дескриптор ввода. Таким образом, если он попытается прочитать ввод, он будет сработал, а не заблокирован.

Кроме того, маркер ввода, который вы передали этой программе, прикреплен к выходному дескриптору. Если программа пытается прочитать, она будет считывать свой собственный вывод, например, ouroboros ввода-вывода. Для обработки ввода и выведите трубы.

(Обратите внимание, что это тупиковый именно проблема I called out in the comments of the answer you used Второй блок кода в том же ответе решает проблему, а также Уроборос проблему.).