Solved

C/C++ programming - redirect stdin and stdout to files

Posted on 2011-02-16
8
1,488 Views
Last Modified: 2012-06-21
Need example of C/C++ program that can run another executable and redirect its stdout and stderr to files.

Thanks.
0
Comment
Question by:longjumps
[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
8 Comments
 
LVL 86

Accepted Solution

by:
jkr earned 400 total points
ID: 34910090
Since you're apparently on Windows, here's the "Full bells and whistles" approach my MS itself: http://support.microsoft.com/default.aspx?scid=kb;en-us;190351 ("How to spawn console processes with redirected standard handles"):
/*++

      Copyright (c) 1998  Microsoft Corporation

      Module Name:

         Redirect.c

      Description:
          This sample illustrates how to spawn a child console based
          application with redirected standard handles.

          The following import libraries are required:
          user32.lib

      Dave McPherson (davemm)   11-March-98

   --*/ 

   #include<windows.h>
   #pragma comment(lib, "User32.lib")
   void DisplayError(char *pszAPI);
   void ReadAndHandleOutput(HANDLE hPipeRead);
   void PrepAndLaunchRedirectedChild(HANDLE hChildStdOut,
                                     HANDLE hChildStdIn,
                                     HANDLE hChildStdErr);
   DWORD WINAPI GetAndSendInputThread(LPVOID lpvThreadParam);

   HANDLE hChildProcess = NULL;
   HANDLE hStdIn = NULL; // Handle to parents std input.
   BOOL bRunThread = TRUE;


   void main ()
   {
      HANDLE hOutputReadTmp,hOutputRead,hOutputWrite;
      HANDLE hInputWriteTmp,hInputRead,hInputWrite;
      HANDLE hErrorWrite;
      HANDLE hThread;
      DWORD ThreadId;
      SECURITY_ATTRIBUTES sa;


      // Set up the security attributes struct.
      sa.nLength= sizeof(SECURITY_ATTRIBUTES);
      sa.lpSecurityDescriptor = NULL;
      sa.bInheritHandle = TRUE;


      // Create the child output pipe.
      if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,&sa,0))
         DisplayError("CreatePipe");


      // Create a duplicate of the output write handle for the std error
      // write handle. This is necessary in case the child application
      // closes one of its std output handles.
      if (!DuplicateHandle(GetCurrentProcess(),hOutputWrite,
                           GetCurrentProcess(),&hErrorWrite,0,
                           TRUE,DUPLICATE_SAME_ACCESS))
         DisplayError("DuplicateHandle");


      // Create the child input pipe.
      if (!CreatePipe(&hInputRead,&hInputWriteTmp,&sa,0))
         DisplayError("CreatePipe");


      // Create new output read handle and the input write handles. Set
      // the Properties to FALSE. Otherwise, the child inherits the
      // properties and, as a result, non-closeable handles to the pipes
      // are created.
      if (!DuplicateHandle(GetCurrentProcess(),hOutputReadTmp,
                           GetCurrentProcess(),
                           &hOutputRead, // Address of new handle.
                           0,FALSE, // Make it uninheritable.
                           DUPLICATE_SAME_ACCESS))
         DisplayError("DupliateHandle");

      if (!DuplicateHandle(GetCurrentProcess(),hInputWriteTmp,
                           GetCurrentProcess(),
                           &hInputWrite, // Address of new handle.
                           0,FALSE, // Make it uninheritable.
                           DUPLICATE_SAME_ACCESS))
      DisplayError("DupliateHandle");


      // Close inheritable copies of the handles you do not want to be
      // inherited.
      if (!CloseHandle(hOutputReadTmp)) DisplayError("CloseHandle");
      if (!CloseHandle(hInputWriteTmp)) DisplayError("CloseHandle");


      // Get std input handle so you can close it and force the ReadFile to
      // fail when you want the input thread to exit.
      if ( (hStdIn = GetStdHandle(STD_INPUT_HANDLE)) ==
                                                INVALID_HANDLE_VALUE )
         DisplayError("GetStdHandle");

      PrepAndLaunchRedirectedChild(hOutputWrite,hInputRead,hErrorWrite);


      // Close pipe handles (do not continue to modify the parent).
      // You need to make sure that no handles to the write end of the
      // output pipe are maintained in this process or else the pipe will
      // not close when the child process exits and the ReadFile will hang.
      if (!CloseHandle(hOutputWrite)) DisplayError("CloseHandle");
      if (!CloseHandle(hInputRead )) DisplayError("CloseHandle");
      if (!CloseHandle(hErrorWrite)) DisplayError("CloseHandle");


      // Launch the thread that gets the input and sends it to the child.
      hThread = CreateThread(NULL,0,GetAndSendInputThread,
                              (LPVOID)hInputWrite,0,&ThreadId);
      if (hThread == NULL) DisplayError("CreateThread");


      // Read the child's output.
      ReadAndHandleOutput(hOutputRead);
      // Redirection is complete


      // Force the read on the input to return by closing the stdin handle.
      if (!CloseHandle(hStdIn)) DisplayError("CloseHandle");


      // Tell the thread to exit and wait for thread to die.
      bRunThread = FALSE;

      if (WaitForSingleObject(hThread,INFINITE) == WAIT_FAILED)
         DisplayError("WaitForSingleObject");

      if (!CloseHandle(hOutputRead)) DisplayError("CloseHandle");
      if (!CloseHandle(hInputWrite)) DisplayError("CloseHandle");
   }


   /////////////////////////////////////////////////////////////////////// 
   // PrepAndLaunchRedirectedChild
   // Sets up STARTUPINFO structure, and launches redirected child.
   /////////////////////////////////////////////////////////////////////// 
   void PrepAndLaunchRedirectedChild(HANDLE hChildStdOut,
                                     HANDLE hChildStdIn,
                                     HANDLE hChildStdErr)
   {
      PROCESS_INFORMATION pi;
      STARTUPINFO si;

      // Set up the start up info struct.
      ZeroMemory(&si,sizeof(STARTUPINFO));
      si.cb = sizeof(STARTUPINFO);
      si.dwFlags = STARTF_USESTDHANDLES;
      si.hStdOutput = hChildStdOut;
      si.hStdInput  = hChildStdIn;
      si.hStdError  = hChildStdErr;
      // Use this if you want to hide the child:
      //     si.wShowWindow = SW_HIDE;
      // Note that dwFlags must include STARTF_USESHOWWINDOW if you want to
      // use the wShowWindow flags.


      // Launch the process that you want to redirect (in this case,
      // Child.exe). Make sure Child.exe is in the same directory as
      // redirect.c launch redirect from a command line to prevent location
      // confusion.
      if (!CreateProcess(NULL,"Child.EXE",NULL,NULL,TRUE,
                         CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi))
         DisplayError("CreateProcess");


      // Set global child process handle to cause threads to exit.
      hChildProcess = pi.hProcess;


      // Close any unnecessary handles.
      if (!CloseHandle(pi.hThread)) DisplayError("CloseHandle");
   }


   /////////////////////////////////////////////////////////////////////// 
   // ReadAndHandleOutput
   // Monitors handle for input. Exits when child exits or pipe breaks.
   /////////////////////////////////////////////////////////////////////// 
   void ReadAndHandleOutput(HANDLE hPipeRead)
   {
      CHAR lpBuffer[256];
      DWORD nBytesRead;
      DWORD nCharsWritten;

      while(TRUE)
      {
         if (!ReadFile(hPipeRead,lpBuffer,sizeof(lpBuffer),
                                          &nBytesRead,NULL) || !nBytesRead)
         {
            if (GetLastError() == ERROR_BROKEN_PIPE)
               break; // pipe done - normal exit path.
            else
               DisplayError("ReadFile"); // Something bad happened.
         }

         // Display the character read on the screen.
         if (!WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),lpBuffer,
                           nBytesRead,&nCharsWritten,NULL))
            DisplayError("WriteConsole");
      }
   }


   /////////////////////////////////////////////////////////////////////// 
   // GetAndSendInputThread
   // Thread procedure that monitors the console for input and sends input
   // to the child process through the input pipe.
   // This thread ends when the child application exits.
   /////////////////////////////////////////////////////////////////////// 
   DWORD WINAPI GetAndSendInputThread(LPVOID lpvThreadParam)
   {
      CHAR read_buff[256];
      DWORD nBytesRead,nBytesWrote;
      HANDLE hPipeWrite = (HANDLE)lpvThreadParam;

      // Get input from our console and send it to child through the pipe.
      while (bRunThread)
      {
         if(!ReadConsole(hStdIn,read_buff,1,&nBytesRead,NULL))
            DisplayError("ReadConsole");

         read_buff[nBytesRead] = '\0'; // Follow input with a NULL.

         if (!WriteFile(hPipeWrite,read_buff,nBytesRead,&nBytesWrote,NULL))
         {
            if (GetLastError() == ERROR_NO_DATA)
               break; // Pipe was closed (normal exit path).
            else
            DisplayError("WriteFile");
         }
      }

      return 1;
   }


   /////////////////////////////////////////////////////////////////////// 
   // DisplayError
   // Displays the error number and corresponding message.
   /////////////////////////////////////////////////////////////////////// 
   void DisplayError(char *pszAPI)
   {
       LPVOID lpvMessageBuffer;
       CHAR szPrintBuffer[512];
       DWORD nCharsWritten;

       FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
                NULL, GetLastError(),
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                (LPTSTR)&lpvMessageBuffer, 0, NULL);

       wsprintf(szPrintBuffer,
         "ERROR: API    = %s.\n   error code = %d.\n   message    = %s.\n",
                pszAPI, GetLastError(), (char *)lpvMessageBuffer);

       WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),szPrintBuffer,
                     lstrlen(szPrintBuffer),&nCharsWritten,NULL);

       LocalFree(lpvMessageBuffer);
       ExitProcess(GetLastError());
   }

   ////////////////////////////////////////////////////////////////////// 
   // child.c
   // Echoes all input to stdout. This will be redirected by the redirect
   // sample. Compile and build child.c as a Win32 Console application and
   // put it in the same directory as the redirect sample.
   // 
   #include<windows.h>
   #include<stdio.h>
   #include<string.h>

   void main ()
   {
      FILE*    fp;
      CHAR     szInput[1024];


      // Open the console. By doing this, you can send output directly to
      // the console that will not be redirected.

      fp = fopen("CON", "w");
      if (!fp) {
         printf("Error opening child console - perhaps there is none.\n");
         fflush(NULL);
      }
      else
      {

      // Write a message direct to the console (will not be redirected).

         fprintf(fp,"This data is being printed directly to the\n");
         fprintf(fp,"console and will not be redirected.\n\n");
         fprintf(fp,"Since the standard input and output have been\n");
         fprintf(fp,"redirected data sent to and from those handles\n");
         fprintf(fp,"will be redirected.\n\n");
         fprintf(fp,"To send data to the std input of this process.\n");
         fprintf(fp,"Click on the console window of the parent process\n");
         fprintf(fp,"(redirect), and enter data from it's console\n\n");
         fprintf(fp,"To exit this process send the string 'exit' to\n");
         fprintf(fp,"it's standard input\n");
         fflush(fp);
      }

      ZeroMemory(szInput,1024);
      while (TRUE)
      {
         gets(szInput);
         printf("Child echoing [%s]\n",szInput);
         fflush(NULL);  // Must flush output buffers or else redirection
                        // will be problematic.
         if (!_stricmp(szInput,"Exit") )
            break;

         ZeroMemory(szInput,strlen(szInput) );

      }
   }

