Execute DOS program and get output dynamically

后端 未结 1 827
臣服心动
臣服心动 2021-01-01 07:57

I need to execute a \'DOS\' program (console app) and to retrieve its output dynamically (it will be also nice to be able to end the DOS program whenever I want because the

相关标签:
1条回答
  • 2021-01-01 08:38

    One obvious problem is your pipe. You have a single pipe and you arrange that the child process stdout writes to one end, and the child process stdin reads from the other. That's no good. Why would you want the process to read its input from its own output? And at the same time the parent process reads from the pipe. You've got two processes trying to read this pipe. I can't imagine that ends well.

    You need two pipes. One for the child's stdin. The parent writes to it, the child reads from it. And the other pipe for the child's stdout. The child writes to it, the parent reads.

    Or if you don't want the child process to have any stdin, then create a single pipe, connect write end to child process stdout and let the parent process read from the read end.

    Another problem is that if the process has terminated, and you've already read all of its contents, the call to ReadFile will block indefinitely. You need to make sure that the pipe contains something before attempting to read from it. I'd use GetFileSizeEx for that.

    Personally I'd be inclined to do all of this inside a thread to avoid the call to ProcessMessages.

    You should also always check API return values for errors. That is not done for the calls to WaitForSingleObject and ReadFile.

    I propose something along these lines:

    program DynamicStdOutCapture;
    
    {$APPTYPE CONSOLE}
    
    uses
      System.SysUtils,
      System.Math,
      Winapi.Windows;
    
    function GetFileSizeEx(hFile: THandle; var FileSize: Int64): BOOL; stdcall;
      external kernel32;
    
    procedure Execute(const Command: string; const Parameters: string;
      const Timeout: DWORD; const Output: TProc<string>);
    
    const
      InheritHandleSecurityAttributes: TSecurityAttributes =
        (nLength: SizeOf(TSecurityAttributes); bInheritHandle: True);
    
    var
      hReadStdout, hWriteStdout: THandle;
      si: TStartupInfo;
      pi: TProcessInformation;
      WaitRes, BytesRead: DWORD;
      FileSize: Int64;
      AnsiBuffer: array [0 .. 1024 - 1] of AnsiChar;
    
    begin
      Win32Check(CreatePipe(hReadStdout, hWriteStdout,
        @InheritHandleSecurityAttributes, 0));
      try
        si := Default (TStartupInfo);
        si.cb := SizeOf(TStartupInfo);
        si.dwFlags := STARTF_USESTDHANDLES;
        si.hStdOutput := hWriteStdout;
        si.hStdError := hWriteStdout;
        Win32Check(CreateProcess(nil, PChar(Command + ' ' + Parameters), nil, nil,
          True, CREATE_NO_WINDOW, nil, nil, si, pi));
        try
          while True do
          begin
            WaitRes := WaitForSingleObject(pi.hProcess, Timeout);
            Win32Check(WaitRes <> WAIT_FAILED);
            while True do
            begin
              Win32Check(GetFileSizeEx(hReadStdout, FileSize));
              if FileSize = 0 then
              begin
                break;
              end;
              Win32Check(ReadFile(hReadStdout, AnsiBuffer, SizeOf(AnsiBuffer) - 1,
                BytesRead, nil));
              if BytesRead = 0 then
              begin
                break;
              end;
              AnsiBuffer[BytesRead] := #0;
              OemToAnsi(AnsiBuffer, AnsiBuffer);
              if Assigned(Output) then
              begin
                Output(string(AnsiBuffer));
              end;
            end;
            if WaitRes = WAIT_OBJECT_0 then
            begin
              break;
            end;
          end;
        finally
          CloseHandle(pi.hProcess);
          CloseHandle(pi.hThread);
        end;
      finally
        CloseHandle(hReadStdout);
        CloseHandle(hWriteStdout);
      end;
    end;
    
    procedure DoOutput(Text: string);
    begin
      Write(Text);
    end;
    
    procedure Main;
    begin
      Execute('ping', 'stackoverflow.com -t', 100, DoOutput);
    end;
    
    begin
      try
        Main;
      except
        on E: Exception do
          Writeln(E.ClassName, ': ', E.Message);
      end;
    end.
    
    0 讨论(0)
提交回复
热议问题