__declspec( align( 4 ) ) class CWmCopyDataPeer { public: // // Types. // typedef DWORD ( __cdecl * PFNRECV )( IN DWORD dwParam, IN BYTE* pbBuffer, IN LONG lBufferSize, IN HWND hwnd ); // // Construction / destruction. // CWmCopyDataPeer ( HINSTANCE hiModuleInstance, BOOL bIsClient, charstring& strChannelName, PFNRECV pfnRecvCallback, DWORD dwRecvCallbackParam ) : m_hiModuleInstance ( hiModuleInstance ), m_bIsClient( bIsClient ), m_strChannelName ( strChannelName ), m_pfnRecvCallback ( pfnRecvCallback ), m_dwRecvCallbackParam ( dwRecvCallbackParam ), m_bConnected( FALSE ), m_hThread( NULL ), m_hwndMessageWnd( NULL ), m_hwndTargetPeer( NULL ), m_bExitThread( FALSE ) { m_strTargetName = "Vpc:WmCopyDataPeer:" + m_strChannelName + ( ! bIsClient ? ":Client" : ":Server" ); m_strChannelName = "Vpc:WmCopyDataPeer:" + m_strChannelName + ( bIsClient ? ":Client" : ":Server" ); m_hAvailEvent = ::CreateEvent( NULL, TRUE, FALSE, NULL ); m_hThreadExitEv = ::CreateEvent( NULL, TRUE, FALSE, NULL ); } virtual ~ CWmCopyDataPeer () { Disconnect(); if ( m_hThread ) { ::CloseHandle( m_hThread ); m_hThread = NULL; } if ( m_hAvailEvent ) { ::CloseHandle( m_hAvailEvent ); m_hAvailEvent = NULL; } if ( m_hThreadExitEv ) { ::CloseHandle( m_hThreadExitEv ); m_hThreadExitEv = NULL; } if ( m_hwndMessageWnd ) { ::DestroyWindow( m_hwndMessageWnd ); m_hwndMessageWnd = NULL; } } // // Methods. // BOOL Connect () { if ( m_bConnected ) return FALSE; ::ResetEvent( m_hAvailEvent ); ::ResetEvent( m_hThreadExitEv ); m_bExitThread = FALSE; DWORD dwThreadID = 0; m_hThread = ::CreateThread( NULL, 0, & WmCopyDataReceiveThread, this, 0, & dwThreadID ); if ( m_hThread ) { ::WaitForSingleObject( m_hAvailEvent, INFINITE ); } return m_bConnected; } void Disconnect () { if ( m_bConnected == FALSE ) return; m_bConnected = FALSE; m_bExitThread = TRUE; if ( ::IsWindow( m_hwndMessageWnd ) ) { ::PostMessage( m_hwndMessageWnd, WM_USER, 0, 0 ); ::WaitForSingleObject( m_hThreadExitEv, MACRO_CBDEADLOCKPREV_TIMEOUT ); } if ( m_hwndMessageWnd ) { if ( ::IsWindow( m_hwndMessageWnd ) ) ::DestroyWindow( m_hwndMessageWnd ); m_hwndMessageWnd = NULL; } if ( m_hThread ) { ::CloseHandle( m_hThread ); m_hThread = NULL; } } int Send ( CONST BYTE* pbBuffer, int nBufferSize, HWND hwnd = NULL, DWORD* pdwRetVal = NULL ) { if ( pdwRetVal ) * pdwRetVal = 0; if ( m_bConnected == FALSE ) return 0; else if ( m_bIsClient == FALSE && hwnd == NULL ) return 0; if ( hwnd == NULL ) hwnd = m_hwndTargetPeer; if ( hwnd == NULL || IsWindowPeer( hwnd ) == FALSE ) return 0; COPYDATASTRUCT copydata; ::memset( & copydata, 0, sizeof( copydata ) ); copydata.cbData = nBufferSize; copydata.lpData = (PVOID) pbBuffer; LRESULT lResult = 0; LRESULT lSmtResult = ::SendMessageTimeout( hwnd, WM_COPYDATA, (WPARAM) m_hwndMessageWnd, (LPARAM) & copydata, SMTO_NORMAL, #ifdef _DEBUG 3 * 60 * 60 * 1000, // 3 hours... #else 30 * 1000, #endif (PDWORD_PTR) & lResult ); if ( lSmtResult == 0 ) return 0; else if ( lResult ) { if ( pdwRetVal ) * pdwRetVal |= (DWORD) lResult; return nBufferSize; } else return 0; } DWORD Broadcast( CONST BYTE* pbBuffer, int nBufferSize ) { if ( m_bIsClient ) return 0; SEwCbBroadcastFnData data; data.pThis = this; data.pbBuffer = (BYTE*) pbBuffer; data.nBufferSize = nBufferSize; data.dwRetVal = 0; ::EnumWindows( & EwCbBroadcast, (LPARAM) & data ); return data.dwRetVal; } BOOL FindPeer ( BOOL force = FALSE ) { if ( m_bIsClient == FALSE ) return FALSE; else if ( force == FALSE && m_hwndTargetPeer ) return TRUE; ::EnumWindows( & EwCbSearchForSinglePeer, (LPARAM) this ); return m_hwndTargetPeer != NULL; } protected: // // Implementation. // static DWORD WINAPI WmCopyDataReceiveThread( LPVOID lpParameter ) { CWmCopyDataPeer* pThis = (CWmCopyDataPeer*) lpParameter; // Register and create the window. WNDCLASS wcWndClass; wcWndClass.style = CS_HREDRAW | CS_VREDRAW; wcWndClass.lpfnWndProc = & WindowProc; wcWndClass.cbClsExtra = 0; wcWndClass.cbWndExtra = 0; wcWndClass.hInstance = pThis->m_hiModuleInstance; wcWndClass.hIcon = NULL; wcWndClass.hCursor = NULL; wcWndClass.hbrBackground = NULL; wcWndClass.lpszMenuName = NULL; wcWndClass.lpszClassName = pThis->m_strChannelName.c_str(); ::RegisterClass( & wcWndClass ); pThis->m_hwndMessageWnd = ::CreateWindow( pThis->m_strChannelName.c_str(), pThis->m_strChannelName.c_str(), WS_POPUP, 0, 0, 0, 0, NULL, NULL, pThis->m_hiModuleInstance, NULL ); if ( pThis->m_hwndMessageWnd ) { ::SetWindowLong( pThis->m_hwndMessageWnd, GWL_USERDATA, (LONG) pThis ); // Detect the other end (if this is the client end...). if ( pThis->m_bIsClient ) pThis->FindPeer(); // Connected. pThis->m_bConnected = TRUE; ::SetEvent( pThis->m_hAvailEvent ); // Message loop. MSG msg; while( ::GetMessage( & msg, NULL, 0, 0 ) ) { if ( pThis->m_bExitThread ) break; ::TranslateMessage( & msg ); ::DispatchMessage( & msg ); } } ::SetEvent( pThis->m_hAvailEvent ); ::SetEvent( pThis->m_hThreadExitEv ); return 0; } static LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { CWmCopyDataPeer* pThis = (CWmCopyDataPeer*) ::GetWindowLong( hwnd, GWL_USERDATA ); if ( pThis == NULL || pThis->m_bConnected == FALSE ) return ::DefWindowProc( hwnd, uMsg, wParam, lParam ); if ( uMsg == WM_COPYDATA ) { // client only check. if ( pThis->m_bIsClient ) { pThis->FindPeer( pThis->m_hwndTargetPeer != (HWND) wParam ); if ( pThis->m_hwndTargetPeer == NULL || pThis->m_hwndTargetPeer != (HWND) wParam ) return FALSE; } else // server only check { if ( pThis->IsWindowPeer( (HWND) wParam ) == FALSE ) return FALSE; } // process data. PCOPYDATASTRUCT pCopyData = (PCOPYDATASTRUCT) lParam; if ( pCopyData->cbData && pCopyData->lpData ) { DWORD retval = pThis->m_pfnRecvCallback( pThis->m_dwRecvCallbackParam, (BYTE*) pCopyData->lpData, pCopyData->cbData, (HWND) wParam ); return retval | 1; } return 0; } else { return ::DefWindowProc( hwnd, uMsg, wParam, lParam ); } } static BOOL CALLBACK EwCbSearchForSinglePeer( HWND hwnd, LPARAM lParam ) { CWmCopyDataPeer* pThis = (CWmCopyDataPeer*) lParam; if ( pThis->IsWindowPeer( hwnd ) ) { pThis->m_hwndTargetPeer = hwnd; return FALSE; } else { return TRUE; } } BOOL IsWindowPeer( IN HWND hwnd ) { char szClassName[ MAX_PATH ]; if ( ::GetClassName( hwnd, szClassName, sizeof( szClassName ) ) ) { if ( ::strcmp( szClassName, m_strTargetName.c_str() ) == 0 ) { return TRUE; } } return FALSE; } struct SEwCbBroadcastFnData { CWmCopyDataPeer* pThis; BYTE* pbBuffer; int nBufferSize; DWORD dwRetVal; }; static BOOL CALLBACK EwCbBroadcast( HWND hwnd, LPARAM lParam ) { SEwCbBroadcastFnData* pData = (SEwCbBroadcastFnData*) lParam; DWORD retval = 0; if ( pData->pThis->IsWindowPeer( hwnd ) ) pData->pThis->Send( pData->pbBuffer, pData->nBufferSize, hwnd, & retval ); pData->dwRetVal |= retval; return TRUE; } // // Data. // __declspec( align( 4 ) ) BOOL m_bConnected; HWND m_hwndTargetPeer; BOOL m_bExitThread; HANDLE m_hAvailEvent; HWND m_hwndMessageWnd; HANDLE m_hThread; HANDLE m_hThreadExitEv; charstring m_strTargetName; HINSTANCE m_hiModuleInstance; BOOL m_bIsClient; charstring m_strChannelName; PFNRECV m_pfnRecvCallback; DWORD m_dwRecvCallbackParam; };