Thank you for the response.
process finish.
the process in a console window.
On Nov 19, 4:49 pm, Joseph M. Newcomer <newco...@flounder.com> wrote:
See my new essay:
http://www.flounder.com/console_threads.htm
My only concession to sloppiness in such a typical example of "proof of concept" is that I
put all the messages as literal strings instead of moving them to the STRINGTABLE
resource. Proof-of-concept does not justify poor programming style. The rest of this
pretty much follows my standard programming guidelines, and is what I would do if I were
writing a little test app for myself.
Key here is that this app does not poll and does not consume additional CPU time if there
is nothing to do.
Sorry it took so long to get this finished, but I had a very full schedule this past week,
a combination of teaching and external commitments, such as herding people around a
dinosaur exhibit.http://www.carnegiemnh.org/, and it took about a day of writing to
document the details of the code.
joe
On Wed, 14 Nov 2007 05:13:28 -0800, luc....@gmail.com wrote:
Hello,
I'm trying I'm trying to create a filter application that filters the
stdout and stderr for a process.
It should work like this:
C:>filterOutput.exe "echo hello"
<p><span class="stdout">hello</span></p>
In other words it should run the program "echo hello" and concatenate
strings in the beginning and end of stdout and stderr. This to
facilitate creation of a log.htm file when compiling an application
for example c:> filterOutput.exe "make project" > log.htm.
I'm using this approach:
Create two unnamed pipes with CreatePipe, assign them to STARTUPINFO
and use CreateProcess to create the process. Use a never ending loop
with PeekNamedPipe and ReadFile to extract the data from stdout and
stderr of the process. Format the data and use std::out to show it.
The problem:
When I run the program from Visual Studio 7.1 it works like a charm.
When I run the program from the command line nothing is outputted to
the console. I traced the error to the call PeekNamedPipe. This does
not fail but it always returns that there is no data in the pipe. How
is this possible? Is it because the HANDLE inheritance rule are wrong?
How can I make this work?
Thank you in advance
/Lucian
Here is the code:
// FilterOutput.cpp : Defines the entry point for the console
application.
//
#include "stdafx.h"
#include <io.h>
#include <string>
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <iostream>
#include <tchar.h>
#include <afxwin.h>
#include <Windows.h>
#pragma hdrstop
#define bzero(a) memset(a,0,sizeof(a)) //easier -- shortcut
bool IsWinNT() //check if we're running NT
{
OSVERSIONINFO osv;
osv.dwOSVersionInfoSize = sizeof(osv);
GetVersionEx(&osv);
return (osv.dwPlatformId == VER_PLATFORM_WIN32_NT);
}
void ErrorMessage(char *str) //display detailed error info
{
LPVOID msg;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &msg,
0,
NULL
);
printf("%s: %s\n",str,msg);
LocalFree(msg);
}
std::string& trim2(std::string& str)
{
std::string::size_type pos = str.find_last_not_of('\n');
if(pos != std::string::npos) {
str.erase(pos + 1);
pos = str.find_first_not_of('\n');
if(pos != std::string::npos) str.erase(0, pos);
}
else str.erase(str.begin(), str.end());
pos = str.find_last_not_of('\r');
if(pos != std::string::npos) {
str.erase(pos + 1);
pos = str.find_first_not_of('\r');
if(pos != std::string::npos) str.erase(0, pos);
}
else str.erase(str.begin(), str.end());
return str;
}
HANDLE
newstdin,newstdout,read_stdout,newstderr,read_stderr,write_stdin; //
pipe handles
void close()
{
CloseHandle(newstdin);
CloseHandle(newstdout);
CloseHandle(read_stdout);
CloseHandle(newstderr);
CloseHandle(read_stderr);
CloseHandle(write_stdin);
}
//---------------------------------------------------------------------------
void main(int argc, char** argv)
{
char buf[1024]; //i/o buffer
STARTUPINFO si;
SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR sd; //security information for
pipes
PROCESS_INFORMATION pi;
if (IsWinNT()) //initialize security descriptor (Windows NT)
{
InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, true, NULL, false);
sa.lpSecurityDescriptor = &sd;
}
else sa.lpSecurityDescriptor = NULL;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = true; //allow inheritable handles
if (!CreatePipe(&newstdin,&write_stdin,&sa,0)) //create stdin pipe
{
ErrorMessage("CreatePipe");
//getch();
return;
}
if (!CreatePipe(&read_stdout,&newstdout,&sa,0)) //create stdout pipe
{
ErrorMessage("CreatePipe");
//getch();
close();
return;
}
if (!CreatePipe(&read_stderr,&newstderr,&sa,0)) //create stderr pipe
{
ErrorMessage("CreatePipe");
//getch();
close();
return;
}
GetStartupInfo(&si); //set startupinfo for the spawned process
/*
The dwFlags member tells CreateProcess how to make the process.
STARTF_USESTDHANDLES validates the hStd* members.
STARTF_USESHOWWINDOW
validates the wShowWindow member.
*/
si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.hStdOutput = newstdout;
si.hStdError = newstderr; //set the new handles for the child
process
si.hStdInput = newstdin;
char* app_spawn = argv[1]; //sample, modify for your
//system
//spawn the child process
printf("hej1\n");
if (/*!
CreateProcess(NULL,app_spawn,NULL,NULL,TRUE,CREATE_NEW_CONSOLE,
NULL,NULL,&si,&pi)*/
!CreateProcess( NULL, // No module name (use command line).
app_spawn , // Command line.
NULL, // Process handle not inheritable.
NULL, // Thread handle not inheritable.
TRUE, // Set handle inheritance to TRUE.
CREATE_NEW_CONSOLE,
NULL, // Use parent's environment block.
NULL, // Use parent's starting directory.
&si, // Pointer to STARTUPINFO structure.
&pi) )
{
ErrorMessage("CreateProcess");
close();
return;
}
printf("hej2\n");
unsigned long exit=0; //process exit code
unsigned long bread; //bytes read
unsigned long avail; //bytes available
bzero(buf);
for(;;) //main program loop
{
GetExitCodeProcess(pi.hProcess,&exit); //while the process is
running
if (exit != STILL_ACTIVE)
break;
if(!PeekNamedPipe(read_stdout,buf,1023,&bread,&avail,NULL))
{
ErrorMessage("PeekNamedPipe stdout");
}
//check to see if there is any data to read from stdout
if (bread != 0)
{
bzero(buf);
if (avail > 1023)
{
while (bread >= 1023)
{
ReadFile(read_stdout,buf,1023,&bread,NULL); //read the stdout
pipe
std::string tmp = trim2(std::string(buf));
std::cout << tmp << std::endl;
std::cerr << "<p><span class=\"stdout\">" << tmp << "</span></p>"
<< std::endl;
bzero(buf);
printf("hej3\n");
}
}
else {
ReadFile(read_stdout,buf,1023,&bread,NULL);
std::string tmp = trim2(std::string(buf));
std::cout << tmp << std::endl;
std::cerr << "<p><span class=\"stdout\">" << tmp << "</span></p>"
<< std::endl;
printf("hej4\n");
}
}/*else{
printf("not stdout read\n");
}*/
if(!PeekNamedPipe(read_stderr,buf,1023,&bread,&avail,NULL))
{
ErrorMessage("PeekNamedPipe stderr");
}
//check to see if there is any data to read from stderr
if (bread != 0)
{
bzero(buf);
if (avail > 1023)
{
while (bread >= 1023)
{
ReadFile(read_stderr,buf,1023,&bread,NULL); //read the stderr
pipe
std::string tmp = trim2(std::string(buf));
std::cout << tmp << std::endl;
std::cerr << "<p><span class=\"stdout\">" << tmp << "</span></p>"
<< std::endl;
bzero(buf);
printf("hej5\n");
}
}
else {
ReadFile(read_stderr,buf,1023,&bread,NULL);
std::string tmp = trim2(std::string(buf));
std::cout << tmp << std::endl;
std::cerr << "<p><span class=\"stdout\">" << tmp << "</span></p>"
<< std::endl;
printf("hej6\n");
}
}/*else{
printf("not stderr read\n");
}*/
if (kbhit()) //check for user input.
{
bzero(buf);
*buf = (char)getche();
//printf("%c",*buf);
WriteFile(write_stdin,buf,1,&bread,NULL); //send it to stdin
if (*buf == '\r') {
*buf = '\n';
printf("%c",*buf);
WriteFile(write_stdin,buf,1,&bread,NULL); //send an extra newline
char,
//if necessary
}
}
}
close();
}
Joseph M. Newcomer [MVP]
email: newco...@flounder.com
Web:http://www.flounder.com
MVP Tips:http://www.flounder.com/mvp_tips.htm