// // SharedMemoryChannel.h // #pragma once #include #include "CCritSec.h" //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #define MACRO_INITPARAMS_DELIMITER_CHR '|' #define MACRO_INITPARAMS_DELIMITER_STR "|" typedef VOID ( __cdecl * PFNSMCRECV )( IN DWORD dwParam, IN LPVOID pvBuffer, IN LONG lBufferSize ); typedef VOID ( __cdecl * PFNSMCDISCONNECT )( IN DWORD dwParam ); class CSharedMemoryChannel { // // Construction. // private: VOID initInstanceData () { m_hFileMapping = NULL; m_dwFileMappingDim = 0; m_hDataAvailEvent = NULL; m_hDataReceivedEvent = NULL; m_pvMem = NULL; m_pfnRecv = NULL; m_pfnDisconnect = NULL; m_dwRecvParam = 0; m_bExit = FALSE; m_hThreadExited = NULL; m_hExitFromThread = NULL; m_dwSendTimeout = 1500; } public: CSharedMemoryChannel( DWORD dwFileMappingDim ) { initInstanceData (); m_dwFileMappingDim = dwFileMappingDim + sizeof( DWORD ); // Create the objects. SECURITY_ATTRIBUTES sa; ::memset( & sa, 0, sizeof( sa ) ); sa.bInheritHandle = TRUE; m_hDataAvailEvent = ::CreateEvent( & sa, FALSE, FALSE, NULL ); m_hDataReceivedEvent = ::CreateEvent( & sa, FALSE, FALSE, NULL ); m_hFileMapping = ::CreateFileMapping( INVALID_HANDLE_VALUE, & sa, PAGE_READWRITE, 0, m_dwFileMappingDim, NULL ); // return. return; } CSharedMemoryChannel( charstring& csInitParams ) { initInstanceData (); // Extract the data. INT pos1 = csInitParams.find( MACRO_INITPARAMS_DELIMITER_CHR ); if ( pos1 == charstring::npos ) return; INT pos2 = csInitParams.find( MACRO_INITPARAMS_DELIMITER_CHR, pos1 + 1 ); if ( pos2 == charstring::npos ) return; INT pos3 = csInitParams.find( MACRO_INITPARAMS_DELIMITER_CHR, pos2 + 1 ); if ( pos3 == charstring::npos ) return; m_hFileMapping = (HANDLE) ::strtoul( csInitParams.substr( 0, pos1 ).c_str (), NULL, 0x10 ); m_dwFileMappingDim = (DWORD) ::strtoul( csInitParams.substr( pos1 + 1, pos2 - pos1 - 1 ).c_str (), NULL, 0x10 ) + sizeof( DWORD ); m_hDataAvailEvent = (HANDLE) ::strtoul( csInitParams.substr( pos2 + 1, pos3 - pos2 - 1 ).c_str (), NULL, 0x10 ); m_hDataReceivedEvent = (HANDLE) ::strtoul( csInitParams.substr( pos3 + 1 ).c_str (), NULL, 0x10 ); // return. return; } virtual ~ CSharedMemoryChannel() { // Stop the thread. m_bExit = TRUE; if ( m_hExitFromThread ) ::SetEvent( m_hExitFromThread ); if ( m_hThreadExited ) { // wait. ::SetEvent( m_hDataAvailEvent ); WaitForSingleObject( m_hThreadExited, INFINITE ); // destroy. ::CloseHandle( m_hThreadExited ); m_hThreadExited = NULL; } // Close the handles. if ( m_hExitFromThread ) { ::CloseHandle( m_hExitFromThread ); m_hExitFromThread = NULL; } if ( m_hFileMapping ) { ::CloseHandle( m_hFileMapping ); m_hFileMapping = NULL; } if ( m_hDataAvailEvent ) { ::CloseHandle( m_hDataAvailEvent ); m_hDataAvailEvent = NULL; } if ( m_hDataReceivedEvent ) { ::CloseHandle( m_hDataReceivedEvent ); m_hDataReceivedEvent = NULL; } } // // Methods. // public: BOOL IsChannelOk () { CCriticalSection::scope __access__( m_cs ); // check the handles. if ( m_hFileMapping == NULL || m_dwFileMappingDim == 0 || m_hDataAvailEvent == NULL || m_hDataReceivedEvent == NULL ) return FALSE; else return TRUE; } BOOL GetSharedMemoryDataString( charstring& data ) { CCriticalSection::scope __access__( m_cs ); // return the data. data = ""; if ( IsChannelOk() == FALSE ) return FALSE; CHAR sz[ 256 ] = ""; ::sprintf( sz, "%X" MACRO_INITPARAMS_DELIMITER_STR "%X" MACRO_INITPARAMS_DELIMITER_STR "%X" MACRO_INITPARAMS_DELIMITER_STR "%X", (DWORD) m_hFileMapping, (DWORD) ( m_dwFileMappingDim - sizeof( DWORD ) ), (DWORD) m_hDataAvailEvent, (DWORD) m_hDataReceivedEvent ); data = sz; return TRUE; } BOOL WaitForPeerConnection ( DWORD dwMillisecs ) { // call the implementation. return SyncAtConnectionTime( dwMillisecs, FALSE ); } BOOL SetRecvFunction ( PFNSMCRECV pfnRecv, PFNSMCDISCONNECT pfnDisconnect, DWORD dwRecvParam ) { // first check on the handles. { CCriticalSection::scope __access__( m_cs ); if ( m_pfnRecv || m_pfnDisconnect ) return FALSE; if ( IsChannelOk () == FALSE ) return FALSE; } // call the implementation. if ( SyncAtConnectionTime( 1500, TRUE ) == FALSE ) return FALSE; // start the thread. { CCriticalSection::scope __access__( m_cs ); m_pfnRecv = pfnRecv; m_pfnDisconnect = pfnDisconnect; m_dwRecvParam = dwRecvParam; m_hExitFromThread = ::CreateEvent( NULL, FALSE, FALSE, NULL ); m_hThreadExited = ::CreateEvent( NULL, FALSE, FALSE, NULL ); DWORD dwThreadId = 0; HANDLE h = ::CreateThread( NULL, 0, & RecvFn, this, 0, & dwThreadId ); if ( h ) ::CloseHandle( h ); } // return success. return TRUE; } VOID SetSendTimeout( DWORD v ) { m_dwSendTimeout = v; } BOOL Send( LPVOID pvBuffer, LONG lBufferSize ) { if ( IsChannelOk () == FALSE ) return FALSE; // declare the vars. LONG max = (LONG) m_dwFileMappingDim - sizeof( DWORD ); LONG lStart = 0; // copy in the shared view and signal. do { // get the dim of the data to transfer. LONG dim; if ( lBufferSize > max ) dim = max; else dim = lBufferSize; // copy. DWORD* pdwView = (DWORD*) GetSharedMemoryPtr (); if ( pdwView == NULL ) return FALSE; * pdwView = dim; if ( pvBuffer ) ::memcpy( pdwView + 1, (BYTE*) pvBuffer + lStart, dim ); // signal the 1st event. { CCriticalSection::scope __access__( m_cs ); if ( m_hDataAvailEvent == NULL ) return FALSE; ::SetEvent( m_hDataAvailEvent ); } // wait for the second event. HANDLE ev = NULL; { CCriticalSection::scope __access__( m_cs ); if ( m_hDataReceivedEvent == NULL ) return FALSE; ev = m_hDataReceivedEvent; } DWORD dwWait = ::WaitForSingleObject( ev, m_dwSendTimeout ); if ( dwWait != WAIT_OBJECT_0 ) return FALSE; // update the var. lBufferSize -= dim; lStart += dim; } while ( lBufferSize > 0 ); // return. return TRUE; } // // Helpers. // public: static HANDLE StartProcess( charstring& csAppPath ) { // start the process. STARTUPINFO startupinfo; ::memset( & startupinfo, 0, sizeof( startupinfo ) ); startupinfo.cb = sizeof( startupinfo ); PROCESS_INFORMATION pi; BOOL start = ::CreateProcess( NULL, const_cast< CHAR* >( csAppPath.c_str() ), NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, & startupinfo, & pi ); if ( start ) { ::CloseHandle( pi.hThread ); } // return. return start ? pi.hProcess : NULL; } // // Implementation. // protected: BOOL SyncAtConnectionTime ( DWORD dwMillisecs, BOOL bDirection ) { // wait until the peer connects... HANDLE h1 = NULL; HANDLE h2 = NULL; { CCriticalSection::scope __access__( m_cs ); if ( IsChannelOk () == FALSE ) return FALSE; if ( bDirection == FALSE ) { h1 = m_hDataAvailEvent; h2 = m_hDataReceivedEvent; } else { h1 = m_hDataReceivedEvent; h2 = m_hDataAvailEvent; } } if ( bDirection != FALSE ) ::SetEvent( h2 ); DWORD dwWait = ::WaitForSingleObject( h1, dwMillisecs ); if ( dwWait != WAIT_OBJECT_0 ) return FALSE; if ( bDirection == FALSE ) ::SetEvent( h2 ); // return success. return TRUE; } LPVOID GetSharedMemoryPtr () { CCriticalSection::scope __access__( m_cs ); // return the ptr. if ( IsChannelOk () == FALSE ) return NULL; if ( m_pvMem ) return m_pvMem; m_pvMem = ::MapViewOfFile( m_hFileMapping, FILE_MAP_WRITE, 0, 0, m_dwFileMappingDim ); return m_pvMem; } static DWORD WINAPI RecvFn ( LPVOID parameter ) { CSharedMemoryChannel* pThis = (CSharedMemoryChannel*) parameter; BOOL bCallDisconnect = TRUE; // dispatch data. while( 1 ) { // wait for the data. DWORD dwWait = ::WaitForSingleObject( pThis->m_hDataAvailEvent, INFINITE ); if ( dwWait != WAIT_OBJECT_0 ) break; if ( pThis->m_bExit ) { bCallDisconnect = FALSE; // <- ### do not call the disconnect fn ! ### break; } // get the data. DWORD* pdwData = (DWORD*) pThis->GetSharedMemoryPtr (); if ( pdwData == NULL || (*pdwData) == 0 ) break; DWORD len = * pdwData; LPVOID pv = (LPVOID) ( ( (BYTE*) pdwData ) + sizeof( DWORD ) ); // call the function. PFNSMCRECV pfnRecv; DWORD dwRecvParam; { CCriticalSection::scope __access__( pThis->m_cs ); pfnRecv = pThis->m_pfnRecv; dwRecvParam = pThis->m_dwRecvParam; } if ( pfnRecv ) pfnRecv( dwRecvParam, pv, len ); // signal the end. ::SetEvent( pThis->m_hDataReceivedEvent ); } // call the disconnect fn. if ( bCallDisconnect ) { PFNSMCDISCONNECT pfnDisconnect; DWORD dwRecvParam; { CCriticalSection::scope __access__( pThis->m_cs ); pfnDisconnect = pThis->m_pfnDisconnect; dwRecvParam = pThis->m_dwRecvParam; } if ( pfnDisconnect ) pfnDisconnect( dwRecvParam ); } // inform that we are exiting... ::SetEvent( pThis->m_hThreadExited ); // return. return 0; } // // Data. // protected: HANDLE m_hFileMapping; DWORD m_dwFileMappingDim; HANDLE m_hDataAvailEvent; HANDLE m_hDataReceivedEvent; // // Implementation data. // CCriticalSection m_cs; LPVOID m_pvMem; PFNSMCRECV m_pfnRecv; PFNSMCDISCONNECT m_pfnDisconnect; DWORD m_dwRecvParam; BOOL m_bExit; HANDLE m_hExitFromThread; HANDLE m_hThreadExited; DWORD m_dwSendTimeout; }; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////