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
|