Solved

Reading from a pipe

Posted on 1998-10-07
4
1,913 Views
Last Modified: 2013-12-03
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;;
      }
    }
  }
0
Comment
Question by:winkle
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 2
  • 2
4 Comments
 
LVL 86

Accepted Solution

by:
jkr earned 100 total points
ID: 1415057
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
 

Author Comment

by:winkle
ID: 1415058
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
 

Author Comment

by:winkle
ID: 1415059
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
 
LVL 86

Expert Comment

by:jkr
ID: 1415060
Is the spawned program a Win32 console application or a DOS program?
0

Featured Post

Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article shows a few slightly more advanced techniques for Windows 7 gadget programming, including how to save and restore user settings for your gadget and how to populate the "details" panel that is displayed in the Windows 7 gadget gallery.  …
zlib is a free compression library (a DLL) on which the popular gzip utility is built.  In this article, we'll see how to use the zlib functions to compress and decompress data in memory; that is, without needing to use a temporary file.  We'll be c…
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…

726 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question