Sign in to follow this  

Redirecting console output.

This topic is 4661 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

My program executes a console application (using CreateProcess). I'd like to capture the output (stdout, stderr) of the spawned application. It seems like most of the stuff google comes up with, reference to this article. But I can't even get the sample code to work! Running the compiled code, provided in the article, never reaches the line:
         // Display the character read on the screen.
==>      if (!WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE),lpBuffer,
                           nBytesRead,&nCharsWritten,NULL))



I've tried to execute several different programs, including command lines like "command.com /c dir *.* c:\*.* /s" etc. No luck! Anyone know the correct way to do it?

Share this post


Link to post
Share on other sites
Quote:
Original post by eq
Running the compiled code, provided in the article, never reaches the line:
Then what's the last line it does reach? Have you tried debugging the application?
We really need more information to be able to help you.

Unix makes this way too easy..

Share this post


Link to post
Share on other sites
Of course I've debugged, it simply doesn't read anything, always returns ERROR_BROKEN_PIPE.

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.
}


After reading some more articles, the following method only works if the executed command flushes the buffers every now and then (which isn't the case with ANY dos commands or ANY of the other programs I've tested with).
This makes it pretty useless. A workaround seems to always spawn a special program, that in turn spawns the desired program!?
Anyone got something better up and going?

Share this post


Link to post
Share on other sites
Partially sorted, it turns out that the flush isn't needed if you have a console.
So test if your app is a console app (or you might hardcode this).
If it's not a console app use AllocConsole to create a console, hide this console. The use CreateProcess with CREATE_NEW_PROCESS_GROUP instead CREATE_NEW_CONSOLE.


// Do first thing in main()
HANDLE hConsole = GetStdHandle(STD_ERROR_HANDLE);
HWND consoleWindow = null;
if ((hConsole == null) || (hConsole == INVALID_HANDLE_VALUE)){
HWND (WINAPI* getConsoleWindow)() = (HWND (WINAPI*)())GetProcAddress(GetModuleHandle("kernel32.dll"), "GetConsoleWindow");
LockWindowUpdate(GetDesktopWindow()); // sometimes it prevents flashing
if (AllocConsole() == FALSE)
DisplayError("Alloc console!");
if (getConsoleWindow){
consoleWindow = getConsoleWindow();
}else {
const char temp[] = "ThisIsMyConsoleNameMustBeUnique";
SetConsoleTitle(temp);
Sleep(40);
consoleWindow = FindWindow(NULL, temp);
}
if (!consoleWindow)
DisplayError("Couldn't find console window!");
ShowWindow(console, SW_HIDE);
LockWindowUpdate(NULL);
}



Now all output is redirected as it should.
When we're done executing a command, the console needs to be freed (FreeConsole).
The problem is that the console isn't terminated as expected (disable the ShowWindow(console, SW_HIDE) or look at the active processes in the task manager).
I tried doing a DestroyWindow on the console window but that doesn't seem to work.
How can I terminate a running application (given the HWND)?

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
As trimmed from the MSDN docs:


#include <windows.h>

#include <iostream>
using std::cout;
using std::endl;


bool CreateChildProcess(LPSTR cmd_line);
void ReadFromHandle(const HANDLE &hReadHandle);


int main(void)
{
// Be nice and save the original handle to stdout, since we will be altering where it is directed at
HANDLE hStdOutOriginal = GetStdHandle(STD_OUTPUT_HANDLE);

// stdout will be redirected from the console to this end of the pipe
HANDLE hWriteEnd;

// this is the handle we use to read back whatever was written to the pipe
HANDLE hReadEnd;

// Set the bInheritHandle flag to TRUE,
// so that the child process inherits the parent's stdio handle (**This is the key**)
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;


// 1. Create a pipe (nothing special)
if(!CreatePipe(&hReadEnd, &hWriteEnd, &saAttr, 0))
{
cout << "CreatePipe failed." << endl;
return 0;
}

//2. Redirect parent's stdout from the console to the "write" end of the pipe (nothing special)
if(!SetStdHandle(STD_OUTPUT_HANDLE, hWriteEnd))
{
cout << "stdout redirection failed." << endl;
return 0;
}

// 3. Create the child process (nothing special, except...)
// Because saAttr.bInheritHandle is TRUE, the child process will inherit the parent's stdout,
// which happens to be currently redirected to the "write" end of the pipe
if(!CreateChildProcess("rsine"))
{
cout << "Process creation failed." << endl;
return 0;
}

// 4. Close the "write" end of the pipe, since the child process is now finished
// If this is not done, the parent process will wait indefinitely for more data to come down the pipe
if(!CloseHandle(hWriteEnd))
{
cout << "Pipe write end closure failed." << endl;
return 0;
}

// 5. Print out whatever's been stuffed into the "write" end of the pipe
cout << "This is what I found in the pipe:" << endl;
cout << "@*******************************@" << endl;
ReadFromHandle(hReadEnd);
cout << "@*******************************@";

// 6. Clean up
if(!CloseHandle(hReadEnd))
{
cout << "Pipe read end closure failed." << endl;
return 0;
}

// 7. More clean up :)
SetStdHandle(STD_OUTPUT_HANDLE, hStdOutOriginal);

return 0;
}


bool CreateChildProcess(LPSTR cmd_line)
{
// Absolutely nothing fancy goes on in this function...

PROCESS_INFORMATION piProcInfo;
ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));

STARTUPINFO siStartInfo;
ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);

// Create the child process, nothing special
if(!CreateProcess(NULL, cmd_line, NULL, NULL, FALSE, 0, NULL, NULL, &siStartInfo, &piProcInfo))
{
return false;
}

// The process started and finished successfully
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
return true;
}


void ReadFromHandle(const HANDLE &hReadHandle)
{
// Nothing really fancy goes on in this function...
// the hReadHandle could be for a file, a pipe, a printer port, whatever, etc.

#define BUFSIZE 4096

DWORD dwRead;
CHAR chBuf[BUFSIZE];

// Read output from handle, and write to cout (in upper case)
for (;;)
{
if(!ReadFile(hReadHandle, chBuf, BUFSIZE, &dwRead, NULL) || dwRead == 0)
break;

for(DWORD i = 0; i < dwRead; i++)
cout << chBuf[i];
}
}

Share this post


Link to post
Share on other sites
Yes I've seen that aswell, this has the same problem as the others.
In order to capture output a console needs to be present.
Creating that console is simple (AllocConsole).
Problem is to get rid of the console once you don't need it anymore.

Share this post


Link to post
Share on other sites
If I use "cmd.exe /C" instead of "command.com /C" things seems to work ok (I've read that the console window could still be visible but haven't seen this myself).

Share this post


Link to post
Share on other sites

This topic is 4661 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this