Open in new window

0
 
LVL 86

Assisted Solution

by:jkr
jkr earned 400 total points
ID: 34910109
BTW, alternatively, you could also use

system("cmd.exe /c file.exe 1>&2 output.txt");

Yet' I'd rather suggest going the API way as outlined by MS.
0
 
LVL 86

Assisted Solution

by:jkr
jkr earned 400 total points
ID: 34910362
Oh, and here's a simplified example achieveing the same using regular pipes - the whole thing is a thread, so just omit the sync'ing:
ULONG   __stdcall   ExecuteJob  (   LPVOID  pv)
{
    PRNENGJOB*  pJob    =   ( PRNENGJOB*) pv;

    BOOL                bRes;
    int                 nErr;

    STARTUPINFO         si;
    PROCESS_INFORMATION pi;

    HANDLE              hMyIn;
    HANDLE              hMyErr;

    HANDLE              hStdIn;
    HANDLE              hStdOut;
    HANDLE              hStdErr;

    DWORD               dwFlags;

    DWORD               dwWritten;
    DWORD               dwRead;
    DWORD               dwReadTotal;

    char                acWork1         [   MAX_PATH];
    char                acWork2         [   MAX_PATH];

    char                acCmdLine    [   256];
    char                acOut        [   IR_LA_OUTPUT_BUFSIZ];
    char                acOutFile    [   MAX_PATH];
    char                acInFile     [   MAX_PATH];
    char                acLogFile    [   MAX_PATH];
    char                acCtrlFile   [   MAX_PATH];
    char*               pc;

    SECURITY_ATTRIBUTES sa;


    // Copy the file names to local buffers and wipe off the path information.
    // Necessary because the process doesn't like quoted file names, and the paths
    // could contain spaces.
    lstrcpyn    (   acInFile,    pJob->acInpFile, MAX_PATH);
    if  (   pc  =   strrchr (   acInFile,    '\\'))
            lstrcpy (   acInFile,    ++pc);

    lstrcpyn    (   acCtrlFile,  pJob->acCtrlFile,    MAX_PATH);
    if  (   pc  =   strrchr (   acCtrlFile,  '\\'))
            lstrcpy (   acCtrlFile,  ++pc);

    // Set up the cmd line
    wsprintf    (   acCmdLine,   
                    "&%s %s\n%s\n",
                    pJob->acFmtFile,
                    acInFile,
                    acCtrlFile
                );

    DBG1 (   "ExecuteJob():\tusing cmdline '%s'\n",
                acCmdLine
            );

    ZeroMemory  (   &sa,    sizeof  (   SECURITY_ATTRIBUTES));

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

    dwFlags =       FILE_ATTRIBUTE_NORMAL 
                |   FILE_ATTRIBUTE_TEMPORARY;

    // create 's stdin pipe
    if  (   !CreatePipe (   &hStdIn,    &hMyIn,     &sa,    0))
        {
            DBG1 (   "ExecuteJob():\tCreatePipe() for stdin failed, reason == %d\n",
                        GetLastError    ()
                    );

            return  ( E_FAIL);
        }

    // create 's stdout pipe
    if  (   !CreatePipe (   &hMyErr,    &hStdErr,   &sa,    0))
        {
            DBG1 (   "ExecuteJob():\tCreatePipe() stderr failed, reason == %d\n",
                        GetLastError    ()
                    );

            return  ( E_FAIL);
        }

    // make up the output file name
    lstrcpyn    (   acLogFile,   pJob->acInpFile, MAX_PATH);
    lstrcat     (   acLogFile,   ".out");

    DBG1 (   "ExecuteJob():\t output is redirected to '%s'\n",
                acLogFile
            );

    // 's stdout will be a temporary file, what the hell should we do
    // with all the useless output?
    hStdOut =   CreateFile  (   acLogFile,
                                GENERIC_WRITE,
                                FILE_SHARE_READ | FILE_SHARE_WRITE,
                                &sa,
                                CREATE_ALWAYS,
                                dwFlags | FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_SEQUENTIAL_SCAN,
                                NULL
                            );

    ZeroMemory  (   &si,    sizeof  (   STARTUPINFO));

    si.cb           =   sizeof  (   STARTUPINFO);
    si.dwFlags      =   STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    si.wShowWindow  =   SW_HIDE;
    si.hStdInput    =   hStdIn;
    si.hStdOutput   =   hStdOut;
    si.hStdError    =   hStdErr;

    sprintf (   acWork1,    "%s\\bin\\process.exe",  g_acSystemDir);
    sprintf (   acWork2,    "%s\\work\\",	g_acSystemDir);

    // run up  with the main thread suspended - this ensures that all
    // instances of  have initialized when the client sets the start event
    bRes    =   CreateProcess   (   acWork1,
                                    NULL,
                                    NULL,
                                    NULL,
                                    TRUE,
                                    NORMAL_PRIORITY_CLASS | CREATE_SUSPENDED,
                                    GetEnvironmentStrings   (),
                                    acWork2,
                                    &si,
                                    &pi
                                );

    // wait with patience .....
    WaitForSingleObject (   pJob->hEventStart,  INFINITE);

    if  (   bRes)
        {
            // ok let the party go on...
            VERIFY  (   ResumeThread    (   pi.hThread));
            VERIFY  (   CloseHandle (   pi.hThread));

            // write the command to the process's stdin
            if  (   !WriteFile  (   hMyIn,  
                                    acCmdLine,
                                    lstrlen (   acCmdLine),
                                    &dwWritten,
                                    NULL
                                )
                )
                {
                    // error ???
                    nErr    =   GetLastError    ();

                    DBG1 (   "ExecuteJob():\tERROR writing  cmdline, reason == %d\n", 
                                nErr
                            );

                    // killing it (not) softly!
                    VERIFY  (   TerminateProcess    (   pi.hProcess,    0));

                    VERIFY  (   CloseHandle (   hMyIn));
                    VERIFY  (   CloseHandle (   hMyErr));
                    VERIFY  (   CloseHandle (   hStdIn));
                    VERIFY  (   CloseHandle (   hStdOut));
                    VERIFY  (   CloseHandle (   hStdErr));

                    return  (   E_FAIL);
                }

            dwRead  =   dwReadTotal =   0;

            pc  =   acOut;
            WaitForSingleObject (   pi.hProcess,    INFINITE);

            VERIFY  (   CloseHandle (   pi.hProcess));
        }
     else
        {
            nErr    =   GetLastError    ();

            DBG1 (   "ExecuteJob():\tERROR creating process, reason == %d\n",    
                        nErr
                    );

            SetEvent    (   pJob->hEventDone);

            return  (   nErr);
        }

    // close the handles & check if successfull - if not, something really
    // horrible happened, and we can't trust anything, so why not _assert() ;-)
    VERIFY  (   CloseHandle (   hMyIn));
    VERIFY  (   CloseHandle (   hMyErr));
    VERIFY  (   CloseHandle (   hStdIn));
    VERIFY  (   CloseHandle (   hStdOut));
    VERIFY  (   CloseHandle (   hStdErr));

    // the  produces a  file with the same name as the input file.
    // Therefore, we make it up by simply replacing the extension
    lstrcpy (   acOutFile,   pJob->acInpFile);
    // trust _NOTHING_!!!
    if  (   pc  =   strstr  (   acOutFile,   "."))
        {
            *pc =   0;

            strcat  (   acOutFile,   ".ext");
        }
     else   strcat  (   acOutFile,   ".ext");

    if  (   0xffffffff  ==  GetFileAttributes   (   acOutFile))
        {
            DBG1 (   "ExecuteJob():\tERROR: file '%s' does NOT EXIST!!!\n",   
                        acOutFile
                    );

            // signal to the client side DLL that we're done ...
            SetEvent    (   pJob->hEventDone);

            return  (   E_UNEXPECTED);
        }

    // store the file name in the appropriate location
    lstrcpy (   pJob->acFile, acOutFile);

    // signal to the client side DLL that we're done ... 
    // SUCCESSFULLY done, i mean!
    DBG0 (   "ExecuteJob():\tdone... setting event\n");
    SetEvent    (   pJob->hEventDone);

    ExitThread  (   NOERROR);

    return  (   NOERROR);
}

