/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ # include "pipeimpl.h" #ifndef _INC_MALLOC # include #endif #ifndef _INC_TCHAR # ifdef UNICODE # define _UNICODE # endif # include #endif const TCHAR PIPE_NAME_PREFIX_MAPPING[] = TEXT("PIPE_FILE_MAPPING_"); const TCHAR PIPE_NAME_PREFIX_SYNCHRONIZE[] = TEXT("PIPE_SYNCHRONIZE_MUTEX_"); const TCHAR PIPE_NAME_PREFIX_CONNECTION[] = TEXT("PIPE_CONNECTION_SEMAPHORE_"); const DWORD PIPE_BUFFER_SIZE = 4096; //============================================================================ // PipeData //============================================================================ struct PipeData { DWORD dwProcessId; HANDLE hReadPipe; HANDLE hWritePipe; }; //============================================================================ // Pipe //============================================================================ #ifdef UNICODE #define Pipe PipeW #define ClientPipe ClientPipeW #define ServerPipe ServerPipeW #else #define Pipe PipeA #define ClientPipe ClientPipeA #define ServerPipe ServerPipeA #endif class Pipe { protected: HANDLE m_hReadPipe; // Handle to use for reading HANDLE m_hWritePipe; // Handle to use for writing Pipe( HANDLE hReadPipe, HANDLE hWritePipe ); static HANDLE CreatePipeDataMutex( LPCTSTR lpName, BOOL bInitialOwner ); static HANDLE CreatePipeDataMapping( LPCTSTR lpName ); static HANDLE OpenPipeDataMapping( LPCTSTR lpName ); static HANDLE CreatePipeConnectionSemaphore( LPCTSTR lpName, LONG lInitialCount, LONG lMaximumcount ); public: Pipe( const Pipe& ); const Pipe& operator = ( const Pipe& ); virtual ~Pipe(); virtual bool Close(); virtual bool Write( LPCVOID lpBuffer, DWORD dwBytesToWrite, LPDWORD lpBytesWritten, bool bWait = true ); virtual bool Read( LPVOID lpBuffer, DWORD dwBytesToRead, LPDWORD lpBytesRead, bool bWait = true ); virtual Pipe *AcceptConnection() { SetLastError( ERROR_INVALID_HANDLE ); return NULL; } void * operator new( size_t nBytes ) { return HeapAlloc( GetProcessHeap(), 0, nBytes ); } void operator delete( void *ptr ) { HeapFree( GetProcessHeap(), 0, ptr ); } bool is() const { return (FALSE != HeapValidate( GetProcessHeap(), 0, this )); } }; //============================================================================ // ClientPipe //============================================================================ class ClientPipe : public Pipe { protected: ClientPipe( HANDLE hReadPipe, HANDLE hWritePipe ); public: static ClientPipe* Create( LPCTSTR lpName ); }; //============================================================================ // ServerPipe //============================================================================ class ServerPipe : public Pipe { protected: HANDLE m_hMapping; HANDLE m_hSynchronize; LPTSTR m_lpName; ServerPipe( LPCTSTR lpName, HANDLE hMapping, HANDLE hSynchronize, HANDLE hReadPipe, HANDLE hWritePipe ); public: virtual ~ServerPipe(); static ServerPipe *Create( LPCTSTR lpName ); virtual Pipe *AcceptConnection(); }; HANDLE Pipe::CreatePipeDataMapping( LPCTSTR lpName ) { HANDLE hMapping = NULL; LPTSTR lpMappingName = (LPTSTR)alloca( _tcslen(lpName) * sizeof(TCHAR) + sizeof(PIPE_NAME_PREFIX_MAPPING) ); if ( lpMappingName ) { _tcscpy( lpMappingName, PIPE_NAME_PREFIX_MAPPING ); _tcscat( lpMappingName, lpName ); LPTSTR lpMappingFileName = (LPTSTR)alloca( MAX_PATH * sizeof(TCHAR) ); if ( lpMappingFileName ) { DWORD nChars = GetTempPath( MAX_PATH, lpMappingFileName ); if ( MAX_PATH + _tcslen(lpName) < nChars + 1 ) { lpMappingFileName = (LPTSTR)alloca( (nChars + 1 + _tcslen(lpName)) * sizeof(TCHAR) ); if ( lpMappingFileName ) nChars = GetTempPath( nChars, lpMappingFileName ); else { nChars = 0; SetLastError( ERROR_NOT_ENOUGH_MEMORY ); } } if ( nChars ) { _tcscat( lpMappingFileName, lpMappingName ); HANDLE hFile = CreateFile( lpMappingFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL ); if ( IsValidHandle(hFile) ) { hMapping = CreateFileMapping( (HANDLE)hFile, (LPSECURITY_ATTRIBUTES)NULL, PAGE_READWRITE, 0, sizeof(PipeData), lpMappingName ); CloseHandle( hFile ); } } } else SetLastError( ERROR_NOT_ENOUGH_MEMORY ); } return hMapping; } HANDLE Pipe::OpenPipeDataMapping( LPCTSTR lpName ) { HANDLE hMapping = NULL; LPTSTR lpMappingName = (LPTSTR)alloca( _tcslen(lpName) * sizeof(TCHAR) + sizeof(PIPE_NAME_PREFIX_MAPPING) ); if ( lpMappingName ) { _tcscpy( lpMappingName, PIPE_NAME_PREFIX_MAPPING ); _tcscat( lpMappingName, lpName ); hMapping = OpenFileMapping( FILE_MAP_ALL_ACCESS, FALSE, lpMappingName ); } return hMapping; } HANDLE Pipe::CreatePipeDataMutex( LPCTSTR lpName, BOOL bInitialOwner ) { HANDLE hMutex = NULL; LPTSTR lpMutexName = (LPTSTR)alloca( _tcslen(lpName) * sizeof(TCHAR) + sizeof(PIPE_NAME_PREFIX_SYNCHRONIZE) ); if ( lpMutexName ) { _tcscpy( lpMutexName, PIPE_NAME_PREFIX_SYNCHRONIZE ); _tcscat( lpMutexName, lpName ); hMutex = CreateMutex( NULL, bInitialOwner, lpMutexName ); } return hMutex; } HANDLE Pipe::CreatePipeConnectionSemaphore( LPCTSTR lpName, LONG lInitialCount, LONG lMaximumCount ) { HANDLE hSemaphore = NULL; LPTSTR lpSemaphoreName = (LPTSTR)alloca( _tcslen(lpName) * sizeof(TCHAR) + sizeof(PIPE_NAME_PREFIX_CONNECTION) ); if ( lpSemaphoreName ) { _tcscpy( lpSemaphoreName, PIPE_NAME_PREFIX_CONNECTION ); _tcscat( lpSemaphoreName, lpName ); hSemaphore = CreateSemaphore( NULL, lInitialCount, lMaximumCount, lpSemaphoreName ); } return hSemaphore; } //---------------------------------------------------------------------------- // Pipe copy ctor //---------------------------------------------------------------------------- Pipe::Pipe( const Pipe& rPipe ) : m_hReadPipe( INVALID_HANDLE_VALUE ), m_hWritePipe( INVALID_HANDLE_VALUE ) { DuplicateHandle( GetCurrentProcess(), rPipe.m_hReadPipe, GetCurrentProcess(), &m_hReadPipe, 0, FALSE, DUPLICATE_SAME_ACCESS ); DuplicateHandle( GetCurrentProcess(), rPipe.m_hWritePipe, GetCurrentProcess(), &m_hWritePipe, 0, FALSE, DUPLICATE_SAME_ACCESS ); } //---------------------------------------------------------------------------- // Pipe assignment operator //---------------------------------------------------------------------------- const Pipe& Pipe::operator = ( const Pipe& rPipe ) { Close(); DuplicateHandle( GetCurrentProcess(), rPipe.m_hReadPipe, GetCurrentProcess(), &m_hReadPipe, 0, FALSE, DUPLICATE_SAME_ACCESS ); DuplicateHandle( GetCurrentProcess(), rPipe.m_hWritePipe, GetCurrentProcess(), &m_hWritePipe, 0, FALSE, DUPLICATE_SAME_ACCESS ); return *this; } //---------------------------------------------------------------------------- // Pipe ctor //---------------------------------------------------------------------------- Pipe::Pipe( HANDLE hReadPipe, HANDLE hWritePipe ) : m_hReadPipe( INVALID_HANDLE_VALUE ), m_hWritePipe( INVALID_HANDLE_VALUE ) { DuplicateHandle( GetCurrentProcess(), hReadPipe, GetCurrentProcess(), &m_hReadPipe, 0, FALSE, DUPLICATE_SAME_ACCESS ); DuplicateHandle( GetCurrentProcess(), hWritePipe, GetCurrentProcess(), &m_hWritePipe, 0, FALSE, DUPLICATE_SAME_ACCESS ); } //---------------------------------------------------------------------------- // Pipe dtor //---------------------------------------------------------------------------- Pipe::~Pipe() { Close(); } //---------------------------------------------------------------------------- // Pipe Close //---------------------------------------------------------------------------- bool Pipe::Close() { bool fSuccess = false; // Assume failure if ( IsValidHandle(m_hReadPipe) ) { CloseHandle( m_hReadPipe ); m_hReadPipe = INVALID_HANDLE_VALUE; } if ( IsValidHandle(m_hWritePipe) ) { CloseHandle( m_hWritePipe ); m_hWritePipe = INVALID_HANDLE_VALUE; } return fSuccess; } //---------------------------------------------------------------------------- // Pipe Write //---------------------------------------------------------------------------- bool Pipe::Write( LPCVOID lpBuffer, DWORD dwBytesToWrite, LPDWORD lpBytesWritten, bool bWait ) { DWORD dwBytesAvailable = 0; BOOL fSuccess = TRUE; if ( !bWait ) fSuccess = PeekNamedPipe( m_hReadPipe, NULL, 0, NULL, &dwBytesAvailable, NULL ); if ( fSuccess ) { if ( !bWait && dwBytesToWrite > PIPE_BUFFER_SIZE - dwBytesAvailable ) dwBytesToWrite = PIPE_BUFFER_SIZE - dwBytesAvailable ; return !!WriteFile( m_hWritePipe, lpBuffer, dwBytesToWrite, lpBytesWritten, NULL ); } return false; } //---------------------------------------------------------------------------- // Pipe Read //---------------------------------------------------------------------------- bool Pipe::Read( LPVOID lpBuffer, DWORD dwBytesToRead, LPDWORD lpBytesRead, bool bWait ) { DWORD dwBytesAvailable = 0; BOOL fSuccess = TRUE; if ( !bWait ) fSuccess = PeekNamedPipe( m_hReadPipe, NULL, 0, NULL, &dwBytesAvailable, NULL ); if ( fSuccess ) { if ( bWait || dwBytesAvailable ) return !!ReadFile( m_hReadPipe, lpBuffer, dwBytesToRead, lpBytesRead, NULL ); else { *lpBytesRead = 0; return true; } } return false; } //---------------------------------------------------------------------------- // Client pipe dtor //---------------------------------------------------------------------------- ClientPipe::ClientPipe( HANDLE hReadPipe, HANDLE hWritePipe ) : Pipe( hReadPipe, hWritePipe ) { } //---------------------------------------------------------------------------- // Client pipe creation //---------------------------------------------------------------------------- ClientPipe *ClientPipe::Create( LPCTSTR lpName ) { ClientPipe *pPipe = NULL; // Assume failure HANDLE hMapping = OpenPipeDataMapping( lpName ); if ( IsValidHandle(hMapping) ) { PipeData *pData = (PipeData*)MapViewOfFile( hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0 ); if ( pData ) { HANDLE hSourceProcess = OpenProcess( PROCESS_DUP_HANDLE, FALSE, pData->dwProcessId ); if ( IsValidHandle(hSourceProcess) ) { BOOL fSuccess; HANDLE hReadPipe = INVALID_HANDLE_VALUE, hWritePipe = INVALID_HANDLE_VALUE; fSuccess = DuplicateHandle( hSourceProcess, pData->hReadPipe, GetCurrentProcess(), &hReadPipe, 0, FALSE, DUPLICATE_SAME_ACCESS ); fSuccess = fSuccess && DuplicateHandle( hSourceProcess, pData->hWritePipe, GetCurrentProcess(), &hWritePipe, 0, FALSE, DUPLICATE_SAME_ACCESS ); if ( fSuccess ) pPipe = new ClientPipe( hReadPipe, hWritePipe ); if ( IsValidHandle(hWritePipe) ) CloseHandle( hWritePipe ); if ( IsValidHandle(hReadPipe) ) CloseHandle( hReadPipe ); HANDLE hConnectionRequest = CreatePipeConnectionSemaphore( lpName, 0, 1 ); ReleaseSemaphore( hConnectionRequest, 1, NULL ); CloseHandle( hConnectionRequest ); CloseHandle( hSourceProcess ); } UnmapViewOfFile( pData ); } CloseHandle( hMapping ); } return pPipe; } //---------------------------------------------------------------------------- // ServerPipe ctor //---------------------------------------------------------------------------- ServerPipe::ServerPipe( LPCTSTR lpName, HANDLE hMapping, HANDLE hSynchronize, HANDLE hReadPipe, HANDLE hWritePipe ) : Pipe( hReadPipe, hWritePipe ), m_hMapping( NULL ), m_hSynchronize( NULL ), m_lpName( NULL ) { DuplicateHandle( GetCurrentProcess(), hMapping, GetCurrentProcess(), &m_hMapping, 0, FALSE, DUPLICATE_SAME_ACCESS ); DuplicateHandle( GetCurrentProcess(), hSynchronize, GetCurrentProcess(), &m_hSynchronize, 0, FALSE, DUPLICATE_SAME_ACCESS ); m_lpName = new TCHAR[_tcslen(lpName) + 1]; if ( m_lpName ) _tcscpy( m_lpName, lpName ); } //---------------------------------------------------------------------------- // ServerPipe dtor //---------------------------------------------------------------------------- ServerPipe::~ServerPipe() { if ( IsValidHandle(m_hMapping) ) CloseHandle( m_hMapping ); if ( m_lpName ) delete[]m_lpName; } //---------------------------------------------------------------------------- // ServerPipe AcceptConnection //---------------------------------------------------------------------------- Pipe *ServerPipe::AcceptConnection() { Pipe *pPipe = NULL; // Assume failure; HANDLE hConnectionRequest = CreatePipeConnectionSemaphore( m_lpName, 0, 1 ); if ( WAIT_OBJECT_0 == WaitForSingleObject( hConnectionRequest, INFINITE ) ) { pPipe = new Pipe( *this ); Close(); // Create new inbound Pipe HANDLE hClientWritePipe = NULL, hServerReadPipe = NULL; BOOL fSuccess = CreatePipe( &hServerReadPipe, &hClientWritePipe, NULL, PIPE_BUFFER_SIZE ); if ( fSuccess ) { // Create outbound pipe HANDLE hClientReadPipe = NULL, hServerWritePipe = NULL; if ( CreatePipe( &hClientReadPipe, &hServerWritePipe, NULL, PIPE_BUFFER_SIZE ) ) { m_hReadPipe = hServerReadPipe; m_hWritePipe = hServerWritePipe; PipeData *pData = (PipeData *)MapViewOfFile( m_hMapping, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(PipeData) ); HANDLE hSynchronize = CreatePipeDataMutex( m_lpName, TRUE ); CloseHandle( pData->hReadPipe ); CloseHandle( pData->hWritePipe ); pData->hReadPipe = hClientReadPipe; pData->hWritePipe = hClientWritePipe; ReleaseMutex( hSynchronize ); CloseHandle( hSynchronize ); } else { CloseHandle( hClientWritePipe ); CloseHandle( hServerWritePipe ); } } ReleaseMutex( hConnectionRequest ); } CloseHandle( hConnectionRequest ); return pPipe; } //---------------------------------------------------------------------------- // Pipe creation //---------------------------------------------------------------------------- ServerPipe *ServerPipe::Create( LPCTSTR lpName ) { ServerPipe *pPipe = NULL; HANDLE hMapping = CreatePipeDataMapping( lpName ); if ( IsValidHandle(hMapping) ) { if ( ERROR_FILE_EXISTS != GetLastError() ) { HANDLE hSynchronize = CreatePipeDataMutex( lpName, FALSE); WaitForSingleObject( hSynchronize, INFINITE ); PipeData *pData = (PipeData*)MapViewOfFile( hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0 ); if ( pData ) { // Initialize pipe data pData->dwProcessId = 0; pData->hReadPipe = NULL; pData->hWritePipe = NULL; // Create inbound pipe HANDLE hServerReadPipe = NULL, hClientWritePipe = NULL; BOOL fSuccess = CreatePipe( &hServerReadPipe, &hClientWritePipe, NULL, PIPE_BUFFER_SIZE ); if ( fSuccess ) { // Create outbound pipe HANDLE hServerWritePipe = NULL, hClientReadPipe = NULL; fSuccess = CreatePipe( &hClientReadPipe, &hServerWritePipe, NULL, PIPE_BUFFER_SIZE ); if ( fSuccess ) { pData->dwProcessId = GetCurrentProcessId(); pData->hReadPipe = hClientReadPipe; pData->hWritePipe = hClientWritePipe; pPipe = new ServerPipe( lpName, hMapping, hSynchronize, hServerReadPipe, hServerWritePipe ); CloseHandle( hServerWritePipe ); CloseHandle( hServerReadPipe ); } else { CloseHandle( hServerReadPipe ); CloseHandle( hClientWritePipe ); } } UnmapViewOfFile( pData ); } ReleaseMutex( hSynchronize ); CloseHandle( hSynchronize ); } CloseHandle( hMapping ); } return pPipe; } //---------------------------------------------------------------------------- // C style API //---------------------------------------------------------------------------- const TCHAR LOCAL_PIPE_PREFIX[] = TEXT("\\\\.\\PIPE\\" ); extern "C" HANDLE WINAPI CreateSimplePipe( LPCTSTR lpName ) { int nPrefixLen = _tcslen( LOCAL_PIPE_PREFIX ); if ( 0 == _tcsnicmp( lpName, LOCAL_PIPE_PREFIX, nPrefixLen ) ) lpName += nPrefixLen; return (HANDLE)ServerPipe::Create( lpName ); } extern "C" HANDLE WINAPI OpenSimplePipe( LPCTSTR lpName ) { int nPrefixLen = _tcslen( LOCAL_PIPE_PREFIX ); if ( 0 == _tcsnicmp( lpName, LOCAL_PIPE_PREFIX, nPrefixLen ) ) lpName += nPrefixLen; return (HANDLE)ClientPipe::Create( lpName ); } extern "C" HANDLE WINAPI AcceptSimplePipeConnection( HANDLE hPipe ) { Pipe *pPipe = (Pipe *)hPipe; if ( pPipe->is() ) return (HANDLE)pPipe->AcceptConnection(); else { SetLastError( ERROR_INVALID_HANDLE ); return NULL; } } extern "C" BOOL WINAPI WaitForSimplePipe( LPCTSTR /*lpName*/, DWORD /*dwTimeOut*/ ) { return FALSE; } extern "C" BOOL WINAPI WriteSimplePipe( HANDLE hPipe, LPCVOID lpBuffer, DWORD dwBytesToWrite, LPDWORD lpBytesWritten, BOOL bWait ) { Pipe *pPipe = (Pipe *)hPipe; if ( pPipe->is() ) return pPipe->Write( lpBuffer, dwBytesToWrite, lpBytesWritten, bWait ); else { SetLastError( ERROR_INVALID_HANDLE ); return FALSE; } } extern "C" BOOL WINAPI ReadSimplePipe( HANDLE hPipe, LPVOID lpBuffer, DWORD dwBytesToRead, LPDWORD lpBytesRead, BOOL bWait ) { Pipe *pPipe = (Pipe *)hPipe; if ( pPipe->is() ) return pPipe->Read( lpBuffer, dwBytesToRead, lpBytesRead, bWait ); else { SetLastError( ERROR_INVALID_HANDLE ); return FALSE; } } extern "C" BOOL WINAPI CloseSimplePipe( HANDLE hPipe ) { Pipe *pPipe = (Pipe *)hPipe; if ( pPipe->is() ) { delete pPipe; return TRUE; } else { SetLastError( ERROR_INVALID_HANDLE ); return FALSE; } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */