diff options
| author | Konstantin <const@const.me> | 2023-01-16 14:52:43 +0100 |
|---|---|---|
| committer | Konstantin <const@const.me> | 2023-01-16 14:52:43 +0100 |
| commit | 8c4603c73675958efc960fbd4bb599a2909d106a (patch) | |
| tree | 714dc6fc9a1672d5fd7f89676b97e10959662abc /ComLightLib/server | |
| parent | 990a8d0dbaefc996244097397259e92758b15cce (diff) | |
Source codes
Diffstat (limited to 'ComLightLib/server')
| -rw-r--r-- | ComLightLib/server/Object.hpp | 139 | ||||
| -rw-r--r-- | ComLightLib/server/ObjectRoot.hpp | 51 | ||||
| -rw-r--r-- | ComLightLib/server/RefCounter.hpp | 38 | ||||
| -rw-r--r-- | ComLightLib/server/freeThreadedMarshaller.cpp | 17 | ||||
| -rw-r--r-- | ComLightLib/server/freeThreadedMarshaller.h | 29 | ||||
| -rw-r--r-- | ComLightLib/server/interfaceMap.h | 31 |
6 files changed, 305 insertions, 0 deletions
diff --git a/ComLightLib/server/Object.hpp b/ComLightLib/server/Object.hpp new file mode 100644 index 0000000..d2e3257 --- /dev/null +++ b/ComLightLib/server/Object.hpp @@ -0,0 +1,139 @@ +#pragma once +#include <type_traits> +#include "../comLightClient.h" +#include "../utils/typeTraits.hpp" +#include "../Exception.hpp" + +namespace ComLight +{ + namespace details + { + GENERATE_HAS_MEMBER( implQueryInterface ); + GENERATE_HAS_MEMBER( implAddRef ); + GENERATE_HAS_MEMBER( implRelease ); + } + + // Outer class of objects, implements IUnknown methods, also the class factory. The type argument must be your class implementing your interfaces, inherited from ObjectRoot<I> + template<class T> + class Object : public T + { + public: + Object() = default; + + template<typename ... Args> + Object( Args&& ... args ) : T{ std::forward<Args>( args )... } {}; + + inline virtual ~Object() override { } + + // Implement IUnknown methods + HRESULT COMLIGHTCALL QueryInterface( REFIID riid, void** ppvObject ) override + { + static_assert( details::has_member_implQueryInterface<T>::value, "Your object class must inherit from ComLight::ObjectRoot" ); + + if( nullptr == ppvObject ) + return E_POINTER; + + if( T::implQueryInterface( riid, ppvObject ) ) + return S_OK; + if( T::queryExtraInterfaces( riid, ppvObject ) ) + return S_OK; + + if( riid == IUnknown::iid() ) + { + ComLight::IUnknown* unk = T::getUnknown(); + unk->AddRef(); + *ppvObject = unk; + return S_OK; + } + + return E_NOINTERFACE; + } + + uint32_t COMLIGHTCALL AddRef() override + { + static_assert( details::has_member_implAddRef<T>::value, "Your object class must inherit from ComLight::ObjectRoot" ); + return T::implAddRef(); + } + + uint32_t COMLIGHTCALL Release() override + { + static_assert( details::has_member_implRelease<T>::value, "Your object class must inherit from ComLight::ObjectRoot" ); + const uint32_t ret = T::implRelease(); + if( 0 == ret ) + { + T::FinalRelease(); + delete this; + } + return ret; + } + + // Create a new object on the heap, store in smart pointer + static inline HRESULT create( CComPtr<Object<T>>& result ) + { + CComPtr<Object<T>> ptr; + try + { + ptr = new Object<T>(); // The RefCounter constructor creates it with ref.counter 0. But then CComPtr constructor calls AddRef so we have RC=1 after this line. + + HRESULT hr = ptr->internalFinalConstruct(); + if( FAILED( hr ) ) + return hr; + + hr = ptr->FinalConstruct(); + if( FAILED( hr ) ) + return hr; + + ptr.swap( result ); + return S_OK; + } + catch( const Exception& ex ) + { + return ex.code(); + } + } + + // Create a new object on the heap, store in smart pointer + template<typename ... Args> + static inline HRESULT create( CComPtr<Object<T>>& result, Args&& ... args ) + { + CComPtr<Object<T>> ptr; + try + { + ptr = new Object<T>( std::forward<Args>( args )... ); + + HRESULT hr = ptr->internalFinalConstruct(); + if( FAILED( hr ) ) + return hr; + + hr = ptr->FinalConstruct(); + if( FAILED( hr ) ) + return hr; + + ptr.swap( result ); + return S_OK; + } + catch( const Exception& ex ) + { + return ex.code(); + } + catch( HRESULT hr ) + { + return hr; + } + } + + // Create a new object on the heap, return one of it's interfaces. The caller is assumed to take ownership of the new object. + template<class I> + static inline HRESULT create( I** pp ) + { + if( pp == nullptr ) + return E_POINTER; + + static_assert( details::pointersAssignable<I, T>(), "Object::create can't cast object to the requested interface" ); + CComPtr<Object<T>> ptr; + CHECK( create( ptr ) ); + ptr.detach( pp ); + return S_OK; + } + }; +}
\ No newline at end of file diff --git a/ComLightLib/server/ObjectRoot.hpp b/ComLightLib/server/ObjectRoot.hpp new file mode 100644 index 0000000..1cc8f4d --- /dev/null +++ b/ComLightLib/server/ObjectRoot.hpp @@ -0,0 +1,51 @@ +#pragma once +#include "RefCounter.hpp" +#include "../comLightCommon.h" +#include "../utils/typeTraits.hpp" + +namespace ComLight +{ + // Base class of objects, implements reference counting, also a few lifetime methods. + // The template argument is the interface you want clients to get when they ask for IID_IUnknown. By convention, that pointer defines object's identity. + template<class I> + class ObjectRoot : public RefCounter, public I + { + protected: + + inline HRESULT internalFinalConstruct() + { + return S_FALSE; + } + + inline HRESULT FinalConstruct() + { + return S_FALSE; + } + + inline void FinalRelease() { } + + IUnknown* getUnknown() + { + static_assert( details::pointersAssignable<IUnknown, I>(), "The interface doesn't derive from IUnknown" ); + return static_cast<I*>( this ); + } + + bool queryExtraInterfaces( REFIID riid, void **ppvObject ) const + { + return false; + } + + // Implement query interface with 2 entries, IUnknown and I. + bool implQueryInterface( REFIID riid, void** ppvObject ) + { + if( riid == I::iid() || riid == IUnknown::iid() ) + { + I* const result = this; + result->AddRef(); + *ppvObject = result; + return true; + } + return false; + } + }; +}
\ No newline at end of file diff --git a/ComLightLib/server/RefCounter.hpp b/ComLightLib/server/RefCounter.hpp new file mode 100644 index 0000000..9698cc8 --- /dev/null +++ b/ComLightLib/server/RefCounter.hpp @@ -0,0 +1,38 @@ +#pragma once +#include <atomic> +#include <assert.h> +#include <limits.h> + +namespace ComLight +{ + // Very base class of objects, implements reference counting. + class RefCounter + { + std::atomic_uint referenceCounter; + + public: + + RefCounter() : referenceCounter( 0 ) { } + + inline virtual ~RefCounter() { } + + RefCounter( const RefCounter &that ) = delete; + RefCounter( RefCounter &&that ) = delete; + + protected: + + uint32_t implAddRef() + { + return ++referenceCounter; + } + + uint32_t implRelease() + { + // Might be a good idea to use locks, at least in debug builds. They're much slower than atomics, but with locks it's possible to detect when 2 threads call release at the same time, for object with counter = 1. + // It's a memory management bug, but it would be nice if debug builds would handle that case gracefully. + const uint32_t rc = --referenceCounter; + assert( rc != UINT_MAX ); + return rc; + } + }; +}
\ No newline at end of file diff --git a/ComLightLib/server/freeThreadedMarshaller.cpp b/ComLightLib/server/freeThreadedMarshaller.cpp new file mode 100644 index 0000000..fc1ea80 --- /dev/null +++ b/ComLightLib/server/freeThreadedMarshaller.cpp @@ -0,0 +1,17 @@ +#include "freeThreadedMarshaller.h" +#ifdef _MSC_VER +#include <combaseapi.h> + +HRESULT ComLight::details::createFreeThreadedMarshaller( IUnknown* pUnkOuter, IUnknown** ppUnkMarshal ) +{ + return ::CoCreateFreeThreadedMarshaler( (LPUNKNOWN)pUnkOuter, (LPUNKNOWN *)ppUnkMarshal ); +} + +bool ComLight::details::queryMarshallerInterface( REFIID riid, void **ppvObject, IUnknown* marshaller ) +{ + if( riid != IID_IMarshal || nullptr == marshaller ) + return false; + const HRESULT hr = marshaller->QueryInterface( IID_IMarshal, ppvObject ); + return SUCCEEDED( hr ) ? true : false; +} +#endif
\ No newline at end of file diff --git a/ComLightLib/server/freeThreadedMarshaller.h b/ComLightLib/server/freeThreadedMarshaller.h new file mode 100644 index 0000000..8ef774e --- /dev/null +++ b/ComLightLib/server/freeThreadedMarshaller.h @@ -0,0 +1,29 @@ +#pragma once +#ifdef _MSC_VER +#include "../comLightCommon.h" + +namespace ComLight +{ + namespace details + { + HRESULT createFreeThreadedMarshaller( IUnknown* pUnkOuter, IUnknown** ppUnkMarshal ); + bool queryMarshallerInterface( REFIID riid, void **ppvObject, IUnknown* marshaller ); + } +} + +#define DECLARE_FREE_THREADED_MARSHALLER() \ +private: \ +ComLight::CComPtr<ComLight::IUnknown> m_freeThreadedMarshaller; \ +protected: \ +HRESULT internalFinalConstruct() \ +{ \ + return ComLight::details::createFreeThreadedMarshaller( getUnknown(), &m_freeThreadedMarshaller ); \ +} \ +bool queryExtraInterfaces( REFIID riid, void **ppvObject ) const \ +{ \ + return ComLight::details::queryMarshallerInterface( riid, ppvObject, m_freeThreadedMarshaller ); \ +} + +#else +#define DECLARE_FREE_THREADED_MARSHALLER() +#endif
\ No newline at end of file diff --git a/ComLightLib/server/interfaceMap.h b/ComLightLib/server/interfaceMap.h new file mode 100644 index 0000000..605ed33 --- /dev/null +++ b/ComLightLib/server/interfaceMap.h @@ -0,0 +1,31 @@ +#pragma once +#include "../utils/typeTraits.hpp" + +// Unlike ATL, the interface map is optional for ComLight. +// If you won't declare a map, the object will support 2 interfaces: IUnknown, and whatever template argument was passed to ObjectRoot class. +#define BEGIN_COM_MAP() \ +protected: \ +bool implQueryInterface( REFIID iid, void** ppvObject ) { + +#define END_COM_MAP() return false; } + +namespace ComLight +{ + namespace details + { + template<typename I, typename C> + inline bool tryReturnInterface( REFIID iid, C* pThis, void** ppvResult ) + { + static_assert( pointersAssignable<IUnknown, I>(), "Trying to implement an interface that doesn't derive from IUnknown" ); + static_assert( pointersAssignable<I, C>(), "Declared support for an interface, but the class doesn't implement it" ); + if( I::iid() != iid ) + return false; + I* const result = pThis; + result->AddRef(); + *ppvResult = result; + return true; + } + } +} + +#define COM_INTERFACE_ENTRY( I ) if( ComLight::details::tryReturnInterface<I>( iid, this, ppvObject ) ) return true;
\ No newline at end of file |