Open in new window

0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 1

Author Comment

by:longjumps
ID: 34913695
jrk, thanks.

How can I compile
system("cmd.exe /c file.exe 1>&2 output.txt");

in MS Visual Studio 2010?

It seems C code, isn't?

0
 
LVL 1

Author Comment

by:longjumps
ID: 34913975
It is not passing compile.

What I am doing wrong?

/ ddProxyEXE.cpp : main project file.

#include "stdafx.h"

using namespace System;

int main(array<System::String ^> ^args)
{
    Console::WriteLine(L"Hello World");

    system("cmd.exe /c file.exe 1>&2 output.txt");

    return 0;
}

in MS VS 2010
0
 
LVL 86

Assisted Solution

by:jkr
jkr earned 400 total points
ID: 34922209
Well,

a) That's managed code
b) What error are you getting?
0
 
LVL 11

Assisted Solution

by:DeepuAbrahamK
DeepuAbrahamK earned 100 total points
ID: 34924823
You should try it in win32->console project
0
 
LVL 1

Author Comment

by:longjumps
ID: 34950899
Thanks guys,

I am checking.
0

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

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

Wouldn’t it be nice if you could test whether an element is contained in an array by using a Contains method just like the one available on List objects? Wouldn’t it be good if you could write code like this? (CODE) In .NET 3.5, this is possible…
Examines three attack vectors, specifically, the different types of malware used in malicious attacks, web application attacks, and finally, network based attacks.  Concludes by examining the means of securing and protecting critical systems and inf…
The goal of this video is to provide viewers with basic examples to understand opening and reading files in the C programming language.
The goal of this video is to provide viewers with basic examples to understand and use conditional statements in the C programming language.

733 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