// // CNamedPipeHost // __declspec( align( 4 ) ) class CNamedPipeHost { public: // // Types. // typedef VOID ( __cdecl * PFNRECV )( IN DWORD dwParam, IN BYTE* pbBuffer, IN LONG lBufferSize ); typedef VOID ( __cdecl * PFNCLOSE )( IN DWORD dwParam ); // // Construction / destruction. // CNamedPipeHost( charstring& strPipeName, PFNRECV pfnRecvCallback, int iRecvBufferSize, DWORD dwRecvCallbackParam, PFNCLOSE pfnCloseCallback = NULL ) : m_strPipeName( strPipeName ), m_pfnRecvCallback( pfnRecvCallback ), m_iRecvBufferSize( iRecvBufferSize ), m_dwRecvCallbackParam( dwRecvCallbackParam ), m_pfnCloseCallback( pfnCloseCallback ), m_hClientConnWaitThread( NULL ), m_bConnected( FALSE ), m_hPipe( INVALID_HANDLE_VALUE ) { m_strPipeName = "\\\\.\\pipe\\" + m_strPipeName; m_hListenThreadExitEv = ::CreateEvent( NULL, TRUE, FALSE, NULL ); m_hAvailEvent = ::CreateEvent( NULL, TRUE, FALSE, NULL ); m_hExitServer = ::CreateEvent( NULL, TRUE, FALSE, NULL ); m_hConnectEvent = ::CreateEvent( NULL, TRUE, TRUE, NULL ); m_hReadEvent = ::CreateEvent( NULL, TRUE, TRUE, NULL ); m_hWriteEvent = ::CreateEvent( NULL, TRUE, TRUE, NULL ); } virtual ~ CNamedPipeHost () { if ( m_hClientConnWaitThread ) // <-- These must be the first instructions of the destructor! { ::SetEvent( m_hExitServer ); ::WaitForSingleObject( m_hListenThreadExitEv, MACRO_CBDEADLOCKPREV_TIMEOUT ); ::CloseHandle( m_hClientConnWaitThread ); m_hClientConnWaitThread = NULL; } if ( m_hListenThreadExitEv ) { ::CloseHandle( m_hListenThreadExitEv ); m_hListenThreadExitEv = NULL; } if ( m_hAvailEvent ) { ::CloseHandle( m_hAvailEvent ); m_hAvailEvent = NULL; } if ( m_hExitServer ) { ::CloseHandle( m_hExitServer ); m_hExitServer = NULL; } if ( m_hConnectEvent ) { ::CloseHandle( m_hConnectEvent ); m_hConnectEvent = NULL; } if ( m_hReadEvent ) { ::CloseHandle( m_hReadEvent ); m_hReadEvent = NULL; } if ( m_hWriteEvent ) { ::CloseHandle( m_hWriteEvent ); m_hWriteEvent = NULL; } if ( m_hPipe != INVALID_HANDLE_VALUE ) { ::CloseHandle( m_hPipe ); m_hPipe = INVALID_HANDLE_VALUE; } return; } // // Methods. // BOOL StartListening () { DWORD dwThreadID = 0; m_hClientConnWaitThread = ::CreateThread( NULL, 0, & ClientConnWaitThread, this, 0, & dwThreadID ); if ( m_hClientConnWaitThread == NULL ) return FALSE; ::WaitForSingleObject( m_hAvailEvent, INFINITE ); return TRUE; } int Send ( CONST BYTE* pbBuffer, int nBufferSize ) { if ( m_bConnected == FALSE ) return 0; OVERLAPPED oOverlap; ::memset( & oOverlap, 0, sizeof( oOverlap ) ); oOverlap.hEvent = m_hWriteEvent; DWORD cbWritten = 0; BOOL fSuccess = WriteFile( m_hPipe, // handle to pipe pbBuffer, // buffer to write from nBufferSize, // number of bytes to write & cbWritten, // number of bytes written & oOverlap ); // overlapped I/O if ( fSuccess && cbWritten == nBufferSize ) return cbWritten; if ( ! fSuccess && ( ::GetLastError() == ERROR_IO_PENDING ) ) { HANDLE vhHandles[ 2 ] = { m_hExitServer, m_hWriteEvent }; DWORD dwRes =::WaitForMultipleObjects( 2, vhHandles, FALSE, INFINITE ); if ( dwRes != WAIT_OBJECT_0 + 1 ) { ::CancelIo( m_hPipe ); return 0; } else { DWORD cbBytes = 0; BOOL fResult = ::GetOverlappedResult( m_hPipe, // handle to pipe & oOverlap, // OVERLAPPED structure & cbBytes, // bytes transferred FALSE ); // do not wait if ( ! fResult || cbBytes != nBufferSize ) { return 0; } else { return cbBytes; } } } return cbWritten; } VOID DisconnectClient () { if ( m_bConnected == FALSE ) return; ::FlushFileBuffers( m_hPipe ); ::DisconnectNamedPipe( m_hPipe ); } protected: // // Implementation. // static DWORD WINAPI ClientConnWaitThread( LPVOID lpParameter ) { CNamedPipeHost* pThis = (CNamedPipeHost*) lpParameter; pThis->m_hPipe = CreateNamedPipe( pThis->m_strPipeName.c_str(), // pipe name PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, // read/write access + OVERLAPPED PIPE_TYPE_MESSAGE | // message type pipe PIPE_READMODE_MESSAGE | // message-read mode PIPE_WAIT, // blocking mode PIPE_UNLIMITED_INSTANCES, // max. instances pThis->m_iRecvBufferSize, // output buffer size pThis->m_iRecvBufferSize, // input buffer size 0, // client time-out NULL ); // no security attribute if ( pThis->m_hPipe != INVALID_HANDLE_VALUE ) { BOOL bTryReconnect; do { bTryReconnect = FALSE; OVERLAPPED oConnectOverlap; ::memset( & oConnectOverlap, 0, sizeof( oConnectOverlap ) ); oConnectOverlap.hEvent = pThis->m_hConnectEvent; BOOL bConnectRes = ::ConnectNamedPipe( pThis->m_hPipe, & oConnectOverlap ); if ( bConnectRes ) { pThis->m_bConnected = FALSE; } else { ::SetEvent( pThis->m_hAvailEvent ); switch( ::GetLastError() ) { case ERROR_IO_PENDING: { HANDLE vhHandles[ 2 ] = { pThis->m_hExitServer, pThis->m_hConnectEvent }; DWORD dwRes = ::WaitForMultipleObjects( 2, vhHandles, FALSE, INFINITE ); if ( dwRes != WAIT_OBJECT_0 + 1 ) { ::CancelIo( pThis->m_hPipe ); pThis->m_bConnected = FALSE; } else { DWORD cbBytes = 0; BOOL fResult = ::GetOverlappedResult( pThis->m_hPipe, // handle to pipe & oConnectOverlap, // OVERLAPPED structure & cbBytes, // bytes transferred FALSE ); // do not wait if ( ! fResult ) { pThis->m_bConnected = FALSE; } else { pThis->m_bConnected = TRUE; } } } break; case ERROR_PIPE_CONNECTED: { pThis->m_bConnected = TRUE; } break; default: { pThis->m_bConnected = FALSE; } break; } } if ( pThis->m_bConnected ) { BYTE* pbBuffer = (BYTE*) ::malloc( pThis->m_iRecvBufferSize ); if ( pbBuffer ) { while( TRUE ) { OVERLAPPED oReadOverlap; ::memset( & oReadOverlap, 0, sizeof( oReadOverlap ) ); oReadOverlap.hEvent = pThis->m_hReadEvent; DWORD cbBytesRead = 0; BOOL fSuccess = ReadFile( pThis->m_hPipe, // handle to pipe pbBuffer, // buffer to receive data pThis->m_iRecvBufferSize, // size of buffer & cbBytesRead, // number of bytes read & oReadOverlap ); // overlapped I/O if ( fSuccess && cbBytesRead != 0 ) { pThis->m_pfnRecvCallback( pThis->m_dwRecvCallbackParam, pbBuffer, cbBytesRead ); } else if ( ! fSuccess && ( ::GetLastError() == ERROR_IO_PENDING ) ) { HANDLE vhHandles[ 2 ] = { pThis->m_hExitServer, pThis->m_hReadEvent }; DWORD dwRes = ::WaitForMultipleObjects( 2, vhHandles, FALSE, INFINITE ); if ( dwRes != WAIT_OBJECT_0 + 1 ) { ::CancelIo( pThis->m_hPipe ); bTryReconnect = FALSE; break; } else { DWORD cbBytes = 0; BOOL fResult = ::GetOverlappedResult( pThis->m_hPipe, // handle to pipe & oReadOverlap, // OVERLAPPED structure & cbBytes, // bytes transferred FALSE ); // do not wait if ( ! fResult || cbBytes == 0 ) { bTryReconnect = TRUE; break; } else { pThis->m_pfnRecvCallback( pThis->m_dwRecvCallbackParam, pbBuffer, cbBytes ); } } } else { bTryReconnect = TRUE; break; } } ::free( pbBuffer ); } pThis->m_bConnected = FALSE; ::FlushFileBuffers( pThis->m_hPipe ); ::DisconnectNamedPipe( pThis->m_hPipe ); if ( pThis->m_pfnCloseCallback ) pThis->m_pfnCloseCallback( pThis->m_dwRecvCallbackParam ); } } while( bTryReconnect ); } ::SetEvent( pThis->m_hAvailEvent ); ::SetEvent( pThis->m_hListenThreadExitEv ); return 0; } // // Data. // __declspec( align( 4 ) ) BOOL m_bConnected; HANDLE m_hPipe; HANDLE m_hClientConnWaitThread; HANDLE m_hListenThreadExitEv; HANDLE m_hAvailEvent; HANDLE m_hExitServer; HANDLE m_hConnectEvent; HANDLE m_hReadEvent; HANDLE m_hWriteEvent; charstring m_strPipeName; PFNRECV m_pfnRecvCallback; int m_iRecvBufferSize; DWORD m_dwRecvCallbackParam; PFNCLOSE m_pfnCloseCallback; }; // // CNamedPipeClient // __declspec( align( 4 ) ) class CNamedPipeClient { public: // // Types. // typedef VOID ( __cdecl * PFNRECV )( IN DWORD dwParam, IN BYTE* pbBuffer, IN LONG lBufferSize ); typedef VOID ( __cdecl * PFNCLOSE )( IN DWORD dwParam ); // // Construction / destruction. // CNamedPipeClient ( charstring& strPipeName, PFNRECV pfnRecvCallback, int iRecvBufferSize, DWORD dwRecvCallbackParam, PFNCLOSE pfnCloseCallback = NULL ) : m_strPipeName ( strPipeName ), m_pfnRecvCallback ( pfnRecvCallback ), m_iRecvBufferSize ( iRecvBufferSize ), m_dwRecvCallbackParam ( dwRecvCallbackParam ), m_pfnCloseCallback( pfnCloseCallback ), m_hPipe( INVALID_HANDLE_VALUE ), m_bConnected( FALSE ), m_hRecvThread( NULL ) { m_strPipeName = "\\\\.\\pipe\\" + m_strPipeName; m_hRecvThreadExitEv = ::CreateEvent( NULL, TRUE, FALSE, NULL ); m_hAvailEvent = ::CreateEvent( NULL, TRUE, FALSE, NULL ); m_hExitClient = ::CreateEvent( NULL, TRUE, FALSE, NULL ); m_hReadEvent = ::CreateEvent( NULL, TRUE, TRUE, NULL ); m_hWriteEvent = ::CreateEvent( NULL, TRUE, TRUE, NULL ); } virtual ~ CNamedPipeClient () { Disconnect(); if ( m_hPipe != INVALID_HANDLE_VALUE ) { ::CloseHandle( m_hPipe ); m_hPipe = INVALID_HANDLE_VALUE; } if ( m_hRecvThread ) { ::CloseHandle( m_hRecvThread ); m_hRecvThread = NULL; } if ( m_hAvailEvent ) { ::CloseHandle( m_hAvailEvent ); m_hAvailEvent = NULL; } if ( m_hExitClient ) { ::CloseHandle( m_hExitClient ); m_hExitClient = NULL; } if ( m_hReadEvent ) { ::CloseHandle( m_hReadEvent ); m_hReadEvent = NULL; } if ( m_hWriteEvent ) { ::CloseHandle( m_hWriteEvent ); m_hWriteEvent = NULL; } if ( m_hRecvThreadExitEv ) { ::CloseHandle( m_hRecvThreadExitEv ); m_hRecvThreadExitEv = NULL; } } // // Methods. // BOOL Connect () { if ( m_bConnected ) return FALSE; ::ResetEvent( m_hRecvThreadExitEv ); ::ResetEvent( m_hAvailEvent ); ::ResetEvent( m_hExitClient ); m_hPipe = ::CreateFile( m_strPipeName.c_str(), // pipe name GENERIC_READ | // read and write access GENERIC_WRITE, 0, // no sharing NULL, // no security attributes OPEN_EXISTING, // opens existing pipe FILE_FLAG_OVERLAPPED, // overlapped i/o NULL); // no template file if ( m_hPipe != INVALID_HANDLE_VALUE ) { DWORD dwMode = PIPE_READMODE_MESSAGE; BOOL fSuccess = SetNamedPipeHandleState( m_hPipe, // pipe handle & dwMode, // new pipe mode NULL, // don't set maximum bytes NULL ); // don't set maximum time if ( fSuccess ) { DWORD dwThreadId = 0; m_hRecvThread = ::CreateThread( NULL, 0, & PipeReceiveThread, this, 0, & dwThreadId ); if ( m_hRecvThread ) { m_bConnected = TRUE; ::WaitForSingleObject( m_hAvailEvent, INFINITE ); } } } return m_bConnected; } void Disconnect ( BOOL reserved = TRUE ) { if ( m_bConnected ) { ::FlushFileBuffers( m_hPipe ); m_bConnected = FALSE; ::SetEvent( m_hExitClient ); if ( reserved ) ::WaitForSingleObject( m_hRecvThreadExitEv, MACRO_CBDEADLOCKPREV_TIMEOUT ); if ( m_hPipe != INVALID_HANDLE_VALUE ) { HANDLE h = m_hPipe; m_hPipe = INVALID_HANDLE_VALUE; ::CloseHandle( h ); } if ( m_hRecvThread ) { ::CloseHandle( m_hRecvThread ); m_hRecvThread = NULL; } } } VOID SetRecvCallbackParam( DWORD dwRecvCallbackParam ) { // Set. m_dwRecvCallbackParam = dwRecvCallbackParam; } int Send ( CONST BYTE* pbBuffer, int nBufferSize ) { if ( m_bConnected == FALSE ) return 0; OVERLAPPED oOverlap; ::memset( & oOverlap, 0, sizeof( oOverlap ) ); oOverlap.hEvent = m_hWriteEvent; DWORD cbWritten = 0; BOOL fSuccess = WriteFile( m_hPipe, // handle to pipe pbBuffer, // buffer to write from nBufferSize, // number of bytes to write & cbWritten, // number of bytes written & oOverlap ); // overlapped I/O if ( fSuccess && cbWritten == nBufferSize ) return cbWritten; if ( ! fSuccess && ( ::GetLastError() == ERROR_IO_PENDING ) ) { HANDLE vhHandles[ 2 ] = { m_hExitClient, m_hWriteEvent }; DWORD dwRes =::WaitForMultipleObjects( 2, vhHandles, FALSE, INFINITE ); if ( dwRes != WAIT_OBJECT_0 + 1 ) { ::CancelIo( m_hPipe ); return 0; } else { DWORD cbBytes = 0; BOOL fResult = ::GetOverlappedResult( m_hPipe, // handle to pipe & oOverlap, // OVERLAPPED structure & cbBytes, // bytes transferred FALSE ); // do not wait if ( ! fResult || cbBytes != nBufferSize ) { return 0; } else { return cbBytes; } } } return cbWritten; } BOOL IsConnected () { return m_bConnected; } protected: // // Implementation. // static DWORD WINAPI PipeReceiveThread( LPVOID lpParameter ) { CNamedPipeClient* pThis = (CNamedPipeClient*) lpParameter; BYTE* pbBuffer = (BYTE*) ::malloc( pThis->m_iRecvBufferSize ); if ( pbBuffer ) { while( TRUE ) { OVERLAPPED oReadOverlap; ::memset( & oReadOverlap, 0, sizeof( oReadOverlap ) ); oReadOverlap.hEvent = pThis->m_hReadEvent; DWORD cbBytesRead = 0; BOOL fSuccess = ::ReadFile( pThis->m_hPipe, // pipe handle pbBuffer, // buffer to receive reply pThis->m_iRecvBufferSize, // size of buffer & cbBytesRead, // number of bytes read & oReadOverlap ); // overlapped ::SetEvent( pThis->m_hAvailEvent ); if ( fSuccess && cbBytesRead != 0 ) { pThis->m_pfnRecvCallback( pThis->m_dwRecvCallbackParam, pbBuffer, cbBytesRead ); } else if ( ! fSuccess && ( ::GetLastError() == ERROR_IO_PENDING ) ) { HANDLE vhHandles[ 2 ] = { pThis->m_hExitClient, pThis->m_hReadEvent }; DWORD dwRes = ::WaitForMultipleObjects( 2, vhHandles, FALSE, INFINITE ); if ( dwRes != WAIT_OBJECT_0 + 1 ) { ::CancelIo( pThis->m_hPipe ); break; } else { DWORD cbBytes = 0; BOOL fResult = ::GetOverlappedResult( pThis->m_hPipe, // handle to pipe & oReadOverlap, // OVERLAPPED structure & cbBytes, // bytes transferred FALSE ); // do not wait if ( ! fResult || cbBytes == 0 ) { break; } else { pThis->m_pfnRecvCallback( pThis->m_dwRecvCallbackParam, pbBuffer, cbBytes ); } } } else { break; } } ::free( pbBuffer ); } pThis->Disconnect( FALSE ); if ( pThis->m_pfnCloseCallback ) pThis->m_pfnCloseCallback( pThis->m_dwRecvCallbackParam ); ::SetEvent( pThis->m_hAvailEvent ); ::SetEvent( pThis->m_hRecvThreadExitEv ); return 0; } // // Data. // __declspec( align( 4 ) ) HANDLE m_hPipe; BOOL m_bConnected; HANDLE m_hRecvThread; HANDLE m_hRecvThreadExitEv; HANDLE m_hAvailEvent; HANDLE m_hExitClient; HANDLE m_hReadEvent; HANDLE m_hWriteEvent; charstring m_strPipeName; PFNRECV m_pfnRecvCallback; int m_iRecvBufferSize; DWORD m_dwRecvCallbackParam; PFNCLOSE m_pfnCloseCallback; };