-1

I want to read a console program output. The problem is that ReadFile deadlocks my thread. Even when process exists still the same. I heard that you must close Write Pipe but when doing so it does not read nothing.

Here is my code:

var
  saSec : TSecurityAttributes;
  hRead, hWrite : THandle;
  SI : TStartupInfo;
  PI : TProcessInformation;
  Running,dwRead : DWORD;
  buf : array [0..1024] of AnsiChar;
  data : AnsiString;
begin
  saSec.nLength := sizeof(TSecurityAttributes);
  saSec.bInheritHandle := false;
  saSec.lpSecurityDescriptor := nil;

  if CreatePipe(hRead, hWrite, @saSec, 0) then
  begin
     FillChar(SI, Sizeof(TStartupInfo), #0);
     FillChar(PI, Sizeof(TProcessInformation), #0);
     SI.hStdInput := hRead;
     SI.hStdOutput := hWrite;
     SI.hStdError := hWrite;
     SI.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
     SI.wShowWindow := SW_HIDE;

     if CreateProcess(nil, PChar('openvpn' + cert), @saSec, @saSec, False, NORMAL_PRIORITY_CLASS, nil, nil, SI, PI) then
     begin
          data := '';
          repeat
            Running := WaitForSingleObject(PI.hProcess, 100);
            repeat
                  FillChar(buf, sizeof(buf), #0);
                  ReadFile(hRead, buf, sizeof(buf), dwRead, nil);
                  data := data + buf;
                   if Pos('sequence completed', data) >0 then
                  begin
                    status := 1;
                    Synchronize(Sync);
                  end;
            until dwRead < sizeof(buf);

          until Running <> WAIT_TIMEOUT ;
          status := 0;
          Synchronize(Sync);
          CloseHandle(PI.hProcess);
          CloseHandle(PI.hThread);
     end;
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
opc0de
  • 11,557
  • 14
  • 94
  • 187
  • The problem is you need to call first PeekNamedPipe to see if data is available, to avoid blocking the thread. Example here : http://thundaxsoftware.blogspot.com/2012/12/capturing-console-output-with-delphi.html – opc0de Jan 24 '15 at 19:58
  • 1
    Why are you connecting both ends of the same pipe to stdin and stdout of the same process? Why would the process want to use its own output as its input? – David Heffernan Jan 24 '15 at 22:17

1 Answers1

1

Unless you're using overlapped I/O, ReadFile will block until data are available from the handle being read, or until some error condition on the handle.

You're waiting to read from a pipe that nothing will ever write to. You disabled inheritance when you created the handles, so the process you create won't receive them. Allow inheritance when you assign to saSec.bInheritHandle, at least prior to calling CreatePipe. (You can change it in preparation for CreateProcess, if you don't want the new process and thread handles to be inheritable by subsequent processes.) Also mind the fifth parameter of CreateProcess, which controls the same thing.

Raymond Chen published an article expanding on the topic of inheriting handles a few years ago.

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
  • Assigned bInheritHandle := true same behaivour. – opc0de Jan 24 '15 at 19:41
  • 1
    @opc0de: You're also passing `False` to the `bInheritHandles` argument to CreateProcess. – Harry Johnston Jan 24 '15 at 22:44
  • @HarryJohnston put True and you will see same behaivour ReadFile blocks until there is data to be read. In order to prevent blocking use PeekNamedPipe – opc0de Jan 25 '15 at 10:46
  • 3
    Oh, I see what you mean - when the process exits, ReadFile() keeps blocking. Yes, you *can* work around that by polling (i.e., PeekNamedPipe) but it isn't the best solution; polling is inefficient. Instead, close your handle `hWrite` after calling CreateProcess(). That way, when the child exits, the write end of the pipe will be closed, and ReadFile() will exit with `ERROR_BROKEN_PIPE`. – Harry Johnston Jan 25 '15 at 11:06