//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//=============================================================================//

#ifndef VALVE_IPC_WIN32
#define VALVE_IPC_WIN32
#ifdef _WIN32
#pragma once
#endif

#include <rpcdce.h>

// Fwd declarations
class CValveIpcMgr;
class CValveIpcServer;
class CValveIpcClient;
class CValveIpcChannel;

// Version of the protocol
#define VALVE_IPC_PROTOCOL_VER "1"

// Memory used for Valve IPC manager = 256 kB
#define VALVE_IPC_MGR_MEMORY 256 * 1024
// Name for Valve IPC manager
#define VALVE_IPC_MGR_NAME "VALVE_IPC_MGR_"

// Default IPC manager interaction timeout = 5 sec
#define VALVE_IPC_TIMEOUT 5 * 1000

// Valve IPC client-server pipe timeout = 5 sec
#define VALVE_IPC_CS_TIMEOUT 5 * 1000

// Valve IPC client-server pipe buffer = 64 kB
#define VALVE_IPC_CS_BUFFER 64 * 1024

#define VALVE_IPC_IMPL inline

//
// CValveIpcMgr
//		Used to discover, register, unregister IPC servers
//
//		Internally the memory is stored as:
//			[zero-terminated server name-1] [128-bit UUID]
//			[zero-terminated server name-2] [128-bit UUID]
//			 ...
//			[zero-terminated server name-N] [128-bit UUID]
//			[<empty string>] [GUID_NULL]
//
class CValveIpcMgr
{
	friend CValveIpcServer;
	friend CValveIpcClient;

protected:	// Create-able only by server/client
	CValveIpcMgr();
	~CValveIpcMgr();

public:
	BOOL Init( DWORD dwTimeout );

public:
	BOOL DiscoverServer( char const *szServerName, RPC_CSTR *pszServerUID );
	BOOL RegisterServer( char const *szServerName, RPC_CSTR *pszServerUID );
	BOOL UnregisterServer( RPC_CSTR szServerUID );

public:
	HANDLE DuplicateMemorySegmentHandle();

private:
	BOOL Shutdown();

private:
	HANDLE m_hMutex;
	HANDLE m_hMemorySegment;
	char *m_pMemory;

private:
	class Iterator
	{
	public:
		explicit Iterator( char *m_pMemory = NULL )
		{
			m_szServerName = m_pMemory ? m_pMemory : "";
			if ( *m_szServerName )
			{
				memcpy( &m_uuid, m_szServerName + strlen( m_szServerName ) + 1, sizeof( UUID ) );
			}
			else
			{
				m_uuid = GUID_NULL;
			}
		}

	public:
		BOOL IsValid() const
		{
			return m_szServerName && *m_szServerName &&
				memcmp( &m_uuid, &GUID_NULL, sizeof( UUID ) );
		}
		
		Iterator Next() const
		{
			return IsValid() ?
				Iterator( m_szServerName + strlen( m_szServerName ) + 1 + sizeof( UUID ) ) :
				Iterator( NULL );
		}

	public:
		char * WriteIntoMemory( char *pMemory ) const
		{
			size_t nLen = strlen( m_szServerName ) + 1;
			memmove( pMemory, m_szServerName, strlen( m_szServerName ) + 1 );
			memmove( pMemory + nLen, &m_uuid, sizeof( UUID ) );
			return pMemory + nLen + sizeof( UUID );
		}

	public:
		char *m_szServerName;
		UUID m_uuid;
	};
};

