//
// QPerforce is a gui interface to the perforce revision control
// system. It is based on the qt library, and should be easily portable
// across any platform that has this toolkit available.
//
// Copyright (C) 2002 Jacob Gladish
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
#include "P4Process.h"
#include <windows.h>
#include <qstringlist.h>
void DisplayError(char *pszAPI);
void ReadAndHandleOutput(HANDLE hPipeRead);
void PrepAndLaunchRedirectedChild(HANDLE hChildStdOut,
HANDLE hChildStdIn,
HANDLE hChildStdErr,
const QString& args );
HANDLE hChildProcess = NULL;
static QString stdoutBuffer;
#if 0
void P4Process::_win32Exec( const QString& args )
{
char path[ 1024 ];
char tempFileName[ 1024 ];
LPCSTR tempFilePrefix = "qperforce";
GetTempPath( 1024, path );
qDebug( "Temp Path: %s", path );
GetTempFileName( path, tempFilePrefix, 0, tempFileName );
qDebug( "TempFile Name: %s", tempFileName );
}
#endif
#ifdef WIN32
void P4Process::_win32Exec( const QString& args )
{
HANDLE hOutputWrite, hOutputRead;
HANDLE hInputWrite, hInputRead;
HANDLE hErrorWrite, hErrorRead;
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(&hOutputRead,&hOutputWrite,&sa,0)) {
DisplayError( "Failed to create stdout pipe." );
}
// 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,&hInputWrite,&sa,0)) {
DisplayError( "Filaed to create stdin pipe." );
}
if (!CreatePipe(&hErrorRead,&hErrorWrite,&sa,0)) {
DisplayError( "Failed to create stderr pipe.");
}
// 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");
PrepAndLaunchRedirectedChild( hOutputWrite,hInputRead,hErrorWrite, args );
// 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( "Failed to close stdout read write." );
}
if (!CloseHandle(hInputRead )) {
DisplayError( "Failed to close stdout read handle." );
}
if (!CloseHandle(hErrorWrite)) {
DisplayError( "Failed to close stderr write handle." );
}
// Read the child's output.
ReadAndHandleOutput( hOutputRead );
// Redirection is complete
if (!CloseHandle(hOutputRead)) {
DisplayError( "Failed to close stdout read handle." );
}
if (!CloseHandle(hInputWrite)) {
DisplayError( "Failed to close stdin write handle." );
}
_cmdStdOutput = QStringList::split( "\r\n", stdoutBuffer );
}
// PrepAndLaunchRedirectedChild
// Sets up STARTUPINFO structure, and launches redirected child.
///////////////////////////////////////////////////////////////////////
void PrepAndLaunchRedirectedChild( HANDLE hChildStdOut,
HANDLE hChildStdIn,
HANDLE hChildStdErr,
const QString& args )
{
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.
char* cmdLine = const_cast< char* >( args.latin1() );
if (!CreateProcess(NULL, cmdLine, NULL, NULL, TRUE,
DETACHED_PROCESS, 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;
stdoutBuffer.truncate( 0 );
while(TRUE)
{
if (!ReadFile(hPipeRead,lpBuffer, 255,
&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");
lpBuffer[ nBytesRead ] = '\0';
QString s( lpBuffer );
stdoutBuffer = stdoutBuffer.append( QString(lpBuffer) );
// qDebug( "Read: %s\n", lpBuffer );
}
}
///////////////////////////////////////////////////////////////////////
// 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);
qWarning( "%s", szPrintBuffer );
LocalFree( lpvMessageBuffer );
// ExitProcess( GetLastError() );
}
#endif