Link to home
Start Free TrialLog in
Avatar of peeldog
peeldog

asked on

Capture standard output from CreateProcess problem (low level write?)

I'm spawning a process using CreateProcess and capturing the standard output and error, which get fed through a pipe to a seperate thread which writes it out to a file.  I've created a simple test app that fprintf's to stdout and stderr and everything works fine.  But when I use someone elses console app, I don't get any data on the pipe.  Is there another way that it could be writing to the console and is there a way I can capture that?

So the question is... how do I capture _everything_ that is written by a console app?

Thanks!
Avatar of peeldog
peeldog

ASKER

P.S. Coding in C/C++ using the Windows API for Windows XP.
Avatar of jkr
See http://support.microsoft.com/kb/190351/en-us ("How to spawn console processes with redirected standard handles") which explains in detail how to do that.
Avatar of peeldog

ASKER

Yup, that's what I am doing, and it works fine with a simple test app, but not with a target app.  I've done a little more looking in to it and I think the problem is that the data does not get written the pipe until the app terminates, and in the case of the app I am working with it never terminates, so I never see any data on the pipe.  I'm trying to parse the stdout from the app so when I see "Job Completed" I can kill it.

I have pasted the code I am using do to reading below.   m_stderr is the parent's read end of the pipe and the process writes to the write end of the pipe.  I have tried using the "s_info.dwFlags = STARTF_USESTDHANDLES" method to pass the write end as well as the inheritance method.  In both cases the PeekNamedPipe does not see any data on the pipe till the app closes.

The document you attached says:

"Note Some console based applications do not use the standard handles for their input/output (IO) operations. The Win32 API does not support redirection of these processes.".  

I'd like to know if it is possible to get _all_ data written to the console and I'd also like to know how to get this data before the app terminates.  Should I be using a Named pipe rather than an Anonymous pipe to keep the pipe flushed or something, or have I doing something wrong in the way I am reading from the pipe?  In debugging PeekNamedPipe always returns 0 values, then when the app finishes it sees all the output data.

Thanks!




DWORD Process::readStd(string file, HANDLE stream)
{
      DWORD dwWritten;
      SECURITY_ATTRIBUTES saAttr;
      ZeroMemory( &saAttr, sizeof(saAttr));
      saAttr.nLength              = sizeof(SECURITY_ATTRIBUTES);
      saAttr.lpSecurityDescriptor = NULL;
      saAttr.bInheritHandle       = TRUE;

      DWORD dwRead, dwAvail, dwUnRead, dwExitCode;
      char chBuf[4096];

      HANDLE fileh = CreateFile(file.c_str(), GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH, &saAttr);

      while(1)
    {
            memset(chBuf,'\0',sizeof(chBuf));
            if(!PeekNamedPipe(stream,chBuf,4096,&dwRead, &dwAvail, &dwUnRead))
            {
                  break;
            }
            else
            {
                  if(dwAvail != 0 || dwRead !=0 || dwUnRead!=0)
                  {
                        if(!ReadFile(stream, chBuf, 4096, &dwRead, NULL))
                        {
                              m_log->error(GetErrorString(GetLastError()).c_str());
                              break;
                        }
                        if(!WriteFile(fileh, chBuf, dwRead, &dwWritten, NULL))
                        {
                              m_log->error(GetErrorString(GetLastError()).c_str());
                              break;
                        }
                  }
                  else
                  {
                        GetExitCodeProcess(m_processHandle,&dwExitCode);
                        if(dwExitCode != STILL_ACTIVE)
                              break;
                  }

            }
            Sleep(100);
            
            continue;
      }

      return 0;
}
Have you tried redirecting 'STARTUPINFO::hStdError' also? I assume the process in question writes its output to that stream.
Avatar of peeldog

ASKER

Yup.   I'm using:

m_stdout_readerThreadHandle =  CreateThread NULL, 0, StdOutThreadFunc, this, 0, &m_stdout_readerThreadID);
m_stderr_readerThreadHandle =  CreateThread(NULL, 0, StdErrThreadFunc, this, 0, &m_stderr_readerThreadID);


DWORD WINAPI StdOutThreadFunc( LPVOID lpParam )
{
      Process *p = (Process *)lpParam;
      DWORD ret = p->readStd(p->m_stdoutfile, p->m_stdout);
      p->m_readingStdout = false;
      return ret;
      
}

DWORD WINAPI StdErrThreadFunc( LPVOID lpParam )
{
      Process * p = (Process *)lpParam;
      DWORD ret = p->readStd(p->m_stderrfile, p->m_stderr);
      p->m_readingStderr = false;
      return ret;
}


I'm playing with named pipes at the moment and getting the data coming in faster... but the original question still stands.... is there a way to capture _all_ data written to the console.  Is there a workaround for:

"Note Some console based applications do not use the standard handles for their input/output (IO) operations. The Win32 API does not support redirection of these processes.".  

Thanks!
ASKER CERTIFIED SOLUTION
Avatar of mxjijo
mxjijo

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of peeldog

ASKER

Great, thanks.. mxjijo.  This is exactly what I was looking for.