Page 1 of 2 12 LastLast
Results 1 to 10 of 12

Thread: C++ - Capture output from externel exe?

  1. #1

    C++ - Capture output from externel exe?

    I have been researching this issue for a few days now and have devoured multple MSDN/Win32 API resources, forums etc.. without a solution. I have written a GUI based C++ application that is basically an overlay for a consle executable that I do not have access to the source code for.

    The console application iterates through a file system for certain file types and returns a count when finished. My GUI application builds out a text file and supplys it as a parameter to the executable, then kicks it off.

    My problem is capturing the output from the executable and logging it to a text file. Does anyone have code examples of how to capture standard output from a console application spawned as a process from within a C++ GUI app?

    I am using CreateProcess to actually spawn the app and would love some resources on how to get this figured out....

    Thanks!
    AMD Opteron 165 Dual Core (1.8ghz stock. 1.8ghz / 200 = 9x multiplier) running at 2.6ghz (still 9x multi), with a 289mhz bus (HTT/FSB), 3x HT for a final HT of 867mhz, memory set to 266mhz aka 2:3 (14) ratio for a speed of DDR377.

  2. #2
    Join Date
    Apr 2007
    Beans
    14,781

    Re: C++ - Capture output from externel exe?

    I am not exactly sure what you are looking for, but:

    Code:
    ls > log.txt
    will redirect the output of ls to a file.

    Could you describe this program more? It doesn't sound all that complicated, and you could probably write one yourself.

  3. #3

    Re: C++ - Capture output from externel exe?

    Sure - sorry if I was unclear.

    I have a console application called SubobCounter.exe that we use at work. Basically, this executable takes a text file as a parameter that specifys a location in the file system where 'subobjects' are located. (i wont go into detail as to what they are). SubobCounter.exe then chews through the file system counting how many subobjects exist and displays the total to the console window.

    I have written a C++ GUI overlay to this application that allows a user to browse for the parent directory of the subobjects and once chosen, writes out a text file with the location. This text file is then passed as the parameter to the console application SubobCounter.exe via my GUI app (using CreateProcess).

    My problem is that once I spawn the SubobCounter.exe process from within my GUI app, and after its completion, I need the results written to a text file, rather than displayed to screen. In order to do this I need to grab the output from the console window.

    I can do this easily in Java or C# with a few lines of code, but in C++ it seams to be much more difficult. I have toyed with using pipes within the Windows API but can't seem to get it working.

    Here's an example in C# i found that would do this very thing:

    Code:
    private void runSyncAndGetResults_Click(object sender, System.EventArgs e)
    {
     System.Diagnostics.ProcessStartInfo psi =   new System.Diagnostics.ProcessStartInfo(@"C:\listfiles.bat"); psi.RedirectStandardOutput = true; 
    psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; 
    psi.UseShellExecute = false; 
    System.Diagnostics.Process listFiles;
     listFiles = System.Diagnostics.Process.Start(psi);
     System.IO.StreamReader myOutput = listFiles.StandardOutput;
     listFiles.WaitForExit(2000); 
    if (listFiles.HasExited)  {  
    string output = myOutput.ReadToEnd();  
    this.processResults.Text = output; 
        }}
    Notice the part in bold - it grabs the console output from listfiles.bat and supplys it to the variable 'output'. This is essentially what I need done in order to write the output to a file.
    AMD Opteron 165 Dual Core (1.8ghz stock. 1.8ghz / 200 = 9x multiplier) running at 2.6ghz (still 9x multi), with a 289mhz bus (HTT/FSB), 3x HT for a final HT of 867mhz, memory set to 266mhz aka 2:3 (14) ratio for a speed of DDR377.

  4. #4

    Re: C++ - Capture output from externel exe?

    And to be more specific, here is a snippet of thel code for the C# version of my application that kicks of the executable console app:

    Code:
    Process myProcess = new Process();
    
                ProcessStartInfo myProcessStartInfo = new ProcessStartInfo(currDir + "\\Resources\\SubobCounter.exe", "shadow_paths.txt");
                myProcessStartInfo.CreateNoWindow = true;
                myProcessStartInfo.UseShellExecute = false;
                myProcessStartInfo.RedirectStandardOutput = true;
                myProcess.StartInfo = myProcessStartInfo;
    
                //set thread/process running to true
                running = true;
                myProcess.Start();
                
                subobProc = myProcess;
                 
                //get console output       
                if (!myProcess.HasExited)
                {
                    StreamReader myStreamReader = myProcess.StandardOutput;
                    // Read the standard output of the spawned process.
                    string myString = myStreamReader.ReadToEnd();
    Again, notice the bold portion that reads the console app's output stream.
    AMD Opteron 165 Dual Core (1.8ghz stock. 1.8ghz / 200 = 9x multiplier) running at 2.6ghz (still 9x multi), with a 289mhz bus (HTT/FSB), 3x HT for a final HT of 867mhz, memory set to 266mhz aka 2:3 (14) ratio for a speed of DDR377.

  5. #5
    Join Date
    May 2007
    Beans
    245
    Distro
    Ubuntu 10.04 Lucid Lynx

    Re: C++ - Capture output from externel exe?

    You can launch a BATCH file and have that BATCH file redirect the output to a file:

    runner.bat
    Code:
    @ECHO OFF
    yourprog.exe > results.txt
    This is assuming 'yourprog.exe' outputs to STDOUT. If, instead, it returns the desired count as an ERRORLEVEL code, then here is how to obtain that:

    CreateProcess fills in the ProcessInformation structure -- one component of this is a handle to the new process (pi.hProcess).

    Use WaitForSingleObject( pi.hProcess, INFINITE ) to detect when 'yourprog' is finished.

    Use GetExitCodeProcess( pi.hProcess, lpExitCode ) to retrieve the wanted value.

  6. #6

    Re: C++ - Capture output from externel exe?

    The batch file is a good idea (the program does write to stdout), although how would I update the GUI app once it has completed?

    I would like to run an undetermined progress bar while the process executes, and once it finishes, show that it has done so in the GUI. I suppose I could write something that monitors the file system for the creation of 'results.txt' file and update at that point, but not sure if that would be the best route.

    In the C# version of this application I have created a thread that executes the process - once the thread has completed it updates the gui with the results. I could be wrong, but I don't thing I could get the handle on the completion status with this method.
    AMD Opteron 165 Dual Core (1.8ghz stock. 1.8ghz / 200 = 9x multiplier) running at 2.6ghz (still 9x multi), with a 289mhz bus (HTT/FSB), 3x HT for a final HT of 867mhz, memory set to 266mhz aka 2:3 (14) ratio for a speed of DDR377.

  7. #7
    Join Date
    Feb 2007
    Beans
    236

    Re: C++ - Capture output from externel exe?

    No, no. Don't do that. Win32 GUI apps have no default stdout, so you can't redirect output to a GUI app that way.

    You're on the right trail. You'll use CreateProcess to launch the console app. But you'll need to create/read a pipe to capture the console app's output. Also, assuming the app allows you to pass the args on its command line, then you don't need to write out a text file containing those args. You can tell the app to read your "args" from another pipe.

    You'll also want to use a STARTUPINFO struct with CreateProcess, to determine how you want the console app to run.

    Here's some code below (untested -- there may be some syntax errors as I wrote it off the top of my head). You pass the name of the app to run, and also the args. It returns a buffer with the app's output. You must later free that buffer with GlobalFree(). For example, to run MyApp.exe, and have it operate upon the files "readme.txt" and "notes.txt", and receive back its output:

    Code:
    char * output;
    
    if ((output = run_command("MyApp.exe", "readme.txt notes.txt")))
    {
       MessageBox(0, output, "Success", MB_OK);
       GlobalFree(output);
    }
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    // Define OUTPUTBUFSIZE to be as big as the largest output you expect
    // from the console app, plus one char. For example, if you
    // expect the app to return 10 chars, define this to be 11.
    #define OUTPUTBUFSIZE 4096
    
    const TCHAR ErrorStr[] = "Error";
    const TCHAR NoMem[] = "Out of memory";
    const TCHAR NoPipeMsg[] = "Can't open pipe";
    const TCHAR NoLaunchMsg[] = "Can't start console app";
    const TCHAR NoOutput[] = "Can't read output of console app";
    
    char * run_command(LPCTSTR cmd, LPCTSTR args)
    {
    STARTUPINFO sinfo;
    PROCESS_INFORMATION pinfo;
    SECURITY_ATTRIBUTES sattr;
    HANDLE readfh, writefh;
    register char *cbuff;
    
    // Allocate a buffer to read the app's output
    if (!(cbuff = (char *)GlobalAlloc(GMEM_FIXED, OUTPUTBUFSIZE)))
    {
       MessageBox(0, &NoMem[0], &ErrorStr[0], MB_OK|MB_ICONEXCLAMATION);
       return 0;
    }
    
    // Initialize the STARTUPINFO struct
    ZeroMemory(&sinfo, sizeof(STARTUPINFO));
    sinfo.cb = sizeof(STARTUPINFO);
    
    sinfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
    
    // Uncomment this if you want to hide the other app's
    // DOS window while it runs
    //sinfo.wShowWindow = SW_HIDE;
    
    sinfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
    sinfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    sinfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
    
    // Initialize security attributes to allow the launched app to
    // inherit the caller's STDOUT, STDIN, and STDERR
    sattr.nLength = sizeof(SECURITY_ATTRIBUTES);
    sattr.lpSecurityDescriptor = 0;
    sattr.bInheritHandle = TRUE;
    
    // Get a pipe from which we read
    // output from the launched app
    if (!CreatePipe(&readfh, &sinfo.hStdOutput, &sattr, 0))
    {
       // Error opening the pipe
       MessageBox(0, &NoPipeMsg[0], &ErrorStr[0], MB_OK|MB_ICONEXCLAMATION);
    bad1:
       GlobalFree(cbuff);
       return 0;
    }
    
    // Get a pipe to which we write the args out to the launched app
    if (!CreatePipe(&sinfo.hStdInput, &writefh, &sattr, 0))
    {
       // Error opening the pipe
       MessageBox(0, &NoPipeMsg[0], &ErrorStr[0], MB_OK|MB_ICONEXCLAMATION);
    bad2:
       CloseHandle(readfh);
       CloseHandle(sinfo.hStdOutput);
       goto bad1;
    }
    
    // Launch the app. We should return immediately (while the app is running)
    if (!CreateProcess(0, cmd, 0, 0, TRUE, 0, 0, 0, &sinfo, &pinfo))
    {
       MessageBox(0, &NoLaunchMsg[0], &ErrorStr[0], MB_OK|MB_ICONEXCLAMATION);
       CloseHandle(sinfo.hStdInput);
       CloseHandle(writefh);
       goto bad2;
    }
    
    // Don't need the read access to these pipes
    CloseHandle(sinfo.hStdInput);
    CloseHandle(sinfo.hStdOutput);
    
    // We haven't yet read app's output
    sinfo.dwFlags = 0;
    
    // Input and/or output still needs to be done?
    while (args || readfh)
    {
       // More args to the app?
       if (args)
       {
          if (!(*args))
          {
             // Ok, we're done writing out the args to the app.
             // We can close that pipe now, and clear 'args' so
             // that we stop writing out args
             CloseHandle(writefh);
             writefh = args = 0;
          }
    
          // Write out more args to the app
          else
          {
             if (!WriteFile(writefh, args, lstrlen(args), &pinfo.dwThreadId, 0))
             {
    bad4:  GlobalFree(cbuff);
                cbuff = 0;
                break;
             }
    
             args += pinfo.dwThreadId;
          }
       }
    
       // Capture more output of the app?
       if (readfh)
       {
          // Read in upto OUTPUTBUFSIZE bytes
          if (!ReadFile(readfh, cbuff + sinfo.dwFlags, OUTPUTBUFSIZE - sinfo.dwFlags, &pinfo.dwProcessId, 0) || !pinfo.dwProcessId)
          {
             // If we aborted for any reason other than that the
             // app has closed that pipe, it's an
             // error. Otherwise, the program has finished its
             // output apparently
             if (GetLastError() != ERROR_BROKEN_PIPE && pinfo.dwProcessId)
             {
                // An error reading the pipe
                MessageBox(0, &NoOutput[0], &ErrorStr[0], MB_OK|MB_ICONEXCLAMATION);
                goto bad4;
             }
    
            // Close the pipe
             CloseHandle(readfh);
             readfh = 0;
          }
    
          sinfo.dwFlags += pinfo.dwProcessId;
       }
    }
    
    // Close input pipe if it's still open
    if (writefh) CloseHandle(writefh);
    
    // Close output pipe
    if (readfh) CloseHandle(readfh);
    
    // Wait for the app to finish
    WaitForSingleObject(pinfo.hProcess, INFINITE);
    
    // Close process and thread handles
    CloseHandle(pinfo.hProcess);
    CloseHandle(pinfo.hThread);
    
    // Nul-terminate it
    if (cbuff) *(cbuff + sinfo.dwFlags) = 0;
    
    // Return the output
    return cbuff;
    }
    
    #ifdef __cplusplus
    }
    #endif
    P.S. Why on earth are you asking Windows programming questions on a Linux distro's forum? You'd best go to a Windows programming newsgroup, or someplace like Code Project.
    Last edited by j_g; November 18th, 2007 at 09:54 PM.

  8. #8

    Re: C++ - Capture output from externel exe?

    Fantastic!!!

    With a little modification that did exactly what I needed to do. The text file parameter is required by the executable (tried using paths as arguments and it specifically looks for a text file), so removing that portion and manually specifying the command and text file in the createprocess worked great.

    Thank you so much!

    p.s. - I posted my question in this forum because I've had great success getting help for a wide variety of issues here. I felt it a good place to start getting feedback since there are some very talented people on this forum - thanks again, i was about to head bang my monitor.
    AMD Opteron 165 Dual Core (1.8ghz stock. 1.8ghz / 200 = 9x multiplier) running at 2.6ghz (still 9x multi), with a 289mhz bus (HTT/FSB), 3x HT for a final HT of 867mhz, memory set to 266mhz aka 2:3 (14) ratio for a speed of DDR377.

  9. #9
    Join Date
    Feb 2007
    Beans
    236

    Re: C++ - Capture output from externel exe?

    Quote Originally Posted by chrisfay View Post
    With a little modification that did exactly what I needed to do.
    Glad to hear that.

    there are some very talented people on this forum.
    Perhaps so, but obviously they will be Linux programmers, and Win32 can do things quite differently than Linux. For example, the obvious Linux solutions of redirecting stdout via the command line (whether specified directly, or in a batch file) do not work in this context. Your best bets are Code Project, MSDN (don't know why your search didn't turn up something -- I think I got some of the info for my above code from there), and a windows programming newsgroup such as comp.os.ms-windows.programmer.win32, as these are where most Windows programmers go for info. In fact, if you ask in that Win32 newsgroup, and MSDN has the answer for you, someone will likely give you the precise MSDN URL.

  10. #10

    Re: C++ - Capture output from externel exe?

    Another question -

    What's the best method to resize the output buffer dynamically if it fills up?

    Some of the commands I'm running return beyond the 4096 character buffer defined. I can increase them manually, but how do I do this within the application dynamically?

    When this happens, the app simply freezes, rather than returning an error as the code emplys it should (at least it seems).

    Thanks
    AMD Opteron 165 Dual Core (1.8ghz stock. 1.8ghz / 200 = 9x multiplier) running at 2.6ghz (still 9x multi), with a 289mhz bus (HTT/FSB), 3x HT for a final HT of 867mhz, memory set to 266mhz aka 2:3 (14) ratio for a speed of DDR377.

Page 1 of 2 12 LastLast

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •