Reading from a pipe

I'm writing a program where I kick off an executable and I want to read information that the program writes to stdout. I did a console app test program using _popen and it worked fine. However, when I dropped the code into the full gui the behaviour changed. It stopped reading from the stream returned from _popen (ie the spawned process would run to completeion before I could step to the read.) So I decided to try using CreatePipe and CreateProcess. (see below) What I'm having the real problem with is reading from the stdout of the created process.

The spawned process takes a while to run and writes update information to stdout. But what happens is that my program seems to wait at the ReadFile call until the process completes. By the time I return from ReadFile the pipe has been closed. Here's a sample of my code. (I'm fuzzy on the overlap structures usage. ) {MSDEV C++ 5.0 on Windows NT SVC pack 3}


  HANDLE hWrite, hRead, hError;
  HANDLE hHeap;
  OVERLAPPED PipeOverlapInfo = {0,0,0,0,0};
  SECURITY_ATTRIBUTES sa;

  sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  sa.lpSecurityDescriptor = NULL;
  sa.bInheritHandle = TRUE;

  if (!CreatePipe(&hRead, &hWrite, &sa, 4096)) {
    printf("error \n");
    exit(1);
  }
  STARTUPINFO StartupInfo;
  PROCESS_INFORMATION ProcessInformation;

  StartupInfo.cb = sizeof(STARTUPINFO);
  StartupInfo.lpReserved = NULL;
  StartupInfo.lpDesktop = NULL;
  StartupInfo.lpTitle = NULL;
  StartupInfo.dwX = 0L;
  StartupInfo.dwY = 0L;
  StartupInfo.dwXSize = 0L;
  StartupInfo.dwYSize = 0L;
  StartupInfo.dwXCountChars = 0L;
  StartupInfo.dwYCountChars = 0L;
  StartupInfo.dwFillAttribute = NULL;
  StartupInfo.dwFlags = STARTF_USESTDHANDLES;
  StartupInfo.wShowWindow = SW_SHOWDEFAULT;
  StartupInfo.cbReserved2 = 0;
  StartupInfo.lpReserved2 = NULL;
  StartupInfo.hStdOutput = hWrite;

  if (!DuplicateHandle(GetCurrentProcess(), hWrite, GetCurrentProcess(),
                       &hError, 0, TRUE, DUPLICATE_SAME_ACCESS)) {
    printf ("error\n");
    exit(1);
  }
  StartupInfo.hStdError = hError;

  PipeOverlapInfo.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  if (PipeOverlapInfo.hEvent == NULL) {
    printf ("error\n");
    exit(1);
  }

  if (!CreateProcess(NULL, "C:\\WINNT\\system32\\more.com < swQueryLog.txt",
                     NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL,
                     &StartupInfo, &ProcessInformation)) {
    printf ("error\n");
    exit(1);
  }
 
  CloseHandle(hWrite);
  CloseHandle(hError);
  LPSTR lpoutput;

  DWORD availableOutput = 4096;
  BOOL TimeoutNotReached = TRUE;
  DWORD bytesRead;
  while (TimeoutNotReached) {
    if (ReadFile(hRead, psBuffer, availableOutput, &bytesRead,
                 &PipeOverlapInfo) == TRUE) {
      printf("we read");
    } else {
      int erco = GetLastError();
      switch (erco) {
      case ERROR_IO_PENDING:
        ResetEvent(PipeOverlapInfo.hEvent);
        break;
      case ERROR_MORE_DATA:
        printf("Buffer is too small");
        break;
      case ERROR_BROKEN_PIPE:
        // We are done..

        //Make sure we are null terminated
        printf("Process completed");
        break;
      case ERROR_INVALID_USER_BUFFER:
      case ERROR_NOT_ENOUGH_MEMORY:
        // Too many I/O requests pending...wait a little while
        Sleep(2000);
        break;
      default:
                                // Wierd error...return
        printf("RUNAPP ERROR: Error reading STDIO");
        break;;
      }
    }
  }
winkleAsked:
Who is Participating?
 
jkrConnect With a Mentor Commented:
The problem is that you have to use 'DuplicateHandle()' for each of the handles you pass to the created process, e.g.

   bStatus = CreatePipe( &hInRead, &hInWrite, NULL, 1024 );

   // ...
                                       
   bStatus = CreatePipe( &hOutRead, &hOutWrite, NULL, 1024 );

   if( ! bStatus )
   {
      CloseHandle( hInRead );          
      CloseHandle( hInWrite );
      return( FALSE );
   }
                                   /
   DuplicateHandle( GetCurrentProcess(),
                    hInWrite,
                    GetCurrentProcess(),
                    &hDupInWrite,
                    0L,
                    TRUE,
                    DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS
                  );
                                           
   DuplicateHandle( GetCurrentProcess(),
                    hOutRead,
                    GetCurrentProcess(),
                    &hDupOutRead,
                    0L,
                    TRUE,
                    DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS
                  );

   si.cb = sizeof(STARTUPINFO);
   si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
   si.wShowWindow = SW_HIDE;        
   si.hStdOut = hDupInWrite;            
   si.hStdError = hDupInWrite;
   si.hStdIn = hDupOutRead;  


0
 
winkleAuthor Commented:
I'm a little confused as to why two Createpipe calls. But I tried it and the calling process still seems to be waiting for the spawned process to be completed. Is there something different I need to be doing with my ReadFile call?

0
 
winkleAuthor Commented:
I'm a little confused as to why two Createpipe calls. But I tried it and the calling process still seems to be waiting for the spawned process to be completed. Is there something different I need to be doing with my ReadFile call?

0
 
jkrCommented:
Is the spawned program a Win32 console application or a DOS program?
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.