1 // 2 // Copyright (C) Microsoft Corporation 3 // All rights reserved. 4 // Modified for native C++ WRL support by Gregory Morse 5 // 6 // Code in Details namespace is for internal usage within the library code 7 // 8 9 #ifndef _PLATFORM_AGILE_H_ 10 #define _PLATFORM_AGILE_H_ 11 12 #ifdef _MSC_VER 13 #pragma once 14 #endif // _MSC_VER 15 16 #include <algorithm> 17 #include <wrl\client.h> 18 19 template <typename T, bool TIsNotAgile> class Agile; 20 21 template <typename T> 22 struct UnwrapAgile 23 { 24 static const bool _IsAgile = false; 25 }; 26 template <typename T> 27 struct UnwrapAgile<Agile<T, false>> 28 { 29 static const bool _IsAgile = true; 30 }; 31 template <typename T> 32 struct UnwrapAgile<Agile<T, true>> 33 { 34 static const bool _IsAgile = true; 35 }; 36 37 #define IS_AGILE(T) UnwrapAgile<T>::_IsAgile 38 39 #define __is_winrt_agile(T) (std::is_same<T, HSTRING__>::value || std::is_base_of<Microsoft::WRL::FtmBase, T>::value || std::is_base_of<IAgileObject, T>::value) //derived from Microsoft::WRL::FtmBase or IAgileObject 40 41 #define __is_win_interface(T) (std::is_base_of<IUnknown, T>::value || std::is_base_of<IInspectable, T>::value) //derived from IUnknown or IInspectable 42 43 #define __is_win_class(T) (std::is_same<T, HSTRING__>::value || std::is_base_of<Microsoft::WRL::Details::RuntimeClassBase, T>::value) //derived from Microsoft::WRL::RuntimeClass or HSTRING 44 45 namespace Details 46 { 47 IUnknown* __stdcall GetObjectContext(); 48 HRESULT __stdcall GetProxyImpl(IUnknown*, REFIID, IUnknown*, IUnknown**); 49 HRESULT __stdcall ReleaseInContextImpl(IUnknown*, IUnknown*); 50 51 template <typename T> 52 #if _MSC_VER >= 1800 GetProxy(T * ObjectIn,IUnknown * ContextCallBack,T ** Proxy)53 __declspec(no_refcount) inline HRESULT GetProxy(T *ObjectIn, IUnknown *ContextCallBack, T **Proxy) 54 #else 55 inline HRESULT GetProxy(T *ObjectIn, IUnknown *ContextCallBack, T **Proxy) 56 #endif 57 { 58 #if _MSC_VER >= 1800 59 return GetProxyImpl(*reinterpret_cast<IUnknown**>(&ObjectIn), __uuidof(T*), ContextCallBack, reinterpret_cast<IUnknown**>(Proxy)); 60 #else 61 return GetProxyImpl(*reinterpret_cast<IUnknown**>(&const_cast<T*>(ObjectIn)), __uuidof(T*), ContextCallBack, reinterpret_cast<IUnknown**>(Proxy)); 62 #endif 63 } 64 65 template <typename T> ReleaseInContext(T * ObjectIn,IUnknown * ContextCallBack)66 inline HRESULT ReleaseInContext(T *ObjectIn, IUnknown *ContextCallBack) 67 { 68 return ReleaseInContextImpl(ObjectIn, ContextCallBack); 69 } 70 71 template <typename T> 72 class AgileHelper 73 { 74 __abi_IUnknown* _p; 75 bool _release; 76 public: AgileHelper(__abi_IUnknown * p,bool release=true)77 AgileHelper(__abi_IUnknown* p, bool release = true) : _p(p), _release(release) 78 { 79 } AgileHelper(AgileHelper && other)80 AgileHelper(AgileHelper&& other) : _p(other._p), _release(other._release) 81 { 82 _other._p = nullptr; 83 _other._release = true; 84 } operator =(AgileHelper && other)85 AgileHelper operator=(AgileHelper&& other) 86 { 87 _p = other._p; 88 _release = other._release; 89 _other._p = nullptr; 90 _other._release = true; 91 return *this; 92 } 93 ~AgileHelper()94 ~AgileHelper() 95 { 96 if (_release && _p) 97 { 98 _p->__abi_Release(); 99 } 100 } 101 102 __declspec(no_refcount) __declspec(no_release_return) operator ->()103 T* operator->() 104 { 105 return reinterpret_cast<T*>(_p); 106 } 107 108 __declspec(no_refcount) __declspec(no_release_return) operator T*()109 operator T * () 110 { 111 return reinterpret_cast<T*>(_p); 112 } 113 private: 114 AgileHelper(const AgileHelper&); 115 AgileHelper operator=(const AgileHelper&); 116 }; 117 template <typename T> 118 struct __remove_hat 119 { 120 typedef T type; 121 }; 122 template <typename T> 123 struct __remove_hat<T*> 124 { 125 typedef T type; 126 }; 127 template <typename T> 128 struct AgileTypeHelper 129 { 130 typename typedef __remove_hat<T>::type type; 131 typename typedef __remove_hat<T>::type* agileMemberType; 132 }; 133 } // namespace Details 134 135 #pragma warning(push) 136 #pragma warning(disable: 4451) // Usage of ref class inside this context can lead to invalid marshaling of object across contexts 137 138 template < 139 typename T, 140 bool TIsNotAgile = (__is_win_class(typename Details::AgileTypeHelper<T>::type) && !__is_winrt_agile(typename Details::AgileTypeHelper<T>::type)) || 141 __is_win_interface(typename Details::AgileTypeHelper<T>::type) 142 > 143 class Agile 144 { 145 static_assert(__is_win_class(typename Details::AgileTypeHelper<T>::type) || __is_win_interface(typename Details::AgileTypeHelper<T>::type), "Agile can only be used with ref class or interface class types"); 146 typename typedef Details::AgileTypeHelper<T>::agileMemberType TypeT; 147 TypeT _object; 148 ::Microsoft::WRL::ComPtr<IUnknown> _contextCallback; 149 ULONG_PTR _contextToken; 150 151 #if _MSC_VER >= 1800 152 enum class AgileState 153 { 154 NonAgilePointer = 0, 155 AgilePointer = 1, 156 Unknown = 2 157 }; 158 AgileState _agileState; 159 #endif 160 CaptureContext()161 void CaptureContext() 162 { 163 _contextCallback = Details::GetObjectContext(); 164 __abi_ThrowIfFailed(CoGetContextToken(&_contextToken)); 165 } 166 SetObject(TypeT object)167 void SetObject(TypeT object) 168 { 169 // Capture context before setting the pointer 170 // If context capture fails then nothing to cleanup 171 Release(); 172 if (object != nullptr) 173 { 174 ::Microsoft::WRL::ComPtr<IAgileObject> checkIfAgile; 175 HRESULT hr = reinterpret_cast<IUnknown*>(object)->QueryInterface(__uuidof(IAgileObject), &checkIfAgile); 176 // Don't Capture context if object is agile 177 if (hr != S_OK) 178 { 179 #if _MSC_VER >= 1800 180 _agileState = AgileState::NonAgilePointer; 181 #endif 182 CaptureContext(); 183 } 184 #if _MSC_VER >= 1800 185 else 186 { 187 _agileState = AgileState::AgilePointer; 188 } 189 #endif 190 } 191 _object = object; 192 } 193 194 public: Agile()195 Agile() throw() : _object(nullptr), _contextToken(0) 196 #if _MSC_VER >= 1800 197 , _agileState(AgileState::Unknown) 198 #endif 199 { 200 } 201 Agile(nullptr_t)202 Agile(nullptr_t) throw() : _object(nullptr), _contextToken(0) 203 #if _MSC_VER >= 1800 204 , _agileState(AgileState::Unknown) 205 #endif 206 { 207 } 208 Agile(TypeT object)209 explicit Agile(TypeT object) throw() : _object(nullptr), _contextToken(0) 210 #if _MSC_VER >= 1800 211 , _agileState(AgileState::Unknown) 212 #endif 213 { 214 // Assumes that the source object is from the current context 215 SetObject(object); 216 } 217 Agile(const Agile & object)218 Agile(const Agile& object) throw() : _object(nullptr), _contextToken(0) 219 #if _MSC_VER >= 1800 220 , _agileState(AgileState::Unknown) 221 #endif 222 { 223 // Get returns pointer valid for current context 224 SetObject(object.Get()); 225 } 226 Agile(Agile && object)227 Agile(Agile&& object) throw() : _object(nullptr), _contextToken(0) 228 #if _MSC_VER >= 1800 229 , _agileState(AgileState::Unknown) 230 #endif 231 { 232 // Assumes that the source object is from the current context 233 Swap(object); 234 } 235 ~Agile()236 ~Agile() throw() 237 { 238 Release(); 239 } 240 Get() const241 TypeT Get() const 242 { 243 // Agile object, no proxy required 244 #if _MSC_VER >= 1800 245 if (_agileState == AgileState::AgilePointer || _object == nullptr) 246 #else 247 if (_contextToken == 0 || _contextCallback == nullptr || _object == nullptr) 248 #endif 249 { 250 return _object; 251 } 252 253 // Do the check for same context 254 ULONG_PTR currentContextToken; 255 __abi_ThrowIfFailed(CoGetContextToken(¤tContextToken)); 256 if (currentContextToken == _contextToken) 257 { 258 return _object; 259 } 260 261 #if _MSC_VER >= 1800 262 // Different context and holding on to a non agile object 263 // Do the costly work of getting a proxy 264 TypeT localObject; 265 __abi_ThrowIfFailed(Details::GetProxy(_object, _contextCallback.Get(), &localObject)); 266 267 if (_agileState == AgileState::Unknown) 268 #else 269 // Object is agile if it implements IAgileObject 270 // GetAddressOf captures the context with out knowing the type of object that it will hold 271 if (_object != nullptr) 272 #endif 273 { 274 #if _MSC_VER >= 1800 275 // Object is agile if it implements IAgileObject 276 // GetAddressOf captures the context with out knowing the type of object that it will hold 277 ::Microsoft::WRL::ComPtr<IAgileObject> checkIfAgile; 278 HRESULT hr = reinterpret_cast<IUnknown*>(localObject)->QueryInterface(__uuidof(IAgileObject), &checkIfAgile); 279 #else 280 ::Microsoft::WRL::ComPtr<IAgileObject> checkIfAgile; 281 HRESULT hr = reinterpret_cast<IUnknown*>(_object)->QueryInterface(__uuidof(IAgileObject), &checkIfAgile); 282 #endif 283 if (hr == S_OK) 284 { 285 auto pThis = const_cast<Agile*>(this); 286 #if _MSC_VER >= 1800 287 pThis->_agileState = AgileState::AgilePointer; 288 #endif 289 pThis->_contextToken = 0; 290 pThis->_contextCallback = nullptr; 291 return _object; 292 } 293 #if _MSC_VER >= 1800 294 else 295 { 296 auto pThis = const_cast<Agile*>(this); 297 pThis->_agileState = AgileState::NonAgilePointer; 298 } 299 #endif 300 } 301 302 #if _MSC_VER < 1800 303 // Different context and holding on to a non agile object 304 // Do the costly work of getting a proxy 305 TypeT localObject; 306 __abi_ThrowIfFailed(Details::GetProxy(_object, _contextCallback.Get(), &localObject)); 307 #endif 308 return localObject; 309 } 310 GetAddressOf()311 TypeT* GetAddressOf() throw() 312 { 313 Release(); 314 CaptureContext(); 315 return &_object; 316 } 317 GetAddressOfForInOut()318 TypeT* GetAddressOfForInOut() throw() 319 { 320 CaptureContext(); 321 return &_object; 322 } 323 operator ->() const324 TypeT operator->() const throw() 325 { 326 return Get(); 327 } 328 operator =(nullptr_t)329 Agile& operator=(nullptr_t) throw() 330 { 331 Release(); 332 return *this; 333 } 334 operator =(TypeT object)335 Agile& operator=(TypeT object) throw() 336 { 337 Agile(object).Swap(*this); 338 return *this; 339 } 340 operator =(Agile object)341 Agile& operator=(Agile object) throw() 342 { 343 // parameter is by copy which gets pointer valid for current context 344 object.Swap(*this); 345 return *this; 346 } 347 348 #if _MSC_VER < 1800 operator =(IUnknown * lp)349 Agile& operator=(IUnknown* lp) throw() 350 { 351 // bump ref count 352 ::Microsoft::WRL::ComPtr<IUnknown> spObject(lp); 353 354 // put it into Platform Object 355 Platform::Object object; 356 *(IUnknown**)(&object) = spObject.Detach(); 357 358 SetObject(object); 359 return *this; 360 } 361 #endif 362 Swap(Agile & object)363 void Swap(Agile& object) 364 { 365 std::swap(_object, object._object); 366 std::swap(_contextCallback, object._contextCallback); 367 std::swap(_contextToken, object._contextToken); 368 #if _MSC_VER >= 1800 369 std::swap(_agileState, object._agileState); 370 #endif 371 } 372 373 // Release the interface and set to NULL Release()374 void Release() throw() 375 { 376 if (_object) 377 { 378 // Cast to IInspectable (no QI) 379 IUnknown* pObject = *(IUnknown**)(&_object); 380 // Set * to null without release 381 *(IUnknown**)(&_object) = nullptr; 382 383 ULONG_PTR currentContextToken; 384 __abi_ThrowIfFailed(CoGetContextToken(¤tContextToken)); 385 if (_contextToken == 0 || _contextCallback == nullptr || _contextToken == currentContextToken) 386 { 387 pObject->Release(); 388 } 389 else 390 { 391 Details::ReleaseInContext(pObject, _contextCallback.Get()); 392 } 393 _contextCallback = nullptr; 394 _contextToken = 0; 395 #if _MSC_VER >= 1800 396 _agileState = AgileState::Unknown; 397 #endif 398 } 399 } 400 operator ==(nullptr_t) const401 bool operator==(nullptr_t) const throw() 402 { 403 return _object == nullptr; 404 } 405 operator ==(const Agile & other) const406 bool operator==(const Agile& other) const throw() 407 { 408 return _object == other._object && _contextToken == other._contextToken; 409 } 410 operator <(const Agile & other) const411 bool operator<(const Agile& other) const throw() 412 { 413 if (reinterpret_cast<void*>(_object) < reinterpret_cast<void*>(other._object)) 414 { 415 return true; 416 } 417 418 return _object == other._object && _contextToken < other._contextToken; 419 } 420 }; 421 422 template <typename T> 423 class Agile<T, false> 424 { 425 static_assert(__is_win_class(typename Details::AgileTypeHelper<T>::type) || __is_win_interface(typename Details::AgileTypeHelper<T>::type), "Agile can only be used with ref class or interface class types"); 426 typename typedef Details::AgileTypeHelper<T>::agileMemberType TypeT; 427 TypeT _object; 428 429 public: Agile()430 Agile() throw() : _object(nullptr) 431 { 432 } 433 Agile(nullptr_t)434 Agile(nullptr_t) throw() : _object(nullptr) 435 { 436 } 437 Agile(TypeT object)438 explicit Agile(TypeT object) throw() : _object(object) 439 { 440 } 441 Agile(const Agile & object)442 Agile(const Agile& object) throw() : _object(object._object) 443 { 444 } 445 Agile(Agile && object)446 Agile(Agile&& object) throw() : _object(nullptr) 447 { 448 Swap(object); 449 } 450 ~Agile()451 ~Agile() throw() 452 { 453 Release(); 454 } 455 Get() const456 TypeT Get() const 457 { 458 return _object; 459 } 460 GetAddressOf()461 TypeT* GetAddressOf() throw() 462 { 463 Release(); 464 return &_object; 465 } 466 GetAddressOfForInOut()467 TypeT* GetAddressOfForInOut() throw() 468 { 469 return &_object; 470 } 471 operator ->() const472 TypeT operator->() const throw() 473 { 474 return Get(); 475 } 476 operator =(nullptr_t)477 Agile& operator=(nullptr_t) throw() 478 { 479 Release(); 480 return *this; 481 } 482 operator =(TypeT object)483 Agile& operator=(TypeT object) throw() 484 { 485 if (_object != object) 486 { 487 _object = object; 488 } 489 return *this; 490 } 491 operator =(Agile object)492 Agile& operator=(Agile object) throw() 493 { 494 object.Swap(*this); 495 return *this; 496 } 497 498 #if _MSC_VER < 1800 operator =(IUnknown * lp)499 Agile& operator=(IUnknown* lp) throw() 500 { 501 Release(); 502 // bump ref count 503 ::Microsoft::WRL::ComPtr<IUnknown> spObject(lp); 504 505 // put it into Platform Object 506 Platform::Object object; 507 *(IUnknown**)(&object) = spObject.Detach(); 508 509 _object = object; 510 return *this; 511 } 512 #endif 513 514 // Release the interface and set to NULL Release()515 void Release() throw() 516 { 517 _object = nullptr; 518 } 519 Swap(Agile & object)520 void Swap(Agile& object) 521 { 522 std::swap(_object, object._object); 523 } 524 operator ==(nullptr_t) const525 bool operator==(nullptr_t) const throw() 526 { 527 return _object == nullptr; 528 } 529 operator ==(const Agile & other) const530 bool operator==(const Agile& other) const throw() 531 { 532 return _object == other._object; 533 } 534 operator <(const Agile & other) const535 bool operator<(const Agile& other) const throw() 536 { 537 return reinterpret_cast<void*>(_object) < reinterpret_cast<void*>(other._object); 538 } 539 }; 540 541 #pragma warning(pop) 542 543 template<class U> operator ==(nullptr_t,const Agile<U> & a)544 bool operator==(nullptr_t, const Agile<U>& a) throw() 545 { 546 return a == nullptr; 547 } 548 549 template<class U> operator !=(const Agile<U> & a,nullptr_t)550 bool operator!=(const Agile<U>& a, nullptr_t) throw() 551 { 552 return !(a == nullptr); 553 } 554 555 template<class U> operator !=(nullptr_t,const Agile<U> & a)556 bool operator!=(nullptr_t, const Agile<U>& a) throw() 557 { 558 return !(a == nullptr); 559 } 560 561 template<class U> operator !=(const Agile<U> & a,const Agile<U> & b)562 bool operator!=(const Agile<U>& a, const Agile<U>& b) throw() 563 { 564 return !(a == b); 565 } 566 567 568 #endif // _PLATFORM_AGILE_H_ 569