/** * @file XTPNotifyConnection.h * * @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 * */ /** @cond */ #ifndef _XTPNOTIFYCONNECTION_H__ # define _XTPNOTIFYCONNECTION_H__ /** @endcond */ # if _MSC_VER > 1000 # pragma once # endif // _MSC_VER > 1000 # include "Common/Base/Diagnostic/XTPDisableNoisyWarnings.h" ///////////////////////////////////////////////////////////////////////////// class CXTPNotifySinkBase; typedef DWORD_PTR XTP_CONNECTION_ID; typedef DWORD XTP_NOTIFY_CODE; /** * @brief * Defines a base ID for common notifications. */ const UINT WM_XTP_COMMON_BASE = (WM_USER + 9550); /** * RANGE for WM_XTP_COMMON_BASE {+ 1 ... + 49} * * Next free COMMON ID = (WM_XTP_COMMON_BASE + 2) */ /** @cond */ /** * Defines an ID for Office2007 Images changed notification. * Sender: CXTPResourceImage */ static const XTP_NOTIFY_CODE XTP_NC_COMMON_RESOURCEIMAGES_CHANGED = (WM_XTP_COMMON_BASE + 1); /** @endcond */ /** * @brief * Notify flags options */ enum XTPNotifyFlags { xtpNotifyPostMessage = 0x80000000, /**< Event will be posted */ xtpNotifyGuarantyPost = 0x40000000, /**< Used together with nofPostMessage. Event will be guaranty posted. (Wait untill PostMessage returns TRUE) */ xtpNotifyDirectCallForOneThread = 0x20000000 /**< Event handler will be called directly if sender and receiver are in one thread. */ }; /** * @brief * This class is used as implementation of Connection interface in * a XTPNotification mechanism. This mechanism consists of * Connection object(s) and Sink object(s). The first are used to * send notification messages and the second are used to receive * these notification. Each notification has unique number * (or EventCode or NotificationCode) for a given connection * object or for the whole system. This depends on implementation. * Using system unique NotificationCodes is a preferred way to avoid * potential errors with intersected NotificationCodes. * @see * CXTPNotifySinkBase overview, CXTPNotifyConnection overview */ class _XTP_EXT_CLASS CXTPNotifyConnection : public CXTPCmdTarget { public: /** * @brief * Default object constructor. * @see * ~CXTPNotifyConnection() */ CXTPNotifyConnection(); /** * @brief * Default object destructor. * @see * CXTPNotifyConnection() */ virtual ~CXTPNotifyConnection(); public: /** * @brief * Establishes a connection between the connection object and * a sink object. * @param dwNotifyCode EventCode of a Notification. * @param pSink Pointer to the Sink object which will receive * events with specified code. * @details * Call this method to establish a connection with the event * source and the sink object. pSink->OnEvent(...) method will be * called if event with specified EventCode will occur. * Use Unadvise() method with returned connection ID to terminate * the connection call. * @return * The unique connection ID used for Unadvise() method. * @see * CXTPNotifySinkBase overview, Unadvise() method. */ XTP_CONNECTION_ID Advise(XTP_NOTIFY_CODE dwNotifyCode, CXTPNotifySinkBase* pSink); /** * @brief * Terminate a connection between the connection object and * a sink object. * @param ConnectionID The unique connection ID previously returned * in Advise() call. * @details * Call this method to Terminate a connection with the event * source and the sink object. * @see * Advise() method. */ void Unadvise(XTP_CONNECTION_ID ConnectionID); /** * @brief * Remove All connections information. * @details * Call this method to remove all connections added using * Advise method. * @see * Advise method */ void RemoveAll(); /** * @brief * Send event with specified EventCode. * @param dwNotifyCode EventCode of a Notification. * @param wParam First parameter specific for this Notification. * @param lParam Second parameter specific for this Notification. * @param dwFlags Additional flags, default is 0. See XTPNotifyFlags. * @details * Call this method to send event with specified EventCode to * sinks which where advised to this EventCode using Advise * method. * @return * TRUE if event was sent to any client (if any client exists), * FALSE otherwise. * @see * Advise(), XTPNotifyFlags. */ BOOL SendEvent(XTP_NOTIFY_CODE dwNotifyCode, WPARAM wParam, LPARAM lParam, DWORD dwFlags = 0); protected: /** * @brief * Find connection information in the m_arConnections array. * @param ConnectionID The unique connection ID. * @details * Call this method to find connection description in the * m_arConnections array using specified connection ID. * @return * The zero-based index of connection description in the * m_arConnections array that matches the requested ConnectionID; * -1 if the it is not found. * @see * Advise method, CONNECTION_DESCRIPTOR */ int FindConnection(XTP_CONNECTION_ID ConnectionID); /** * @brief * Get synchronization object to lock internal class data. * @details * This method is used as prototype for derived classes to * support multi threaded clients calls. In base * implementation pointer to an empty locker object is returned. * @return * Pointer to the locker object. * @see * CCriticalSection, CSingleLock, CSyncObject. */ virtual CSyncObject* GetDataLock(); /** * @brief * This struct is used in the implementation of the * class CXTPNotifyConnection to store connection between * a Connection object, Notification code and the sink object. * @see * CXTPNotifyConnection overview, CXTPNotifySinkBase overview */ struct CONNECTION_DESCRIPTOR { XTP_NOTIFY_CODE dwNotifyCode; /**< The Notification code. */ CXTPNotifySinkBase* pSink; /**< The pointer to the sink object. (with InternalAddRef) */ XTP_CONNECTION_ID dwConnectionID; /**< The unique connection ID used for Unadvise() method. */ }; /** * @brief * This struct is derived from CSyncObject and should be used * instead of CCriticalSection to improve performance and * to provide compatibility. It just provide CSyncObject * interface with empty Lock and Unlock methods. * @see * CCriticalSection, CSingleLock, CSyncObject. */ class CEmptySyncObject : public CSyncObject { public: /** * @brief * Default object constructor. */ CEmptySyncObject(); /** * @brief * Set object state as locked. * @param dwTimeout Specifies the amount of time to wait for the * synchronization object to be available (signaled) * @details * Empty Lock method. * @return * TRUE * @see * CCriticalSection::Lock, CSingleLock, CSyncObject. */ virtual BOOL Lock(DWORD dwTimeout = INFINITE); /** * @brief * Set object state as unlocked. * @details * Empty Unlock method. * @return * TRUE * @see * CCriticalSection::Unlock, CSingleLock, CSyncObject. */ virtual BOOL Unlock(); /** * @brief * Set object state as unlocked. * @param lCount Number of accesses to release * @param lPrevCount Points to a variable to receive the previous count of the * synchronization object * @details * Empty Unlock method. * @return * TRUE * @see * CCriticalSection::Unlock, CSingleLock, CSyncObject. */ virtual BOOL Unlock(LONG lCount, LPLONG lPrevCount = NULL); }; protected: CArray m_arrConnections; /**< store connections between a Connection object, Notification code and the sink object. */ CArray m_arrSendQueueCache; /**< used in SendEvent method for safety reason */ int m_nSendQueueCacheSize; /**< used in SendEvent method together with m_arrSendQueueCache. */ CEmptySyncObject m_emptyLocker; /**< Pseudo-locker object */ }; /** * @brief * This class is used as thread-safety implementation of Connection * interface in a XTPNotification mechanism. This mechanism consists of * Connection object(s) and Sink object(s). The first are used to * send notification messages and the second are used to receive * these notification. Each notification has unique number * (or EventCode or NotificationCode) for a given connection * object or for the whole system. This depends on implementation. * Using system unique NotificationCodes is a preferred way to avoid * potential errors with intersected NotificationCodes. * @see * CXTPNotifySinkBase, CXTPNotifyConnection */ class _XTP_EXT_CLASS CXTPNotifyConnectionMT : public CXTPNotifyConnection { public: /** * @brief * Default object constructor. */ CXTPNotifyConnectionMT(); /** * @brief * Default object destructor. */ virtual ~CXTPNotifyConnectionMT(); /** * @brief * Post event with specified EventCode. * @param dwNotifyCode EventCode of a Notification. * @param wParam First parameter specific for this Notification. * @param lParam Second parameter specific for this Notification. * @param dwFlags Additional flags, default is 0. See XTPNotifyFlags. * @details * Call this method to post event with specified EventCode to * sinks which where advised to this EventCode using Advise * method. Event will be posted only to the sinks which * implementations support Post event possibility, like standard * multithreaded sinks. * The sink implementation is responsible for the Post mechanism. * For posted event the appropriate bit of dwFlags is set * automatically and SendEvent is called. * @return * TRUE if event was sent to any client (if any client exists), * FALSE otherwise. * @see * Advise(), SendEvent(), XTPNotifyFlags */ BOOL PostEvent(XTP_NOTIFY_CODE dwNotifyCode, WPARAM wParam, LPARAM lParam, DWORD dwFlags = 0); protected: /** * @brief * Get synchronization object to lock internal class data. * @details * This method is used to support multi threaded clients calls.. * @return * Pointer to the locker object (CCriticalSection). * @see * CCriticalSection, CSingleLock, CSyncObject. */ virtual CSyncObject* GetDataLock(); CCriticalSection m_DataLockerCS; /**< Data locker object */ }; /** @cond */ static LPCTSTR XTP_NOTIFICATION_SINK_MT_ON_EVENT_MSG = _T("XTPNotificationSinkMTOnEvent"); const UINT xtp_wm_NotificationSinkMTOnEvent = RegisterWindowMessage( XTP_NOTIFICATION_SINK_MT_ON_EVENT_MSG); /** @endcond */ /** * @brief * This class is used as implementation of Sink interface in * a XTPNotification mechanism. This mechanism consists of * Connection object(s) and Sink object(s). The first are used to * send notification messages and the second are used to receive * these notification. Each notification has unique number * (or dwNotifyCode or NotificationCode) for a given connection * object or for the whole system. This depends on implementation. * Using system unique NotificationCodes is a preferred way to avoid * potential errors with intersected NotificationCodes. * @see * CXTPNotifySinkBase overview, DECLARE_XTPSINK macro, * CXTPNotifyConnection overview */ class _XTP_EXT_CLASS CXTPNotifySinkBase { public: /** * @brief * Default object constructor. * @see * ~CXTPNotifySinkBase() */ CXTPNotifySinkBase(); protected: /** * @brief * Default object destructor. * @see * CXTPNotifySinkBase() */ virtual ~CXTPNotifySinkBase(); public: /** * @brief * Event with specified dwNotifyCode was occurred. * @param dwNotifyCode EventCode of a Notification. * @param wParam First parameter specific for this Notification. * @param lParam Second parameter specific for this Notification. * @param dwFlags Additional flags. See XTPNotifyFlags. * @details * This method is called by Connection object to notify advice sink * that event with specified EventCode was occurred. It should be * override in your derived class to receive notification(s). * @see * DECLARE_XTPSINK macro, DECLARE_XTPSINK_MT macro, * CXTPNotifyConnection, CXTPNotifyConnection::Advise method */ virtual void OnEvent(XTP_NOTIFY_CODE dwNotifyCode, WPARAM wParam, LPARAM lParam, DWORD dwFlags) = 0; public: /** * @brief * Terminate all connections between the connection object(s) and * this sink object. * @details * Call this method to Terminate all connections with the event * source(s) and this sink object. * @see * CXTPNotifySinkBase, UnadviseAll method, Advise method, * CXTPNotifyConnection, CXTPNotifyConnection::Unadvise method */ void UnadviseAll(); /** * @brief * Terminate a connection between the connection object and * this sink object. * @param id The unique connection ID previously returned * in Advise() call. * @details * Call this method to Terminate a connection with the event * source and this sink object. * @see * CXTPNotifySinkBase, UnadviseAll method, Advise method, * CXTPNotifyConnection, CXTPNotifyConnection::Unadvise method */ void Unadvise(XTP_CONNECTION_ID id); protected: /** * @brief * Establishes a connection between the connection object and * a sink object. * @param pConnection Pointer to the Connection object. * @param dwNotifyCode Notify Code of a Notification. * @details * Call this method to establish a connection with the event * source and the sink object. OnEvent(...) method will be * called if event with specified dwNotifyCode will occur. * Use Unadvise() method with returned connection ID to terminate * the connection call. * @return * The unique connection ID used for Unadvise() method. * @see * CXTPNotifySinkBase, Unadvise method, GetParam method, * CXTPNotifyConnection, CXTPNotifyConnection::Advise method */ XTP_CONNECTION_ID Advise(CXTPNotifyConnection* pConnection, XTP_NOTIFY_CODE dwNotifyCode); protected: virtual void OnUnadvise(XTP_NOTIFY_CODE dwNotifyCode); protected: /** * @brief * This struct is used in to store connection between a Connection * object(s), Notification code and the sink object. * @see * CXTPNotifySinkBase overview, */ struct ADVISE_DESCRIPTOR { XTP_NOTIFY_CODE dwNotifyCode; /**< The Notification code. */ CXTPNotifyConnection* pConnection; /**< The pointer to the connection object. (with InternalAddRef) */ XTP_CONNECTION_ID dwConnectionID; /**< Original Connection ID returned by pConnection->Advise() method. */ }; CMap m_mapAdviseData; /**< store connections between a Connection objects, Notification code and this sink object. */ }; ////////////////////////////////////////////////////////////////////////////// /** @cond */ AFX_INLINE BOOL CXTPNotifyConnection::CEmptySyncObject::Lock(DWORD /*dwTimeout*/) { return TRUE; } AFX_INLINE BOOL CXTPNotifyConnection::CEmptySyncObject::Unlock() { return TRUE; } AFX_INLINE BOOL CXTPNotifyConnection::CEmptySyncObject::Unlock(LONG /*lCount*/, LPLONG /*lPrevCount = NULL*/) { return TRUE; } AFX_INLINE CSyncObject* CXTPNotifyConnection::GetDataLock() { return &m_emptyLocker; } AFX_INLINE CSyncObject* CXTPNotifyConnectionMT::GetDataLock() { return &m_DataLockerCS; } AFX_INLINE BOOL CXTPNotifyConnectionMT::PostEvent(XTP_NOTIFY_CODE dwNotifyCode, WPARAM wParam, LPARAM lParam, DWORD dwFlags) { return SendEvent(dwNotifyCode, wParam, lParam, dwFlags | xtpNotifyPostMessage); } template class CXTPNotifySinkBaseImpl : public CXTPNotifySinkBase { public: CXTPNotifySinkBaseImpl() { m_mapHandlers.InitHashTable(101, FALSE); } virtual ~CXTPNotifySinkBaseImpl() { UnadviseAll(); }; public: typedef void (ownerClassName::*T_pfHandler)(XTP_NOTIFY_CODE dwNotifyCode, WPARAM wParam, LPARAM lParam); XTP_CONNECTION_ID Advise(CXTPNotifyConnection* pConnection, XTP_NOTIFY_CODE dwNotifyCode, T_pfHandler pfHandler) { m_mapHandlers.SetAt(dwNotifyCode, pfHandler); return CXTPNotifySinkBase::Advise(pConnection, dwNotifyCode); } virtual void OnEvent(XTP_NOTIFY_CODE dwNotifyCode, WPARAM wParam, LPARAM lParam, DWORD /*dwFlags*/) { ownerClassName* pThis = _CInformator::GetPThis((BYTE*)this); if (!pThis) { _ASSERTE(FALSE); return; } T_pfHandler pfHandler = 0; if (m_mapHandlers.Lookup(dwNotifyCode, pfHandler) && (pfHandler != NULL)) { (pThis->*(pfHandler))(dwNotifyCode, wParam, lParam); return; } // WARNING. no handler found. ??? _ASSERTE(FALSE); } protected: CMap m_mapHandlers; }; class _XTP_EXT_CLASS CXTPNotifySinkImplMTMsgWnd : public CWnd { public: CXTPNotifySinkImplMTMsgWnd(); virtual ~CXTPNotifySinkImplMTMsgWnd(); protected: virtual LRESULT OnInterThreadEvent(WPARAM pEventData, LPARAM reserved) = 0; afx_msg LRESULT HandleInterThreadEvent(WPARAM pEventData, LPARAM reserved); virtual BOOL CreateWnd(); DECLARE_MESSAGE_MAP() }; //=== Multithreaded sink implementation ===================================== struct XTP_INTER_THREAD_EVENT_DATA { XTP_NOTIFY_CODE dwNotifyCode; WPARAM wParam; LPARAM lParam; DWORD dwFlags; }; # define DECLARE_XTP_SINKEX(ownerClassName, MemberName, _CSinkClass) \ class C_##MemberName##_Informator \ { \ public: \ static ownerClassName* AFX_CDECL GetPThis(BYTE* pSink) \ { \ ownerClassName* pThis = (( \ ownerClassName*)((BYTE*)pSink - offsetof(ownerClassName, MemberName))); \ return pThis; \ }; \ }; \ friend class C_##MemberName##_Informator; \ typedef _CSinkClass T_##MemberName; \ T_##MemberName MemberName; /** @endcond */ /** * @brief * This macro is used as implementation of Sink object in * the events receiver class. * @param ownerClassName Name of the class where DECLARE_XTPSINK macro * is placed. * @param MemberName Name of the sink class member. * * Example: * class CMyObjectEventsReceiver : public CXTPCmdTarget * { * protected: * // Declare sink object * DECLARE_XTPSINK(CMyObjectEventsReceiver, m_Sink) * * // Declare Event handlers methods * void NotificationHandler1(XTP_NOTIFY_CODE Event, * WPARAM wParam , LPARAM lParam); * void NotificationHandler2(XTP_NOTIFY_CODE Event, * WPARAM wParam , LPARAM lParam); * // ... * }; * @see * CXTPNotifySinkBase overview, CXTPNotifyConnection overview, */ # define DECLARE_XTP_SINK(ownerClassName, MemberName) \ DECLARE_XTP_SINKEX(ownerClassName, MemberName, CXTPNotifySinkBaseImpl) \ friend class CXTPNotifySinkBaseImpl; /** @cond */ class _XTP_EXT_CLASS CXTPNotifySinkDelegate { public: virtual ~CXTPNotifySinkDelegate(); public: virtual void OnEvent(XTP_NOTIFY_CODE dwNotifyCode, WPARAM wParam, LPARAM lParam) = 0; }; template class CXTPNotifySinkClassDelegate : public CXTPNotifySinkDelegate { public: typedef void (T::*EVENTHANDLER)(XTP_NOTIFY_CODE dwNotifyCode, WPARAM wParam, LPARAM lParam); public: CXTPNotifySinkClassDelegate(T* pObject, EVENTHANDLER pHandler) { m_pObject = pObject; m_pHandler = pHandler; } virtual void OnEvent(XTP_NOTIFY_CODE dwNotifyCode, WPARAM wParam, LPARAM lParam) { (m_pObject->*m_pHandler)(dwNotifyCode, wParam, lParam); } protected: T* m_pObject; EVENTHANDLER m_pHandler; }; template AFX_INLINE CXTPNotifySinkDelegate* CreateNotfySinkClassDelegate(T* pClass, EVENTHANDLER pfnDelegate) { return new CXTPNotifySinkClassDelegate( pClass, (typename CXTPNotifySinkClassDelegate::EVENTHANDLER)pfnDelegate); } /** @endcond */ class _XTP_EXT_CLASS CXTPNotifySink : public CXTPNotifySinkBase { public: CXTPNotifySink(); protected: ~CXTPNotifySink(); public: void Delete(); public: XTP_CONNECTION_ID Advise(CXTPNotifyConnection* pConnection, XTP_NOTIFY_CODE dwNotifyCode, CXTPNotifySinkDelegate* pDelegate); virtual void OnEvent(XTP_NOTIFY_CODE dwNotifyCode, WPARAM wParam, LPARAM lParam, DWORD /*dwFlags*/); protected: virtual void OnUnadvise(XTP_NOTIFY_CODE dwNotifyCode); protected: CMap m_mapHandlers; }; class _XTP_EXT_CLASS CXTPNotifySinkMT : public CXTPNotifySink , public CXTPNotifySinkImplMTMsgWnd { public: CXTPNotifySinkMT(BOOL bInitInternal = TRUE); virtual ~CXTPNotifySinkMT(); public: virtual void OnEvent(XTP_NOTIFY_CODE dwNotifyCode, WPARAM wParam, LPARAM lParam, DWORD dwFlags); protected: virtual LRESULT OnInterThreadEvent(WPARAM pEventData, LPARAM dwPostDataID); protected: CMap m_PostedEvents; DWORD m_dwNexDataID; BOOL m_bWndCreated; DWORD m_dwOwnerThreadID; CCriticalSection m_DataCS; private: DWORD m_dwTraceFlag0; }; /** @cond */ AFX_INLINE CXTPNotifySinkDelegate::~CXTPNotifySinkDelegate() { } /** @endcond */ # include "Common/Base/Diagnostic/XTPEnableNoisyWarnings.h" /** @cond */ #endif // !defined(_XTPNOTIFYCONNECTION_H__) /** @endcond */