//
// CValveIpcServer
//		Used to host an IPC server
//
class CValveIpcServer
{
public:
	explicit CValveIpcServer( char const *szServerName );
	~CValveIpcServer();

public:
	BOOL Register();
	BOOL Unregister();

public:
	virtual BOOL ExecuteCommand( char *bufCommand, DWORD numCommandBytes, char *bufResult, DWORD &numResultBytes ) = 0;

public:
	BOOL Start();
	BOOL Stop();
	BOOL IsRunning() const { return m_hThread && m_bRunning; }

public:
	BOOL EnsureRegisteredAndRunning()
	{
		if ( IsRunning() )
			return TRUE;
		
		if ( Register() &&
			 Start() &&
			 IsRunning() )
			return TRUE;

		Unregister();
		return FALSE;
	}
	BOOL EnsureStoppedAndUnregistered()
	{
		Unregister();
		return TRUE;
	}

public:
	static DWORD WINAPI RunDelegate( LPVOID lpvParam );
	DWORD RunImpl();

protected:
	BOOL WaitForEvent();
	BOOL WaitForClient();
	BOOL WaitForCommand();
	BOOL WaitForResult();

protected:
	char *m_szServerName;
	RPC_CSTR m_szServerUID;
	
	HANDLE m_hMemorySegment;
	HANDLE m_hServerAlive;

	HANDLE m_hServerPipe;
	HANDLE m_hPipeEvent;

	HANDLE m_hThread;
	BOOL m_bRunning;

	char *m_pBufferRead;
	DWORD m_cbBufferRead;

	char *m_pBufferWrite;
	DWORD m_cbBufferWrite;
};

//
// CValveIpcClient
//		Used to discover a server and establish a comm channel
//
class CValveIpcClient
{
public:
	explicit CValveIpcClient( char const *szServerName );
	~CValveIpcClient();

public:
	BOOL Connect();
	BOOL Disconnect();

public:
	BOOL ExecuteCommand( LPVOID bufIn, DWORD numInBytes, LPVOID bufOut, DWORD &numOutBytes );

protected:
	char *m_szServerName;
	RPC_CSTR m_szServerUID;

	HANDLE m_hClientPipe;
};


#ifdef UTLBUFFER_H


class CValveIpcServerUtl : public CValveIpcServer
{
public:
	explicit CValveIpcServerUtl( char const *szServerName ) : CValveIpcServer( szServerName ) {}
	virtual BOOL ExecuteCommand( char *bufCommand, DWORD numCommandBytes, char *bufResult, DWORD &numResultBytes )
	{
		CUtlBuffer cmd( bufCommand, numCommandBytes, CUtlBuffer::READ_ONLY );
		CUtlBuffer res( bufResult, VALVE_IPC_CS_BUFFER, int( 0 ) );
		if ( !ExecuteCommand( cmd, res ) )
			return FALSE;
		numResultBytes = res.TellPut();
		return TRUE;
	}
	virtual BOOL ExecuteCommand( CUtlBuffer &cmd, CUtlBuffer &res ) = 0;
};

class CValveIpcClientUtl : public CValveIpcClient
{
public:
	explicit CValveIpcClientUtl( char const *szServerName ) : CValveIpcClient( szServerName ) {}
	using CValveIpcClient::ExecuteCommand;
	BOOL ExecuteCommand( CUtlBuffer &cmd, CUtlBuffer &res )
	{
		DWORD numResBytes = res.Size();
		if ( !ExecuteCommand( cmd.Base(), cmd.TellPut(), res.Base(), numResBytes ) )
			return FALSE;
		res.SeekPut( CUtlBuffer::SEEK_HEAD, numResBytes );
		return TRUE;
	}
};


#endif // UTLBUFFER_H





//////////////////////////////////////////////////////////////////////////
//
// Implementation section of CValveIpcMgr
//
//////////////////////////////////////////////////////////////////////////


VALVE_IPC_IMPL CValveIpcMgr::CValveIpcMgr() :
	m_hMutex( NULL ),
	m_hMemorySegment( NULL ),
	m_pMemory( NULL )
{
}

VALVE_IPC_IMPL CValveIpcMgr::~CValveIpcMgr()
{
	Shutdown();
}

