// // Copyright (C) Microsoft Corporation // All rights reserved. // // Code in Details namespace is for internal usage within the library code // #ifndef _PLATFORM_AGILE_H_ #define _PLATFORM_AGILE_H_ #ifdef _MSC_VER #pragma once #endif // _MSC_VER #include #include #if !defined(__cplusplus_winrt) #error agile.h can only be used with /ZW #endif namespace Platform { namespace Details { __declspec(dllimport) IUnknown* __stdcall GetObjectContext(); __declspec(dllimport) HRESULT __stdcall GetProxyImpl(::IUnknown*, REFIID, ::IUnknown*, ::IUnknown**); __declspec(dllimport) HRESULT __stdcall ReleaseInContextImpl(::IUnknown*, ::IUnknown*); template __declspec(no_refcount) inline HRESULT GetProxy(T ^ObjectIn, ::IUnknown *ContextCallBack, T ^*Proxy) { return GetProxyImpl(*reinterpret_cast(&ObjectIn), __uuidof(T^) , ContextCallBack, reinterpret_cast< ::IUnknown**>(Proxy)); } template inline HRESULT ReleaseInContext(T *ObjectIn, ::IUnknown *ContextCallBack) { return ReleaseInContextImpl(ObjectIn, ContextCallBack); } template class AgileHelper { __abi_IUnknown* _p; bool _release; public: AgileHelper(__abi_IUnknown* p, bool release = true) : _p(p), _release(release) { } AgileHelper(AgileHelper && other) : _p(other._p), _release(other._release) { _other._p = nullptr; _other._release = true; } AgileHelper operator=(AgileHelper && other) { _p = other._p; _release = other._release; _other._p = nullptr; _other._release = true; return *this; } ~AgileHelper() { if (_release && _p) { _p->__abi_Release(); } } __declspec(no_refcount) __declspec(no_release_return) T^ operator->() { return reinterpret_cast(_p); } __declspec(no_refcount) __declspec(no_release_return) operator T^() { return reinterpret_cast(_p); } private: AgileHelper(const AgileHelper&); AgileHelper operator=(const AgileHelper&) ; }; template struct __remove_hat { typedef T type; }; template struct __remove_hat { typedef T type; }; template struct AgileTypeHelper { typename typedef __remove_hat::type type; typename typedef __remove_hat::type^ agileMemberType; }; } // namespace Details #pragma warning(push) #pragma warning(disable: 4451) // Usage of ref class inside this context can lead to invalid marshaling of object across contexts template < typename T, bool TIsNotAgile = (__is_win_class(typename Details::AgileTypeHelper::type) && !__is_winrt_agile(typename Details::AgileTypeHelper::type)) || __is_win_interface(typename Details::AgileTypeHelper::type) > class Agile { static_assert(__is_win_class(typename Details::AgileTypeHelper::type) || __is_win_interface(typename Details::AgileTypeHelper::type), "Agile can only be used with ref class or interface class types"); typename typedef Details::AgileTypeHelper::agileMemberType TypeT; TypeT _object; ::Microsoft::WRL::ComPtr< ::IUnknown> _contextCallback; ULONG_PTR _contextToken; enum class AgileState { NonAgilePointer = 0, AgilePointer = 1, Unknown = 2 }; AgileState _agileState; void CaptureContext() { _contextCallback = Details::GetObjectContext(); __abi_ThrowIfFailed(CoGetContextToken(&_contextToken)); } void SetObject(TypeT object) { // Capture context before setting the pointer // If context capture fails then nothing to cleanup Release(); if (object != nullptr) { ::Microsoft::WRL::ComPtr< ::IAgileObject> checkIfAgile; HRESULT hr = reinterpret_cast< ::IUnknown*>(object)->QueryInterface(__uuidof(::IAgileObject), &checkIfAgile); // Don't Capture context if object is agile if (hr != S_OK) { _agileState = AgileState::NonAgilePointer; CaptureContext(); } else { _agileState = AgileState::AgilePointer; } } _object = object; } public: Agile() throw() : _object(nullptr), _contextToken(0), _agileState(AgileState::Unknown) { } Agile(nullptr_t) throw() : _object(nullptr), _contextToken(0), _agileState(AgileState::Unknown) { } explicit Agile(TypeT object) throw() : _object(nullptr), _contextToken(0), _agileState(AgileState::Unknown) { // Assumes that the source object is from the current context SetObject(object); } Agile(const Agile& object) throw() : _object(nullptr), _contextToken(0), _agileState(AgileState::Unknown) { // Get returns pointer valid for current context SetObject(object.Get()); } Agile(Agile && object) throw() : _object(nullptr), _contextToken(0), _agileState(AgileState::Unknown) { // Assumes that the source object is from the current context Swap(object); } ~Agile() throw() { Release(); } TypeT Get() const { // Agile object, no proxy required if (_agileState == AgileState::AgilePointer || _object == nullptr) { return _object; } // Do the check for same context ULONG_PTR currentContextToken; __abi_ThrowIfFailed(CoGetContextToken(¤tContextToken)); if (currentContextToken == _contextToken) { return _object; } // Different context and holding on to a non agile object // Do the costly work of getting a proxy TypeT localObject; __abi_ThrowIfFailed(Details::GetProxy(_object, _contextCallback.Get(), &localObject)); if (_agileState == AgileState::Unknown) { // Object is agile if it implements IAgileObject // GetAddressOf captures the context with out knowing the type of object that it will hold ::Microsoft::WRL::ComPtr< ::IAgileObject> checkIfAgile; HRESULT hr = reinterpret_cast(localObject)->QueryInterface(__uuidof(::IAgileObject), &checkIfAgile); if (hr == S_OK) { auto pThis = const_cast(this); pThis->_agileState = AgileState::AgilePointer; pThis->_contextToken = 0; pThis->_contextCallback = nullptr; return _object; } else { auto pThis = const_cast(this); pThis->_agileState = AgileState::NonAgilePointer; } } return localObject; } TypeT* GetAddressOf() throw() { Release(); CaptureContext(); return &_object; } TypeT* GetAddressOfForInOut() throw() { CaptureContext(); return &_object; } TypeT operator->() const throw() { return Get(); } Agile& operator=(nullptr_t) throw() { Release(); return *this; } Agile& operator=(TypeT object) throw() { Agile(object).Swap(*this); return *this; } Agile& operator=(Agile object) throw() { // parameter is by copy which gets pointer valid for current context object.Swap(*this); return *this; } void Swap(Agile& object) { std::swap(_object, object._object); std::swap(_contextCallback, object._contextCallback); std::swap(_contextToken, object._contextToken); std::swap(_agileState, object._agileState); } // Release the interface and set to NULL void Release() throw() { if (_object) { // Cast to IInspectable (no QI) IUnknown* pObject = *(::IUnknown**) (&_object); // Set ^ to null without release *(IUnknown**) (&_object) = nullptr; ULONG_PTR currentContextToken; __abi_ThrowIfFailed(CoGetContextToken(¤tContextToken)); if (_contextToken == 0 || _contextCallback == nullptr || _contextToken == currentContextToken) { pObject->Release(); } else { Details::ReleaseInContext(pObject, _contextCallback.Get()); } _contextCallback = nullptr; _contextToken = 0; _agileState = AgileState::Unknown; } } bool operator==(nullptr_t) const throw() { return _object == nullptr; } bool operator==(const Agile& other) const throw() { return _object == other._object && _contextToken == other._contextToken; } bool operator < (const Agile& other) const throw() { if (reinterpret_cast(_object) < reinterpret_cast(other._object)) { return true; } return _object == other._object && _contextToken < other._contextToken; } }; template class Agile { static_assert(__is_win_class(typename Details::AgileTypeHelper::type) || __is_win_interface(typename Details::AgileTypeHelper::type), "Agile can only be used with ref class or interface class types"); typename typedef Details::AgileTypeHelper::agileMemberType TypeT; TypeT _object; public: Agile() throw() : _object(nullptr) { } Agile(nullptr_t) throw() : _object(nullptr) { } explicit Agile(TypeT object) throw() : _object(object) { } Agile(const Agile& object) throw() : _object(object._object) { } Agile(Agile && object) throw() : _object(nullptr) { Swap(object); } ~Agile() throw() { Release(); } TypeT Get() const { return _object; } TypeT* GetAddressOf() throw() { Release(); return &_object; } TypeT* GetAddressOfForInOut() throw() { return &_object; } TypeT operator->() const throw() { return Get(); } Agile& operator=(nullptr_t) throw() { Release(); return *this; } Agile& operator=(TypeT object) throw() { if (_object != object) { _object = object; } return *this; } Agile& operator=(Agile object) throw() { object.Swap(*this); return *this; } // Release the interface and set to NULL void Release() throw() { _object = nullptr; } void Swap(Agile& object) { std::swap(_object, object._object); } bool operator==(nullptr_t) const throw() { return _object == nullptr; } bool operator==(const Agile& other) const throw() { return _object == other._object; } bool operator < (const Agile& other) const throw() { return reinterpret_cast(_object) < reinterpret_cast(other._object); } }; #pragma warning(pop) template bool operator==(nullptr_t, const Agile& a) throw() { return a == nullptr; } template bool operator!=(const Agile& a, nullptr_t) throw() { return !(a == nullptr); } template bool operator!=(nullptr_t, const Agile& a) throw() { return !(a == nullptr); } template bool operator!=(const Agile& a, const Agile& b) throw() { return !(a == b); } } // namespace Platform #endif // _PLATFORM_AGILE_H_