Newsgroup: it.comp.lang.c++
Titolo: Visual C++: Redirezione stdin/stderr

Ciao a tutti,
Di recente ho scritto del codice (qui di seguito riporto uno snippet)
che esegue un comando shell (osql) e ne redirige lo stdio al chiamante
(redirigendo la pipe nelle STARTUP_INFO, vd. CreateProcess).
Si tratta di qualcosa di abbastanza ben documentato in giro.
Ho provato a fare la stessa cosa con stderr e funziona a patto che ci
sia almeno un byte sullo stderr: se il programma (come ad es e' per
osql) non scrive nulla sullo stderr, la prima ReadFile su quella pipe
si blocca indefinitamente.
Qualche idea? 
grazie a tutti, ciao
Fernando
-------------codice------------------------------------------
HANDLE hReadPipe, hWritePipe, hErrReadPipe, hErrWritePipe;
char Buffer[100];
char ErrBuffer[1000];
DWORD dwRead;
DWORD dwErrRead;
STARTUPINFO si = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION procinfo = {0};
BOOL started;
SECURITY_ATTRIBUTES sa;
DWORD exitcode;
char exitstr[1000];
char cmd[4000];
strcpy(cmd,(LPCTSTR) cmdline);
//setup the stdout
//fist create the sec attrib
sa.nLength = sizeof (SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = true;
//create pipe handles
if(!CreatePipe(&hReadPipe, &hWritePipe, &sa, 2500000))
{
	MessageBox("Can't create read pipe.");
	return FALSE;
}
if (hReadPipe == INVALID_HANDLE_VALUE ||
	hWritePipe == INVALID_HANDLE_VALUE){
	MessageBox ("Not a valid Handle for STDOUT", "Error",
           		MB_OK | MB_ICONEXCLAMATION);
	return FALSE;
}
//create pipe handles for stderr
if(!CreatePipe(&hErrReadPipe, &hErrWritePipe, &sa, 2500000))
{
	MessageBox("Can't create error pipe.");
	return FALSE;
}
if (hErrReadPipe== INVALID_HANDLE_VALUE ||
	hErrWritePipe == INVALID_HANDLE_VALUE){
	MessageBox ("Not a valid Handle for STDERR", "Error",
           		MB_OK | MB_ICONEXCLAMATION);
	return FALSE;
}
//setup the start up info
memset(&si, 0, sizeof(STARTUPINFO));
si.cb = sizeof (STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.hStdOutput = hWritePipe;
si.hStdError = hErrWritePipe;
started = CreateProcess ( 
	NULL,		// command is part of input string
        cmd,		// (writeable) command string (eg: "osql -?")
        NULL,		// process security
        NULL,		// thread security
	TRUE,		// inherit handles flag
	NULL,		// flags
	NULL,		// inherit environment
	NULL,		// inherit directory
	&si,		// STARTUPINFO
	&procinfo	// PROCESS_INFORMATION
	);
// handle error in CreateProcess
if(!started)
{
	LPVOID lpMsgBuf;
	FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL,
SUBLANG_DEFAULT),(LPTSTR)
&lpMsgBuf,0, NULL 
	);
	// Display the string.
	MessageBox( (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );
	// Free the buffer.
	LocalFree( lpMsgBuf );
	return FALSE;
}
CloseHandle(procinfo.hThread);
WaitForSingleObject(procinfo.hProcess, 30000);
bool ReadLoopDone = false;
//read from the pipe and put it in the message control (AddMessage
function, not shown here)
OutDlg.m_strOutput = "";
while (!ReadLoopDone){
	memset(Buffer, 0, 100);
	ReadFile(hReadPipe, &Buffer, sizeof(Buffer), &dwRead, NULL);
	Buffer[dwRead] = '\0';
	CString strBuf(Buffer);		
	AddMessage(CString(Buffer));
	strBuf.Replace("\n","\r\n");
	OutDlg.m_strOutput += strBuf;
	if (dwRead < 100 ) ReadLoopDone = true;
}
// same for error
ReadLoopDone = false;
while (!ReadLoopDone){
	memset(ErrBuffer, 0, 1000);
	ReadFile(hReadErrPipe, &ErrBuffer, sizeof(ErrBuffer), &dwErrRead,
NULL);
	if (dwErrRead < 1000) ReadLoopDone = true;
}
// NOTE: also using a single while loop for both pipes cause the 
// first call to ReadFile on stderr to block
// unless there is something on stderr (in this case all is fine!)
// retrive exit code of the process in char[] exitstr
while(GetExitCodeProcess(procinfo.hProcess,&exitcode) == STILL_ACTIVE)
	;
sprintf(exitstr,"%d",exitcode);

In generale domande come la tua, che si riferiscono a meccansimi
specifici di particolari piattaforme, sono OT in questo newsgroup.
In ogni caso questo e' il funzionamento tipico dei pipe e serve a
sincronizzare l'attivita' del processo che scrive sul pipe con quella
del processo che legge. Prova a vedere se il tuo sistema permette di
effettuare letture non bloccanti (Non blocking I/O oppure Asynchronous
I/O).
Ciao,
Nicola

 Qualche idea?
Dovresti usare qualcosa tipo <code>select()</code> di UNIX prima di
effettuare la lettura.
Non so adesso nel mondo windows cosa c'è!
--
Luigi Mantellini
Per il pessimista il bicchiere è mezzo vuoto,
per l'ottimista è mezzo pieno,
per un ingegnere è semplicemente sovradimensionato rispetto l'uso.

 Qualche idea? 
Ricorda sempre che una pipe è una struttura dati di tipo FIFO. Ti serve 
una peek, in questo caso particolare PeekNamedPipe(), o I/O asincrono, 
(ReadFileEx()) per una soluzione più generale per tutti i tipi di file
-- 
 < 
http://www.reactos.com/
 >  Open source clone of Windows NT. Current
     Don't stand, REACT!      version 0.0.20. C, C++ and ASM developers
                              and beta testers are welcome!