VALVE_IPC_IMPL BOOL CValveIpcMgr::Init( DWORD dwTimeout )
{
	if ( m_pMemory )
		return TRUE;

	m_hMutex = ::CreateMutex( NULL, FALSE, VALVE_IPC_MGR_NAME "_MTX_" VALVE_IPC_PROTOCOL_VER  );
	DWORD dwWaitResult = m_hMutex ? ::WaitForSingleObject( m_hMutex, dwTimeout ) : WAIT_FAILED;

	if ( dwWaitResult == WAIT_OBJECT_0 ||
		 dwWaitResult == WAIT_ABANDONED_0 )
	{
		// We own the mgr segment

		m_hMemorySegment = ::CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, VALVE_IPC_MGR_MEMORY,
			VALVE_IPC_MGR_NAME "_MEM_" VALVE_IPC_PROTOCOL_VER );

		if ( m_hMemorySegment )
		{
			LPVOID lpvMemSegment = ::MapViewOfFile( m_hMemorySegment, FILE_MAP_ALL_ACCESS, 0, 0, 0 );

			if ( lpvMemSegment )
			{
				m_pMemory = ( char * ) lpvMemSegment;
				return TRUE;
			}
		}
	}

	if ( dwWaitResult == WAIT_OBJECT_0 ||
		 dwWaitResult == WAIT_ABANDONED_0 )
	{
		::ReleaseMutex( m_hMutex );
		::CloseHandle( m_hMutex );
		m_hMutex = NULL;
	}
	
	// Otherwise shutdown due to an init error
	Shutdown();
	return FALSE;
}

VALVE_IPC_IMPL BOOL CValveIpcMgr::Shutdown()
{
	if ( m_pMemory )
	{
		::UnmapViewOfFile( m_pMemory );
		m_pMemory = NULL;
	}

	if ( m_hMemorySegment )
	{
		::CloseHandle( m_hMemorySegment );
		m_hMemorySegment = NULL;
	}

	if ( m_hMutex )
	{
		::ReleaseMutex( m_hMutex );
		::CloseHandle( m_hMutex );
		m_hMutex = NULL;
	}

	return TRUE;
}

VALVE_IPC_IMPL BOOL CValveIpcMgr::DiscoverServer( char const *szServerName, RPC_CSTR *pszServerUID )
{
	if ( !szServerName || !*szServerName )
		return FALSE;

	for ( Iterator it( m_pMemory ); it.IsValid(); it = it.Next() )
	{
		if ( !stricmp( szServerName, it.m_szServerName ) )
		{
			if ( pszServerUID )
			{
				UuidToString( &it.m_uuid, pszServerUID );
			}
			return TRUE;
		}
	}
	return FALSE;
}

VALVE_IPC_IMPL BOOL CValveIpcMgr::RegisterServer( char const *szServerName, RPC_CSTR *pszServerUID )
{
	if ( !szServerName || !*szServerName )
		return FALSE;

	Iterator it( m_pMemory );
	for ( ; it.IsValid(); it = it.Next() )
	{
		if ( !stricmp( szServerName, it.m_szServerName ) )
		{
			// Server with same name already registered,
			// check if it is alive
			char chAliveName[ MAX_PATH ];
				RPC_CSTR szBaseName;
				UuidToString( &it.m_uuid, &szBaseName );
			sprintf( chAliveName, "%s" "_ALIVE_" VALVE_IPC_PROTOCOL_VER, szBaseName );
				RpcStringFree( &szBaseName );
			HANDLE hAliveTest = ::OpenMutex( MUTEX_ALL_ACCESS, FALSE, chAliveName );
			if ( hAliveTest )
			{
				::CloseHandle( hAliveTest );
				return FALSE; // Server is alive, can't register again
			}
			else
			{
				// Server is dead, re-use its UID
				if ( pszServerUID )
				{
					UuidToString( &it.m_uuid, pszServerUID );
				}
				return TRUE;
			}
		}
	}

	// Iterator points at the last element in the list, write the new server info
	Iterator itNewServer;
	itNewServer.m_szServerName = const_cast< char * >( szServerName );
	UuidCreate( &itNewServer.m_uuid );

	// Check that there's enough memory left in the storage
	char *pUpdateMemory = it.m_szServerName;
	if ( pUpdateMemory + strlen( szServerName ) + 1 + 32 + 2 * sizeof( UUID ) >
		 m_pMemory + VALVE_IPC_MGR_MEMORY )
	{
		return FALSE;
	}

	// Insert the new server in the list
	pUpdateMemory = itNewServer.WriteIntoMemory( pUpdateMemory );
	pUpdateMemory = Iterator().WriteIntoMemory( pUpdateMemory );

	if ( pszServerUID )
	{
		UuidToString( &itNewServer.m_uuid, pszServerUID );
	}

	return TRUE;
}

