You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
937 lines
19 KiB
937 lines
19 KiB
//========= 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
|
|
|