/** * @file XTPNotifyConnection.cpp * * @copyright * (c) 1998-2025 Codejock Software, All Rights Reserved. * * This source file is the property of Codejock Software and must not be * redistributed by any means without the explicit written permission of * Codejock Software. * * The use of this source code is governed by the terms and conditions specified * in the Toolkit Pro license agreement. Codejock Software grants you, as a * single software developer, the limited right to use this software on one * computer only. * * Contact Information: * support@codejock.com * http://www.codejock.com * */ #include "stdafx.h" #include "Common/XTPFramework.h" #include "Common/XTPNotifyConnection.h" #include "Common/Base/Diagnostic/XTPDisableNoisyWarnings.h" #ifdef _DEBUG # define new DEBUG_NEW # undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #pragma warning(disable : 4571) // warning C4571: catch(...) blocks compiled with /EHs do not catch // or re-throw Structured Exceptions ///////////////////////////////////////////////////////////////////////////// static LPCTSTR XTP_NOTIFICATION_SINK_MT_MSGWND_NAME = _T("XTPNotificationSinkMT_MsgWnd"); ///////////////////////////////////////////////////////////////////////////// // class CEmptySyncObject : public CSyncObject CXTPNotifyConnection::CEmptySyncObject::CEmptySyncObject() : CSyncObject(_T("XTPEmptySyncObject")) { } //////////////////////////////////////////////////////////////////////////// CXTPNotifyConnection::CXTPNotifyConnection() { m_nSendQueueCacheSize = 0; } CXTPNotifyConnection::~CXTPNotifyConnection() { RemoveAll(); } void CXTPNotifyConnection::RemoveAll() { CSingleLock singleLock(GetDataLock(), TRUE); int nCount = (int)m_arrConnections.GetSize(); for (int i = 0; i < nCount; i++) { CONNECTION_DESCRIPTOR* pCurElem = m_arrConnections[i]; _ASSERTE(pCurElem); if (pCurElem) { delete pCurElem; } m_arrConnections[i] = NULL; } m_arrConnections.RemoveAll(); } XTP_CONNECTION_ID CXTPNotifyConnection::Advise(XTP_NOTIFY_CODE Event, CXTPNotifySinkBase* pSink) { _ASSERTE(pSink); CSingleLock singleLock(GetDataLock(), TRUE); CONNECTION_DESCRIPTOR* pNewElem = new CONNECTION_DESCRIPTOR; if (!pNewElem) { return 0; } pNewElem->dwConnectionID = (XTP_CONNECTION_ID)pNewElem; pNewElem->dwNotifyCode = Event; pNewElem->pSink = pSink; m_arrConnections.Add(pNewElem); return pNewElem->dwConnectionID; } void CXTPNotifyConnection::Unadvise(XTP_CONNECTION_ID ConnectionID) { CSingleLock singleLock(GetDataLock(), TRUE); try { int nCount = (int)m_arrConnections.GetSize(); int nFIndex = FindConnection(ConnectionID); if (nFIndex >= 0 && nFIndex < nCount) { CONNECTION_DESCRIPTOR* pElem = m_arrConnections[nFIndex]; _ASSERTE(pElem); if (pElem) { delete pElem; } m_arrConnections.RemoveAt(nFIndex); } else { _ASSERTE(FALSE); } } catch (...) { _ASSERTE(FALSE); TRACE(_T("EXCEPTION! CXTPNotifyConnection::Unadvise(ConnectionID = %d)\n"), ConnectionID); } } BOOL CXTPNotifyConnection::SendEvent(XTP_NOTIFY_CODE Event, WPARAM wParam, LPARAM lParam, DWORD dwFlags) { CSingleLock singleLock(GetDataLock(), TRUE); InternalAddRef(); int nCount = (int)m_arrConnections.GetSize(); if (m_arrSendQueueCache.GetSize() < nCount + m_nSendQueueCacheSize) { m_arrSendQueueCache.SetSize(nCount + m_nSendQueueCacheSize); } //****************************************************************************** int nFirstLocalClientIndex = m_nSendQueueCacheSize; int i; for (i = 0; i < nCount; i++) { CONNECTION_DESCRIPTOR* pElem = m_arrConnections[i]; _ASSERTE(pElem); if (pElem && pElem->dwNotifyCode == Event) { m_arrSendQueueCache.SetAt(m_nSendQueueCacheSize, *pElem); m_nSendQueueCacheSize++; } } int nLastLocalClientIndex = m_nSendQueueCacheSize - 1; singleLock.Unlock(); //************************************************************************ for (i = nFirstLocalClientIndex; i <= nLastLocalClientIndex; i++) { singleLock.Lock(); if (i >= m_arrSendQueueCache.GetSize()) { _ASSERTE(FALSE); return FALSE; } CONNECTION_DESCRIPTOR& rElem = m_arrSendQueueCache.ElementAt(i); int nFIndex = FindConnection(rElem.dwConnectionID); if (nFIndex < 0) { // Unadvise was called inside OnEvent(...) Call continue; } try { CXTPNotifySinkBase* ptrSink = rElem.pSink; singleLock.Unlock(); if (ptrSink) { ptrSink->OnEvent(Event, wParam, lParam, dwFlags); } else { _ASSERTE(FALSE); TRACE(_T("CXTPNotifyConnection::SendEvent(Event = %d, wParam = %d, lParam = %d, ") _T("dwFlags = %x) pSink = %x\n"), Event, wParam, lParam, dwFlags, rElem.pSink); } } catch (...) { _ASSERTE(FALSE); TRACE(_T("EXCEPTION! CXTPNotifyConnection::SendEvent(Event = %d, wParam = %d, lParam ") _T("= %d, dwFlags = %x)\n"), Event, wParam, lParam, dwFlags); } } //============================================================================== singleLock.Lock(); m_nSendQueueCacheSize = nFirstLocalClientIndex; singleLock.Unlock(); InternalRelease(); return (nFirstLocalClientIndex <= nLastLocalClientIndex); } int CXTPNotifyConnection::FindConnection(XTP_CONNECTION_ID ConnectionID) { CSingleLock singleLock(GetDataLock(), TRUE); int nCount = (int)m_arrConnections.GetSize(); for (int i = 0; i < nCount; i++) { CONNECTION_DESCRIPTOR* pCurElem = m_arrConnections[i]; _ASSERTE(pCurElem); if (pCurElem) { if (pCurElem->dwConnectionID == ConnectionID) { return i; } } } return -1; } //////////////////////////////////////////////////////////////////////////// // class CXTPNotifyConnectionMT : public CXTPNotifyConnection CXTPNotifyConnectionMT::CXTPNotifyConnectionMT() { } CXTPNotifyConnectionMT::~CXTPNotifyConnectionMT() { } //////////////////////////////////////////////////////////////////////////// #include "Common/Base/Diagnostic/XTPBeginAfxMap.h" BEGIN_MESSAGE_MAP(CXTPNotifySinkImplMTMsgWnd, CWnd) ON_REGISTERED_MESSAGE(xtp_wm_NotificationSinkMTOnEvent, HandleInterThreadEvent) END_MESSAGE_MAP() #include "Common/Base/Diagnostic/XTPEndAfxMap.h" CXTPNotifySinkImplMTMsgWnd::CXTPNotifySinkImplMTMsgWnd() { } CXTPNotifySinkImplMTMsgWnd::~CXTPNotifySinkImplMTMsgWnd() { } LRESULT CXTPNotifySinkImplMTMsgWnd::HandleInterThreadEvent(WPARAM wParam, LPARAM lParam) { return OnInterThreadEvent(wParam, lParam); } BOOL CXTPNotifySinkImplMTMsgWnd::CreateWnd() { LPCTSTR pcszSimpleWndClass = AfxRegisterWndClass(0); CRect rcEmpty(0, 0, 0, 0); BOOL bCreated = CreateEx(0, pcszSimpleWndClass, XTP_NOTIFICATION_SINK_MT_MSGWND_NAME, WS_POPUP, rcEmpty, NULL, 0); _ASSERTE(bCreated); return bCreated; } ////////////////////////////////////////////////////////////////////////// // CXTPNotifySinkBase CXTPNotifySinkBase::CXTPNotifySinkBase() { } CXTPNotifySinkBase::~CXTPNotifySinkBase() { // Should call UnadviseAll before delete _ASSERTE(m_mapAdviseData.IsEmpty()); } XTP_CONNECTION_ID CXTPNotifySinkBase::Advise(CXTPNotifyConnection* pConnection, XTP_NOTIFY_CODE dwNotifyCode) { _ASSERTE(pConnection); if (!pConnection) { return 0; } XTP_CONNECTION_ID dwConnectionID = pConnection->Advise(dwNotifyCode, this); _ASSERTE(dwConnectionID); if (dwConnectionID) { ADVISE_DESCRIPTOR advDataNew; _ASSERTE(!m_mapAdviseData.Lookup(dwConnectionID, advDataNew)); advDataNew.dwConnectionID = dwConnectionID; advDataNew.pConnection = pConnection; advDataNew.dwNotifyCode = dwNotifyCode; pConnection->InternalAddRef(); m_mapAdviseData.SetAt(dwConnectionID, advDataNew); } return dwConnectionID; } void CXTPNotifySinkBase::UnadviseAll() { XTP_CONNECTION_ID ConnectionID = 0; ADVISE_DESCRIPTOR advData; POSITION pos = m_mapAdviseData.GetStartPosition(); while (pos) { m_mapAdviseData.GetNextAssoc(pos, ConnectionID, advData); if (advData.pConnection) { OnUnadvise(advData.dwNotifyCode); advData.pConnection->Unadvise(advData.dwConnectionID); advData.pConnection->InternalRelease(); } } m_mapAdviseData.RemoveAll(); } void CXTPNotifySinkBase::Unadvise(XTP_CONNECTION_ID ConnectionID) { ADVISE_DESCRIPTOR advData; if (m_mapAdviseData.Lookup(ConnectionID, advData)) { if (advData.pConnection) { OnUnadvise(advData.dwNotifyCode); advData.pConnection->Unadvise(advData.dwConnectionID); advData.pConnection->InternalRelease(); } m_mapAdviseData.RemoveKey(ConnectionID); } else { _ASSERTE(FALSE); } } void CXTPNotifySinkBase::OnUnadvise(XTP_NOTIFY_CODE /*dwNotifyCode*/) { } ////////////////////////////////////////////////////////////////////////// // CXTPNotifySink CXTPNotifySink::CXTPNotifySink() { m_mapHandlers.InitHashTable(10, FALSE); } CXTPNotifySink::~CXTPNotifySink() { _ASSERTE(m_mapHandlers.IsEmpty()); } void CXTPNotifySink::OnUnadvise(XTP_NOTIFY_CODE dwNotifyCode) { CXTPNotifySinkDelegate* pOldDelegate = NULL; if (m_mapHandlers.Lookup(dwNotifyCode, pOldDelegate)) { delete pOldDelegate; m_mapHandlers.RemoveKey(dwNotifyCode); } else { _ASSERTE(FALSE); } } XTP_CONNECTION_ID CXTPNotifySink::Advise(CXTPNotifyConnection* pConnection, XTP_NOTIFY_CODE dwNotifyCode, CXTPNotifySinkDelegate* pDelegate) { CXTPNotifySinkDelegate* pOldDelegate = NULL; if (m_mapHandlers.Lookup(dwNotifyCode, pOldDelegate)) { _ASSERTE(FALSE); delete pOldDelegate; } m_mapHandlers[dwNotifyCode] = pDelegate; return CXTPNotifySinkBase::Advise(pConnection, dwNotifyCode); } void CXTPNotifySink::OnEvent(XTP_NOTIFY_CODE dwNotifyCode, WPARAM wParam, LPARAM lParam, DWORD /*dwFlags*/) { CXTPNotifySinkDelegate* pDelegate = NULL; if (m_mapHandlers.Lookup(dwNotifyCode, pDelegate) && (pDelegate != NULL)) { pDelegate->OnEvent(dwNotifyCode, wParam, lParam); } else { // WARNING. no handler found. ??? _ASSERTE(FALSE); } } void CXTPNotifySink::Delete() { UnadviseAll(); delete this; } ////////////////////////////////////////////////////////////////////////// // CXTPNotifySinkMT CXTPNotifySinkMT::CXTPNotifySinkMT(BOOL bInitInternal /*= TRUE*/) { m_dwTraceFlag0 = 0; m_dwNexDataID = 0; m_bWndCreated = FALSE; m_dwOwnerThreadID = ::GetCurrentThreadId(); if (bInitInternal) { m_bWndCreated = CreateWnd(); } m_PostedEvents.InitHashTable(199, FALSE); } CXTPNotifySinkMT::~CXTPNotifySinkMT() { UnadviseAll(); DestroyWindow(); m_bWndCreated = FALSE; } void CXTPNotifySinkMT::OnEvent(XTP_NOTIFY_CODE dwNotifyCode, WPARAM wParam, LPARAM lParam, DWORD dwFlags) { _ASSERTE(m_bWndCreated); if (dwFlags & xtpNotifyDirectCallForOneThread) { if (GetCurrentThreadId() == m_dwOwnerThreadID) { CXTPNotifySink::OnEvent(dwNotifyCode, wParam, lParam, dwFlags); return; } } //==================================================================== XTP_INTER_THREAD_EVENT_DATA ithData = { dwNotifyCode, wParam, lParam, dwFlags }; if (dwFlags & xtpNotifyPostMessage) { CSingleLock singleLock(&m_DataCS, TRUE); #ifdef _DEBUG XTP_INTER_THREAD_EVENT_DATA ithDataTmp; _ASSERTE(m_PostedEvents.Lookup(m_dwNexDataID, ithDataTmp) == FALSE); #endif DWORD dwDataID = m_dwNexDataID; m_dwNexDataID++; BOOL bTrace1 = FALSE; BOOL bPosted = FALSE; do { bPosted = PostMessage(xtp_wm_NotificationSinkMTOnEvent, NULL, static_cast(dwDataID)); if (!bPosted && (dwFlags & xtpNotifyGuarantyPost)) { if (!bTrace1) { TRACE(_T("WARNING! CXTPNotifySinkBaseImplMT: PostMessage return FALSE. WAIT ") _T("and retry. (ThreadID = %x, dataID = %d) \n"), GetCurrentThreadId(), dwDataID); bTrace1 = TRUE; } singleLock.Unlock(); Sleep(100); singleLock.Lock(); } } while (!bPosted && (dwFlags & xtpNotifyGuarantyPost)); if (bPosted) { m_PostedEvents[dwDataID] = ithData; if (bTrace1) { TRACE(_T("WARNING.* CXTPNotifySinkBaseImplMT: Event is posted! (ThreadID = %x, ") _T("dataID = %d) \n"), GetCurrentThreadId(), dwDataID); } m_dwTraceFlag0 = 0; } else if (!(dwFlags & xtpNotifyGuarantyPost)) { if (m_dwTraceFlag0 < 3) { TRACE(_T("WARNING! CXTPNotifySinkBaseImplMT: PostMessage return FALSE - Event is ") _T("skipped! (ThreadID = %x, dataID = %d) \n"), GetCurrentThreadId(), dwDataID); TRACE(_T(" Use xtpNotifyGuarantyPost flag for important events. \n")); } m_dwTraceFlag0++; } } else { SendMessage(xtp_wm_NotificationSinkMTOnEvent, (WPARAM)&ithData, 0); } }; LRESULT CXTPNotifySinkMT::OnInterThreadEvent(WPARAM pEventData, LPARAM dwPostDataID) { XTP_INTER_THREAD_EVENT_DATA* pIthData = (XTP_INTER_THREAD_EVENT_DATA*)pEventData; if (pIthData) { CXTPNotifySink::OnEvent(pIthData->dwNotifyCode, pIthData->wParam, pIthData->lParam, pIthData->dwFlags); } else { XTP_INTER_THREAD_EVENT_DATA ithData; CSingleLock singleLock(&m_DataCS, TRUE); if (m_PostedEvents.Lookup((DWORD)dwPostDataID, ithData)) { m_PostedEvents.RemoveKey((DWORD)dwPostDataID); singleLock.Unlock(); CXTPNotifySink::OnEvent(ithData.dwNotifyCode, ithData.wParam, ithData.lParam, ithData.dwFlags); } else { _ASSERTE(FALSE); } } //==================================================================== return 0; };