VALVE_IPC_IMPL BOOL CValveIpcMgr::UnregisterServer( RPC_CSTR szServerUID )
{
	if ( !szServerUID || !*szServerUID )
		return FALSE;

	RPC_STATUS rpcS;
	UUID uuid;
	if ( RPC_S_OK != UuidFromString( szServerUID, &uuid ) )
		return FALSE;

	for ( Iterator it( m_pMemory ); it.IsValid(); it = it.Next() )
	{
		if ( UuidEqual( &it.m_uuid, &uuid, &rpcS ) )
		{
			// This is our server, remove it from the list
			char *pMemoryUpdate = it.m_szServerName;
			for ( Iterator itRemaining = it.Next(); itRemaining.IsValid(); )
			{
				Iterator itNext = itRemaining.Next();
				pMemoryUpdate = itRemaining.WriteIntoMemory( pMemoryUpdate );
				itRemaining = itNext;
			}
			Iterator().WriteIntoMemory( pMemoryUpdate );

			return TRUE;
		}
	}
	return FALSE;
}

VALVE_IPC_IMPL HANDLE CValveIpcMgr::DuplicateMemorySegmentHandle()
{
	if ( !m_hMemorySegment )
		return NULL;

	HANDLE hDup = NULL;
	::DuplicateHandle( GetCurrentProcess(), m_hMemorySegment,
		GetCurrentProcess(), &hDup, DUPLICATE_SAME_ACCESS,
		FALSE, DUPLICATE_SAME_ACCESS );

	return hDup;
}



//////////////////////////////////////////////////////////////////////////
//
// Implementation section of CValveIpcServer
//
//////////////////////////////////////////////////////////////////////////

VALVE_IPC_IMPL CValveIpcServer::CValveIpcServer( char const *szServerName )
{
	// Copy server name
	size_t nLen = szServerName ? strlen( szServerName ) : 0;
	m_szServerName = new char[ nLen + 1 ];
	strcpy( m_szServerName, szServerName ? szServerName : "" );

	// Init remaining
	m_szServerUID = NULL;
	m_hMemorySegment = NULL;
	m_hServerAlive = NULL;
	m_hServerPipe = NULL;
	m_hPipeEvent = NULL;
	m_hThread = NULL;
	m_bRunning = FALSE;
	
	m_pBufferRead = NULL;
	m_cbBufferRead = 0;
	m_pBufferWrite = NULL;
	m_cbBufferWrite = 0;
}

VALVE_IPC_IMPL CValveIpcServer::~CValveIpcServer()
{
	Unregister();

	if ( m_szServerName )
	{
		delete [] m_szServerName;
		m_szServerName = NULL;
	}
}

