You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

563 lines
13 KiB
C++

// XTPNotifyConnection.cpp: implementation of the CXTPNotifyConnection and CXTPNotifySink classes.
//
// This file is a part of the XTREME TOOLKIT PRO MFC class library.
// (c)1998-2012 Codejock Software, All Rights Reserved.
//
// THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE
// RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN
// CONSENT OF CODEJOCK SOFTWARE.
//
// THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED
// IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO
// YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A
// SINGLE COMPUTER.
//
// CONTACT INFORMATION:
// support@codejock.com
// http://www.codejock.com
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "XTPNotifyConnection.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];
ASSERT(pCurElem);
if (pCurElem)
{
delete pCurElem;
}
m_arrConnections[i] = NULL;
}
m_arrConnections.RemoveAll();
}
XTP_CONNECTION_ID CXTPNotifyConnection::Advise(XTP_NOTIFY_CODE Event, CXTPNotifySinkBase* pSink)
{
ASSERT(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];
ASSERT(pElem);
if (pElem)
{
delete pElem;
}
m_arrConnections.RemoveAt(nFIndex);
}
else
{
ASSERT(FALSE);
}
}
catch(...)
{
ASSERT(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];
ASSERT(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())
{
ASSERT(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
{
ASSERT(FALSE);
TRACE(_T("CXTPNotifyConnection::SendEvent(Event = %d, wParam = %d, lParam = %d, dwFlags = %x) pSink = %x\n"),
Event, wParam, lParam, dwFlags, rElem.pSink);
}
}
catch(...)
{
ASSERT(FALSE);
TRACE(_T("EXCEPTION! CXTPNotifyConnection::SendEvent(Event = %d, wParam = %d, lParam = %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];
ASSERT(pCurElem);
if (pCurElem)
{
if (pCurElem->dwConnectionID == ConnectionID)
{
return i;
}
}
}
return -1;
}
////////////////////////////////////////////////////////////////////////////
//class CXTPNotifyConnectionMT : public CXTPNotifyConnection
CXTPNotifyConnectionMT::CXTPNotifyConnectionMT()
{
}
CXTPNotifyConnectionMT::~CXTPNotifyConnectionMT()
{
}
////////////////////////////////////////////////////////////////////////////
BEGIN_MESSAGE_MAP(CXTPNotifySinkImplMTMsgWnd, CWnd)
ON_REGISTERED_MESSAGE(xtp_wm_NotificationSinkMTOnEvent, HandleInterThreadEvent)
END_MESSAGE_MAP()
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);
ASSERT(bCreated);
return bCreated;
}
//////////////////////////////////////////////////////////////////////////
// CXTPNotifySinkBase
CXTPNotifySinkBase::CXTPNotifySinkBase()
{
}
CXTPNotifySinkBase::~CXTPNotifySinkBase()
{
// Should call UnadviseAll before delete
ASSERT(m_mapAdviseData.IsEmpty());
}
XTP_CONNECTION_ID CXTPNotifySinkBase::Advise(CXTPNotifyConnection* pConnection,
XTP_NOTIFY_CODE dwNotifyCode)
{
ASSERT(pConnection);
if (!pConnection)
{
return 0;
}
XTP_CONNECTION_ID dwConnectionID = pConnection->Advise(dwNotifyCode, this);
ASSERT(dwConnectionID);
if (dwConnectionID)
{
ADVISE_DESCRIPTOR advDataNew;
ASSERT(!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
{
ASSERT(FALSE);
}
}
void CXTPNotifySinkBase::OnUnadvise(XTP_NOTIFY_CODE /*dwNotifyCode*/)
{
}
//////////////////////////////////////////////////////////////////////////
// CXTPNotifySink
CXTPNotifySink::CXTPNotifySink()
{
m_mapHandlers.InitHashTable(10, FALSE);
}
CXTPNotifySink::~CXTPNotifySink()
{
ASSERT(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
{
ASSERT(FALSE);
}
}
XTP_CONNECTION_ID CXTPNotifySink::Advise(CXTPNotifyConnection* pConnection, XTP_NOTIFY_CODE dwNotifyCode, CXTPNotifySinkDelegate* pDelegate)
{
CXTPNotifySinkDelegate* pOldDelegate = NULL;
if (m_mapHandlers.Lookup(dwNotifyCode, pOldDelegate))
{
ASSERT(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. ???
ASSERT(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)
{
ASSERT(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;
ASSERT(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, dwDataID);
if (!bPosted && (dwFlags & xtpNotifyGuarantyPost))
{
if (!bTrace1) {
TRACE(_T("WARNING! CXTPNotifySinkBaseImplMT: PostMessage return FALSE. WAIT 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, 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 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
{
ASSERT(FALSE);
}
}
//====================================================================
return 0;
};