Solved

Redirecting STDOUT of a child process under Windows NT

Posted on 2000-04-07
9
403 Views
Last Modified: 2013-11-20
I have a program that calls `ping.exe'.
The program then displays the result in a dialog-thingy.
This is all working fine under Win9x, but not on NT...

Relevant code:

HANDLE hChildStdoutRd, hChildStdoutWr, hChildStdoutRdDup, hSaveStdout;
      SECURITY_ATTRIBUTES      saAttr;
      PROCESS_INFORMATION piProcInfo;
      STARTUPINFO                  siStartInfo;  
      BOOL                        fSuccess;
      char                        szBuf[BUFSIZE];
      CString                  strAddr;
      DWORD                        dwRead;
      
      if ( ((CSupportDlg*)(AfxGetApp()->m_pMainWnd))->m_bIsWindowsNT == TRUE ) {
            strAddr.Format("%s\\ping.exe %s", GetSysDir(), tmpdlg->m_strPingAddr);
      } else {
            strAddr.Format("%s\\ping.exe %s", GetWinDir(), tmpdlg->m_strPingAddr);
      }
      
      saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
      saAttr.bInheritHandle = TRUE;
      saAttr.lpSecurityDescriptor = NULL;
      
      // Save the handle to the current STDOUT.
      hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);
      
      // Create a pipe for the child process's STDOUT.  
      if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
            return 1;

      // Set a write handle to the pipe to be STDOUT.  
      if (! SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr))
            return 2;

      // Create noninheritable read handle and close the inheritable read handle.
      fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd, GetCurrentProcess(), &hChildStdoutRdDup , 0,
                                                            FALSE, DUPLICATE_SAME_ACCESS);
      if (!fSuccess)
            return 3;
      CloseHandle(hChildStdoutRd);

      // Create process.
      ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
      siStartInfo.cb = sizeof(STARTUPINFO);  // Create the child process.  
      siStartInfo.dwFlags = STARTF_USESHOWWINDOW;
      siStartInfo.wShowWindow = SW_HIDE;
      fSuccess = CreateProcess(NULL,      
            (LPTSTR)(LPCTSTR)strAddr,       // command line
            NULL,                                          // process security attributes
            NULL,                                          // primary thread security attributes
            TRUE,                                          // handles are inherited
            0,                                                // creation flags
            NULL,                                          // use parent's environment
            NULL,                                          // use parent's current directory
            &siStartInfo,                              // STARTUPINFO pointer
            &piProcInfo);                              // receives PROCESS_INFORMATION

      
      if (!fSuccess)
            AfxMessageBox("Unable to spawn child process, 'Ping.exe' could not be run.");

      if (! SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout))
            return 8;
      
      
      tmpdlg->m_bRunning = TRUE;

      // Close the write end of the pipe before reading from the
      // read end of the pipe.
       CloseHandle(hChildStdoutWr);

      do
      {
            memset( szBuf, 0, BUFSIZE);
            tmpdlg->m_strOutput += szBuf;
            TRACE(szBuf);
            ::SendMessage( tmpdlg->GetSafeHwnd(), WM_UPDMSG, 0, 0);
      }
      while(ReadFile( hChildStdoutRdDup, szBuf, BUFSIZE, &dwRead, NULL) && dwRead != 0);
                  
      CloseHandle(hChildStdoutRdDup);
      
      tmpdlg->m_bRunning = FALSE;
      return 0;
0
Comment
Question by:niven
  • 4
  • 3
  • 2
9 Comments
 
LVL 8

Expert Comment

by:gelbert
ID: 2693810
Try:
siStartInfo.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
siStartInfo.hStdInput = hChildStdinRd;
siStartInfo.hStdOutput = hChildStdoutWr;
siStartInfo.hStdError = hChildStdoutWr;

STARTF_USESTDHANDLES sets the standard input, standard output, and standard error handles for the process to the handles specified in the hStdInput, hStdOutput, and hStdError members of the STARTUPINFO structure. The CreateProcess function's fInheritHandles parameter must be set to TRUE for this to work properly.
0
 

Author Comment

by:niven
ID: 2693896
Doesn't seem to work...

Do you know what the difference could be between NT and W9x?

0
 

Author Comment

by:niven
ID: 2693927
Doesn't seem to work...

Do you know what the difference could be between NT and W9x?

0
 
LVL 8

Expert Comment

by:gelbert
ID: 2694112
Take a look at:"INFO: Redirection Issues on Windows 95 MS-DOS Applications"
ID: Q150956
0
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 

Author Comment

by:niven
ID: 2694164
I have, and it's just telling me to do exactly what I'm already doing...
(the code is even practically the same)
0
 
LVL 1

Expert Comment

by:charlass
ID: 2699493
You have to set:
saAttr.dwFlags |= STARTF_USESTDHANDLES;

By that is CreateProcess() looking for the new handles in saAttr.hStdOut and ...

SetStdHandle() and DuplicateHandle() are not required (my experience with WinNT).

Feel free to ask for the (wicked) code.
Bye!

0
 

Author Comment

by:niven
ID: 2699719
That's what I'm doing, but it just doesn't do anything...
Could you post some example code?

0
 
LVL 1

Expert Comment

by:charlass
ID: 2699737
Here we go...
(U can't compile it without removing some of my own stuff but the code should be self-explaining)


//===========================================================================
// Execute a console(!) applicatin. Can redirect stdio.
//===========================================================================
// IN:      application            Full(!) name of the application to execute
// IN:      cmd_line            Optional arguments for the app - can be NULL
// IN:      work_dir            Optional - can be NULL (means the current dir)
// IN:      flags                  See GenOsse.h
// RET: 0 == success
//===========================================================================
// Redirection of stdio:
//      The required/created files must/will reside in "work_dir".
//===========================================================================
// WIN32:
//      The code creates the redirection files and hides the console window.
// MAC:
//      The console app is itself responsible to create the redirect files
//      and to hide the window.
//===========================================================================
// Once you called the app, check the redirected files for data.
//===========================================================================
DLLAPI extern int os_execconsole(       const char* application,
                                                      const char* cmd_line,
                                                      const char* work_dir,
                                                      const unsigned flags)
{
// Param check
      if( ! application) { return -1; }

// Remember automatically the previous work dir
      OS_DirectoryStack ds;

// Will contain the expanded redirction file names
      const char stdin_file [] = {"stdin.txt"};
      const char stdout_file[] = {"stdout.txt"};
      const char stderr_file[] = {"stderr.txt"};

// Change into the workdir, if given
      if( work_dir)
            if( os_chdir( work_dir) == -1)
                  return os_errno();

//-----------------------------------------------------------------------------
#ifdef WIN32
// NOTE: All functions/structures are ASCII-forced!
//       You see it by the trails "A".

// WIN32 parameters
      SECURITY_ATTRIBUTES      sa = { sizeof(sa) };      // Open files in inheritable mode
      STARTUPINFOA            si = { sizeof(si) };      // Input for CreateProcess()
      PROCESS_INFORMATION      pi = {0};                        // Output of CreateProcess()

      sa.bInheritHandle            = TRUE;            // Allow inheritance
      sa.lpSecurityDescriptor = NULL;            // handles to the child process

      si.dwFlags |= STARTF_USESHOWWINDOW;
      si.dwFlags |= (flags & OS_EC_REDIRECT_MASK) ? STARTF_USESTDHANDLES : 0;
      si.wShowWindow = (flags & OS_EC_HIDDEN) ? SW_HIDE : 0;

// Open/create the stdio-redirections with the inherited security handle
      if( flags & OS_EC_REDIRECT_STDIN)
            if( (si.hStdOutput = CreateFileA( stdin_file, GENERIC_READ, FILE_SHARE_READ, &sa, OPEN_EXISTING, 0, NULL)) == NULL)
                  return GetLastError();

      if( flags & OS_EC_REDIRECT_STDOUT)
            if( (si.hStdOutput = CreateFileA( stdout_file, GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, 0, NULL)) == NULL)
                  return GetLastError();

      if( flags & OS_EC_REDIRECT_STDERR)
            if( (si.hStdError = CreateFileA( stderr_file,  GENERIC_WRITE, FILE_SHARE_READ, &sa, CREATE_ALWAYS, 0, NULL)) == NULL)
                  return GetLastError();

// Build the command line to call
      char cmd[4*MAX_PATH];
      sprintf( cmd, "%s %s", application, (cmd_line ? cmd_line : ""));

// Call the process now! Hot! Only 1.29 USD/min!
      int err = CreateProcessA(NULL,                        // name of process - is done in "cmd"
                                          cmd,                        // the complete command to execute
                                          NULL,                        // lpProcessAttr
                                          NULL,                        // lpThreadAttr
                                          (flags & OS_EC_REDIRECT_MASK),      // bInheritHandles
                                          0,                              // dwCreationFlags
                                          NULL,                        // lpEnvironment
                                          NULL,                        // lpCurrDir
                                          &si,                        // StartupInfo - contains the StdHandles!
                                          &pi);                        // lpProcessInfo
      if( err == FALSE)
      {
      // Oops. "err" is only a boolean flag - get here the correct error value;
            err = GetLastError();
      }
      else
      {
            if( flags & OS_EC_WAIT)
            {
                  switch( err = WaitForSingleObject( pi.hProcess, INFINITE))
                  {
                        case WAIT_ABANDONED:      err = 0;      break;
                        case WAIT_OBJECT_0:            err = 0;      break;
                        case WAIT_TIMEOUT:            break;
                        case WAIT_FAILED:            break;
                        default:                        break;
                  }
            }
            else
            {
                  err = 0;
            }
      }

// Close the opened files, if the call was blocking
      if( err || (flags & OS_EC_WAIT))
      {
            if( si.hStdInput)  { CloseHandle( si.hStdInput); }
            if( si.hStdOutput) { CloseHandle( si.hStdOutput); }
            if( si.hStdError)  { CloseHandle( si.hStdError); }
      }

      return err;
#endif
//-----------------------------------------------------------------------------


0
 
LVL 1

Accepted Solution

by:
charlass earned 100 total points
ID: 2699740
Oops.
Just saw that all tabs are gone.
Request it again at charlass@saxsys.de
0

Featured Post

What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

Join & Write a Comment

This is to be the first in a series of articles demonstrating the development of a complete windows based application using the MFC classes.  I’ll try to keep each article focused on one (or a couple) of the tasks that one may meet.   Introductio…
Introduction: Dynamic window placements and drawing on a form, simple usage of windows registry as a storage place for information. Continuing from the first article about sudoku.  There we have designed the application and put a lot of user int…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
You have products, that come in variants and want to set different prices for them? Watch this micro tutorial that describes how to configure prices for Magento super attributes. Assigning simple products to configurable: We assigned simple products…

762 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

Need Help in Real-Time?

Connect with top rated Experts

24 Experts available now in Live!

Get 1:1 Help Now