VALVE_IPC_IMPL BOOL CValveIpcServer::Register()
{
	if ( m_szServerUID )
		return TRUE;

	CValveIpcMgr mgr;
	if ( !mgr.Init( VALVE_IPC_TIMEOUT ) )
		return FALSE;

	// Try registering the server
	if ( !mgr.RegisterServer( m_szServerName, &m_szServerUID ) )
		return FALSE;
	
	// Server got registered, duplicate memory segment handle
	m_hMemorySegment = mgr.DuplicateMemorySegmentHandle();
	
	// create the "server alive" object
	char chAliveName[ MAX_PATH ];
	sprintf( chAliveName, "%s" "_ALIVE_" VALVE_IPC_PROTOCOL_VER, m_szServerUID );
	m_hServerAlive = ::CreateMutex( NULL, FALSE, chAliveName );
	if ( !m_hServerAlive )
	{
		Unregister();
		return FALSE;
	}

	// Create the server pipe event
	m_hPipeEvent = ::CreateEvent( NULL, TRUE, FALSE, NULL );
	if ( !m_hPipeEvent )
	{
		Unregister();
		return FALSE;
	}

	// Create the server end of the pipe
	char chPipeName[ MAX_PATH ];
	sprintf( chPipeName, "\\\\.\\pipe\\" "%s" "_PIPE_" VALVE_IPC_PROTOCOL_VER, m_szServerUID  );
	m_hServerPipe = ::CreateNamedPipe(
		chPipeName,
		PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_WRITE_THROUGH,
		PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE,
		1,
		VALVE_IPC_CS_BUFFER, VALVE_IPC_CS_BUFFER,
		VALVE_IPC_CS_TIMEOUT,
		NULL
		);
	if ( !m_hServerPipe )
	{
		Unregister();
		return FALSE;
	}

	// Allocate the pipe buffer
	m_pBufferRead = new char [ VALVE_IPC_CS_BUFFER ];
	if ( !m_pBufferRead )
	{
		Unregister();
		return FALSE;
	}
	m_pBufferWrite = new char [ VALVE_IPC_CS_BUFFER ];
	if ( !m_pBufferWrite )
	{
		Unregister();
		return FALSE;
	}

	return TRUE;
}

VALVE_IPC_IMPL BOOL CValveIpcServer::Unregister()
{
	if ( !m_szServerUID )
		return FALSE;
	else
	{
		CValveIpcMgr mgr;
		if ( mgr.Init( VALVE_IPC_TIMEOUT ) )
		{
			mgr.UnregisterServer( m_szServerUID );
		}
	}

	// Stop the server if it is running
	Stop();

	m_cbBufferRead = 0;
	delete [] m_pBufferRead;
	m_pBufferRead = NULL;

	m_cbBufferWrite = 0;
	delete [] m_pBufferWrite;
	m_pBufferWrite = NULL;

	if ( m_hServerPipe )
	{
		::CloseHandle( m_hServerPipe );
		m_hServerPipe = NULL;
	}

	if ( m_hPipeEvent )
	{
		::CloseHandle( m_hPipeEvent );
		m_hPipeEvent = NULL;
	}

	if ( m_hServerAlive )
	{
		::CloseHandle( m_hServerAlive );
		m_hServerAlive = NULL;
	}

	if ( m_hMemorySegment )
	{
		::CloseHandle( m_hMemorySegment );
		m_hMemorySegment = NULL;
	}

	if ( m_szServerUID )
	{
		RpcStringFree( &m_szServerUID );
		m_szServerUID = NULL;
	}

	return TRUE;
}

VALVE_IPC_IMPL BOOL CValveIpcServer::WaitForEvent()
{
	for ( ; m_bRunning ; )
	{
		DWORD dwWaitResult = ::WaitForSingleObject( m_hPipeEvent, 50 );
		switch ( dwWaitResult )
		{
		case WAIT_TIMEOUT:
			continue;
		
		case WAIT_OBJECT_0:
		case WAIT_ABANDONED_0:
			::ResetEvent( m_hPipeEvent );
			return m_bRunning;
		
		default:
			return FALSE;
		}
	}

	return FALSE;
}

