Solved

Redirecting STDOUT of a child process under Windows NT

Posted on 2000-04-07
9
433 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
[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
  • 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
Will your db performance match your db growth?

In Percona’s white paper “Performance at Scale: Keeping Your Database on Its Toes,” we take a high-level approach to what you need to think about when planning for database scalability.

 
LVL 8

Expert Comment

by:gelbert
ID: 2694112
Take a look at:"INFO: Redirection Issues on Windows 95 MS-DOS Applications"
ID: Q150956
0
 

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

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say 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

Introduction: Finishing the grid – keyboard support for arrow keys to manoeuvre, entering the numbers.  The PreTranslateMessage function is to be used to intercept and respond to keyboard events. Continuing from the fourth article about sudoku. …
In this post we will learn different types of Android Layout and some basics of an Android App.
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.
This is my first video review of Microsoft Bookings, I will be doing a part two with a bit more information, but wanted to get this out to you folks.

617 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