710 lines
17 KiB
C++
710 lines
17 KiB
C++
|
//===== Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ======//
|
|||
|
//
|
|||
|
// Purpose:
|
|||
|
//
|
|||
|
//===========================================================================//
|
|||
|
|
|||
|
#if !defined( _X360 )
|
|||
|
#include <windows.h>
|
|||
|
#endif
|
|||
|
#include "vstdlib/iprocessutils.h"
|
|||
|
#include "tier1/utllinkedlist.h"
|
|||
|
#include "tier1/utlstring.h"
|
|||
|
#include "tier1/utlbuffer.h"
|
|||
|
#include "tier1/tier1.h"
|
|||
|
|
|||
|
// NOTE: This has to be the last file included!
|
|||
|
#include "tier0/memdbgon.h"
|
|||
|
|
|||
|
|
|||
|
class CProcess;
|
|||
|
|
|||
|
class CProcessPipeRead : public IPipeRead
|
|||
|
{
|
|||
|
// IPipeRead overrides.
|
|||
|
public:
|
|||
|
virtual int GetNumBytesAvailable();
|
|||
|
virtual void ReadAvailable( CUtlString &sStr, int nMaxBytes );
|
|||
|
virtual void ReadAvailable( CUtlBuffer *pOutBuffer, int nMaxBytes );
|
|||
|
virtual void Read( CUtlString &sStr, int nMaxBytes );
|
|||
|
virtual void ReadLine( CUtlString &sStr );
|
|||
|
|
|||
|
public:
|
|||
|
bool IsValid() const;
|
|||
|
|
|||
|
// This reads in whatever data is available in the pipe and caches it.
|
|||
|
void CacheOutput();
|
|||
|
|
|||
|
// Returns true when there is data in m_hRead, false otherwise.
|
|||
|
bool WaitForOutput();
|
|||
|
|
|||
|
int GetActualProcessOutputSize();
|
|||
|
int GetProcessOutput( void *pBuf, int nBytes );
|
|||
|
int GetActualProcessOutput( void *pBuf, int nBufLen );
|
|||
|
|
|||
|
public:
|
|||
|
CProcess *m_pProcess;
|
|||
|
|
|||
|
HANDLE m_hRead; // Comes from ProcessInfo_t::m_hChildStdoutRd or m_hChildStderrRd.
|
|||
|
|
|||
|
// This is stored if they wait for the process to exit. Even though they haven't read the data yet,
|
|||
|
// they can still call ReadAvailable() or GetNumBytesAvailable() on stdio after the process was terminated.
|
|||
|
CUtlBuffer m_CachedOutput;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
struct ProcessInfo_t
|
|||
|
{
|
|||
|
HANDLE m_hChildStdinRd;
|
|||
|
HANDLE m_hChildStdinWr;
|
|||
|
|
|||
|
HANDLE m_hChildStdoutRd;
|
|||
|
HANDLE m_hChildStdoutWr;
|
|||
|
|
|||
|
HANDLE m_hChildStderrRd;
|
|||
|
HANDLE m_hChildStderrWr;
|
|||
|
|
|||
|
HANDLE m_hProcess;
|
|||
|
CUtlString m_CommandLine;
|
|||
|
int m_fFlags; // PROCESSSTART_xxx.
|
|||
|
};
|
|||
|
|
|||
|
class CProcessUtils;
|
|||
|
|
|||
|
class CProcess : public IProcess
|
|||
|
{
|
|||
|
public:
|
|||
|
CProcess( CProcessUtils *pProcessUtils, const ProcessInfo_t &info );
|
|||
|
|
|||
|
// IProcess overrides.
|
|||
|
public:
|
|||
|
virtual void Release();
|
|||
|
virtual void Abort();
|
|||
|
virtual bool IsComplete();
|
|||
|
virtual int WaitUntilComplete();
|
|||
|
|
|||
|
virtual int WriteStdin( char *pBuf, int nBufLen );
|
|||
|
virtual IPipeRead* GetStdout();
|
|||
|
virtual IPipeRead* GetStderr();
|
|||
|
|
|||
|
virtual int GetExitCode();
|
|||
|
|
|||
|
public:
|
|||
|
ProcessInfo_t m_Info;
|
|||
|
|
|||
|
CProcessPipeRead m_StdoutRead;
|
|||
|
CProcessPipeRead m_StderrRead;
|
|||
|
|
|||
|
CProcessUtils *m_pProcessUtils;
|
|||
|
intp m_nProcessesIndex; // Index into CProcessUtils::m_Processes.
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
//-----------------------------------------------------------------------------
|
|||
|
// At the moment, we can only run one process at a time
|
|||
|
//-----------------------------------------------------------------------------
|
|||
|
class CProcessUtils : public CTier1AppSystem< IProcessUtils >
|
|||
|
{
|
|||
|
typedef CTier1AppSystem< IProcessUtils > BaseClass;
|
|||
|
|
|||
|
public:
|
|||
|
CProcessUtils() {}
|
|||
|
|
|||
|
// Inherited from IAppSystem
|
|||
|
virtual InitReturnVal_t Init();
|
|||
|
virtual void Shutdown();
|
|||
|
|
|||
|
// Inherited from IProcessUtils
|
|||
|
virtual IProcess* StartProcess( const char *pCommandLine, int fFlags, const char *pWorkingDir );
|
|||
|
virtual IProcess* StartProcess( int argc, const char **argv, int fFlags, const char *pWorkingDir );
|
|||
|
virtual int SimpleRunProcess( const char *pCommandLine, const char *pWorkingDir, CUtlString *pStdout );
|
|||
|
|
|||
|
public:
|
|||
|
void OnProcessDelete( CProcess *pProcess );
|
|||
|
|
|||
|
private:
|
|||
|
|
|||
|
// creates the process, adds it to the list and writes the windows HANDLE into info.m_hProcess
|
|||
|
CProcess* CreateProcess( ProcessInfo_t &info, int fFlags, const char *pWorkingDir );
|
|||
|
|
|||
|
CUtlFixedLinkedList< CProcess* > m_Processes;
|
|||
|
bool m_bInitialized;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
//-----------------------------------------------------------------------------
|
|||
|
// Purpose: singleton accessor
|
|||
|
//-----------------------------------------------------------------------------
|
|||
|
static CProcessUtils s_ProcessUtils;
|
|||
|
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CProcessUtils, IProcessUtils, PROCESS_UTILS_INTERFACE_VERSION, s_ProcessUtils );
|
|||
|
|
|||
|
|
|||
|
//-----------------------------------------------------------------------------
|
|||
|
// Returns the last error that occurred
|
|||
|
//-----------------------------------------------------------------------------
|
|||
|
char *GetErrorString( char *pBuf, int nBufLen )
|
|||
|
{
|
|||
|
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, pBuf, nBufLen, NULL );
|
|||
|
char *p = strchr(pBuf, '\r'); // get rid of \r\n
|
|||
|
if(p)
|
|||
|
{
|
|||
|
p[0] = 0;
|
|||
|
}
|
|||
|
return pBuf;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// ------------------------------------------------------------------------------------- //
|
|||
|
// CProcessPipeRead implementation.
|
|||
|
// ------------------------------------------------------------------------------------- //
|
|||
|
|
|||
|
bool CProcessPipeRead::IsValid() const
|
|||
|
{
|
|||
|
return m_hRead != INVALID_HANDLE_VALUE;
|
|||
|
}
|
|||
|
|
|||
|
int CProcessPipeRead::GetNumBytesAvailable()
|
|||
|
{
|
|||
|
return GetActualProcessOutputSize() + m_CachedOutput.TellPut();
|
|||
|
}
|
|||
|
|
|||
|
void CProcessPipeRead::ReadAvailable( CUtlString &sStr, int32 nMaxBytes )
|
|||
|
{
|
|||
|
int nBytesAvailable = GetNumBytesAvailable();
|
|||
|
nBytesAvailable = MIN( nBytesAvailable, nMaxBytes );
|
|||
|
|
|||
|
sStr.SetLength( nBytesAvailable ); // If nBytesAvailable != 0, this auto allocates an extra byte for the null terminator.
|
|||
|
if ( nBytesAvailable > 0 )
|
|||
|
{
|
|||
|
char *pOut = sStr.Get();
|
|||
|
int nBytesGotten = GetProcessOutput( pOut, nBytesAvailable );
|
|||
|
Assert( nBytesGotten == nBytesAvailable );
|
|||
|
|
|||
|
// Null-terminate it.
|
|||
|
pOut[nBytesGotten] = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void CProcessPipeRead::ReadAvailable( CUtlBuffer *pOutBuffer, int nMaxBytes )
|
|||
|
{
|
|||
|
int nBytesAvailable = GetNumBytesAvailable();
|
|||
|
nBytesAvailable = MIN( nBytesAvailable, nMaxBytes );
|
|||
|
|
|||
|
if ( nBytesAvailable > 0 )
|
|||
|
{
|
|||
|
char *pOut = (char*)pOutBuffer->AccessForDirectRead(nBytesAvailable+1);
|
|||
|
int nBytesGotten = GetProcessOutput( pOut, nBytesAvailable );
|
|||
|
Assert( nBytesGotten == nBytesAvailable );
|
|||
|
|
|||
|
// Null-terminate it.
|
|||
|
pOut[nBytesGotten] = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
void CProcessPipeRead::Read( CUtlString &sStr, int32 nBytes )
|
|||
|
{
|
|||
|
sStr.SetLength( 0 );
|
|||
|
|
|||
|
int nBytesLeftToRead = nBytes;
|
|||
|
while ( nBytesLeftToRead > 0 )
|
|||
|
{
|
|||
|
int nAvail = GetNumBytesAvailable();
|
|||
|
if ( nAvail == 0 )
|
|||
|
{
|
|||
|
if ( m_pProcess->IsComplete() )
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
WaitForOutput();
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
int nToRead = MIN( nBytesLeftToRead, nAvail );
|
|||
|
|
|||
|
// Read as much as we need and add it to the string.
|
|||
|
CUtlString sTemp;
|
|||
|
ReadAvailable( sTemp, nToRead );
|
|||
|
Assert( sTemp.Length() == nToRead );
|
|||
|
sStr += sTemp;
|
|||
|
|
|||
|
nBytesLeftToRead -= nToRead;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void CProcessPipeRead::ReadLine( CUtlString &sStr )
|
|||
|
{
|
|||
|
sStr.SetLength( 0 );
|
|||
|
while ( 1 )
|
|||
|
{
|
|||
|
// Wait for output if there's nothing left in our cached output.
|
|||
|
if ( m_CachedOutput.GetBytesRemaining() == 0 && !WaitForOutput() )
|
|||
|
return;
|
|||
|
|
|||
|
CacheOutput();
|
|||
|
|
|||
|
char *pStr = (char*)m_CachedOutput.PeekGet();
|
|||
|
int nBytes = m_CachedOutput.GetBytesRemaining();
|
|||
|
|
|||
|
// No more stuff available and the process is dead?
|
|||
|
if ( nBytes == 0 && m_pProcess->IsComplete() )
|
|||
|
break;
|
|||
|
|
|||
|
int i;
|
|||
|
for ( i=0; i < nBytes; i++ )
|
|||
|
{
|
|||
|
if ( pStr[i] == '\n' )
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if ( i < nBytes )
|
|||
|
{
|
|||
|
// We hit a line ending.
|
|||
|
int nBytesToRead = i;
|
|||
|
if ( nBytesToRead > 0 && pStr[nBytesToRead-1] == '\r' )
|
|||
|
--nBytesToRead;
|
|||
|
|
|||
|
CUtlString sTemp;
|
|||
|
sTemp.SetDirect( pStr, nBytesToRead );
|
|||
|
sStr += sTemp;
|
|||
|
|
|||
|
m_CachedOutput.SeekGet( CUtlBuffer::SEEK_CURRENT, i+1 ); // Use i here because it'll be at the \n, not the \r\n.
|
|||
|
if ( m_CachedOutput.GetBytesRemaining() == 0 )
|
|||
|
m_CachedOutput.Purge();
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
bool CProcessPipeRead::WaitForOutput()
|
|||
|
{
|
|||
|
if ( m_hRead == INVALID_HANDLE_VALUE )
|
|||
|
{
|
|||
|
Assert( false );
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
while ( GetActualProcessOutputSize() == 0 )
|
|||
|
{
|
|||
|
if ( m_pProcess->IsComplete() )
|
|||
|
return false;
|
|||
|
else
|
|||
|
ThreadSleep( 1 );
|
|||
|
}
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
void CProcessPipeRead::CacheOutput()
|
|||
|
{
|
|||
|
int nBytes = GetActualProcessOutputSize();
|
|||
|
if ( nBytes == 0 )
|
|||
|
return;
|
|||
|
|
|||
|
int nPut = m_CachedOutput.TellPut();
|
|||
|
m_CachedOutput.EnsureCapacity( nPut + nBytes );
|
|||
|
|
|||
|
int nBytesRead = GetActualProcessOutput( (char*)m_CachedOutput.PeekPut(), nBytes );
|
|||
|
Assert( nBytesRead == nBytes );
|
|||
|
|
|||
|
m_CachedOutput.SeekPut( CUtlBuffer::SEEK_HEAD, nPut + nBytesRead );
|
|||
|
}
|
|||
|
|
|||
|
//-----------------------------------------------------------------------------
|
|||
|
// Methods used to read output back from a process
|
|||
|
//-----------------------------------------------------------------------------
|
|||
|
int CProcessPipeRead::GetActualProcessOutputSize()
|
|||
|
{
|
|||
|
if ( m_hRead == INVALID_HANDLE_VALUE )
|
|||
|
{
|
|||
|
Assert( false );
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
DWORD dwCount = 0;
|
|||
|
if ( !PeekNamedPipe( m_hRead, NULL, NULL, NULL, &dwCount, NULL ) )
|
|||
|
{
|
|||
|
char buf[ 512 ];
|
|||
|
Warning( "Could not read from pipe associated with command %s\n"
|
|||
|
"Windows gave the error message:\n \"%s\"\n",
|
|||
|
m_pProcess->m_Info.m_CommandLine.Get(), GetErrorString( buf, sizeof(buf) ) );
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
return (int)dwCount;
|
|||
|
}
|
|||
|
|
|||
|
int CProcessPipeRead::GetProcessOutput( void *pBuf, int nBytes )
|
|||
|
{
|
|||
|
int nCachedBytes = MIN( nBytes, m_CachedOutput.TellPut() );
|
|||
|
int nPipeBytes = nBytes - nCachedBytes;
|
|||
|
|
|||
|
// Read from the cached buffer.
|
|||
|
m_CachedOutput.Get( pBuf, nCachedBytes );
|
|||
|
if ( m_CachedOutput.GetBytesRemaining() == 0 )
|
|||
|
{
|
|||
|
m_CachedOutput.Purge();
|
|||
|
}
|
|||
|
|
|||
|
// Read from the pipe.
|
|||
|
int nPipedBytesRead = GetActualProcessOutput( (char*)pBuf + nCachedBytes, nPipeBytes );
|
|||
|
return nCachedBytes + nPipedBytesRead;
|
|||
|
}
|
|||
|
|
|||
|
int CProcessPipeRead::GetActualProcessOutput( void *pBuf, int nBufLen )
|
|||
|
{
|
|||
|
if ( m_hRead == INVALID_HANDLE_VALUE )
|
|||
|
{
|
|||
|
Assert( false );
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
// ReadFile can deadlock in a blaze of awesomeness if you ask for 0 bytes.
|
|||
|
if ( nBufLen == 0 )
|
|||
|
return 0;
|
|||
|
|
|||
|
DWORD nBytesRead = 0;
|
|||
|
BOOL bSuccess = ReadFile( m_hRead, pBuf, nBufLen, &nBytesRead, NULL );
|
|||
|
|
|||
|
if ( bSuccess )
|
|||
|
{
|
|||
|
// ReadFile -should- block until it gets the number of bytes requested OR until
|
|||
|
// the process is complete. So if it didn't return the # of bytes requested,
|
|||
|
// then make sure the process is complete.
|
|||
|
if ( (int)nBytesRead != nBufLen )
|
|||
|
{
|
|||
|
Assert( m_pProcess->IsComplete() );
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Assert( false );
|
|||
|
}
|
|||
|
|
|||
|
return (int)nBytesRead;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// ------------------------------------------------------------------------------------- //
|
|||
|
// CProcess implementation.
|
|||
|
// ------------------------------------------------------------------------------------- //
|
|||
|
|
|||
|
CProcess::CProcess( CProcessUtils *pProcessUtils, const ProcessInfo_t &info )
|
|||
|
{
|
|||
|
m_pProcessUtils = pProcessUtils;
|
|||
|
m_Info = info;
|
|||
|
|
|||
|
m_StdoutRead.m_pProcess = this;
|
|||
|
m_StdoutRead.m_hRead = info.m_hChildStdoutRd;
|
|||
|
|
|||
|
m_StderrRead.m_pProcess = this;
|
|||
|
m_StderrRead.m_hRead = info.m_hChildStderrRd;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void CProcess::Release()
|
|||
|
{
|
|||
|
if ( !( m_Info.m_fFlags & STARTPROCESS_NOAUTOKILL ) )
|
|||
|
{
|
|||
|
Abort();
|
|||
|
}
|
|||
|
|
|||
|
ProcessInfo_t& info = m_Info;
|
|||
|
CloseHandle( info.m_hChildStderrRd );
|
|||
|
CloseHandle( info.m_hChildStderrWr );
|
|||
|
CloseHandle( info.m_hChildStdinRd );
|
|||
|
CloseHandle( info.m_hChildStdinWr );
|
|||
|
CloseHandle( info.m_hChildStdoutRd );
|
|||
|
CloseHandle( info.m_hChildStdoutWr );
|
|||
|
|
|||
|
m_pProcessUtils->OnProcessDelete( this );
|
|||
|
delete this;
|
|||
|
}
|
|||
|
|
|||
|
void CProcess::Abort()
|
|||
|
{
|
|||
|
if ( !IsComplete() )
|
|||
|
{
|
|||
|
TerminateProcess( m_Info.m_hProcess, 1 );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
bool CProcess::IsComplete()
|
|||
|
{
|
|||
|
HANDLE h = m_Info.m_hProcess;
|
|||
|
return ( WaitForSingleObject( h, 0 ) != WAIT_TIMEOUT );
|
|||
|
}
|
|||
|
|
|||
|
int CProcess::WaitUntilComplete()
|
|||
|
{
|
|||
|
ProcessInfo_t &info = m_Info;
|
|||
|
|
|||
|
if ( info.m_hChildStdoutRd == INVALID_HANDLE_VALUE )
|
|||
|
{
|
|||
|
WaitForSingleObject( info.m_hProcess, INFINITE );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// NOTE: The called process can block during writes to stderr + stdout
|
|||
|
// if the pipe buffer is empty. Therefore, waiting INFINITE is not
|
|||
|
// possible here. We must queue up messages received to allow the
|
|||
|
// process to continue
|
|||
|
while ( WaitForSingleObject( info.m_hProcess, 50 ) == WAIT_TIMEOUT )
|
|||
|
{
|
|||
|
m_StdoutRead.CacheOutput();
|
|||
|
if ( m_StderrRead.IsValid() )
|
|||
|
m_StderrRead.CacheOutput();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return GetExitCode();
|
|||
|
}
|
|||
|
|
|||
|
int CProcess::WriteStdin( char *pBuf, int nBufLen )
|
|||
|
{
|
|||
|
ProcessInfo_t& info = m_Info;
|
|||
|
if ( info.m_hChildStdinWr == INVALID_HANDLE_VALUE )
|
|||
|
{
|
|||
|
Assert( false );
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
DWORD nBytesWritten = 0;
|
|||
|
if ( WriteFile( info.m_hChildStdinWr, pBuf, nBufLen, &nBytesWritten, NULL ) )
|
|||
|
return (int)nBytesWritten;
|
|||
|
else
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
IPipeRead* CProcess::GetStdout()
|
|||
|
{
|
|||
|
return &m_StdoutRead;
|
|||
|
}
|
|||
|
|
|||
|
IPipeRead* CProcess::GetStderr()
|
|||
|
{
|
|||
|
return &m_StderrRead;
|
|||
|
}
|
|||
|
|
|||
|
int CProcess::GetExitCode()
|
|||
|
{
|
|||
|
ProcessInfo_t &info = m_Info;
|
|||
|
DWORD nExitCode;
|
|||
|
BOOL bOk = GetExitCodeProcess( info.m_hProcess, &nExitCode );
|
|||
|
if ( !bOk || nExitCode == STILL_ACTIVE )
|
|||
|
return -1;
|
|||
|
|
|||
|
return nExitCode;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//-----------------------------------------------------------------------------
|
|||
|
// Initialize, shutdown process system
|
|||
|
//-----------------------------------------------------------------------------
|
|||
|
InitReturnVal_t CProcessUtils::Init()
|
|||
|
{
|
|||
|
InitReturnVal_t nRetVal = BaseClass::Init();
|
|||
|
if ( nRetVal != INIT_OK )
|
|||
|
return nRetVal;
|
|||
|
|
|||
|
m_bInitialized = true;
|
|||
|
return INIT_OK;
|
|||
|
}
|
|||
|
|
|||
|
void CProcessUtils::Shutdown()
|
|||
|
{
|
|||
|
Assert( m_bInitialized );
|
|||
|
Assert( m_Processes.Count() == 0 );
|
|||
|
|
|||
|
// Delete any lingering processes.
|
|||
|
while ( m_Processes.Count() > 0 )
|
|||
|
{
|
|||
|
m_Processes[ m_Processes.Head() ]->Release();
|
|||
|
}
|
|||
|
|
|||
|
m_bInitialized = false;
|
|||
|
return BaseClass::Shutdown();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void CProcessUtils::OnProcessDelete( CProcess *pProcess )
|
|||
|
{
|
|||
|
m_Processes.Remove( pProcess->m_nProcessesIndex );
|
|||
|
}
|
|||
|
|
|||
|
CProcess *CProcessUtils::CreateProcess( ProcessInfo_t &info, int fFlags, const char *pWorkingDir )
|
|||
|
{
|
|||
|
STARTUPINFO si;
|
|||
|
memset(&si, 0, sizeof si);
|
|||
|
si.cb = sizeof(si);
|
|||
|
if ( fFlags & STARTPROCESS_CONNECTSTDPIPES )
|
|||
|
{
|
|||
|
si.dwFlags = STARTF_USESTDHANDLES;
|
|||
|
si.hStdInput = info.m_hChildStdinRd;
|
|||
|
si.hStdError = info.m_hChildStderrWr;
|
|||
|
si.hStdOutput = info.m_hChildStdoutWr;
|
|||
|
}
|
|||
|
|
|||
|
DWORD dwCreateProcessFlags = 0;
|
|||
|
if ( !( fFlags & STARTPROCESS_SHARE_CONSOLE ) )
|
|||
|
{
|
|||
|
dwCreateProcessFlags |= DETACHED_PROCESS;
|
|||
|
}
|
|||
|
|
|||
|
PROCESS_INFORMATION pi;
|
|||
|
if ( ::CreateProcess( NULL, info.m_CommandLine.Get(), NULL, NULL, TRUE, dwCreateProcessFlags, NULL, pWorkingDir, &si, &pi ) )
|
|||
|
{
|
|||
|
info.m_hProcess = pi.hProcess;
|
|||
|
|
|||
|
CProcess *pProcess = new CProcess( this, info );
|
|||
|
|
|||
|
pProcess->m_nProcessesIndex = m_Processes.AddToTail( pProcess );
|
|||
|
return pProcess;
|
|||
|
}
|
|||
|
|
|||
|
char buf[ 512 ];
|
|||
|
Warning( "Could not execute the command:\n %s\n"
|
|||
|
"Windows gave the error message:\n \"%s\"\n",
|
|||
|
info.m_CommandLine.Get(), GetErrorString( buf, sizeof(buf) ) );
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
//-----------------------------------------------------------------------------
|
|||
|
// Options for compilation
|
|||
|
//-----------------------------------------------------------------------------
|
|||
|
IProcess* CProcessUtils::StartProcess( const char *pCommandLine, int fFlags, const char *pWorkingDir )
|
|||
|
{
|
|||
|
Assert( m_bInitialized );
|
|||
|
|
|||
|
ProcessInfo_t info;
|
|||
|
info.m_CommandLine = pCommandLine;
|
|||
|
|
|||
|
if ( !(fFlags & STARTPROCESS_CONNECTSTDPIPES) )
|
|||
|
{
|
|||
|
info.m_hChildStderrRd = info.m_hChildStderrWr = INVALID_HANDLE_VALUE;
|
|||
|
info.m_hChildStdinRd = info.m_hChildStdinWr = INVALID_HANDLE_VALUE;
|
|||
|
info.m_hChildStdoutRd = info.m_hChildStdoutWr = INVALID_HANDLE_VALUE;
|
|||
|
|
|||
|
return CreateProcess( info, fFlags, pWorkingDir );
|
|||
|
}
|
|||
|
|
|||
|
SECURITY_ATTRIBUTES saAttr;
|
|||
|
|
|||
|
// Set the bInheritHandle flag so pipe handles are inherited.
|
|||
|
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|||
|
saAttr.bInheritHandle = TRUE;
|
|||
|
saAttr.lpSecurityDescriptor = NULL;
|
|||
|
|
|||
|
|
|||
|
DWORD nPipeSize = 0; // default size
|
|||
|
|
|||
|
if ( fFlags & STARTPROCESS_FATPIPES )
|
|||
|
{
|
|||
|
nPipeSize = 1024*1024;
|
|||
|
}
|
|||
|
|
|||
|
// Create a pipe for the child's STDOUT.
|
|||
|
if ( CreatePipe( &info.m_hChildStdoutRd, &info.m_hChildStdoutWr, &saAttr, nPipeSize ) )
|
|||
|
{
|
|||
|
if ( CreatePipe( &info.m_hChildStdinRd, &info.m_hChildStdinWr, &saAttr, nPipeSize ) )
|
|||
|
{
|
|||
|
BOOL bSuccess = false;
|
|||
|
if ( fFlags & STARTPROCESS_SEPARATE_STDERR )
|
|||
|
{
|
|||
|
bSuccess = CreatePipe( &info.m_hChildStderrRd, &info.m_hChildStderrWr, &saAttr, nPipeSize );
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
bSuccess = DuplicateHandle( GetCurrentProcess(), info.m_hChildStdoutWr, GetCurrentProcess(),
|
|||
|
&info.m_hChildStderrWr, 0, TRUE, DUPLICATE_SAME_ACCESS );
|
|||
|
|
|||
|
info.m_hChildStderrRd = INVALID_HANDLE_VALUE;
|
|||
|
}
|
|||
|
|
|||
|
if ( bSuccess )
|
|||
|
{
|
|||
|
IProcess *pProcess = CreateProcess( info, fFlags, pWorkingDir );
|
|||
|
if ( pProcess )
|
|||
|
return pProcess;
|
|||
|
|
|||
|
CloseHandle( info.m_hChildStderrRd );
|
|||
|
CloseHandle( info.m_hChildStderrWr );
|
|||
|
}
|
|||
|
CloseHandle( info.m_hChildStdinRd );
|
|||
|
CloseHandle( info.m_hChildStdinWr );
|
|||
|
}
|
|||
|
CloseHandle( info.m_hChildStdoutRd );
|
|||
|
CloseHandle( info.m_hChildStdoutWr );
|
|||
|
}
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//-----------------------------------------------------------------------------
|
|||
|
// Start up a process
|
|||
|
//-----------------------------------------------------------------------------
|
|||
|
IProcess* CProcessUtils::StartProcess( int argc, const char **argv, int fFlags, const char *pWorkingDir )
|
|||
|
{
|
|||
|
CUtlString commandLine;
|
|||
|
for ( int i = 0; i < argc; ++i )
|
|||
|
{
|
|||
|
commandLine += argv[i];
|
|||
|
if ( i != argc-1 )
|
|||
|
{
|
|||
|
commandLine += " ";
|
|||
|
}
|
|||
|
}
|
|||
|
return StartProcess( commandLine.Get(), fFlags, pWorkingDir );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
int CProcessUtils::SimpleRunProcess( const char *pCommandLine, const char *pWorkingDir, CUtlString *pStdout )
|
|||
|
{
|
|||
|
int nFlags = 0;
|
|||
|
if ( pStdout )
|
|||
|
nFlags |= STARTPROCESS_CONNECTSTDPIPES;
|
|||
|
|
|||
|
IProcess *pProcess = StartProcess( pCommandLine, nFlags, pWorkingDir );
|
|||
|
if ( !pProcess )
|
|||
|
return -1;
|
|||
|
|
|||
|
int nExitCode = pProcess->WaitUntilComplete();
|
|||
|
if ( pStdout )
|
|||
|
{
|
|||
|
pProcess->GetStdout()->Read( *pStdout );
|
|||
|
}
|
|||
|
|
|||
|
pProcess->Release();
|
|||
|
return nExitCode;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Translate '\r\n' to '\n'.
|
|||
|
void TranslateLinefeedsToUnix( char *pBuffer )
|
|||
|
{
|
|||
|
char *pOut = pBuffer;
|
|||
|
while ( *pBuffer )
|
|||
|
{
|
|||
|
if ( pBuffer[0] == '\r' && pBuffer[1] == '\n' )
|
|||
|
{
|
|||
|
*pOut = '\n';
|
|||
|
++pBuffer;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
*pOut = *pBuffer;
|
|||
|
}
|
|||
|
++pBuffer;
|
|||
|
++pOut;
|
|||
|
}
|
|||
|
*pOut = 0;
|
|||
|
}
|
|||
|
|
|||
|
|