VALVE_IPC_IMPL BOOL CValveIpcServer::WaitForClient()
{
	OVERLAPPED ov;
	memset( &ov, 0, sizeof( ov ) );
	ov.hEvent = m_hPipeEvent;
	
	BOOL bResult = ::ConnectNamedPipe( m_hServerPipe, &ov );
	if ( bResult )	// Overlapped "ConnectNamedPipe" always returns FALSE
		return FALSE;

	switch ( GetLastError() )
	{
	case ERROR_IO_PENDING:
		// Wait for client to connect
		break;

	case ERROR_PIPE_CONNECTED:
		SetEvent( ov.hEvent );
		return TRUE;

	default:
		return FALSE;
	}

	if ( !WaitForEvent() )
		return FALSE;

	DWORD dwConnectDummy;
	if ( !::GetOverlappedResult( m_hServerPipe, &ov, &dwConnectDummy, FALSE ) )
		return FALSE;

	return TRUE;
}

VALVE_IPC_IMPL BOOL CValveIpcServer::WaitForCommand()
{
	OVERLAPPED ov;
	memset( &ov, 0, sizeof( ov ) );
	ov.hEvent = m_hPipeEvent;

	m_cbBufferRead = 0;
	BOOL bRead = ::ReadFile( m_hServerPipe, m_pBufferRead, VALVE_IPC_CS_BUFFER, &m_cbBufferRead, &ov );

	if ( bRead &&
		 m_cbBufferRead )
		return TRUE;

	if ( !bRead &&
		 GetLastError() == ERROR_IO_PENDING )
	{
		if ( !WaitForEvent() )
			return FALSE;

		bRead = GetOverlappedResult( m_hServerPipe, &ov, &m_cbBufferRead, FALSE );
		if ( !bRead || !m_cbBufferRead )
			return FALSE;

		return TRUE;
	}

	return FALSE;
}

VALVE_IPC_IMPL BOOL CValveIpcServer::WaitForResult()
{
	OVERLAPPED ov;
	memset( &ov, 0, sizeof( ov ) );
	ov.hEvent = m_hPipeEvent;

	DWORD cbWrite;
	BOOL bWrite = ::WriteFile( m_hServerPipe, m_pBufferWrite, m_cbBufferWrite, &cbWrite, &ov );
	
	if ( bWrite &&
		 cbWrite == m_cbBufferWrite )
		return TRUE;

	if ( !bWrite &&
		 GetLastError() == ERROR_IO_PENDING )
	{
		if ( !WaitForEvent() )
			return FALSE;

		bWrite = GetOverlappedResult( m_hServerPipe, &ov, &cbWrite, FALSE );
		if ( !bWrite ||
			 cbWrite != m_cbBufferWrite )
			return FALSE;

		return TRUE;
	}

	return FALSE;
}

VALVE_IPC_IMPL DWORD WINAPI CValveIpcServer::RunDelegate( LPVOID lpvParam )
{
	return reinterpret_cast< CValveIpcServer * >( lpvParam )->RunImpl();
}

VALVE_IPC_IMPL DWORD CValveIpcServer::RunImpl()
{
	for ( ; WaitForClient() ; )
	{
		for ( ; WaitForCommand() ; )
		{
			m_cbBufferWrite = 0;
			BOOL bResult = ExecuteCommand( m_pBufferRead, m_cbBufferRead, m_pBufferWrite, m_cbBufferWrite );
			if ( !bResult || !m_cbBufferWrite )
				break;

			bResult = WaitForResult();
			if ( !bResult )
				break;
		}

		::DisconnectNamedPipe( m_hServerPipe );
	}
	
	m_bRunning = FALSE;
	return FALSE;
}

VALVE_IPC_IMPL BOOL CValveIpcServer::Start()
{
	if ( m_hThread )
		return FALSE;

	m_hThread = ::CreateThread( NULL, 0, RunDelegate, this, CREATE_SUSPENDED, NULL );
	if ( !m_hThread )
		return FALSE;

	m_bRunning = TRUE;
	::ResumeThread( m_hThread );
	
	return TRUE;
}

