summaryrefslogtreecommitdiff
path: root/common/valve_ipc_win32.h
diff options
context:
space:
mode:
Diffstat (limited to 'common/valve_ipc_win32.h')
-rw-r--r--common/valve_ipc_win32.h937
1 files changed, 937 insertions, 0 deletions
diff --git a/common/valve_ipc_win32.h b/common/valve_ipc_win32.h
new file mode 100644
index 0000000..13e63c8
--- /dev/null
+++ b/common/valve_ipc_win32.h
@@ -0,0 +1,937 @@
+//========= 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