VALVE_IPC_IMPL BOOL CValveIpcServer::Stop()
{
	if ( !m_hThread )
		return FALSE;

	m_bRunning = FALSE;
	::WaitForSingleObject( m_hThread, INFINITE );

	::CloseHandle( m_hThread );
	m_hThread = NULL;

	return TRUE;
}


//////////////////////////////////////////////////////////////////////////
//
// Implementation section of CValveIpcClient
//
//////////////////////////////////////////////////////////////////////////


VALVE_IPC_IMPL CValveIpcClient::CValveIpcClient( char const *szServerName )
{
	// Copy server name
	size_t nLen = szServerName ? strlen( szServerName ) : 0;
	m_szServerName = new char[ nLen + 1 ];
	strcpy( m_szServerName, szServerName ? szServerName : "" );

	// Init remaining
	m_szServerUID = NULL;
	m_hClientPipe = NULL;
}

VALVE_IPC_IMPL CValveIpcClient::~CValveIpcClient()
{
	Disconnect();

	if ( m_szServerName )
	{
		delete [] m_szServerName;
		m_szServerName = NULL;
	}
}

VALVE_IPC_IMPL BOOL CValveIpcClient::Connect()
{
	if ( m_szServerUID )
		return TRUE;

	CValveIpcMgr mgr;
	if ( !mgr.Init( VALVE_IPC_TIMEOUT ) )
		return FALSE;

	// Try discovering the server
	if ( !mgr.DiscoverServer( m_szServerName, &m_szServerUID ) )
		return FALSE;

	// Server got discovered
	// check the "server alive" object
	char chAliveName[ MAX_PATH ];
	sprintf( chAliveName, "%s" "_ALIVE_" VALVE_IPC_PROTOCOL_VER, m_szServerUID );
	
	HANDLE hServerAlive = ::OpenMutex( MUTEX_ALL_ACCESS, FALSE, chAliveName );
	if ( !hServerAlive )
	{
		Disconnect();
		return FALSE;
	}
	else
	{
		::CloseHandle( hServerAlive );
		hServerAlive = NULL;
	}

	// Connect the server pipe
	char chPipeName[ MAX_PATH ];
	sprintf( chPipeName, "\\\\.\\pipe\\" "%s" "_PIPE_" VALVE_IPC_PROTOCOL_VER, m_szServerUID  );
	m_hClientPipe = ::CreateFile(
		chPipeName,
		GENERIC_READ | GENERIC_WRITE,
		0,
		NULL,
		OPEN_EXISTING,
		FILE_FLAG_WRITE_THROUGH,
		NULL
		);
	if ( !m_hClientPipe )
	{
		Disconnect();
		return FALSE;
	}
	
	DWORD dwPipeMode = PIPE_READMODE_MESSAGE;
	SetNamedPipeHandleState( m_hClientPipe, &dwPipeMode, NULL, NULL );

	return TRUE;
}

VALVE_IPC_IMPL BOOL CValveIpcClient::Disconnect()
{
	if ( !m_szServerUID )
		return FALSE;

	if ( m_hClientPipe )
	{
		::CloseHandle( m_hClientPipe );
		m_hClientPipe = NULL;
	}

	if ( m_szServerUID )
	{
		RpcStringFree( &m_szServerUID );
		m_szServerUID = NULL;
	}

	return TRUE;
}

VALVE_IPC_IMPL BOOL CValveIpcClient::ExecuteCommand( LPVOID bufIn, DWORD numInBytes, LPVOID bufOut, DWORD &numOutBytes )
{
	if ( !m_szServerUID || !m_hClientPipe )
		return FALSE;

	BOOL bTransact = TransactNamedPipe( m_hClientPipe,
		bufIn, numInBytes,
		bufOut, numOutBytes,
		&numOutBytes,
		NULL );
	if ( !bTransact &&
		 GetLastError() == ERROR_MORE_DATA )
	{
		bTransact = TRUE;
	}
	return bTransact;
}




#endif // #ifndef VALVE_IPC_WIN32