From b9d5f3f6e9323743b7e0302e490d51d3f178a078 Mon Sep 17 00:00:00 2001 From: GregoryMorse Date: Sat, 14 Dec 2013 16:53:30 +0800 Subject: [PATCH] Add support for WinRT in the MF capture framework by removing the disallowed calls to enumerate devices and create a sample grabber sink and adding framework for the MediaCapture interface and a custom sink which interfaces with the sample grabber callback interface. The change requires discussion for making it completely functional as redundancy is required given that if the source is a video file, the old code pathways must be used. Otherwise all IMFMediaSession, IMFMediaSource, and IMFActivate code must use a MediaCapture code path and all sink code must use the CMediaSink custom sink. Support for the custom sink is extended to non-WinRT not for compatibility as Windows Vista client is a minimum regardless, but because it offers more flexibility, could be faster and is able to be used as an optionally different code path during sink creation based on a future configuration parameter. My discussion and proposal to finish this change: Devices are so easily enumerated through WinRT Windows.Devices namespace that wrapping the calls in a library is quite a chore for little benefit though to get the various modes and formats could still be a worthwhile project. For now conditional compilation to remove videodevices and any offending non-video file related activity in videodevice. In my opinion, this is a different , far less fundamental and important change which can possibly be done as a future project and also much more easily implemented in C++/CX. ImageGrabber has the IMFSampleGrabberSinkCallback replaced with a base class (SharedSampleGrabber) which also be is base class for ImageGrabberRT. This change is necessary as the custom sink does not require a thread to pump events which is done through MediaCapture already. IMFSampleGrabberSinkCallback is the common element between both models and that piece can be shared. Initializing the new ImageGrabberRT is as simple as passing an already initialized MediaCapture object and any video format/encoding parameters. The concurrency event is necessary to wait for completion and is the way the underlying, IAsyncAction wrappers in the task library work as well. Native WIN32 event objects would be an option if HAVE_CONCURRENCY is not defined. I could even imagine doing it with sleep/thread yield and InterlockedCompareExchange yet I am not enthusiastic about that approach either. Since there is a specific compiler HAVE_ for concurrency, I do not like pulling it in though I think for WinRT it is safe to say we will always have it available though should probably conditionally compile with the Interlocked option as WIN32 events would require HAVE_WIN32. It looks like C++/CX cannot be used for the IMediaExtension sink (which should not be a problem) as using COM objects requires WRL and though deriving from IMediaExtension can be done, there is little purpose without COM. Objects from C++/CX can be swapped to interact with objects from native C++ as Inspectable* can reinterpret_cast to the ref object IInspectable^ and vice-versa. A solution to the COM class with C++/CX would be great so we could have dual support. Also without #define for every WRL object in use, the code will get quite muddy given that the */^ would need to be ifdef'd everywhere. Update cap_msmf.cpp Fixed bugs and completed the change. I believe the new classes need to be moved to a header file as the file has become to large and more classes need to be added for handling all the asynchronous problems (one wrapping IAsyncAction in a task and another for making a task out of IAsyncAction). Unfortunately, blocking on the UI thread is not an option in WinRT so a synchronous architecture is considered "illegal" by Microsoft's standards even if implementable (C++/CX ppltasks library throws errors if you try it). Worse, either by design or a bug in the MF MediaCapture class with Custom Sinks causes a crash if stop/start previewing without reinitializing (spPreferredPreviewMediaType is fatally nulled). After decompiling Windows.Media.dll, I worked around this in my own projects by using an activate-able custom sink ID which strangely assigns 1 to this pointer allowing it to be reinitialized in what can only be described as a hack by Microsoft. This would add additional overhead to the project to implement especially for static libraries as it requires IDL/DLL exporting followed by manifest declaration. Better to document that it is not supported. Furthermore, an additional class for IMFAttributes should be implemented to make clean architecture for passing around attributes as opposed to directly calling non-COM interface calls on the objects and making use of SetProperties which would also be a set up for an object that uses the RuntimeClass activation ID. The remaining changes are not difficult and will be complete soon along with debug tracing messages. Update and rename cap_msmf.h to cap_msmf.hpp Successful test - samples are grabbed Library updated and cleaned up with comments, marshaling, exceptions and linker settings Fixed trailing whitespace VS 2013 support and cleanup consistency plus C++/CX new object fixed VS 2013 Update 2 library bug fix integrated Various minor cleanup Create agile_wrl.h a-wi's changes integrated Update cap_msmf.hpp Update cap_msmf.cpp Regression test fixes and simplifications --- modules/highgui/CMakeLists.txt | 4 + modules/highgui/src/agile_wrl.h | 568 ++ modules/highgui/src/cap_msmf.cpp | 1551 ++++- modules/highgui/src/cap_msmf.hpp | 3279 +++++++++ modules/highgui/src/ppltasks_winrt.h | 9466 ++++++++++++++++++++++++++ 5 files changed, 14533 insertions(+), 335 deletions(-) create mode 100644 modules/highgui/src/agile_wrl.h create mode 100644 modules/highgui/src/cap_msmf.hpp create mode 100644 modules/highgui/src/ppltasks_winrt.h diff --git a/modules/highgui/CMakeLists.txt b/modules/highgui/CMakeLists.txt index 7d2547bcff..8c5e947448 100644 --- a/modules/highgui/CMakeLists.txt +++ b/modules/highgui/CMakeLists.txt @@ -9,6 +9,10 @@ ocv_add_module(highgui opencv_imgproc OPTIONAL opencv_androidcamera) ocv_clear_vars(GRFMT_LIBS) +if(HAVE_WINRT_CX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /ZW") +endif() + if(HAVE_PNG OR HAVE_TIFF OR HAVE_OPENEXR) ocv_include_directories(${ZLIB_INCLUDE_DIR}) list(APPEND GRFMT_LIBS ${ZLIB_LIBRARIES}) diff --git a/modules/highgui/src/agile_wrl.h b/modules/highgui/src/agile_wrl.h new file mode 100644 index 0000000000..99fbf41856 --- /dev/null +++ b/modules/highgui/src/agile_wrl.h @@ -0,0 +1,568 @@ +// +// Copyright (C) Microsoft Corporation +// All rights reserved. +// Modified for native C++ WRL support by Gregory Morse +// +// 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 + +template class Agile; + +template +struct UnwrapAgile +{ + static const bool _IsAgile = false; +}; +template +struct UnwrapAgile> +{ + static const bool _IsAgile = true; +}; +template +struct UnwrapAgile> +{ + static const bool _IsAgile = true; +}; + +#define IS_AGILE(T) UnwrapAgile::_IsAgile + +#define __is_winrt_agile(T) (std::is_same::value || std::is_base_of::value || std::is_base_of::value) //derived from Microsoft::WRL::FtmBase or IAgileObject + +#define __is_win_interface(T) (std::is_base_of::value || std::is_base_of::value) //derived from IUnknown or IInspectable + +#define __is_win_class(T) (std::is_same::value || std::is_base_of::value) //derived from Microsoft::WRL::RuntimeClass or HSTRING + + namespace Details + { + IUnknown* __stdcall GetObjectContext(); + HRESULT __stdcall GetProxyImpl(IUnknown*, REFIID, IUnknown*, IUnknown**); + HRESULT __stdcall ReleaseInContextImpl(IUnknown*, IUnknown*); + + template +#if _MSC_VER >= 1800 + __declspec(no_refcount) inline HRESULT GetProxy(T *ObjectIn, IUnknown *ContextCallBack, T **Proxy) +#else + inline HRESULT GetProxy(T *ObjectIn, IUnknown *ContextCallBack, T **Proxy) +#endif + { +#if _MSC_VER >= 1800 + return GetProxyImpl(*reinterpret_cast(&ObjectIn), __uuidof(T*), ContextCallBack, reinterpret_cast(Proxy)); +#else + return GetProxyImpl(*reinterpret_cast(&const_cast(ObjectIn)), __uuidof(T*), ContextCallBack, reinterpret_cast(Proxy)); +#endif + } + + 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 _contextCallback; + ULONG_PTR _contextToken; + +#if _MSC_VER >= 1800 + enum class AgileState + { + NonAgilePointer = 0, + AgilePointer = 1, + Unknown = 2 + }; + AgileState _agileState; +#endif + + 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 checkIfAgile; + HRESULT hr = reinterpret_cast(object)->QueryInterface(__uuidof(IAgileObject), &checkIfAgile); + // Don't Capture context if object is agile + if (hr != S_OK) + { +#if _MSC_VER >= 1800 + _agileState = AgileState::NonAgilePointer; +#endif + CaptureContext(); + } +#if _MSC_VER >= 1800 + else + { + _agileState = AgileState::AgilePointer; + } +#endif + } + _object = object; + } + + public: + Agile() throw() : _object(nullptr), _contextToken(0) +#if _MSC_VER >= 1800 + , _agileState(AgileState::Unknown) +#endif + { + } + + Agile(nullptr_t) throw() : _object(nullptr), _contextToken(0) +#if _MSC_VER >= 1800 + , _agileState(AgileState::Unknown) +#endif + { + } + + explicit Agile(TypeT object) throw() : _object(nullptr), _contextToken(0) +#if _MSC_VER >= 1800 + , _agileState(AgileState::Unknown) +#endif + { + // Assumes that the source object is from the current context + SetObject(object); + } + + Agile(const Agile& object) throw() : _object(nullptr), _contextToken(0) +#if _MSC_VER >= 1800 + , _agileState(AgileState::Unknown) +#endif + { + // Get returns pointer valid for current context + SetObject(object.Get()); + } + + Agile(Agile&& object) throw() : _object(nullptr), _contextToken(0) +#if _MSC_VER >= 1800 + , _agileState(AgileState::Unknown) +#endif + { + // Assumes that the source object is from the current context + Swap(object); + } + + ~Agile() throw() + { + Release(); + } + + TypeT Get() const + { + // Agile object, no proxy required +#if _MSC_VER >= 1800 + if (_agileState == AgileState::AgilePointer || _object == nullptr) +#else + if (_contextToken == 0 || _contextCallback == nullptr || _object == nullptr) +#endif + { + return _object; + } + + // Do the check for same context + ULONG_PTR currentContextToken; + __abi_ThrowIfFailed(CoGetContextToken(¤tContextToken)); + if (currentContextToken == _contextToken) + { + return _object; + } + +#if _MSC_VER >= 1800 + // 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) +#else + // Object is agile if it implements IAgileObject + // GetAddressOf captures the context with out knowing the type of object that it will hold + if (_object != nullptr) +#endif + { +#if _MSC_VER >= 1800 + // 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 checkIfAgile; + HRESULT hr = reinterpret_cast(localObject)->QueryInterface(__uuidof(IAgileObject), &checkIfAgile); +#else + ::Microsoft::WRL::ComPtr checkIfAgile; + HRESULT hr = reinterpret_cast(_object)->QueryInterface(__uuidof(IAgileObject), &checkIfAgile); +#endif + if (hr == S_OK) + { + auto pThis = const_cast(this); +#if _MSC_VER >= 1800 + pThis->_agileState = AgileState::AgilePointer; +#endif + pThis->_contextToken = 0; + pThis->_contextCallback = nullptr; + return _object; + } +#if _MSC_VER >= 1800 + else + { + auto pThis = const_cast(this); + pThis->_agileState = AgileState::NonAgilePointer; + } +#endif + } + +#if _MSC_VER < 1800 + // 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)); +#endif + 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; + } + +#if _MSC_VER < 1800 + Agile& operator=(IUnknown* lp) throw() + { + // bump ref count + ::Microsoft::WRL::ComPtr spObject(lp); + + // put it into Platform Object + Platform::Object object; + *(IUnknown**)(&object) = spObject.Detach(); + + SetObject(object); + return *this; + } +#endif + + void Swap(Agile& object) + { + std::swap(_object, object._object); + std::swap(_contextCallback, object._contextCallback); + std::swap(_contextToken, object._contextToken); +#if _MSC_VER >= 1800 + std::swap(_agileState, object._agileState); +#endif + } + + // 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; +#if _MSC_VER >= 1800 + _agileState = AgileState::Unknown; +#endif + } + } + + 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; + } + +#if _MSC_VER < 1800 + Agile& operator=(IUnknown* lp) throw() + { + Release(); + // bump ref count + ::Microsoft::WRL::ComPtr spObject(lp); + + // put it into Platform Object + Platform::Object object; + *(IUnknown**)(&object) = spObject.Detach(); + + _object = object; + return *this; + } +#endif + + // 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); + } + + +#endif // _PLATFORM_AGILE_H_ diff --git a/modules/highgui/src/cap_msmf.cpp b/modules/highgui/src/cap_msmf.cpp index df52f04bdf..d08829eb37 100644 --- a/modules/highgui/src/cap_msmf.cpp +++ b/modules/highgui/src/cap_msmf.cpp @@ -47,12 +47,21 @@ Originaly licensed under The Code Project Open License (CPOL) 1.02: http://www.codeproject.com/info/cpol10.aspx */ +//require Windows 8 for some of the formats defined otherwise could baseline on lower version +#if WINVER < _WIN32_WINNT_WIN7 +#undef WINVER +#define WINVER _WIN32_WINNT_WIN7 +#endif +#if defined _MSC_VER && _MSC_VER >= 1600 + #define HAVE_CONCURRENCY +#endif #include #include #include #include #include #include +#include #include #include #include @@ -71,13 +80,192 @@ #pragma comment(lib, "Mfreadwrite") #pragma comment(lib, "MinCore_Downlevel") -// for ComPtr usage +#include + +#ifdef HAVE_WINRT + // for ComPtr usage #include -using namespace Microsoft::WRL; +#ifdef __cplusplus_winrt +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_CONCURRENCY +#include +#ifndef __cplusplus_winrt +__declspec(noreturn) void __stdcall __abi_WinRTraiseException(long); + +inline void __abi_ThrowIfFailed(long __hrArg) +{ + if (__hrArg < 0) + { + __abi_WinRTraiseException(__hrArg); + } +} + +struct Guid +{ +public: + Guid(); + Guid(__rcGUID_t); + operator ::__rcGUID_t(); + bool Equals(Guid __guidArg); + bool Equals(__rcGUID_t __guidArg); + Guid(unsigned int __aArg, unsigned short __bArg, unsigned short __cArg, unsigned __int8 __dArg, + unsigned __int8 __eArg, unsigned __int8 __fArg, unsigned __int8 __gArg, unsigned __int8 __hArg, + unsigned __int8 __iArg, unsigned __int8 __jArg, unsigned __int8 __kArg); + Guid(unsigned int __aArg, unsigned short __bArg, unsigned short __cArg, const unsigned __int8* __dArg); +private: + unsigned long __a; + unsigned short __b; + unsigned short __c; + unsigned char __d; + unsigned char __e; + unsigned char __f; + unsigned char __g; + unsigned char __h; + unsigned char __i; + unsigned char __j; + unsigned char __k; +}; + +static_assert(sizeof(Guid) == sizeof(::_GUID), "Incorect size for Guid"); +static_assert(sizeof(__rcGUID_t) == sizeof(::_GUID), "Incorect size for __rcGUID_t"); + +//////////////////////////////////////////////////////////////////////////////// +inline Guid::Guid() : __a(0), __b(0), __c(0), __d(0), __e(0), __f(0), __g(0), __h(0), __i(0), __j(0), __k(0) +{ +} + +inline Guid::Guid(__rcGUID_t __guid) : +__a(reinterpret_cast(__guid).Data1), +__b(reinterpret_cast(__guid).Data2), +__c(reinterpret_cast(__guid).Data3), +__d(reinterpret_cast(__guid).Data4[0]), +__e(reinterpret_cast(__guid).Data4[1]), +__f(reinterpret_cast(__guid).Data4[2]), +__g(reinterpret_cast(__guid).Data4[3]), +__h(reinterpret_cast(__guid).Data4[4]), +__i(reinterpret_cast(__guid).Data4[5]), +__j(reinterpret_cast(__guid).Data4[6]), +__k(reinterpret_cast(__guid).Data4[7]) +{ +} + +inline Guid::operator ::__rcGUID_t() +{ + return reinterpret_cast<__rcGUID_t>(*this); +} + +inline bool Guid::Equals(Guid __guidArg) +{ + return *this == __guidArg; +} + +inline bool Guid::Equals(__rcGUID_t __guidArg) +{ + return *this == static_cast< Guid>(__guidArg); +} + +inline bool operator==(Guid __aArg, Guid __bArg) +{ + auto __a = reinterpret_cast(&__aArg); + auto __b = reinterpret_cast(&__bArg); + + return (__a[0] == __b[0] && __a[1] == __b[1] && __a[2] == __b[2] && __a[3] == __b[3]); +} + +inline bool operator!=(Guid __aArg, Guid __bArg) +{ + return !(__aArg == __bArg); +} + +inline bool operator<(Guid __aArg, Guid __bArg) +{ + auto __a = reinterpret_cast(&__aArg); + auto __b = reinterpret_cast(&__bArg); + + if (__a[0] != __b[0]) + { + return __a[0] < __b[0]; + } + + if (__a[1] != __b[1]) + { + return __a[1] < __b[1]; + } + + if (__a[2] != __b[2]) + { + return __a[2] < __b[2]; + } + + if (__a[3] != __b[3]) + { + return __a[3] < __b[3]; + } + + return false; +} + +inline Guid::Guid(unsigned int __aArg, unsigned short __bArg, unsigned short __cArg, unsigned __int8 __dArg, + unsigned __int8 __eArg, unsigned __int8 __fArg, unsigned __int8 __gArg, unsigned __int8 __hArg, + unsigned __int8 __iArg, unsigned __int8 __jArg, unsigned __int8 __kArg) : + __a(__aArg), __b(__bArg), __c(__cArg), __d(__dArg), __e(__eArg), __f(__fArg), __g(__gArg), __h(__hArg), __i(__iArg), __j(__jArg), __k(__kArg) +{ +} + +inline Guid::Guid(unsigned int __aArg, unsigned short __bArg, unsigned short __cArg, const unsigned __int8 __dArg[8]) : +__a(__aArg), __b(__bArg), __c(__cArg) +{ + __d = __dArg[0]; + __e = __dArg[1]; + __f = __dArg[2]; + __g = __dArg[3]; + __h = __dArg[4]; + __i = __dArg[5]; + __j = __dArg[6]; + __k = __dArg[7]; +} + +__declspec(selectany) Guid __winrt_GUID_NULL(0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + +// +//// Don't want to define the real IUnknown from unknown.h here. That would means if the user has +//// any broken code that uses it, compile errors will take the form of e.g.: +//// predefined C++ WinRT types (compiler internal)(41) : see declaration of 'IUnknown::QueryInterface' +//// This is not helpful. If they use IUnknown, we still need to point them to the actual unknown.h so +//// that they can see the original definition. +//// +//// For WinRT, we'll instead have a parallel COM interface hierarchy for basic interfaces starting with _. +//// The type mismatch is not an issue. COM passes types through GUID / void* combos - the original type +//// doesn't come into play unless the user static_casts an implementation type to one of these, but +//// the WinRT implementation types are hidden. +__interface __declspec(uuid("00000000-0000-0000-C000-000000000046")) __abi_IUnknown +{ +public: + virtual long __stdcall __abi_QueryInterface(Guid&, void**) = 0; + virtual unsigned long __stdcall __abi_AddRef() = 0; + virtual unsigned long __stdcall __abi_Release() = 0; +}; +#endif +#include "ppltasks_winrt.h" +#endif +#else +#include +#endif struct IMFMediaType; +#ifndef HAVE_WINRT struct IMFActivate; struct IMFMediaSource; +#endif struct IMFAttributes; namespace @@ -92,18 +280,25 @@ template void SafeRelease(T **ppT) } } -/// Class for printing info into consol -class DebugPrintOut +#ifdef _DEBUG +/// Class for printing info into console +class DPO { public: - ~DebugPrintOut(void); - static DebugPrintOut& getInstance(); + ~DPO(void); + static DPO& getInstance(); void printOut(const wchar_t *format, ...); void setVerbose(bool state); bool verbose; private: - DebugPrintOut(void); + DPO(void); }; +#define DebugPrintOut(...) DPO::getInstance().printOut(__VA_ARGS__) +#else +#define DebugPrintOut(...) void() +#endif + +#include "cap_msmf.hpp" // Structure for collecting info about types of video, which are supported by current video device struct MediaType @@ -171,57 +366,107 @@ private: RawImage(unsigned int size); }; -// Class for grabbing image from video stream -class ImageGrabber : public IMFSampleGrabberSinkCallback +class ImageGrabberCallback : public IMFSampleGrabberSinkCallback { public: - ~ImageGrabber(void); - HRESULT initImageGrabber(IMFMediaSource *pSource, GUID VideoFormat); - HRESULT startGrabbing(void); void pauseGrabbing(); void resumeGrabbing(); - void stopGrabbing(); RawImage *getRawImage(); - // Function of creation of the instance of the class - static HRESULT CreateInstance(ImageGrabber **ppIG, unsigned int deviceID, bool synchronous = false); + // IMFClockStateSink methods + STDMETHODIMP OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset); + STDMETHODIMP OnClockStop(MFTIME hnsSystemTime); + STDMETHODIMP OnClockPause(MFTIME hnsSystemTime); + STDMETHODIMP OnClockRestart(MFTIME hnsSystemTime); + STDMETHODIMP OnClockSetRate(MFTIME hnsSystemTime, float flRate); + // IMFSampleGrabberSinkCallback methods + STDMETHODIMP OnSetPresentationClock(IMFPresentationClock* pClock); + STDMETHODIMP OnProcessSample(REFGUID guidMajorMediaType, DWORD dwSampleFlags, + LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer, + DWORD dwSampleSize); + STDMETHODIMP OnShutdown(); const HANDLE ig_hFrameReady; const HANDLE ig_hFrameGrabbed; const HANDLE ig_hFinish; - -private: +protected: + ImageGrabberCallback(bool synchronous); bool ig_RIE; bool ig_Close; bool ig_Synchronous; long m_cRef; + + RawImage *ig_RIFirst; + RawImage *ig_RISecond; + RawImage *ig_RIOut; +private: + ImageGrabberCallback& operator=(const ImageGrabberCallback&); // Declared to fix compilation warning. + }; + +#ifdef HAVE_WINRT +extern const __declspec(selectany) WCHAR RuntimeClass_CV_ImageGrabberWinRT[] = L"cv.ImageGrabberWinRT"; + +class ImageGrabberWinRT : + public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::RuntimeClassType::WinRtClassicComMix>, + IMFSampleGrabberSinkCallback>, public ImageGrabberCallback +{ + InspectableClass(RuntimeClass_CV_ImageGrabberWinRT, BaseTrust) +public: + ImageGrabberWinRT(bool synchronous); + ~ImageGrabberWinRT(void); + + HRESULT initImageGrabber(MAKE_WRL_REF(_MediaCapture) pSource, + GUID VideoFormat); + HRESULT startGrabbing(MAKE_WRL_REF(_AsyncAction)* action); + HRESULT stopGrabbing(MAKE_WRL_REF(_AsyncAction)* action); + // IMFClockStateSink methods + STDMETHODIMP OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset) { return ImageGrabberCallback::OnClockStart(hnsSystemTime, llClockStartOffset); } + STDMETHODIMP OnClockStop(MFTIME hnsSystemTime) { return ImageGrabberCallback::OnClockStop(hnsSystemTime); } + STDMETHODIMP OnClockPause(MFTIME hnsSystemTime) { return ImageGrabberCallback::OnClockPause(hnsSystemTime); } + STDMETHODIMP OnClockRestart(MFTIME hnsSystemTime) { return ImageGrabberCallback::OnClockRestart(hnsSystemTime); } + STDMETHODIMP OnClockSetRate(MFTIME hnsSystemTime, float flRate) { return ImageGrabberCallback::OnClockSetRate(hnsSystemTime, flRate); } + // IMFSampleGrabberSinkCallback methods + STDMETHODIMP OnSetPresentationClock(IMFPresentationClock* pClock) { return ImageGrabberCallback::OnSetPresentationClock(pClock); } + STDMETHODIMP OnProcessSample(REFGUID guidMajorMediaType, DWORD dwSampleFlags, + LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer, + DWORD dwSampleSize) { return ImageGrabberCallback::OnProcessSample(guidMajorMediaType, dwSampleFlags, llSampleTime, llSampleDuration, pSampleBuffer, dwSampleSize); } + STDMETHODIMP OnShutdown() { return ImageGrabberCallback::OnShutdown(); } + // Function of creation of the instance of the class + static HRESULT CreateInstance(ImageGrabberWinRT **ppIG, bool synchronous = false); +private: + MAKE_WRL_AGILE_REF(_MediaCapture) ig_pMedCapSource; + MediaSink* ig_pMediaSink; +}; +#endif + +// Class for grabbing image from video stream +class ImageGrabber : public ImageGrabberCallback +{ +public: + ~ImageGrabber(void); + HRESULT initImageGrabber(IMFMediaSource *pSource); + HRESULT startGrabbing(void); + void stopGrabbing(); + // IUnknown methods + STDMETHODIMP QueryInterface(REFIID iid, void** ppv); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + // Function of creation of the instance of the class + static HRESULT CreateInstance(ImageGrabber **ppIG, unsigned int deviceID, bool synchronous = false); + +private: unsigned int ig_DeviceID; + IMFMediaSource *ig_pSource; IMFMediaSession *ig_pSession; IMFTopology *ig_pTopology; - RawImage *ig_RIFirst; - RawImage *ig_RISecond; - RawImage *ig_RIOut; ImageGrabber(unsigned int deviceID, bool synchronous); HRESULT CreateTopology(IMFMediaSource *pSource, IMFActivate *pSinkActivate, IMFTopology **ppTopo); HRESULT AddSourceNode(IMFTopology *pTopology, IMFMediaSource *pSource, IMFPresentationDescriptor *pPD, IMFStreamDescriptor *pSD, IMFTopologyNode **ppNode); HRESULT AddOutputNode(IMFTopology *pTopology, IMFActivate *pActivate, DWORD dwId, IMFTopologyNode **ppNode); - // IUnknown methods - STDMETHODIMP QueryInterface(REFIID iid, void** ppv); - STDMETHODIMP_(ULONG) AddRef(); - STDMETHODIMP_(ULONG) Release(); - // IMFClockStateSink methods - STDMETHODIMP OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset); - STDMETHODIMP OnClockStop(MFTIME hnsSystemTime); - STDMETHODIMP OnClockPause(MFTIME hnsSystemTime); - STDMETHODIMP OnClockRestart(MFTIME hnsSystemTime); - STDMETHODIMP OnClockSetRate(MFTIME hnsSystemTime, float flRate); - // IMFSampleGrabberSinkCallback methods - STDMETHODIMP OnSetPresentationClock(IMFPresentationClock* pClock); - STDMETHODIMP OnProcessSample(REFGUID guidMajorMediaType, DWORD dwSampleFlags, - LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer, - DWORD dwSampleSize); - STDMETHODIMP OnShutdown(); + + ImageGrabber& operator=(const ImageGrabber&); // Declared to fix comiplation error. }; /// Class for controlling of thread of the grabbing raw data from video device @@ -298,11 +543,22 @@ public: CamParametrs getParametrs(); void setParametrs(CamParametrs parametrs); void setEmergencyStopEvent(void *userData, void(*func)(int, void *)); +#ifdef HAVE_WINRT + long readInfoOfDevice(MAKE_WRL_REF(_IDeviceInformation) pDevice, unsigned int Num); + void waitForDevice() + { +#ifdef HAVE_CONCURRENCY + vd_pAction.wait(); +#endif + } +#else long readInfoOfDevice(IMFActivate *pActivate, unsigned int Num); +#endif wchar_t *getName(); int getCountFormats(); unsigned int getWidth(); unsigned int getHeight(); + unsigned int getFrameRate() const; MediaType getFormat(unsigned int id); bool setupDevice(unsigned int w, unsigned int h, unsigned int idealFramerate = 0); bool setupDevice(unsigned int id); @@ -324,20 +580,36 @@ private: CamParametrs vd_PrevParametrs; unsigned int vd_Width; unsigned int vd_Height; + unsigned int vd_FrameRate; unsigned int vd_CurrentNumber; bool vd_IsSetuped; std::map vd_CaptureFormats; std::vector vd_CurrentFormats; IMFMediaSource *vd_pSource; +#ifdef HAVE_WINRT + MAKE_WRL_AGILE_REF(_MediaCapture) vd_pMedCap; + EventRegistrationToken vd_cookie; + ImageGrabberWinRT *vd_pImGr; + DEFINE_TASK vd_pAction; +#endif emergensyStopEventCallback vd_func; void *vd_userData; HRESULT enumerateCaptureFormats(IMFMediaSource *pSource); long setDeviceFormat(IMFMediaSource *pSource, unsigned long dwFormatIndex); void buildLibraryofTypes(); int findType(unsigned int size, unsigned int frameRate = 0); +#ifdef HAVE_WINRT + HRESULT enumerateCaptureFormats(MAKE_WRL_REF(_MediaCapture) pSource); + long setDeviceFormat(MAKE_WRL_REF(_MediaCapture) pSource, unsigned long dwFormatIndex, MAKE_WRL_REF(_AsyncAction)* pAction); + long resetDevice(MAKE_WRL_REF(_IDeviceInformation) pDevice); +#ifdef HAVE_CONCURRENCY + long checkDevice(_DeviceClass devClass, DEFINE_TASK* pTask, MAKE_WRL_REF(_IDeviceInformation)* ppDevice); +#endif +#else long resetDevice(IMFActivate *pActivate); - long initDevice(); long checkDevice(IMFAttributes *pAttributes, IMFActivate **pDevice); +#endif + long initDevice(); }; /// Class for managing of list of video devices @@ -345,13 +617,25 @@ class videoDevices { public: ~videoDevices(void); +#ifdef HAVE_WINRT + long initDevices(_DeviceClass devClass); + void waitInit() { +#ifdef HAVE_CONCURRENCY + vds_enumTask.wait(); +#endif + } +#else long initDevices(IMFAttributes *pAttributes); +#endif static videoDevices& getInstance(); videoDevice *getDevice(unsigned int i); unsigned int getCount(); void clearDevices(); private: UINT32 count; +#ifdef HAVE_WINRT + DEFINE_TASK vds_enumTask; +#endif std::vector vds_Devices; videoDevices(void); }; @@ -392,6 +676,8 @@ public: unsigned int getWidth(int deviceID); // Getting height of image, which is getting from videodevice with deviceID unsigned int getHeight(int deviceID); + // Getting frame rate, which is getting from videodevice with deviceID + unsigned int getFrameRate(int deviceID) const; // Getting name of videodevice with deviceID wchar_t *getNameVideoDevice(int deviceID); // Getting interface MediaSource for Media Foundation from videodevice with deviceID @@ -406,14 +692,19 @@ public: bool isDeviceMediaSource(int deviceID); // Checking of using Raw Data of pixels from videodevice with deviceID bool isDeviceRawDataSource(int deviceID); +#ifdef _DEBUG // Setting of the state of outprinting info in console static void setVerbose(bool state); +#endif // Initialization of video device with deviceID by media type with id bool setupDevice(int deviceID, unsigned int id = 0); // Initialization of video device with deviceID by wisth w, height h and fps idealFramerate bool setupDevice(int deviceID, unsigned int w, unsigned int h, unsigned int idealFramerate = 30); // Checking of recivig of new frame from video device with deviceID bool isFrameNew(int deviceID); +#ifdef HAVE_WINRT + void waitForDevice(int deviceID); +#endif // Writing of Raw Data pixels from video device with deviceID with correction of RedAndBlue flipping flipRedAndBlue and vertical flipping flipImage bool getPixels(int deviceID, unsigned char * pixels, bool flipRedAndBlue = false, bool flipImage = false); static void processPixels(unsigned char * src, unsigned char * dst, unsigned int width, unsigned int height, unsigned int bpp, bool bRGB, bool bFlip); @@ -423,21 +714,22 @@ private: void updateListOfDevices(); }; -DebugPrintOut::DebugPrintOut(void):verbose(true) +#ifdef _DEBUG +DPO::DPO(void):verbose(true) { } -DebugPrintOut::~DebugPrintOut(void) +DPO::~DPO(void) { } -DebugPrintOut& DebugPrintOut::getInstance() +DPO& DPO::getInstance() { - static DebugPrintOut instance; + static DPO instance; return instance; } -void DebugPrintOut::printOut(const wchar_t *format, ...) +void DPO::printOut(const wchar_t *format, ...) { if(verbose) { @@ -445,23 +737,33 @@ void DebugPrintOut::printOut(const wchar_t *format, ...) wchar_t *p = NULL; va_list args; va_start(args, format); - if(wcscmp(format, L"%i")) + if( ::IsDebuggerPresent() ) { - i = va_arg (args, int); + WCHAR szMsg[512]; + ::StringCchVPrintfW(szMsg, sizeof(szMsg)/sizeof(szMsg[0]), format, args); + ::OutputDebugStringW(szMsg); } - if(wcscmp(format, L"%s")) + else { - p = va_arg (args, wchar_t *); + if(wcscmp(format, L"%i")) + { + i = va_arg (args, int); + } + if(wcscmp(format, L"%s")) + { + p = va_arg (args, wchar_t *); + } + wprintf(format, i,p); } - wprintf(format, i,p); va_end (args); } } -void DebugPrintOut::setVerbose(bool state) +void DPO::setVerbose(bool state) { verbose = state; } +#endif LPCWSTR GetGUIDNameConstNew(const GUID& guid); HRESULT GetGUIDNameNew(const GUID& guid, WCHAR **ppwsz); @@ -537,7 +839,7 @@ HRESULT LogAttributeValueByIndexNew(IMFAttributes *pAttr, DWORD index, MediaType hr = GetGUIDNameNew(*var.puuid, &pGuidValName); if (SUCCEEDED(hr)) { - out.MF_MT_AM_FORMAT_TYPE = MF_MT_AM_FORMAT_TYPE; + out.MF_MT_AM_FORMAT_TYPE = *var.puuid; out.pMF_MT_AM_FORMAT_TYPEName = pGuidValName; pGuidValName = NULL; } @@ -547,7 +849,7 @@ HRESULT LogAttributeValueByIndexNew(IMFAttributes *pAttr, DWORD index, MediaType hr = GetGUIDNameNew(*var.puuid, &pGuidValName); if (SUCCEEDED(hr)) { - out.MF_MT_MAJOR_TYPE = MF_MT_MAJOR_TYPE; + out.MF_MT_MAJOR_TYPE = *var.puuid; out.pMF_MT_MAJOR_TYPEName = pGuidValName; pGuidValName = NULL; } @@ -557,7 +859,7 @@ HRESULT LogAttributeValueByIndexNew(IMFAttributes *pAttr, DWORD index, MediaType hr = GetGUIDNameNew(*var.puuid, &pGuidValName); if (SUCCEEDED(hr)) { - out.MF_MT_SUBTYPE = MF_MT_SUBTYPE; + out.MF_MT_SUBTYPE = *var.puuid; out.pMF_MT_SUBTYPEName = pGuidValName; pGuidValName = NULL; } @@ -850,9 +1152,8 @@ FormatReader::FormatReader(void) MediaType FormatReader::Read(IMFMediaType *pType) { UINT32 count = 0; - HRESULT hr = S_OK; MediaType out; - hr = pType->LockStore(); + HRESULT hr = pType->LockStore(); if (FAILED(hr)) { return out; @@ -884,12 +1185,8 @@ FormatReader::~FormatReader(void) #define CHECK_HR(x) if (FAILED(x)) { goto done; } -ImageGrabber::ImageGrabber(unsigned int deviceID, bool synchronous): +ImageGrabberCallback::ImageGrabberCallback(bool synchronous): m_cRef(1), - ig_DeviceID(deviceID), - ig_pSource(NULL), - ig_pSession(NULL), - ig_pTopology(NULL), ig_RIE(true), ig_Close(false), ig_Synchronous(synchronous), @@ -898,6 +1195,14 @@ ImageGrabber::ImageGrabber(unsigned int deviceID, bool synchronous): ig_hFinish(CreateEvent(NULL, TRUE, FALSE, NULL)) {} +ImageGrabber::ImageGrabber(unsigned int deviceID, bool synchronous): + ImageGrabberCallback(synchronous), + ig_DeviceID(deviceID), + ig_pSource(NULL), + ig_pSession(NULL), + ig_pTopology(NULL) +{} + ImageGrabber::~ImageGrabber(void) { if (ig_pSession) @@ -915,20 +1220,168 @@ ImageGrabber::~ImageGrabber(void) SafeRelease(&ig_pSession); SafeRelease(&ig_pTopology); - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); - DPO->printOut(L"IMAGEGRABBER VIDEODEVICE %i: Destroing instance of the ImageGrabber class\n", ig_DeviceID); + DebugPrintOut(L"IMAGEGRABBER VIDEODEVICE %i: Destroying instance of the ImageGrabber class\n", ig_DeviceID); +} + +#ifdef HAVE_WINRT + +ImageGrabberWinRT::ImageGrabberWinRT(bool synchronous): + ImageGrabberCallback(synchronous), + ig_pMediaSink(NULL) +{ + ig_pMedCapSource = nullptr; +} + +ImageGrabberWinRT::~ImageGrabberWinRT(void) +{ + //stop must already be performed and complete by object owner + if (ig_pMediaSink != NULL) { + ((IMFMediaSink*)ig_pMediaSink)->Shutdown(); + } + SafeRelease(&ig_pMediaSink); + RELEASE_AGILE_WRL(ig_pMedCapSource) + + CloseHandle(ig_hFinish); + + if (ig_Synchronous) + { + CloseHandle(ig_hFrameReady); + CloseHandle(ig_hFrameGrabbed); + } + + DebugPrintOut(L"IMAGEGRABBER VIDEODEVICE: Destroying instance of the ImageGrabberWinRT class\n"); +} + +HRESULT ImageGrabberWinRT::initImageGrabber(MAKE_WRL_REF(_MediaCapture) pSource, + GUID VideoFormat) +{ + HRESULT hr; + MAKE_WRL_OBJ(_VideoDeviceController) pDevCont; + WRL_PROP_GET(pSource, VideoDeviceController, pDevCont, hr) + if (FAILED(hr)) return hr; + GET_WRL_OBJ_FROM_OBJ(_MediaDeviceController, pMedDevCont, pDevCont, hr) + if (FAILED(hr)) return hr; + MAKE_WRL_OBJ(_MediaEncodingProperties) pMedEncProps; + WRL_METHOD(pMedDevCont, GetMediaStreamProperties, pMedEncProps, hr, WRL_ENUM_GET(_MediaStreamType, MediaStreamType, VideoPreview)) + if (FAILED(hr)) return hr; + GET_WRL_OBJ_FROM_OBJ(_VideoEncodingProperties, pVidProps, pMedEncProps, hr); + if (FAILED(hr)) return hr; + _ComPtr pType = NULL; + hr = MediaSink::ConvertPropertiesToMediaType(DEREF_AS_NATIVE_WRL_OBJ(ABI::Windows::Media::MediaProperties::IMediaEncodingProperties, pMedEncProps), &pType); + if (FAILED(hr)) return hr; + MediaType MT = FormatReader::Read(pType.Get()); + unsigned int sizeRawImage = 0; + if(VideoFormat == MFVideoFormat_RGB24) + { + sizeRawImage = MT.MF_MT_FRAME_SIZE * 3; + } + else if(VideoFormat == MFVideoFormat_RGB32) + { + sizeRawImage = MT.MF_MT_FRAME_SIZE * 4; + } + sizeRawImage = MT.MF_MT_SAMPLE_SIZE; + CHECK_HR(hr = RawImage::CreateInstance(&ig_RIFirst, sizeRawImage)); + CHECK_HR(hr = RawImage::CreateInstance(&ig_RISecond, sizeRawImage)); + ig_RIOut = ig_RISecond; + ig_pMedCapSource = pSource; +done: + return hr; +} + +HRESULT ImageGrabberWinRT::stopGrabbing(MAKE_WRL_REF(_AsyncAction)* action) +{ + HRESULT hr = S_OK; + if (ig_pMedCapSource != nullptr) { + GET_WRL_OBJ_FROM_REF(_MediaCaptureVideoPreview, imedPrevCap, DEREF_AGILE_WRL_OBJ(ig_pMedCapSource), hr) + if (FAILED(hr)) return hr; + MAKE_WRL_REF(_AsyncAction) pAction; + WRL_METHOD_BASE(imedPrevCap, StopPreviewAsync, pAction, hr) + if (SUCCEEDED(hr)) { +#ifdef HAVE_CONCURRENCY + DEFINE_TASK _task = CREATE_TASK DEFINE_RET_TYPE(void)(pAction); + *action = BEGIN_CREATE_ASYNC(void, _task, this) + HRESULT hr = S_OK; + _task.wait(); + SafeRelease(&ig_pMediaSink); + SetEvent(ig_hFinish); + END_CREATE_ASYNC(hr); +#else + *action = nullptr; +#endif + } + } + return hr; } -HRESULT ImageGrabber::initImageGrabber(IMFMediaSource *pSource, GUID VideoFormat) +HRESULT ImageGrabberWinRT::startGrabbing(MAKE_WRL_REF(_AsyncAction)* action) { - ComPtr pSinkActivate = NULL; - ComPtr pType = NULL; - ComPtr pPD = NULL; - ComPtr pSD = NULL; - ComPtr pHandler = NULL; - ComPtr pCurrentType = NULL; HRESULT hr = S_OK; + GET_WRL_OBJ_FROM_REF(_MediaCaptureVideoPreview, imedPrevCap, DEREF_AGILE_WRL_OBJ(ig_pMedCapSource), hr) + if (FAILED(hr)) return hr; + ACTIVATE_OBJ(RuntimeClass_Windows_Foundation_Collections_PropertySet, _PropertySet, pSet, hr) + if (FAILED(hr)) return hr; + GET_WRL_OBJ_FROM_OBJ(_Map, spSetting, pSet, hr) + if (FAILED(hr)) return hr; + ACTIVATE_STATIC_OBJ(RuntimeClass_Windows_Foundation_PropertyValue, MAKE_WRL_OBJ(_PropertyValueStatics), spPropVal, hr) + if (FAILED(hr)) return hr; + _ObjectObj pVal; + boolean bReplaced; + WRL_METHOD(spPropVal, CreateUInt32, pVal, hr, (unsigned int)WRL_ENUM_GET(_MediaStreamType, MediaStreamType, VideoPreview)) + if (FAILED(hr)) return hr; + WRL_METHOD(spSetting, Insert, bReplaced, hr, DEREF_WRL_OBJ(_StringReference(MF_PROP_VIDTYPE)), DEREF_WRL_OBJ(pVal)) + if (FAILED(hr)) return hr; + WRL_METHOD(spSetting, Insert, bReplaced, hr, DEREF_WRL_OBJ(_StringReference(MF_PROP_SAMPLEGRABBERCALLBACK)), reinterpret_cast<_Object>(this)) + if (FAILED(hr)) return hr; + MAKE_WRL_OBJ(_VideoDeviceController) pDevCont; + WRL_PROP_GET(ig_pMedCapSource, VideoDeviceController, pDevCont, hr) + if (FAILED(hr)) return hr; + GET_WRL_OBJ_FROM_OBJ(_MediaDeviceController, pMedDevCont, pDevCont, hr) + if (FAILED(hr)) return hr; + MAKE_WRL_OBJ(_MediaEncodingProperties) pMedEncProps; + WRL_METHOD(pMedDevCont, GetMediaStreamProperties, pMedEncProps, hr, WRL_ENUM_GET(_MediaStreamType, MediaStreamType, VideoPreview)) + if (FAILED(hr)) return hr; + GET_WRL_OBJ_FROM_OBJ(_VideoEncodingProperties, pVidProps, pMedEncProps, hr); + if (FAILED(hr)) return hr; + ACTIVATE_OBJ(RuntimeClass_Windows_Media_MediaProperties_MediaEncodingProfile, _MediaEncodingProfile, pEncProps, hr) + if (FAILED(hr)) return hr; + WRL_PROP_PUT(pEncProps, Video, DEREF_WRL_OBJ(pVidProps), hr) + if (FAILED(hr)) return hr; + WRL_METHOD(spSetting, Insert, bReplaced, hr, DEREF_WRL_OBJ(_StringReference(MF_PROP_VIDENCPROPS)), DEREF_WRL_OBJ(pVidProps)) + if (SUCCEEDED(hr)) { + //can start/stop multiple times with same MediaCapture object if using activatable class + WRL_METHOD(imedPrevCap, _StartPreviewToCustomSinkIdAsync, *action, hr, DEREF_WRL_OBJ(pEncProps), DEREF_WRL_OBJ(_StringReference(RuntimeClass_CV_MediaSink)), DEREF_WRL_OBJ(pSet)) + if (FAILED(hr) && hr == REGDB_E_CLASSNOTREG) { + hr = Microsoft::WRL::Make().CopyTo(&ig_pMediaSink); + if (FAILED(hr)) return hr; + hr = ((ABI::Windows::Media::IMediaExtension*)ig_pMediaSink)->SetProperties(DEREF_AS_NATIVE_WRL_OBJ(ABI::Windows::Foundation::Collections::IPropertySet, pSet)); + if (FAILED(hr)) return hr; + WRL_METHOD(imedPrevCap, StartPreviewToCustomSinkAsync, *action, hr, DEREF_WRL_OBJ(pEncProps), reinterpret_cast(ig_pMediaSink)) + } + } + return hr; +} + +HRESULT ImageGrabberWinRT::CreateInstance(ImageGrabberWinRT **ppIG, bool synchronous) +{ + *ppIG = Microsoft::WRL::Make(synchronous).Detach(); + if (ppIG == NULL) + { + return E_OUTOFMEMORY; + } + DebugPrintOut(L"IMAGEGRABBER VIDEODEVICE: Creating instance of ImageGrabberWinRT\n"); + return S_OK; +} +#endif + +HRESULT ImageGrabber::initImageGrabber(IMFMediaSource *pSource) +{ + _ComPtr pSinkActivate = NULL; + _ComPtr pType = NULL; + _ComPtr pPD = NULL; + _ComPtr pSD = NULL; + _ComPtr pHandler = NULL; + _ComPtr pCurrentType = NULL; MediaType MT; // Clean up. if (ig_pSession) @@ -938,7 +1391,7 @@ HRESULT ImageGrabber::initImageGrabber(IMFMediaSource *pSource, GUID VideoFormat SafeRelease(&ig_pSession); SafeRelease(&ig_pTopology); ig_pSource = pSource; - hr = pSource->CreatePresentationDescriptor(&pPD); + HRESULT hr = pSource->CreatePresentationDescriptor(&pPD); if (FAILED(hr)) { goto err; @@ -966,24 +1419,16 @@ HRESULT ImageGrabber::initImageGrabber(IMFMediaSource *pSource, GUID VideoFormat MT = FormatReader::Read(pCurrentType.Get()); } err: - unsigned int sizeRawImage = 0; - if(VideoFormat == MFVideoFormat_RGB24) - { - sizeRawImage = MT.MF_MT_FRAME_SIZE * 3; - } - else if(VideoFormat == MFVideoFormat_RGB32) - { - sizeRawImage = MT.MF_MT_FRAME_SIZE * 4; - } - CHECK_HR(hr = RawImage::CreateInstance(&ig_RIFirst, sizeRawImage)); - CHECK_HR(hr = RawImage::CreateInstance(&ig_RISecond, sizeRawImage)); + CHECK_HR(hr); + CHECK_HR(hr = RawImage::CreateInstance(&ig_RIFirst, MT.MF_MT_SAMPLE_SIZE)); + CHECK_HR(hr = RawImage::CreateInstance(&ig_RISecond, MT.MF_MT_SAMPLE_SIZE)); ig_RIOut = ig_RISecond; // Configure the media type that the Sample Grabber will receive. // Setting the major and subtype is usually enough for the topology loader // to resolve the topology. CHECK_HR(hr = MFCreateMediaType(pType.GetAddressOf())); - CHECK_HR(hr = pType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video)); - CHECK_HR(hr = pType->SetGUID(MF_MT_SUBTYPE, VideoFormat)); + CHECK_HR(hr = pType->SetGUID(MF_MT_MAJOR_TYPE, MT.MF_MT_MAJOR_TYPE)); + CHECK_HR(hr = pType->SetGUID(MF_MT_SUBTYPE, MT.MF_MT_SUBTYPE)); // Create the sample grabber sink. CHECK_HR(hr = MFCreateSampleGrabberSinkActivate(pType.Get(), this, pSinkActivate.GetAddressOf())); // To run as fast as possible, set this attribute (requires Windows 7): @@ -1003,7 +1448,6 @@ done: SafeRelease(&ig_pSession); SafeRelease(&ig_pTopology); } - return hr; } @@ -1011,19 +1455,16 @@ void ImageGrabber::stopGrabbing() { if(ig_pSession) ig_pSession->Stop(); - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); - DPO->printOut(L"IMAGEGRABBER VIDEODEVICE %i: Stopping of of grabbing of images\n", ig_DeviceID); + DebugPrintOut(L"IMAGEGRABBER VIDEODEVICE %i: Stopping of of grabbing of images\n", ig_DeviceID); } HRESULT ImageGrabber::startGrabbing(void) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); - ComPtr pEvent = NULL; + _ComPtr pEvent = NULL; PROPVARIANT var; PropVariantInit(&var); - HRESULT hr = S_OK; - hr = ig_pSession->SetTopology(0, ig_pTopology); - DPO->printOut(L"IMAGEGRABBER VIDEODEVICE %i: Start Grabbing of the images\n", ig_DeviceID); + HRESULT hr = ig_pSession->SetTopology(0, ig_pTopology); + DebugPrintOut(L"IMAGEGRABBER VIDEODEVICE %i: Start Grabbing of the images\n", ig_DeviceID); hr = ig_pSession->Start(&GUID_NULL, &var); for(;;) { @@ -1050,28 +1491,28 @@ HRESULT ImageGrabber::startGrabbing(void) } if (met == MESessionEnded) { - DPO->printOut(L"IMAGEGRABBER VIDEODEVICE %i: MESessionEnded \n", ig_DeviceID); + DebugPrintOut(L"IMAGEGRABBER VIDEODEVICE %i: MESessionEnded\n", ig_DeviceID); ig_pSession->Stop(); break; } if (met == MESessionStopped) { - DPO->printOut(L"IMAGEGRABBER VIDEODEVICE %i: MESessionStopped \n", ig_DeviceID); + DebugPrintOut(L"IMAGEGRABBER VIDEODEVICE %i: MESessionStopped \n", ig_DeviceID); break; } if (met == MEVideoCaptureDeviceRemoved) { - DPO->printOut(L"IMAGEGRABBER VIDEODEVICE %i: MEVideoCaptureDeviceRemoved \n", ig_DeviceID); + DebugPrintOut(L"IMAGEGRABBER VIDEODEVICE %i: MEVideoCaptureDeviceRemoved \n", ig_DeviceID); break; } if ((met == MEError) || (met == MENonFatalError)) { pEvent->GetStatus(&hrStatus); - DPO->printOut(L"IMAGEGRABBER VIDEODEVICE %i: MEError | MENonFatalError: %u\n", ig_DeviceID, hrStatus); + DebugPrintOut(L"IMAGEGRABBER VIDEODEVICE %i: MEError | MENonFatalError: %u\n", ig_DeviceID, hrStatus); break; } } - DPO->printOut(L"IMAGEGRABBER VIDEODEVICE %i: Finish startGrabbing \n", ig_DeviceID); + DebugPrintOut(L"IMAGEGRABBER VIDEODEVICE %i: Finish startGrabbing \n", ig_DeviceID); done: SetEvent(ig_hFinish); @@ -1079,22 +1520,22 @@ done: return hr; } -void ImageGrabber::pauseGrabbing() +void ImageGrabberCallback::pauseGrabbing() { } -void ImageGrabber::resumeGrabbing() +void ImageGrabberCallback::resumeGrabbing() { } HRESULT ImageGrabber::CreateTopology(IMFMediaSource *pSource, IMFActivate *pSinkActivate, IMFTopology **ppTopo) { IMFTopology* pTopology = NULL; - ComPtr pPD = NULL; - ComPtr pSD = NULL; - ComPtr pHandler = NULL; - ComPtr pNode1 = NULL; - ComPtr pNode2 = NULL; + _ComPtr pPD = NULL; + _ComPtr pSD = NULL; + _ComPtr pHandler = NULL; + _ComPtr pNode1 = NULL; + _ComPtr pNode2 = NULL; HRESULT hr = S_OK; DWORD cStreams = 0; CHECK_HR(hr = MFCreateTopology(&pTopology)); @@ -1134,7 +1575,7 @@ HRESULT ImageGrabber::AddSourceNode( IMFStreamDescriptor *pSD, // Stream descriptor. IMFTopologyNode **ppNode) // Receives the node pointer. { - ComPtr pNode = NULL; + _ComPtr pNode = NULL; HRESULT hr = S_OK; CHECK_HR(hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, pNode.GetAddressOf())); CHECK_HR(hr = pNode->SetUnknown(MF_TOPONODE_SOURCE, pSource)); @@ -1155,7 +1596,7 @@ HRESULT ImageGrabber::AddOutputNode( DWORD dwId, // Identifier of the stream sink. IMFTopologyNode **ppNode) // Receives the node pointer. { - ComPtr pNode = NULL; + _ComPtr pNode = NULL; HRESULT hr = S_OK; CHECK_HR(hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, pNode.GetAddressOf())); CHECK_HR(hr = pNode->SetObject(pActivate)); @@ -1177,8 +1618,7 @@ HRESULT ImageGrabber::CreateInstance(ImageGrabber **ppIG, unsigned int deviceID, { return E_OUTOFMEMORY; } - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); - DPO->printOut(L"IMAGEGRABBER VIDEODEVICE %i: Creating instance of ImageGrabber\n", deviceID); + DebugPrintOut(L"IMAGEGRABBER VIDEODEVICE %i: Creating instance of ImageGrabber\n", deviceID); return S_OK; } @@ -1218,45 +1658,45 @@ STDMETHODIMP_(ULONG) ImageGrabber::Release() return cRef; } -STDMETHODIMP ImageGrabber::OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset) +STDMETHODIMP ImageGrabberCallback::OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset) { (void)hnsSystemTime; (void)llClockStartOffset; return S_OK; } -STDMETHODIMP ImageGrabber::OnClockStop(MFTIME hnsSystemTime) +STDMETHODIMP ImageGrabberCallback::OnClockStop(MFTIME hnsSystemTime) { (void)hnsSystemTime; return S_OK; } -STDMETHODIMP ImageGrabber::OnClockPause(MFTIME hnsSystemTime) +STDMETHODIMP ImageGrabberCallback::OnClockPause(MFTIME hnsSystemTime) { (void)hnsSystemTime; return S_OK; } -STDMETHODIMP ImageGrabber::OnClockRestart(MFTIME hnsSystemTime) +STDMETHODIMP ImageGrabberCallback::OnClockRestart(MFTIME hnsSystemTime) { (void)hnsSystemTime; return S_OK; } -STDMETHODIMP ImageGrabber::OnClockSetRate(MFTIME hnsSystemTime, float flRate) +STDMETHODIMP ImageGrabberCallback::OnClockSetRate(MFTIME hnsSystemTime, float flRate) { (void)flRate; (void)hnsSystemTime; return S_OK; } -STDMETHODIMP ImageGrabber::OnSetPresentationClock(IMFPresentationClock* pClock) +STDMETHODIMP ImageGrabberCallback::OnSetPresentationClock(IMFPresentationClock* pClock) { (void)pClock; return S_OK; } -STDMETHODIMP ImageGrabber::OnProcessSample(REFGUID guidMajorMediaType, DWORD dwSampleFlags, +STDMETHODIMP ImageGrabberCallback::OnProcessSample(REFGUID guidMajorMediaType, DWORD dwSampleFlags, LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer, DWORD dwSampleSize) { @@ -1271,7 +1711,7 @@ STDMETHODIMP ImageGrabber::OnProcessSample(REFGUID guidMajorMediaType, DWORD dwS DWORD status = WaitForMultipleObjects(2, tmp, FALSE, INFINITE); if (status == WAIT_OBJECT_0) { - printf("OnProcessFrame called after ig_hFinish event\n"); + DebugPrintOut(L"OnProcessFrame called after ig_hFinish event\n"); return S_OK; } @@ -1298,13 +1738,13 @@ STDMETHODIMP ImageGrabber::OnProcessSample(REFGUID guidMajorMediaType, DWORD dwS return S_OK; } -STDMETHODIMP ImageGrabber::OnShutdown() +STDMETHODIMP ImageGrabberCallback::OnShutdown() { SetEvent(ig_hFinish); return S_OK; } -RawImage *ImageGrabber::getRawImage() +RawImage *ImageGrabberCallback::getRawImage() { return ig_RIOut; } @@ -1318,41 +1758,39 @@ DWORD WINAPI MainThreadFunction( LPVOID lpParam ) HRESULT ImageGrabberThread::CreateInstance(ImageGrabberThread **ppIGT, IMFMediaSource *pSource, unsigned int deviceID, bool synchronious) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); *ppIGT = new (std::nothrow) ImageGrabberThread(pSource, deviceID, synchronious); if (ppIGT == NULL) { - DPO->printOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i: Memory cannot be allocated\n", deviceID); + DebugPrintOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i: Memory cannot be allocated\n", deviceID); return E_OUTOFMEMORY; } else - DPO->printOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i: Creating of the instance of ImageGrabberThread\n", deviceID); + DebugPrintOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i: Creating of the instance of ImageGrabberThread\n", deviceID); return S_OK; } -ImageGrabberThread::ImageGrabberThread(IMFMediaSource *pSource, unsigned int deviceID, bool synchronious): +ImageGrabberThread::ImageGrabberThread(IMFMediaSource *pSource, unsigned int deviceID, bool synchronious) : igt_func(NULL), igt_Handle(NULL), igt_stop(false) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); HRESULT hr = ImageGrabber::CreateInstance(&igt_pImageGrabber, deviceID, synchronious); igt_DeviceID = deviceID; if(SUCCEEDED(hr)) { - hr = igt_pImageGrabber->initImageGrabber(pSource, MFVideoFormat_RGB24); + hr = igt_pImageGrabber->initImageGrabber(pSource); if(!SUCCEEDED(hr)) { - DPO->printOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i: There is a problem with initialization of the instance of the ImageGrabber class\n", deviceID); + DebugPrintOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i: There is a problem with initialization of the instance of the ImageGrabber class\n", deviceID); } else { - DPO->printOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i: Initialization of instance of the ImageGrabber class\n", deviceID); + DebugPrintOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i: Initialization of instance of the ImageGrabber class\n", deviceID); } } else { - DPO->printOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i There is a problem with creation of the instance of the ImageGrabber class\n", deviceID); + DebugPrintOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i: There is a problem with creation of the instance of the ImageGrabber class\n", deviceID); } } @@ -1367,8 +1805,7 @@ void ImageGrabberThread::setEmergencyStopEvent(void *userData, void(*func)(int, ImageGrabberThread::~ImageGrabberThread(void) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); - DPO->printOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i: Destroing ImageGrabberThread\n", igt_DeviceID); + DebugPrintOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i: Destroing ImageGrabberThread\n", igt_DeviceID); if (igt_Handle) WaitForSingleObject(igt_Handle, INFINITE); delete igt_pImageGrabber; @@ -1396,30 +1833,29 @@ void ImageGrabberThread::start() void ImageGrabberThread::run() { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); if(igt_pImageGrabber) { - DPO->printOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i: Thread for grabbing images is started\n", igt_DeviceID); + DebugPrintOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i: Thread for grabbing images is started\n", igt_DeviceID); HRESULT hr = igt_pImageGrabber->startGrabbing(); if(!SUCCEEDED(hr)) { - DPO->printOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i: There is a problem with starting the process of grabbing\n", igt_DeviceID); + DebugPrintOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i: There is a problem with starting the process of grabbing\n", igt_DeviceID); } } else { - DPO->printOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i The thread is finished without execution of grabbing\n", igt_DeviceID); + DebugPrintOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i The thread is finished without execution of grabbing\n", igt_DeviceID); } if(!igt_stop) { - DPO->printOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i: Emergency Stop thread\n", igt_DeviceID); + DebugPrintOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i: Emergency Stop thread\n", igt_DeviceID); if(igt_func) { igt_func(igt_DeviceID, igt_userData); } } else - DPO->printOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i: Finish thread\n", igt_DeviceID); + DebugPrintOut(L"IMAGEGRABBERTHREAD VIDEODEVICE %i: Finish thread\n", igt_DeviceID); } ImageGrabber *ImageGrabberThread::getImageGrabber() @@ -1432,8 +1868,7 @@ Media_Foundation::Media_Foundation(void) HRESULT hr = MFStartup(MF_VERSION); if(!SUCCEEDED(hr)) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); - DPO->printOut(L"MEDIA FOUNDATION: It cannot be created!!!\n"); + DebugPrintOut(L"MEDIA FOUNDATION: It cannot be created!!!\n"); } } @@ -1442,15 +1877,18 @@ Media_Foundation::~Media_Foundation(void) HRESULT hr = MFShutdown(); if(!SUCCEEDED(hr)) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); - DPO->printOut(L"MEDIA FOUNDATION: Resources cannot be released\n"); + DebugPrintOut(L"MEDIA FOUNDATION: Resources cannot be released\n"); } } bool Media_Foundation::buildListOfDevices() { HRESULT hr = S_OK; - ComPtr pAttributes = NULL; +#ifdef HAVE_WINRT + videoDevices *vDs = &videoDevices::getInstance(); + hr = vDs->initDevices(WRL_ENUM_GET(_DeviceClass, DeviceClass, VideoCapture)); +#else + _ComPtr pAttributes = NULL; CoInitialize(NULL); hr = MFCreateAttributes(pAttributes.GetAddressOf(), 1); if (SUCCEEDED(hr)) @@ -1465,10 +1903,10 @@ bool Media_Foundation::buildListOfDevices() videoDevices *vDs = &videoDevices::getInstance(); hr = vDs->initDevices(pAttributes.Get()); } - else +#endif + if (FAILED(hr)) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); - DPO->printOut(L"MEDIA FOUNDATION: The access to the video cameras denied\n"); + DebugPrintOut(L"MEDIA FOUNDATION: The access to the video cameras denied\n"); } return (SUCCEEDED(hr)); @@ -1532,8 +1970,13 @@ unsigned char * RawImage::getpPixels() } videoDevice::videoDevice(void): vd_IsSetuped(false), vd_LockOut(OpenLock), vd_pFriendlyName(NULL), - vd_Width(0), vd_Height(0), vd_pSource(NULL), vd_func(NULL), vd_userData(NULL) + vd_Width(0), vd_Height(0), vd_FrameRate(0), vd_pSource(NULL), vd_pImGrTh(NULL), vd_func(NULL), vd_userData(NULL) { +#ifdef HAVE_WINRT + vd_pMedCap = nullptr; + vd_cookie.value = 0; + vd_pImGr = NULL; +#endif } void videoDevice::setParametrs(CamParametrs parametrs) @@ -1616,13 +2059,61 @@ CamParametrs videoDevice::getParametrs() return out; } +#ifdef HAVE_WINRT +long videoDevice::resetDevice(MAKE_WRL_REF(_IDeviceInformation) pDevice) +#else long videoDevice::resetDevice(IMFActivate *pActivate) +#endif { - HRESULT hr = -1; + HRESULT hr = E_FAIL; vd_CurrentFormats.clear(); if(vd_pFriendlyName) CoTaskMemFree(vd_pFriendlyName); vd_pFriendlyName = NULL; +#ifdef HAVE_WINRT + if (pDevice) + { + ACTIVATE_OBJ(RuntimeClass_Windows_Media_Capture_MediaCapture, _MediaCapture, pIMedCap, hr) + if (FAILED(hr)) return hr; + ACTIVATE_OBJ(RuntimeClass_Windows_Media_Capture_MediaCaptureInitializationSettings, _MediaCaptureInitializationSettings, pCapInitSet, hr) + if (FAILED(hr)) return hr; + _StringObj str; + WRL_PROP_GET(pDevice, Name, *REF_WRL_OBJ(str), hr) + if (FAILED(hr)) return hr; + unsigned int length = 0; + PCWSTR wstr = WindowsGetStringRawBuffer(reinterpret_cast(DEREF_WRL_OBJ(str)), &length); + vd_pFriendlyName = (wchar_t*)CoTaskMemAlloc((length + 1) * sizeof(wchar_t)); + wcscpy(vd_pFriendlyName, wstr); + WRL_PROP_GET(pDevice, Id, *REF_WRL_OBJ(str), hr) + if (FAILED(hr)) return hr; + WRL_PROP_PUT(pCapInitSet, VideoDeviceId, DEREF_WRL_OBJ(str), hr) + if (FAILED(hr)) return hr; + WRL_PROP_PUT(pCapInitSet, StreamingCaptureMode, WRL_ENUM_GET(_StreamingCaptureMode, StreamingCaptureMode, Video), hr) + if (FAILED(hr)) return hr; + MAKE_WRL_REF(_AsyncAction) pAction; + WRL_METHOD(DEREF_WRL_OBJ(pIMedCap), _InitializeWithSettingsAsync, pAction, hr, DEREF_WRL_OBJ(pCapInitSet)) +#ifdef HAVE_CONCURRENCY + if (FAILED(hr)) return hr; + MAKE_WRL_AGILE_OBJ(_MediaCapture) pAgileMedCap; + pAgileMedCap = PREPARE_TRANSFER_WRL_OBJ(pIMedCap); + SAVE_CURRENT_CONTEXT(context); + DEFINE_TASK task = CREATE_TASK DEFINE_RET_TYPE(void)(pAction); + auto func = [task]() { task.wait(); RET_VAL_BASE }; + CREATE_OR_CONTINUE_TASK(vd_pAction, void, func); + vd_pAction = vd_pAction.then([context, pAgileMedCap, this]()->DEFINE_RET_FORMAL(void) { + HRESULT hr = S_OK; + if (SUCCEEDED(hr)) { + //all camera capture calls only in original context + BEGIN_CALL_IN_CONTEXT(hr, context, pAgileMedCap, this) + enumerateCaptureFormats(DEREF_AGILE_WRL_MADE_OBJ(pAgileMedCap)); + END_CALL_IN_CONTEXT_BASE + } + buildLibraryofTypes(); + RET_VAL_BASE + }); +#endif + } +#else if(pActivate) { IMFMediaSource *pSource = NULL; @@ -1641,29 +2132,73 @@ long videoDevice::resetDevice(IMFActivate *pActivate) if(FAILED(hr)) { vd_pFriendlyName = NULL; - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); - DPO->printOut(L"VIDEODEVICE %i: IMFMediaSource interface cannot be created \n", vd_CurrentNumber); + DebugPrintOut(L"VIDEODEVICE %i: IMFMediaSource interface cannot be created \n", vd_CurrentNumber); } } +#endif return hr; } -long videoDevice::readInfoOfDevice(IMFActivate *pActivate, unsigned int Num) +#ifdef HAVE_WINRT +long videoDevice::readInfoOfDevice(MAKE_WRL_REF(_IDeviceInformation) pDevice, unsigned int Num) { HRESULT hr = -1; vd_CurrentNumber = Num; - hr = resetDevice(pActivate); + hr = resetDevice(pDevice); return hr; } +#else +long videoDevice::readInfoOfDevice(IMFActivate *pActivate, unsigned int Num) +{ + vd_CurrentNumber = Num; + return resetDevice(pActivate); +} +#endif -long videoDevice::checkDevice(IMFAttributes *pAttributes, IMFActivate **pDevice) +#ifdef HAVE_WINRT +#ifdef HAVE_CONCURRENCY +long videoDevice::checkDevice(_DeviceClass devClass, DEFINE_TASK* pTask, MAKE_WRL_REF(_IDeviceInformation)* ppDevice) { HRESULT hr = S_OK; + ACTIVATE_STATIC_OBJ(RuntimeClass_Windows_Devices_Enumeration_DeviceInformation, MAKE_WRL_OBJ(_DeviceInformationStatics), pDevStat, hr) + if (FAILED(hr)) return hr; + MAKE_WRL_REF(_AsyncOperation) pAction; + WRL_METHOD(pDevStat, _FindAllAsyncDeviceClass, pAction, hr, devClass) + if (SUCCEEDED(hr)) { + *pTask = CREATE_TASK DEFINE_RET_TYPE(void)([pAction, &ppDevice, this]() -> DEFINE_RET_FORMAL(void) { + HRESULT hr = S_OK; + MAKE_WRL_OBJ(_VectorView) pVector = + CREATE_TASK DEFINE_RET_TYPE(MAKE_WRL_REF(_VectorView))(pAction).get(); + UINT32 count = 0; + if (SUCCEEDED(hr)) WRL_PROP_GET(pVector, Size, count, hr) + if (SUCCEEDED(hr) && count > 0) { + for (UINT32 i = 0; i < count; i++) { + MAKE_WRL_OBJ(_IDeviceInformation) pDevice; + WRL_METHOD(pVector, GetAt, pDevice, hr, i) + if (SUCCEEDED(hr)) { + _StringObj str; + unsigned int length = 0; + WRL_PROP_GET(pDevice, Name, *REF_WRL_OBJ(str), hr) + PCWSTR wstr = WindowsGetStringRawBuffer(reinterpret_cast(DEREF_WRL_OBJ(str)), &length); + if (wcscmp(wstr, vd_pFriendlyName) == 0) { + *ppDevice = PREPARE_TRANSFER_WRL_OBJ(pDevice); + } + } + } + } + RET_VAL_BASE; + }); + } + return hr; +} +#endif +#else +long videoDevice::checkDevice(IMFAttributes *pAttributes, IMFActivate **pDevice) +{ IMFActivate **ppDevices = NULL; - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); UINT32 count; wchar_t *newFriendlyName = NULL; - hr = MFEnumDeviceSources(pAttributes, &ppDevices, &count); + HRESULT hr = MFEnumDeviceSources(pAttributes, &ppDevices, &count); if (SUCCEEDED(hr)) { if(count > 0) @@ -1679,8 +2214,8 @@ long videoDevice::checkDevice(IMFAttributes *pAttributes, IMFActivate **pDevice) { if(wcscmp(newFriendlyName, vd_pFriendlyName) != 0) { - DPO->printOut(L"VIDEODEVICE %i: Chosen device cannot be found \n", vd_CurrentNumber); - hr = -1; + DebugPrintOut(L"VIDEODEVICE %i: Chosen device cannot be found \n", vd_CurrentNumber); + hr = E_INVALIDARG; pDevice = NULL; } else @@ -1691,13 +2226,13 @@ long videoDevice::checkDevice(IMFAttributes *pAttributes, IMFActivate **pDevice) } else { - DPO->printOut(L"VIDEODEVICE %i: Name of device cannot be gotten \n", vd_CurrentNumber); + DebugPrintOut(L"VIDEODEVICE %i: Name of device cannot be gotten \n", vd_CurrentNumber); } } else { - DPO->printOut(L"VIDEODEVICE %i: Number of devices more than corrent number of the device \n", vd_CurrentNumber); - hr = -1; + DebugPrintOut(L"VIDEODEVICE %i: Number of devices more than corrent number of the device \n", vd_CurrentNumber); + hr = E_INVALIDARG; } for(UINT32 i = 0; i < count; i++) { @@ -1706,22 +2241,68 @@ long videoDevice::checkDevice(IMFAttributes *pAttributes, IMFActivate **pDevice) SafeRelease(ppDevices); } else - hr = -1; + hr = E_FAIL; } else { - DPO->printOut(L"VIDEODEVICE %i: List of DeviceSources cannot be enumerated \n", vd_CurrentNumber); + DebugPrintOut(L"VIDEODEVICE %i: List of DeviceSources cannot be enumerated \n", vd_CurrentNumber); } return hr; } +#endif long videoDevice::initDevice() { - HRESULT hr = -1; - ComPtr pAttributes = NULL; - IMFActivate *vd_pActivate = NULL; - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); + HRESULT hr = S_OK; CoInitialize(NULL); +#ifdef HAVE_WINRT +#ifdef HAVE_CONCURRENCY + SAVE_CURRENT_CONTEXT(context); + auto func = [context, this]()->DEFINE_RET_FORMAL(void) { + HRESULT hr; + DEFINE_TASK pTask; + MAKE_WRL_OBJ(_IDeviceInformation) pDevInfo; + hr = checkDevice(WRL_ENUM_GET(_DeviceClass, DeviceClass, VideoCapture), &pTask, REF_WRL_OBJ(pDevInfo)); + if (SUCCEEDED(hr)) pTask.wait(); + if (SUCCEEDED(hr)) { + DEFINE_TASK _task; + BEGIN_CALL_IN_CONTEXT(hr, context, pDevInfo, &_task, context, this) + HRESULT hr; + ACTIVATE_OBJ(RuntimeClass_Windows_Media_Capture_MediaCapture, _MediaCapture, pIMedCap, hr) + if (SUCCEEDED(hr)) { + RELEASE_WRL(vd_pMedCap); + vd_pMedCap = PREPARE_TRANSFER_WRL_OBJ(pIMedCap); + ACTIVATE_OBJ(RuntimeClass_Windows_Media_Capture_MediaCaptureInitializationSettings, _MediaCaptureInitializationSettings, pCapInitSet, hr) + _StringObj str; + if (SUCCEEDED(hr)) { + WRL_PROP_GET(pDevInfo, Id, *REF_WRL_OBJ(str), hr) + if (SUCCEEDED(hr)) { + WRL_PROP_PUT(pCapInitSet, VideoDeviceId, DEREF_WRL_OBJ(str), hr) + } + } + if (SUCCEEDED(hr)) + WRL_PROP_PUT(pCapInitSet, StreamingCaptureMode, WRL_ENUM_GET(_StreamingCaptureMode, StreamingCaptureMode, Video), hr) + if (SUCCEEDED(hr)) reinterpret_cast(DEREF_AGILE_WRL_OBJ(vd_pMedCap))->add_Failed(Microsoft::WRL::Callback([this, context](ABI::Windows::Media::Capture::IMediaCapture*, ABI::Windows::Media::Capture::IMediaCaptureFailedEventArgs*) -> HRESULT { + HRESULT hr; + BEGIN_CALL_IN_CONTEXT(hr, context, this) + closeDevice(); + END_CALL_IN_CONTEXT_BASE + return hr; + }).Get(), &vd_cookie); + MAKE_WRL_OBJ(_AsyncAction) pAction; + if (SUCCEEDED(hr)) WRL_METHOD(vd_pMedCap, _InitializeWithSettingsAsync, *REF_WRL_OBJ(pAction), hr, DEREF_WRL_OBJ(pCapInitSet)) + if (SUCCEEDED(hr)) _task = CREATE_TASK DEFINE_RET_TYPE(void)(DEREF_WRL_OBJ(pAction)); + } + END_CALL_IN_CONTEXT(hr) + _task.wait(); + } + RET_VAL_BASE + }; + CREATE_OR_CONTINUE_TASK(vd_pAction, void, func); +#endif +#else + _ComPtr pAttributes = NULL; + IMFActivate *vd_pActivate = NULL; hr = MFCreateAttributes(pAttributes.GetAddressOf(), 1); if (SUCCEEDED(hr)) { @@ -1747,14 +2328,14 @@ long videoDevice::initDevice() } else { - DPO->printOut(L"VIDEODEVICE %i: Device there is not \n", vd_CurrentNumber); + DebugPrintOut(L"VIDEODEVICE %i: Device there is not \n", vd_CurrentNumber); } } else { - DPO->printOut(L"VIDEODEVICE %i: The attribute of video cameras cannot be getting \n", vd_CurrentNumber); + DebugPrintOut(L"VIDEODEVICE %i: The attribute of video cameras cannot be getting \n", vd_CurrentNumber); } - +#endif return hr; } @@ -1768,7 +2349,7 @@ MediaType videoDevice::getFormat(unsigned int id) } int videoDevice::getCountFormats() { - return vd_CurrentFormats.size(); + return (int)vd_CurrentFormats.size(); } void videoDevice::setEmergencyStopEvent(void *userData, void(*func)(int, void *)) { @@ -1780,7 +2361,32 @@ void videoDevice::closeDevice() if(vd_IsSetuped) { vd_IsSetuped = false; - vd_pSource->Stop(); + +#ifdef HAVE_WINRT +#ifdef HAVE_CONCURRENCY + if (DEREF_AGILE_WRL_OBJ(vd_pMedCap)) { + MAKE_WRL_REF(_AsyncAction) action; + vd_pImGr->stopGrabbing(&action); + reinterpret_cast(DEREF_AGILE_WRL_OBJ(vd_pMedCap))->remove_Failed(vd_cookie); + vd_cookie.value = 0; + DEFINE_TASK task = CREATE_TASK DEFINE_RET_TYPE(void)(action); + auto func = [task]() { task.wait(); RET_VAL_BASE }; + CREATE_OR_CONTINUE_TASK(vd_pAction, void, func); + vd_pAction = vd_pAction.then([this]()->DEFINE_RET_FORMAL(void) { + RELEASE_WRL(vd_pMedCap) + if (vd_LockOut == RawDataLock) { + delete vd_pImGr; + } + vd_pImGr = NULL; + vd_LockOut = OpenLock; + RET_VAL_BASE + }); + return; + } +#endif +#endif + + vd_pSource->Shutdown(); SafeRelease(&vd_pSource); if(vd_LockOut == RawDataLock) { @@ -1790,8 +2396,7 @@ void videoDevice::closeDevice() } vd_pImGrTh = NULL; vd_LockOut = OpenLock; - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); - DPO->printOut(L"VIDEODEVICE %i: Device is stopped \n", vd_CurrentNumber); + DebugPrintOut(L"VIDEODEVICE %i: Device is stopped \n", vd_CurrentNumber); } } unsigned int videoDevice::getWidth() @@ -1808,6 +2413,15 @@ unsigned int videoDevice::getHeight() else return 0; } + +unsigned int videoDevice::getFrameRate() const +{ + if(vd_IsSetuped) + return vd_FrameRate; + else + return 0; +} + IMFMediaSource *videoDevice::getMediaSource() { IMFMediaSource *out = NULL; @@ -1820,17 +2434,25 @@ IMFMediaSource *videoDevice::getMediaSource() } int videoDevice::findType(unsigned int size, unsigned int frameRate) { - if(vd_CaptureFormats.size() == 0) - return 0; - FrameRateMap FRM = vd_CaptureFormats[size]; - if(FRM.size() == 0) - return 0; + // For required frame size look for the suitable video format. + // If not found, get the format for the largest available frame size. + FrameRateMap FRM; + std::map::const_iterator fmt; + fmt = vd_CaptureFormats.find(size); + if( fmt != vd_CaptureFormats.end() ) + FRM = fmt->second; + else if (FRM.empty()) + return -1; + else + FRM = vd_CaptureFormats.rbegin()->second; + UINT64 frameRateMax = 0; SUBTYPEMap STMMax; if(frameRate == 0) { std::map::iterator f = FRM.begin(); for(; f != FRM.end(); f++) { + // Looking for highest possible frame rate. if((*f).first >= frameRateMax) { frameRateMax = (*f).first; @@ -1843,22 +2465,26 @@ int videoDevice::findType(unsigned int size, unsigned int frameRate) std::map::iterator f = FRM.begin(); for(; f != FRM.end(); f++) { - if((*f).first >= frameRateMax) - { - if(frameRate > (*f).first) - { - frameRateMax = (*f).first; - STMMax = (*f).second; - } - } + // Looking for frame rate higher that recently found but not higher then demanded. + if( (*f).first >= frameRateMax && (*f).first <= frameRate ) + { + frameRateMax = (*f).first; + STMMax = (*f).second; + } } } - if(STMMax.size() == 0) - return 0; - std::map::iterator S = STMMax.begin(); - vectorNum VN = (*S).second; - if(VN.size() == 0) - return 0; + // Get first (default) item from the list if no suitable frame rate found. + if( STMMax.empty() ) + STMMax = FRM.begin()->second; + + // Check if there are any format types on the list. + if( STMMax.empty() ) + return -1; + + vectorNum VN = STMMax.begin()->second; + if( VN.empty() ) + return -1; + return VN[0]; } @@ -1870,26 +2496,50 @@ void videoDevice::buildLibraryofTypes() int count = 0; for(; i != vd_CurrentFormats.end(); i++) { - size = (*i).MF_MT_FRAME_SIZE; - framerate = (*i).MF_MT_FRAME_RATE_NUMERATOR; - FrameRateMap FRM = vd_CaptureFormats[size]; - SUBTYPEMap STM = FRM[framerate]; - String subType((*i).pMF_MT_SUBTYPEName); - vectorNum VN = STM[subType]; - VN.push_back(count); - STM[subType] = VN; - FRM[framerate] = STM; - vd_CaptureFormats[size] = FRM; + // Count only supported video formats. + if( (*i).MF_MT_SUBTYPE == MFVideoFormat_RGB24 ) + { + size = (*i).MF_MT_FRAME_SIZE; + framerate = (*i).MF_MT_FRAME_RATE_NUMERATOR / (*i).MF_MT_FRAME_RATE_DENOMINATOR; + FrameRateMap FRM = vd_CaptureFormats[size]; + SUBTYPEMap STM = FRM[framerate]; + String subType((*i).pMF_MT_SUBTYPEName); + vectorNum VN = STM[subType]; + VN.push_back(count); + STM[subType] = VN; + FRM[framerate] = STM; + vd_CaptureFormats[size] = FRM; + } count++; } } +#ifdef HAVE_WINRT +long videoDevice::setDeviceFormat(MAKE_WRL_REF(_MediaCapture) pSource, unsigned long dwFormatIndex, MAKE_WRL_REF(_AsyncAction)* pAction) +{ + HRESULT hr; + MAKE_WRL_OBJ(_VideoDeviceController) pDevCont; + WRL_PROP_GET(pSource, VideoDeviceController, pDevCont, hr) + if (FAILED(hr)) return hr; + GET_WRL_OBJ_FROM_OBJ(_MediaDeviceController, pMedDevCont, pDevCont, hr) + if (FAILED(hr)) return hr; + MAKE_WRL_OBJ(_VectorView) pVector; + WRL_METHOD(pMedDevCont, GetAvailableMediaStreamProperties, pVector, hr, WRL_ENUM_GET(_MediaStreamType, MediaStreamType, VideoPreview)) + if (FAILED(hr)) return hr; + MAKE_WRL_OBJ(_MediaEncodingProperties) pMedEncProps; + WRL_METHOD(pVector, GetAt, pMedEncProps, hr, dwFormatIndex) + if (FAILED(hr)) return hr; + WRL_METHOD(pMedDevCont, SetMediaStreamPropertiesAsync, *pAction, hr, WRL_ENUM_GET(_MediaStreamType, MediaStreamType, VideoPreview), DEREF_WRL_OBJ(pMedEncProps)) + return hr; +} +#endif + long videoDevice::setDeviceFormat(IMFMediaSource *pSource, unsigned long dwFormatIndex) { - ComPtr pPD = NULL; - ComPtr pSD = NULL; - ComPtr pHandler = NULL; - ComPtr pType = NULL; + _ComPtr pPD = NULL; + _ComPtr pSD = NULL; + _ComPtr pHandler = NULL; + _ComPtr pType = NULL; HRESULT hr = pSource->CreatePresentationDescriptor(pPD.GetAddressOf()); if (FAILED(hr)) { @@ -1925,12 +2575,14 @@ bool videoDevice::isDeviceSetup() RawImage * videoDevice::getRawImageOut() { if(!vd_IsSetuped) return NULL; +#ifdef HAVE_WINRT + if(vd_pImGr) return vd_pImGr->getRawImage(); +#endif if(vd_pImGrTh) return vd_pImGrTh->getImageGrabber()->getRawImage(); else { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); - DPO->printOut(L"VIDEODEVICE %i: The instance of ImageGrabberThread class does not exist \n", vd_CurrentNumber); + DebugPrintOut(L"VIDEODEVICE %i: The instance of ImageGrabberThread class does not exist \n", vd_CurrentNumber); } return NULL; } @@ -1943,17 +2595,38 @@ bool videoDevice::isFrameNew() if(vd_LockOut == OpenLock) { vd_LockOut = RawDataLock; + + //must already be closed +#ifdef HAVE_WINRT + if (DEREF_AGILE_WRL_OBJ(vd_pMedCap)) { + MAKE_WRL_REF(_AsyncAction) action; + if (FAILED(ImageGrabberWinRT::CreateInstance(&vd_pImGr))) return false; + if (FAILED(vd_pImGr->initImageGrabber(DEREF_AGILE_WRL_OBJ(vd_pMedCap), MFVideoFormat_RGB24)) || FAILED(vd_pImGr->startGrabbing(&action))) { + delete vd_pImGr; + return false; + } +#ifdef HAVE_CONCURRENCY + DEFINE_TASK task = CREATE_TASK DEFINE_RET_TYPE(void)(action); + auto func = [task]() { task.wait(); RET_VAL_BASE }; + CREATE_OR_CONTINUE_TASK(vd_pAction, void, func); +#endif + return true; + } +#endif HRESULT hr = ImageGrabberThread::CreateInstance(&vd_pImGrTh, vd_pSource, vd_CurrentNumber); if(FAILED(hr)) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); - DPO->printOut(L"VIDEODEVICE %i: The instance of ImageGrabberThread class cannot be created.\n", vd_CurrentNumber); + DebugPrintOut(L"VIDEODEVICE %i: The instance of ImageGrabberThread class cannot be created.\n", vd_CurrentNumber); return false; } vd_pImGrTh->setEmergencyStopEvent(vd_userData, vd_func); vd_pImGrTh->start(); return true; } +#ifdef HAVE_WINRT + if(vd_pImGr) + return vd_pImGr->getRawImage()->isNew(); +#endif if(vd_pImGrTh) return vd_pImGrTh->getImageGrabber()->getRawImage()->isNew(); } @@ -1974,39 +2647,93 @@ bool videoDevice::isDeviceRawDataSource() bool videoDevice::setupDevice(unsigned int id) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); if(!vd_IsSetuped) { - HRESULT hr = -1; - hr = initDevice(); + HRESULT hr = initDevice(); if(SUCCEEDED(hr)) { - vd_Width = vd_CurrentFormats[id].width; - vd_Height = vd_CurrentFormats[id].height; - hr = setDeviceFormat(vd_pSource, (DWORD) id); - vd_IsSetuped = (SUCCEEDED(hr)); - if(vd_IsSetuped) - DPO->printOut(L"\n\nVIDEODEVICE %i: Device is setuped \n", vd_CurrentNumber); - vd_PrevParametrs = getParametrs(); +#ifdef HAVE_WINRT +#ifdef HAVE_CONCURRENCY + SAVE_CURRENT_CONTEXT(context); + auto func = [context, id, this]()->DEFINE_RET_FORMAL(void) { + HRESULT hr; +#endif +#endif + vd_Width = vd_CurrentFormats[id].width; + vd_Height = vd_CurrentFormats[id].height; + vd_FrameRate = vd_CurrentFormats[id].MF_MT_FRAME_RATE_NUMERATOR / + vd_CurrentFormats[id].MF_MT_FRAME_RATE_DENOMINATOR; +#ifdef HAVE_WINRT +#ifdef HAVE_CONCURRENCY + if (DEREF_AGILE_WRL_OBJ(vd_pMedCap)) { + DEFINE_TASK _task; + BEGIN_CALL_IN_CONTEXT(hr, context, id, &_task, this) + MAKE_WRL_REF(_AsyncAction) pAction; + HRESULT hr = setDeviceFormat(DEREF_AGILE_WRL_OBJ(vd_pMedCap), (DWORD)id, &pAction); + if (SUCCEEDED(hr)) _task = CREATE_TASK DEFINE_RET_TYPE(void)(pAction); + END_CALL_IN_CONTEXT(hr) + if (SUCCEEDED(hr)) _task.wait(); + } + else +#endif +#endif + hr = setDeviceFormat(vd_pSource, (DWORD)id); + vd_IsSetuped = (SUCCEEDED(hr)); + if (vd_IsSetuped) + DebugPrintOut(L"\n\nVIDEODEVICE %i: Device is setuped \n", vd_CurrentNumber); + vd_PrevParametrs = getParametrs(); +#ifdef HAVE_WINRT +#ifdef HAVE_CONCURRENCY + RET_VAL_BASE + }; + CREATE_OR_CONTINUE_TASK(vd_pAction, void, func); +#endif + return true; +#else return vd_IsSetuped; +#endif } else { - DPO->printOut(L"VIDEODEVICE %i: Interface IMFMediaSource cannot be got \n", vd_CurrentNumber); + DebugPrintOut(L"VIDEODEVICE %i: Interface IMFMediaSource cannot be got \n", vd_CurrentNumber); return false; } } else { - DPO->printOut(L"VIDEODEVICE %i: Device is setuped already \n", vd_CurrentNumber); + DebugPrintOut(L"VIDEODEVICE %i: Device is setuped already \n", vd_CurrentNumber); return false; } } bool videoDevice::setupDevice(unsigned int w, unsigned int h, unsigned int idealFramerate) { - unsigned int id = findType(w * h, idealFramerate); - return setupDevice(id); +#ifdef HAVE_WINRT +#ifdef HAVE_CONCURRENCY + SAVE_CURRENT_CONTEXT(context); + auto func = [context, w, h, idealFramerate, this]()->DEFINE_RET_FORMAL(void) { + HRESULT hr; + BEGIN_CALL_IN_CONTEXT(hr, context, w, h, idealFramerate, this) +#endif +#endif + int id = findType(w * h, idealFramerate); + if (id < 0) +#ifdef HAVE_WINRT + {} else +#else + return false; + return +#endif + setupDevice(id); +#ifdef HAVE_WINRT +#ifdef HAVE_CONCURRENCY + END_CALL_IN_CONTEXT_BASE + RET_VAL_BASE + }; + CREATE_OR_CONTINUE_TASK(vd_pAction, void, func); + return true; +#endif +#endif } wchar_t *videoDevice::getName() @@ -2017,17 +2744,49 @@ wchar_t *videoDevice::getName() videoDevice::~videoDevice(void) { closeDevice(); +#ifdef HAVE_WINRT + RELEASE_WRL(vd_pMedCap) +#endif SafeRelease(&vd_pSource); if(vd_pFriendlyName) CoTaskMemFree(vd_pFriendlyName); } +#ifdef HAVE_WINRT +HRESULT videoDevice::enumerateCaptureFormats(MAKE_WRL_REF(_MediaCapture) pSource) +{ + HRESULT hr; + MAKE_WRL_OBJ(_VideoDeviceController) pDevCont; + WRL_PROP_GET(pSource, VideoDeviceController, pDevCont, hr) + if (FAILED(hr)) return hr; + GET_WRL_OBJ_FROM_OBJ(_MediaDeviceController, pMedDevCont, pDevCont, hr) + if (FAILED(hr)) return hr; + MAKE_WRL_OBJ(_VectorView) pVector; + WRL_METHOD(pMedDevCont, GetAvailableMediaStreamProperties, pVector, hr, WRL_ENUM_GET(_MediaStreamType, MediaStreamType, VideoPreview)) + if (FAILED(hr)) return hr; + UINT32 count; + WRL_PROP_GET(pVector, Size, count, hr) + if (FAILED(hr)) return hr; + for (UINT32 i = 0; i < count; i++) { + MAKE_WRL_OBJ(_MediaEncodingProperties) pMedEncProps; + WRL_METHOD(pVector, GetAt, pMedEncProps, hr, i) + if (FAILED(hr)) return hr; + _ComPtr pType = NULL; + hr = MediaSink::ConvertPropertiesToMediaType(DEREF_AS_NATIVE_WRL_OBJ(ABI::Windows::Media::MediaProperties::IMediaEncodingProperties, pMedEncProps), &pType); + if (FAILED(hr)) return hr; + MediaType MT = FormatReader::Read(pType.Get()); + vd_CurrentFormats.push_back(MT); + } + return hr; +} +#endif + HRESULT videoDevice::enumerateCaptureFormats(IMFMediaSource *pSource) { - ComPtr pPD = NULL; - ComPtr pSD = NULL; - ComPtr pHandler = NULL; - ComPtr pType = NULL; + _ComPtr pPD = NULL; + _ComPtr pSD = NULL; + _ComPtr pHandler = NULL; + _ComPtr pType = NULL; HRESULT hr = pSource->CreatePresentationDescriptor(pPD.GetAddressOf()); if (FAILED(hr)) { @@ -2066,7 +2825,8 @@ done: } videoDevices::videoDevices(void): count(0) -{} +{ +} void videoDevices::clearDevices() { @@ -2094,12 +2854,47 @@ videoDevice * videoDevices::getDevice(unsigned int i) return vds_Devices[i]; } -long videoDevices::initDevices(IMFAttributes *pAttributes) +#ifdef HAVE_WINRT +long videoDevices::initDevices(_DeviceClass devClass) { HRESULT hr = S_OK; - IMFActivate **ppDevices = NULL; + ACTIVATE_STATIC_OBJ(RuntimeClass_Windows_Devices_Enumeration_DeviceInformation, MAKE_WRL_OBJ(_DeviceInformationStatics), pDevStat, hr) + if (FAILED(hr)) return hr; + MAKE_WRL_REF(_AsyncOperation) pAction; + WRL_METHOD(pDevStat, _FindAllAsyncDeviceClass, pAction, hr, devClass) + if (SUCCEEDED(hr)) { +#ifdef HAVE_CONCURRENCY + SAVE_CURRENT_CONTEXT(context); + vds_enumTask = CREATE_TASK DEFINE_RET_TYPE(void)([pAction, context, this]()->DEFINE_RET_FORMAL(void) { + HRESULT hr = S_OK; + MAKE_WRL_OBJ(_VectorView) pVector = + CREATE_TASK DEFINE_RET_TYPE(MAKE_WRL_REF(_VectorView))(pAction).get(); + if (SUCCEEDED(hr)) WRL_PROP_GET(pVector, Size, count, hr) + if (SUCCEEDED(hr) && count > 0) { + for (UINT32 i = 0; i < count; i++) { + videoDevice *vd = new videoDevice; + MAKE_WRL_OBJ(_IDeviceInformation) pDevice; + WRL_METHOD(pVector, GetAt, pDevice, hr, i) + if (SUCCEEDED(hr)) { + BEGIN_CALL_IN_CONTEXT(hr, context, vd, pDevice, i) + vd->readInfoOfDevice(DEREF_WRL_OBJ(pDevice), i); + END_CALL_IN_CONTEXT_BASE + vds_Devices.push_back(vd); + } + } + } + RET_VAL_BASE + }); +#endif + } + return hr; +} +#else +long videoDevices::initDevices(IMFAttributes *pAttributes) +{ clearDevices(); - hr = MFEnumDeviceSources(pAttributes, &ppDevices, &count); + IMFActivate **ppDevices = NULL; + HRESULT hr = MFEnumDeviceSources(pAttributes, &ppDevices, &count); if (SUCCEEDED(hr)) { if(count > 0) @@ -2114,19 +2909,19 @@ long videoDevices::initDevices(IMFAttributes *pAttributes) SafeRelease(ppDevices); } else - hr = -1; + hr = E_INVALIDARG; } else { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); - DPO->printOut(L"VIDEODEVICES: The instances of the videoDevice class cannot be created\n"); + DebugPrintOut(L"VIDEODEVICES: The instances of the videoDevice class cannot be created\n"); } return hr; } +#endif unsigned int videoDevices::getCount() { - return vds_Devices.size(); + return (unsigned int)vds_Devices.size(); } videoDevices& videoDevices::getInstance() @@ -2188,56 +2983,50 @@ void MediaType::Clear() videoInput::videoInput(void): accessToDevices(false) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); - DPO->printOut(L"\n***** VIDEOINPUT LIBRARY - 2013 (Author: Evgeny Pereguda) *****\n\n"); + DebugPrintOut(L"\n***** VIDEOINPUT LIBRARY - 2013 (Author: Evgeny Pereguda) *****\n\n"); updateListOfDevices(); if(!accessToDevices) - DPO->printOut(L"INITIALIZATION: Ther is not any suitable video device\n"); + DebugPrintOut(L"INITIALIZATION: There is not any suitable video device\n"); } void videoInput::updateListOfDevices() { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); Media_Foundation *MF = &Media_Foundation::getInstance(); accessToDevices = MF->buildListOfDevices(); if(!accessToDevices) - DPO->printOut(L"UPDATING: Ther is not any suitable video device\n"); + DebugPrintOut(L"UPDATING: There is not any suitable video device\n"); } videoInput::~videoInput(void) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); - DPO->printOut(L"\n***** CLOSE VIDEOINPUT LIBRARY - 2013 *****\n\n"); + DebugPrintOut(L"\n***** CLOSE VIDEOINPUT LIBRARY - 2013 *****\n\n"); } IMFMediaSource *videoInput::getMediaSource(int deviceID) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); if(accessToDevices) { - videoDevices *VDS = &videoDevices::getInstance(); - videoDevice * VD = VDS->getDevice(deviceID); + videoDevice * VD = videoDevices::getInstance().getDevice(deviceID); if(VD) { IMFMediaSource *out = VD->getMediaSource(); if(!out) - DPO->printOut(L"VideoDevice %i: There is not any suitable IMFMediaSource interface\n", deviceID); + DebugPrintOut(L"VideoDevice %i: There is not any suitable IMFMediaSource interface\n", deviceID); return out; } } else { - DPO->printOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); + DebugPrintOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); } return NULL; } bool videoInput::setupDevice(int deviceID, unsigned int id) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); if (deviceID < 0 ) { - DPO->printOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); + DebugPrintOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); return false; } if(accessToDevices) @@ -2248,23 +3037,22 @@ bool videoInput::setupDevice(int deviceID, unsigned int id) { bool out = VD->setupDevice(id); if(!out) - DPO->printOut(L"VIDEODEVICE %i: This device cannot be started\n", deviceID); + DebugPrintOut(L"VIDEODEVICE %i: This device cannot be started\n", deviceID); return out; } } else { - DPO->printOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); + DebugPrintOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); } return false; } bool videoInput::setupDevice(int deviceID, unsigned int w, unsigned int h, unsigned int idealFramerate) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); if (deviceID < 0 ) { - DPO->printOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); + DebugPrintOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); return false; } if(accessToDevices) @@ -2275,23 +3063,22 @@ bool videoInput::setupDevice(int deviceID, unsigned int w, unsigned int h, unsig { bool out = VD->setupDevice(w, h, idealFramerate); if(!out) - DPO->printOut(L"VIDEODEVICE %i: this device cannot be started\n", deviceID); + DebugPrintOut(L"VIDEODEVICE %i: this device cannot be started\n", deviceID); return out; } } else { - DPO->printOut(L"VIDEODEVICE(s): There is not any suitable video device\n", deviceID); + DebugPrintOut(L"VIDEODEVICE(s): There is not any suitable video device\n", deviceID); } return false; } MediaType videoInput::getFormat(int deviceID, unsigned int id) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); if (deviceID < 0) { - DPO->printOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); + DebugPrintOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); return MediaType(); } if(accessToDevices) @@ -2303,17 +3090,16 @@ MediaType videoInput::getFormat(int deviceID, unsigned int id) } else { - DPO->printOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); + DebugPrintOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); } return MediaType(); } bool videoInput::isDeviceSetup(int deviceID) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); if (deviceID < 0) { - DPO->printOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); + DebugPrintOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); return false; } if(accessToDevices) @@ -2325,17 +3111,16 @@ bool videoInput::isDeviceSetup(int deviceID) } else { - DPO->printOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); + DebugPrintOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); } return false; } bool videoInput::isDeviceMediaSource(int deviceID) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); if (deviceID < 0) { - DPO->printOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); + DebugPrintOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); return false; } if(accessToDevices) @@ -2347,17 +3132,16 @@ bool videoInput::isDeviceMediaSource(int deviceID) } else { - DPO->printOut(L"Device(s): There is not any suitable video device\n"); + DebugPrintOut(L"Device(s): There is not any suitable video device\n"); } return false; } bool videoInput::isDeviceRawDataSource(int deviceID) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); if (deviceID < 0) { - DPO->printOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); + DebugPrintOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); return false; } if(accessToDevices) @@ -2372,17 +3156,16 @@ bool videoInput::isDeviceRawDataSource(int deviceID) } else { - DPO->printOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); + DebugPrintOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); } return false; } bool videoInput::isFrameNew(int deviceID) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); if (deviceID < 0) { - DPO->printOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); + DebugPrintOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); return false; } if(accessToDevices) @@ -2401,17 +3184,46 @@ bool videoInput::isFrameNew(int deviceID) } else { - DPO->printOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); + DebugPrintOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); } return false; } +#ifdef HAVE_WINRT +void videoInput::waitForDevice(int deviceID) +{ + if (deviceID < 0) + { + DebugPrintOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); + return; + } + if(accessToDevices) + { + if(!isDeviceSetup(deviceID)) + { + if(isDeviceMediaSource(deviceID)) + return; + } + videoDevices *VDS = &videoDevices::getInstance(); + videoDevice * VD = VDS->getDevice(deviceID); + if(VD) + { + VD->waitForDevice(); + } + } + else + { + DebugPrintOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); + } + return; +} +#endif + unsigned int videoInput::getCountFormats(int deviceID) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); if (deviceID < 0) { - DPO->printOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); + DebugPrintOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); return 0; } if(accessToDevices) @@ -2423,7 +3235,7 @@ unsigned int videoInput::getCountFormats(int deviceID) } else { - DPO->printOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); + DebugPrintOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); } return 0; } @@ -2437,10 +3249,9 @@ void videoInput::closeAllDevices() void videoInput::setParametrs(int deviceID, CamParametrs parametrs) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); if (deviceID < 0) { - DPO->printOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); + DebugPrintOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); return; } if(accessToDevices) @@ -2452,17 +3263,16 @@ void videoInput::setParametrs(int deviceID, CamParametrs parametrs) } else { - DPO->printOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); + DebugPrintOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); } } CamParametrs videoInput::getParametrs(int deviceID) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); CamParametrs out; if (deviceID < 0) { - DPO->printOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); + DebugPrintOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); return out; } if(accessToDevices) @@ -2474,17 +3284,16 @@ CamParametrs videoInput::getParametrs(int deviceID) } else { - DPO->printOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); + DebugPrintOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); } return out; } void videoInput::closeDevice(int deviceID) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); if (deviceID < 0) { - DPO->printOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); + DebugPrintOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); return; } if(accessToDevices) @@ -2496,16 +3305,15 @@ void videoInput::closeDevice(int deviceID) } else { - DPO->printOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); + DebugPrintOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); } } unsigned int videoInput::getWidth(int deviceID) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); if (deviceID < 0) { - DPO->printOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); + DebugPrintOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); return 0; } if(accessToDevices) @@ -2517,17 +3325,16 @@ unsigned int videoInput::getWidth(int deviceID) } else { - DPO->printOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); + DebugPrintOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); } return 0; } unsigned int videoInput::getHeight(int deviceID) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); if (deviceID < 0) { - DPO->printOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); + DebugPrintOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); return 0; } if(accessToDevices) @@ -2539,17 +3346,36 @@ unsigned int videoInput::getHeight(int deviceID) } else { - DPO->printOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); + DebugPrintOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); + } + return 0; +} + +unsigned int videoInput::getFrameRate(int deviceID) const +{ + if (deviceID < 0) + { + DebugPrintOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); + return 0; + } + if(accessToDevices) + { + videoDevice * VD = videoDevices::getInstance().getDevice(deviceID); + if(VD) + return VD->getFrameRate(); + } + else + { + DebugPrintOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); } return 0; } wchar_t *videoInput::getNameVideoDevice(int deviceID) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); if (deviceID < 0) { - DPO->printOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); + DebugPrintOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); return NULL; } if(accessToDevices) @@ -2561,31 +3387,32 @@ wchar_t *videoInput::getNameVideoDevice(int deviceID) } else { - DPO->printOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); + DebugPrintOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); } return L"Empty"; } unsigned int videoInput::listDevices(bool silent) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); int out = 0; if(accessToDevices) { videoDevices *VDS = &videoDevices::getInstance(); +#ifdef HAVE_WINRT + VDS->waitInit(); +#endif out = VDS->getCount(); - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); - if(!silent)DPO->printOut(L"\nVIDEOINPUT SPY MODE!\n\n"); - if(!silent)DPO->printOut(L"SETUP: Looking For Capture Devices\n"); + if(!silent) DebugPrintOut(L"\nVIDEOINPUT SPY MODE!\n\n"); + if(!silent) DebugPrintOut(L"SETUP: Looking For Capture Devices\n"); for(int i = 0; i < out; i++) { - if(!silent)DPO->printOut(L"SETUP: %i) %s \n",i, getNameVideoDevice(i)); + if(!silent) DebugPrintOut(L"SETUP: %i) %s \n",i, getNameVideoDevice(i)); } - if(!silent)DPO->printOut(L"SETUP: %i Device(s) found\n\n", out); + if(!silent) DebugPrintOut(L"SETUP: %i Device(s) found\n\n", out); } else { - DPO->printOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); + DebugPrintOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); } return out; } @@ -2601,18 +3428,19 @@ bool videoInput::isDevicesAcceable() return accessToDevices; } +#ifdef _DEBUG void videoInput::setVerbose(bool state) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); - DPO->setVerbose(state); + DPO *dpo = &DPO::getInstance(); + dpo->setVerbose(state); } +#endif void videoInput::setEmergencyStopEvent(int deviceID, void *userData, void(*func)(int, void *)) { - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); if (deviceID < 0) { - DPO->printOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); + DebugPrintOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); return; } if(accessToDevices) @@ -2627,18 +3455,16 @@ void videoInput::setEmergencyStopEvent(int deviceID, void *userData, void(*func) } else { - DPO->printOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); + DebugPrintOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); } } bool videoInput::getPixels(int deviceID, unsigned char * dstBuffer, bool flipRedAndBlue, bool flipImage) { bool success = false; - unsigned int bytes = 3; - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); if (deviceID < 0) { - DPO->printOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); + DebugPrintOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID); return success; } if(accessToDevices) @@ -2646,14 +3472,14 @@ bool videoInput::getPixels(int deviceID, unsigned char * dstBuffer, bool flipRed bool isRaw = isDeviceRawDataSource(deviceID); if(isRaw) { - videoDevices *VDS = &videoDevices::getInstance(); - DebugPrintOut *DPO = &DebugPrintOut::getInstance(); - RawImage *RIOut = VDS->getDevice(deviceID)->getRawImageOut(); + videoDevice *VD = videoDevices::getInstance().getDevice(deviceID); + RawImage *RIOut = VD->getRawImageOut(); if(RIOut) { - unsigned int height = VDS->getDevice(deviceID)->getHeight(); - unsigned int width = VDS->getDevice(deviceID)->getWidth(); - unsigned int size = bytes * width * height; + const unsigned int bytes = 3; + const unsigned int height = VD->getHeight(); + const unsigned int width = VD->getWidth(); + const unsigned int size = bytes * width * height; if(size == RIOut->getSize()) { processPixels(RIOut->getpPixels(), dstBuffer, width, height, bytes, flipRedAndBlue, flipImage); @@ -2661,22 +3487,22 @@ bool videoInput::getPixels(int deviceID, unsigned char * dstBuffer, bool flipRed } else { - DPO->printOut(L"ERROR: GetPixels() - bufferSizes do not match!\n"); + DebugPrintOut(L"ERROR: GetPixels() - bufferSizes do not match!\n"); } } else { - DPO->printOut(L"ERROR: GetPixels() - Unable to grab frame for device %i\n", deviceID); + DebugPrintOut(L"ERROR: GetPixels() - Unable to grab frame for device %i\n", deviceID); } } else { - DPO->printOut(L"ERROR: GetPixels() - Not raw data source device %i\n", deviceID); + DebugPrintOut(L"ERROR: GetPixels() - Not raw data source device %i\n", deviceID); } } else { - DPO->printOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); + DebugPrintOut(L"VIDEODEVICE(s): There is not any suitable video device\n"); } return success; } @@ -2762,14 +3588,22 @@ protected: int index, width, height, fourcc; IplImage* frame; videoInput VI; +#ifdef HAVE_WINRT +#ifdef HAVE_CONCURRENCY + DEFINE_TASK openTask; + Concurrency::critical_section lock; +#endif +#endif }; +#ifdef _DEBUG struct SuppressVideoInputMessages { SuppressVideoInputMessages() { videoInput::setVerbose(true); } }; static SuppressVideoInputMessages do_it; +#endif CvCaptureCAM_MSMF::CvCaptureCAM_MSMF(): index(-1), @@ -2802,6 +3636,12 @@ void CvCaptureCAM_MSMF::close() // Initialize camera input bool CvCaptureCAM_MSMF::open( int _index ) { +#ifdef HAVE_WINRT +#ifdef HAVE_CONCURRENCY + SAVE_CURRENT_CONTEXT(context); + auto func = [_index, context, this](DEFINE_RET_VAL(bool)) -> DEFINE_RET_FORMAL(bool) { +#endif +#endif int try_index = _index; int devices = 0; close(); @@ -2809,10 +3649,36 @@ bool CvCaptureCAM_MSMF::open( int _index ) if (devices == 0) return false; try_index = try_index < 0 ? 0 : (try_index > devices-1 ? devices-1 : try_index); - VI.setupDevice(try_index); +#ifdef HAVE_WINRT + HRESULT hr; +#ifdef HAVE_CONCURRENCY + BEGIN_CALL_IN_CONTEXT(hr, context, this, try_index) +#endif +#endif + VI.setupDevice(try_index, 0, 0, 0); // With maximum frame size. +#ifdef HAVE_WINRT +#ifdef HAVE_CONCURRENCY + END_CALL_IN_CONTEXT_BASE + VI.waitForDevice(try_index); + BEGIN_CALL_IN_CONTEXT(hr, context, this, try_index) + HRESULT hr = S_OK; +#endif +#endif if( !VI.isFrameNew(try_index) ) +#ifdef HAVE_WINRT + hr = E_FAIL; +#else return false; +#endif index = try_index; +#ifdef HAVE_WINRT +#ifdef HAVE_CONCURRENCY + END_CALL_IN_CONTEXT_BASE + RET_VAL(true) + }; + CREATE_OR_CONTINUE_TASK_RET(openTask, bool, func) +#endif +#endif return true; } @@ -2825,11 +3691,12 @@ bool CvCaptureCAM_MSMF::grabFrame() IplImage* CvCaptureCAM_MSMF::retrieveFrame(int) { - if( !frame || (int)VI.getWidth(index) != frame->width || (int)VI.getHeight(index) != frame->height ) + const int w = (int)VI.getWidth(index); + const int h = (int)VI.getHeight(index); + if( !frame || w != frame->width || h != frame->height ) { if (frame) cvReleaseImage( &frame ); - unsigned int w = VI.getWidth(index), h = VI.getHeight(index); frame = cvCreateImage( cvSize(w,h), 8, 3 ); } VI.getPixels( index, (uchar*)frame->imageData, false, true ); @@ -2845,33 +3712,47 @@ double CvCaptureCAM_MSMF::getProperty( int property_id ) return VI.getWidth(index); case CV_CAP_PROP_FRAME_HEIGHT: return VI.getHeight(index); + case CV_CAP_PROP_FPS: + return VI.getFrameRate(index); + default: + break; } - return -1; + return 0; } bool CvCaptureCAM_MSMF::setProperty( int property_id, double value ) { // image capture properties + unsigned int fps = 0; bool handled = false; switch( property_id ) { case CV_CAP_PROP_FRAME_WIDTH: width = cvRound(value); + fps = VI.getFrameRate(index); handled = true; break; case CV_CAP_PROP_FRAME_HEIGHT: height = cvRound(value); + fps = VI.getFrameRate(index); handled = true; break; + case CV_CAP_PROP_FPS: + width = (int)VI.getHeight(index); + height = (int)VI.getWidth(index); + fps = cvRound(value); + break; } if ( handled ) { if( width > 0 && height > 0 ) { - if( width != (int)VI.getWidth(index) || height != (int)VI.getHeight(index) && VI.isDeviceSetup(index))//|| fourcc != VI.getFourcc(index) ) + if( (width != (int)VI.getWidth(index) || height != (int)VI.getHeight(index) || fps != VI.getFrameRate(index)) + && VI.isDeviceSetup(index))//|| fourcc != VI.getFourcc(index) ) { VI.closeDevice(index); - VI.setupDevice(index, width, height); + VI.setupDevice(index, width, height, fps); } + width = height = -1; return VI.isDeviceSetup(index); } return true; @@ -2928,16 +3809,14 @@ bool CvCaptureFile_MSMF::open(const char* filename) return false; wchar_t* unicodeFileName = new wchar_t[strlen(filename)+1]; - MultiByteToWideChar(CP_ACP, 0, filename, -1, unicodeFileName, strlen(filename)+1); - - HRESULT hr = S_OK; + MultiByteToWideChar(CP_ACP, 0, filename, -1, unicodeFileName, (int)strlen(filename)+1); MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID; - ComPtr pSourceResolver = NULL; + _ComPtr pSourceResolver = NULL; IUnknown* pUnkSource = NULL; - hr = MFCreateSourceResolver(pSourceResolver.GetAddressOf()); + HRESULT hr = MFCreateSourceResolver(pSourceResolver.GetAddressOf()); if (SUCCEEDED(hr)) { @@ -3071,10 +3950,10 @@ IplImage* CvCaptureFile_MSMF::retrieveFrame(int) HRESULT CvCaptureFile_MSMF::enumerateCaptureFormats(IMFMediaSource *pSource) { - ComPtr pPD = NULL; - ComPtr pSD = NULL; - ComPtr pHandler = NULL; - ComPtr pType = NULL; + _ComPtr pPD = NULL; + _ComPtr pSD = NULL; + _ComPtr pHandler = NULL; + _ComPtr pType = NULL; HRESULT hr = pSource->CreatePresentationDescriptor(pPD.GetAddressOf()); if (FAILED(hr)) { @@ -3191,7 +4070,7 @@ private: GUID inputFormat; DWORD streamIndex; - ComPtr sinkWriter; + _ComPtr sinkWriter; bool initiated; @@ -3231,8 +4110,10 @@ const GUID CvVideoWriter_MSMF::FourCC2GUID(int fourcc) return MFVideoFormat_DVSD; break; case CV_FOURCC_MACRO('d', 'v', 's', 'l'): return MFVideoFormat_DVSL; break; - case CV_FOURCC_MACRO('H', '2', '6', '3'): +#if (WINVER >= _WIN32_WINNT_WIN8) + case CV_FOURCC_MACRO('H', '2', '6', '3'): // Available only for Win 8 target. return MFVideoFormat_H263; break; +#endif case CV_FOURCC_MACRO('H', '2', '6', '4'): return MFVideoFormat_H264; break; case CV_FOURCC_MACRO('M', '4', 'S', '2'): @@ -3342,16 +4223,16 @@ bool CvVideoWriter_MSMF::writeFrame(const IplImage* img) HRESULT CvVideoWriter_MSMF::InitializeSinkWriter(const char* filename) { - ComPtr spAttr; - ComPtr mediaTypeOut; - ComPtr mediaTypeIn; - ComPtr spByteStream; + _ComPtr spAttr; + _ComPtr mediaTypeOut; + _ComPtr mediaTypeIn; + _ComPtr spByteStream; MFCreateAttributes(&spAttr, 10); spAttr->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, true); wchar_t* unicodeFileName = new wchar_t[strlen(filename)+1]; - MultiByteToWideChar(CP_ACP, 0, filename, -1, unicodeFileName, strlen(filename)+1); + MultiByteToWideChar(CP_ACP, 0, filename, -1, unicodeFileName, (int)strlen(filename)+1); HRESULT hr = MFCreateSinkWriterFromURL(unicodeFileName, NULL, spAttr.Get(), &sinkWriter); @@ -3442,8 +4323,8 @@ HRESULT CvVideoWriter_MSMF::InitializeSinkWriter(const char* filename) HRESULT CvVideoWriter_MSMF::WriteFrame(DWORD *videoFrameBuffer, const LONGLONG& Start, const LONGLONG& Duration) { - ComPtr sample; - ComPtr buffer; + _ComPtr sample; + _ComPtr buffer; const LONG cbWidth = 4 * videoWidth; const DWORD cbBuffer = cbWidth * videoHeight; diff --git a/modules/highgui/src/cap_msmf.hpp b/modules/highgui/src/cap_msmf.hpp new file mode 100644 index 0000000000..c03f1ecc3f --- /dev/null +++ b/modules/highgui/src/cap_msmf.hpp @@ -0,0 +1,3279 @@ +#ifdef HAVE_WINRT +#define ICustomStreamSink StreamSink +#ifndef __cplusplus_winrt + +#define __is_winrt_array(type) (type == ABI::Windows::Foundation::PropertyType::PropertyType_UInt8Array || type == ABI::Windows::Foundation::PropertyType::PropertyType_Int16Array ||\ + type == ABI::Windows::Foundation::PropertyType::PropertyType_UInt16Array || type == ABI::Windows::Foundation::PropertyType::PropertyType_Int32Array ||\ + type == ABI::Windows::Foundation::PropertyType::PropertyType_UInt32Array || type == ABI::Windows::Foundation::PropertyType::PropertyType_Int64Array ||\ + type == ABI::Windows::Foundation::PropertyType::PropertyType_UInt64Array || type == ABI::Windows::Foundation::PropertyType::PropertyType_SingleArray ||\ + type == ABI::Windows::Foundation::PropertyType::PropertyType_DoubleArray || type == ABI::Windows::Foundation::PropertyType::PropertyType_Char16Array ||\ + type == ABI::Windows::Foundation::PropertyType::PropertyType_BooleanArray || type == ABI::Windows::Foundation::PropertyType::PropertyType_StringArray ||\ + type == ABI::Windows::Foundation::PropertyType::PropertyType_InspectableArray || type == ABI::Windows::Foundation::PropertyType::PropertyType_DateTimeArray ||\ + type == ABI::Windows::Foundation::PropertyType::PropertyType_TimeSpanArray || type == ABI::Windows::Foundation::PropertyType::PropertyType_GuidArray ||\ + type == ABI::Windows::Foundation::PropertyType::PropertyType_PointArray || type == ABI::Windows::Foundation::PropertyType::PropertyType_SizeArray ||\ + type == ABI::Windows::Foundation::PropertyType::PropertyType_RectArray || type == ABI::Windows::Foundation::PropertyType::PropertyType_OtherTypeArray) + +template::value> +struct winrt_type +{ +}; +template +struct winrt_type<_Type, true> +{ + static IUnknown* create(_Type* _ObjInCtx) { + return reinterpret_cast(_ObjInCtx); + } + static IID getuuid() { return __uuidof(_Type); } + static const ABI::Windows::Foundation::PropertyType _PropType = ABI::Windows::Foundation::PropertyType::PropertyType_OtherType; +}; +template +struct winrt_type<_Type, false> +{ + static IUnknown* create(_Type* _ObjInCtx) { + Microsoft::WRL::ComPtr _PObj; + Microsoft::WRL::ComPtr objFactory; + HRESULT hr = Windows::Foundation::GetActivationFactory(Microsoft::WRL::Wrappers::HStringReference(RuntimeClass_Windows_Foundation_PropertyValue).Get(), objFactory.ReleaseAndGetAddressOf()); + if (FAILED(hr)) return nullptr; + Microsoft::WRL::ComPtr spPropVal; + if (SUCCEEDED(hr)) + hr = objFactory.As(&spPropVal); + if (SUCCEEDED(hr)) { + hr = winrt_type<_Type>::create(spPropVal.Get(), _ObjInCtx, _PObj.GetAddressOf()); + if (SUCCEEDED(hr)) + return reinterpret_cast(_PObj.Detach()); + } + return nullptr; + } + static IID getuuid() { return __uuidof(ABI::Windows::Foundation::IPropertyValue); } + static const ABI::Windows::Foundation::PropertyType _PropType = ABI::Windows::Foundation::PropertyType::PropertyType_OtherType; +}; + +template<> +struct winrt_type +{ + static HRESULT create(ABI::Windows::Foundation::IPropertyValueStatics* spPropVal, void* _ObjInCtx, IInspectable** ppInsp) { + (void)_ObjInCtx; + return spPropVal->CreateEmpty(ppInsp); + } + static const ABI::Windows::Foundation::PropertyType _PropType = ABI::Windows::Foundation::PropertyType::PropertyType_Empty; +}; +#define MAKE_TYPE(Type, Name) template<>\ +struct winrt_type\ +{\ + static HRESULT create(ABI::Windows::Foundation::IPropertyValueStatics* spPropVal, Type* _ObjInCtx, IInspectable** ppInsp) {\ + return spPropVal->Create##Name(*_ObjInCtx, ppInsp);\ +}\ + static const ABI::Windows::Foundation::PropertyType _PropType = ABI::Windows::Foundation::PropertyType::PropertyType_##Name;\ +}; + +template +struct winrt_array_type +{ + static IUnknown* create(_Type* _ObjInCtx, size_t N) { + Microsoft::WRL::ComPtr _PObj; + Microsoft::WRL::ComPtr objFactory; + HRESULT hr = Windows::Foundation::GetActivationFactory(Microsoft::WRL::Wrappers::HStringReference(RuntimeClass_Windows_Foundation_PropertyValue).Get(), objFactory.ReleaseAndGetAddressOf()); + if (FAILED(hr)) return nullptr; + Microsoft::WRL::ComPtr spPropVal; + if (SUCCEEDED(hr)) + hr = objFactory.As(&spPropVal); + if (SUCCEEDED(hr)) { + hr = winrt_array_type<_Type>::create(spPropVal.Get(), N, _ObjInCtx, _PObj.GetAddressOf()); + if (SUCCEEDED(hr)) + return reinterpret_cast(_PObj.Detach()); + } + return nullptr; + } + static const ABI::Windows::Foundation::PropertyType _PropType = ABI::Windows::Foundation::PropertyType::PropertyType_OtherTypeArray; +}; +template +struct winrt_prop_type {}; + +template <> +struct winrt_prop_type { + typedef void _Type; +}; + +template <> +struct winrt_prop_type { + typedef void _Type; +}; + +template <> +struct winrt_prop_type { + typedef void _Type; +}; + +#define MAKE_PROP(Prop, Type) template <>\ +struct winrt_prop_type {\ + typedef Type _Type;\ +}; + +#define MAKE_ARRAY_TYPE(Type, Name) MAKE_PROP(Name, Type)\ + MAKE_PROP(Name##Array, Type*)\ + MAKE_TYPE(Type, Name)\ + template<>\ +struct winrt_array_type\ +{\ + static HRESULT create(ABI::Windows::Foundation::IPropertyValueStatics* spPropVal, UINT32 __valueSize, Type** _ObjInCtx, IInspectable** ppInsp) {\ + return spPropVal->Create##Name##Array(__valueSize, *_ObjInCtx, ppInsp);\ +}\ + static const ABI::Windows::Foundation::PropertyType _PropType = ABI::Windows::Foundation::PropertyType::PropertyType_##Name##Array;\ + static std::vector PropertyValueToVector(ABI::Windows::Foundation::IPropertyValue* propValue)\ +{\ + UINT32 uLen = 0;\ + Type* pArray = nullptr;\ + propValue->Get##Name##Array(&uLen, &pArray);\ + return std::vector(pArray, pArray + uLen);\ +}\ +}; +MAKE_ARRAY_TYPE(BYTE, UInt8) +MAKE_ARRAY_TYPE(INT16, Int16) +MAKE_ARRAY_TYPE(UINT16, UInt16) +MAKE_ARRAY_TYPE(INT32, Int32) +MAKE_ARRAY_TYPE(UINT32, UInt32) +MAKE_ARRAY_TYPE(INT64, Int64) +MAKE_ARRAY_TYPE(UINT64, UInt64) +MAKE_ARRAY_TYPE(FLOAT, Single) +MAKE_ARRAY_TYPE(DOUBLE, Double) +MAKE_ARRAY_TYPE(WCHAR, Char16) +//MAKE_ARRAY_TYPE(boolean, Boolean) //conflict with identical type in C++ of BYTE/UInt8 +MAKE_ARRAY_TYPE(HSTRING, String) +MAKE_ARRAY_TYPE(IInspectable*, Inspectable) +MAKE_ARRAY_TYPE(GUID, Guid) +MAKE_ARRAY_TYPE(ABI::Windows::Foundation::DateTime, DateTime) +MAKE_ARRAY_TYPE(ABI::Windows::Foundation::TimeSpan, TimeSpan) +MAKE_ARRAY_TYPE(ABI::Windows::Foundation::Point, Point) +MAKE_ARRAY_TYPE(ABI::Windows::Foundation::Size, Size) +MAKE_ARRAY_TYPE(ABI::Windows::Foundation::Rect, Rect) + +template < typename T > +struct DerefHelper +{ + typedef T DerefType; +}; + +template < typename T > +struct DerefHelper +{ + typedef T DerefType; +}; + +#define __is_valid_winrt_type(_Type) (std::is_void<_Type>::value || \ + std::is_same<_Type, BYTE>::value || \ + std::is_same<_Type, INT16>::value || \ + std::is_same<_Type, UINT16>::value || \ + std::is_same<_Type, INT32>::value || \ + std::is_same<_Type, UINT32>::value || \ + std::is_same<_Type, INT64>::value || \ + std::is_same<_Type, UINT64>::value || \ + std::is_same<_Type, FLOAT>::value || \ + std::is_same<_Type, DOUBLE>::value || \ + std::is_same<_Type, WCHAR>::value || \ + std::is_same<_Type, boolean>::value || \ + std::is_same<_Type, HSTRING>::value || \ + std::is_same<_Type, IInspectable *>::value || \ + std::is_base_of::value || \ + std::is_base_of::DerefType>::value || \ + std::is_same<_Type, GUID>::value || \ + std::is_same<_Type, ABI::Windows::Foundation::DateTime>::value || \ + std::is_same<_Type, ABI::Windows::Foundation::TimeSpan>::value || \ + std::is_same<_Type, ABI::Windows::Foundation::Point>::value || \ + std::is_same<_Type, ABI::Windows::Foundation::Size>::value || \ + std::is_same<_Type, ABI::Windows::Foundation::Rect>::value || \ + std::is_same<_Type, BYTE*>::value || \ + std::is_same<_Type, INT16*>::value || \ + std::is_same<_Type, UINT16*>::value || \ + std::is_same<_Type, INT32*>::value || \ + std::is_same<_Type, UINT32*>::value || \ + std::is_same<_Type, INT64*>::value || \ + std::is_same<_Type, UINT64*>::value || \ + std::is_same<_Type, FLOAT*>::value || \ + std::is_same<_Type, DOUBLE*>::value || \ + std::is_same<_Type, WCHAR*>::value || \ + std::is_same<_Type, boolean*>::value || \ + std::is_same<_Type, HSTRING*>::value || \ + std::is_same<_Type, IInspectable **>::value || \ + std::is_same<_Type, GUID*>::value || \ + std::is_same<_Type, ABI::Windows::Foundation::DateTime*>::value || \ + std::is_same<_Type, ABI::Windows::Foundation::TimeSpan*>::value || \ + std::is_same<_Type, ABI::Windows::Foundation::Point*>::value || \ + std::is_same<_Type, ABI::Windows::Foundation::Size*>::value || \ + std::is_same<_Type, ABI::Windows::Foundation::Rect*>::value) +#endif +#else +EXTERN_C const IID IID_ICustomStreamSink; + +class DECLSPEC_UUID("4F8A1939-2FD3-46DB-AE70-DB7E0DD79B73") DECLSPEC_NOVTABLE ICustomStreamSink : public IUnknown +{ +public: + virtual HRESULT Initialize() = 0; + virtual HRESULT Shutdown() = 0; + virtual HRESULT Start(MFTIME start) = 0; + virtual HRESULT Pause() = 0; + virtual HRESULT Restart() = 0; + virtual HRESULT Stop() = 0; +}; +#endif + +#define MF_PROP_SAMPLEGRABBERCALLBACK L"samplegrabbercallback" +#define MF_PROP_VIDTYPE L"vidtype" +#define MF_PROP_VIDENCPROPS L"videncprops" + +#include + +// MF_MEDIASINK_SAMPLEGRABBERCALLBACK: {26957AA7-AFF4-464c-BB8B-07BA65CE11DF} +// Type: IUnknown* +DEFINE_GUID(MF_MEDIASINK_SAMPLEGRABBERCALLBACK, + 0x26957aa7, 0xaff4, 0x464c, 0xbb, 0x8b, 0x7, 0xba, 0x65, 0xce, 0x11, 0xdf); + +// {4BD133CC-EB9B-496E-8865-0813BFBC6FAA} +DEFINE_GUID(MF_STREAMSINK_ID, 0x4bd133cc, 0xeb9b, 0x496e, 0x88, 0x65, 0x8, 0x13, 0xbf, 0xbc, 0x6f, 0xaa); + +// {C9E22A8C-6A50-4D78-9183-0834A02A3780} +DEFINE_GUID(MF_STREAMSINK_MEDIASINKINTERFACE, + 0xc9e22a8c, 0x6a50, 0x4d78, 0x91, 0x83, 0x8, 0x34, 0xa0, 0x2a, 0x37, 0x80); + +// {DABD13AB-26B7-47C2-97C1-4B04C187B838} +DEFINE_GUID(MF_MEDIASINK_PREFERREDTYPE, + 0xdabd13ab, 0x26b7, 0x47c2, 0x97, 0xc1, 0x4b, 0x4, 0xc1, 0x87, 0xb8, 0x38); + +#include +#ifdef _UNICODE +#define MAKE_MAP(e) std::map +#define MAKE_ENUM(e) std::pair +#define MAKE_ENUM_PAIR(e, str) std::pair(str, L#str) +#else +#define MAKE_MAP(e) std::map +#define MAKE_ENUM(e) std::pair +#define MAKE_ENUM_PAIR(e, str) std::pair(str, #str) +#endif + +MAKE_ENUM(MediaEventType) MediaEventTypePairs[] = { + MAKE_ENUM_PAIR(MediaEventType, MEUnknown), + MAKE_ENUM_PAIR(MediaEventType, MEError), + MAKE_ENUM_PAIR(MediaEventType, MEExtendedType), + MAKE_ENUM_PAIR(MediaEventType, MENonFatalError), + MAKE_ENUM_PAIR(MediaEventType, MEGenericV1Anchor), + MAKE_ENUM_PAIR(MediaEventType, MESessionUnknown), + MAKE_ENUM_PAIR(MediaEventType, MESessionTopologySet), + MAKE_ENUM_PAIR(MediaEventType, MESessionTopologiesCleared), + MAKE_ENUM_PAIR(MediaEventType, MESessionStarted), + MAKE_ENUM_PAIR(MediaEventType, MESessionPaused), + MAKE_ENUM_PAIR(MediaEventType, MESessionStopped), + MAKE_ENUM_PAIR(MediaEventType, MESessionClosed), + MAKE_ENUM_PAIR(MediaEventType, MESessionEnded), + MAKE_ENUM_PAIR(MediaEventType, MESessionRateChanged), + MAKE_ENUM_PAIR(MediaEventType, MESessionScrubSampleComplete), + MAKE_ENUM_PAIR(MediaEventType, MESessionCapabilitiesChanged), + MAKE_ENUM_PAIR(MediaEventType, MESessionTopologyStatus), + MAKE_ENUM_PAIR(MediaEventType, MESessionNotifyPresentationTime), + MAKE_ENUM_PAIR(MediaEventType, MENewPresentation), + MAKE_ENUM_PAIR(MediaEventType, MELicenseAcquisitionStart), + MAKE_ENUM_PAIR(MediaEventType, MELicenseAcquisitionCompleted), + MAKE_ENUM_PAIR(MediaEventType, MEIndividualizationStart), + MAKE_ENUM_PAIR(MediaEventType, MEIndividualizationCompleted), + MAKE_ENUM_PAIR(MediaEventType, MEEnablerProgress), + MAKE_ENUM_PAIR(MediaEventType, MEEnablerCompleted), + MAKE_ENUM_PAIR(MediaEventType, MEPolicyError), + MAKE_ENUM_PAIR(MediaEventType, MEPolicyReport), + MAKE_ENUM_PAIR(MediaEventType, MEBufferingStarted), + MAKE_ENUM_PAIR(MediaEventType, MEBufferingStopped), + MAKE_ENUM_PAIR(MediaEventType, MEConnectStart), + MAKE_ENUM_PAIR(MediaEventType, MEConnectEnd), + MAKE_ENUM_PAIR(MediaEventType, MEReconnectStart), + MAKE_ENUM_PAIR(MediaEventType, MEReconnectEnd), + MAKE_ENUM_PAIR(MediaEventType, MERendererEvent), + MAKE_ENUM_PAIR(MediaEventType, MESessionStreamSinkFormatChanged), + MAKE_ENUM_PAIR(MediaEventType, MESessionV1Anchor), + MAKE_ENUM_PAIR(MediaEventType, MESourceUnknown), + MAKE_ENUM_PAIR(MediaEventType, MESourceStarted), + MAKE_ENUM_PAIR(MediaEventType, MEStreamStarted), + MAKE_ENUM_PAIR(MediaEventType, MESourceSeeked), + MAKE_ENUM_PAIR(MediaEventType, MEStreamSeeked), + MAKE_ENUM_PAIR(MediaEventType, MENewStream), + MAKE_ENUM_PAIR(MediaEventType, MEUpdatedStream), + MAKE_ENUM_PAIR(MediaEventType, MESourceStopped), + MAKE_ENUM_PAIR(MediaEventType, MEStreamStopped), + MAKE_ENUM_PAIR(MediaEventType, MESourcePaused), + MAKE_ENUM_PAIR(MediaEventType, MEStreamPaused), + MAKE_ENUM_PAIR(MediaEventType, MEEndOfPresentation), + MAKE_ENUM_PAIR(MediaEventType, MEEndOfStream), + MAKE_ENUM_PAIR(MediaEventType, MEMediaSample), + MAKE_ENUM_PAIR(MediaEventType, MEStreamTick), + MAKE_ENUM_PAIR(MediaEventType, MEStreamThinMode), + MAKE_ENUM_PAIR(MediaEventType, MEStreamFormatChanged), + MAKE_ENUM_PAIR(MediaEventType, MESourceRateChanged), + MAKE_ENUM_PAIR(MediaEventType, MEEndOfPresentationSegment), + MAKE_ENUM_PAIR(MediaEventType, MESourceCharacteristicsChanged), + MAKE_ENUM_PAIR(MediaEventType, MESourceRateChangeRequested), + MAKE_ENUM_PAIR(MediaEventType, MESourceMetadataChanged), + MAKE_ENUM_PAIR(MediaEventType, MESequencerSourceTopologyUpdated), + MAKE_ENUM_PAIR(MediaEventType, MESourceV1Anchor), + MAKE_ENUM_PAIR(MediaEventType, MESinkUnknown), + MAKE_ENUM_PAIR(MediaEventType, MEStreamSinkStarted), + MAKE_ENUM_PAIR(MediaEventType, MEStreamSinkStopped), + MAKE_ENUM_PAIR(MediaEventType, MEStreamSinkPaused), + MAKE_ENUM_PAIR(MediaEventType, MEStreamSinkRateChanged), + MAKE_ENUM_PAIR(MediaEventType, MEStreamSinkRequestSample), + MAKE_ENUM_PAIR(MediaEventType, MEStreamSinkMarker), + MAKE_ENUM_PAIR(MediaEventType, MEStreamSinkPrerolled), + MAKE_ENUM_PAIR(MediaEventType, MEStreamSinkScrubSampleComplete), + MAKE_ENUM_PAIR(MediaEventType, MEStreamSinkFormatChanged), + MAKE_ENUM_PAIR(MediaEventType, MEStreamSinkDeviceChanged), + MAKE_ENUM_PAIR(MediaEventType, MEQualityNotify), + MAKE_ENUM_PAIR(MediaEventType, MESinkInvalidated), + MAKE_ENUM_PAIR(MediaEventType, MEAudioSessionNameChanged), + MAKE_ENUM_PAIR(MediaEventType, MEAudioSessionVolumeChanged), + MAKE_ENUM_PAIR(MediaEventType, MEAudioSessionDeviceRemoved), + MAKE_ENUM_PAIR(MediaEventType, MEAudioSessionServerShutdown), + MAKE_ENUM_PAIR(MediaEventType, MEAudioSessionGroupingParamChanged), + MAKE_ENUM_PAIR(MediaEventType, MEAudioSessionIconChanged), + MAKE_ENUM_PAIR(MediaEventType, MEAudioSessionFormatChanged), + MAKE_ENUM_PAIR(MediaEventType, MEAudioSessionDisconnected), + MAKE_ENUM_PAIR(MediaEventType, MEAudioSessionExclusiveModeOverride), + MAKE_ENUM_PAIR(MediaEventType, MESinkV1Anchor), + MAKE_ENUM_PAIR(MediaEventType, MECaptureAudioSessionVolumeChanged), + MAKE_ENUM_PAIR(MediaEventType, MECaptureAudioSessionDeviceRemoved), + MAKE_ENUM_PAIR(MediaEventType, MECaptureAudioSessionFormatChanged), + MAKE_ENUM_PAIR(MediaEventType, MECaptureAudioSessionDisconnected), + MAKE_ENUM_PAIR(MediaEventType, MECaptureAudioSessionExclusiveModeOverride), + MAKE_ENUM_PAIR(MediaEventType, MECaptureAudioSessionServerShutdown), + MAKE_ENUM_PAIR(MediaEventType, MESinkV2Anchor), + MAKE_ENUM_PAIR(MediaEventType, METrustUnknown), + MAKE_ENUM_PAIR(MediaEventType, MEPolicyChanged), + MAKE_ENUM_PAIR(MediaEventType, MEContentProtectionMessage), + MAKE_ENUM_PAIR(MediaEventType, MEPolicySet), + MAKE_ENUM_PAIR(MediaEventType, METrustV1Anchor), + MAKE_ENUM_PAIR(MediaEventType, MEWMDRMLicenseBackupCompleted), + MAKE_ENUM_PAIR(MediaEventType, MEWMDRMLicenseBackupProgress), + MAKE_ENUM_PAIR(MediaEventType, MEWMDRMLicenseRestoreCompleted), + MAKE_ENUM_PAIR(MediaEventType, MEWMDRMLicenseRestoreProgress), + MAKE_ENUM_PAIR(MediaEventType, MEWMDRMLicenseAcquisitionCompleted), + MAKE_ENUM_PAIR(MediaEventType, MEWMDRMIndividualizationCompleted), + MAKE_ENUM_PAIR(MediaEventType, MEWMDRMIndividualizationProgress), + MAKE_ENUM_PAIR(MediaEventType, MEWMDRMProximityCompleted), + MAKE_ENUM_PAIR(MediaEventType, MEWMDRMLicenseStoreCleaned), + MAKE_ENUM_PAIR(MediaEventType, MEWMDRMRevocationDownloadCompleted), + MAKE_ENUM_PAIR(MediaEventType, MEWMDRMV1Anchor), + MAKE_ENUM_PAIR(MediaEventType, METransformUnknown), + MAKE_ENUM_PAIR(MediaEventType, METransformNeedInput), + MAKE_ENUM_PAIR(MediaEventType, METransformHaveOutput), + MAKE_ENUM_PAIR(MediaEventType, METransformDrainComplete), + MAKE_ENUM_PAIR(MediaEventType, METransformMarker), + MAKE_ENUM_PAIR(MediaEventType, MEByteStreamCharacteristicsChanged), + MAKE_ENUM_PAIR(MediaEventType, MEVideoCaptureDeviceRemoved), + MAKE_ENUM_PAIR(MediaEventType, MEVideoCaptureDevicePreempted), + MAKE_ENUM_PAIR(MediaEventType, MEReservedMax) +}; +MAKE_MAP(MediaEventType) MediaEventTypeMap(MediaEventTypePairs, MediaEventTypePairs + sizeof(MediaEventTypePairs) / sizeof(MediaEventTypePairs[0])); + +MAKE_ENUM(MFSTREAMSINK_MARKER_TYPE) StreamSinkMarkerTypePairs[] = { + MAKE_ENUM_PAIR(MFSTREAMSINK_MARKER_TYPE, MFSTREAMSINK_MARKER_DEFAULT), + MAKE_ENUM_PAIR(MFSTREAMSINK_MARKER_TYPE, MFSTREAMSINK_MARKER_ENDOFSEGMENT), + MAKE_ENUM_PAIR(MFSTREAMSINK_MARKER_TYPE, MFSTREAMSINK_MARKER_TICK), + MAKE_ENUM_PAIR(MFSTREAMSINK_MARKER_TYPE, MFSTREAMSINK_MARKER_EVENT) +}; +MAKE_MAP(MFSTREAMSINK_MARKER_TYPE) StreamSinkMarkerTypeMap(StreamSinkMarkerTypePairs, StreamSinkMarkerTypePairs + sizeof(StreamSinkMarkerTypePairs) / sizeof(StreamSinkMarkerTypePairs[0])); + +#ifdef HAVE_WINRT + +#ifdef __cplusplus_winrt +#define _ContextCallback Concurrency::details::_ContextCallback +#define BEGIN_CALL_IN_CONTEXT(hr, var, ...) hr = S_OK;\ + var._CallInContext([__VA_ARGS__]() { +#define END_CALL_IN_CONTEXT(hr) if (FAILED(hr)) throw Platform::Exception::CreateException(hr);\ +}); +#define END_CALL_IN_CONTEXT_BASE }); +#else +#define _ContextCallback Concurrency_winrt::details::_ContextCallback +#define BEGIN_CALL_IN_CONTEXT(hr, var, ...) hr = var._CallInContext([__VA_ARGS__]() -> HRESULT { +#define END_CALL_IN_CONTEXT(hr) return hr;\ +}); +#define END_CALL_IN_CONTEXT_BASE return S_OK;\ +}); +#endif +#define GET_CURRENT_CONTEXT _ContextCallback::_CaptureCurrent() +#define SAVE_CURRENT_CONTEXT(var) _ContextCallback var = GET_CURRENT_CONTEXT + +#define COMMA , + +#ifdef __cplusplus_winrt +#define _Object Platform::Object^ +#define _ObjectObj Platform::Object^ +#define _String Platform::String^ +#define _StringObj Platform::String^ +#define _StringReference ref new Platform::String +#define _StringReferenceObj Platform::String^ +#define _DeviceInformationCollection Windows::Devices::Enumeration::DeviceInformationCollection +#define _MediaCapture Windows::Media::Capture::MediaCapture +#define _MediaCaptureVideoPreview Windows::Media::Capture::MediaCapture +#define _MediaCaptureInitializationSettings Windows::Media::Capture::MediaCaptureInitializationSettings +#define _VideoDeviceController Windows::Media::Devices::VideoDeviceController +#define _MediaDeviceController Windows::Media::Devices::VideoDeviceController +#define _MediaEncodingProperties Windows::Media::MediaProperties::IMediaEncodingProperties +#define _VideoEncodingProperties Windows::Media::MediaProperties::VideoEncodingProperties +#define _MediaStreamType Windows::Media::Capture::MediaStreamType +#define _AsyncInfo Windows::Foundation::IAsyncInfo +#define _AsyncAction Windows::Foundation::IAsyncAction +#define _AsyncOperation Windows::Foundation::IAsyncOperation +#define _DeviceClass Windows::Devices::Enumeration::DeviceClass +#define _IDeviceInformation Windows::Devices::Enumeration::DeviceInformation +#define _DeviceInformation Windows::Devices::Enumeration::DeviceInformation +#define _DeviceInformationStatics Windows::Devices::Enumeration::DeviceInformation +#define _MediaEncodingProfile Windows::Media::MediaProperties::MediaEncodingProfile +#define _StreamingCaptureMode Windows::Media::Capture::StreamingCaptureMode +#define _PropertySet Windows::Foundation::Collections::PropertySet +#define _Map Windows::Foundation::Collections::PropertySet +#define _PropertyValueStatics Windows::Foundation::PropertyValue +#define _VectorView Windows::Foundation::Collections::IVectorView +#define _StartPreviewToCustomSinkIdAsync StartPreviewToCustomSinkAsync +#define _InitializeWithSettingsAsync InitializeAsync +#define _FindAllAsyncDeviceClass FindAllAsync +#define _MediaExtension Windows::Media::IMediaExtension +#define BEGIN_CREATE_ASYNC(type, ...) (Concurrency::create_async([__VA_ARGS__]() { +#define END_CREATE_ASYNC(hr) if (FAILED(hr)) throw Platform::Exception::CreateException(hr);\ +})) +#define DEFINE_TASK Concurrency::task +#define CREATE_TASK Concurrency::create_task +#define CREATE_OR_CONTINUE_TASK(_task, rettype, func) _task = (_task == Concurrency::task()) ? Concurrency::create_task(func) : _task.then([func](rettype) -> rettype { return func(); }); +#define CREATE_OR_CONTINUE_TASK_RET(_task, rettype, func) _task = (_task == Concurrency::task()) ? Concurrency::create_task(func) : _task.then([func](rettype) -> rettype { return func(); }); +#define DEFINE_RET_VAL(x) +#define DEFINE_RET_TYPE(x) +#define DEFINE_RET_FORMAL(x) x +#define RET_VAL(x) return x; +#define RET_VAL_BASE +#define MAKE_STRING(str) str +#define GET_STL_STRING(str) std::wstring(str->Data()) +#define GET_STL_STRING_RAW(str) std::wstring(str->Data()) +#define MAKE_WRL_OBJ(x) x^ +#define MAKE_WRL_REF(x) x^ +#define MAKE_OBJ_REF(x) x^ +#define MAKE_WRL_AGILE_REF(x) Platform::Agile +#define MAKE_WRL_AGILE_OBJ(x) Platform::Agile +#define MAKE_PROPERTY_BACKING(Type, PropName) property Type PropName; +#define MAKE_PROPERTY(Type, PropName, PropValue) +#define MAKE_PROPERTY_STRING(Type, PropName, PropValue) +#define MAKE_READONLY_PROPERTY(Type, PropName, PropValue) property Type PropName\ +{\ + Type get() { return PropValue; }\ +} +#define THROW_INVALID_ARG throw ref new Platform::InvalidArgumentException(); +#define RELEASE_AGILE_WRL(x) x = nullptr; +#define RELEASE_WRL(x) x = nullptr; +#define GET_WRL_OBJ_FROM_REF(objtype, obj, orig, hr) objtype^ obj = orig;\ +hr = S_OK; +#define GET_WRL_OBJ_FROM_OBJ(objtype, obj, orig, hr) objtype^ obj = safe_cast(orig);\ +hr = S_OK; +#define WRL_ENUM_GET(obj, prefix, prop) obj::##prop +#define WRL_PROP_GET(obj, prop, arg, hr) arg = obj->##prop;\ +hr = S_OK; +#define WRL_PROP_PUT(obj, prop, arg, hr) obj->##prop = arg;\ +hr = S_OK; +#define WRL_METHOD_BASE(obj, method, ret, hr) ret = obj->##method();\ +hr = S_OK; +#define WRL_METHOD(obj, method, ret, hr, ...) ret = obj->##method(__VA_ARGS__);\ +hr = S_OK; +#define WRL_METHOD_NORET_BASE(obj, method, hr) obj->##method();\ + hr = S_OK; +#define WRL_METHOD_NORET(obj, method, hr, ...) obj->##method(__VA_ARGS__);\ + hr = S_OK; +#define REF_WRL_OBJ(obj) &obj +#define DEREF_WRL_OBJ(obj) obj +#define DEREF_AGILE_WRL_MADE_OBJ(obj) obj.Get() +#define DEREF_AGILE_WRL_OBJ(obj) obj.Get() +#define DEREF_AS_NATIVE_WRL_OBJ(type, obj) reinterpret_cast(obj) +#define PREPARE_TRANSFER_WRL_OBJ(obj) obj +#define ACTIVATE_LOCAL_OBJ_BASE(objtype) ref new objtype() +#define ACTIVATE_LOCAL_OBJ(objtype, ...) ref new objtype(__VA_ARGS__) +#define ACTIVATE_EVENT_HANDLER(objtype, ...) ref new objtype(__VA_ARGS__) +#define ACTIVATE_OBJ(rtclass, objtype, obj, hr) MAKE_WRL_OBJ(objtype) obj = ref new objtype();\ +hr = S_OK; +#define ACTIVATE_STATIC_OBJ(rtclass, objtype, obj, hr) objtype obj;\ +hr = S_OK; +#else +#define _Object IInspectable* +#define _ObjectObj Microsoft::WRL::ComPtr +#define _String HSTRING +#define _StringObj Microsoft::WRL::Wrappers::HString +#define _StringReference Microsoft::WRL::Wrappers::HStringReference +#define _StringReferenceObj Microsoft::WRL::Wrappers::HStringReference +#define _DeviceInformationCollection ABI::Windows::Devices::Enumeration::DeviceInformationCollection +#define _MediaCapture ABI::Windows::Media::Capture::IMediaCapture +#define _MediaCaptureVideoPreview ABI::Windows::Media::Capture::IMediaCaptureVideoPreview +#define _MediaCaptureInitializationSettings ABI::Windows::Media::Capture::IMediaCaptureInitializationSettings +#define _VideoDeviceController ABI::Windows::Media::Devices::IVideoDeviceController +#define _MediaDeviceController ABI::Windows::Media::Devices::IMediaDeviceController +#define _MediaEncodingProperties ABI::Windows::Media::MediaProperties::IMediaEncodingProperties +#define _VideoEncodingProperties ABI::Windows::Media::MediaProperties::IVideoEncodingProperties +#define _MediaStreamType ABI::Windows::Media::Capture::MediaStreamType +#define _AsyncInfo ABI::Windows::Foundation::IAsyncInfo +#define _AsyncAction ABI::Windows::Foundation::IAsyncAction +#define _AsyncOperation ABI::Windows::Foundation::IAsyncOperation +#define _DeviceClass ABI::Windows::Devices::Enumeration::DeviceClass +#define _IDeviceInformation ABI::Windows::Devices::Enumeration::IDeviceInformation +#define _DeviceInformation ABI::Windows::Devices::Enumeration::DeviceInformation +#define _DeviceInformationStatics ABI::Windows::Devices::Enumeration::IDeviceInformationStatics +#define _MediaEncodingProfile ABI::Windows::Media::MediaProperties::IMediaEncodingProfile +#define _StreamingCaptureMode ABI::Windows::Media::Capture::StreamingCaptureMode +#define _PropertySet ABI::Windows::Foundation::Collections::IPropertySet +#define _Map ABI::Windows::Foundation::Collections::IMap +#define _PropertyValueStatics ABI::Windows::Foundation::IPropertyValueStatics +#define _VectorView ABI::Windows::Foundation::Collections::IVectorView +#define _StartPreviewToCustomSinkIdAsync StartPreviewToCustomSinkIdAsync +#define _InitializeWithSettingsAsync InitializeWithSettingsAsync +#define _FindAllAsyncDeviceClass FindAllAsyncDeviceClass +#define _MediaExtension ABI::Windows::Media::IMediaExtension +#define BEGIN_CREATE_ASYNC(type, ...) Concurrency_winrt::create_async([__VA_ARGS__]() -> HRESULT { +#define END_CREATE_ASYNC(hr) return hr;\ +}) +#define DEFINE_TASK Concurrency_winrt::task +#define CREATE_TASK Concurrency_winrt::create_task +#define CREATE_OR_CONTINUE_TASK(_task, rettype, func) _task = (_task == Concurrency_winrt::task()) ? Concurrency_winrt::create_task(func) : _task.then(func); +#define CREATE_OR_CONTINUE_TASK_RET(_task, rettype, func) _task = (_task == Concurrency_winrt::task()) ? Concurrency_winrt::create_task(func) : _task.then([func](rettype, rettype* retVal) -> HRESULT { return func(retVal); }); +#define DEFINE_RET_VAL(x) x* retVal +#define DEFINE_RET_TYPE(x) +#define DEFINE_RET_FORMAL(x) HRESULT +#define RET_VAL(x) *retVal = x;\ +return S_OK; +#define RET_VAL_BASE return S_OK; +#define MAKE_STRING(str) Microsoft::WRL::Wrappers::HStringReference(L##str) +#define GET_STL_STRING(str) std::wstring(str.GetRawBuffer(NULL)) +#define GET_STL_STRING_RAW(str) WindowsGetStringRawBuffer(str, NULL) +#define MAKE_WRL_OBJ(x) Microsoft::WRL::ComPtr +#define MAKE_WRL_REF(x) x* +#define MAKE_OBJ_REF(x) x +#define MAKE_WRL_AGILE_REF(x) x* +#define MAKE_WRL_AGILE_OBJ(x) Microsoft::WRL::ComPtr +#define MAKE_PROPERTY_BACKING(Type, PropName) Type PropName; +#define MAKE_PROPERTY(Type, PropName, PropValue) STDMETHODIMP get_##PropName(Type* pVal) { if (pVal) { *pVal = PropValue; } else { return E_INVALIDARG; } return S_OK; }\ + STDMETHODIMP put_##PropName(Type Val) { PropValue = Val; return S_OK; } +#define MAKE_PROPERTY_STRING(Type, PropName, PropValue) STDMETHODIMP get_##PropName(Type* pVal) { if (pVal) { return ::WindowsDuplicateString(PropValue.Get(), pVal); } else { return E_INVALIDARG; } }\ + STDMETHODIMP put_##PropName(Type Val) { return PropValue.Set(Val); } +#define MAKE_READONLY_PROPERTY(Type, PropName, PropValue) STDMETHODIMP get_##PropName(Type* pVal) { if (pVal) { *pVal = PropValue; } else { return E_INVALIDARG; } return S_OK; } +#define THROW_INVALID_ARG RoOriginateError(E_INVALIDARG, nullptr); +#define RELEASE_AGILE_WRL(x) if (x) { (x)->Release(); x = nullptr; } +#define RELEASE_WRL(x) if (x) { (x)->Release(); x = nullptr; } +#define GET_WRL_OBJ_FROM_REF(objtype, obj, orig, hr) Microsoft::WRL::ComPtr obj;\ +hr = orig->QueryInterface(__uuidof(objtype), &obj); +#define GET_WRL_OBJ_FROM_OBJ(objtype, obj, orig, hr) Microsoft::WRL::ComPtr obj;\ +hr = orig.As(&obj); +#define WRL_ENUM_GET(obj, prefix, prop) obj::prefix##_##prop +#define WRL_PROP_GET(obj, prop, arg, hr) hr = obj->get_##prop(&arg); +#define WRL_PROP_PUT(obj, prop, arg, hr) hr = obj->put_##prop(arg); +#define WRL_METHOD_BASE(obj, method, ret, hr) hr = obj->##method(&ret); +#define WRL_METHOD(obj, method, ret, hr, ...) hr = obj->##method(__VA_ARGS__, &ret); +#define WRL_METHOD_NORET_BASE(obj, method, hr) hr = obj->##method(); +#define REF_WRL_OBJ(obj) obj.GetAddressOf() +#define DEREF_WRL_OBJ(obj) obj.Get() +#define DEREF_AGILE_WRL_MADE_OBJ(obj) obj.Get() +#define DEREF_AGILE_WRL_OBJ(obj) obj +#define DEREF_AS_NATIVE_WRL_OBJ(type, obj) obj.Get() +#define PREPARE_TRANSFER_WRL_OBJ(obj) obj.Detach() +#define ACTIVATE_LOCAL_OBJ_BASE(objtype) Microsoft::WRL::Make() +#define ACTIVATE_LOCAL_OBJ(objtype, ...) Microsoft::WRL::Make(__VA_ARGS__) +#define ACTIVATE_EVENT_HANDLER(objtype, ...) Microsoft::WRL::Callback(__VA_ARGS__).Get() +#define ACTIVATE_OBJ(rtclass, objtype, obj, hr) MAKE_WRL_OBJ(objtype) obj;\ +{\ + Microsoft::WRL::ComPtr objFactory;\ + hr = Windows::Foundation::GetActivationFactory(Microsoft::WRL::Wrappers::HStringReference(rtclass).Get(), objFactory.ReleaseAndGetAddressOf());\ + if (SUCCEEDED(hr)) {\ + Microsoft::WRL::ComPtr pInsp;\ + hr = objFactory->ActivateInstance(pInsp.GetAddressOf());\ + if (SUCCEEDED(hr)) hr = pInsp.As(&obj);\ + }\ +} +#define ACTIVATE_STATIC_OBJ(rtclass, objtype, obj, hr) objtype obj;\ +{\ + Microsoft::WRL::ComPtr objFactory;\ + hr = Windows::Foundation::GetActivationFactory(Microsoft::WRL::Wrappers::HStringReference(rtclass).Get(), objFactory.ReleaseAndGetAddressOf());\ + if (SUCCEEDED(hr)) {\ + if (SUCCEEDED(hr)) hr = objFactory.As(&obj);\ + }\ +} +#endif + +#define _ComPtr Microsoft::WRL::ComPtr +#else + +template +class ComPtr : public ATL::CComPtr +{ +public: + ComPtr() throw() + { + } + ComPtr(int nNull) throw() : + CComPtr((T*)nNull) + { + } + ComPtr(T* lp) throw() : + CComPtr(lp) + + { + } + ComPtr(_In_ const CComPtr& lp) throw() : + CComPtr(lp.p) + { + } + virtual ~ComPtr() {} + + T* const* GetAddressOf() const throw() + { + return &p; + } + + T** GetAddressOf() throw() + { + return &p; + } + + T** ReleaseAndGetAddressOf() throw() + { + InternalRelease(); + return &p; + } + + T* Get() const throw() + { + return p; + } + ComPtr& operator=(decltype(__nullptr)) throw() + { + InternalRelease(); + return *this; + } + ComPtr& operator=(_In_ const int nNull) throw() + { + ASSERT(nNull == 0); + (void)nNull; + InternalRelease(); + return *this; + } + unsigned long Reset() + { + return InternalRelease(); + } + // query for U interface + template + HRESULT As(_Inout_ U** lp) const throw() + { + return p->QueryInterface(__uuidof(U), (void**)lp); + } + // query for U interface + template + HRESULT As(_Out_ ComPtr* lp) const throw() + { + return p->QueryInterface(__uuidof(U), reinterpret_cast(lp->ReleaseAndGetAddressOf())); + } +private: + unsigned long InternalRelease() throw() + { + unsigned long ref = 0; + T* temp = p; + + if (temp != nullptr) + { + p = nullptr; + ref = temp->Release(); + } + + return ref; + } +}; + +#define _ComPtr ComPtr +#endif + +template +class CBaseAttributes : public TBase +{ +protected: + // This version of the constructor does not initialize the + // attribute store. The derived class must call Initialize() in + // its own constructor. + CBaseAttributes() + { + } + + // This version of the constructor initializes the attribute + // store, but the derived class must pass an HRESULT parameter + // to the constructor. + + CBaseAttributes(HRESULT& hr, UINT32 cInitialSize = 0) + { + hr = Initialize(cInitialSize); + } + + // The next version of the constructor uses a caller-provided + // implementation of IMFAttributes. + + // (Sometimes you want to delegate IMFAttributes calls to some + // other object that implements IMFAttributes, rather than using + // MFCreateAttributes.) + + CBaseAttributes(HRESULT& hr, IUnknown *pUnk) + { + hr = Initialize(pUnk); + } + + virtual ~CBaseAttributes() + { + } + + // Initializes the object by creating the standard Media Foundation attribute store. + HRESULT Initialize(UINT32 cInitialSize = 0) + { + if (_spAttributes.Get() == nullptr) + { + return MFCreateAttributes(&_spAttributes, cInitialSize); + } + else + { + return S_OK; + } + } + + // Initializes this object from a caller-provided attribute store. + // pUnk: Pointer to an object that exposes IMFAttributes. + HRESULT Initialize(IUnknown *pUnk) + { + if (_spAttributes) + { + _spAttributes.Reset(); + _spAttributes = nullptr; + } + + + return pUnk->QueryInterface(IID_PPV_ARGS(&_spAttributes)); + } + +public: + + // IMFAttributes methods + + STDMETHODIMP GetItem(REFGUID guidKey, PROPVARIANT* pValue) + { + assert(_spAttributes); + return _spAttributes->GetItem(guidKey, pValue); + } + + STDMETHODIMP GetItemType(REFGUID guidKey, MF_ATTRIBUTE_TYPE* pType) + { + assert(_spAttributes); + return _spAttributes->GetItemType(guidKey, pType); + } + + STDMETHODIMP CompareItem(REFGUID guidKey, REFPROPVARIANT Value, BOOL* pbResult) + { + assert(_spAttributes); + return _spAttributes->CompareItem(guidKey, Value, pbResult); + } + + STDMETHODIMP Compare( + IMFAttributes* pTheirs, + MF_ATTRIBUTES_MATCH_TYPE MatchType, + BOOL* pbResult + ) + { + assert(_spAttributes); + return _spAttributes->Compare(pTheirs, MatchType, pbResult); + } + + STDMETHODIMP GetUINT32(REFGUID guidKey, UINT32* punValue) + { + assert(_spAttributes); + return _spAttributes->GetUINT32(guidKey, punValue); + } + + STDMETHODIMP GetUINT64(REFGUID guidKey, UINT64* punValue) + { + assert(_spAttributes); + return _spAttributes->GetUINT64(guidKey, punValue); + } + + STDMETHODIMP GetDouble(REFGUID guidKey, double* pfValue) + { + assert(_spAttributes); + return _spAttributes->GetDouble(guidKey, pfValue); + } + + STDMETHODIMP GetGUID(REFGUID guidKey, GUID* pguidValue) + { + assert(_spAttributes); + return _spAttributes->GetGUID(guidKey, pguidValue); + } + + STDMETHODIMP GetStringLength(REFGUID guidKey, UINT32* pcchLength) + { + assert(_spAttributes); + return _spAttributes->GetStringLength(guidKey, pcchLength); + } + + STDMETHODIMP GetString(REFGUID guidKey, LPWSTR pwszValue, UINT32 cchBufSize, UINT32* pcchLength) + { + assert(_spAttributes); + return _spAttributes->GetString(guidKey, pwszValue, cchBufSize, pcchLength); + } + + STDMETHODIMP GetAllocatedString(REFGUID guidKey, LPWSTR* ppwszValue, UINT32* pcchLength) + { + assert(_spAttributes); + return _spAttributes->GetAllocatedString(guidKey, ppwszValue, pcchLength); + } + + STDMETHODIMP GetBlobSize(REFGUID guidKey, UINT32* pcbBlobSize) + { + assert(_spAttributes); + return _spAttributes->GetBlobSize(guidKey, pcbBlobSize); + } + + STDMETHODIMP GetBlob(REFGUID guidKey, UINT8* pBuf, UINT32 cbBufSize, UINT32* pcbBlobSize) + { + assert(_spAttributes); + return _spAttributes->GetBlob(guidKey, pBuf, cbBufSize, pcbBlobSize); + } + + STDMETHODIMP GetAllocatedBlob(REFGUID guidKey, UINT8** ppBuf, UINT32* pcbSize) + { + assert(_spAttributes); + return _spAttributes->GetAllocatedBlob(guidKey, ppBuf, pcbSize); + } + + STDMETHODIMP GetUnknown(REFGUID guidKey, REFIID riid, LPVOID* ppv) + { + assert(_spAttributes); + return _spAttributes->GetUnknown(guidKey, riid, ppv); + } + + STDMETHODIMP SetItem(REFGUID guidKey, REFPROPVARIANT Value) + { + assert(_spAttributes); + return _spAttributes->SetItem(guidKey, Value); + } + + STDMETHODIMP DeleteItem(REFGUID guidKey) + { + assert(_spAttributes); + return _spAttributes->DeleteItem(guidKey); + } + + STDMETHODIMP DeleteAllItems() + { + assert(_spAttributes); + return _spAttributes->DeleteAllItems(); + } + + STDMETHODIMP SetUINT32(REFGUID guidKey, UINT32 unValue) + { + assert(_spAttributes); + return _spAttributes->SetUINT32(guidKey, unValue); + } + + STDMETHODIMP SetUINT64(REFGUID guidKey,UINT64 unValue) + { + assert(_spAttributes); + return _spAttributes->SetUINT64(guidKey, unValue); + } + + STDMETHODIMP SetDouble(REFGUID guidKey, double fValue) + { + assert(_spAttributes); + return _spAttributes->SetDouble(guidKey, fValue); + } + + STDMETHODIMP SetGUID(REFGUID guidKey, REFGUID guidValue) + { + assert(_spAttributes); + return _spAttributes->SetGUID(guidKey, guidValue); + } + + STDMETHODIMP SetString(REFGUID guidKey, LPCWSTR wszValue) + { + assert(_spAttributes); + return _spAttributes->SetString(guidKey, wszValue); + } + + STDMETHODIMP SetBlob(REFGUID guidKey, const UINT8* pBuf, UINT32 cbBufSize) + { + assert(_spAttributes); + return _spAttributes->SetBlob(guidKey, pBuf, cbBufSize); + } + + STDMETHODIMP SetUnknown(REFGUID guidKey, IUnknown* pUnknown) + { + assert(_spAttributes); + return _spAttributes->SetUnknown(guidKey, pUnknown); + } + + STDMETHODIMP LockStore() + { + assert(_spAttributes); + return _spAttributes->LockStore(); + } + + STDMETHODIMP UnlockStore() + { + assert(_spAttributes); + return _spAttributes->UnlockStore(); + } + + STDMETHODIMP GetCount(UINT32* pcItems) + { + assert(_spAttributes); + return _spAttributes->GetCount(pcItems); + } + + STDMETHODIMP GetItemByIndex(UINT32 unIndex, GUID* pguidKey, PROPVARIANT* pValue) + { + assert(_spAttributes); + return _spAttributes->GetItemByIndex(unIndex, pguidKey, pValue); + } + + STDMETHODIMP CopyAllItems(IMFAttributes* pDest) + { + assert(_spAttributes); + return _spAttributes->CopyAllItems(pDest); + } + + // Helper functions + + HRESULT SerializeToStream(DWORD dwOptions, IStream* pStm) + // dwOptions: Flags from MF_ATTRIBUTE_SERIALIZE_OPTIONS + { + assert(_spAttributes); + return MFSerializeAttributesToStream(_spAttributes.Get(), dwOptions, pStm); + } + + HRESULT DeserializeFromStream(DWORD dwOptions, IStream* pStm) + { + assert(_spAttributes); + return MFDeserializeAttributesFromStream(_spAttributes.Get(), dwOptions, pStm); + } + + // SerializeToBlob: Stores the attributes in a byte array. + // + // ppBuf: Receives a pointer to the byte array. + // pcbSize: Receives the size of the byte array. + // + // The caller must free the array using CoTaskMemFree. + HRESULT SerializeToBlob(UINT8 **ppBuffer, UINT *pcbSize) + { + assert(_spAttributes); + + if (ppBuffer == NULL) + { + return E_POINTER; + } + if (pcbSize == NULL) + { + return E_POINTER; + } + + HRESULT hr = S_OK; + UINT32 cbSize = 0; + BYTE *pBuffer = NULL; + + CHECK_HR(hr = MFGetAttributesAsBlobSize(_spAttributes.Get(), &cbSize)); + + pBuffer = (BYTE*)CoTaskMemAlloc(cbSize); + if (pBuffer == NULL) + { + CHECK_HR(hr = E_OUTOFMEMORY); + } + + CHECK_HR(hr = MFGetAttributesAsBlob(_spAttributes.Get(), pBuffer, cbSize)); + + *ppBuffer = pBuffer; + *pcbSize = cbSize; + +done: + if (FAILED(hr)) + { + *ppBuffer = NULL; + *pcbSize = 0; + CoTaskMemFree(pBuffer); + } + return hr; + } + + HRESULT DeserializeFromBlob(const UINT8* pBuffer, UINT cbSize) + { + assert(_spAttributes); + return MFInitAttributesFromBlob(_spAttributes.Get(), pBuffer, cbSize); + } + + HRESULT GetRatio(REFGUID guidKey, UINT32* pnNumerator, UINT32* punDenominator) + { + assert(_spAttributes); + return MFGetAttributeRatio(_spAttributes.Get(), guidKey, pnNumerator, punDenominator); + } + + HRESULT SetRatio(REFGUID guidKey, UINT32 unNumerator, UINT32 unDenominator) + { + assert(_spAttributes); + return MFSetAttributeRatio(_spAttributes.Get(), guidKey, unNumerator, unDenominator); + } + + // Gets an attribute whose value represents the size of something (eg a video frame). + HRESULT GetSize(REFGUID guidKey, UINT32* punWidth, UINT32* punHeight) + { + assert(_spAttributes); + return MFGetAttributeSize(_spAttributes.Get(), guidKey, punWidth, punHeight); + } + + // Sets an attribute whose value represents the size of something (eg a video frame). + HRESULT SetSize(REFGUID guidKey, UINT32 unWidth, UINT32 unHeight) + { + assert(_spAttributes); + return MFSetAttributeSize (_spAttributes.Get(), guidKey, unWidth, unHeight); + } + +protected: + _ComPtr _spAttributes; +}; + +class StreamSink : +#ifdef HAVE_WINRT + public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::RuntimeClassType::ClassicCom>, + IMFStreamSink, + IMFMediaEventGenerator, + IMFMediaTypeHandler, + CBaseAttributes<> > +#else + public IMFStreamSink, + public IMFMediaTypeHandler, + public CBaseAttributes<>, + public ICustomStreamSink +#endif +{ +public: + // IUnknown methods + STDMETHOD(QueryInterface)(REFIID riid, _Outptr_result_nullonfailure_ void **ppv) + { + if (ppv == nullptr) { + return E_POINTER; + } + (*ppv) = nullptr; + HRESULT hr = S_OK; + if (riid == IID_IMarshal) { + return MarshalQI(riid, ppv); + } else { +#ifdef HAVE_WINRT + hr = RuntimeClassT::QueryInterface(riid, ppv); +#else + if (riid == IID_IUnknown || riid == IID_IMFStreamSink) { + *ppv = static_cast(this); + AddRef(); + } else if (riid == IID_IMFMediaEventGenerator) { + *ppv = static_cast(this); + AddRef(); + } else if (riid == IID_IMFMediaTypeHandler) { + *ppv = static_cast(this); + AddRef(); + } else if (riid == IID_IMFAttributes) { + *ppv = static_cast(this); + AddRef(); + } else if (riid == IID_ICustomStreamSink) { + *ppv = static_cast(this); + AddRef(); + } else + hr = E_NOINTERFACE; +#endif + } + + return hr; + } + +#ifdef HAVE_WINRT + STDMETHOD(RuntimeClassInitialize)() { return S_OK; } +#else + ULONG STDMETHODCALLTYPE AddRef() + { + return InterlockedIncrement(&m_cRef); + } + ULONG STDMETHODCALLTYPE Release() + { + ULONG cRef = InterlockedDecrement(&m_cRef); + if (cRef == 0) + { + delete this; + } + return cRef; + } +#endif + HRESULT MarshalQI(REFIID riid, LPVOID* ppv) + { + HRESULT hr = S_OK; + if (m_spFTM == nullptr) { + EnterCriticalSection(&m_critSec); + if (m_spFTM == nullptr) { + hr = CoCreateFreeThreadedMarshaler((IMFStreamSink*)this, &m_spFTM); + } + LeaveCriticalSection(&m_critSec); + } + + if (SUCCEEDED(hr)) { + if (m_spFTM == nullptr) { + hr = E_UNEXPECTED; + } + else { + hr = m_spFTM.Get()->QueryInterface(riid, ppv); + } + } + return hr; + } + enum State + { + State_TypeNotSet = 0, // No media type is set + State_Ready, // Media type is set, Start has never been called. + State_Started, + State_Stopped, + State_Paused, + State_Count // Number of states + }; + StreamSink() : m_IsShutdown(false), + m_StartTime(0), m_fGetStartTimeFromSample(false), m_fWaitingForFirstSample(false), + m_state(State_TypeNotSet), m_pParent(nullptr), + m_imageWidthInPixels(0), m_imageHeightInPixels(0) { +#ifdef HAVE_WINRT + m_token.value = 0; +#else + m_bConnected = false; +#endif + InitializeCriticalSectionEx(&m_critSec, 3000, 0); + ZeroMemory(&m_guiCurrentSubtype, sizeof(m_guiCurrentSubtype)); + CBaseAttributes::Initialize(0U); + DebugPrintOut(L"StreamSink::StreamSink\n"); + } + virtual ~StreamSink() { + DeleteCriticalSection(&m_critSec); + assert(m_IsShutdown); + DebugPrintOut(L"StreamSink::~StreamSink\n"); + } + + HRESULT Initialize() + { + HRESULT hr; + // Create the event queue helper. + hr = MFCreateEventQueue(&m_spEventQueue); + if (SUCCEEDED(hr)) + { + _ComPtr pMedSink; + hr = CBaseAttributes<>::GetUnknown(MF_STREAMSINK_MEDIASINKINTERFACE, __uuidof(IMFMediaSink), (LPVOID*)pMedSink.GetAddressOf()); + assert(pMedSink.Get() != NULL); + if (SUCCEEDED(hr)) { + hr = pMedSink.Get()->QueryInterface(IID_PPV_ARGS(&m_pParent)); + } + } + return hr; + } + + HRESULT CheckShutdown() const + { + if (m_IsShutdown) + { + return MF_E_SHUTDOWN; + } + else + { + return S_OK; + } + } + // Called when the presentation clock starts. + HRESULT Start(MFTIME start) + { + HRESULT hr = S_OK; + EnterCriticalSection(&m_critSec); + if (m_state != State_TypeNotSet) { + if (start != PRESENTATION_CURRENT_POSITION) + { + m_StartTime = start; // Cache the start time. + m_fGetStartTimeFromSample = false; + } + else + { + m_fGetStartTimeFromSample = true; + } + m_state = State_Started; + GUID guiMajorType; + m_fWaitingForFirstSample = SUCCEEDED(m_spCurrentType->GetMajorType(&guiMajorType)) && (guiMajorType == MFMediaType_Video); + hr = QueueEvent(MEStreamSinkStarted, GUID_NULL, hr, NULL); + if (SUCCEEDED(hr)) { + hr = QueueEvent(MEStreamSinkRequestSample, GUID_NULL, hr, NULL); + } + } + else hr = MF_E_NOT_INITIALIZED; + LeaveCriticalSection(&m_critSec); + return hr; + } + + // Called when the presentation clock pauses. + HRESULT Pause() + { + EnterCriticalSection(&m_critSec); + + HRESULT hr = S_OK; + + if (m_state != State_Stopped && m_state != State_TypeNotSet) { + m_state = State_Paused; + hr = QueueEvent(MEStreamSinkPaused, GUID_NULL, hr, NULL); + } else if (hr == State_TypeNotSet) + hr = MF_E_NOT_INITIALIZED; + else + hr = MF_E_INVALIDREQUEST; + LeaveCriticalSection(&m_critSec); + return hr; + } + // Called when the presentation clock restarts. + HRESULT Restart() + { + EnterCriticalSection(&m_critSec); + + HRESULT hr = S_OK; + + if (m_state == State_Paused) { + m_state = State_Started; + hr = QueueEvent(MEStreamSinkStarted, GUID_NULL, hr, NULL); + if (SUCCEEDED(hr)) { + hr = QueueEvent(MEStreamSinkRequestSample, GUID_NULL, hr, NULL); + } + } else if (hr == State_TypeNotSet) + hr = MF_E_NOT_INITIALIZED; + else + hr = MF_E_INVALIDREQUEST; + LeaveCriticalSection(&m_critSec); + return hr; + } + // Called when the presentation clock stops. + HRESULT Stop() + { + EnterCriticalSection(&m_critSec); + + HRESULT hr = S_OK; + if (m_state != State_TypeNotSet) { + m_state = State_Stopped; + hr = QueueEvent(MEStreamSinkStopped, GUID_NULL, hr, NULL); + } + else hr = MF_E_NOT_INITIALIZED; + LeaveCriticalSection(&m_critSec); + return hr; + } + + // Shuts down the stream sink. + HRESULT Shutdown() + { + _ComPtr pSampleCallback; + HRESULT hr = S_OK; + assert(!m_IsShutdown); + hr = m_pParent->GetUnknown(MF_MEDIASINK_SAMPLEGRABBERCALLBACK, IID_IMFSampleGrabberSinkCallback, (LPVOID*)pSampleCallback.GetAddressOf()); + if (SUCCEEDED(hr)) { + hr = pSampleCallback->OnShutdown(); + } + + if (m_spEventQueue) { + hr = m_spEventQueue->Shutdown(); + } + if (m_pParent) + m_pParent->Release(); + m_spCurrentType.Reset(); + m_IsShutdown = TRUE; + + return hr; + } + + //IMFStreamSink + HRESULT STDMETHODCALLTYPE GetMediaSink( + /* [out] */ __RPC__deref_out_opt IMFMediaSink **ppMediaSink) { + if (ppMediaSink == NULL) + { + return E_INVALIDARG; + } + + EnterCriticalSection(&m_critSec); + + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + _ComPtr pMedSink; + hr = CBaseAttributes<>::GetUnknown(MF_STREAMSINK_MEDIASINKINTERFACE, __uuidof(IMFMediaSink), (LPVOID*)pMedSink.GetAddressOf()); + if (SUCCEEDED(hr)) { + *ppMediaSink = pMedSink.Detach(); + } + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut(L"StreamSink::GetMediaSink: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE GetIdentifier( + /* [out] */ __RPC__out DWORD *pdwIdentifier) { + if (pdwIdentifier == NULL) + { + return E_INVALIDARG; + } + + EnterCriticalSection(&m_critSec); + + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + hr = GetUINT32(MF_STREAMSINK_ID, (UINT32*)pdwIdentifier); + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut(L"StreamSink::GetIdentifier: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE GetMediaTypeHandler( + /* [out] */ __RPC__deref_out_opt IMFMediaTypeHandler **ppHandler) { + if (ppHandler == NULL) + { + return E_INVALIDARG; + } + + EnterCriticalSection(&m_critSec); + + HRESULT hr = CheckShutdown(); + + // This stream object acts as its own type handler, so we QI ourselves. + if (SUCCEEDED(hr)) + { + hr = QueryInterface(IID_IMFMediaTypeHandler, (void**)ppHandler); + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut(L"StreamSink::GetMediaTypeHandler: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE ProcessSample(IMFSample *pSample) { + _ComPtr pInput; + _ComPtr pSampleCallback; + BYTE *pSrc = NULL; // Source buffer. + // Stride if the buffer does not support IMF2DBuffer + LONGLONG hnsTime = 0; + LONGLONG hnsDuration = 0; + DWORD cbMaxLength; + DWORD cbCurrentLength = 0; + GUID guidMajorType; + if (pSample == NULL) + { + return E_INVALIDARG; + } + HRESULT hr = S_OK; + + EnterCriticalSection(&m_critSec); + + if (m_state != State_Started && m_state != State_Paused) { + if (m_state == State_TypeNotSet) + hr = MF_E_NOT_INITIALIZED; + else + hr = MF_E_INVALIDREQUEST; + } + if (SUCCEEDED(hr)) + hr = CheckShutdown(); + if (SUCCEEDED(hr)) { + hr = pSample->ConvertToContiguousBuffer(&pInput); + if (SUCCEEDED(hr)) { + hr = pSample->GetSampleTime(&hnsTime); + } + if (SUCCEEDED(hr)) { + hr = pSample->GetSampleDuration(&hnsDuration); + } + if (SUCCEEDED(hr)) { + hr = GetMajorType(&guidMajorType); + } + if (SUCCEEDED(hr)) { + hr = m_pParent->GetUnknown(MF_MEDIASINK_SAMPLEGRABBERCALLBACK, IID_IMFSampleGrabberSinkCallback, (LPVOID*)pSampleCallback.GetAddressOf()); + } + if (SUCCEEDED(hr)) { + hr = pInput->Lock(&pSrc, &cbMaxLength, &cbCurrentLength); + } + if (SUCCEEDED(hr)) { + hr = pSampleCallback->OnProcessSample(guidMajorType, 0, hnsTime, hnsDuration, pSrc, cbCurrentLength); + pInput->Unlock(); + } + if (SUCCEEDED(hr)) { + hr = QueueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, NULL); + } + } + LeaveCriticalSection(&m_critSec); + return hr; + } + + HRESULT STDMETHODCALLTYPE PlaceMarker( + /* [in] */ MFSTREAMSINK_MARKER_TYPE eMarkerType, + /* [in] */ __RPC__in const PROPVARIANT * /*pvarMarkerValue*/, + /* [in] */ __RPC__in const PROPVARIANT * /*pvarContextValue*/) { + eMarkerType; + EnterCriticalSection(&m_critSec); + + HRESULT hr = S_OK; + if (m_state == State_TypeNotSet) + hr = MF_E_NOT_INITIALIZED; + + if (SUCCEEDED(hr)) + hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + //at shutdown will receive MFSTREAMSINK_MARKER_ENDOFSEGMENT + hr = QueueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, NULL); + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut(L"StreamSink::PlaceMarker: HRESULT=%i %s\n", hr, StreamSinkMarkerTypeMap.at(eMarkerType).c_str()); + return hr; + } + + HRESULT STDMETHODCALLTYPE Flush(void) { + EnterCriticalSection(&m_critSec); + + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut(L"StreamSink::Flush: HRESULT=%i\n", hr); + return hr; + } + + //IMFMediaEventGenerator + HRESULT STDMETHODCALLTYPE GetEvent( + DWORD dwFlags, IMFMediaEvent **ppEvent) { + // NOTE: + // GetEvent can block indefinitely, so we don't hold the lock. + // This requires some juggling with the event queue pointer. + + HRESULT hr = S_OK; + + _ComPtr pQueue; + + { + EnterCriticalSection(&m_critSec); + + // Check shutdown + hr = CheckShutdown(); + + // Get the pointer to the event queue. + if (SUCCEEDED(hr)) + { + pQueue = m_spEventQueue.Get(); + } + LeaveCriticalSection(&m_critSec); + } + + // Now get the event. + if (SUCCEEDED(hr)) + { + hr = pQueue->GetEvent(dwFlags, ppEvent); + } + MediaEventType meType = MEUnknown; + if (SUCCEEDED(hr) && SUCCEEDED((*ppEvent)->GetType(&meType)) && meType == MEStreamSinkStopped) { + } + HRESULT hrStatus = S_OK; + if (SUCCEEDED(hr)) + hr = (*ppEvent)->GetStatus(&hrStatus); + if (SUCCEEDED(hr)) + DebugPrintOut(L"StreamSink::GetEvent: HRESULT=%i %s\n", hrStatus, MediaEventTypeMap.at(meType).c_str()); + else + DebugPrintOut(L"StreamSink::GetEvent: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE BeginGetEvent( + IMFAsyncCallback *pCallback, IUnknown *punkState) { + HRESULT hr = S_OK; + + EnterCriticalSection(&m_critSec); + + hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + hr = m_spEventQueue->BeginGetEvent(pCallback, punkState); + } + LeaveCriticalSection(&m_critSec); + DebugPrintOut(L"StreamSink::BeginGetEvent: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE EndGetEvent( + IMFAsyncResult *pResult, IMFMediaEvent **ppEvent) { + HRESULT hr = S_OK; + + EnterCriticalSection(&m_critSec); + + hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + hr = m_spEventQueue->EndGetEvent(pResult, ppEvent); + } + + MediaEventType meType = MEUnknown; + if (SUCCEEDED(hr) && SUCCEEDED((*ppEvent)->GetType(&meType)) && meType == MEStreamSinkStopped) { + } + + LeaveCriticalSection(&m_critSec); + HRESULT hrStatus = S_OK; + if (SUCCEEDED(hr)) + hr = (*ppEvent)->GetStatus(&hrStatus); + if (SUCCEEDED(hr)) + DebugPrintOut(L"StreamSink::EndGetEvent: HRESULT=%i %s\n", hrStatus, MediaEventTypeMap.at(meType).c_str()); + else + DebugPrintOut(L"StreamSink::EndGetEvent: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE QueueEvent( + MediaEventType met, REFGUID guidExtendedType, + HRESULT hrStatus, const PROPVARIANT *pvValue) { + HRESULT hr = S_OK; + + EnterCriticalSection(&m_critSec); + + hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + hr = m_spEventQueue->QueueEventParamVar(met, guidExtendedType, hrStatus, pvValue); + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut(L"StreamSink::QueueEvent: HRESULT=%i %s\n", hrStatus, MediaEventTypeMap.at(met).c_str()); + DebugPrintOut(L"StreamSink::QueueEvent: HRESULT=%i\n", hr); + return hr; + } + + /// IMFMediaTypeHandler methods + + // Check if a media type is supported. + STDMETHODIMP IsMediaTypeSupported( + /* [in] */ IMFMediaType *pMediaType, + /* [out] */ IMFMediaType **ppMediaType) + { + if (pMediaType == nullptr) + { + return E_INVALIDARG; + } + + EnterCriticalSection(&m_critSec); + + GUID majorType = GUID_NULL; + + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + hr = pMediaType->GetGUID(MF_MT_MAJOR_TYPE, &majorType); + } + + // First make sure it's video or audio type. + if (SUCCEEDED(hr)) + { + if (majorType != MFMediaType_Video && majorType != MFMediaType_Audio) + { + hr = MF_E_INVALIDTYPE; + } + } + + if (SUCCEEDED(hr) && m_spCurrentType != nullptr) + { + GUID guiNewSubtype; + if (FAILED(pMediaType->GetGUID(MF_MT_SUBTYPE, &guiNewSubtype)) || + guiNewSubtype != m_guiCurrentSubtype) + { + hr = MF_E_INVALIDTYPE; + } + } + // We don't return any "close match" types. + if (ppMediaType) + { + *ppMediaType = nullptr; + } + + if (ppMediaType && SUCCEEDED(hr)) { + _ComPtr pType; + hr = MFCreateMediaType(ppMediaType); + if (SUCCEEDED(hr)) { + hr = m_pParent->GetUnknown(MF_MEDIASINK_PREFERREDTYPE, __uuidof(IMFMediaType), (LPVOID*)&pType); + } + if (SUCCEEDED(hr)) { + hr = pType->LockStore(); + } + bool bLocked = false; + if (SUCCEEDED(hr)) { + bLocked = true; + UINT32 uiCount; + UINT32 uiTotal; + hr = pType->GetCount(&uiTotal); + for (uiCount = 0; SUCCEEDED(hr) && uiCount < uiTotal; uiCount++) { + GUID guid; + PROPVARIANT propval; + hr = pType->GetItemByIndex(uiCount, &guid, &propval); + if (SUCCEEDED(hr) && (guid == MF_MT_FRAME_SIZE || guid == MF_MT_MAJOR_TYPE || guid == MF_MT_PIXEL_ASPECT_RATIO || + guid == MF_MT_ALL_SAMPLES_INDEPENDENT || guid == MF_MT_INTERLACE_MODE || guid == MF_MT_SUBTYPE)) { + hr = (*ppMediaType)->SetItem(guid, propval); + PropVariantClear(&propval); + } + } + } + if (bLocked) { + hr = pType->UnlockStore(); + } + } + LeaveCriticalSection(&m_critSec); + DebugPrintOut(L"StreamSink::IsMediaTypeSupported: HRESULT=%i\n", hr); + return hr; + } + + + // Return the number of preferred media types. + STDMETHODIMP GetMediaTypeCount(DWORD *pdwTypeCount) + { + if (pdwTypeCount == nullptr) + { + return E_INVALIDARG; + } + + EnterCriticalSection(&m_critSec); + + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + // We've got only one media type + *pdwTypeCount = 1; + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut(L"StreamSink::GetMediaTypeCount: HRESULT=%i\n", hr); + return hr; + } + + + // Return a preferred media type by index. + STDMETHODIMP GetMediaTypeByIndex( + /* [in] */ DWORD dwIndex, + /* [out] */ IMFMediaType **ppType) + { + if (ppType == NULL) { + return E_INVALIDARG; + } + + EnterCriticalSection(&m_critSec); + + HRESULT hr = CheckShutdown(); + + if (dwIndex > 0) + { + hr = MF_E_NO_MORE_TYPES; + } else { + //return preferred type based on media capture library 6 elements preferred preview type + //hr = m_spCurrentType.CopyTo(ppType); + if (SUCCEEDED(hr)) { + _ComPtr pType; + hr = MFCreateMediaType(ppType); + if (SUCCEEDED(hr)) { + hr = m_pParent->GetUnknown(MF_MEDIASINK_PREFERREDTYPE, __uuidof(IMFMediaType), (LPVOID*)&pType); + } + if (SUCCEEDED(hr)) { + hr = pType->LockStore(); + } + bool bLocked = false; + if (SUCCEEDED(hr)) { + bLocked = true; + UINT32 uiCount; + UINT32 uiTotal; + hr = pType->GetCount(&uiTotal); + for (uiCount = 0; SUCCEEDED(hr) && uiCount < uiTotal; uiCount++) { + GUID guid; + PROPVARIANT propval; + hr = pType->GetItemByIndex(uiCount, &guid, &propval); + if (SUCCEEDED(hr) && (guid == MF_MT_FRAME_SIZE || guid == MF_MT_MAJOR_TYPE || guid == MF_MT_PIXEL_ASPECT_RATIO || + guid == MF_MT_ALL_SAMPLES_INDEPENDENT || guid == MF_MT_INTERLACE_MODE || guid == MF_MT_SUBTYPE)) { + hr = (*ppType)->SetItem(guid, propval); + PropVariantClear(&propval); + } + } + } + if (bLocked) { + hr = pType->UnlockStore(); + } + } + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut(L"StreamSink::GetMediaTypeByIndex: HRESULT=%i\n", hr); + return hr; + } + + + // Set the current media type. + STDMETHODIMP SetCurrentMediaType(IMFMediaType *pMediaType) + { + if (pMediaType == NULL) { + return E_INVALIDARG; + } + EnterCriticalSection(&m_critSec); + + HRESULT hr = S_OK; + if (m_state != State_TypeNotSet && m_state != State_Ready) + hr = MF_E_INVALIDREQUEST; + if (SUCCEEDED(hr)) + hr = CheckShutdown(); + + // We don't allow format changes after streaming starts. + + // We set media type already + if (m_state >= State_Ready) + { + if (SUCCEEDED(hr)) + { + hr = IsMediaTypeSupported(pMediaType, NULL); + } + } + + if (SUCCEEDED(hr)) + { + hr = MFCreateMediaType(m_spCurrentType.ReleaseAndGetAddressOf()); + if (SUCCEEDED(hr)) + { + hr = pMediaType->CopyAllItems(m_spCurrentType.Get()); + } + if (SUCCEEDED(hr)) + { + hr = m_spCurrentType->GetGUID(MF_MT_SUBTYPE, &m_guiCurrentSubtype); + } + GUID guid; + if (SUCCEEDED(hr)) { + hr = m_spCurrentType->GetMajorType(&guid); + } + if (SUCCEEDED(hr) && guid == MFMediaType_Video) { + hr = MFGetAttributeSize(m_spCurrentType.Get(), MF_MT_FRAME_SIZE, &m_imageWidthInPixels, &m_imageHeightInPixels); + } + if (SUCCEEDED(hr)) + { + m_state = State_Ready; + } + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut(L"StreamSink::SetCurrentMediaType: HRESULT=%i\n", hr); + return hr; + } + + // Return the current media type, if any. + STDMETHODIMP GetCurrentMediaType(IMFMediaType **ppMediaType) + { + if (ppMediaType == NULL) { + return E_INVALIDARG; + } + + EnterCriticalSection(&m_critSec); + + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) { + if (m_spCurrentType == nullptr) { + hr = MF_E_NOT_INITIALIZED; + } + } + + if (SUCCEEDED(hr)) { + hr = m_spCurrentType.CopyTo(ppMediaType); + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut(L"StreamSink::GetCurrentMediaType: HRESULT=%i\n", hr); + return hr; + } + + + // Return the major type GUID. + STDMETHODIMP GetMajorType(GUID *pguidMajorType) + { + HRESULT hr; + if (pguidMajorType == nullptr) { + return E_INVALIDARG; + } + + _ComPtr pType; + hr = m_pParent->GetUnknown(MF_MEDIASINK_PREFERREDTYPE, __uuidof(IMFMediaType), (LPVOID*)&pType); + if (SUCCEEDED(hr)) { + hr = pType->GetMajorType(pguidMajorType); + } + DebugPrintOut(L"StreamSink::GetMajorType: HRESULT=%i\n", hr); + return hr; + } +private: +#ifdef HAVE_WINRT + EventRegistrationToken m_token; +#else + bool m_bConnected; +#endif + + bool m_IsShutdown; // Flag to indicate if Shutdown() method was called. + CRITICAL_SECTION m_critSec; +#ifndef HAVE_WINRT + long m_cRef; +#endif + IMFAttributes* m_pParent; + _ComPtr m_spCurrentType; + _ComPtr m_spEventQueue; // Event queue + + _ComPtr m_spFTM; + State m_state; + bool m_fGetStartTimeFromSample; + bool m_fWaitingForFirstSample; + MFTIME m_StartTime; // Presentation time when the clock started. + GUID m_guiCurrentSubtype; + UINT32 m_imageWidthInPixels; + UINT32 m_imageHeightInPixels; +}; + +// Notes: +// +// The List class template implements a simple double-linked list. +// It uses STL's copy semantics. + +// There are two versions of the Clear() method: +// Clear(void) clears the list w/out cleaning up the object. +// Clear(FN fn) takes a functor object that releases the objects, if they need cleanup. + +// The List class supports enumeration. Example of usage: +// +// List::POSIITON pos = list.GetFrontPosition(); +// while (pos != list.GetEndPosition()) +// { +// T item; +// hr = list.GetItemPos(&item); +// pos = list.Next(pos); +// } + +// The ComPtrList class template derives from List<> and implements a list of COM pointers. + +template +struct NoOp +{ + void operator()(T& /*t*/) + { + } +}; + +template +class List +{ +protected: + + // Nodes in the linked list + struct Node + { + Node *prev; + Node *next; + T item; + + Node() : prev(nullptr), next(nullptr) + { + } + + Node(T item) : prev(nullptr), next(nullptr) + { + this->item = item; + } + + T Item() const { return item; } + }; + +public: + + // Object for enumerating the list. + class POSITION + { + friend class List; + + public: + POSITION() : pNode(nullptr) + { + } + + bool operator==(const POSITION &p) const + { + return pNode == p.pNode; + } + + bool operator!=(const POSITION &p) const + { + return pNode != p.pNode; + } + + private: + const Node *pNode; + + POSITION(Node *p) : pNode(p) + { + } + }; + +protected: + Node m_anchor; // Anchor node for the linked list. + DWORD m_count; // Number of items in the list. + + Node* Front() const + { + return m_anchor.next; + } + + Node* Back() const + { + return m_anchor.prev; + } + + virtual HRESULT InsertAfter(T item, Node *pBefore) + { + if (pBefore == nullptr) + { + return E_POINTER; + } + + Node *pNode = new Node(item); + if (pNode == nullptr) + { + return E_OUTOFMEMORY; + } + + Node *pAfter = pBefore->next; + + pBefore->next = pNode; + pAfter->prev = pNode; + + pNode->prev = pBefore; + pNode->next = pAfter; + + m_count++; + + return S_OK; + } + + virtual HRESULT GetItem(const Node *pNode, T* ppItem) + { + if (pNode == nullptr || ppItem == nullptr) + { + return E_POINTER; + } + + *ppItem = pNode->item; + return S_OK; + } + + // RemoveItem: + // Removes a node and optionally returns the item. + // ppItem can be nullptr. + virtual HRESULT RemoveItem(Node *pNode, T *ppItem) + { + if (pNode == nullptr) + { + return E_POINTER; + } + + assert(pNode != &m_anchor); // We should never try to remove the anchor node. + if (pNode == &m_anchor) + { + return E_INVALIDARG; + } + + + T item; + + // The next node's previous is this node's previous. + pNode->next->prev = pNode->prev; + + // The previous node's next is this node's next. + pNode->prev->next = pNode->next; + + item = pNode->item; + delete pNode; + + m_count--; + + if (ppItem) + { + *ppItem = item; + } + + return S_OK; + } + +public: + + List() + { + m_anchor.next = &m_anchor; + m_anchor.prev = &m_anchor; + + m_count = 0; + } + + virtual ~List() + { + Clear(); + } + + // Insertion functions + HRESULT InsertBack(T item) + { + return InsertAfter(item, m_anchor.prev); + } + + + HRESULT InsertFront(T item) + { + return InsertAfter(item, &m_anchor); + } + + HRESULT InsertPos(POSITION pos, T item) + { + if (pos.pNode == nullptr) + { + return InsertBack(item); + } + + return InsertAfter(item, pos.pNode->prev); + } + + // RemoveBack: Removes the tail of the list and returns the value. + // ppItem can be nullptr if you don't want the item back. (But the method does not release the item.) + HRESULT RemoveBack(T *ppItem) + { + if (IsEmpty()) + { + return E_FAIL; + } + else + { + return RemoveItem(Back(), ppItem); + } + } + + // RemoveFront: Removes the head of the list and returns the value. + // ppItem can be nullptr if you don't want the item back. (But the method does not release the item.) + HRESULT RemoveFront(T *ppItem) + { + if (IsEmpty()) + { + return E_FAIL; + } + else + { + return RemoveItem(Front(), ppItem); + } + } + + // GetBack: Gets the tail item. + HRESULT GetBack(T *ppItem) + { + if (IsEmpty()) + { + return E_FAIL; + } + else + { + return GetItem(Back(), ppItem); + } + } + + // GetFront: Gets the front item. + HRESULT GetFront(T *ppItem) + { + if (IsEmpty()) + { + return E_FAIL; + } + else + { + return GetItem(Front(), ppItem); + } + } + + + // GetCount: Returns the number of items in the list. + DWORD GetCount() const { return m_count; } + + bool IsEmpty() const + { + return (GetCount() == 0); + } + + // Clear: Takes a functor object whose operator() + // frees the object on the list. + template + void Clear(FN& clear_fn) + { + Node *n = m_anchor.next; + + // Delete the nodes + while (n != &m_anchor) + { + clear_fn(n->item); + + Node *tmp = n->next; + delete n; + n = tmp; + } + + // Reset the anchor to point at itself + m_anchor.next = &m_anchor; + m_anchor.prev = &m_anchor; + + m_count = 0; + } + + // Clear: Clears the list. (Does not delete or release the list items.) + virtual void Clear() + { + NoOp clearOp; + Clear<>(clearOp); + } + + + // Enumerator functions + + POSITION FrontPosition() + { + if (IsEmpty()) + { + return POSITION(nullptr); + } + else + { + return POSITION(Front()); + } + } + + POSITION EndPosition() const + { + return POSITION(); + } + + HRESULT GetItemPos(POSITION pos, T *ppItem) + { + if (pos.pNode) + { + return GetItem(pos.pNode, ppItem); + } + else + { + return E_FAIL; + } + } + + POSITION Next(const POSITION pos) + { + if (pos.pNode && (pos.pNode->next != &m_anchor)) + { + return POSITION(pos.pNode->next); + } + else + { + return POSITION(nullptr); + } + } + + // Remove an item at a position. + // The item is returns in ppItem, unless ppItem is nullptr. + // NOTE: This method invalidates the POSITION object. + HRESULT Remove(POSITION& pos, T *ppItem) + { + if (pos.pNode) + { + // Remove const-ness temporarily... + Node *pNode = const_cast(pos.pNode); + + pos = POSITION(); + + return RemoveItem(pNode, ppItem); + } + else + { + return E_INVALIDARG; + } + } + +}; + + + +// Typical functors for Clear method. + +// ComAutoRelease: Releases COM pointers. +// MemDelete: Deletes pointers to new'd memory. + +class ComAutoRelease +{ +public: + void operator()(IUnknown *p) + { + if (p) + { + p->Release(); + } + } +}; + +class MemDelete +{ +public: + void operator()(void *p) + { + if (p) + { + delete p; + } + } +}; + + +// ComPtrList class +// Derived class that makes it safer to store COM pointers in the List<> class. +// It automatically AddRef's the pointers that are inserted onto the list +// (unless the insertion method fails). +// +// T must be a COM interface type. +// example: ComPtrList +// +// NULLABLE: If true, client can insert nullptr pointers. This means GetItem can +// succeed but return a nullptr pointer. By default, the list does not allow nullptr +// pointers. + +template +class ComPtrList : public List +{ +public: + + typedef T* Ptr; + + void Clear() + { + ComAutoRelease car; + List::Clear(car); + } + + ~ComPtrList() + { + Clear(); + } + +protected: + HRESULT InsertAfter(Ptr item, Node *pBefore) + { + // Do not allow nullptr item pointers unless NULLABLE is true. + if (item == nullptr && !NULLABLE) + { + return E_POINTER; + } + + if (item) + { + item->AddRef(); + } + + HRESULT hr = List::InsertAfter(item, pBefore); + if (FAILED(hr) && item != nullptr) + { + item->Release(); + } + return hr; + } + + HRESULT GetItem(const Node *pNode, Ptr* ppItem) + { + Ptr pItem = nullptr; + + // The base class gives us the pointer without AddRef'ing it. + // If we return the pointer to the caller, we must AddRef(). + HRESULT hr = List::GetItem(pNode, &pItem); + if (SUCCEEDED(hr)) + { + assert(pItem || NULLABLE); + if (pItem) + { + *ppItem = pItem; + (*ppItem)->AddRef(); + } + } + return hr; + } + + HRESULT RemoveItem(Node *pNode, Ptr *ppItem) + { + // ppItem can be nullptr, but we need to get the + // item so that we can release it. + + // If ppItem is not nullptr, we will AddRef it on the way out. + + Ptr pItem = nullptr; + + HRESULT hr = List::RemoveItem(pNode, &pItem); + + if (SUCCEEDED(hr)) + { + assert(pItem || NULLABLE); + if (ppItem && pItem) + { + *ppItem = pItem; + (*ppItem)->AddRef(); + } + + if (pItem) + { + pItem->Release(); + pItem = nullptr; + } + } + + return hr; + } +}; + +/* Be sure to declare webcam device capability in manifest + For better media capture support, add the following snippet with correct module name to the project manifest + (highgui needs DLL activation class factoryentry points): + + + + modulename + + + + */ + +extern const __declspec(selectany) WCHAR RuntimeClass_CV_MediaSink[] = L"cv.MediaSink"; + +class MediaSink : +#ifdef HAVE_WINRT + public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::RuntimeClassType::WinRtClassicComMix >, + Microsoft::WRL::Implements, + IMFMediaSink, + IMFClockStateSink, + Microsoft::WRL::FtmBase, + CBaseAttributes<>> +#else + public IMFMediaSink, public IMFClockStateSink, public CBaseAttributes<> +#endif +{ +#ifdef HAVE_WINRT + InspectableClass(RuntimeClass_CV_MediaSink, BaseTrust) +public: +#else +public: + ULONG STDMETHODCALLTYPE AddRef() + { + return InterlockedIncrement(&m_cRef); + } + ULONG STDMETHODCALLTYPE Release() + { + ULONG cRef = InterlockedDecrement(&m_cRef); + if (cRef == 0) + { + delete this; + } + return cRef; + } + STDMETHOD(QueryInterface)(REFIID riid, _Outptr_result_nullonfailure_ void **ppv) + { + if (ppv == nullptr) { + return E_POINTER; + } + (*ppv) = nullptr; + HRESULT hr = S_OK; + if (riid == IID_IUnknown || + riid == IID_IMFMediaSink) { + (*ppv) = static_cast(this); + AddRef(); + } else if (riid == IID_IMFClockStateSink) { + (*ppv) = static_cast(this); + AddRef(); + } else if (riid == IID_IMFAttributes) { + (*ppv) = static_cast(this); + AddRef(); + } else { + hr = E_NOINTERFACE; + } + + return hr; + } +#endif + MediaSink() : m_IsShutdown(false), m_llStartTime(0) { + CBaseAttributes<>::Initialize(0U); + InitializeCriticalSectionEx(&m_critSec, 3000, 0); + DebugPrintOut(L"MediaSink::MediaSink\n"); + } + + virtual ~MediaSink() { + DebugPrintOut(L"MediaSink::~MediaSink\n"); + DeleteCriticalSection(&m_critSec); + assert(m_IsShutdown); + } + HRESULT CheckShutdown() const + { + if (m_IsShutdown) + { + return MF_E_SHUTDOWN; + } + else + { + return S_OK; + } + } +#ifdef HAVE_WINRT + STDMETHODIMP SetProperties(ABI::Windows::Foundation::Collections::IPropertySet *pConfiguration) + { + HRESULT hr = S_OK; + if (pConfiguration) { + Microsoft::WRL::ComPtr spInsp; + Microsoft::WRL::ComPtr> spSetting; + Microsoft::WRL::ComPtr spPropVal; + Microsoft::WRL::ComPtr pMedEncProps; + UINT32 uiType = ABI::Windows::Media::Capture::MediaStreamType_VideoPreview; + + hr = pConfiguration->QueryInterface(IID_PPV_ARGS(&spSetting)); + if (FAILED(hr)) { + hr = E_FAIL; + } + + if (SUCCEEDED(hr)) { + hr = spSetting->Lookup(Microsoft::WRL::Wrappers::HStringReference(MF_PROP_SAMPLEGRABBERCALLBACK).Get(), spInsp.ReleaseAndGetAddressOf()); + if (FAILED(hr)) { + hr = E_INVALIDARG; + } + if (SUCCEEDED(hr)) { + hr = SetUnknown(MF_MEDIASINK_SAMPLEGRABBERCALLBACK, spInsp.Get()); + } + } + if (SUCCEEDED(hr)) { + hr = spSetting->Lookup(Microsoft::WRL::Wrappers::HStringReference(MF_PROP_VIDTYPE).Get(), spInsp.ReleaseAndGetAddressOf()); + if (FAILED(hr)) { + hr = E_INVALIDARG; + } + if (SUCCEEDED(hr)) { + if (SUCCEEDED(hr = spInsp.As(&spPropVal))) { + hr = spPropVal->GetUInt32(&uiType); + } + } + } + if (SUCCEEDED(hr)) { + hr = spSetting->Lookup(Microsoft::WRL::Wrappers::HStringReference(MF_PROP_VIDENCPROPS).Get(), spInsp.ReleaseAndGetAddressOf()); + if (FAILED(hr)) { + hr = E_INVALIDARG; + } + if (SUCCEEDED(hr)) { + hr = spInsp.As(&pMedEncProps); + } + } + if (SUCCEEDED(hr)) { + hr = SetMediaStreamProperties((ABI::Windows::Media::Capture::MediaStreamType)uiType, pMedEncProps.Get()); + } + } + + return hr; + } + static DWORD GetStreamId(ABI::Windows::Media::Capture::MediaStreamType mediaStreamType) + { + return 3 - mediaStreamType; + } + static HRESULT AddAttribute(_In_ GUID guidKey, _In_ ABI::Windows::Foundation::IPropertyValue *pValue, _In_ IMFAttributes* pAttr) + { + HRESULT hr = S_OK; + PROPVARIANT var; + ABI::Windows::Foundation::PropertyType type; + hr = pValue->get_Type(&type); + ZeroMemory(&var, sizeof(var)); + + if (SUCCEEDED(hr)) + { + switch (type) + { + case ABI::Windows::Foundation::PropertyType_UInt8Array: + { + UINT32 cbBlob; + BYTE *pbBlog = nullptr; + hr = pValue->GetUInt8Array(&cbBlob, &pbBlog); + if (SUCCEEDED(hr)) + { + if (pbBlog == nullptr) + { + hr = E_INVALIDARG; + } + else + { + hr = pAttr->SetBlob(guidKey, pbBlog, cbBlob); + } + } + CoTaskMemFree(pbBlog); + } + break; + + case ABI::Windows::Foundation::PropertyType_Double: + { + DOUBLE value; + hr = pValue->GetDouble(&value); + if (SUCCEEDED(hr)) + { + hr = pAttr->SetDouble(guidKey, value); + } + } + break; + + case ABI::Windows::Foundation::PropertyType_Guid: + { + GUID value; + hr = pValue->GetGuid(&value); + if (SUCCEEDED(hr)) + { + hr = pAttr->SetGUID(guidKey, value); + } + } + break; + + case ABI::Windows::Foundation::PropertyType_String: + { + Microsoft::WRL::Wrappers::HString value; + hr = pValue->GetString(value.GetAddressOf()); + if (SUCCEEDED(hr)) + { + UINT32 len = 0; + LPCWSTR szValue = WindowsGetStringRawBuffer(value.Get(), &len); + hr = pAttr->SetString(guidKey, szValue); + } + } + break; + + case ABI::Windows::Foundation::PropertyType_UInt32: + { + UINT32 value; + hr = pValue->GetUInt32(&value); + if (SUCCEEDED(hr)) + { + pAttr->SetUINT32(guidKey, value); + } + } + break; + + case ABI::Windows::Foundation::PropertyType_UInt64: + { + UINT64 value; + hr = pValue->GetUInt64(&value); + if (SUCCEEDED(hr)) + { + hr = pAttr->SetUINT64(guidKey, value); + } + } + break; + + case ABI::Windows::Foundation::PropertyType_Inspectable: + { + Microsoft::WRL::ComPtr value; + hr = TYPE_E_TYPEMISMATCH; + if (SUCCEEDED(hr)) + { + pAttr->SetUnknown(guidKey, value.Get()); + } + } + break; + + // ignore unknown values + } + } + + return hr; + } + static HRESULT ConvertPropertiesToMediaType(_In_ ABI::Windows::Media::MediaProperties::IMediaEncodingProperties *pMEP, _Outptr_ IMFMediaType **ppMT) + { + HRESULT hr = S_OK; + _ComPtr spMT; + Microsoft::WRL::ComPtr> spMap; + Microsoft::WRL::ComPtr*>> spIterable; + Microsoft::WRL::ComPtr*>> spIterator; + + if (pMEP == nullptr || ppMT == nullptr) + { + return E_INVALIDARG; + } + *ppMT = nullptr; + + hr = pMEP->get_Properties(spMap.GetAddressOf()); + + if (SUCCEEDED(hr)) + { + hr = spMap.As(&spIterable); + } + if (SUCCEEDED(hr)) + { + hr = spIterable->First(&spIterator); + } + if (SUCCEEDED(hr)) + { + MFCreateMediaType(spMT.ReleaseAndGetAddressOf()); + } + + boolean hasCurrent = false; + if (SUCCEEDED(hr)) + { + hr = spIterator->get_HasCurrent(&hasCurrent); + } + + while (hasCurrent) + { + Microsoft::WRL::ComPtr > spKeyValuePair; + Microsoft::WRL::ComPtr spValue; + Microsoft::WRL::ComPtr spPropValue; + GUID guidKey; + + hr = spIterator->get_Current(&spKeyValuePair); + if (FAILED(hr)) + { + break; + } + hr = spKeyValuePair->get_Key(&guidKey); + if (FAILED(hr)) + { + break; + } + hr = spKeyValuePair->get_Value(&spValue); + if (FAILED(hr)) + { + break; + } + hr = spValue.As(&spPropValue); + if (FAILED(hr)) + { + break; + } + hr = AddAttribute(guidKey, spPropValue.Get(), spMT.Get()); + if (FAILED(hr)) + { + break; + } + + hr = spIterator->MoveNext(&hasCurrent); + if (FAILED(hr)) + { + break; + } + } + + + if (SUCCEEDED(hr)) + { + Microsoft::WRL::ComPtr spValue; + Microsoft::WRL::ComPtr spPropValue; + GUID guiMajorType; + + hr = spMap->Lookup(MF_MT_MAJOR_TYPE, spValue.GetAddressOf()); + + if (SUCCEEDED(hr)) + { + hr = spValue.As(&spPropValue); + } + if (SUCCEEDED(hr)) + { + hr = spPropValue->GetGuid(&guiMajorType); + } + if (SUCCEEDED(hr)) + { + if (guiMajorType != MFMediaType_Video && guiMajorType != MFMediaType_Audio) + { + hr = E_UNEXPECTED; + } + } + } + + if (SUCCEEDED(hr)) + { + *ppMT = spMT.Detach(); + } + + return hr; + } + //this should be passed through SetProperties! + HRESULT SetMediaStreamProperties(ABI::Windows::Media::Capture::MediaStreamType MediaStreamType, + _In_opt_ ABI::Windows::Media::MediaProperties::IMediaEncodingProperties *mediaEncodingProperties) + { + HRESULT hr = S_OK; + _ComPtr spMediaType; + + if (MediaStreamType != ABI::Windows::Media::Capture::MediaStreamType_VideoPreview && + MediaStreamType != ABI::Windows::Media::Capture::MediaStreamType_VideoRecord && + MediaStreamType != ABI::Windows::Media::Capture::MediaStreamType_Audio) + { + return E_INVALIDARG; + } + + RemoveStreamSink(GetStreamId(MediaStreamType)); + + if (mediaEncodingProperties != nullptr) + { + _ComPtr spStreamSink; + hr = ConvertPropertiesToMediaType(mediaEncodingProperties, &spMediaType); + if (SUCCEEDED(hr)) + { + hr = AddStreamSink(GetStreamId(MediaStreamType), nullptr, spStreamSink.GetAddressOf()); + } + if (SUCCEEDED(hr)) { + hr = SetUnknown(MF_MEDIASINK_PREFERREDTYPE, spMediaType.Detach()); + } + } + + return hr; + } +#endif + //IMFMediaSink + HRESULT STDMETHODCALLTYPE GetCharacteristics( + /* [out] */ __RPC__out DWORD *pdwCharacteristics) { + HRESULT hr; + if (pdwCharacteristics == NULL) return E_INVALIDARG; + EnterCriticalSection(&m_critSec); + if (SUCCEEDED(hr = CheckShutdown())) { + //if had an activation object for the sink, shut down would be managed and MF_STREAM_SINK_SUPPORTS_ROTATION appears to be setable to TRUE + *pdwCharacteristics = MEDIASINK_FIXED_STREAMS;// | MEDIASINK_REQUIRE_REFERENCE_MEDIATYPE; + } + LeaveCriticalSection(&m_critSec); + DebugPrintOut(L"MediaSink::GetCharacteristics: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE AddStreamSink( + DWORD dwStreamSinkIdentifier, IMFMediaType * /*pMediaType*/, IMFStreamSink **ppStreamSink) { + _ComPtr spMFStream; + _ComPtr pStream; + EnterCriticalSection(&m_critSec); + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + hr = GetStreamSinkById(dwStreamSinkIdentifier, &spMFStream); + } + + if (SUCCEEDED(hr)) + { + hr = MF_E_STREAMSINK_EXISTS; + } + else + { + hr = S_OK; + } + + if (SUCCEEDED(hr)) + { +#ifdef HAVE_WINRT + pStream = Microsoft::WRL::Make(); + if (pStream == nullptr) { + hr = E_OUTOFMEMORY; + } + if (SUCCEEDED(hr)) + hr = pStream.As(&spMFStream); +#else + StreamSink* pSink = new StreamSink(); + if (pSink) { + hr = pSink->QueryInterface(IID_IMFStreamSink, (void**)spMFStream.GetAddressOf()); + if (SUCCEEDED(hr)) { + hr = spMFStream.As(&pStream); + } + if (FAILED(hr)) delete pSink; + } +#endif + } + + // Initialize the stream. + _ComPtr pAttr; + if (SUCCEEDED(hr)) { + hr = pStream.As(&pAttr); + } + if (SUCCEEDED(hr)) { + hr = pAttr->SetUINT32(MF_STREAMSINK_ID, dwStreamSinkIdentifier); + if (SUCCEEDED(hr)) { + hr = pAttr->SetUnknown(MF_STREAMSINK_MEDIASINKINTERFACE, (IMFMediaSink*)this); + } + } + if (SUCCEEDED(hr)) { + hr = pStream->Initialize(); + } + + if (SUCCEEDED(hr)) + { + ComPtrList::POSITION pos = m_streams.FrontPosition(); + ComPtrList::POSITION posEnd = m_streams.EndPosition(); + + // Insert in proper position + for (; pos != posEnd; pos = m_streams.Next(pos)) + { + DWORD dwCurrId; + _ComPtr spCurr; + hr = m_streams.GetItemPos(pos, &spCurr); + if (FAILED(hr)) + { + break; + } + hr = spCurr->GetIdentifier(&dwCurrId); + if (FAILED(hr)) + { + break; + } + + if (dwCurrId > dwStreamSinkIdentifier) + { + break; + } + } + + if (SUCCEEDED(hr)) + { + hr = m_streams.InsertPos(pos, spMFStream.Get()); + } + } + + if (SUCCEEDED(hr)) + { + *ppStreamSink = spMFStream.Detach(); + } + LeaveCriticalSection(&m_critSec); + DebugPrintOut(L"MediaSink::AddStreamSink: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE RemoveStreamSink(DWORD dwStreamSinkIdentifier) { + EnterCriticalSection(&m_critSec); + HRESULT hr = CheckShutdown(); + ComPtrList::POSITION pos = m_streams.FrontPosition(); + ComPtrList::POSITION endPos = m_streams.EndPosition(); + _ComPtr spStream; + + if (SUCCEEDED(hr)) + { + for (; pos != endPos; pos = m_streams.Next(pos)) + { + hr = m_streams.GetItemPos(pos, &spStream); + DWORD dwId; + + if (FAILED(hr)) + { + break; + } + + hr = spStream->GetIdentifier(&dwId); + if (FAILED(hr) || dwId == dwStreamSinkIdentifier) + { + break; + } + } + + if (pos == endPos) + { + hr = MF_E_INVALIDSTREAMNUMBER; + } + } + + if (SUCCEEDED(hr)) + { + hr = m_streams.Remove(pos, nullptr); + _ComPtr spCustomSink; +#ifdef HAVE_WINRT + spCustomSink = static_cast(spStream.Get()); + hr = S_OK; +#else + hr = spStream.As(&spCustomSink); +#endif + if (SUCCEEDED(hr)) + hr = spCustomSink->Shutdown(); + } + LeaveCriticalSection(&m_critSec); + DebugPrintOut(L"MediaSink::RemoveStreamSink: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE GetStreamSinkCount(DWORD *pStreamSinkCount) { + if (pStreamSinkCount == NULL) + { + return E_INVALIDARG; + } + + EnterCriticalSection(&m_critSec); + + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + *pStreamSinkCount = m_streams.GetCount(); + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut(L"MediaSink::GetStreamSinkCount: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE GetStreamSinkByIndex( + DWORD dwIndex, IMFStreamSink **ppStreamSink) { + if (ppStreamSink == NULL) + { + return E_INVALIDARG; + } + + _ComPtr spStream; + EnterCriticalSection(&m_critSec); + DWORD cStreams = m_streams.GetCount(); + + if (dwIndex >= cStreams) + { + return MF_E_INVALIDINDEX; + } + + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + ComPtrList::POSITION pos = m_streams.FrontPosition(); + ComPtrList::POSITION endPos = m_streams.EndPosition(); + DWORD dwCurrent = 0; + + for (; pos != endPos && dwCurrent < dwIndex; pos = m_streams.Next(pos), ++dwCurrent) + { + // Just move to proper position + } + + if (pos == endPos) + { + hr = MF_E_UNEXPECTED; + } + else + { + hr = m_streams.GetItemPos(pos, &spStream); + } + } + + if (SUCCEEDED(hr)) + { + *ppStreamSink = spStream.Detach(); + } + LeaveCriticalSection(&m_critSec); + DebugPrintOut(L"MediaSink::GetStreamSinkByIndex: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE GetStreamSinkById( + DWORD dwStreamSinkIdentifier, IMFStreamSink **ppStreamSink) { + if (ppStreamSink == NULL) + { + return E_INVALIDARG; + } + + EnterCriticalSection(&m_critSec); + HRESULT hr = CheckShutdown(); + _ComPtr spResult; + + if (SUCCEEDED(hr)) + { + ComPtrList::POSITION pos = m_streams.FrontPosition(); + ComPtrList::POSITION endPos = m_streams.EndPosition(); + + for (; pos != endPos; pos = m_streams.Next(pos)) + { + _ComPtr spStream; + hr = m_streams.GetItemPos(pos, &spStream); + DWORD dwId; + + if (FAILED(hr)) + { + break; + } + + hr = spStream->GetIdentifier(&dwId); + if (FAILED(hr)) + { + break; + } + else if (dwId == dwStreamSinkIdentifier) + { + spResult = spStream; + break; + } + } + + if (pos == endPos) + { + hr = MF_E_INVALIDSTREAMNUMBER; + } + } + + if (SUCCEEDED(hr)) + { + assert(spResult); + *ppStreamSink = spResult.Detach(); + } + LeaveCriticalSection(&m_critSec); + DebugPrintOut(L"MediaSink::GetStreamSinkById: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE SetPresentationClock( + IMFPresentationClock *pPresentationClock) { + EnterCriticalSection(&m_critSec); + + HRESULT hr = CheckShutdown(); + + // If we already have a clock, remove ourselves from that clock's + // state notifications. + if (SUCCEEDED(hr)) { + if (m_spClock) { + hr = m_spClock->RemoveClockStateSink(this); + } + } + + // Register ourselves to get state notifications from the new clock. + if (SUCCEEDED(hr)) { + if (pPresentationClock) { + hr = pPresentationClock->AddClockStateSink(this); + } + } + + _ComPtr pSampleCallback; + if (SUCCEEDED(hr)) { + // Release the pointer to the old clock. + // Store the pointer to the new clock. + m_spClock = pPresentationClock; + hr = GetUnknown(MF_MEDIASINK_SAMPLEGRABBERCALLBACK, IID_IMFSampleGrabberSinkCallback, (LPVOID*)pSampleCallback.GetAddressOf()); + } + LeaveCriticalSection(&m_critSec); + if (SUCCEEDED(hr)) + hr = pSampleCallback->OnSetPresentationClock(pPresentationClock); + DebugPrintOut(L"MediaSink::SetPresentationClock: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE GetPresentationClock( + IMFPresentationClock **ppPresentationClock) { + if (ppPresentationClock == NULL) { + return E_INVALIDARG; + } + + EnterCriticalSection(&m_critSec); + + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) { + if (m_spClock == NULL) { + hr = MF_E_NO_CLOCK; // There is no presentation clock. + } else { + // Return the pointer to the caller. + hr = m_spClock.CopyTo(ppPresentationClock); + } + } + LeaveCriticalSection(&m_critSec); + DebugPrintOut(L"MediaSink::GetPresentationClock: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE Shutdown(void) { + EnterCriticalSection(&m_critSec); + + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) { + ForEach(m_streams, ShutdownFunc()); + m_streams.Clear(); + m_spClock.ReleaseAndGetAddressOf(); + + _ComPtr pType; + hr = CBaseAttributes<>::GetUnknown(MF_MEDIASINK_PREFERREDTYPE, __uuidof(IMFMediaType), (LPVOID*)pType.GetAddressOf()); + if (SUCCEEDED(hr)) { + hr = DeleteItem(MF_MEDIASINK_PREFERREDTYPE); + } + m_IsShutdown = true; + } + + LeaveCriticalSection(&m_critSec); + DebugPrintOut(L"MediaSink::Shutdown: HRESULT=%i\n", hr); + return hr; + } + class ShutdownFunc + { + public: + HRESULT operator()(IMFStreamSink *pStream) const + { + _ComPtr spCustomSink; + HRESULT hr; +#ifdef HAVE_WINRT + spCustomSink = static_cast(pStream); +#else + hr = pStream->QueryInterface(IID_PPV_ARGS(spCustomSink.GetAddressOf())); + if (FAILED(hr)) return hr; +#endif + hr = spCustomSink->Shutdown(); + return hr; + } + }; + + class StartFunc + { + public: + StartFunc(LONGLONG llStartTime) + : _llStartTime(llStartTime) + { + } + + HRESULT operator()(IMFStreamSink *pStream) const + { + _ComPtr spCustomSink; + HRESULT hr; +#ifdef HAVE_WINRT + spCustomSink = static_cast(pStream); +#else + hr = pStream->QueryInterface(IID_PPV_ARGS(spCustomSink.GetAddressOf())); + if (FAILED(hr)) return hr; +#endif + hr = spCustomSink->Start(_llStartTime); + return hr; + } + + LONGLONG _llStartTime; + }; + + class StopFunc + { + public: + HRESULT operator()(IMFStreamSink *pStream) const + { + _ComPtr spCustomSink; + HRESULT hr; +#ifdef HAVE_WINRT + spCustomSink = static_cast(pStream); +#else + hr = pStream->QueryInterface(IID_PPV_ARGS(spCustomSink.GetAddressOf())); + if (FAILED(hr)) return hr; +#endif + hr = spCustomSink->Stop(); + return hr; + } + }; + + template + HRESULT ForEach(ComPtrList &col, TFunc fn) + { + ComPtrList::POSITION pos = col.FrontPosition(); + ComPtrList::POSITION endPos = col.EndPosition(); + HRESULT hr = S_OK; + + for (; pos != endPos; pos = col.Next(pos)) + { + _ComPtr spStream; + + hr = col.GetItemPos(pos, &spStream); + if (FAILED(hr)) + { + break; + } + + hr = fn(spStream.Get()); + } + + return hr; + } + //IMFClockStateSink + HRESULT STDMETHODCALLTYPE OnClockStart( + MFTIME hnsSystemTime, + LONGLONG llClockStartOffset) { + EnterCriticalSection(&m_critSec); + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + // Start each stream. + m_llStartTime = llClockStartOffset; + hr = ForEach(m_streams, StartFunc(llClockStartOffset)); + } + _ComPtr pSampleCallback; + if (SUCCEEDED(hr)) + hr = GetUnknown(MF_MEDIASINK_SAMPLEGRABBERCALLBACK, IID_IMFSampleGrabberSinkCallback, (LPVOID*)pSampleCallback.GetAddressOf()); + LeaveCriticalSection(&m_critSec); + if (SUCCEEDED(hr)) + hr = pSampleCallback->OnClockStart(hnsSystemTime, llClockStartOffset); + DebugPrintOut(L"MediaSink::OnClockStart: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE OnClockStop( + MFTIME hnsSystemTime) { + EnterCriticalSection(&m_critSec); + HRESULT hr = CheckShutdown(); + + if (SUCCEEDED(hr)) + { + // Stop each stream + hr = ForEach(m_streams, StopFunc()); + } + _ComPtr pSampleCallback; + if (SUCCEEDED(hr)) + hr = GetUnknown(MF_MEDIASINK_SAMPLEGRABBERCALLBACK, IID_IMFSampleGrabberSinkCallback, (LPVOID*)pSampleCallback.GetAddressOf()); + LeaveCriticalSection(&m_critSec); + if (SUCCEEDED(hr)) + hr = pSampleCallback->OnClockStop(hnsSystemTime); + DebugPrintOut(L"MediaSink::OnClockStop: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE OnClockPause( + MFTIME hnsSystemTime) { + HRESULT hr; + _ComPtr pSampleCallback; + hr = GetUnknown(MF_MEDIASINK_SAMPLEGRABBERCALLBACK, IID_IMFSampleGrabberSinkCallback, (LPVOID*)pSampleCallback.GetAddressOf()); + if (SUCCEEDED(hr)) + hr = pSampleCallback->OnClockPause(hnsSystemTime); + DebugPrintOut(L"MediaSink::OnClockPause: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE OnClockRestart( + MFTIME hnsSystemTime) { + HRESULT hr; + _ComPtr pSampleCallback; + hr = GetUnknown(MF_MEDIASINK_SAMPLEGRABBERCALLBACK, IID_IMFSampleGrabberSinkCallback, (LPVOID*)pSampleCallback.GetAddressOf()); + if (SUCCEEDED(hr)) + hr = pSampleCallback->OnClockRestart(hnsSystemTime); + DebugPrintOut(L"MediaSink::OnClockRestart: HRESULT=%i\n", hr); + return hr; + } + + HRESULT STDMETHODCALLTYPE OnClockSetRate( + MFTIME hnsSystemTime, + float flRate) { + HRESULT hr; + _ComPtr pSampleCallback; + hr = GetUnknown(MF_MEDIASINK_SAMPLEGRABBERCALLBACK, IID_IMFSampleGrabberSinkCallback, (LPVOID*)pSampleCallback.GetAddressOf()); + if (SUCCEEDED(hr)) + hr = pSampleCallback->OnClockSetRate(hnsSystemTime, flRate); + DebugPrintOut(L"MediaSink::OnClockSetRate: HRESULT=%i\n", hr); + return hr; + } +private: +#ifndef HAVE_WINRT + long m_cRef; +#endif + CRITICAL_SECTION m_critSec; + bool m_IsShutdown; + ComPtrList m_streams; + _ComPtr m_spClock; + LONGLONG m_llStartTime; +}; + +#ifdef HAVE_WINRT +ActivatableClass(MediaSink); +#endif diff --git a/modules/highgui/src/ppltasks_winrt.h b/modules/highgui/src/ppltasks_winrt.h new file mode 100644 index 0000000000..1243baea97 --- /dev/null +++ b/modules/highgui/src/ppltasks_winrt.h @@ -0,0 +1,9466 @@ +/*** +* ==++== +* +* Copyright (c) Microsoft Corporation. All rights reserved. +* +* Modified for native C++ WRL support by Gregory Morse +* +* ==--== +* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +* +* ppltasks_winrt.h +* +* Parallel Patterns Library - PPL Tasks +* +* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +****/ + +#pragma once + +#ifndef _PPLTASKS_WINRT_H +#define _PPLTASKS_WINRT_H + +#include +#include +#if _MSC_VER >= 1800 +#include + +// Cannot build using a compiler that is older than dev10 SP1 +#ifdef _MSC_VER +#if _MSC_FULL_VER < 160040219 /*IFSTRIP=IGN*/ +#error ERROR: Visual Studio 2010 SP1 or later is required to build ppltasks +#endif /*IFSTRIP=IGN*/ +#endif +#else +#include +#endif +#include +#include +#include +#include +#if _MSC_VER >= 1800 +#include +#endif + +#ifndef __cplusplus_winrt + +#include +#include +#if _MSC_VER >= 1800 +#include "agile_wrl.h" +#endif +#include +#include + +#ifndef _UITHREADCTXT_SUPPORT + +#ifdef WINAPI_FAMILY /*IFSTRIP=IGN*/ + +// It is safe to include winapifamily as WINAPI_FAMILY was defined by the user +#include + +#if WINAPI_FAMILY == WINAPI_FAMILY_APP /*IFSTRIP=IGN*/ + // UI thread context support is not required for desktop and Windows Store apps + #define _UITHREADCTXT_SUPPORT 0 +#elif WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP /*IFSTRIP=IGN*/ + // UI thread context support is not required for desktop and Windows Store apps + #define _UITHREADCTXT_SUPPORT 0 +#else /* WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP */ + #define _UITHREADCTXT_SUPPORT 1 +#endif /* WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP */ + +#else /* WINAPI_FAMILY */ + // Not supported without a WINAPI_FAMILY setting. + #define _UITHREADCTXT_SUPPORT 0 +#endif /* WINAPI_FAMILY */ + +#endif /* _UITHREADCTXT_SUPPORT */ + +#if _UITHREADCTXT_SUPPORT +#include +#endif /* _UITHREADCTXT_SUPPORT */ + +#pragma detect_mismatch("_PPLTASKS_WITH_WINRT", "0") + +#ifdef _DEBUG +#define _DBG_ONLY(X) X +#else +#define _DBG_ONLY(X) +#endif // #ifdef _DEBUG + +// std::copy_exception changed to std::make_exception_ptr from VS 2010 to VS 11. +#ifdef _MSC_VER +#if _MSC_VER < 1700 /*IFSTRIP=IGN*/ +namespace std +{ + template exception_ptr make_exception_ptr(_E _Except) + { + return copy_exception(_Except); + } +} +#endif +#ifndef _PPLTASK_ASYNC_LOGGING +#if _MSC_VER >= 1800 && defined(__cplusplus_winrt) +#define _PPLTASK_ASYNC_LOGGING 1 // Only enable async logging under dev12 winrt +#else +#define _PPLTASK_ASYNC_LOGGING 0 +#endif +#endif +#endif + +#pragma pack(push,_CRT_PACKING) + +#pragma warning(push) +#pragma warning(disable: 28197) +#pragma warning(disable: 4100) // Unreferenced formal parameter - needed for document generation +#if _MSC_VER >= 1800 +#pragma warning(disable: 4127) // constant express in if condition - we use it for meta programming +#else +#pragma warning(disable: 4702) // Unreachable code - it is caused by user lambda throw exceptions +#endif + +// All CRT public header files are required to be protected from the macro new +#pragma push_macro("new") +#undef new + +// stuff ported from Dev11 CRT +// NOTE: this doesn't actually match std::declval. it behaves differently for void! +// so don't blindly change it to std::declval. +namespace stdx +{ + template + _T&& declval(); +} + +/// +/// The Concurrency_winrt namespace provides classes and functions that give you access to the Concurrency Runtime, +/// a concurrent programming framework for C++. For more information, see . +/// +/**/ +namespace Concurrency_winrt +{ + // In debug builds, default to 10 frames, unless this is overridden prior to #includ'ing ppltasks.h. In retail builds, default to only one frame. +#ifndef PPL_TASK_SAVE_FRAME_COUNT +#ifdef _DEBUG +#define PPL_TASK_SAVE_FRAME_COUNT 10 +#else +#define PPL_TASK_SAVE_FRAME_COUNT 1 +#endif +#endif + + /// + /// Helper macro to determine how many stack frames need to be saved. When any number less or equal to 1 is specified, + /// only one frame is captured and no stackwalk will be involved. Otherwise, the number of callstack frames will be captured. + /// + /// + /// This needs to be defined as a macro rather than a function so that if we're only gathering one frame, _ReturnAddress() + /// will evaluate to client code, rather than a helper function inside of _TaskCreationCallstack, itself. + /// +#ifdef _CAPTURE_CALLSTACK +#undef _CAPTURE_CALLSTACK +#endif +#if PPL_TASK_SAVE_FRAME_COUNT > 1 +#if !defined(_DEBUG) +#pragma message ("WARNING: Redefinning PPL_TASK_SAVE_FRAME_COUNT under Release build for non-desktop applications is not supported; only one frame will be captured!") +#define _CAPTURE_CALLSTACK() ::Concurrency_winrt::details::_TaskCreationCallstack::_CaptureSingleFrameCallstack(_ReturnAddress()) +#else +#define _CAPTURE_CALLSTACK() ::Concurrency_winrt::details::_TaskCreationCallstack::_CaptureMultiFramesCallstack(PPL_TASK_SAVE_FRAME_COUNT) +#endif +#else +#define _CAPTURE_CALLSTACK() ::Concurrency_winrt::details::_TaskCreationCallstack::_CaptureSingleFrameCallstack(_ReturnAddress()) +#endif +/// + +/// A type that represents the terminal state of a task. Valid values are completed and canceled. +/// +/// +/**/ +typedef Concurrency::task_group_status task_status; + +template class task; +template <> class task; + +/// +/// Returns an indication of whether the task that is currently executing has received a request to cancel its +/// execution. Cancellation is requested on a task if the task was created with a cancellation token, and +/// the token source associated with that token is canceled. +/// +/// +/// true if the currently executing task has received a request for cancellation, false otherwise. +/// +/// +/// If you call this method in the body of a task and it returns true, you must respond with a call to +/// cancel_current_task to acknowledge the cancellation request, +/// after performing any cleanup you need. This will abort the execution of the task and cause it to enter into +/// the canceled state. If you do not respond and continue execution, or return instead of calling +/// cancel_current_task, the task will enter the completed state when it is done. +/// state. +/// A task is not cancellable if it was created without a cancellation token. +/// +/// +/// +/// +/// +/**/ +#if _MSC_VER >= 1800 +inline bool __cdecl is_task_cancellation_requested() +{ + return ::Concurrency::details::_TaskCollection_t::_Is_cancellation_requested(); +} +#else +inline bool __cdecl is_task_cancellation_requested() +{ + // ConcRT scheduler under the hood is using TaskCollection, which is same as task_group + return ::Concurrency::is_current_task_group_canceling(); +} +#endif + +/// +/// Cancels the currently executing task. This function can be called from within the body of a task to abort the +/// task's execution and cause it to enter the canceled state. While it may be used in response to +/// the is_task_cancellation_requested function, you may +/// also use it by itself, to initiate cancellation of the task that is currently executing. +/// It is not a supported scenario to call this function if you are not within the body of a task. +/// Doing so will result in undefined behavior such as a crash or a hang in your application. +/// +/// +/// +/**/ +//#if _MSC_VER >= 1800 +inline __declspec(noreturn) void __cdecl cancel_current_task() +{ + throw Concurrency::task_canceled(); +} +//#else +//_CRTIMP2 __declspec(noreturn) void __cdecl cancel_current_task(); +//#endif + +namespace details +{ +#if _MSC_VER >= 1800 + /// + /// Callstack container, which is used to capture and preserve callstacks in ppltasks. + /// Members of this class is examined by vc debugger, thus there will be no public access methods. + /// Please note that names of this class should be kept stable for debugger examining. + /// + class _TaskCreationCallstack + { + private: + // If _M_SingleFrame != nullptr, there will be only one frame of callstacks, which is stored in _M_SingleFrame; + // otherwise, _M_Frame will store all the callstack frames. + void* _M_SingleFrame; + std::vector _M_frames; + public: + _TaskCreationCallstack() + { + _M_SingleFrame = nullptr; + } + + // Store one frame of callstack. This function works for both Debug / Release CRT. + static _TaskCreationCallstack _CaptureSingleFrameCallstack(void *_SingleFrame) + { + _TaskCreationCallstack _csc; + _csc._M_SingleFrame = _SingleFrame; + return _csc; + } + + // Capture _CaptureFrames number of callstack frames. This function only work properly for Desktop or Debug CRT. + __declspec(noinline) + static _TaskCreationCallstack _CaptureMultiFramesCallstack(size_t _CaptureFrames) + { + _TaskCreationCallstack _csc; + _csc._M_frames.resize(_CaptureFrames); + // skip 2 frames to make sure callstack starts from user code + _csc._M_frames.resize(::Concurrency::details::platform::CaptureCallstack(&_csc._M_frames[0], 2, _CaptureFrames)); + return _csc; + } + }; +#endif + typedef UINT32 _Unit_type; + + struct _TypeSelectorNoAsync {}; + struct _TypeSelectorAsyncOperationOrTask {}; + struct _TypeSelectorAsyncOperation : public _TypeSelectorAsyncOperationOrTask { }; + struct _TypeSelectorAsyncTask : public _TypeSelectorAsyncOperationOrTask { }; + struct _TypeSelectorAsyncAction {}; + struct _TypeSelectorAsyncActionWithProgress {}; + struct _TypeSelectorAsyncOperationWithProgress {}; + + template + struct _NormalizeVoidToUnitType + { + typedef _Ty _Type; + }; + + template<> + struct _NormalizeVoidToUnitType + { + typedef _Unit_type _Type; + }; + + template + struct _IsUnwrappedAsyncSelector + { + static const bool _Value = true; + }; + + template<> + struct _IsUnwrappedAsyncSelector<_TypeSelectorNoAsync> + { + static const bool _Value = false; + }; + + template + struct _UnwrapTaskType + { + typedef _Ty _Type; + }; + + template + struct _UnwrapTaskType> + { + typedef _Ty _Type; + }; + + template + _TypeSelectorAsyncTask _AsyncOperationKindSelector(task<_T>); + + _TypeSelectorNoAsync _AsyncOperationKindSelector(...); + + template + struct _Unhat + { + typedef _Type _Value; + }; + + template + struct _Unhat<_Type*> + { + typedef _Type _Value; + }; + + //struct _NonUserType { public: int _Dummy; }; + + template + struct _ValueTypeOrRefType + { + typedef _Unit_type _Value; + }; + + template + struct _ValueTypeOrRefType<_Type, true> + { + typedef _Type _Value; + }; + + template + _Ty _UnwrapAsyncActionWithProgressSelector(ABI::Windows::Foundation::IAsyncActionWithProgress_impl<_Ty>*); + + template + _Ty _UnwrapAsyncActionWithProgressSelector(...); + + template + _Progress _UnwrapAsyncOperationWithProgressProgressSelector(ABI::Windows::Foundation::IAsyncOperationWithProgress_impl<_Ty, _Progress>*); + + template + _Progress _UnwrapAsyncOperationWithProgressProgressSelector(...); + + template + _T2 _ProgressTypeSelector(ABI::Windows::Foundation::IAsyncOperationWithProgress<_T1, _T2>*); + + template + _T1 _ProgressTypeSelector(ABI::Windows::Foundation::IAsyncActionWithProgress<_T1>*); + + template + struct _GetProgressType + { + typedef decltype(_ProgressTypeSelector(stdx::declval<_Type>())) _Value; + }; + + template + _TypeSelectorAsyncOperation _AsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncOperation<_T>*); + + _TypeSelectorAsyncAction _AsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncAction*); + + template + _TypeSelectorAsyncOperationWithProgress _AsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncOperationWithProgress<_T1, _T2>*); + + template + _TypeSelectorAsyncActionWithProgress _AsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncActionWithProgress<_T>*); + + template + struct _IsIAsyncInfo + { + static const bool _Value = std::is_base_of::_Value>::value || + std::is_same<_TypeSelectorAsyncAction, decltype(details::_AsyncOperationKindSelector(stdx::declval<_Type>()))>::value || + std::is_same<_TypeSelectorAsyncOperation, decltype(details::_AsyncOperationKindSelector(stdx::declval<_Type>()))>::value || + std::is_same<_TypeSelectorAsyncOperationWithProgress, decltype(details::_AsyncOperationKindSelector(stdx::declval<_Type>()))>::value || + std::is_same<_TypeSelectorAsyncActionWithProgress, decltype(details::_AsyncOperationKindSelector(stdx::declval<_Type>()))>::value; + }; + + template <> + struct _IsIAsyncInfo + { + static const bool _Value = false; + }; + + template + _Ty _UnwrapAsyncOperationSelector(ABI::Windows::Foundation::IAsyncOperation_impl<_Ty>*); + + template + _Ty _UnwrapAsyncOperationSelector(...); + + template + _Ty _UnwrapAsyncOperationWithProgressSelector(ABI::Windows::Foundation::IAsyncOperationWithProgress_impl<_Ty, _Progress>*); + + template + _Ty _UnwrapAsyncOperationWithProgressSelector(...); + + // Unwrap functions for asyncOperations + template + auto _GetUnwrappedType(ABI::Windows::Foundation::IAsyncOperation<_Ty>*) -> typename ABI::Windows::Foundation::Internal::GetAbiType*>()))>::type; + + void _GetUnwrappedType(ABI::Windows::Foundation::IAsyncAction*); + + template + auto _GetUnwrappedType(ABI::Windows::Foundation::IAsyncOperationWithProgress<_Ty, _Progress>*) -> typename ABI::Windows::Foundation::Internal::GetAbiType*>()))>::type; + + template + void _GetUnwrappedType(ABI::Windows::Foundation::IAsyncActionWithProgress<_Progress>*); + + template + _T _ReturnAsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncOperation<_T>*); + + void _ReturnAsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncAction*); + + template + _T1 _ReturnAsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncOperationWithProgress<_T1, _T2>*); + + template + void _ReturnAsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncActionWithProgress<_T>*); + + class _ProgressReporterCtorArgType{}; + + template ::_Value> + struct _TaskTypeTraits + { + typedef typename details::_UnwrapTaskType<_Type>::_Type _TaskRetType; + typedef _TaskRetType _TaskRetType_abi; + typedef decltype(_AsyncOperationKindSelector(stdx::declval<_Type>())) _AsyncKind; + typedef typename details::_NormalizeVoidToUnitType<_TaskRetType>::_Type _NormalizedTaskRetType; + + static const bool _IsAsyncTask = _IsAsync; + static const bool _IsUnwrappedTaskOrAsync = details::_IsUnwrappedAsyncSelector<_AsyncKind>::_Value; + }; + + template + struct _TaskTypeTraits<_Type, true> + { + typedef decltype(_ReturnAsyncOperationKindSelector(stdx::declval<_Type>())) _TaskRetType; + typedef decltype(_GetUnwrappedType(stdx::declval<_Type>())) _TaskRetType_abi; + typedef _TaskRetType _NormalizedTaskRetType; + typedef decltype(_AsyncOperationKindSelector(stdx::declval<_Type>())) _AsyncKind; + + static const bool _IsAsyncTask = true; + static const bool _IsUnwrappedTaskOrAsync = details::_IsUnwrappedAsyncSelector<_AsyncKind>::_Value; + }; + + template auto _IsCallable(_Function _Func, int, int, int) -> decltype(_Func(stdx::declval*>()), std::true_type()) { (void)_Func; return std::true_type(); } + template auto _IsCallable(_Function _Func, int, int, ...) -> decltype(_Func(stdx::declval<_ReturnType*>()), std::true_type()) { (void)_Func; return std::true_type(); } + template auto _IsCallable(_Function _Func, int, ...) -> decltype(_Func(), std::true_type()) { (void)_Func; return std::true_type(); } + template std::false_type _IsCallable(_Function, ...) { return std::false_type(); } + + template <> + struct _TaskTypeTraits + { + typedef void _TaskRetType; + typedef void _TaskRetType_abi; + typedef _TypeSelectorNoAsync _AsyncKind; + typedef _Unit_type _NormalizedTaskRetType; + + static const bool _IsAsyncTask = false; + static const bool _IsUnwrappedTaskOrAsync = false; + }; + + // *************************************************************************** + // Template type traits and helpers for async production APIs: + // + + struct _ZeroArgumentFunctor { }; + struct _OneArgumentFunctor { }; + struct _TwoArgumentFunctor { }; + struct _ThreeArgumentFunctor { }; + + // **************************************** + // CLASS TYPES: + + // mutable functions + // ******************** + // THREE ARGUMENTS: + + // non-void arg: + template + _Arg1 _Arg1ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3)); + + // non-void arg: + template + _Arg2 _Arg2ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3)); + + // non-void arg: + template + _Arg3 _Arg3ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3)); + + template + _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3)); + + template + _ThreeArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3)); + + // ******************** + // TWO ARGUMENTS: + + // non-void arg: + template + _Arg1 _Arg1ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2)); + + // non-void arg: + template + _Arg2 _Arg2ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2)); + + // non-void arg: + template + void _Arg3ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2)); + + template + _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2)); + + template + _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)(_Arg1, _Arg2)); + + // ******************** + // ONE ARGUMENT: + + // non-void arg: + template + _Arg1 _Arg1ClassHelperThunk(_ReturnType(_Class::*)(_Arg1)); + + // non-void arg: + template + void _Arg2ClassHelperThunk(_ReturnType(_Class::*)(_Arg1)); + + // non-void arg: + template + void _Arg3ClassHelperThunk(_ReturnType(_Class::*)(_Arg1)); + + template + _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)(_Arg1)); + + template + _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)(_Arg1)); + + // ******************** + // ZERO ARGUMENT: + + // void arg: + template + void _Arg1ClassHelperThunk(_ReturnType(_Class::*)()); + + // void arg: + template + void _Arg2ClassHelperThunk(_ReturnType(_Class::*)()); + + // void arg: + template + void _Arg3ClassHelperThunk(_ReturnType(_Class::*)()); + + // void arg: + template + _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)()); + + template + _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)()); + + // ******************** + // THREE ARGUMENTS: + + // non-void arg: + template + _Arg1 _Arg1ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3) const); + + // non-void arg: + template + _Arg2 _Arg2ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3) const); + + // non-void arg: + template + _Arg3 _Arg3ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3) const); + + template + _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3) const); + + template + _ThreeArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3) const); + + // ******************** + // TWO ARGUMENTS: + + // non-void arg: + template + _Arg1 _Arg1ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2) const); + + // non-void arg: + template + _Arg2 _Arg2ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2) const); + + // non-void arg: + template + void _Arg3ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2) const); + + template + _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2) const); + + template + _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)(_Arg1, _Arg2) const); + + // ******************** + // ONE ARGUMENT: + + // non-void arg: + template + _Arg1 _Arg1ClassHelperThunk(_ReturnType(_Class::*)(_Arg1) const); + + // non-void arg: + template + void _Arg2ClassHelperThunk(_ReturnType(_Class::*)(_Arg1) const); + + // non-void arg: + template + void _Arg3ClassHelperThunk(_ReturnType(_Class::*)(_Arg1) const); + + template + _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)(_Arg1) const); + + template + _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)(_Arg1) const); + + // ******************** + // ZERO ARGUMENT: + + // void arg: + template + void _Arg1ClassHelperThunk(_ReturnType(_Class::*)() const); + + // void arg: + template + void _Arg2ClassHelperThunk(_ReturnType(_Class::*)() const); + + // void arg: + template + void _Arg3ClassHelperThunk(_ReturnType(_Class::*)() const); + + // void arg: + template + _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)() const); + + template + _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)() const); + + // **************************************** + // POINTER TYPES: + + // ******************** + // THREE ARGUMENTS: + + template + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2, _Arg3)); + + template + _Arg2 _Arg2PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2, _Arg3)); + + template + _Arg3 _Arg3PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2, _Arg3)); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2, _Arg3)); + + template + _ThreeArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)(_Arg1, _Arg2, _Arg3)); + + template + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2, _Arg3)); + + template + _Arg2 _Arg2PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2, _Arg3)); + + template + _Arg3 _Arg3PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2, _Arg3)); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2, _Arg3)); + + template + _ThreeArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)(_Arg1, _Arg2, _Arg3)); + + template + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2, _Arg3)); + + template + _Arg2 _Arg2PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2, _Arg3)); + + template + _Arg3 _Arg3PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2, _Arg3)); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2, _Arg3)); + + template + _ThreeArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)(_Arg1, _Arg2, _Arg3)); + + // ******************** + // TWO ARGUMENTS: + + template + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); + + template + _Arg2 _Arg2PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); + + template + void _Arg3PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2)); + + template + _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)(_Arg1, _Arg2)); + + template + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); + + template + _Arg2 _Arg2PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); + + template + void _Arg3PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2)); + + template + _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)(_Arg1, _Arg2)); + + template + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); + + template + _Arg2 _Arg2PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); + + template + void _Arg3PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2)); + + template + _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)(_Arg1, _Arg2)); + + // ******************** + // ONE ARGUMENT: + + template + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); + + template + void _Arg2PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); + + template + void _Arg3PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)(_Arg1)); + + template + _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)(_Arg1)); + + template + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); + + template + void _Arg2PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); + + template + void _Arg3PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)(_Arg1)); + + template + _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)(_Arg1)); + + template + _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); + + template + void _Arg2PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); + + template + void _Arg3PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)(_Arg1)); + + template + _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)(_Arg1)); + + // ******************** + // ZERO ARGUMENT: + + template + void _Arg1PFNHelperThunk(_ReturnType(__cdecl *)()); + + template + void _Arg2PFNHelperThunk(_ReturnType(__cdecl *)()); + + template + void _Arg3PFNHelperThunk(_ReturnType(__cdecl *)()); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)()); + + template + _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)()); + + template + void _Arg1PFNHelperThunk(_ReturnType(__stdcall *)()); + + template + void _Arg2PFNHelperThunk(_ReturnType(__stdcall *)()); + + template + void _Arg3PFNHelperThunk(_ReturnType(__stdcall *)()); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)()); + + template + _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)()); + + template + void _Arg1PFNHelperThunk(_ReturnType(__fastcall *)()); + + template + void _Arg2PFNHelperThunk(_ReturnType(__fastcall *)()); + + template + void _Arg3PFNHelperThunk(_ReturnType(__fastcall *)()); + + template + _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)()); + + template + _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)()); + + template + struct _FunctorArguments + { + static const size_t _Count = 0; + }; + + template<> + struct _FunctorArguments<_OneArgumentFunctor> + { + static const size_t _Count = 1; + }; + + template<> + struct _FunctorArguments<_TwoArgumentFunctor> + { + static const size_t _Count = 2; + }; + + template<> + struct _FunctorArguments<_ThreeArgumentFunctor> + { + static const size_t _Count = 3; + }; + + template + struct _FunctorTypeTraits + { + typedef decltype(_ArgumentCountHelper(&(_T::operator()))) _ArgumentCountType; + static const size_t _ArgumentCount = _FunctorArguments<_ArgumentCountType>::_Count; + + typedef decltype(_ReturnTypeClassHelperThunk(&(_T::operator()))) _ReturnType; + typedef decltype(_Arg1ClassHelperThunk(&(_T::operator()))) _Argument1Type; + typedef decltype(_Arg2ClassHelperThunk(&(_T::operator()))) _Argument2Type; + typedef decltype(_Arg3ClassHelperThunk(&(_T::operator()))) _Argument3Type; + }; + + template + struct _FunctorTypeTraits<_T *> + { + typedef decltype(_ArgumentCountHelper(stdx::declval<_T*>())) _ArgumentCountType; + static const size_t _ArgumentCount = _FunctorArguments<_ArgumentCountType>::_Count; + + typedef decltype(_ReturnTypePFNHelperThunk(stdx::declval<_T*>())) _ReturnType; + typedef decltype(_Arg1PFNHelperThunk(stdx::declval<_T*>())) _Argument1Type; + typedef decltype(_Arg2PFNHelperThunk(stdx::declval<_T*>())) _Argument2Type; + typedef decltype(_Arg3PFNHelperThunk(stdx::declval<_T*>())) _Argument3Type; + }; + + task _To_task(); + + template auto _IsVoidConversionHelper(_Function _Func, int) -> typename decltype(_Func(_To_task()), std::true_type()); + template std::false_type _IsVoidConversionHelper(_Function _Func, ...); + + template std::true_type _VoidIsTaskHelper(task _Arg, int); + template std::false_type _VoidIsTaskHelper(T _Arg, ...); + + template(), 0)), std::true_type>::value, const size_t _Count = _FunctorTypeTraits<_Function>::_ArgumentCount> + struct _FunctionTypeTraits + { + typedef typename _Unhat::_Argument2Type>::_Value _FuncRetType; + static_assert(std::is_same::_Argument1Type, _ExpectedParameterType>::value || + std::is_same::_Argument1Type, task<_ExpectedParameterType>>::value, "incorrect parameter type for the callable object in 'then'; consider _ExpectedParameterType or task<_ExpectedParameterType> (see below)"); + + typedef decltype(_VoidIsTaskHelper(stdx::declval<_FunctorTypeTraits<_Function>::_Argument1Type>(), 0)) _Takes_task; + }; + + //if there is a continuation parameter, then must use void/no return value + template + struct _FunctionTypeTraits<_Function, _ExpectedParameterType, _IsVoidConversion, 1> + { + typedef void _FuncRetType; + static_assert(std::is_same::_Argument1Type, _ExpectedParameterType>::value || + std::is_same::_Argument1Type, task<_ExpectedParameterType>>::value, "incorrect parameter type for the callable object in 'then'; consider _ExpectedParameterType or task<_ExpectedParameterType> (see below)"); + + typedef decltype(_VoidIsTaskHelper(stdx::declval<_FunctorTypeTraits<_Function>::_Argument1Type>(), 0)) _Takes_task; + }; + + template + struct _FunctionTypeTraits<_Function, void, true, 1> + { + typedef void _FuncRetType; + static_assert(std::is_same::_Argument1Type, decltype(_To_task())>::value, "incorrect parameter type for the callable object in 'then'; consider _ExpectedParameterType or task<_ExpectedParameterType> (see below)"); + + typedef decltype(_VoidIsTaskHelper(stdx::declval<_FunctorTypeTraits<_Function>::_Argument1Type>(), 0)) _Takes_task; + }; + + template + struct _FunctionTypeTraits<_Function, void, false, 1> + { + typedef typename _Unhat::_Argument1Type>::_Value _FuncRetType; + + typedef std::false_type _Takes_task; + }; + + template + struct _FunctionTypeTraits<_Function, _ExpectedParameterType, _IsVoidConversion, 0> + { + typedef void _FuncRetType; + + typedef std::false_type _Takes_task; + }; + + template + struct _ContinuationTypeTraits + { + typedef typename task::_FuncRetType>::_TaskRetType_abi> _TaskOfType; + }; + + // _InitFunctorTypeTraits is used to decide whether a task constructed with a lambda should be unwrapped. Depending on how the variable is + // declared, the constructor may or may not perform unwrapping. For eg. + // + // This declaration SHOULD NOT cause unwrapping + // task> t1([]() -> task { + // task t2([]() {}); + // return t2; + // }); + // + // This declaration SHOULD cause unwrapping + // task> t1([]() -> task { + // task t2([]() {}); + // return t2; + // }); + // If the type of the task is the same as the return type of the function, no unwrapping should take place. Else normal rules apply. + template + struct _InitFunctorTypeTraits + { + typedef typename _TaskTypeTraits<_FuncRetType>::_AsyncKind _AsyncKind; + static const bool _IsAsyncTask = _TaskTypeTraits<_FuncRetType>::_IsAsyncTask; + static const bool _IsUnwrappedTaskOrAsync = _TaskTypeTraits<_FuncRetType>::_IsUnwrappedTaskOrAsync; + }; + + template + struct _InitFunctorTypeTraits + { + typedef _TypeSelectorNoAsync _AsyncKind; + static const bool _IsAsyncTask = false; + static const bool _IsUnwrappedTaskOrAsync = false; + }; + /// + /// Helper object used for LWT invocation. + /// + struct _TaskProcThunk + { + _TaskProcThunk(const std::function & _Callback) : + _M_func(_Callback) + { + } + + static void __cdecl _Bridge(void *_PData) + { + _TaskProcThunk *_PThunk = reinterpret_cast<_TaskProcThunk *>(_PData); +#if _MSC_VER >= 1800 + _Holder _ThunkHolder(_PThunk); +#endif + _PThunk->_M_func(); +#if _MSC_VER < 1800 + delete _PThunk; +#endif + } + private: +#if _MSC_VER >= 1800 + // RAII holder + struct _Holder + { + _Holder(_TaskProcThunk * _PThunk) : _M_pThunk(_PThunk) + { + } + + ~_Holder() + { + delete _M_pThunk; + } + + _TaskProcThunk * _M_pThunk; + + private: + _Holder& operator=(const _Holder&); + }; +#endif + std::function _M_func; + _TaskProcThunk& operator=(const _TaskProcThunk&); + }; + + /// + /// Schedule a functor with automatic inlining. Note that this is "fire and forget" scheduling, which cannot be + /// waited on or canceled after scheduling. + /// This schedule method will perform automatic inlining base on . + /// + /// + /// The user functor need to be scheduled. + /// + /// + /// The inlining scheduling policy for current functor. + /// +#if _MSC_VER >= 1800 + typedef Concurrency::details::_TaskInliningMode_t _TaskInliningMode; +#else + typedef Concurrency::details::_TaskInliningMode _TaskInliningMode; +#endif + static void _ScheduleFuncWithAutoInline(const std::function & _Func, _TaskInliningMode _InliningMode) + { +#if _MSC_VER >= 1800 + Concurrency::details::_TaskCollection_t::_RunTask(&_TaskProcThunk::_Bridge, new _TaskProcThunk(_Func), _InliningMode); +#else + Concurrency::details::_StackGuard _Guard; + if (_Guard._ShouldInline(_InliningMode)) + { + _Func(); + } + else + { + Concurrency::details::_CurrentScheduler::_ScheduleTask(reinterpret_cast(&_TaskProcThunk::_Bridge), new _TaskProcThunk(_Func)); + } +#endif + } + class _ContextCallback + { + typedef std::function _CallbackFunction; + + public: + + static _ContextCallback _CaptureCurrent() + { + _ContextCallback _Context; + _Context._Capture(); + return _Context; + } + + ~_ContextCallback() + { + _Reset(); + } + + _ContextCallback(bool _DeferCapture = false) + { + if (_DeferCapture) + { + _M_context._M_captureMethod = _S_captureDeferred; + } + else + { + _M_context._M_pContextCallback = nullptr; + } + } + + // Resolves a context that was created as _S_captureDeferred based on the environment (ancestor, current context). + void _Resolve(bool _CaptureCurrent) + { + if (_M_context._M_captureMethod == _S_captureDeferred) + { + _M_context._M_pContextCallback = nullptr; + + if (_CaptureCurrent) + { + if (_IsCurrentOriginSTA()) + { + _Capture(); + } +#if _UITHREADCTXT_SUPPORT + else + { + // This method will fail if not called from the UI thread. + HRESULT _Hr = CaptureUiThreadContext(&_M_context._M_pContextCallback); + if (FAILED(_Hr)) + { + _M_context._M_pContextCallback = nullptr; + } + } +#endif // _UITHREADCTXT_SUPPORT + } + } + } + + void _Capture() + { + HRESULT _Hr = CoGetObjectContext(IID_IContextCallback, reinterpret_cast(&_M_context._M_pContextCallback)); + if (FAILED(_Hr)) + { + _M_context._M_pContextCallback = nullptr; + } + } + + _ContextCallback(const _ContextCallback& _Src) + { + _Assign(_Src._M_context._M_pContextCallback); + } + + _ContextCallback(_ContextCallback&& _Src) + { + _M_context._M_pContextCallback = _Src._M_context._M_pContextCallback; + _Src._M_context._M_pContextCallback = nullptr; + } + + _ContextCallback& operator=(const _ContextCallback& _Src) + { + if (this != &_Src) + { + _Reset(); + _Assign(_Src._M_context._M_pContextCallback); + } + return *this; + } + + _ContextCallback& operator=(_ContextCallback&& _Src) + { + if (this != &_Src) + { + _M_context._M_pContextCallback = _Src._M_context._M_pContextCallback; + _Src._M_context._M_pContextCallback = nullptr; + } + return *this; + } + + bool _HasCapturedContext() const + { + _CONCRT_ASSERT(_M_context._M_captureMethod != _S_captureDeferred); + return (_M_context._M_pContextCallback != nullptr); + } + + HRESULT _CallInContext(_CallbackFunction _Func) const + { + if (!_HasCapturedContext()) + { + _Func(); + } + else + { + ComCallData callData; + ZeroMemory(&callData, sizeof(callData)); + callData.pUserDefined = reinterpret_cast(&_Func); + + HRESULT _Hr = _M_context._M_pContextCallback->ContextCallback(&_Bridge, &callData, IID_ICallbackWithNoReentrancyToApplicationSTA, 5, nullptr); + if (FAILED(_Hr)) + { + return _Hr; + } + } + return S_OK; + } + + bool operator==(const _ContextCallback& _Rhs) const + { + return (_M_context._M_pContextCallback == _Rhs._M_context._M_pContextCallback); + } + + bool operator!=(const _ContextCallback& _Rhs) const + { + return !(operator==(_Rhs)); + } + + private: + + void _Reset() + { + if (_M_context._M_captureMethod != _S_captureDeferred && _M_context._M_pContextCallback != nullptr) + { + _M_context._M_pContextCallback->Release(); + } + } + + void _Assign(IContextCallback *_PContextCallback) + { + _M_context._M_pContextCallback = _PContextCallback; + if (_M_context._M_captureMethod != _S_captureDeferred && _M_context._M_pContextCallback != nullptr) + { + _M_context._M_pContextCallback->AddRef(); + } + } + + static HRESULT __stdcall _Bridge(ComCallData *_PParam) + { + _CallbackFunction *pFunc = reinterpret_cast<_CallbackFunction *>(_PParam->pUserDefined); + return (*pFunc)(); + } + + // Returns the origin information for the caller (runtime / Windows Runtime apartment as far as task continuations need know) + static bool _IsCurrentOriginSTA() + { + APTTYPE _AptType; + APTTYPEQUALIFIER _AptTypeQualifier; + + HRESULT hr = CoGetApartmentType(&_AptType, &_AptTypeQualifier); + if (SUCCEEDED(hr)) + { + // We determine the origin of a task continuation by looking at where .then is called, so we can tell whether + // to need to marshal the continuation back to the originating apartment. If an STA thread is in executing in + // a neutral aparment when it schedules a continuation, we will not marshal continuations back to the STA, + // since variables used within a neutral apartment are expected to be apartment neutral. + switch (_AptType) + { + case APTTYPE_MAINSTA: + case APTTYPE_STA: + return true; + default: + break; + } + } + return false; + } + + union + { + IContextCallback *_M_pContextCallback; + size_t _M_captureMethod; + } _M_context; + + static const size_t _S_captureDeferred = 1; + }; + +#if _MSC_VER >= 1800 + template + struct _ResultHolder + { + void Set(const _Type& _type) + { + _Result = _type; + } + + _Type Get() + { + return _Result; + } + + _Type _Result; + }; + + template + struct _ResultHolder<_Type*> + { + void Set(_Type* const & _type) + { + _M_Result = _type; + } + + _Type* Get() + { + return _M_Result.Get(); + } + private: + // ::Platform::Agile handle specialization of all hats + // including ::Platform::String and ::Platform::Array + Agile<_Type*> _M_Result; + }; + + // + // The below are for composability with tasks auto-created from when_any / when_all / && / || constructs. + // + template + struct _ResultHolder> + { + void Set(const std::vector<_Type*>& _type) + { + _Result.reserve(_type.size()); + + for (auto _PTask = _type.begin(); _PTask != _type.end(); ++_PTask) + { + _Result.emplace_back(*_PTask); + } + } + + std::vector<_Type*> Get() + { + // Return vectory with the objects that are marshaled in the proper appartment + std::vector<_Type*> _Return; + _Return.reserve(_Result.size()); + + for (auto _PTask = _Result.begin(); _PTask != _Result.end(); ++_PTask) + { + _Return.push_back(_PTask->Get()); // Agile will marshal the object to appropriate appartment if neccessary + } + + return _Return; + } + + std::vector< Agile<_Type*> > _Result; + }; + + template + struct _ResultHolder > + { + void Set(const std::pair<_Type*, size_t>& _type) + { + _M_Result = _type; + } + + std::pair<_Type*, size_t> Get() + { + return std::make_pair(_M_Result.first, _M_Result.second); + } + private: + std::pair, size_t> _M_Result; + }; +#else + template + struct _ResultContext + { + static _ContextCallback _GetContext(bool /* _RuntimeAggregate */) + { + return _ContextCallback(); + } + + static _Type _GetValue(_Type _ObjInCtx, const _ContextCallback & /* _Ctx */, bool /* _RuntimeAggregate */) + { + return _ObjInCtx; + } + }; + + template::value> + struct _MarshalHelper + { + }; + template + struct _MarshalHelper<_Type, N, true> + { + static _Type* _Perform(_Type(&_ObjInCtx)[N], const _ContextCallback& _Ctx) + { + static_assert(__is_valid_winrt_type(_Type*), "must be a WinRT array compatible type"); + if (_ObjInCtx == nullptr) + { + return nullptr; + } + + HRESULT _Hr; + IStream * _PStream; + _Ctx._CallInContext([&]() -> HRESULT { + // It isn't safe to simply reinterpret_cast a hat type to IUnknown* because some types do not have a real vtable ptr. + // Instead, we could to create a property value to make it "grow" the vtable ptr but instead primitives are not marshalled. + + IUnknown * _PUnk = winrt_array_type::create(_ObjInCtx, N); + _Hr = CoMarshalInterThreadInterfaceInStream(winrt_type<_Type>::getuuid(), _PUnk, &_PStream); + return S_OK; + }); + + // With an APPX manifest, this call should never fail. + _CONCRT_ASSERT(SUCCEEDED(_Hr)); + + _Type* _Proxy; + // + // Cannot use IID_PPV_ARGS with ^ types. + // + _Hr = CoGetInterfaceAndReleaseStream(_PStream, winrt_type<_Type>::getuuid(), reinterpret_cast(&_Proxy)); + if (FAILED(_Hr)) + { + throw std::make_exception_ptr(_Hr); + } + return _Proxy; + } + }; + template + struct _MarshalHelper<_Type, 0, false> + { + static _Type* _Perform(_Type* _ObjInCtx, const _ContextCallback& _Ctx) + { + static_assert(std::is_base_of::value || __is_valid_winrt_type(_Type), "must be a COM or WinRT type"); + if (_ObjInCtx == nullptr) + { + return nullptr; + } + + HRESULT _Hr; + IStream * _PStream; + _Ctx._CallInContext([&]() -> HRESULT { + // It isn't safe to simply reinterpret_cast a hat type to IUnknown* because some types do not have a real vtable ptr. + // Instead, we could to create a property value to make it "grow" the vtable ptr but instead primitives are not marshalled. + + IUnknown * _PUnk = winrt_type<_Type>::create(_ObjInCtx); + _Hr = CoMarshalInterThreadInterfaceInStream(winrt_type<_Type>::getuuid(), _PUnk, &_PStream); + return S_OK; + }); + + // With an APPX manifest, this call should never fail. + _CONCRT_ASSERT(SUCCEEDED(_Hr)); + + _Type* _Proxy; + // + // Cannot use IID_PPV_ARGS with ^ types. + // + _Hr = CoGetInterfaceAndReleaseStream(_PStream, winrt_type<_Type>::getuuid(), reinterpret_cast(&_Proxy)); + if (FAILED(_Hr)) + { + throw std::make_exception_ptr(_Hr); + } + return _Proxy; + } + }; + + // Arrays must be converted to IPropertyValue objects. + + template<> + struct _MarshalHelper + { + static HSTRING _Perform(HSTRING _ObjInCtx, const _ContextCallback& _Ctx) + { + return _ObjInCtx; + } + }; + + template + _Type* _Marshal(_Type* _ObjInCtx, const _ContextCallback& _Ctx) + { + return _MarshalHelper<_Type>::_Perform(_ObjInCtx, _Ctx); + } + + template + struct _InContext + { + static _Type _Get(_Type _ObjInCtx, const _ContextCallback& _Ctx) + { + return _ObjInCtx; + } + }; + + template + struct _InContext<_Type*> + { + static _Type* _Get(_Type* _ObjInCtx, const _ContextCallback& _Ctx) + { + _ContextCallback _CurrentContext = _ContextCallback::_CaptureCurrent(); + if (!_Ctx._HasCapturedContext() || _Ctx == _CurrentContext) + { + return _ObjInCtx; + } + + // + // The object is from another apartment. If it's marshalable, do so. + // + return _Marshal<_Type>(_ObjInCtx, _Ctx); + } + }; + + template + struct _ResultContext<_Type*> + { + static _Type* _GetValue(_Type* _ObjInCtx, const _ContextCallback& _Ctx, bool /* _RuntimeAggregate */) + { + return _InContext<_Type*>::_Get(_ObjInCtx, _Ctx); + } + + static _ContextCallback _GetContext(bool /* _RuntimeAggregate */) + { + return _ContextCallback::_CaptureCurrent(); + } + }; + + // + // The below are for composability with tasks auto-created from when_any / when_all / && / || constructs. + // + template + struct _ResultContext> + { + static std::vector<_Type*> _GetValue(std::vector<_Type*> _ObjInCtx, const _ContextCallback& _Ctx, bool _RuntimeAggregate) + { + if (!_RuntimeAggregate) + { + return _ObjInCtx; + } + + _ContextCallback _CurrentContext = _ContextCallback::_CaptureCurrent(); + if (!_Ctx._HasCapturedContext() || _Ctx == _CurrentContext) + { + return _ObjInCtx; + } + + for (auto _It = _ObjInCtx.begin(); _It != _ObjInCtx.end(); ++_It) + { + *_It = _Marshal<_Type>(*_It, _Ctx); + } + + return _ObjInCtx; + } + + static _ContextCallback _GetContext(bool _RuntimeAggregate) + { + if (!_RuntimeAggregate) + { + return _ContextCallback(); + } + else + { + return _ContextCallback::_CaptureCurrent(); + } + } + }; + + template + struct _ResultContext> + { + static std::pair<_Type*, size_t> _GetValue(std::pair<_Type*, size_t> _ObjInCtx, const _ContextCallback& _Ctx, bool _RuntimeAggregate) + { + if (!_RuntimeAggregate) + { + return _ObjInCtx; + } + + _ContextCallback _CurrentContext = _ContextCallback::_CaptureCurrent(); + if (!_Ctx._HasCapturedContext() || _Ctx == _CurrentContext) + { + return _ObjInCtx; + } + + return std::pair<_Type*, size_t>(_Marshal<_Type>(_ObjInCtx.first, _Ctx), _ObjInCtx.second); + } + + static _ContextCallback _GetContext(bool _RuntimeAggregate) + { + if (!_RuntimeAggregate) + { + return _ContextCallback(); + } + else + { + return _ContextCallback::_CaptureCurrent(); + } + } + }; +#endif + // An exception thrown by the task body is captured in an exception holder and it is shared with all value based continuations rooted at the task. + // The exception is 'observed' if the user invokes get()/wait() on any of the tasks that are sharing this exception holder. If the exception + // is not observed by the time the internal object owned by the shared pointer destructs, the process will fail fast. + struct _ExceptionHolder + { +#if _MSC_VER >= 1800 + private: + void ReportUnhandledError() + { + if (_M_winRTException != nullptr) + { + throw _M_winRTException.Get(); + } + } + public: + explicit _ExceptionHolder(const std::exception_ptr& _E, const _TaskCreationCallstack &_stackTrace) : + _M_exceptionObserved(0), _M_stdException(_E), _M_stackTrace(_stackTrace) + { + } + + explicit _ExceptionHolder(IRestrictedErrorInfo*& _E, const _TaskCreationCallstack &_stackTrace) : + _M_exceptionObserved(0), _M_winRTException(_E), _M_stackTrace(_stackTrace) + { + } +#else + explicit _ExceptionHolder(const std::exception_ptr& _E, void* _SourceAddressHint) : + _M_exceptionObserved(0), _M_stdException(_E), _M_disassembleMe(_SourceAddressHint) + { + } + + explicit _ExceptionHolder(IRestrictedErrorInfo*& _E, void* _SourceAddressHint) : + _M_exceptionObserved(0), _M_disassembleMe(_SourceAddressHint), _M_winRTException(_E) + { + } +#endif + __declspec(noinline) + ~_ExceptionHolder() + { + if (_M_exceptionObserved == 0) + { +#if _MSC_VER >= 1800 + // If you are trapped here, it means an exception thrown in task chain didn't get handled. + // Please add task-based continuation to handle all exceptions coming from tasks. + // this->_M_stackTrace keeps the creation callstack of the task generates this exception. + _REPORT_PPLTASK_UNOBSERVED_EXCEPTION(); +#else + // Disassemble at this->_M_disassembleMe to get to the source location right after either the creation of the task (constructor + // or then method) that encountered this exception, or the set_exception call for a task_completion_event. + Concurrency::details::_ReportUnobservedException(); +#endif + } + } + + void _RethrowUserException() + { + if (_M_exceptionObserved == 0) + { +#if _MSC_VER >= 1800 + Concurrency::details::atomic_exchange(_M_exceptionObserved, 1l); +#else + _InterlockedExchange(&_M_exceptionObserved, 1); +#endif + } + + if (_M_winRTException != nullptr) + { + throw _M_winRTException.Get(); + } + std::rethrow_exception(_M_stdException); + } + + // A variable that remembers if this exception was every rethrown into user code (and hence handled by the user). Exceptions that + // are unobserved when the exception holder is destructed will terminate the process. +#if _MSC_VER >= 1800 + Concurrency::details::atomic_long _M_exceptionObserved; +#else + long volatile _M_exceptionObserved; +#endif + + // Either _M_stdException or _M_winRTException is populated based on the type of exception encountered. + std::exception_ptr _M_stdException; + Microsoft::WRL::ComPtr _M_winRTException; + + // Disassembling this value will point to a source instruction right after a call instruction. If the call is to create_task, + // a task constructor or the then method, the task created by that method is the one that encountered this exception. If the call + // is to task_completion_event::set_exception, the set_exception method was the source of the exception. + // DO NOT REMOVE THIS VARIABLE. It is extremely helpful for debugging. +#if _MSC_VER >= 1800 + _TaskCreationCallstack _M_stackTrace; +#else + void* _M_disassembleMe; +#endif + }; + +#ifndef RUNTIMECLASS_Concurrency_winrt_details__AsyncInfoImpl_DEFINED +#define RUNTIMECLASS_Concurrency_winrt_details__AsyncInfoImpl_DEFINED + extern const __declspec(selectany) WCHAR RuntimeClass_Concurrency_winrt_details__AsyncInfoImpl[] = L"Concurrency_winrt.details._AsyncInfoImpl"; +#endif + + /// + /// Base converter class for converting asynchronous interfaces to IAsyncOperation + /// + template + struct _AsyncInfoImpl abstract : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::RuntimeClassType::WinRt>, + Microsoft::WRL::Implements>> + { + InspectableClass(RuntimeClass_Concurrency_winrt_details__AsyncInfoImpl, BaseTrust) + public: + // The async action, action with progress or operation with progress that this stub forwards to. +#if _MSC_VER >= 1800 + Agile<_AsyncOperationType> _M_asyncInfo; +#else + Microsoft::WRL::ComPtr<_AsyncOperationType> _M_asyncInfo; + // The context in which this async info is valid - may be different from the context where the completion handler runs, + // and may require marshalling before it is used. + _ContextCallback _M_asyncInfoContext; +#endif + + Microsoft::WRL::ComPtr<_CompletionHandlerType> _M_CompletedHandler; + + _AsyncInfoImpl(_AsyncOperationType* _AsyncInfo) : _M_asyncInfo(_AsyncInfo) +#if _MSC_VER < 1800 + , _M_asyncInfoContext(_ContextCallback::_CaptureCurrent()) +#endif + {} + + public: + virtual HRESULT OnStart() { return S_OK; } + virtual void OnCancel() { + Microsoft::WRL::ComPtr pAsyncInfo; + HRESULT hr; +#if _MSC_VER >= 1800 + if (SUCCEEDED(hr = _M_asyncInfo.Get()->QueryInterface(pAsyncInfo.GetAddressOf()))) +#else + if (SUCCEEDED(hr = _M_asyncInfo.As(&pAsyncInfo))) +#endif + pAsyncInfo->Cancel(); + else + throw std::make_exception_ptr(hr); + } + virtual void OnClose() { + Microsoft::WRL::ComPtr pAsyncInfo; + HRESULT hr; +#if _MSC_VER >= 1800 + if (SUCCEEDED(hr = _M_asyncInfo.Get()->QueryInterface(pAsyncInfo.GetAddressOf()))) +#else + if (SUCCEEDED(hr = _M_asyncInfo.As(&pAsyncInfo))) +#endif + pAsyncInfo->Close(); + else + throw std::make_exception_ptr(hr); + } + + virtual STDMETHODIMP get_ErrorCode(HRESULT* errorCode) + { + Microsoft::WRL::ComPtr pAsyncInfo; + HRESULT hr; +#if _MSC_VER >= 1800 + if (SUCCEEDED(hr = _M_asyncInfo.Get()->QueryInterface(pAsyncInfo.GetAddressOf()))) +#else + if (SUCCEEDED(hr = _M_asyncInfo.As(&pAsyncInfo))) +#endif + return pAsyncInfo->get_ErrorCode(errorCode); + return hr; + } + + virtual STDMETHODIMP get_Id(UINT* id) + { + Microsoft::WRL::ComPtr pAsyncInfo; + HRESULT hr; +#if _MSC_VER >= 1800 + if (SUCCEEDED(hr = _M_asyncInfo.Get()->QueryInterface(pAsyncInfo.GetAddressOf()))) +#else + if (SUCCEEDED(hr = _M_asyncInfo.As(&pAsyncInfo))) +#endif + return pAsyncInfo->get_Id(id); + return hr; + } + + virtual STDMETHODIMP get_Status(ABI::Windows::Foundation::AsyncStatus *status) + { + Microsoft::WRL::ComPtr pAsyncInfo; + HRESULT hr; +#if _MSC_VER >= 1800 + if (SUCCEEDED(hr = _M_asyncInfo.Get()->QueryInterface(pAsyncInfo.GetAddressOf()))) +#else + if (SUCCEEDED(hr = _M_asyncInfo.As(&pAsyncInfo))) +#endif + return pAsyncInfo->get_Status(status); + return hr; + } + + virtual STDMETHODIMP GetResults(_Result_abi*) { throw std::runtime_error("derived class must implement"); } + + virtual STDMETHODIMP get_Completed(_CompletionHandlerType** handler) + { + if (!handler) return E_POINTER; + _M_CompletedHandler.CopyTo(handler); + return S_OK; + } + + virtual STDMETHODIMP put_Completed(_CompletionHandlerType* value) + { + _M_CompletedHandler = value; + Microsoft::WRL::ComPtr<_CompletionHandlerType> handler = Microsoft::WRL::Callback<_CompletionHandlerType>([&](_AsyncOperationType*, ABI::Windows::Foundation::AsyncStatus status) -> HRESULT { +#if _MSC_VER < 1800 + // Update the saved _M_asyncInfo with a proxy valid in the current context if required. Some Windows APIs return an IAsyncInfo + // that is only valid for the thread that called the API to retrieve. Since this completion handler can run on any thread, we + // need to ensure that the async info is valid in the current apartment. _M_asyncInfo will be accessed via calls to 'this' inside + // _AsyncInit. + _M_asyncInfo = _ResultContext<_AsyncOperationType*>::_GetValue(_M_asyncInfo.Get(), _M_asyncInfoContext, false); +#endif + return _M_CompletedHandler->Invoke(_M_asyncInfo.Get(), status); + }); +#if _MSC_VER >= 1800 + return _M_asyncInfo.Get()->put_Completed(handler.Get()); +#else + return _M_asyncInfo->put_Completed(handler.Get()); +#endif + } + }; + + extern const __declspec(selectany) WCHAR RuntimeClass_IAsyncOperationToAsyncOperationConverter[] = L"_IAsyncOperationToAsyncOperationConverter"; + + /// + /// Class _IAsyncOperationToAsyncOperationConverter is used to convert an instance of IAsyncOperationWithProgress into IAsyncOperation + /// + template + struct _IAsyncOperationToAsyncOperationConverter : + _AsyncInfoImpl, + ABI::Windows::Foundation::IAsyncOperationCompletedHandler<_Result>, + typename ABI::Windows::Foundation::Internal::GetAbiType*>()))>::type> + { + typedef typename ABI::Windows::Foundation::Internal::GetAbiType*>()))>::type _Result_abi; + + InspectableClass(RuntimeClass_IAsyncOperationToAsyncOperationConverter, BaseTrust) + public: + _IAsyncOperationToAsyncOperationConverter(ABI::Windows::Foundation::IAsyncOperation<_Result>* _Operation) : + _AsyncInfoImpl, + ABI::Windows::Foundation::IAsyncOperationCompletedHandler<_Result>, + _Result_abi>(_Operation) {} + public: + virtual STDMETHODIMP GetResults(_Result_abi* results) override { + if (!results) return E_POINTER; +#if _MSC_VER >= 1800 + return _M_asyncInfo.Get()->GetResults(results); +#else + return _M_asyncInfo->GetResults(results); +#endif + } + }; + + extern const __declspec(selectany) WCHAR RuntimeClass_IAsyncOperationWithProgressToAsyncOperationConverter[] = L"_IAsyncOperationWithProgressToAsyncOperationConverter"; + + /// + /// Class _IAsyncOperationWithProgressToAsyncOperationConverter is used to convert an instance of IAsyncOperationWithProgress into IAsyncOperation + /// + template + struct _IAsyncOperationWithProgressToAsyncOperationConverter : + _AsyncInfoImpl, + ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler<_Result, _Progress>, + typename ABI::Windows::Foundation::Internal::GetAbiType*>()))>::type> + { + typedef typename ABI::Windows::Foundation::Internal::GetAbiType*>()))>::type _Result_abi; + + InspectableClass(RuntimeClass_IAsyncOperationWithProgressToAsyncOperationConverter, BaseTrust) + public: + _IAsyncOperationWithProgressToAsyncOperationConverter(ABI::Windows::Foundation::IAsyncOperationWithProgress<_Result, _Progress>* _Operation) : + _AsyncInfoImpl, + ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler<_Result, _Progress>, + _Result_abi>(_Operation) {} + public: + virtual STDMETHODIMP GetResults(_Result_abi* results) override { + if (!results) return E_POINTER; +#if _MSC_VER >= 1800 + return _M_asyncInfo.Get()->GetResults(results); +#else + return _M_asyncInfo->GetResults(results); +#endif + } + }; + + extern const __declspec(selectany) WCHAR RuntimeClass_IAsyncActionToAsyncOperationConverter[] = L"_IAsyncActionToAsyncOperationConverter"; + + /// + /// Class _IAsyncActionToAsyncOperationConverter is used to convert an instance of IAsyncAction into IAsyncOperation<_Unit_type> + /// + struct _IAsyncActionToAsyncOperationConverter : + _AsyncInfoImpl + { + InspectableClass(RuntimeClass_IAsyncActionToAsyncOperationConverter, BaseTrust) + public: + _IAsyncActionToAsyncOperationConverter(ABI::Windows::Foundation::IAsyncAction* _Operation) : + _AsyncInfoImpl(_Operation) {} + + public: + virtual STDMETHODIMP GetResults(details::_Unit_type* results) + { + if (!results) return E_POINTER; + // Invoke GetResults on the IAsyncAction to allow exceptions to be thrown to higher layers before returning a dummy value. +#if _MSC_VER >= 1800 + HRESULT hr = _M_asyncInfo.Get()->GetResults(); +#else + HRESULT hr = _M_asyncInfo->GetResults(); +#endif + if (SUCCEEDED(hr)) *results = _Unit_type(); + return hr; + } + }; + + extern const __declspec(selectany) WCHAR RuntimeClass_IAsyncActionWithProgressToAsyncOperationConverter[] = L"_IAsyncActionWithProgressToAsyncOperationConverter"; + + /// + /// Class _IAsyncActionWithProgressToAsyncOperationConverter is used to convert an instance of IAsyncActionWithProgress into IAsyncOperation<_Unit_type> + /// + template + struct _IAsyncActionWithProgressToAsyncOperationConverter : + _AsyncInfoImpl, + ABI::Windows::Foundation::IAsyncActionWithProgressCompletedHandler<_Progress>, + _Unit_type> + { + InspectableClass(RuntimeClass_IAsyncActionWithProgressToAsyncOperationConverter, BaseTrust) + public: + _IAsyncActionWithProgressToAsyncOperationConverter(ABI::Windows::Foundation::IAsyncActionWithProgress<_Progress>* _Action) : + _AsyncInfoImpl, + ABI::Windows::Foundation::IAsyncActionWithProgressCompletedHandler<_Progress>, + _Unit_type>(_Action) {} + public: + virtual STDMETHODIMP GetResults(_Unit_type* results) override + { + if (!results) return E_POINTER; + // Invoke GetResults on the IAsyncActionWithProgress to allow exceptions to be thrown before returning a dummy value. +#if _MSC_VER >= 1800 + HRESULT hr = _M_asyncInfo.Get()->GetResults(); +#else + HRESULT hr = _M_asyncInfo->GetResults(); +#endif + if (SUCCEEDED(hr)) *results = _Unit_type(); + return hr; + } + }; +} + +/// +/// The task_continuation_context class allows you to specify where you would like a continuation to be executed. +/// It is only useful to use this class from a Windows Store app. For non-Windows Store apps, the task continuation's +/// execution context is determined by the runtime, and not configurable. +/// +/// +/**/ +class task_continuation_context : public details::_ContextCallback +{ +public: + + /// + /// Creates the default task continuation context. + /// + /// + /// The default continuation context. + /// + /// + /// The default context is used if you don't specifiy a continuation context when you call the then method. In Windows + /// applications for Windows 7 and below, as well as desktop applications on Windows 8 and higher, the runtime determines where + /// task continuations will execute. However, in a Windows Store app, the default continuation context for a continuation on an + /// apartment aware task is the apartment where then is invoked. + /// An apartment aware task is a task that unwraps a Windows Runtime IAsyncInfo interface, or a task that is descended from such + /// a task. Therefore, if you schedule a continuation on an apartment aware task in a Windows Runtime STA, the continuation will execute in + /// that STA. + /// A continuation on a non-apartment aware task will execute in a context the Runtime chooses. + /// + /**/ + static task_continuation_context use_default() + { + // The callback context is created with the context set to CaptureDeferred and resolved when it is used in .then() + return task_continuation_context(true); // sets it to deferred, is resolved in the constructor of _ContinuationTaskHandle + } + + /// + /// Creates a task continuation context which allows the Runtime to choose the execution context for a continuation. + /// + /// + /// A task continuation context that represents an arbitrary location. + /// + /// + /// When this continuation context is used the continuation will execute in a context the runtime chooses even if the antecedent task + /// is apartment aware. + /// use_arbitrary can be used to turn off the default behavior for a continuation on an apartment + /// aware task created in an STA. + /// This method is only available to Windows Store apps. + /// + /**/ + static task_continuation_context use_arbitrary() + { + task_continuation_context _Arbitrary(true); + _Arbitrary._Resolve(false); + return _Arbitrary; + } + + /// + /// Returns a task continuation context object that represents the current execution context. + /// + /// + /// The current execution context. + /// + /// + /// This method captures the caller's Windows Runtime context so that continuations can be executed in the right apartment. + /// The value returned by use_current can be used to indicate to the Runtime that the continuation should execute in + /// the captured context (STA vs MTA) regardless of whether or not the antecedent task is apartment aware. An apartment aware task is + /// a task that unwraps a Windows Runtime IAsyncInfo interface, or a task that is descended from such a task. + /// This method is only available to Windows Store apps. + /// + /**/ + static task_continuation_context use_current() + { + task_continuation_context _Current(true); + _Current._Resolve(true); + return _Current; + } + +private: + + task_continuation_context(bool _DeferCapture = false) : details::_ContextCallback(_DeferCapture) + { + } +}; + +#if _MSC_VER >= 1800 +class task_options; +namespace details +{ + struct _Internal_task_options + { + bool _M_hasPresetCreationCallstack; + _TaskCreationCallstack _M_presetCreationCallstack; + + void _set_creation_callstack(const _TaskCreationCallstack &_callstack) + { + _M_hasPresetCreationCallstack = true; + _M_presetCreationCallstack = _callstack; + } + _Internal_task_options() + { + _M_hasPresetCreationCallstack = false; + } + }; + + inline _Internal_task_options &_get_internal_task_options(task_options &options); + inline const _Internal_task_options &_get_internal_task_options(const task_options &options); +} +/// +/// Represents the allowed options for creating a task +/// +class task_options +{ +public: + + + /// + /// Default list of task creation options + /// + task_options() + : _M_Scheduler(Concurrency::get_ambient_scheduler()), + _M_CancellationToken(Concurrency::cancellation_token::none()), + _M_ContinuationContext(task_continuation_context::use_default()), + _M_HasCancellationToken(false), + _M_HasScheduler(false) + { + } + + /// + /// Task option that specify a cancellation token + /// + task_options(Concurrency::cancellation_token _Token) + : _M_Scheduler(Concurrency::get_ambient_scheduler()), + _M_CancellationToken(_Token), + _M_ContinuationContext(task_continuation_context::use_default()), + _M_HasCancellationToken(true), + _M_HasScheduler(false) + { + } + + /// + /// Task option that specify a continuation context. This is valid only for continuations (then) + /// + task_options(task_continuation_context _ContinuationContext) + : _M_Scheduler(Concurrency::get_ambient_scheduler()), + _M_CancellationToken(Concurrency::cancellation_token::none()), + _M_ContinuationContext(_ContinuationContext), + _M_HasCancellationToken(false), + _M_HasScheduler(false) + { + } + + /// + /// Task option that specify a cancellation token and a continuation context. This is valid only for continuations (then) + /// + task_options(Concurrency::cancellation_token _Token, task_continuation_context _ContinuationContext) + : _M_Scheduler(Concurrency::get_ambient_scheduler()), + _M_CancellationToken(_Token), + _M_ContinuationContext(_ContinuationContext), + _M_HasCancellationToken(false), + _M_HasScheduler(false) + { + } + + /// + /// Task option that specify a scheduler with shared lifetime + /// + template + task_options(std::shared_ptr<_SchedType> _Scheduler) + : _M_Scheduler(std::move(_Scheduler)), + _M_CancellationToken(cancellation_token::none()), + _M_ContinuationContext(task_continuation_context::use_default()), + _M_HasCancellationToken(false), + _M_HasScheduler(true) + { + } + + /// + /// Task option that specify a scheduler reference + /// + task_options(Concurrency::scheduler_interface& _Scheduler) + : _M_Scheduler(&_Scheduler), + _M_CancellationToken(Concurrency::cancellation_token::none()), + _M_ContinuationContext(task_continuation_context::use_default()), + _M_HasCancellationToken(false), + _M_HasScheduler(true) + { + } + + /// + /// Task option that specify a scheduler + /// + task_options(Concurrency::scheduler_ptr _Scheduler) + : _M_Scheduler(std::move(_Scheduler)), + _M_CancellationToken(Concurrency::cancellation_token::none()), + _M_ContinuationContext(task_continuation_context::use_default()), + _M_HasCancellationToken(false), + _M_HasScheduler(true) + { + } + + /// + /// Task option copy constructor + /// + task_options(const task_options& _TaskOptions) + : _M_Scheduler(_TaskOptions.get_scheduler()), + _M_CancellationToken(_TaskOptions.get_cancellation_token()), + _M_ContinuationContext(_TaskOptions.get_continuation_context()), + _M_HasCancellationToken(_TaskOptions.has_cancellation_token()), + _M_HasScheduler(_TaskOptions.has_scheduler()) + { + } + + /// + /// Sets the given token in the options + /// + void set_cancellation_token(Concurrency::cancellation_token _Token) + { + _M_CancellationToken = _Token; + _M_HasCancellationToken = true; + } + + /// + /// Sets the given continuation context in the options + /// + void set_continuation_context(task_continuation_context _ContinuationContext) + { + _M_ContinuationContext = _ContinuationContext; + } + + /// + /// Indicates whether a cancellation token was specified by the user + /// + bool has_cancellation_token() const + { + return _M_HasCancellationToken; + } + + /// + /// Returns the cancellation token + /// + Concurrency::cancellation_token get_cancellation_token() const + { + return _M_CancellationToken; + } + + /// + /// Returns the continuation context + /// + task_continuation_context get_continuation_context() const + { + return _M_ContinuationContext; + } + + /// + /// Indicates whether a scheduler n was specified by the user + /// + bool has_scheduler() const + { + return _M_HasScheduler; + } + + /// + /// Returns the scheduler + /// + Concurrency::scheduler_ptr get_scheduler() const + { + return _M_Scheduler; + } + +private: + + task_options const& operator=(task_options const& _Right); + friend details::_Internal_task_options &details::_get_internal_task_options(task_options &); + friend const details::_Internal_task_options &details::_get_internal_task_options(const task_options &); + + Concurrency::scheduler_ptr _M_Scheduler; + Concurrency::cancellation_token _M_CancellationToken; + task_continuation_context _M_ContinuationContext; + details::_Internal_task_options _M_InternalTaskOptions; + bool _M_HasCancellationToken; + bool _M_HasScheduler; +}; +#endif + +namespace details +{ +#if _MSC_VER >= 1800 + inline _Internal_task_options & _get_internal_task_options(task_options &options) + { + return options._M_InternalTaskOptions; + } + inline const _Internal_task_options & _get_internal_task_options(const task_options &options) + { + return options._M_InternalTaskOptions; + } +#endif + struct _Task_impl_base; + template struct _Task_impl; + + template + struct _Task_ptr + { + typedef std::shared_ptr<_Task_impl<_ReturnType>> _Type; +#if _MSC_VER >= 1800 + static _Type _Make(Concurrency::details::_CancellationTokenState * _Ct, Concurrency::scheduler_ptr _Scheduler_arg) { return std::make_shared<_Task_impl<_ReturnType>>(_Ct, _Scheduler_arg); } +#else + static _Type _Make(Concurrency::details::_CancellationTokenState * _Ct) { return std::make_shared<_Task_impl<_ReturnType>>(_Ct); } +#endif + }; +#if _MSC_VER >= 1800 + typedef Concurrency::details::_TaskCollection_t::_TaskProcHandle_t _UnrealizedChore_t; + typedef _UnrealizedChore_t _UnrealizedChore; + typedef Concurrency::extensibility::scoped_critical_section_t scoped_lock; + typedef Concurrency::extensibility::critical_section_t critical_section; + typedef Concurrency::details::atomic_size_t atomic_size_t; +#else + typedef Concurrency::details::_UnrealizedChore _UnrealizedChore; + typedef Concurrency::critical_section::scoped_lock scoped_lock; + typedef Concurrency::critical_section critical_section; + typedef volatile size_t atomic_size_t; +#endif + typedef std::shared_ptr<_Task_impl_base> _Task_ptr_base; + // The weak-typed base task handler for continuation tasks. + struct _ContinuationTaskHandleBase : _UnrealizedChore + { + _ContinuationTaskHandleBase * _M_next; + task_continuation_context _M_continuationContext; + bool _M_isTaskBasedContinuation; + + // This field gives inlining scheduling policy for current chore. + _TaskInliningMode _M_inliningMode; + + virtual _Task_ptr_base _GetTaskImplBase() const = 0; + + _ContinuationTaskHandleBase() : + _M_next(nullptr), _M_isTaskBasedContinuation(false), _M_continuationContext(task_continuation_context::use_default()), _M_inliningMode(Concurrency::details::_NoInline) + { + } + virtual ~_ContinuationTaskHandleBase() {} + }; +#if _MSC_VER >= 1800 +#if _PPLTASK_ASYNC_LOGGING + // GUID used for identifying causality logs from PPLTask + const ::Platform::Guid _PPLTaskCausalityPlatformID(0x7A76B220, 0xA758, 0x4E6E, 0xB0, 0xE0, 0xD7, 0xC6, 0xD7, 0x4A, 0x88, 0xFE); + + __declspec(selectany) volatile long _isCausalitySupported = 0; + + inline bool _IsCausalitySupported() + { +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + if (_isCausalitySupported == 0) + { + long _causality = 1; + OSVERSIONINFOEX _osvi = {}; + _osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + + // The Causality is supported on Windows version higher than Windows 8 + _osvi.dwMajorVersion = 6; + _osvi.dwMinorVersion = 3; + + DWORDLONG _conditionMask = 0; + VER_SET_CONDITION(_conditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION(_conditionMask, VER_MINORVERSION, VER_GREATER_EQUAL); + + if (::VerifyVersionInfo(&_osvi, VER_MAJORVERSION | VER_MINORVERSION, _conditionMask)) + { + _causality = 2; + } + + _isCausalitySupported = _causality; + return _causality == 2; + } + + return _isCausalitySupported == 2 ? true : false; +#else + return true; +#endif + } + + // Stateful logger rests inside task_impl_base. + struct _TaskEventLogger + { + _Task_impl_base *_M_task; + bool _M_scheduled; + bool _M_taskPostEventStarted; + + // Log before scheduling task + void _LogScheduleTask(bool _isContinuation) + { + if (details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationCreation(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), + _isContinuation ? "Concurrency::PPLTask::ScheduleContinuationTask" : "Concurrency::PPLTask::ScheduleTask", 0); + _M_scheduled = true; + } + } + + // It will log the cancel event but not canceled state. _LogTaskCompleted will log the terminal state, which includes cancel state. + void _LogCancelTask() + { + if (details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationRelation(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Important, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), ::Windows::Foundation::Diagnostics::CausalityRelation::Cancel); + + } + } + + // Log when task reaches terminal state. Note: the task can reach a terminal state (by cancellation or exception) without having run + void _LogTaskCompleted(); + + // Log when task body (which includes user lambda and other scheduling code) begin to run + void _LogTaskExecutionStarted() { } + + // Log when task body finish executing + void _LogTaskExecutionCompleted() + { + if (_M_taskPostEventStarted && details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::CompletionNotification); + } + } + + // Log right before user lambda being invoked + void _LogWorkItemStarted() + { + if (details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkStart(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::Execution); + } + } + + // Log right after user lambda being invoked + void _LogWorkItemCompleted() + { + if (details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::Execution); + + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceSynchronousWorkStart(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), ::Windows::Foundation::Diagnostics::CausalitySynchronousWork::CompletionNotification); + _M_taskPostEventStarted = true; + } + } + + _TaskEventLogger(_Task_impl_base *_task) : _M_task(_task) + { + _M_scheduled = false; + _M_taskPostEventStarted = false; + } + }; + + // Exception safe logger for user lambda + struct _TaskWorkItemRAIILogger + { + _TaskEventLogger &_M_logger; + _TaskWorkItemRAIILogger(_TaskEventLogger &_taskHandleLogger) : _M_logger(_taskHandleLogger) + { + _M_logger._LogWorkItemStarted(); + } + + ~_TaskWorkItemRAIILogger() + { + _M_logger._LogWorkItemCompleted(); + } + _TaskWorkItemRAIILogger &operator =(const _TaskWorkItemRAIILogger &); // cannot be assigned + }; + +#else + inline void _LogCancelTask(_Task_impl_base *) {} + struct _TaskEventLogger + { + void _LogScheduleTask(bool) {} + void _LogCancelTask() {} + void _LogWorkItemStarted() {} + void _LogWorkItemCompleted() {} + void _LogTaskExecutionStarted() {} + void _LogTaskExecutionCompleted() {} + void _LogTaskCompleted() {} + _TaskEventLogger(_Task_impl_base *) {} + }; + struct _TaskWorkItemRAIILogger + { + _TaskWorkItemRAIILogger(_TaskEventLogger &) {} + }; +#endif +#endif + /// + /// The _PPLTaskHandle is the strong-typed task handle base. All user task functions need to be wrapped in this task handler + /// to be executable by PPL. By deriving from a different _BaseTaskHandle, it can be used for both initial tasks and continuation tasks. + /// For initial tasks, _PPLTaskHandle will be derived from _UnrealizedChore, and for continuation tasks, it will be derived from + /// _ContinuationTaskHandleBase. The life time of the _PPLTaskHandle object is be managed by runtime if task handle is scheduled. + /// + /// + /// The result type of the _Task_impl. + /// + /// + /// The derived task handle class. The operator () needs to be implemented. + /// + /// + /// The base class from which _PPLTaskHandle should be derived. This is either _UnrealizedChore or _ContinuationTaskHandleBase. + /// + template + struct _PPLTaskHandle : _BaseTaskHandle + { + _PPLTaskHandle(const typename _Task_ptr<_ReturnType>::_Type & _PTask) : _M_pTask(_PTask) + { +#if _MSC_VER < 1800 + m_pFunction = reinterpret_cast (&_UnrealizedChore::_InvokeBridge<_PPLTaskHandle>); + _SetRuntimeOwnsLifetime(true); +#endif + } + virtual ~_PPLTaskHandle() { +#if _MSC_VER >= 1800 + // Here is the sink of all task completion code paths + _M_pTask->_M_taskEventLogger._LogTaskCompleted(); +#endif + } +#if _MSC_VER >= 1800 + virtual void invoke() const +#else + void operator()() const +#endif + { + // All exceptions should be rethrown to finish cleanup of the task collection. They will be caught and handled + // by the runtime. + _CONCRT_ASSERT(_M_pTask != nullptr); + if (!_M_pTask->_TransitionedToStarted()) { +#if _MSC_VER >= 1800 + static_cast(this)->_SyncCancelAndPropagateException(); +#endif + return; + } +#if _MSC_VER >= 1800 + _M_pTask->_M_taskEventLogger._LogTaskExecutionStarted(); +#endif + try + { + // All derived task handle must implement this contract function. + static_cast(this)->_Perform(); + } + catch (const Concurrency::task_canceled &) + { + _M_pTask->_Cancel(true); +#if _MSC_VER < 1800 + throw; +#endif + } + catch (const Concurrency::details::_Interruption_exception &) + { + _M_pTask->_Cancel(true); +#if _MSC_VER < 1800 + throw; +#endif + } + catch (IRestrictedErrorInfo*& _E) + { + _M_pTask->_CancelWithException(_E); +#if _MSC_VER < 1800 + throw; +#endif + } + catch (...) + { + _M_pTask->_CancelWithException(std::current_exception()); +#if _MSC_VER < 1800 + throw; +#endif + } +#if _MSC_VER >= 1800 + _M_pTask->_M_taskEventLogger._LogTaskExecutionCompleted(); +#endif + } + + // Cast _M_pTask pointer to "type-less" _Task_impl_base pointer, which can be used in _ContinuationTaskHandleBase. + // The return value should be automatically optimized by R-value ref. + _Task_ptr_base _GetTaskImplBase() const + { + return _M_pTask; + } + + typename _Task_ptr<_ReturnType>::_Type _M_pTask; + + private: + _PPLTaskHandle const & operator=(_PPLTaskHandle const&); // no assignment operator + }; + + /// + /// The base implementation of a first-class task. This class contains all the non-type specific + /// implementation details of the task. + /// + /**/ + struct _Task_impl_base + { + enum _TaskInternalState + { + // Tracks the state of the task, rather than the task collection on which the task is scheduled + _Created, + _Started, + _PendingCancel, + _Completed, + _Canceled + }; +#if _MSC_VER >= 1800 + _Task_impl_base(Concurrency::details::_CancellationTokenState * _PTokenState, Concurrency::scheduler_ptr _Scheduler_arg) + : _M_TaskState(_Created), + _M_fFromAsync(false), _M_fUnwrappedTask(false), + _M_pRegistration(nullptr), _M_Continuations(nullptr), _M_TaskCollection(_Scheduler_arg), + _M_taskEventLogger(this) +#else + _Task_impl_base(Concurrency::details::_CancellationTokenState * _PTokenState) : _M_TaskState(_Created), + _M_fFromAsync(false), _M_fRuntimeAggregate(false), _M_fUnwrappedTask(false), + _M_pRegistration(nullptr), _M_Continuations(nullptr), _M_pTaskCollection(nullptr), + _M_pTaskCreationAddressHint(nullptr) +#endif + { + // Set cancelation token + _M_pTokenState = _PTokenState; + _CONCRT_ASSERT(_M_pTokenState != nullptr); + if (_M_pTokenState != Concurrency::details::_CancellationTokenState::_None()) + _M_pTokenState->_Reference(); + + } + + virtual ~_Task_impl_base() + { + _CONCRT_ASSERT(_M_pTokenState != nullptr); + if (_M_pTokenState != Concurrency::details::_CancellationTokenState::_None()) + { + _M_pTokenState->_Release(); + } +#if _MSC_VER < 1800 + if (_M_pTaskCollection != nullptr) + { + _M_pTaskCollection->_Release(); + _M_pTaskCollection = nullptr; + } +#endif + } + + task_status _Wait() + { + bool _DoWait = true; + + if (_IsNonBlockingThread()) + { + // In order to prevent Windows Runtime STA threads from blocking the UI, calling task.wait() task.get() is illegal + // if task has not been completed. + if (!_IsCompleted() && !_IsCanceled()) + { + throw Concurrency::invalid_operation("Illegal to wait on a task in a Windows Runtime STA"); + } + else + { + // Task Continuations are 'scheduled' *inside* the chore that is executing on the ancestors's task group. If a continuation + // needs to be marshalled to a different apartment, instead of scheduling, we make a synchronous cross apartment COM + // call to execute the continuation. If it then happens to do something which waits on the ancestor (say it calls .get(), which + // task based continuations are wont to do), waiting on the task group results in on the chore that is making this + // synchronous callback, which causes a deadlock. To avoid this, we test the state ancestor's event , and we will NOT wait on + // if it has finished execution (which means now we are on the inline synchronous callback). + _DoWait = false; + } + } + if (_DoWait) + { +#if _MSC_VER < 1800 + // Wait for the task to be actually scheduled, otherwise the underlying task collection + // might not be created yet. If we don't wait, we will miss the chance to inline this task. + _M_Scheduled.wait(); + + + // A PPL task created by a task_completion_event does not have an underlying TaskCollection. For + // These tasks, a call to wait should wait for the event to be set. The TaskCollection must either + // be nullptr or allocated (the setting of _M_Scheduled) ensures that. +#endif + // If this task was created from a Windows Runtime async operation, do not attempt to inline it. The + // async operation will take place on a thread in the appropriate apartment Simply wait for the completed + // event to be set. +#if _MSC_VER >= 1800 + if (_M_fFromAsync) +#else + if ((_M_pTaskCollection == nullptr) || _M_fFromAsync) +#endif + { +#if _MSC_VER >= 1800 + _M_TaskCollection._Wait(); +#else + _M_Completed.wait(); +#endif + } + else + { + // Wait on the task collection to complete. The task collection is guaranteed to still be + // valid since the task must be still within scope so that the _Task_impl_base destructor + // has not yet been called. This call to _Wait potentially inlines execution of work. + try + { + // Invoking wait on a task collection resets the state of the task collection. This means that + // if the task collection itself were canceled, or had encountered an exception, only the first + // call to wait will receive this status. However, both cancellation and exceptions flowing through + // tasks set state in the task impl itself. + + // When it returns cancelled, either work chore or the cancel thread should already have set task's state + // properly -- cancelled state or completed state (because there was no interruption point). + // For tasks with unwrapped tasks, we should not change the state of current task, since the unwrapped task are still running. +#if _MSC_VER >= 1800 + _M_TaskCollection._RunAndWait(); +#else + _M_pTaskCollection->_RunAndWait(); +#endif + } + catch (Concurrency::details::_Interruption_exception&) + { + // The _TaskCollection will never be an interruption point since it has a none token. + _CONCRT_ASSERT(false); + } + catch (Concurrency::task_canceled&) + { + // task_canceled is a special exception thrown by cancel_current_task. The spec states that cancel_current_task + // must be called from code that is executed within the task (throwing it from parallel work created by and waited + // upon by the task is acceptable). We can safely assume that the task wrapper _PPLTaskHandle::operator() has seen + // the exception and canceled the task. Swallow the exception here. + _CONCRT_ASSERT(_IsCanceled()); + } + catch (IRestrictedErrorInfo*& _E) + { + // Its possible the task body hasn't seen the exception, if so we need to cancel with exception here. + if(!_HasUserException()) + { + _CancelWithException(_E); + } + // Rethrow will mark the exception as observed. + _M_exceptionHolder->_RethrowUserException(); + } + catch (...) + { + // Its possible the task body hasn't seen the exception, if so we need to cancel with exception here. + if (!_HasUserException()) + { + _CancelWithException(std::current_exception()); + } + // Rethrow will mark the exception as observed. + _M_exceptionHolder->_RethrowUserException(); + } + + // If the lambda body for this task (executed or waited upon in _RunAndWait above) happened to return a task + // which is to be unwrapped and plumbed to the output of this task, we must not only wait on the lambda body, we must + // wait on the **INNER** body. It is in theory possible that we could inline such if we plumb a series of things through; + // however, this takes the tact of simply waiting upon the completion signal. + if (_M_fUnwrappedTask) + { +#if _MSC_VER >= 1800 + _M_TaskCollection._Wait(); +#else + _M_Completed.wait(); +#endif + } + } + } + + if (_HasUserException()) + { + _M_exceptionHolder->_RethrowUserException(); + } + else if (_IsCanceled()) + { + return Concurrency::canceled; + } + _CONCRT_ASSERT(_IsCompleted()); + return Concurrency::completed; + } + /// + /// Requests cancellation on the task and schedules continuations if the task can be transitioned to a terminal state. + /// + /// + /// Set to true if the cancel takes place as a result of the task body encountering an exception, or because an ancestor or task_completion_event the task + /// was registered with were canceled with an exception. A synchronous cancel is one that assures the task could not be running on a different thread at + /// the time the cancellation is in progress. An asynchronous cancel is one where the thread performing the cancel has no control over the thread that could + /// be executing the task, that is the task could execute concurrently while the cancellation is in progress. + /// + /// + /// Whether an exception other than the internal runtime cancellation exceptions caused this cancellation. + /// + /// + /// Whether this exception came from an ancestor task or a task_completion_event as opposed to an exception that was encountered by the task itself. Only valid when + /// _UserException is set to true. + /// + /// + /// The exception holder that represents the exception. Only valid when _UserException is set to true. + /// + virtual bool _CancelAndRunContinuations(bool _SynchronousCancel, bool _UserException, bool _PropagatedFromAncestor, const std::shared_ptr<_ExceptionHolder>& _ExHolder) = 0; + + bool _Cancel(bool _SynchronousCancel) + { + // Send in a dummy value for exception. It is not used when the first parameter is false. + return _CancelAndRunContinuations(_SynchronousCancel, false, false, _M_exceptionHolder); + } + + bool _CancelWithExceptionHolder(const std::shared_ptr<_ExceptionHolder>& _ExHolder, bool _PropagatedFromAncestor) + { + // This task was canceled because an ancestor task encountered an exception. + return _CancelAndRunContinuations(true, true, _PropagatedFromAncestor, _ExHolder); + } + + bool _CancelWithException(IRestrictedErrorInfo*& _Exception) + { + // This task was canceled because the task body encountered an exception. + _CONCRT_ASSERT(!_HasUserException()); +#if _MSC_VER >= 1800 + return _CancelAndRunContinuations(true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationCallstack())); +#else + return _CancelAndRunContinuations(true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationAddressHint())); +#endif + } + bool _CancelWithException(const std::exception_ptr& _Exception) + { + // This task was canceled because the task body encountered an exception. + _CONCRT_ASSERT(!_HasUserException()); +#if _MSC_VER >= 1800 + return _CancelAndRunContinuations(true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationCallstack())); +#else + return _CancelAndRunContinuations(true, true, false, std::make_shared<_ExceptionHolder>(_Exception, _GetTaskCreationAddressHint())); +#endif + } + +#if _MSC_VER >= 1800 + void _RegisterCancellation(std::weak_ptr<_Task_impl_base> _WeakPtr) +#else + void _RegisterCancellation() +#endif + { + _CONCRT_ASSERT(Concurrency::details::_CancellationTokenState::_IsValid(_M_pTokenState)); +#if _MSC_VER >= 1800 + auto _CancellationCallback = [_WeakPtr](){ + // Taking ownership of the task prevents dead lock during destruction + // if the destructor waits for the cancellations to be finished + auto _task = _WeakPtr.lock(); + if (_task != nullptr) + _task->_Cancel(false); + }; + + _M_pRegistration = new Concurrency::details::_CancellationTokenCallback(_CancellationCallback); + _M_pTokenState->_RegisterCallback(_M_pRegistration); +#else + _M_pRegistration = _M_pTokenState->_RegisterCallback(reinterpret_cast(&_CancelViaToken), (_Task_impl_base *)this); +#endif + } + + void _DeregisterCancellation() + { + if (_M_pRegistration != nullptr) + { + _M_pTokenState->_DeregisterCallback(_M_pRegistration); + _M_pRegistration->_Release(); + _M_pRegistration = nullptr; + } + } +#if _MSC_VER < 1800 + static void _CancelViaToken(_Task_impl_base *_PImpl) + { + _PImpl->_Cancel(false); + } +#endif + bool _IsCreated() + { + return (_M_TaskState == _Created); + } + + bool _IsStarted() + { + return (_M_TaskState == _Started); + } + + bool _IsPendingCancel() + { + return (_M_TaskState == _PendingCancel); + } + + bool _IsCompleted() + { + return (_M_TaskState == _Completed); + } + + bool _IsCanceled() + { + return (_M_TaskState == _Canceled); + } + + bool _HasUserException() + { + return static_cast(_M_exceptionHolder); + } +#if _MSC_VER < 1800 + void _SetScheduledEvent() + { + _M_Scheduled.set(); + } +#endif + const std::shared_ptr<_ExceptionHolder>& _GetExceptionHolder() + { + _CONCRT_ASSERT(_HasUserException()); + return _M_exceptionHolder; + } + + bool _IsApartmentAware() + { + return _M_fFromAsync; + } + + void _SetAsync(bool _Async = true) + { + _M_fFromAsync = _Async; + } +#if _MSC_VER >= 1800 + _TaskCreationCallstack _GetTaskCreationCallstack() + { + return _M_pTaskCreationCallstack; + } + + void _SetTaskCreationCallstack(const _TaskCreationCallstack &_Callstack) + { + _M_pTaskCreationCallstack = _Callstack; + } +#else + void* _GetTaskCreationAddressHint() + { + return _M_pTaskCreationAddressHint; + } + + void _SetTaskCreationAddressHint(void* _AddressHint) + { + _M_pTaskCreationAddressHint = _AddressHint; + } +#endif + /// + /// Helper function to schedule the task on the Task Collection. + /// + /// + /// The task chore handle that need to be executed. + /// + /// + /// The inlining scheduling policy for current _PTaskHandle. + /// + void _ScheduleTask(_UnrealizedChore * _PTaskHandle, _TaskInliningMode _InliningMode) + { +#if _MSC_VER < 1800 + // Construct the task collection; We use none token to provent it becoming interruption point. + _M_pTaskCollection = Concurrency::details::_AsyncTaskCollection::_NewCollection(Concurrency::details::_CancellationTokenState::_None()); + // _M_pTaskCollection->_ScheduleWithAutoInline will schedule the chore onto AsyncTaskCollection with automatic inlining, in a way that honors cancellation etc. +#endif + try + { +#if _MSC_VER >= 1800 + _M_TaskCollection._ScheduleTask(_PTaskHandle, _InliningMode); +#else + // Do not need to check its returning state, more details please refer to _Wait method. + _M_pTaskCollection->_ScheduleWithAutoInline(_PTaskHandle, _InliningMode); +#endif + } + catch (const Concurrency::task_canceled &) + { + // task_canceled is a special exception thrown by cancel_current_task. The spec states that cancel_current_task + // must be called from code that is executed within the task (throwing it from parallel work created by and waited + // upon by the task is acceptable). We can safely assume that the task wrapper _PPLTaskHandle::operator() has seen + // the exception and canceled the task. Swallow the exception here. + _CONCRT_ASSERT(_IsCanceled()); + } + catch (const Concurrency::details::_Interruption_exception &) + { + // The _TaskCollection will never be an interruption point since it has a none token. + _CONCRT_ASSERT(false); + } + catch (...) + { + // This exception could only have come from within the chore body. It should've been caught + // and the task should be canceled with exception. Swallow the exception here. + _CONCRT_ASSERT(_HasUserException()); + } +#if _MSC_VER < 1800 + // Set the event in case anyone is waiting to notify that this task has been scheduled. In the case where we + // execute the chore inline, the event should be set after the chore has executed, to prevent a different thread + // performing a wait on the task from waiting on the task collection before the chore is actually added to it, + // and thereby returning from the wait() before the chore has executed. + _SetScheduledEvent(); +#endif + } + + /// + /// Function executes a continuation. This function is recorded by a parent task implementation + /// when a continuation is created in order to execute later. + /// + /// + /// The continuation task chore handle that need to be executed. + /// + /**/ + void _RunContinuation(_ContinuationTaskHandleBase * _PTaskHandle) + { + _Task_ptr_base _ImplBase = _PTaskHandle->_GetTaskImplBase(); + if (_IsCanceled() && !_PTaskHandle->_M_isTaskBasedContinuation) + { + if (_HasUserException()) + { + // If the ancestor encountered an exception, transfer the exception to the continuation + // This traverses down the tree to propagate the exception. + _ImplBase->_CancelWithExceptionHolder(_GetExceptionHolder(), true); + } + else + { + // If the ancestor was canceled, then your own execution should be canceled. + // This traverses down the tree to cancel it. + _ImplBase->_Cancel(true); + } + } + else + { + // This can only run when the ancestor has completed or it's a task based continuation that fires when a task is canceled + // (with or without a user exception). + _CONCRT_ASSERT(_IsCompleted() || _PTaskHandle->_M_isTaskBasedContinuation); + +#if _MSC_VER >= 1800 + _CONCRT_ASSERT(!_ImplBase->_IsCanceled()); + return _ImplBase->_ScheduleContinuationTask(_PTaskHandle); +#else + // If it has been canceled here (before starting), do nothing. The guy firing cancel will do the clean up. + if (!_ImplBase->_IsCanceled()) + { + return _ImplBase->_ScheduleContinuationTask(_PTaskHandle); + } +#endif + } + + // If the handle is not scheduled, we need to manually delete it. + delete _PTaskHandle; + } + + // Schedule a continuation to run + void _ScheduleContinuationTask(_ContinuationTaskHandleBase * _PTaskHandle) + { +#if _MSC_VER >= 1800 + _M_taskEventLogger._LogScheduleTask(true); +#endif + // Ensure that the continuation runs in proper context (this might be on a Concurrency Runtime thread or in a different Windows Runtime apartment) + if (_PTaskHandle->_M_continuationContext._HasCapturedContext()) + { + // For those continuations need to be scheduled inside captured context, we will try to apply automatic inlining to their inline modes, + // if they haven't been specified as _ForceInline yet. This change will encourage those continuations to be executed inline so that reduce + // the cost of marshaling. + // For normal continuations we won't do any change here, and their inline policies are completely decided by ._ThenImpl method. + if (_PTaskHandle->_M_inliningMode != Concurrency::details::_ForceInline) + { + _PTaskHandle->_M_inliningMode = Concurrency::details::_DefaultAutoInline; + } + details::_ScheduleFuncWithAutoInline([_PTaskHandle]() -> HRESULT { + // Note that we cannot directly capture "this" pointer, instead, we should use _TaskImplPtr, a shared_ptr to the _Task_impl_base. + // Because "this" pointer will be invalid as soon as _PTaskHandle get deleted. _PTaskHandle will be deleted after being scheduled. + auto _TaskImplPtr = _PTaskHandle->_GetTaskImplBase(); + if (details::_ContextCallback::_CaptureCurrent() == _PTaskHandle->_M_continuationContext) + { + _TaskImplPtr->_ScheduleTask(_PTaskHandle, Concurrency::details::_ForceInline); + } + else + { + // + // It's entirely possible that the attempt to marshal the call into a differing context will fail. In this case, we need to handle + // the exception and mark the continuation as canceled with the appropriate exception. There is one slight hitch to this: + // + // NOTE: COM's legacy behavior is to swallow SEH exceptions and marshal them back as HRESULTS. This will in effect turn an SEH into + // a C++ exception that gets tagged on the task. One unfortunate result of this is that various pieces of the task infrastructure will + // not be in a valid state after this in /EHsc (due to the lack of destructors running, etc...). + // + try + { + // Dev10 compiler needs this! + auto _PTaskHandle1 = _PTaskHandle; + _PTaskHandle->_M_continuationContext._CallInContext([_PTaskHandle1, _TaskImplPtr]() -> HRESULT { + _TaskImplPtr->_ScheduleTask(_PTaskHandle1, Concurrency::details::_ForceInline); + return S_OK; + }); + } + catch (IRestrictedErrorInfo*& _E) + { + _TaskImplPtr->_CancelWithException(_E); + } + catch (...) + { + _TaskImplPtr->_CancelWithException(std::current_exception()); + } + } + return S_OK; + }, _PTaskHandle->_M_inliningMode); + } + else + { + _ScheduleTask(_PTaskHandle, _PTaskHandle->_M_inliningMode); + } + } + + /// + /// Schedule the actual continuation. This will either schedule the function on the continuation task's implementation + /// if the task has completed or append it to a list of functions to execute when the task actually does complete. + /// + /// + /// The input type of the task. + /// + /// + /// The output type of the task. + /// + /**/ + void _ScheduleContinuation(_ContinuationTaskHandleBase * _PTaskHandle) + { + enum { _Nothing, _Schedule, _Cancel, _CancelWithException } _Do = _Nothing; + + // If the task has canceled, cancel the continuation. If the task has completed, execute the continuation right away. + // Otherwise, add it to the list of pending continuations + { + scoped_lock _LockHolder(_M_ContinuationsCritSec); + if (_IsCompleted() || (_IsCanceled() && _PTaskHandle->_M_isTaskBasedContinuation)) + { + _Do = _Schedule; + } + else if (_IsCanceled()) + { + if (_HasUserException()) + { + _Do = _CancelWithException; + } + else + { + _Do = _Cancel; + } + } + else + { + // chain itself on the continuation chain. + _PTaskHandle->_M_next = _M_Continuations; + _M_Continuations = _PTaskHandle; + } + } + + // Cancellation and execution of continuations should be performed after releasing the lock. Continuations off of + // async tasks may execute inline. + switch (_Do) + { + case _Schedule: + { + _PTaskHandle->_GetTaskImplBase()->_ScheduleContinuationTask(_PTaskHandle); + break; + } + case _Cancel: + { + // If the ancestor was canceled, then your own execution should be canceled. + // This traverses down the tree to cancel it. + _PTaskHandle->_GetTaskImplBase()->_Cancel(true); + + delete _PTaskHandle; + break; + } + case _CancelWithException: + { + // If the ancestor encountered an exception, transfer the exception to the continuation + // This traverses down the tree to propagate the exception. + _PTaskHandle->_GetTaskImplBase()->_CancelWithExceptionHolder(_GetExceptionHolder(), true); + + delete _PTaskHandle; + break; + } + case _Nothing: + default: + // In this case, we have inserted continuation to continuation chain, + // nothing more need to be done, just leave. + break; + } + } + + void _RunTaskContinuations() + { + // The link list can no longer be modified at this point, + // since all following up continuations will be scheduled by themselves. + _ContinuationList _Cur = _M_Continuations, _Next; + _M_Continuations = nullptr; + while (_Cur) + { + // Current node might be deleted after running, + // so we must fetch the next first. + _Next = _Cur->_M_next; + _RunContinuation(_Cur); + _Cur = _Next; + } + } + static bool _IsNonBlockingThread() + { + APTTYPE _AptType; + APTTYPEQUALIFIER _AptTypeQualifier; + + HRESULT hr = CoGetApartmentType(&_AptType, &_AptTypeQualifier); + // + // If it failed, it's not a Windows Runtime/COM initialized thread. This is not a failure. + // + if (SUCCEEDED(hr)) + { + switch (_AptType) + { + case APTTYPE_STA: + case APTTYPE_MAINSTA: + return true; + break; + case APTTYPE_NA: + switch (_AptTypeQualifier) + { + // A thread executing in a neutral apartment is either STA or MTA. To find out if this thread is allowed + // to wait, we check the app qualifier. If it is an STA thread executing in a neutral apartment, waiting + // is illegal, because the thread is responsible for pumping messages and waiting on a task could take the + // thread out of circulation for a while. + case APTTYPEQUALIFIER_NA_ON_STA: + case APTTYPEQUALIFIER_NA_ON_MAINSTA: + return true; + break; + } + break; + } + } +#if _UITHREADCTXT_SUPPORT + // This method is used to throw an exepection in _Wait() if called within STA. We + // want the same behavior if _Wait is called on the UI thread. + if (SUCCEEDED(CaptureUiThreadContext(nullptr))) + { + return true; + } +#endif // _UITHREADCTXT_SUPPORT + + return false; + } + + template + static void _AsyncInit(const typename _Task_ptr<_ReturnType>::_Type & _OuterTask, + _AsyncInfoImpl<_OpType, _CompHandlerType, _ResultType>* _AsyncOp) + { + typedef typename ABI::Windows::Foundation::Internal::GetAbiType()))>::type _Result_abi; + // This method is invoked either when a task is created from an existing async operation or + // when a lambda that creates an async operation executes. + + // If the outer task is pending cancel, cancel the async operation before setting the completed handler. The COM reference on + // the IAsyncInfo object will be released when all *references to the operation go out of scope. + + // This assertion uses the existence of taskcollection to determine if the task was created from an event. + // That is no longer valid as even tasks created from a user lambda could have no underlying taskcollection + // when a custom scheduler is used. +#if _MSC_VER < 1800 + _CONCRT_ASSERT(((_OuterTask->_M_pTaskCollection == nullptr) || _OuterTask->_M_fUnwrappedTask) && !_OuterTask->_IsCanceled()); +#endif + + // Pass the shared_ptr by value into the lambda instead of using 'this'. + + _AsyncOp->put_Completed(Microsoft::WRL::Callback<_CompHandlerType>( + [_OuterTask, _AsyncOp](_OpType* _Operation, ABI::Windows::Foundation::AsyncStatus _Status) mutable -> HRESULT + { + HRESULT hr = S_OK; + if (_Status == ABI::Windows::Foundation::AsyncStatus::Canceled) + { + _OuterTask->_Cancel(true); + } + else if (_Status == ABI::Windows::Foundation::AsyncStatus::Error) + { + HRESULT _hr; + Microsoft::WRL::ComPtr pAsyncInfo; + if (SUCCEEDED(hr = _Operation->QueryInterface(pAsyncInfo.GetAddressOf())) && SUCCEEDED(hr = pAsyncInfo->get_ErrorCode(&_hr))) + _OuterTask->_CancelWithException(std::make_exception_ptr(_hr)); + } + else + { + _CONCRT_ASSERT(_Status == ABI::Windows::Foundation::AsyncStatus::Completed); + _NormalizeVoidToUnitType<_Result_abi>::_Type results; + if (SUCCEEDED(hr = _AsyncOp->GetResults(&results))) + _OuterTask->_FinalizeAndRunContinuations(results); + } + // Take away this shared pointers reference on the task instead of waiting for the delegate to be released. It could + // be released on a different thread after a delay, and not releasing the reference here could cause the tasks to hold + // on to resources longer than they should. As an example, without this reset, writing to a file followed by reading from + // it using the Windows Runtime Async APIs causes a sharing violation. + // Using const_cast is the workaround for failed mutable keywords + const_cast<_Task_ptr<_ReturnType>::_Type &>(_OuterTask).reset(); + return hr; + }).Get()); + _OuterTask->_SetUnwrappedAsyncOp(_AsyncOp); + } + template + static void _AsyncInit(const typename _Task_ptr<_ReturnType>::_Type& _OuterTask, const task<_InternalReturnType> & _UnwrappedTask) + { + _CONCRT_ASSERT(_OuterTask->_M_fUnwrappedTask && !_OuterTask->_IsCanceled()); + // + // We must ensure that continuations off _OuterTask (especially exception handling ones) continue to function in the + // presence of an exception flowing out of the inner task _UnwrappedTask. This requires an exception handling continuation + // off the inner task which does the appropriate funnelling to the outer one. We use _Then instead of then to prevent + // the exception from being marked as observed by our internal continuation. This continuation must be scheduled regardless + // of whether or not the _OuterTask task is canceled. + // + _UnwrappedTask._Then([_OuterTask](task<_InternalReturnType> _AncestorTask) -> HRESULT { + + if (_AncestorTask._GetImpl()->_IsCompleted()) + { + _OuterTask->_FinalizeAndRunContinuations(_AncestorTask._GetImpl()->_GetResult()); + } + else + { + _CONCRT_ASSERT(_AncestorTask._GetImpl()->_IsCanceled()); + if (_AncestorTask._GetImpl()->_HasUserException()) + { + // Set _PropagatedFromAncestor to false, since _AncestorTask is not an ancestor of _UnwrappedTask. + // Instead, it is the enclosing task. + _OuterTask->_CancelWithExceptionHolder(_AncestorTask._GetImpl()->_GetExceptionHolder(), false); + } + else + { + _OuterTask->_Cancel(true); + } + } + return S_OK; +#if _MSC_VER >= 1800 + }, nullptr, Concurrency::details::_DefaultAutoInline); +#else + }, nullptr, false, Concurrency::details::_DefaultAutoInline); +#endif + } + +#if _MSC_VER >= 1800 + Concurrency::scheduler_ptr _GetScheduler() const + { + return _M_TaskCollection._GetScheduler(); + } +#else + Concurrency::event _M_Completed; + Concurrency::event _M_Scheduled; +#endif + + // Tracks the internal state of the task + volatile _TaskInternalState _M_TaskState; + // Set to true either if the ancestor task had the flag set to true, or if the lambda that does the work of this task returns an + // async operation or async action that is unwrapped by the runtime. + bool _M_fFromAsync; +#if _MSC_VER < 1800 + // Set to true if we need to marshal the inner parts of an aggregate type like std::vector or std::pair. We only marshal + // the contained T^s if we create the vector or pair, such as on a when_any or a when_all operation. + bool _M_fRuntimeAggregate; +#endif + // Set to true when a continuation unwraps a task or async operation. + bool _M_fUnwrappedTask; + + // An exception thrown by the task body is captured in an exception holder and it is shared with all value based continuations rooted at the task. + // The exception is 'observed' if the user invokes get()/wait() on any of the tasks that are sharing this exception holder. If the exception + // is not observed by the time the internal object owned by the shared pointer destructs, the process will fail fast. + std::shared_ptr<_ExceptionHolder> _M_exceptionHolder; + + typedef _ContinuationTaskHandleBase * _ContinuationList; + + critical_section _M_ContinuationsCritSec; + _ContinuationList _M_Continuations; + + // The cancellation token state. + Concurrency::details::_CancellationTokenState * _M_pTokenState; + + // The registration on the token. + Concurrency::details::_CancellationTokenRegistration * _M_pRegistration; + + // The async task collection wrapper +#if _MSC_VER >= 1800 + Concurrency::details::_TaskCollection_t _M_TaskCollection; + + // Callstack for function call (constructor or .then) that created this task impl. + _TaskCreationCallstack _M_pTaskCreationCallstack; + + _TaskEventLogger _M_taskEventLogger; +#else + Concurrency::details::_AsyncTaskCollection * _M_pTaskCollection; + + // Points to the source code instruction right after the function call (constructor or .then) that created this task impl. + void* _M_pTaskCreationAddressHint; +#endif + + private: + // Must not be copied by value: + _Task_impl_base(const _Task_impl_base&); + _Task_impl_base const & operator=(_Task_impl_base const&); + }; + +#if _MSC_VER >= 1800 +#if _PPLTASK_ASYNC_LOGGING + inline void _TaskEventLogger::_LogTaskCompleted() + { + if (_M_scheduled) + { + ::Windows::Foundation::AsyncStatus _State; + if (_M_task->_IsCompleted()) + _State = ::Windows::Foundation::AsyncStatus::Completed; + else if (_M_task->_HasUserException()) + _State = ::Windows::Foundation::AsyncStatus::Error; + else + _State = ::Windows::Foundation::AsyncStatus::Canceled; + + if (details::_IsCausalitySupported()) + { + ::Windows::Foundation::Diagnostics::AsyncCausalityTracer::TraceOperationCompletion(::Windows::Foundation::Diagnostics::CausalityTraceLevel::Required, ::Windows::Foundation::Diagnostics::CausalitySource::Library, + _PPLTaskCausalityPlatformID, reinterpret_cast(_M_task), _State); + } + } + } +#endif +#endif + + template + struct _Task_impl : public _Task_impl_base + { + typedef ABI::Windows::Foundation::IAsyncInfo _AsyncOperationType; +#if _MSC_VER >= 1800 + _Task_impl(Concurrency::details::_CancellationTokenState * _Ct, Concurrency::scheduler_ptr _Scheduler_arg) + : _Task_impl_base(_Ct, _Scheduler_arg) +#else + _Task_impl(Concurrency::details::_CancellationTokenState * _Ct) : _Task_impl_base(_Ct) +#endif + { + _M_unwrapped_async_op = nullptr; + } + virtual ~_Task_impl() + { + // We must invoke _DeregisterCancellation in the derived class destructor. Calling it in the base class destructor could cause + // a partially initialized _Task_impl to be in the list of registrations for a cancellation token. + _DeregisterCancellation(); + } + virtual bool _CancelAndRunContinuations(bool _SynchronousCancel, bool _UserException, bool _PropagatedFromAncestor, const std::shared_ptr<_ExceptionHolder> & _ExceptionHolder) + { + enum { _Nothing, _RunContinuations, _Cancel } _Do = _Nothing; + { + scoped_lock _LockHolder(_M_ContinuationsCritSec); + if (_UserException) + { + _CONCRT_ASSERT(_SynchronousCancel && !_IsCompleted()); + // If the state is _Canceled, the exception has to be coming from an ancestor. + _CONCRT_ASSERT(!_IsCanceled() || _PropagatedFromAncestor); +#if _MSC_VER < 1800 + // If the state is _Started or _PendingCancel, the exception cannot be coming from an ancestor. + _CONCRT_ASSERT((!_IsStarted() && !_IsPendingCancel()) || !_PropagatedFromAncestor); +#endif + // We should not be canceled with an exception more than once. + _CONCRT_ASSERT(!_HasUserException()); + + if (_M_TaskState == _Canceled) + { + // If the task has finished cancelling there should not be any continuation records in the array. + return false; + } + else + { + _CONCRT_ASSERT(_M_TaskState != _Completed); + _M_exceptionHolder = _ExceptionHolder; + } + } + else + { + // Completed is a non-cancellable state, and if this is an asynchronous cancel, we're unable to do better than the last async cancel + // which is to say, cancellation is already initiated, so return early. + if (_IsCompleted() || _IsCanceled() || (_IsPendingCancel() && !_SynchronousCancel)) + { + _CONCRT_ASSERT(!_IsCompleted() || !_HasUserException()); + return false; + } + _CONCRT_ASSERT(!_SynchronousCancel || !_HasUserException()); + } + +#if _MSC_VER >= 1800 + if (_SynchronousCancel) +#else + if (_SynchronousCancel || _IsCreated()) +#endif + { + // Be aware that this set must be done BEFORE _M_Scheduled being set, or race will happen between this and wait() + _M_TaskState = _Canceled; +#if _MSC_VER < 1800 + _M_Scheduled.set(); +#endif + + // Cancellation completes the task, so all dependent tasks must be run to cancel them + // They are canceled when they begin running (see _RunContinuation) and see that their + // ancestor has been canceled. + _Do = _RunContinuations; + } + else + { +#if _MSC_VER >= 1800 + _CONCRT_ASSERT(!_UserException); + + if (_IsStarted()) + { + // should not initiate cancellation under a lock + _Do = _Cancel; + } + + // The _M_TaskState variable transitions to _Canceled when cancellation is completed (the task is not executing user code anymore). + // In the case of a synchronous cancel, this can happen immediately, whereas with an asynchronous cancel, the task has to move from + // _Started to _PendingCancel before it can move to _Canceled when it is finished executing. + _M_TaskState = _PendingCancel; + + _M_taskEventLogger._LogCancelTask(); + } + } + + switch (_Do) + { + case _Cancel: + { +#else + _CONCRT_ASSERT(_IsStarted() && !_UserException); +#endif + // The _M_TaskState variable transitions to _Canceled when cancellation is completed (the task is not executing user code anymore). + // In the case of a synchronous cancel, this can happen immediately, whereas with an asynchronous cancel, the task has to move from + // _Started to _PendingCancel before it can move to _Canceled when it is finished executing. + _M_TaskState = _PendingCancel; + if (_M_unwrapped_async_op != nullptr) + { + // We will only try to cancel async operation but not unwrapped tasks, since unwrapped tasks cannot be canceled without its token. + if (_M_unwrapped_async_op) _M_unwrapped_async_op->Cancel(); + } +#if _MSC_VER >= 1800 + _M_TaskCollection._Cancel(); + break; +#else + // Optimistic trying for cancelation + if (_M_pTaskCollection != nullptr) + { + _M_pTaskCollection->_Cancel(); + } +#endif + } +#if _MSC_VER < 1800 + } +#endif + + // Only execute continuations and mark the task as completed if we were able to move the task to the _Canceled state. +#if _MSC_VER >= 1800 + case _RunContinuations: + { + _M_TaskCollection._Complete(); +#else + if (_RunContinuations) + { + _M_Completed.set(); +#endif + + if (_M_Continuations) + { + // Scheduling cancellation with automatic inlining. + details::_ScheduleFuncWithAutoInline([=]() -> HRESULT { _RunTaskContinuations(); return S_OK; }, Concurrency::details::_DefaultAutoInline); + } +#if _MSC_VER >= 1800 + break; + } +#endif + } + return true; + } + void _FinalizeAndRunContinuations(_ReturnType _Result) + { + +#if _MSC_VER >= 1800 + _M_Result.Set(_Result); +#else + _M_Result = _Result; + _M_ResultContext = _ResultContext<_ReturnType>::_GetContext(_M_fRuntimeAggregate); +#endif + { + // + // Hold this lock to ensure continuations being concurrently either get added + // to the _M_Continuations vector or wait for the result + // + scoped_lock _LockHolder(_M_ContinuationsCritSec); + + // A task could still be in the _Created state if it was created with a task_completion_event. + // It could also be in the _Canceled state for the same reason. + _CONCRT_ASSERT(!_HasUserException() && !_IsCompleted()); + if (_IsCanceled()) + { + return; + } + + // Always transition to "completed" state, even in the face of unacknowledged pending cancellation + _M_TaskState = _Completed; + } +#if _MSC_VER >= 1800 + _M_TaskCollection._Complete(); +#else + _M_Completed.set(); +#endif + _RunTaskContinuations(); + } + // + // This method is invoked when the starts executing. The task returns early if this method returns true. + // + bool _TransitionedToStarted() + { + scoped_lock _LockHolder(_M_ContinuationsCritSec); +#if _MSC_VER >= 1800 + // Canceled state could only result from antecedent task's canceled state, but that code path will not reach here. + _ASSERT(!_IsCanceled()); + if (_IsPendingCancel()) +#else + if (_IsCanceled()) +#endif + { + return false; + } + _CONCRT_ASSERT(_IsCreated()); + _M_TaskState = _Started; + return true; + } + void _SetUnwrappedAsyncOp(_AsyncOperationType* _AsyncOp) + { + scoped_lock _LockHolder(_M_ContinuationsCritSec); + // Cancel the async operation if the task itself is canceled, since the thread that canceled the task missed it. + if (_IsPendingCancel()) + { + _CONCRT_ASSERT(!_IsCanceled()); + if (_AsyncOp) _AsyncOp->Cancel(); + } + else + { + _M_unwrapped_async_op = _AsyncOp; + } + } +#if _MSC_VER >= 1800 + // Return true if the task has reached a terminal state + bool _IsDone() + { + return _IsCompleted() || _IsCanceled(); + } +#endif + _ReturnType _GetResult() + { +#if _MSC_VER >= 1800 + return _M_Result.Get(); +#else + return _ResultContext<_ReturnType>::_GetValue(_M_Result, _M_ResultContext, _M_fRuntimeAggregate); +#endif + } +#if _MSC_VER >= 1800 + _ResultHolder<_ReturnType> _M_Result; // this means that the result type must have a public default ctor. +#else + _ReturnType _M_Result; // this means that the result type must have a public default ctor. +#endif + Microsoft::WRL::ComPtr<_AsyncOperationType> _M_unwrapped_async_op; +#if _MSC_VER < 1800 + _ContextCallback _M_ResultContext; +#endif + }; + + template + struct _Task_completion_event_impl + { +#if _MSC_VER >= 1800 + private: + _Task_completion_event_impl(const _Task_completion_event_impl&); + _Task_completion_event_impl& operator=(const _Task_completion_event_impl&); + + public: +#endif + typedef std::vector::_Type> _TaskList; + + _Task_completion_event_impl() : _M_fHasValue(false), _M_fIsCanceled(false) + { + } + + bool _HasUserException() + { + return _M_exceptionHolder != nullptr; + } + + ~_Task_completion_event_impl() + { + for (auto _TaskIt = _M_tasks.begin(); _TaskIt != _M_tasks.end(); ++_TaskIt) + { + _CONCRT_ASSERT(!_M_fHasValue && !_M_fIsCanceled); + // Cancel the tasks since the event was never signaled or canceled. + (*_TaskIt)->_Cancel(true); + } + } + + // We need to protect the loop over the array, so concurrent_vector would not have helped + _TaskList _M_tasks; + critical_section _M_taskListCritSec; +#if _MSC_VER >= 1800 + _ResultHolder<_ResultType> _M_value; +#else + _ResultType _M_value; +#endif + std::shared_ptr<_ExceptionHolder> _M_exceptionHolder; + bool _M_fHasValue; + bool _M_fIsCanceled; + }; + + // Utility method for dealing with void functions + inline std::function _MakeVoidToUnitFunc(const std::function& _Func) + { + return [=](_Unit_type* retVal) -> HRESULT { HRESULT hr = _Func(); *retVal = _Unit_type(); return hr; }; + } + + template + std::function _MakeUnitToTFunc(const std::function& _Func) + { + return [=](_Unit_type, _Type* retVal) -> HRESULT { HRESULT hr = _Func(retVal); return hr; }; + } + + template + std::function _MakeTToUnitFunc(const std::function& _Func) + { + return[=](_Type t, _Unit_type* retVal) -> HRESULT { HRESULT hr = _Func(t); *retVal = _Unit_type(); return hr; }; + } + + inline std::function _MakeUnitToUnitFunc(const std::function& _Func) + { + return [=](_Unit_type, _Unit_type* retVal) -> HRESULT { HRESULT hr = _Func(); *retVal = _Unit_type(); return hr; }; + } +} + + +/// +/// The task_completion_event class allows you to delay the execution of a task until a condition is satisfied, +/// or start a task in response to an external event. +/// +/// +/// The result type of this task_completion_event class. +/// +/// +/// Use a task created from a task completion event when your scenario requires you to create a task that will complete, and +/// thereby have its continuations scheduled for execution, at some point in the future. The task_completion_event must +/// have the same type as the task you create, and calling the set method on the task completion event with a value of that type +/// will cause the associated task to complete, and provide that value as a result to its continuations. +/// If the task completion event is never signaled, any tasks created from it will be canceled when it is destructed. +/// task_completion_event behaves like a smart pointer, and should be passed by value. +/// +/// +/**/ +template +class task_completion_event +{ +public: + /// + /// Constructs a task_completion_event object. + /// + /**/ + task_completion_event() : _M_Impl(std::make_shared>()) + { + } + + /// + /// Sets the task completion event. + /// + /// + /// The result to set this event with. + /// + /// + /// The method returns true if it was successful in setting the event. It returns false if the event is already set. + /// + /// + /// In the presence of multiple or concurrent calls to set, only the first call will succeed and its result (if any) will be stored in the + /// task completion event. The remaining sets are ignored and the method will return false. When you set a task completion event, all the + /// tasks created from that event will immediately complete, and its continuations, if any, will be scheduled. Task completion objects that have + /// a other than void will pass the value to their continuations. + /// + /**/ + bool set(_ResultType _Result) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + // Subsequent sets are ignored. This makes races to set benign: the first setter wins and all others are ignored. + if (_IsTriggered()) + { + return false; + } + + _TaskList _Tasks; + bool _RunContinuations = false; + { + details::scoped_lock _LockHolder(_M_Impl->_M_taskListCritSec); + + if (!_IsTriggered()) + { +#if _MSC_VER >= 1800 + _M_Impl->_M_value.Set(_Result); +#else + _M_Impl->_M_value = _Result; +#endif + _M_Impl->_M_fHasValue = true; + + _Tasks.swap(_M_Impl->_M_tasks); + _RunContinuations = true; + } + } + + if (_RunContinuations) + { + for (auto _TaskIt = _Tasks.begin(); _TaskIt != _Tasks.end(); ++_TaskIt) + { +#if _MSC_VER >= 1800 + // If current task was cancelled by a cancellation_token, it would be in cancel pending state. + if ((*_TaskIt)->_IsPendingCancel()) + (*_TaskIt)->_Cancel(true); + else + { + // Tasks created with task_completion_events can be marked as async, (we do this in when_any and when_all + // if one of the tasks involved is an async task). Since continuations of async tasks can execute inline, we + // need to run continuations after the lock is released. + (*_TaskIt)->_FinalizeAndRunContinuations(_M_Impl->_M_value.Get()); + } +#else + // Tasks created with task_completion_events can be marked as async, (we do this in when_any and when_all + // if one of the tasks involved is an async task). Since continuations of async tasks can execute inline, we + // need to run continuations after the lock is released. + (*_TaskIt)->_FinalizeAndRunContinuations(_M_Impl->_M_value); +#endif + } + if (_M_Impl->_HasUserException()) + { + _M_Impl->_M_exceptionHolder.reset(); + } + return true; + } + + return false; + } +#if _MSC_VER >= 1800 + + template + __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result + bool set_exception(_E _Except) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + // It is important that _CAPTURE_CALLSTACK() evaluate to the instruction after the call instruction for set_exception. + return _Cancel(std::make_exception_ptr(_Except), _CAPTURE_CALLSTACK()); + } +#endif + + /// + /// Propagates an exception to all tasks associated with this event. + /// + /// + /// The exception_ptr that indicates the exception to set this event with. + /// + /**/ + __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result + bool set_exception(std::exception_ptr _ExceptionPtr) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + // It is important that _ReturnAddress() evaluate to the instruction after the call instruction for set_exception. +#if _MSC_VER >= 1800 + return _Cancel(_ExceptionPtr, _CAPTURE_CALLSTACK()); +#else + return _Cancel(_ExceptionPtr, _ReturnAddress()); +#endif + } + + /// + /// Internal method to cancel the task_completion_event. Any task created using this event will be marked as canceled if it has + /// not already been set. + /// + bool _Cancel() const + { + // Cancel with the stored exception if one exists. + return _CancelInternal(); + } + + /// + /// Internal method to cancel the task_completion_event with the exception provided. Any task created using this event will be canceled + /// with the same exception. + /// + template +#if _MSC_VER >= 1800 + bool _Cancel(_ExHolderType _ExHolder, const details::_TaskCreationCallstack &_SetExceptionAddressHint = details::_TaskCreationCallstack()) const +#else + bool _Cancel(_ExHolderType _ExHolder, void* _SetExceptionAddressHint = nullptr) const +#endif + { + (void)_SetExceptionAddressHint; + bool _Canceled; +#if _MSC_VER >= 1800 + if(_StoreException(_ExHolder, _SetExceptionAddressHint)) +#else + if (_StoreException(_ExHolder)) +#endif + { + _Canceled = _CancelInternal(); + _CONCRT_ASSERT(_Canceled); + } + else + { + _Canceled = false; + } + return _Canceled; + } + + /// + /// Internal method that stores an exception in the task completion event. This is used internally by when_any. + /// Note, this does not cancel the task completion event. A task completion event with a stored exception + /// can bet set() successfully. If it is canceled, it will cancel with the stored exception, if one is present. + /// + template +#if _MSC_VER >= 1800 + bool _StoreException(_ExHolderType _ExHolder, const details::_TaskCreationCallstack &_SetExceptionAddressHint = details::_TaskCreationCallstack()) const +#else + bool _StoreException(_ExHolderType _ExHolder, void* _SetExceptionAddressHint = nullptr) const +#endif + { + details::scoped_lock _LockHolder(_M_Impl->_M_taskListCritSec); + if (!_IsTriggered() && !_M_Impl->_HasUserException()) + { + // Create the exception holder only if we have ensured there we will be successful in setting it onto the + // task completion event. Failing to do so will result in an unobserved task exception. + _M_Impl->_M_exceptionHolder = _ToExceptionHolder(_ExHolder, _SetExceptionAddressHint); + return true; + } + return false; + } + + /// + /// Tests whether current event has been either Set, or Canceled. + /// + bool _IsTriggered() const + { + return _M_Impl->_M_fHasValue || _M_Impl->_M_fIsCanceled; + } + +private: + +#if _MSC_VER >= 1800 + static std::shared_ptr _ToExceptionHolder(const std::shared_ptr& _ExHolder, const details::_TaskCreationCallstack&) +#else + static std::shared_ptr _ToExceptionHolder(const std::shared_ptr& _ExHolder, void*) +#endif + { + return _ExHolder; + } + +#if _MSC_VER >= 1800 + static std::shared_ptr _ToExceptionHolder(std::exception_ptr _ExceptionPtr, const details::_TaskCreationCallstack &_SetExceptionAddressHint) +#else + static std::shared_ptr _ToExceptionHolder(std::exception_ptr _ExceptionPtr, void* _SetExceptionAddressHint) +#endif + { + return std::make_shared(_ExceptionPtr, _SetExceptionAddressHint); + } + + template friend class task; // task can register itself with the event by calling the private _RegisterTask + template friend class task_completion_event; + + typedef typename details::_Task_completion_event_impl<_ResultType>::_TaskList _TaskList; + + /// + /// Cancels the task_completion_event. + /// + bool _CancelInternal() const + { + // Cancellation of task completion events is an internal only utility. Our usage is such that _CancelInternal + // will never be invoked if the task completion event has been set. + _CONCRT_ASSERT(!_M_Impl->_M_fHasValue); + if (_M_Impl->_M_fIsCanceled) + { + return false; + } + + _TaskList _Tasks; + bool _Cancel = false; + { + details::scoped_lock _LockHolder(_M_Impl->_M_taskListCritSec); + _CONCRT_ASSERT(!_M_Impl->_M_fHasValue); + if (!_M_Impl->_M_fIsCanceled) + { + _M_Impl->_M_fIsCanceled = true; + _Tasks.swap(_M_Impl->_M_tasks); + _Cancel = true; + } + } + + bool _UserException = _M_Impl->_HasUserException(); + + if (_Cancel) + { + for (auto _TaskIt = _Tasks.begin(); _TaskIt != _Tasks.end(); ++_TaskIt) + { + // Need to call this after the lock is released. See comments in set(). + if (_UserException) + { + (*_TaskIt)->_CancelWithExceptionHolder(_M_Impl->_M_exceptionHolder, true); + } + else + { + (*_TaskIt)->_Cancel(true); + } + } + } + return _Cancel; + } + + /// + /// Register a task with this event. This function is called when a task is constructed using + /// a task_completion_event. + /// + void _RegisterTask(const typename details::_Task_ptr<_ResultType>::_Type & _TaskParam) + { + details::scoped_lock _LockHolder(_M_Impl->_M_taskListCritSec); +#if _MSC_VER < 1800 + _TaskParam->_SetScheduledEvent(); +#endif + //If an exception was already set on this event, then cancel the task with the stored exception. + if (_M_Impl->_HasUserException()) + { + _TaskParam->_CancelWithExceptionHolder(_M_Impl->_M_exceptionHolder, true); + } + else if (_M_Impl->_M_fHasValue) + { +#if _MSC_VER >= 1800 + _TaskParam->_FinalizeAndRunContinuations(_M_Impl->_M_value.Get()); +#else + _TaskParam->_FinalizeAndRunContinuations(_M_Impl->_M_value); +#endif + } + else + { + _M_Impl->_M_tasks.push_back(_TaskParam); + } + } + + std::shared_ptr> _M_Impl; +}; + +/// +/// The task_completion_event class allows you to delay the execution of a task until a condition is satisfied, +/// or start a task in response to an external event. +/// +/// +/// Use a task created from a task completion event when your scenario requires you to create a task that will complete, and +/// thereby have its continuations scheduled for execution, at some point in the future. The task_completion_event must +/// have the same type as the task you create, and calling the set method on the task completion event with a value of that type +/// will cause the associated task to complete, and provide that value as a result to its continuations. +/// If the task completion event is never signaled, any tasks created from it will be canceled when it is destructed. +/// task_completion_event behaves like a smart pointer, and should be passed by value. +/// +/// +/**/ +template<> +class task_completion_event +{ +public: + /// + /// Sets the task completion event. + /// + /// + /// The method returns true if it was successful in setting the event. It returns false if the event is already set. + /// + /// + /// In the presence of multiple or concurrent calls to set, only the first call will succeed and its result (if any) will be stored in the + /// task completion event. The remaining sets are ignored and the method will return false. When you set a task completion event, all the + /// tasks created from that event will immediately complete, and its continuations, if any, will be scheduled. Task completion objects that have + /// a other than void will pass the value to their continuations. + /// + /**/ + bool set() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + return _M_unitEvent.set(details::_Unit_type()); + } +#if _MSC_VER >= 1800 + + template + __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result + bool set_exception(_E _Except) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + return _M_unitEvent._Cancel(std::make_exception_ptr(_Except), _CAPTURE_CALLSTACK()); + } +#endif + + /// + /// Propagates an exception to all tasks associated with this event. + /// + /// + /// The exception_ptr that indicates the exception to set this event with. + /// + /**/ + __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result + bool set_exception(std::exception_ptr _ExceptionPtr) const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + // It is important that _ReturnAddress() evaluate to the instruction after the call instruction for set_exception. +#if _MSC_VER >= 1800 + return _M_unitEvent._Cancel(_ExceptionPtr, _CAPTURE_CALLSTACK()); +#else + return _M_unitEvent._Cancel(_ExceptionPtr, _ReturnAddress()); +#endif + } + + /// + /// Cancel the task_completion_event. Any task created using this event will be marked as canceled if it has + /// not already been set. + /// + void _Cancel() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas + { + _M_unitEvent._Cancel(); + } + + /// + /// Cancel the task_completion_event with the exception holder provided. Any task created using this event will be canceled + /// with the same exception. + /// + void _Cancel(const std::shared_ptr& _ExHolder) const + { + _M_unitEvent._Cancel(_ExHolder); + } + + /// + /// Method that stores an exception in the task completion event. This is used internally by when_any. + /// Note, this does not cancel the task completion event. A task completion event with a stored exception + /// can bet set() successfully. If it is canceled, it will cancel with the stored exception, if one is present. + /// + bool _StoreException(const std::shared_ptr& _ExHolder) const + { + return _M_unitEvent._StoreException(_ExHolder); + } + + /// + /// Test whether current event has been either Set, or Canceled. + /// + bool _IsTriggered() const + { + return _M_unitEvent._IsTriggered(); + } + +private: + template friend class task; // task can register itself with the event by calling the private _RegisterTask + + /// + /// Register a task with this event. This function is called when a task is constructed using + /// a task_completion_event. + /// + void _RegisterTask(details::_Task_ptr::_Type _TaskParam) + { + _M_unitEvent._RegisterTask(_TaskParam); + } + + // The void event contains an event a dummy type so common code can be used for events with void and non-void results. + task_completion_event _M_unitEvent; +}; +namespace details +{ + // + // Compile-time validation helpers + // + + // Task constructor validation: issue helpful diagnostics for common user errors. Do not attempt full validation here. + // + // Anything callable is fine + template + auto _IsValidTaskCtor(_Ty _Param, int, int, int, int, int, int, int) -> typename decltype(_Param(), std::true_type()); + + // Anything callable with a task return value is fine + template + auto _IsValidTaskCtor(_Ty _Param, int, int, int, int, int, int, ...) -> typename decltype(_Param(stdx::declval*>()), std::true_type()); + + // Anything callable with a return value is fine + template + auto _IsValidTaskCtor(_Ty _Param, int, int, int, int, int, ...) -> typename decltype(_Param(stdx::declval<_ReturnType*>()), std::true_type()); + + // Anything that has GetResults is fine: this covers AsyncAction* + template + auto _IsValidTaskCtor(_Ty _Param, int, int, int, int, ...) -> typename decltype(_Param->GetResults(), std::true_type()); + + // Anything that has GetResults(TResult_abi*) is fine: this covers AsyncOperation* + template + auto _IsValidTaskCtor(_Ty _Param, int, int, int, ...) -> typename decltype(_Param->GetResults(stdx::declval()))*>()), std::true_type()); + + // Allow parameters with set: this covers task_completion_event + template + auto _IsValidTaskCtor(_Ty _Param, int, int, ...) -> typename decltype(_Param.set(stdx::declval<_ReturnType>()), std::true_type()); + + template + auto _IsValidTaskCtor(_Ty _Param, int, ...) -> typename decltype(_Param.set(), std::true_type()); + + // All else is invalid + template + std::false_type _IsValidTaskCtor(_Ty _Param, ...); + + template + void _ValidateTaskConstructorArgs(_Ty _Param) + { + (void)_Param; + static_assert(std::is_same(_Param, 0, 0, 0, 0, 0, 0, 0)), std::true_type>::value, + "incorrect argument for task constructor; can be a callable object, an asynchronous operation, or a task_completion_event" + ); + static_assert(!(std::is_same<_Ty, _ReturnType>::value && details::_IsIAsyncInfo<_Ty>::_Value), + "incorrect template argument for task; consider using the return type of the async operation"); + } + // Helpers for create_async validation + // + // A parameter lambda taking no arguments is valid + template + static auto _IsValidCreateAsync(_Ty _Param, int, int, int, int, int, int, int, int) -> typename decltype(_Param(), std::true_type()); + + // A parameter lambda taking a result argument is valid + template + static auto _IsValidCreateAsync(_Ty _Param, int, int, int, int, int, int, int, ...) -> typename decltype(_Param(stdx::declval<_ReturnType*>()), std::true_type()); + + // A parameter lambda taking an cancellation_token argument is valid + template + static auto _IsValidCreateAsync(_Ty _Param, int, int, int, int, int, int, ...) -> typename decltype(_Param(Concurrency::cancellation_token::none()), std::true_type()); + + // A parameter lambda taking an cancellation_token argument and a result argument is valid + template + static auto _IsValidCreateAsync(_Ty _Param, int, int, int, int, int, ...) -> typename decltype(_Param(Concurrency::cancellation_token::none(), stdx::declval<_ReturnType*>()), std::true_type()); + + // A parameter lambda taking a progress report argument is valid + template + static auto _IsValidCreateAsync(_Ty _Param, int, int, int, int, ...) -> typename decltype(_Param(details::_ProgressReporterCtorArgType()), std::true_type()); + + // A parameter lambda taking a progress report argument and a result argument is valid + template + static auto _IsValidCreateAsync(_Ty _Param, int, int, int, ...) -> typename decltype(_Param(details::_ProgressReporterCtorArgType(), stdx::declval<_ReturnType*>()), std::true_type()); + + // A parameter lambda taking a progress report and a cancellation_token argument is valid + template + static auto _IsValidCreateAsync(_Ty _Param, int, int, ...) -> typename decltype(_Param(details::_ProgressReporterCtorArgType(), Concurrency::cancellation_token::none()), std::true_type()); + + // A parameter lambda taking a progress report and a cancellation_token argument and a result argument is valid + template + static auto _IsValidCreateAsync(_Ty _Param, int, ...) -> typename decltype(_Param(details::_ProgressReporterCtorArgType(), Concurrency::cancellation_token::none(), stdx::declval<_ReturnType*>()), std::true_type()); + + // All else is invalid + template + static std::false_type _IsValidCreateAsync(_Ty _Param, ...); +} + +/// +/// The Parallel Patterns Library (PPL) task class. A task object represents work that can be executed asynchronously, +/// and concurrently with other tasks and parallel work produced by parallel algorithms in the Concurrency Runtime. It produces +/// a result of type on successful completion. Tasks of type task<void> produce no result. +/// A task can be waited upon and canceled independently of other tasks. It can also be composed with other tasks using +/// continuations(then), and join(when_all) and choice(when_any) patterns. +/// +/// +/// The result type of this task. +/// +/// +/// For more information, see . +/// +/**/ +template +class task +{ +public: + /// + /// The type of the result an object of this class produces. + /// + /**/ + typedef _ReturnType result_type; + + /// + /// Constructs a task object. + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + task() : _M_Impl(nullptr) + { + // The default constructor should create a task with a nullptr impl. This is a signal that the + // task is not usable and should throw if any wait(), get() or then() APIs are used. + } + + /// + /// Constructs a task object. + /// + /// + /// The type of the parameter from which the task is to be constructed. + /// + /// + /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> + /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function + /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, + /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. + /// + /// + /// The cancellation token to associate with this task. A task created without a cancellation token cannot be canceled. It implicitly receives + /// the token cancellation_token::none(). + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result + explicit task(_Ty _Param) + { +#if _MSC_VER >= 1800 + task_options _TaskOptions; +#endif + details::_ValidateTaskConstructorArgs<_ReturnType, _Ty>(_Param); + +#if _MSC_VER >= 1800 + _CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); +#else + _CreateImpl(Concurrency::cancellation_token::none()._GetImplValue()); +#endif + // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of the task constructor. +#if _MSC_VER >= 1800 + _SetTaskCreationCallstack(_CAPTURE_CALLSTACK()); +#else + _SetTaskCreationAddressHint(_ReturnAddress()); +#endif + _TaskInitMaybeFunctor(_Param, details::_IsCallable<_ReturnType>(_Param, 0, 0, 0)); + } + + /// + /// Constructs a task object. + /// + /// + /// The type of the parameter from which the task is to be constructed. + /// + /// + /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> + /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function + /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, + /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. + /// + /// + /// The cancellation token to associate with this task. A task created without a cancellation token cannot be canceled. It implicitly receives + /// the token cancellation_token::none(). + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result +#if _MSC_VER >= 1800 + explicit task(_Ty _Param, const task_options &_TaskOptions) +#else + explicit task(_Ty _Param, Concurrency::cancellation_token _Token) +#endif + { + details::_ValidateTaskConstructorArgs<_ReturnType, _Ty>(_Param); + +#if _MSC_VER >= 1800 + _CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); +#else + _CreateImpl(_Token._GetImplValue()); +#endif + // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of the task constructor. +#if _MSC_VER >= 1800 + _SetTaskCreationCallstack(details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : _CAPTURE_CALLSTACK()); +#else + _SetTaskCreationAddressHint(_ReturnAddress()); +#endif + _TaskInitMaybeFunctor(_Param, details::_IsCallable<_ReturnType>(_Param, 0, 0, 0)); + } + + /// + /// Constructs a task object. + /// + /// + /// The source task object. + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + task(const task& _Other) : _M_Impl(_Other._M_Impl) {} + + /// + /// Constructs a task object. + /// + /// + /// The source task object. + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + task(task&& _Other) : _M_Impl(std::move(_Other._M_Impl)) {} + + /// + /// Replaces the contents of one task object with another. + /// + /// + /// The source task object. + /// + /// + /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same + /// actual task as does. + /// + /**/ + task& operator=(const task& _Other) + { + if (this != &_Other) + { + _M_Impl = _Other._M_Impl; + } + return *this; + } + + /// + /// Replaces the contents of one task object with another. + /// + /// + /// The source task object. + /// + /// + /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same + /// actual task as does. + /// + /**/ + task& operator=(task&& _Other) + { + if (this != &_Other) + { + _M_Impl = std::move(_Other._M_Impl); + } + return *this; + } + + /// + /// Adds a continuation task to this task. + /// + /// + /// The type of the function object that will be invoked by this task. + /// + /// + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either result_type or task<result_type>, where result_type is the type + /// of the result this task produces. + /// + /// + /// The newly created continuation task. The result type of the returned task is determined by what returns. + /// + /// + /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// For more information on how to use task continuations to compose asynchronous work, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result + auto then(const _Function& _Func) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType + { +#if _MSC_VER >= 1800 + task_options _TaskOptions; + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); + return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions); +#else + auto _ContinuationTask = _ThenImpl<_ReturnType, _Function>(_Func, nullptr, task_continuation_context::use_default()); + // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of then. + _ContinuationTask._SetTaskCreationAddressHint(_ReturnAddress()); + return _ContinuationTask; +#endif + } + + /// + /// Adds a continuation task to this task. + /// + /// + /// The type of the function object that will be invoked by this task. + /// + /// + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either result_type or task<result_type>, where result_type is the type + /// of the result this task produces. + /// + /// + /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit + /// the token of its antecedent task. + /// + /// + /// The newly created continuation task. The result type of the returned task is determined by what returns. + /// + /// + /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// For more information on how to use task continuations to compose asynchronous work, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result +#if _MSC_VER >= 1800 + auto then(const _Function& _Func, task_options _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType +#else + auto then(const _Function& _Func, Concurrency::cancellation_token _CancellationToken) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType +#endif + { +#if _MSC_VER >= 1800 + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); + return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions); +#else + auto _ContinuationTask = _ThenImpl<_ReturnType, _Function>(_Func, _CancellationToken._GetImplValue(), task_continuation_context::use_default()); + // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of then. + _ContinuationTask._SetTaskCreationAddressHint(_ReturnAddress()); + return _ContinuationTask; +#endif + } +#if _MSC_VER < 1800 + /// + /// Adds a continuation task to this task. + /// + /// + /// The type of the function object that will be invoked by this task. + /// + /// + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either result_type or task<result_type>, where result_type is the type + /// of the result this task produces. + /// + /// + /// A variable that specifies where the continuation should execute. This variable is only useful when used in a + /// Windows Store app. For more information, see task_continuation_context + /// + /// + /// The newly created continuation task. The result type of the returned task is determined by what returns. + /// + /// + /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// For more information on how to use task continuations to compose asynchronous work, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result + auto then(const _Function& _Func, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType + { + auto _ContinuationTask = _ThenImpl<_ReturnType, _Function>(_Func, nullptr, _ContinuationContext); + // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of then. + _ContinuationTask._SetTaskCreationAddressHint(_ReturnAddress()); + return _ContinuationTask; + } +#endif + /// + /// Adds a continuation task to this task. + /// + /// + /// The type of the function object that will be invoked by this task. + /// + /// + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either result_type or task<result_type>, where result_type is the type + /// of the result this task produces. + /// + /// + /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit + /// the token of its antecedent task. + /// + /// + /// A variable that specifies where the continuation should execute. This variable is only useful when used in a + /// Windows Store app. For more information, see task_continuation_context + /// + /// + /// The newly created continuation task. The result type of the returned task is determined by what returns. + /// + /// + /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// For more information on how to use task continuations to compose asynchronous work, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result + auto then(const _Function& _Func, Concurrency::cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType + { +#if _MSC_VER >= 1800 + task_options _TaskOptions(_CancellationToken, _ContinuationContext); + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); + return _ThenImpl<_ReturnType, _Function>(_Func, _TaskOptions); +#else + auto _ContinuationTask = _ThenImpl<_ReturnType, _Function>(_Func, _CancellationToken._GetImplValue(), _ContinuationContext); + // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of then. + _ContinuationTask._SetTaskCreationAddressHint(_ReturnAddress()); + return _ContinuationTask; +#endif + } + + /// + /// Waits for this task to reach a terminal state. It is possible for wait to execute the task inline, if all of the tasks + /// dependencies are satisfied, and it has not already been picked up for execution by a background worker. + /// + /// + /// A task_status value which could be either completed or canceled. If the task encountered an exception + /// during execution, or an exception was propagated to it from an antecedent task, wait will throw that exception. + /// + /**/ + task_status wait() const + { + if (_M_Impl == nullptr) + { + throw Concurrency::invalid_operation("wait() cannot be called on a default constructed task."); + } + + return _M_Impl->_Wait(); + } + + /// + /// Returns the result this task produced. If the task is not in a terminal state, a call to get will wait for the task to + /// finish. This method does not return a value when called on a task with a result_type of void. + /// + /// + /// The result of the task. + /// + /// + /// If the task is canceled, a call to get will throw a task_canceled exception. If the task + /// encountered an different exception or an exception was propagated to it from an antecedent task, a call to get will throw that exception. + /// + /**/ + _ReturnType get() const + { + if (_M_Impl == nullptr) + { + throw Concurrency::invalid_operation("get() cannot be called on a default constructed task."); + } + + if (_M_Impl->_Wait() == Concurrency::canceled) + { + throw Concurrency::task_canceled(); + } + + return _M_Impl->_GetResult(); + } +#if _MSC_VER >= 1800 + /// + /// Determines if the task is completed. + /// + /// + /// True if the task has completed, false otherwise. + /// + /// + /// The function returns true if the task is completed or canceled (with or without user exception). + /// + bool is_done() const + { + if (!_M_Impl) + { + throw Concurrency::invalid_operation("is_done() cannot be called on a default constructed task."); + } + + return _M_Impl->_IsDone(); + } + + /// + /// Returns the scheduler for this task + /// + /// + /// A pointer to the scheduler + /// + Concurrency::scheduler_ptr scheduler() const + { + if (!_M_Impl) + { + throw Concurrency::invalid_operation("scheduler() cannot be called on a default constructed task."); + } + + return _M_Impl->_GetScheduler(); + } +#endif + /// + /// Determines whether the task unwraps a Windows Runtime IAsyncInfo interface or is descended from such a task. + /// + /// + /// true if the task unwraps an IAsyncInfo interface or is descended from such a task, false otherwise. + /// + /**/ + bool is_apartment_aware() const + { + if (_M_Impl == nullptr) + { + throw Concurrency::invalid_operation("is_apartment_aware() cannot be called on a default constructed task."); + } + return _M_Impl->_IsApartmentAware(); + } + + /// + /// Determines whether two task objects represent the same internal task. + /// + /// + /// true if the objects refer to the same underlying task, and false otherwise. + /// + /**/ + bool operator==(const task<_ReturnType>& _Rhs) const + { + return (_M_Impl == _Rhs._M_Impl); + } + + /// + /// Determines whether two task objects represent different internal tasks. + /// + /// + /// true if the objects refer to different underlying tasks, and false otherwise. + /// + /**/ + bool operator!=(const task<_ReturnType>& _Rhs) const + { + return !operator==(_Rhs); + } + + /// + /// Create an underlying task implementation. + /// +#if _MSC_VER >= 1800 + void _CreateImpl(Concurrency::details::_CancellationTokenState * _Ct, Concurrency::scheduler_ptr _Scheduler) +#else + void _CreateImpl(Concurrency::details::_CancellationTokenState * _Ct) +#endif + { + _CONCRT_ASSERT(_Ct != nullptr); +#if _MSC_VER >= 1800 + _M_Impl = details::_Task_ptr<_ReturnType>::_Make(_Ct, _Scheduler); +#else + _M_Impl = details::_Task_ptr<_ReturnType>::_Make(_Ct); +#endif + if (_Ct != Concurrency::details::_CancellationTokenState::_None()) + { +#if _MSC_VER >= 1800 + _M_Impl->_RegisterCancellation(_M_Impl); +#else + _M_Impl->_RegisterCancellation(); +#endif + } + } + + /// + /// Return the underlying implementation for this task. + /// + const typename details::_Task_ptr<_ReturnType>::_Type & _GetImpl() const + { + return _M_Impl; + } + + /// + /// Set the implementation of the task to be the supplied implementaion. + /// + void _SetImpl(const typename details::_Task_ptr<_ReturnType>::_Type & _Impl) + { + _CONCRT_ASSERT(_M_Impl == nullptr); + _M_Impl = _Impl; + } + + /// + /// Set the implementation of the task to be the supplied implementaion using a move instead of a copy. + /// + void _SetImpl(typename details::_Task_ptr<_ReturnType>::_Type && _Impl) + { + _CONCRT_ASSERT(_M_Impl == nullptr); + _M_Impl = std::move(_Impl); + } + + /// + /// Sets a property determining whether the task is apartment aware. + /// + void _SetAsync(bool _Async = true) + { + _GetImpl()->_SetAsync(_Async); + } + + /// + /// Sets a field in the task impl to the return address for calls to the task constructors and the then method. + /// +#if _MSC_VER >= 1800 + void _SetTaskCreationCallstack(const details::_TaskCreationCallstack &_callstack) + { + _GetImpl()->_SetTaskCreationCallstack(_callstack); + } +#else + void _SetTaskCreationAddressHint(void* _Address) + { + _GetImpl()->_SetTaskCreationAddressHint(_Address); + } +#endif + /// + /// An internal version of then that takes additional flags and always execute the continuation inline by default. + /// When _ForceInline is set to false, continuations inlining will be limited to default _DefaultAutoInline. + /// This function is Used for runtime internal continuations only. + /// + template +#if _MSC_VER >= 1800 + auto _Then(const _Function& _Func, Concurrency::details::_CancellationTokenState *_PTokenState, + details::_TaskInliningMode _InliningMode = Concurrency::details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType + { + // inherit from antecedent + auto _Scheduler = _GetImpl()->_GetScheduler(); + + return _ThenImpl<_ReturnType, _Function>(_Func, _PTokenState, task_continuation_context::use_default(), _Scheduler, _CAPTURE_CALLSTACK(), _InliningMode); + } +#else + auto _Then(const _Function& _Func, Concurrency::details::_CancellationTokenState *_PTokenState, bool _Aggregating, + details::_TaskInliningMode _InliningMode = Concurrency::details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, _ReturnType>::_TaskOfType + { + return _ThenImpl<_ReturnType, _Function>(_Func, _PTokenState, task_continuation_context::use_default(), _Aggregating, _InliningMode); + } +#endif + +private: + template friend class task; + + // A helper class template that transforms an intial task lambda returns void into a lambda that returns a non-void type (details::_Unit_type is used + // to substitute for void). This is to minimize the special handling required for 'void'. + template + class _Init_func_transformer + { + public: + static auto _Perform(std::function _Func) -> decltype(_Func) + { + return _Func; + } + }; + + template<> + class _Init_func_transformer + { + public: + static auto _Perform(std::function _Func) -> decltype(details::_MakeVoidToUnitFunc(_Func)) + { + return details::_MakeVoidToUnitFunc(_Func); + } + }; + + // The task handle type used to construct an 'initial task' - a task with no dependents. + template + struct _InitialTaskHandle : + details::_PPLTaskHandle<_ReturnType, _InitialTaskHandle<_InternalReturnType, _Function, _TypeSelection>, details::_UnrealizedChore> + { + _Function _M_function; + _InitialTaskHandle(const typename details::_Task_ptr<_ReturnType>::_Type & _TaskImpl, const _Function & _Function) : _M_function(_Function), _PPLTaskHandle(_TaskImpl) + { + } + virtual ~_InitialTaskHandle() {} + +#if _MSC_VER >= 1800 + template + auto _LogWorkItemAndInvokeUserLambda(_Func && _func, _RetArg && _retArg) const -> decltype(_func(std::forward<_RetArg>(_retArg))) + { + details::_TaskWorkItemRAIILogger _LogWorkItem(this->_M_pTask->_M_taskEventLogger); + return _func(std::forward<_RetArg>(_retArg)); + } +#endif + + void _Perform() const + { + _Init(_TypeSelection()); + } +#if _MSC_VER >= 1800 + + void _SyncCancelAndPropagateException() const + { + this->_M_pTask->_Cancel(true); + } +#endif + // + // Overload 0: returns _InternalReturnType + // + // This is the most basic task with no unwrapping + // + void _Init(details::_TypeSelectorNoAsync) const + { + _ReturnType retVal; +#if _MSC_VER >= 1800 + HRESULT hr = _LogWorkItemAndInvokeUserLambda(_Init_func_transformer<_InternalReturnType>::_Perform(_M_function), &retVal); +#else + HRESULT hr = _Init_func_transformer<_InternalReturnType>::_Perform(_M_function)(&retVal); +#endif + if (FAILED(hr)) throw std::make_exception_ptr(hr); + _M_pTask->_FinalizeAndRunContinuations(retVal); + } + + // + // Overload 1: returns IAsyncOperation<_InternalReturnType>* + // or + // returns task<_InternalReturnType> + // + // This is task whose functor returns an async operation or a task which will be unwrapped for continuation + // Depending on the output type, the right _AsyncInit gets invoked + // + void _Init(details::_TypeSelectorAsyncTask) const + { + task<_InternalReturnType> retVal; +#if _MSC_VER >= 1800 + HRESULT hr = _LogWorkItemAndInvokeUserLambda(_M_function, &retVal); +#else + HRESULT hr = _M_function(&retVal); +#endif + if (FAILED(hr)) throw std::make_exception_ptr(hr); + details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(_M_pTask, retVal); + } + void _Init(details::_TypeSelectorAsyncOperation) const + { + _ReturnType retVal; +#if _MSC_VER >= 1800 + HRESULT hr = _LogWorkItemAndInvokeUserLambda(_M_function, &retVal); +#else + HRESULT hr = _M_function(&retVal); +#endif + if (FAILED(hr)) throw std::make_exception_ptr(hr); + details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(_M_pTask, + Microsoft::WRL::Make>(retVal).Get()); + } + + // + // Overload 2: returns IAsyncAction* + // + // This is task whose functor returns an async action which will be unwrapped for continuation + // + void _Init(details::_TypeSelectorAsyncAction) const + { + _ReturnType retVal; +#if _MSC_VER >= 1800 + HRESULT hr = _LogWorkItemAndInvokeUserLambda(_M_function, &retVal); +#else + HRESULT hr = _M_function(&retVal); +#endif + if (FAILED(hr)) throw std::make_exception_ptr(hr); + details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(_M_pTask, Microsoft::WRL::Make(retVal).Get()); + } + + // + // Overload 3: returns IAsyncOperationWithProgress<_InternalReturnType, _ProgressType>* + // + // This is task whose functor returns an async operation with progress which will be unwrapped for continuation + // + void _Init(details::_TypeSelectorAsyncOperationWithProgress) const + { + typedef details::_GetProgressType::_Value _ProgressType; + _ReturnType retVal; +#if _MSC_VER >= 1800 + HRESULT hr = _LogWorkItemAndInvokeUserLambda(_M_function, &retVal); +#else + HRESULT hr = _M_function(&retVal); +#endif + if (FAILED(hr)) throw std::make_exception_ptr(hr); + details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(_M_pTask, + Microsoft::WRL::Make>(retVal).Get()); + } + + // + // Overload 4: returns IAsyncActionWithProgress<_ProgressType>* + // + // This is task whose functor returns an async action with progress which will be unwrapped for continuation + // + void _Init(details::_TypeSelectorAsyncActionWithProgress) const + { + typedef details::_GetProgressType::_Value _ProgressType; + _ReturnType retVal; +#if _MSC_VER >= 1800 + HRESULT hr = _LogWorkItemAndInvokeUserLambda(_M_function, &retVal); +#else + HRESULT hr = _M_function(&retVal); +#endif + if (FAILED(hr)) throw std::make_exception_ptr(hr); + details::_Task_impl_base::_AsyncInit<_ReturnType, _InternalReturnType>(_M_pTask, + Microsoft::WRL::Make>(retVal).Get()); + } + }; + + /// + /// A helper class template that transforms a continuation lambda that either takes or returns void, or both, into a lambda that takes and returns a + /// non-void type (details::_Unit_type is used to substitute for void). This is to minimize the special handling required for 'void'. + /// + template + class _Continuation_func_transformer + { + public: + static auto _Perform(std::function _Func) -> decltype(_Func) + { + return _Func; + } + }; + + template + class _Continuation_func_transformer + { + public: + static auto _Perform(std::function _Func) -> decltype(details::_MakeUnitToTFunc<_OutType>(_Func)) + { + return details::_MakeUnitToTFunc<_OutType>(_Func); + } + }; + + template + class _Continuation_func_transformer<_InType, void> + { + public: + static auto _Perform(std::function _Func) -> decltype(details::_MakeTToUnitFunc<_InType>(_Func)) + { + return details::_MakeTToUnitFunc<_InType>(_Func); + } + }; + + template<> + class _Continuation_func_transformer + { + public: + static auto _Perform(std::function _Func) -> decltype(details::_MakeUnitToUnitFunc(_Func)) + { + return details::_MakeUnitToUnitFunc(_Func); + } + }; + /// + /// The task handle type used to create a 'continuation task'. + /// + template + struct _ContinuationTaskHandle : + details::_PPLTaskHandle::_Type, + _ContinuationTaskHandle<_InternalReturnType, _ContinuationReturnType, _Function, _IsTaskBased, _TypeSelection>, details::_ContinuationTaskHandleBase> + { + typedef typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_Type _NormalizedContinuationReturnType; + + typename details::_Task_ptr<_ReturnType>::_Type _M_ancestorTaskImpl; + _Function _M_function; + + _ContinuationTaskHandle(const typename details::_Task_ptr<_ReturnType>::_Type & _AncestorImpl, + const typename details::_Task_ptr<_NormalizedContinuationReturnType>::_Type & _ContinuationImpl, + const _Function & _Func, const task_continuation_context & _Context, details::_TaskInliningMode _InliningMode) : +#if _MSC_VER >= 1800 + details::_PPLTaskHandle::_Type, + _ContinuationTaskHandle<_InternalReturnType, _ContinuationReturnType, _Function, _IsTaskBased, _TypeSelection>, details::_ContinuationTaskHandleBase> + ::_PPLTaskHandle(_ContinuationImpl) + , _M_ancestorTaskImpl(_AncestorImpl) + , _M_function(_Func) +#else + _M_ancestorTaskImpl(_AncestorImpl), _PPLTaskHandle(_ContinuationImpl), _M_function(_Func) +#endif + { + _M_isTaskBasedContinuation = _IsTaskBased::value; + _M_continuationContext = _Context; + _M_continuationContext._Resolve(_AncestorImpl->_IsApartmentAware()); + _M_inliningMode = _InliningMode; + } + + virtual ~_ContinuationTaskHandle() {} + +#if _MSC_VER >= 1800 + template + auto _LogWorkItemAndInvokeUserLambda(_Func && _func, _Arg && _value, _RetArg && _retArg) const -> decltype(_func(std::forward<_Arg>(_value), std::forward<_RetArg>(_retArg))) + { + details::_TaskWorkItemRAIILogger _LogWorkItem(this->_M_pTask->_M_taskEventLogger); + return _func(std::forward<_Arg>(_value), std::forward<_RetArg>(_retArg)); + } +#endif + + void _Perform() const + { + _Continue(_IsTaskBased(), _TypeSelection()); + } + +#if _MSC_VER >= 1800 + void _SyncCancelAndPropagateException() const + { + if (_M_ancestorTaskImpl->_HasUserException()) + { + // If the ancestor encountered an exception, transfer the exception to the continuation + // This traverses down the tree to propagate the exception. + this->_M_pTask->_CancelWithExceptionHolder(_M_ancestorTaskImpl->_GetExceptionHolder(), true); + } + else + { + // If the ancestor was canceled, then your own execution should be canceled. + // This traverses down the tree to cancel it. + this->_M_pTask->_Cancel(true); + } + } +#endif + + // + // Overload 0-0: _InternalReturnType -> _TaskType + // + // This is a straight task continuation which simply invokes its target with the ancestor's completion argument + // + void _Continue(std::false_type, details::_TypeSelectorNoAsync) const + { + _NormalizedContinuationReturnType retVal; +#if _MSC_VER >= 1800 + HRESULT hr = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _ContinuationReturnType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult(), &retVal); +#else + HRESULT hr =_Continuation_func_transformer<_InternalReturnType, _ContinuationReturnType>::_Perform(_M_function)(_M_ancestorTaskImpl->_GetResult(), &retVal); +#endif + if (FAILED(hr)) throw std::make_exception_ptr(hr); + _M_pTask->_FinalizeAndRunContinuations(retVal); + } + + // + // Overload 0-1: _InternalReturnType -> IAsyncOperation<_TaskType>* + // or + // _InternalReturnType -> task<_TaskType> + // + // This is a straight task continuation which returns an async operation or a task which will be unwrapped for continuation + // Depending on the output type, the right _AsyncInit gets invoked + // + void _Continue(std::false_type, details::_TypeSelectorAsyncTask) const + { + typedef typename details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; + _FuncOutputType retVal; +#if _MSC_VER >= 1800 + HRESULT hr = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult(), &retVal); +#else + HRESULT hr = _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function)(_M_ancestorTaskImpl->_GetResult(), &retVal); +#endif + if (FAILED(hr)) throw std::make_exception_ptr(hr); + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( + _M_pTask, + retVal + ); + } + void _Continue(std::false_type, details::_TypeSelectorAsyncOperation) const + { + typedef typename details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; + _FuncOutputType retVal; +#if _MSC_VER >= 1800 + HRESULT hr = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult(), &retVal); +#else + HRESULT hr = _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function)(_M_ancestorTaskImpl->_GetResult(), &retVal); +#endif + if (FAILED(hr)) throw std::make_exception_ptr(hr); + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( + _M_pTask, + Microsoft::WRL::Make>(retVal).Get()); + } + + // + // Overload 0-2: _InternalReturnType -> IAsyncAction* + // + // This is a straight task continuation which returns an async action which will be unwrapped for continuation + // + void _Continue(std::false_type, details::_TypeSelectorAsyncAction) const + { + typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; + _FuncOutputType retVal; +#if _MSC_VER >= 1800 + HRESULT hr = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult(), &retVal); +#else + HRESULT hr = _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function)(_M_ancestorTaskImpl->_GetResult(), &retVal); +#endif + if (FAILED(hr)) throw std::make_exception_ptr(hr); + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( + _M_pTask, + Microsoft::WRL::Make( + retVal).Get()); + } + + // + // Overload 0-3: _InternalReturnType -> IAsyncOperationWithProgress<_TaskType, _ProgressType>* + // + // This is a straight task continuation which returns an async operation with progress which will be unwrapped for continuation + // + void _Continue(std::false_type, details::_TypeSelectorAsyncOperationWithProgress) const + { + typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; + + _FuncOutputType _OpWithProgress; +#if _MSC_VER >= 1800 + HRESULT hr = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult(), &_OpWithProgress); +#else + HRESULT hr = _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function)(_M_ancestorTaskImpl->_GetResult(), &_OpWithProgress); +#endif + typedef details::_GetProgressType::_Value _ProgressType; + + if (FAILED(hr)) throw std::make_exception_ptr(hr); + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( + _M_pTask, + Microsoft::WRL::Make>(_OpWithProgress).Get()); + } + + // + // Overload 0-4: _InternalReturnType -> IAsyncActionWithProgress<_ProgressType>* + // + // This is a straight task continuation which returns an async action with progress which will be unwrapped for continuation + // + void _Continue(std::false_type, details::_TypeSelectorAsyncActionWithProgress) const + { + typedef details::_FunctionTypeTraits<_Function, _InternalReturnType>::_FuncRetType _FuncOutputType; + + _FuncOutputType _OpWithProgress; +#if _MSC_VER >= 1800 + HRESULT hr = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function), _M_ancestorTaskImpl->_GetResult(), &_OpWithProgress); +#else + HRESULT hr = _Continuation_func_transformer<_InternalReturnType, _FuncOutputType>::_Perform(_M_function)(_M_ancestorTaskImpl->_GetResult(), &_OpWithProgress); +#endif + typedef details::_GetProgressType::_Value _ProgressType; + + if (FAILED(hr)) throw std::make_exception_ptr(hr); + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>( + _M_pTask, + Microsoft::WRL::Make>(_OpWithProgress).Get()); + } + + + // + // Overload 1-0: task<_InternalReturnType> -> _TaskType + // + // This is an exception handling type of continuation which takes the task rather than the task's result. + // + void _Continue(std::true_type, details::_TypeSelectorNoAsync) const + { + typedef task<_InternalReturnType> _FuncInputType; + task<_InternalReturnType> _ResultTask; + _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); + _NormalizedContinuationReturnType retVal; +#if _MSC_VER >= 1800 + HRESULT hr = _LogWorkItemAndInvokeUserLambda(_Continuation_func_transformer<_FuncInputType, _ContinuationReturnType>::_Perform(_M_function), std::move(_ResultTask), &retVal); +#else + HRESULT hr = _Continuation_func_transformer<_FuncInputType, _ContinuationReturnType>::_Perform(_M_function)(std::move(_ResultTask), &retVal); +#endif + if (FAILED(hr)) throw std::make_exception_ptr(hr); + _M_pTask->_FinalizeAndRunContinuations(retVal); + } + + // + // Overload 1-1: task<_InternalReturnType> -> IAsyncOperation<_TaskType>^ + // or + // task<_TaskType> + // + // This is an exception handling type of continuation which takes the task rather than + // the task's result. It also returns an async operation or a task which will be unwrapped + // for continuation + // + void _Continue(std::true_type, details::_TypeSelectorAsyncTask) const + { + // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. + task<_InternalReturnType> _ResultTask; + _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); + _ContinuationReturnType retVal; +#if _MSC_VER >= 1800 + HRESULT hr = _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask), &retVal); +#else + HRESULT hr = _M_function(std::move(_ResultTask), &retVal); +#endif + if (FAILED(hr)) throw std::make_exception_ptr(hr); + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(_M_pTask, retVal); + } + void _Continue(std::true_type, details::_TypeSelectorAsyncOperation) const + { + // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. + task<_InternalReturnType> _ResultTask; + _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); + _ContinuationReturnType retVal; +#if _MSC_VER >= 1800 + HRESULT hr = _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask), &retVal); +#else + HRESULT hr = _M_function(std::move(_ResultTask), &retVal); +#endif + if (FAILED(hr)) throw std::make_exception_ptr(hr); + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(_M_pTask, + Microsoft::WRL::Make>(retVal)); + } + + // + // Overload 1-2: task<_InternalReturnType> -> IAsyncAction* + // + // This is an exception handling type of continuation which takes the task rather than + // the task's result. It also returns an async action which will be unwrapped for continuation + // + void _Continue(std::true_type, details::_TypeSelectorAsyncAction) const + { + // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. + task<_InternalReturnType> _ResultTask; + _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); + _ContinuationReturnType retVal; +#if _MSC_VER >= 1800 + HRESULT hr = _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask), &retVal); +#else + HRESULT hr = _M_function(std::move(_ResultTask), &retVal); +#endif + if (FAILED(hr)) throw std::make_exception_ptr(hr); + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(_M_pTask, + Microsoft::WRL::Make(retVal)); + } + + // + // Overload 1-3: task<_InternalReturnType> -> IAsyncOperationWithProgress<_TaskType, _ProgressType>* + // + // This is an exception handling type of continuation which takes the task rather than + // the task's result. It also returns an async operation with progress which will be unwrapped + // for continuation + // + void _Continue(std::true_type, details::_TypeSelectorAsyncOperationWithProgress) const + { + // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. + task<_InternalReturnType> _ResultTask; + _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); + + typedef details::_GetProgressType::_Value _ProgressType; + _ContinuationReturnType retVal; +#if _MSC_VER >= 1800 + HRESULT hr = _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask), &retVal); +#else + HRESULT hr = _M_function(std::move(_ResultTask), &retVal); +#endif + if (FAILED(hr)) throw std::make_exception_ptr(hr); + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(_M_pTask, + Microsoft::WRL::Make>(retVal)); + } + + // + // Overload 1-4: task<_InternalReturnType> -> IAsyncActionWithProgress<_ProgressType>* + // + // This is an exception handling type of continuation which takes the task rather than + // the task's result. It also returns an async operation with progress which will be unwrapped + // for continuation + // + void _Continue(std::true_type, details::_TypeSelectorAsyncActionWithProgress) const + { + // The continuation takes a parameter of type task<_Input>, which is the same as the ancestor task. + task<_InternalReturnType> _ResultTask; + _ResultTask._SetImpl(std::move(_M_ancestorTaskImpl)); + + typedef details::_GetProgressType::_Value _ProgressType; + _ContinuationReturnType retVal; +#if _MSC_VER >= 1800 + HRESULT hr = _LogWorkItemAndInvokeUserLambda(_M_function, std::move(_ResultTask), &retVal); +#else + HRESULT hr = _M_function(std::move(_ResultTask), &retVal); +#endif + if (FAILED(hr)) throw std::make_exception_ptr(hr); + details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(_M_pTask, + Microsoft::WRL::Make>(retVal)); + } + }; + /// + /// Initializes a task using a lambda, function pointer or function object. + /// + template + void _TaskInitWithFunctor(const _Function& _Func) + { + typedef details::_InitFunctorTypeTraits<_InternalReturnType, details::_FunctionTypeTraits<_Function, void>::_FuncRetType> _Async_type_traits; + + _M_Impl->_M_fFromAsync = _Async_type_traits::_IsAsyncTask; + _M_Impl->_M_fUnwrappedTask = _Async_type_traits::_IsUnwrappedTaskOrAsync; +#if _MSC_VER >= 1800 + _M_Impl->_M_taskEventLogger._LogScheduleTask(false); +#endif + _M_Impl->_ScheduleTask(new _InitialTaskHandle<_InternalReturnType, _Function, typename _Async_type_traits::_AsyncKind>(_GetImpl(), _Func), Concurrency::details::_NoInline); + } + + /// + /// Initializes a task using a task completion event. + /// + void _TaskInitNoFunctor(task_completion_event<_ReturnType>& _Event) + { + _Event._RegisterTask(_M_Impl); + } + + /// + /// Initializes a task using an asynchronous operation IAsyncOperation* + /// + template + void _TaskInitAsyncOp(details::_AsyncInfoImpl<_OpType, _CompHandlerType, _ResultType>* _AsyncOp) + { + _M_Impl->_M_fFromAsync = true; +#if _MSC_VER < 1800 + _M_Impl->_SetScheduledEvent(); +#endif + // Mark this task as started here since we can set the state in the constructor without acquiring a lock. Once _AsyncInit + // returns a completion could execute concurrently and the task must be fully initialized before that happens. + _M_Impl->_M_TaskState = details::_Task_impl_base::_Started; + // Pass the shared pointer into _AsyncInit for storage in the Async Callback. + details::_Task_impl_base::_AsyncInit<_ReturnType, _Result>(_M_Impl, _AsyncOp); + } + + /// + /// Initializes a task using an asynchronous operation IAsyncOperation* + /// + template + void _TaskInitNoFunctor(ABI::Windows::Foundation::IAsyncOperation<_Result>* _AsyncOp) + { + _TaskInitAsyncOp<_Result>(Microsoft::WRL::Make>(_AsyncOp).Get()); + } + + /// + /// Initializes a task using an asynchronous operation with progress IAsyncOperationWithProgress* + /// + template + void _TaskInitNoFunctor(ABI::Windows::Foundation::IAsyncOperationWithProgress<_Result, _Progress>* _AsyncOp) + { + _TaskInitAsyncOp<_Result>(Microsoft::WRL::Make>(_AsyncOp).Get()); + } + /// + /// Initializes a task using a callable object. + /// + template + void _TaskInitMaybeFunctor(_Function & _Func, std::true_type) + { + _TaskInitWithFunctor<_ReturnType, _Function>(_Func); + } + + /// + /// Initializes a task using a non-callable object. + /// + template + void _TaskInitMaybeFunctor(_Ty & _Param, std::false_type) + { + _TaskInitNoFunctor(_Param); + } +#if _MSC_VER >= 1800 + template + auto _ThenImpl(const _Function& _Func, const task_options& _TaskOptions) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType + { + if (!_M_Impl) + { + throw Concurrency::invalid_operation("then() cannot be called on a default constructed task."); + } + + Concurrency::details::_CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; + auto _Scheduler = _TaskOptions.has_scheduler() ? _TaskOptions.get_scheduler() : _GetImpl()->_GetScheduler(); + auto _CreationStack = details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : details::_TaskCreationCallstack(); + return _ThenImpl<_InternalReturnType, _Function>(_Func, _PTokenState, _TaskOptions.get_continuation_context(), _Scheduler, _CreationStack); + } +#endif + /// + /// The one and only implementation of then for void and non-void tasks. + /// + template +#if _MSC_VER >= 1800 + auto _ThenImpl(const _Function& _Func, Concurrency::details::_CancellationTokenState *_PTokenState, const task_continuation_context& _ContinuationContext, Concurrency::scheduler_ptr _Scheduler, details::_TaskCreationCallstack _CreationStack, + details::_TaskInliningMode _InliningMode = Concurrency::details::_NoInline) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType +#else + auto _ThenImpl(const _Function& _Func, Concurrency::details::_CancellationTokenState *_PTokenState, const task_continuation_context& _ContinuationContext, + bool _Aggregating = false, details::_TaskInliningMode _InliningMode = Concurrency::details::_NoInline) const -> typename details::_ContinuationTypeTraits<_Function, _InternalReturnType>::_TaskOfType +#endif + { + if (_M_Impl == nullptr) + { + throw Concurrency::invalid_operation("then() cannot be called on a default constructed task."); + } + + typedef details::_FunctionTypeTraits<_Function, _InternalReturnType> _Function_type_traits; + typedef details::_TaskTypeTraits _Async_type_traits; + typedef typename _Async_type_traits::_TaskRetType _TaskType; + + // + // A **nullptr** token state indicates that it was not provided by the user. In this case, we inherit the antecedent's token UNLESS this is a + // an exception handling continuation. In that case, we break the chain with a _None. That continuation is never canceled unless the user + // explicitly passes the same token. + // + if (_PTokenState == nullptr) + { +#if _MSC_VER >= 1800 + if (_Function_type_traits::_Takes_task::value) +#else + if (_Function_type_traits::_Takes_task()) +#endif + { + _PTokenState = Concurrency::details::_CancellationTokenState::_None(); + } + else + { + _PTokenState = _GetImpl()->_M_pTokenState; + } + } + + task<_TaskType> _ContinuationTask; +#if _MSC_VER >= 1800 + _ContinuationTask._CreateImpl(_PTokenState, _Scheduler); +#else + _ContinuationTask._CreateImpl(_PTokenState); +#endif + _ContinuationTask._GetImpl()->_M_fFromAsync = (_GetImpl()->_M_fFromAsync || _Async_type_traits::_IsAsyncTask); +#if _MSC_VER < 1800 + _ContinuationTask._GetImpl()->_M_fRuntimeAggregate = _Aggregating; +#endif + _ContinuationTask._GetImpl()->_M_fUnwrappedTask = _Async_type_traits::_IsUnwrappedTaskOrAsync; +#if _MSC_VER >= 1800 + _ContinuationTask._SetTaskCreationCallstack(_CreationStack); +#endif + _GetImpl()->_ScheduleContinuation(new _ContinuationTaskHandle<_InternalReturnType, _TaskType, _Function, typename _Function_type_traits::_Takes_task, typename _Async_type_traits::_AsyncKind>( + _GetImpl(), _ContinuationTask._GetImpl(), _Func, _ContinuationContext, _InliningMode)); + + return _ContinuationTask; + } + + // The underlying implementation for this task + typename details::_Task_ptr<_ReturnType>::_Type _M_Impl; +}; + +/// +/// The Parallel Patterns Library (PPL) task class. A task object represents work that can be executed asynchronously, +/// and concurrently with other tasks and parallel work produced by parallel algorithms in the Concurrency Runtime. It produces +/// a result of type on successful completion. Tasks of type task<void> produce no result. +/// A task can be waited upon and canceled independently of other tasks. It can also be composed with other tasks using +/// continuations(then), and join(when_all) and choice(when_any) patterns. +/// +/// +/// For more information, see . +/// +/**/ +template<> +class task +{ +public: + /// + /// The type of the result an object of this class produces. + /// + /**/ + typedef void result_type; + + /// + /// Constructs a task object. + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + task() : _M_unitTask() + { + // The default constructor should create a task with a nullptr impl. This is a signal that the + // task is not usable and should throw if any wait(), get() or then() APIs are used. + } +#if _MSC_VER < 1800 + /// + /// Constructs a task object. + /// + /// + /// The type of the parameter from which the task is to be constructed. + /// + /// + /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> + /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function + /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, + /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result + explicit task(_Ty _Param) + { + details::_ValidateTaskConstructorArgs(_Param); + + _M_unitTask._CreateImpl(Concurrency::cancellation_token::none()._GetImplValue()); + // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of the task constructor. + _M_unitTask._SetTaskCreationAddressHint(_ReturnAddress()); + + _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param, 0, 0, 0)); + } +#endif + /// + /// Constructs a task object. + /// + /// + /// The type of the parameter from which the task is to be constructed. + /// + /// + /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a task_completion_event<result_type> + /// object, or a Windows::Foundation::IAsyncInfo if you are using tasks in your Windows Store app. The lambda or function + /// object should be a type equivalent to std::function<X(void)>, where X can be a variable of type result_type, + /// task<result_type>, or a Windows::Foundation::IAsyncInfo in Windows Store apps. + /// + /// + /// The cancellation token to associate with this task. A task created without a cancellation token cannot be canceled. It implicitly receives + /// the token cancellation_token::none(). + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result +#if _MSC_VER >= 1800 + explicit task(_Ty _Param, const task_options& _TaskOptions = task_options()) +#else + explicit task(_Ty _Param, Concurrency::cancellation_token _CancellationToken) +#endif + { + details::_ValidateTaskConstructorArgs(_Param); +#if _MSC_VER >= 1800 + _M_unitTask._CreateImpl(_TaskOptions.get_cancellation_token()._GetImplValue(), _TaskOptions.get_scheduler()); +#else + _M_unitTask._CreateImpl(_CancellationToken._GetImplValue()); +#endif + // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of the task constructor. +#if _MSC_VER >= 1800 + _M_unitTask._SetTaskCreationCallstack(details::_get_internal_task_options(_TaskOptions)._M_hasPresetCreationCallstack ? details::_get_internal_task_options(_TaskOptions)._M_presetCreationCallstack : _CAPTURE_CALLSTACK()); +#else + _M_unitTask._SetTaskCreationAddressHint(_ReturnAddress()); +#endif + _TaskInitMaybeFunctor(_Param, details::_IsCallable(_Param, 0, 0, 0)); + } + + /// + /// Constructs a task object. + /// + /// + /// The source task object. + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + task(const task& _Other) : _M_unitTask(_Other._M_unitTask){} + + /// + /// Constructs a task object. + /// + /// + /// The source task object. + /// + /// + /// The default constructor for a task is only present in order to allow tasks to be used within containers. + /// A default constructed task cannot be used until you assign a valid task to it. Methods such as get, wait or then + /// will throw an invalid_argument exception when called on a default constructed task. + /// A task that is created from a task_completion_event will complete (and have its continuations scheduled) when the task + /// completion event is set. + /// The version of the constructor that takes a cancellation token creates a task that can be canceled using the + /// cancellation_token_source the token was obtained from. Tasks created without a cancellation token are not cancelable. + /// Tasks created from a Windows::Foundation::IAsyncInfo interface or a lambda that returns an IAsyncInfo interface + /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created + /// from a lamda that returns a task<result_type> reach their terminal state when the inner task reaches its terminal state, + /// and not when the lamda returns. + /// task behaves like a smart pointer and is safe to pass around by value. It can be accessed by multiple threads + /// without the need for locks. + /// The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available + /// to Windows Store apps. + /// For more information, see . + /// + /**/ + task(task&& _Other) : _M_unitTask(std::move(_Other._M_unitTask)) {} + + /// + /// Replaces the contents of one task object with another. + /// + /// + /// The source task object. + /// + /// + /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same + /// actual task as does. + /// + /**/ + task& operator=(const task& _Other) + { + if (this != &_Other) + { + _M_unitTask = _Other._M_unitTask; + } + return *this; + } + + /// + /// Replaces the contents of one task object with another. + /// + /// + /// The source task object. + /// + /// + /// As task behaves like a smart pointer, after a copy assignment, this task objects represents the same + /// actual task as does. + /// + /**/ + task& operator=(task&& _Other) + { + if (this != &_Other) + { + _M_unitTask = std::move(_Other._M_unitTask); + } + return *this; + } +#if _MSC_VER < 1800 + /// + /// Adds a continuation task to this task. + /// + /// + /// The type of the function object that will be invoked by this task. + /// + /// + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either result_type or task<result_type>, where result_type is the type + /// of the result this task produces. + /// + /// + /// The newly created continuation task. The result type of the returned task is determined by what returns. + /// + /// + /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// For more information on how to use task continuations to compose asynchronous work, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result + auto then(const _Function& _Func) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType + { + auto _ContinuationTask = _M_unitTask._ThenImpl(_Func, nullptr, task_continuation_context::use_default()); + // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of then. + _ContinuationTask._SetTaskCreationAddressHint(_ReturnAddress()); + return _ContinuationTask; + } +#endif + /// + /// Adds a continuation task to this task. + /// + /// + /// The type of the function object that will be invoked by this task. + /// + /// + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either result_type or task<result_type>, where result_type is the type + /// of the result this task produces. + /// + /// + /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit + /// the token of its antecedent task. + /// + /// + /// The newly created continuation task. The result type of the returned task is determined by what returns. + /// + /// + /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// For more information on how to use task continuations to compose asynchronous work, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result +#if _MSC_VER >= 1800 + auto then(const _Function& _Func, task_options _TaskOptions = task_options()) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType + { + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); + return _M_unitTask._ThenImpl(_Func, _TaskOptions); + } +#else + auto then(const _Function& _Func, Concurrency::cancellation_token _CancellationToken) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType + { + auto _ContinuationTask = _M_unitTask._ThenImpl(_Func, _CancellationToken._GetImplValue(), task_continuation_context::use_default()); + // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of then. + _ContinuationTask._SetTaskCreationAddressHint(_ReturnAddress()); + return _ContinuationTask; + } + /// + /// Adds a continuation task to this task. + /// + /// + /// The type of the function object that will be invoked by this task. + /// + /// + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either result_type or task<result_type>, where result_type is the type + /// of the result this task produces. + /// + /// + /// A variable that specifies where the continuation should execute. This variable is only useful when used in a + /// Windows Store app. For more information, see task_continuation_context + /// + /// + /// The newly created continuation task. The result type of the returned task is determined by what returns. + /// + /// + /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// For more information on how to use task continuations to compose asynchronous work, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result + auto then(const _Function& _Func, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType + { + auto _ContinuationTask = _M_unitTask._ThenImpl(_Func, nullptr, _ContinuationContext); + // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of then. + _ContinuationTask._SetTaskCreationAddressHint(_ReturnAddress()); + return _ContinuationTask; + + } +#endif + /// + /// Adds a continuation task to this task. + /// + /// + /// The type of the function object that will be invoked by this task. + /// + /// + /// The continuation function to execute when this task completes. This continuation function must take as input + /// a variable of either result_type or task<result_type>, where result_type is the type + /// of the result this task produces. + /// + /// + /// The cancellation token to associate with the continuation task. A continuation task that is created without a cancellation token will inherit + /// the token of its antecedent task. + /// + /// + /// A variable that specifies where the continuation should execute. This variable is only useful when used in a + /// Windows Store app. For more information, see task_continuation_context + /// + /// + /// The newly created continuation task. The result type of the returned task is determined by what returns. + /// + /// + /// The overloads of then that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available + /// to Windows Store apps. + /// For more information on how to use task continuations to compose asynchronous work, see . + /// + /**/ + template + __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result +#if _MSC_VER >= 1800 + auto then(const _Function& _Func, Concurrency::cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType + { + task_options _TaskOptions(_CancellationToken, _ContinuationContext); + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); + return _M_unitTask._ThenImpl(_Func, _TaskOptions); + } +#else + auto then(const _Function& _Func, Concurrency::cancellation_token _CancellationToken, task_continuation_context _ContinuationContext) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType + { + auto _ContinuationTask = _M_unitTask._ThenImpl(_Func, _CancellationToken._GetImplValue(), _ContinuationContext); + // Do not move the next line out of this function. It is important that _ReturnAddress() evaluate to the the call site of then. + _ContinuationTask._SetTaskCreationAddressHint(_ReturnAddress()); + return _ContinuationTask; + } +#endif + + /// + /// Waits for this task to reach a terminal state. It is possible for wait to execute the task inline, if all of the tasks + /// dependencies are satisfied, and it has not already been picked up for execution by a background worker. + /// + /// + /// A task_status value which could be either completed or canceled. If the task encountered an exception + /// during execution, or an exception was propagated to it from an antecedent task, wait will throw that exception. + /// + /**/ + task_status wait() const + { + return _M_unitTask.wait(); + } + + /// + /// Returns the result this task produced. If the task is not in a terminal state, a call to get will wait for the task to + /// finish. This method does not return a value when called on a task with a result_type of void. + /// + /// + /// If the task is canceled, a call to get will throw a task_canceled exception. If the task + /// encountered an different exception or an exception was propagated to it from an antecedent task, a call to get will throw that exception. + /// + /**/ + void get() const + { + _M_unitTask.get(); + } +#if _MSC_VER >= 1800 + + /// + /// Determines if the task is completed. + /// + /// + /// True if the task has completed, false otherwise. + /// + /// + /// The function returns true if the task is completed or canceled (with or without user exception). + /// + bool is_done() const + { + return _M_unitTask.is_done(); + } + + /// + /// Returns the scheduler for this task + /// + /// + /// A pointer to the scheduler + /// + Concurrency::scheduler_ptr scheduler() const + { + return _M_unitTask.scheduler(); + } +#endif + /// + /// Determines whether the task unwraps a Windows Runtime IAsyncInfo interface or is descended from such a task. + /// + /// + /// true if the task unwraps an IAsyncInfo interface or is descended from such a task, false otherwise. + /// + /**/ + bool is_apartment_aware() const + { + return _M_unitTask.is_apartment_aware(); + } + + /// + /// Determines whether two task objects represent the same internal task. + /// + /// + /// true if the objects refer to the same underlying task, and false otherwise. + /// + /**/ + bool operator==(const task& _Rhs) const + { + return (_M_unitTask == _Rhs._M_unitTask); + } + + /// + /// Determines whether two task objects represent different internal tasks. + /// + /// + /// true if the objects refer to different underlying tasks, and false otherwise. + /// + /**/ + bool operator!=(const task& _Rhs) const + { + return !operator==(_Rhs); + } + + /// + /// Create an underlying task implementation. + /// +#if _MSC_VER >= 1800 + void _CreateImpl(Concurrency::details::_CancellationTokenState * _Ct, Concurrency::scheduler_ptr _Scheduler) + { + _M_unitTask._CreateImpl(_Ct, _Scheduler); + } +#else + void _CreateImpl(Concurrency::details::_CancellationTokenState * _Ct) + { + _M_unitTask._CreateImpl(_Ct); + } +#endif + + /// + /// Return the underlying implementation for this task. + /// + const details::_Task_ptr::_Type & _GetImpl() const + { + return _M_unitTask._M_Impl; + } + + /// + /// Set the implementation of the task to be the supplied implementaion. + /// + void _SetImpl(const details::_Task_ptr::_Type & _Impl) + { + _M_unitTask._SetImpl(_Impl); + } + + /// + /// Set the implementation of the task to be the supplied implementaion using a move instead of a copy. + /// + void _SetImpl(details::_Task_ptr::_Type && _Impl) + { + _M_unitTask._SetImpl(std::move(_Impl)); + } + + /// + /// Sets a property determining whether the task is apartment aware. + /// + void _SetAsync(bool _Async = true) + { + _M_unitTask._SetAsync(_Async); + } + + /// + /// Sets a field in the task impl to the return address for calls to the task constructors and the then method. + /// +#if _MSC_VER >= 1800 + void _SetTaskCreationCallstack(const details::_TaskCreationCallstack &_callstack) + { + _M_unitTask._SetTaskCreationCallstack(_callstack); + } +#else + void _SetTaskCreationAddressHint(void* _Address) + { + _M_unitTask._SetTaskCreationAddressHint(_Address); + } +#endif + + /// + /// An internal version of then that takes additional flags and executes the continuation inline. Used for runtime internal continuations only. + /// + template +#if _MSC_VER >= 1800 + auto _Then(const _Function& _Func, Concurrency::details::_CancellationTokenState *_PTokenState, + details::_TaskInliningMode _InliningMode = Concurrency::details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType + { + // inherit from antecedent + auto _Scheduler = _GetImpl()->_GetScheduler(); + + return _M_unitTask._ThenImpl(_Func, _PTokenState, task_continuation_context::use_default(), _Scheduler, _CAPTURE_CALLSTACK(), _InliningMode); + } +#else + auto _Then(const _Function& _Func, Concurrency::details::_CancellationTokenState *_PTokenState, + bool _Aggregating, details::_TaskInliningMode _InliningMode = Concurrency::details::_ForceInline) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType + { + return _M_unitTask._ThenImpl(_Func, _PTokenState, task_continuation_context::use_default(), _Aggregating, _InliningMode); + } +#endif + +private: + template friend class task; + template friend class task_completion_event; + + /// + /// Initializes a task using a task completion event. + /// + void _TaskInitNoFunctor(task_completion_event& _Event) + { + _M_unitTask._TaskInitNoFunctor(_Event._M_unitEvent); + } + /// + /// Initializes a task using an asynchronous action IAsyncAction* + /// + void _TaskInitNoFunctor(ABI::Windows::Foundation::IAsyncAction* _AsyncAction) + { + _M_unitTask._TaskInitAsyncOp(Microsoft::WRL::Make(_AsyncAction).Get()); + } + + /// + /// Initializes a task using an asynchronous action with progress IAsyncActionWithProgress<_P>* + /// + template + void _TaskInitNoFunctor(ABI::Windows::Foundation::IAsyncActionWithProgress<_P>* _AsyncActionWithProgress) + { + _M_unitTask._TaskInitAsyncOp(Microsoft::WRL::Make>(_AsyncActionWithProgress).Get()); + } + /// + /// Initializes a task using a callable object. + /// + template + void _TaskInitMaybeFunctor(_Function & _Func, std::true_type) + { + _M_unitTask._TaskInitWithFunctor(_Func); + } + + /// + /// Initializes a task using a non-callable object. + /// + template + void _TaskInitMaybeFunctor(_T & _Param, std::false_type) + { + _TaskInitNoFunctor(_Param); + } + + // The void task contains a task of a dummy type so common code can be used for tasks with void and non-void results. + task _M_unitTask; +}; + +namespace details +{ + + /// + /// The following type traits are used for the create_task function. + /// + + // Unwrap task + template + _Ty _GetUnwrappedType(task<_Ty>); + + // Unwrap all supported types + template + auto _GetUnwrappedReturnType(_Ty _Arg, int) -> decltype(_GetUnwrappedType(_Arg)); + // fallback + template + _Ty _GetUnwrappedReturnType(_Ty, ...); + + /// + /// _GetTaskType functions will retrieve task type T in task[T](Arg), + /// for given constructor argument Arg and its property "callable". + /// It will automatically unwrap argument to get the final return type if necessary. + /// + + // Non-Callable + template + _Ty _GetTaskType(task_completion_event<_Ty>, std::false_type); + + // Non-Callable + template + auto _GetTaskType(_Ty _NonFunc, std::false_type) -> decltype(_GetUnwrappedType(_NonFunc)); + + // Callable + template + auto _GetTaskType(_Ty _Func, std::true_type) -> decltype(_GetUnwrappedReturnType(stdx::declval<_FunctionTypeTraits<_Ty, void>::_FuncRetType>(), 0)); + + // Special callable returns void + void _GetTaskType(std::function, std::true_type); + struct _BadArgType{}; + + template + auto _FilterValidTaskType(_Ty _Param, int) -> decltype(_GetTaskType(_Param, _IsCallable<_ReturnType>(_Param, 0, 0, 0))); + + template + _BadArgType _FilterValidTaskType(_Ty _Param, ...); + + template + struct _TaskTypeFromParam + { + typedef decltype(_FilterValidTaskType<_ReturnType>(stdx::declval<_Ty>(), 0)) _Type; + }; +} + + +/// +/// Creates a PPL task object. create_task can be used anywhere you would have used a task constructor. +/// It is provided mainly for convenience, because it allows use of the auto keyword while creating tasks. +/// +/// +/// The type of the parameter from which the task is to be constructed. +/// +/// +/// The parameter from which the task is to be constructed. This could be a lambda or function object, a task_completion_event +/// object, a different task object, or a Windows::Foundation::IAsyncInfo interface if you are using tasks in your Windows Store app. +/// +/// +/// A new task of type T, that is inferred from . +/// +/// +/// The first overload behaves like a task constructor that takes a single parameter. +/// The second overload associates the cancellation token provided with the newly created task. If you use this overload you are not +/// allowed to pass in a different task object as the first parameter. +/// The type of the returned task is inferred from the first parameter to the function. If is a task_completion_event<T>, +/// a task<T>, or a functor that returns either type T or task<T>, the type of the created task is task<T>. +/// In a Windows Store app, if is of type Windows::Foundation::IAsyncOperation<T>^ or +/// Windows::Foundation::IAsyncOperationWithProgress<T,P>^, or a functor that returns either of those types, the created task will be of type task<T>. +/// If is of type Windows::Foundation::IAsyncAction^ or Windows::Foundation::IAsyncActionWithProgress<P>^, or a functor +/// that returns either of those types, the created task will have type task<void>. +/// +/// +/// +/**/ +template +__declspec(noinline) +#if _MSC_VER >= 1800 +auto create_task(_Ty _Param, task_options _TaskOptions = task_options()) -> task::_Type> +#else +auto create_task(_Ty _Param) -> task::_Type> +#endif +{ + static_assert(!std::is_same::_Type, details::_BadArgType>::value, + "incorrect argument for create_task; can be a callable object, an asynchronous operation, or a task_completion_event" + ); +#if _MSC_VER >= 1800 + details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK()); + task::_Type> _CreatedTask(_Param, _TaskOptions); +#else + task::_Type> _CreatedTask(_Param); + // Ideally we would like to forceinline create_task, but __forceinline does nothing on debug builds. Therefore, we ask for no inlining + // and overwrite the creation address hint set by the task constructor. DO NOT REMOVE this next line from create_task. It is + // essential that _ReturnAddress() evaluate to the instruction right after the call to create_task in client code. + _CreatedTask._SetTaskCreationAddressHint(_ReturnAddress()); +#endif + return _CreatedTask; +} + +/// +/// Creates a PPL task object. create_task can be used anywhere you would have used a task constructor. +/// It is provided mainly for convenience, because it allows use of the auto keyword while creating tasks. +/// +/// +/// The type of the parameter from which the task is to be constructed. +/// +/// +/// The parameter from which the task is to be constructed. This could be a lambda or function object, a task_completion_event +/// object, a different task object, or a Windows::Foundation::IAsyncInfo interface if you are using tasks in your Windows Store app. +/// +/// +/// The cancellation token to associate with the task. When the source for this token is canceled, cancellation will be requested on the task. +/// +/// +/// A new task of type T, that is inferred from . +/// +/// +/// The first overload behaves like a task constructor that takes a single parameter. +/// The second overload associates the cancellation token provided with the newly created task. If you use this overload you are not +/// allowed to pass in a different task object as the first parameter. +/// The type of the returned task is inferred from the first parameter to the function. If is a task_completion_event<T>, +/// a task<T>, or a functor that returns either type T or task<T>, the type of the created task is task<T>. +/// In a Windows Store app, if is of type Windows::Foundation::IAsyncOperation<T>^ or +/// Windows::Foundation::IAsyncOperationWithProgress<T,P>^, or a functor that returns either of those types, the created task will be of type task<T>. +/// If is of type Windows::Foundation::IAsyncAction^ or Windows::Foundation::IAsyncActionWithProgress<P>^, or a functor +/// that returns either of those types, the created task will have type task<void>. +/// +/// +/// +/**/ +#if _MSC_VER >= 1800 +template +__declspec(noinline) +task<_ReturnType> create_task(const task<_ReturnType>& _Task) +{ + task<_ReturnType> _CreatedTask(_Task); + return _CreatedTask; +} +#else +template +__declspec(noinline) +auto create_task(_Ty _Param, Concurrency::cancellation_token _Token) -> task::_Type> +{ + static_assert(!std::is_same::_Type, details::_BadArgType>::value, + "incorrect argument for create_task; can be a callable object, an asynchronous operation, or a task_completion_event" + ); + task::_Type> _CreatedTask(_Param, _Token); + // Ideally we would like to forceinline create_task, but __forceinline does nothing on debug builds. Therefore, we ask for no inlining + // and overwrite the creation address hint set by the task constructor. DO NOT REMOVE this next line from create_task. It is + // essential that _ReturnAddress() evaluate to the instruction right after the call to create_task in client code. + _CreatedTask._SetTaskCreationAddressHint(_ReturnAddress()); + return _CreatedTask; +} +#endif + +namespace details +{ + template + task*>()))>::type> _To_task_helper(ABI::Windows::Foundation::IAsyncOperation<_T>* op) + { + return task<_T>(op); + } + + template + task*>()))>::type> _To_task_helper(ABI::Windows::Foundation::IAsyncOperationWithProgress<_T, _Progress>* op) + { + return task<_T>(op); + } + + inline task _To_task_helper(ABI::Windows::Foundation::IAsyncAction* op) + { + return task(op); + } + + template + task _To_task_helper(ABI::Windows::Foundation::IAsyncActionWithProgress<_Progress>* op) + { + return task(op); + } + + template + class _ProgressDispatcherBase + { + public: + + virtual ~_ProgressDispatcherBase() + { + } + + virtual void _Report(const _ProgressType& _Val) = 0; + }; + + template + class _ProgressDispatcher : public _ProgressDispatcherBase<_ProgressType> + { + public: + + virtual ~_ProgressDispatcher() + { + } + + _ProgressDispatcher(_ClassPtrType _Ptr) : _M_ptr(_Ptr) + { + } + + virtual void _Report(const _ProgressType& _Val) + { + _M_ptr->_FireProgress(_Val); + } + + private: + + _ClassPtrType _M_ptr; + }; +} // namespace details + + +/// +/// The progress reporter class allows reporting progress notifications of a specific type. Each progress_reporter object is bound +/// to a particular asynchronous action or operation. +/// +/// +/// The payload type of each progress notification reported through the progress reporter. +/// +/// +/// This type is only available to Windows Store apps. +/// +/// +/**/ +template +class progress_reporter +{ + typedef std::shared_ptr> _PtrType; + +public: + + /// + /// Sends a progress report to the asynchronous action or operation to which this progress reporter is bound. + /// + /// + /// The payload to report through a progress notification. + /// + /**/ + void report(const _ProgressType& _Val) const + { + _M_dispatcher->_Report(_Val); + } + + template + static progress_reporter _CreateReporter(_ClassPtrType _Ptr) + { + progress_reporter _Reporter; + details::_ProgressDispatcherBase<_ProgressType> *_PDispatcher = new details::_ProgressDispatcher<_ProgressType, _ClassPtrType>(_Ptr); + _Reporter._M_dispatcher = _PtrType(_PDispatcher); + return _Reporter; + } + progress_reporter() {} + +private: + progress_reporter(details::_ProgressReporterCtorArgType); + + _PtrType _M_dispatcher; +}; + +namespace details +{ + // + // maps internal definitions for AsyncStatus and defines states that are not client visible + // + enum _AsyncStatusInternal + { + _AsyncCreated = -1, // externally invisible + // client visible states (must match AsyncStatus exactly) + _AsyncStarted = ABI::Windows::Foundation::AsyncStatus::Started, // 0 + _AsyncCompleted = ABI::Windows::Foundation::AsyncStatus::Completed, // 1 + _AsyncCanceled = ABI::Windows::Foundation::AsyncStatus::Canceled, // 2 + _AsyncError = ABI::Windows::Foundation::AsyncStatus::Error, // 3 + // non-client visible internal states + _AsyncCancelPending, + _AsyncClosed, + _AsyncUndefined + }; + + // + // designates whether the "GetResults" method returns a single result (after complete fires) or multiple results + // (which are progressively consumable between Start state and before Close is called) + // + enum _AsyncResultType + { + SingleResult = 0x0001, + MultipleResults = 0x0002 + }; + + template + struct _ProgressTypeTraits + { + static const bool _TakesProgress = false; + typedef void _ProgressType; + }; + + template + struct _ProgressTypeTraits> + { + static const bool _TakesProgress = true; + typedef typename _T _ProgressType; + }; + + template::value, bool bTakesProgress = _ProgressTypeTraits<_T>::_TakesProgress> + struct _TokenTypeTraits + { + static const bool _TakesToken = false; + typedef typename _T _ReturnType; + }; + + template + struct _TokenTypeTraits<_T, false, true> + { + static const bool _TakesToken = false; + typedef void _ReturnType; + }; + + template + struct _TokenTypeTraits<_T, true, false> + { + static const bool _TakesToken = true; + typedef void _ReturnType; + }; + + template::_ArgumentCount> + struct _CAFunctorOptions + { + static const bool _TakesProgress = false; + static const bool _TakesToken = false; + typedef void _ProgressType; + typedef void _ReturnType; + }; + + template + struct _CAFunctorOptions<_T, 1> + { + private: + + typedef typename _FunctorTypeTraits<_T>::_Argument1Type _Argument1Type; + + public: + + static const bool _TakesProgress = _ProgressTypeTraits<_Argument1Type>::_TakesProgress; + static const bool _TakesToken = _TokenTypeTraits<_Argument1Type>::_TakesToken; + typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType; + typedef typename _TokenTypeTraits<_Argument1Type>::_ReturnType _ReturnType; + }; + + template + struct _CAFunctorOptions<_T, 2> + { + private: + + typedef typename _FunctorTypeTraits<_T>::_Argument1Type _Argument1Type; + typedef typename _FunctorTypeTraits<_T>::_Argument2Type _Argument2Type; + + public: + + static const bool _TakesProgress = _ProgressTypeTraits<_Argument1Type>::_TakesProgress; + static const bool _TakesToken = !_TakesProgress ? true : _TokenTypeTraits<_Argument2Type>::_TakesToken; + typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType; + typedef typename _TokenTypeTraits<_Argument2Type>::_ReturnType _ReturnType; + }; + + template + struct _CAFunctorOptions<_T, 3> + { + private: + + typedef typename _FunctorTypeTraits<_T>::_Argument1Type _Argument1Type; + + public: + + static const bool _TakesProgress = true; + static const bool _TakesToken = true; + typedef typename _ProgressTypeTraits<_Argument1Type>::_ProgressType _ProgressType; + typedef typename _FunctorTypeTraits<_T>::_Argument3Type _ReturnType; + }; + + class _Zip + { + }; + + // *************************************************************************** + // Async Operation Task Generators + // + + // + // Functor returns an IAsyncInfo - result needs to be wrapped in a task: + // + template + struct _SelectorTaskGenerator + { +#if _MSC_VER >= 1800 + template + static task<_ReturnType> _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>(_Func(_pRet), _taskOptinos); + } + + template + static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>(_Func(_Cts.get_token(), _pRet), _taskOptinos); + } + + template + static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>(_Func(_Progress, _pRet), _taskOptinos); + } + + template + static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>(_Func(_Progress, _Cts.get_token(), _pRet), _taskOptinos); + } +#else + template + static task<_ReturnType> _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) + { + return task<_ReturnType>(_Func(_pRet), _Cts.get_token()); + } + + template + static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) + { + return task<_ReturnType>(_Func(_Cts.get_token(), _pRet), _Cts.get_token()); + } + + template + static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) + { + return task<_ReturnType>(_Func(_Progress, _pRet), _Cts.get_token()); + } + + template + static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) + { + return task<_ReturnType>(_Func(_Progress, _Cts.get_token(), _pRet), _Cts.get_token()); + } +#endif + }; + + template + struct _SelectorTaskGenerator<_AsyncSelector, void> + { +#if _MSC_VER >= 1800 + template + static task _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task(_Func(), _taskOptinos); + } + + template + static task _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task(_Func(_Cts.get_token()), _taskOptinos); + } + + template + static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task(_Func(_Progress), _taskOptinos); + } + + template + static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task(_Func(_Progress, _Cts.get_token()), _taskOptinos); + } +#else + template + static task _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts) + { + return task(_Func(), _Cts.get_token()); + } + + template + static task _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts) + { + return task(_Func(_Cts.get_token()), _Cts.get_token()); + } + + template + static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts) + { + return task(_Func(_Progress), _Cts.get_token()); + } + + template + static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts) + { + return task(_Func(_Progress, _Cts.get_token()), _Cts.get_token()); + } +#endif + }; + +#if _MSC_VER < 1800 + // For create_async lambdas that return a (non-task) result, we oversubscriber the current task for the duration of the + // lambda. + struct _Task_generator_oversubscriber + { + _Task_generator_oversubscriber() + { + Concurrency::details::_Context::_Oversubscribe(true); + } + + ~_Task_generator_oversubscriber() + { + Concurrency::details::_Context::_Oversubscribe(false); + } + }; +#endif + + // + // Functor returns a result - it needs to be wrapped in a task: + // + template + struct _SelectorTaskGenerator + { +#if _MSC_VER >= 1800 + +#pragma warning(push) +#pragma warning(disable: 4702) + template + static task<_ReturnType> _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>([=](_ReturnType* retVal) -> HRESULT { + Concurrency::details::_Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + HRESULT hr = _Func(_pRet); + retVal = _pRet; + return hr; + }, _taskOptinos); + } +#pragma warning(pop) + + template + static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>([=](_ReturnType* retVal) -> HRESULT { + Concurrency::details::_Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + HRESULT hr = _Func(_Cts.get_token(), _pRet); + retVal = _pRet; + return hr; + }, _taskOptinos); + } + + template + static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>([=](_ReturnType* retVal) -> HRESULT { + Concurrency::details::_Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + HRESULT hr = _Func(_Progress, _pRet); + retVal = _pRet; + return hr; + }, _taskOptinos); + } + + template + static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task<_ReturnType>([=](_ReturnType* retVal) -> HRESULT { + Concurrency::details::_Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + HRESULT hr = _Func(_Progress, _Cts.get_token(), _pRet); + retVal = _pRet; + return hr; + }, _taskOptinos); + } +#else + template + static task<_ReturnType> _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) + { + return task<_ReturnType>([=](_ReturnType* retVal) -> HRESULT { + _Task_generator_oversubscriber _Oversubscriber; + HRESULT hr = _Func(_pRet); + retVal = _pRet; + return hr; + }, _Cts.get_token()); + } + + template + static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) + { + return task<_ReturnType>([=](_ReturnType* retVal) -> HRESULT { + _Task_generator_oversubscriber _Oversubscriber; + HRESULT hr = _Func(_Cts.get_token(), _pRet); + retVal = _pRet; + return hr; + }, _Cts.get_token()); + } + + template + static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) + { + return task<_ReturnType>([=](_ReturnType* retVal) -> HRESULT { + _Task_generator_oversubscriber _Oversubscriber; + HRESULT hr = _Func(_Progress, _pRet); + retVal = _pRet; + return hr; + }, _Cts.get_token()); + } + + template + static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) + { + return task<_ReturnType>([=](_ReturnType* retVal) -> HRESULT { + _Task_generator_oversubscriber _Oversubscriber; + HRESULT hr = _Func(_Progress, _Cts.get_token(), _pRet); + retVal = _pRet; + return hr; + }, _Cts.get_token()); + } +#endif + }; + + template<> + struct _SelectorTaskGenerator + { +#if _MSC_VER >= 1800 + template + static task _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task([=]() -> HRESULT { + Concurrency::details::_Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + return _Func(); + }, _taskOptinos); + } + + template + static task _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task([=]() -> HRESULT { + Concurrency::details::_Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + return _Func(_Cts.get_token()); + }, _taskOptinos); + } + + template + static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task([=]() -> HRESULT { + Concurrency::details::_Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + return _Func(_Progress); + }, _taskOptinos); + } + + template + static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + task_options _taskOptinos(_Cts.get_token()); + details::_get_internal_task_options(_taskOptinos)._set_creation_callstack(_callstack); + return task([=]() -> HRESULT { + Concurrency::details::_Task_generator_oversubscriber_t _Oversubscriber; + (_Oversubscriber); + return _Func(_Progress, _Cts.get_token()); + }, _taskOptinos); + } +#else + template + static task _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts) + { + return task([=]() -> HRESULT { + _Task_generator_oversubscriber _Oversubscriber; + return _Func(); + }, _Cts.get_token()); + } + + template + static task _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts) + { + return task([=]() -> HRESULT { + _Task_generator_oversubscriber _Oversubscriber; + return _Func(_Cts.get_token()); + }, _Cts.get_token()); + } + + template + static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts) + { + return task([=]() -> HRESULT { + _Task_generator_oversubscriber _Oversubscriber; + return _Func(_Progress); + }, _Cts.get_token()); + } + + template + static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts) + { + return task([=]() -> HRESULT { + _Task_generator_oversubscriber _Oversubscriber; + return _Func(_Progress, _Cts.get_token()); + }, _Cts.get_token()); + } +#endif + }; + + // + // Functor returns a task - the task can directly be returned: + // + template + struct _SelectorTaskGenerator + { + template +#if _MSC_VER >= 1800 + static task<_ReturnType> _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) +#else + static task<_ReturnType> _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) +#endif + { + task<_ReturnType> _task; + _Func(&_task); + return _task; + } + + template +#if _MSC_VER >= 1800 + static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) +#else + static task<_ReturnType> _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) +#endif + { + task<_ReturnType> _task; + _Func(_Cts.get_token(), &_task); + return _task; + } + + template +#if _MSC_VER >= 1800 + static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) +#else + static task<_ReturnType> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) +#endif + { + task<_ReturnType> _task; + _Func(_Progress, &_task); + return _task; + } + + template +#if _MSC_VER >= 1800 + static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) +#else + static task<_ReturnType> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) +#endif + { + task<_ReturnType> _task; + _Func(_Progress, _Cts.get_token(), &_task); + return _task; + } + }; + + template<> + struct _SelectorTaskGenerator + { + template +#if _MSC_VER >= 1800 + static task _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) +#else + static task _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts) +#endif + { + task _task; + _Func(&_task); + return _task; + } + + template +#if _MSC_VER >= 1800 + static task _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) +#else + static task _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts) +#endif + { + task _task; + _Func(_Cts.get_token(), &_task); + return _task; + } + + template +#if _MSC_VER >= 1800 + static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) +#else + static task _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts) +#endif + { + task _task; + _Func(_Progress, &_task); + return _task; + } + + template +#if _MSC_VER >= 1800 + static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) +#else + static task _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts) +#endif + { + task _task; + _Func(_Progress, _Cts.get_token(), &_task); + return _task; + } + }; + + template + struct _TaskGenerator + { + }; + + template + struct _TaskGenerator<_Generator, false, false> + { +#if _MSC_VER >= 1800 + template + static auto _GenerateTaskNoRet(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) + { + (void)_Ptr; + return _Generator::_GenerateTask_0(_Func, _Cts, _callstack); + } + + template + static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _pRet, _callstack)) + { + return _Generator::_GenerateTask_0(_Func, _Cts, _pRet, _callstack); + } +#else + template + static auto _GenerateTaskNoRet(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts)) + { + (void)_Ptr; + return _Generator::_GenerateTask_0(_Func, _Cts); + } + + template + static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _pRet)) + { + return _Generator::_GenerateTask_0(_Func, _Cts, _pRet); + } +#endif + }; + + template + struct _TaskGenerator<_Generator, true, false> + { +#if _MSC_VER >= 1800 + template + static auto _GenerateTaskNoRet(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) + { + return _Generator::_GenerateTask_1C(_Func, _Cts, _callstack); + } + + template + static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _pRet, _callstack)) + { + return _Generator::_GenerateTask_1C(_Func, _Cts, _pRet, _callstack); + } +#else + template + static auto _GenerateTaskNoRet(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts)) + { + return _Generator::_GenerateTask_1C(_Func, _Cts); + } + + template + static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _pRet)) + { + return _Generator::_GenerateTask_1C(_Func, _Cts, _pRet); + } +#endif + }; + + template + struct _TaskGenerator<_Generator, false, true> + { +#if _MSC_VER >= 1800 + template + static auto _GenerateTaskNoRet(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) + { + return _Generator::_GenerateTask_1P(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _callstack); + } + + template + static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _pRet, _callstack)) + { + return _Generator::_GenerateTask_1P(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _pRet, _callstack); + } +#else + template + static auto _GenerateTaskNoRet(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts)) + { + return _Generator::_GenerateTask_1P(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts); + } + + template + static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _pRet)) + { + return _Generator::_GenerateTask_1P(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _pRet); + } +#endif + }; + + template + struct _TaskGenerator<_Generator, true, true> + { +#if _MSC_VER >= 1800 + template + static auto _GenerateTaskNoRet(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _callstack)) + { + return _Generator::_GenerateTask_2PC(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _callstack); + } + + template + static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _pRet, _callstack)) + { + return _Generator::_GenerateTask_2PC(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _pRet, _callstack); + } +#else + template + static auto _GenerateTaskNoRet(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts)) + { + return _Generator::_GenerateTask_2PC(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts); + } + + template + static auto _GenerateTask(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) + -> decltype(_Generator::_GenerateTask_0(_Func, _Cts, _pRet)) + { + return _Generator::_GenerateTask_2PC(_Func, progress_reporter<_ProgressType>::_CreateReporter(_Ptr), _Cts, _pRet); + } +#endif + }; + + // *************************************************************************** + // Async Operation Attributes Classes + // + // These classes are passed through the hierarchy of async base classes in order to hold multiple attributes of a given async construct in + // a single container. An attribute class must define: + // + // Mandatory: + // ------------------------- + // + // _AsyncBaseType : The Windows Runtime interface which is being implemented. + // _CompletionDelegateType : The Windows Runtime completion delegate type for the interface. + // _ProgressDelegateType : If _TakesProgress is true, the Windows Runtime progress delegate type for the interface. If it is false, an empty Windows Runtime type. + // _ReturnType : The return type of the async construct (void for actions / non-void for operations) + // + // _TakesProgress : An indication as to whether or not + // + // _Generate_Task : A function adapting the user's function into what's necessary to produce the appropriate task + // + // Optional: + // ------------------------- + // + + template + struct _AsyncAttributes + { + }; + + template + struct _AsyncAttributes<_Function, _ProgressType, _ReturnType, _TaskTraits, _TakesToken, true> + { + typedef typename ABI::Windows::Foundation::IAsyncOperationWithProgress<_ReturnType, _ProgressType> _AsyncBaseType; + typedef typename ABI::Windows::Foundation::IAsyncOperationProgressHandler<_ReturnType, _ProgressType> _ProgressDelegateType; + typedef typename ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler<_ReturnType, _ProgressType> _CompletionDelegateType; + typedef typename _ReturnType _ReturnType; + typedef typename ABI::Windows::Foundation::Internal::GetAbiType()))>::type _ReturnType_abi; + typedef typename _ProgressType _ProgressType; + typedef typename ABI::Windows::Foundation::Internal::GetAbiType()))>::type _ProgressType_abi; + typedef typename _TaskTraits::_AsyncKind _AsyncKind; + typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; + typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, true> _TaskGenerator; + + static const bool _TakesProgress = true; + static const bool _TakesToken = _TakesToken; + + template +#if _MSC_VER >= 1800 + static task _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) + { + return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType_abi, _ReturnType>(_Func, _Ptr, _Cts, _pRet, _callstack); + } +#else + static task _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) + { + return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType_abi, _ReturnType>(_Func, _Ptr, _Cts, _pRet); + } +#endif + }; + + template + struct _AsyncAttributes<_Function, _ProgressType, _ReturnType, _TaskTraits, _TakesToken, false> + { + typedef typename ABI::Windows::Foundation::IAsyncOperation<_ReturnType> _AsyncBaseType; + typedef _Zip _ProgressDelegateType; + typedef typename ABI::Windows::Foundation::IAsyncOperationCompletedHandler<_ReturnType> _CompletionDelegateType; + typedef typename _ReturnType _ReturnType; + typedef typename ABI::Windows::Foundation::Internal::GetAbiType()))>::type _ReturnType_abi; + typedef typename _TaskTraits::_AsyncKind _AsyncKind; + typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; + typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, false> _TaskGenerator; + + static const bool _TakesProgress = false; + static const bool _TakesToken = _TakesToken; + + template +#if _MSC_VER >= 1800 + static task _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) + { + return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType, _ReturnType>(_Func, _Ptr, _Cts, _pRet, _callstack); + } +#else + static task _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) + { + return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType, _ReturnType>(_Func, _Ptr, _Cts, _pRet); + } +#endif + }; + + template + struct _AsyncAttributes<_Function, _ProgressType, void, _TaskTraits, _TakesToken, true> + { + typedef typename ABI::Windows::Foundation::IAsyncActionWithProgress<_ProgressType> _AsyncBaseType; + typedef typename ABI::Windows::Foundation::IAsyncActionProgressHandler<_ProgressType> _ProgressDelegateType; + typedef typename ABI::Windows::Foundation::IAsyncActionWithProgressCompletedHandler<_ProgressType> _CompletionDelegateType; + typedef void _ReturnType; + typedef void _ReturnType_abi; + typedef typename _ProgressType _ProgressType; + typedef typename ABI::Windows::Foundation::Internal::GetAbiType()))>::type _ProgressType_abi; + typedef typename _TaskTraits::_AsyncKind _AsyncKind; + typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; + typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, true> _TaskGenerator; + + static const bool _TakesProgress = true; + static const bool _TakesToken = _TakesToken; + +#if _MSC_VER >= 1800 + template + static task _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _TaskGenerator::_GenerateTaskNoRet<_Function, _ClassPtr, _ProgressType_abi>(_Func, _Ptr, _Cts, _callstack); + } + template + static task> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) + { + return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType_abi>(_Func, _Ptr, _Cts, _pRet, _callstack); + } +#else + template + static task _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts) + { + return _TaskGenerator::_GenerateTaskNoRet<_Function, _ClassPtr, _ProgressType_abi>(_Func, _Ptr, _Cts); + } + template + static task> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) + { + return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType_abi>(_Func, _Ptr, _Cts, _pRet); + } +#endif + }; + + template + struct _AsyncAttributes<_Function, _ProgressType, void, _TaskTraits, _TakesToken, false> + { + typedef typename ABI::Windows::Foundation::IAsyncAction _AsyncBaseType; + typedef _Zip _ProgressDelegateType; + typedef typename ABI::Windows::Foundation::IAsyncActionCompletedHandler _CompletionDelegateType; + typedef void _ReturnType; + typedef void _ReturnType_abi; + typedef typename _TaskTraits::_AsyncKind _AsyncKind; + typedef typename _SelectorTaskGenerator<_AsyncKind, _ReturnType> _SelectorTaskGenerator; + typedef typename _TaskGenerator<_SelectorTaskGenerator, _TakesToken, false> _TaskGenerator; + + static const bool _TakesProgress = false; + static const bool _TakesToken = _TakesToken; + +#if _MSC_VER >= 1800 + template + static task _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack) + { + return _TaskGenerator::_GenerateTaskNoRet<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _callstack); + } + template + static task> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet, const _TaskCreationCallstack & _callstack) + { + return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _pRet, _callstack); + } +#else + template + static task _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts) + { + return _TaskGenerator::_GenerateTaskNoRet<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts); + } + template + static task> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet) + { + return _TaskGenerator::_GenerateTask<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts, _pRet); + } +#endif + }; + + template + struct _AsyncLambdaTypeTraits + { + typedef typename _Unhat::_ReturnType>::_Value _ReturnType; + typedef typename _FunctorTypeTraits<_Function>::_Argument1Type _Argument1Type; + typedef typename _CAFunctorOptions<_Function>::_ProgressType _ProgressType; + + static const bool _TakesProgress = _CAFunctorOptions<_Function>::_TakesProgress; + static const bool _TakesToken = _CAFunctorOptions<_Function>::_TakesToken; + + typedef typename _TaskTypeTraits<_ReturnType> _TaskTraits; + typedef typename _AsyncAttributes<_Function, _ProgressType, typename _TaskTraits::_TaskRetType, _TaskTraits, _TakesToken, _TakesProgress> _AsyncAttributes; + }; + // *************************************************************************** + // AsyncInfo (and completion) Layer: + // +#ifndef RUNTIMECLASS_Concurrency_winrt_details__AsyncInfoBase_DEFINED +#define RUNTIMECLASS_Concurrency_winrt_details__AsyncInfoBase_DEFINED + extern const __declspec(selectany) WCHAR RuntimeClass_Concurrency_winrt_details__AsyncInfoBase[] = L"Concurrency_winrt.details._AsyncInfoBase"; +#endif + + // + // Internal base class implementation for async operations (based on internal Windows representation for ABI level async operations) + // + template < typename _Attributes, _AsyncResultType resultType = SingleResult > + class _AsyncInfoBase abstract : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::RuntimeClassType::WinRt>, Microsoft::WRL::Implements> + { + InspectableClass(RuntimeClass_Concurrency_winrt_details__AsyncInfoBase, BaseTrust) + public: + _AsyncInfoBase() : + _M_currentStatus(_AsyncStatusInternal::_AsyncCreated), + _M_errorCode(S_OK), + _M_completeDelegate(nullptr), + _M_CompleteDelegateAssigned(0), + _M_CallbackMade(0) + { +#if _MSC_VER < 1800 + _M_id = Concurrency::details::_GetNextAsyncId(); +#else + _M_id = Concurrency::details::platform::GetNextAsyncId(); +#endif + } + public: + virtual STDMETHODIMP GetResults(typename _Attributes::_ReturnType_abi* results) + { + (void)results; + return E_UNEXPECTED; + } + + virtual STDMETHODIMP get_Id(unsigned int* id) + { + HRESULT hr = _CheckValidStateForAsyncInfoCall(); + if (FAILED(hr)) return hr; + if (!id) return E_POINTER; + *id = _M_id; + return S_OK; + } + + virtual STDMETHODIMP put_Id(unsigned int id) + { + HRESULT hr = _CheckValidStateForAsyncInfoCall(); + if (FAILED(hr)) return hr; + + if (id == 0) + { + return E_INVALIDARG; + } + else if (_M_currentStatus != _AsyncStatusInternal::_AsyncCreated) + { + return E_ILLEGAL_METHOD_CALL; + } + + _M_id = id; + return S_OK; + } + virtual STDMETHODIMP get_Status(ABI::Windows::Foundation::AsyncStatus* status) + { + HRESULT hr = _CheckValidStateForAsyncInfoCall(); + if (FAILED(hr)) return hr; + if (!status) return E_POINTER; + + _AsyncStatusInternal _Current = _M_currentStatus; + // + // Map our internal cancel pending to cancelled. This way "pending cancelled" looks to the outside as "cancelled" but + // can still transition to "completed" if the operation completes without acknowledging the cancellation request + // + switch (_Current) + { + case _AsyncCancelPending: + _Current = _AsyncCanceled; + break; + case _AsyncCreated: + _Current = _AsyncStarted; + break; + default: + break; + } + + *status = static_cast(_Current); + return S_OK; + } + + virtual STDMETHODIMP get_ErrorCode(HRESULT* errorCode) + { + HRESULT hr = _CheckValidStateForAsyncInfoCall(); + if (FAILED(hr)) return hr; + if (!hr) return hr; + *errorCode = _M_errorCode; + return S_OK; + } + + virtual STDMETHODIMP get_Progress(typename _Attributes::_ProgressDelegateType** _ProgressHandler) + { + return _GetOnProgress(_ProgressHandler); + } + + virtual STDMETHODIMP put_Progress(typename _Attributes::_ProgressDelegateType* _ProgressHandler) + { + return _PutOnProgress(_ProgressHandler); + } + + virtual STDMETHODIMP Cancel() + { + if (_TransitionToState(_AsyncCancelPending)) + { + _OnCancel(); + } + return S_OK; + } + + virtual STDMETHODIMP Close() + { + if (_TransitionToState(_AsyncClosed)) + { + _OnClose(); + } + else + { + if (_M_currentStatus != _AsyncClosed) // Closed => Closed transition is just ignored + { + return E_ILLEGAL_STATE_CHANGE; + } + } + return S_OK; + } + + virtual STDMETHODIMP get_Completed(typename _Attributes::_CompletionDelegateType** _CompleteHandler) + { + _CheckValidStateForDelegateCall(); + if (!_CompleteHandler) return E_POINTER; + *_CompleteHandler = _M_completeDelegate.Get(); + return S_OK; + } + + virtual STDMETHODIMP put_Completed(typename _Attributes::_CompletionDelegateType* _CompleteHandler) + { + _CheckValidStateForDelegateCall(); + // this delegate property is "write once" + if (InterlockedIncrement(&_M_CompleteDelegateAssigned) == 1) + { + _M_completeDelegateContext = _ContextCallback::_CaptureCurrent(); + _M_completeDelegate = _CompleteHandler; + // Guarantee that the write of _M_completeDelegate is ordered with respect to the read of state below + // as perceived from _FireCompletion on another thread. + MemoryBarrier(); + if (_IsTerminalState()) + { + _FireCompletion(); + } + } + else + { + return E_ILLEGAL_DELEGATE_ASSIGNMENT; + } + return S_OK; + } + + protected: + // _Start - this is not externally visible since async operations "hot start" before returning to the caller + STDMETHODIMP _Start() + { + if (_TransitionToState(_AsyncStarted)) + { + _OnStart(); + } + else + { + return E_ILLEGAL_STATE_CHANGE; + } + return S_OK; + } + + HRESULT _FireCompletion() + { + HRESULT hr = S_OK; + _TryTransitionToCompleted(); + + // we guarantee that completion can only ever be fired once + if (_M_completeDelegate != nullptr && InterlockedIncrement(&_M_CallbackMade) == 1) + { + hr = _M_completeDelegateContext._CallInContext([=]() -> HRESULT { + ABI::Windows::Foundation::AsyncStatus status; + HRESULT hr; + if (SUCCEEDED(hr = this->get_Status(&status))) + _M_completeDelegate->Invoke((_Attributes::_AsyncBaseType*)this, status); + _M_completeDelegate = nullptr; + return hr; + }); + } + return hr; + } + + virtual STDMETHODIMP _GetOnProgress(typename _Attributes::_ProgressDelegateType** _ProgressHandler) + { + (void)_ProgressHandler; + return E_UNEXPECTED; + } + + virtual STDMETHODIMP _PutOnProgress(typename _Attributes::_ProgressDelegateType* _ProgressHandler) + { + (void)_ProgressHandler; + return E_UNEXPECTED; + } + + + bool _TryTransitionToCompleted() + { + return _TransitionToState(_AsyncStatusInternal::_AsyncCompleted); + } + + bool _TryTransitionToCancelled() + { + return _TransitionToState(_AsyncStatusInternal::_AsyncCanceled); + } + + bool _TryTransitionToError(const HRESULT error) + { + _InterlockedCompareExchange(reinterpret_cast(&_M_errorCode), error, S_OK); + return _TransitionToState(_AsyncStatusInternal::_AsyncError); + } + + // This method checks to see if the delegate properties can be + // modified in the current state and generates the appropriate + // error hr in the case of violation. + inline HRESULT _CheckValidStateForDelegateCall() + { + if (_M_currentStatus == _AsyncClosed) + { + return E_ILLEGAL_METHOD_CALL; + } + return S_OK; + } + + // This method checks to see if results can be collected in the + // current state and generates the appropriate error hr in + // the case of a violation. + inline HRESULT _CheckValidStateForResultsCall() + { + _AsyncStatusInternal _Current = _M_currentStatus; + + if (_Current == _AsyncError) + { + return _M_errorCode; + } +#pragma warning(push) +#pragma warning(disable: 4127) // Conditional expression is constant + // single result illegal before transition to Completed or Cancelled state + if (resultType == SingleResult) +#pragma warning(pop) + { + if (_Current != _AsyncCompleted) + { + return E_ILLEGAL_METHOD_CALL; + } + } + // multiple results can be called after Start has been called and before/after Completed + else if (_Current != _AsyncStarted && + _Current != _AsyncCancelPending && + _Current != _AsyncCanceled && + _Current != _AsyncCompleted) + { + return E_ILLEGAL_METHOD_CALL; + } + return S_OK; + } + + // This method can be called by derived classes periodically to determine + // whether the asynchronous operation should continue processing or should + // be halted. + inline bool _ContinueAsyncOperation() + { + return _M_currentStatus == _AsyncStarted; + } + + // These two methods are used to allow the async worker implementation do work on + // state transitions. No real "work" should be done in these methods. In other words + // they should not block for a long time on UI timescales. + virtual void _OnStart() = 0; + virtual void _OnClose() = 0; + virtual void _OnCancel() = 0; + + private: + + // This method is used to check if calls to the AsyncInfo properties + // (id, status, errorcode) are legal in the current state. It also + // generates the appropriate error hr to return in the case of an + // illegal call. + inline HRESULT _CheckValidStateForAsyncInfoCall() + { + _AsyncStatusInternal _Current = _M_currentStatus; + if (_Current == _AsyncClosed) + { + return E_ILLEGAL_METHOD_CALL; + } + else if (_Current == _AsyncCreated) + { + return E_ASYNC_OPERATION_NOT_STARTED; + } + return S_OK; + } + + inline bool _TransitionToState(const _AsyncStatusInternal _NewState) + { + _AsyncStatusInternal _Current = _M_currentStatus; + + // This enforces the valid state transitions of the asynchronous worker object + // state machine. + switch (_NewState) + { + case _AsyncStatusInternal::_AsyncStarted: + if (_Current != _AsyncCreated) + { + return false; + } + break; + case _AsyncStatusInternal::_AsyncCompleted: + if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) + { + return false; + } + break; + case _AsyncStatusInternal::_AsyncCancelPending: + if (_Current != _AsyncStarted) + { + return false; + } + break; + case _AsyncStatusInternal::_AsyncCanceled: + if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) + { + return false; + } + break; + case _AsyncStatusInternal::_AsyncError: + if (_Current != _AsyncStarted && _Current != _AsyncCancelPending) + { + return false; + } + break; + case _AsyncStatusInternal::_AsyncClosed: + if (!_IsTerminalState(_Current)) + { + return false; + } + break; + default: + return false; + break; + } + + // attempt the transition to the new state + // Note: if currentStatus_ == _Current, then there was no intervening write + // by the async work object and the swap succeeded. + _AsyncStatusInternal _RetState = static_cast<_AsyncStatusInternal>( + _InterlockedCompareExchange(reinterpret_cast(&_M_currentStatus), + _NewState, + static_cast(_Current))); + + // ICE returns the former state, if the returned state and the + // state we captured at the beginning of this method are the same, + // the swap succeeded. + return (_RetState == _Current); + } + + inline bool _IsTerminalState() + { + return _IsTerminalState(_M_currentStatus); + } + + inline bool _IsTerminalState(_AsyncStatusInternal status) + { + return (status == _AsyncError || + status == _AsyncCanceled || + status == _AsyncCompleted || + status == _AsyncClosed); + } + + private: + + _ContextCallback _M_completeDelegateContext; + Microsoft::WRL::ComPtr _M_completeDelegate; //ComPtr cannot be volatile as it does not have volatile accessors + _AsyncStatusInternal volatile _M_currentStatus; + HRESULT volatile _M_errorCode; + unsigned int _M_id; + long volatile _M_CompleteDelegateAssigned; + long volatile _M_CallbackMade; + }; + + // *************************************************************************** + // Progress Layer (optional): + // + + template< typename _Attributes, bool _HasProgress, _AsyncResultType _ResultType = SingleResult > + class _AsyncProgressBase abstract : public _AsyncInfoBase<_Attributes, _ResultType> + { + }; + + template< typename _Attributes, _AsyncResultType _ResultType> + class _AsyncProgressBase<_Attributes, true, _ResultType> abstract : public _AsyncInfoBase<_Attributes, _ResultType> + { + public: + + _AsyncProgressBase() : _AsyncInfoBase<_Attributes, _ResultType>(), + _M_progressDelegate(nullptr) + { + } + + virtual STDMETHODIMP _GetOnProgress(typename _Attributes::_ProgressDelegateType** _ProgressHandler) override + { + HRESULT hr = _CheckValidStateForDelegateCall(); + if (FAILED(hr)) return hr; + *_ProgressHandler = _M_progressDelegate; + return S_OK; + } + + virtual STDMETHODIMP _PutOnProgress(typename _Attributes::_ProgressDelegateType* _ProgressHandler) override + { + HRESULT hr = _CheckValidStateForDelegateCall(); + if (FAILED(hr)) return hr; + _M_progressDelegate = _ProgressHandler; + _M_progressDelegateContext = _ContextCallback::_CaptureCurrent(); + return S_OK; + } + + public: + + void _FireProgress(const typename _Attributes::_ProgressType_abi& _ProgressValue) + { + if (_M_progressDelegate != nullptr) + { + _M_progressDelegateContext._CallInContext([=]() -> HRESULT { + _M_progressDelegate->Invoke((_Attributes::_AsyncBaseType*)this, _ProgressValue); + return S_OK; + }); + } + } + + private: + + _ContextCallback _M_progressDelegateContext; + typename _Attributes::_ProgressDelegateType* _M_progressDelegate; + }; + + template + class _AsyncBaseProgressLayer abstract : public _AsyncProgressBase<_Attributes, _Attributes::_TakesProgress, _ResultType> + { + }; + + // *************************************************************************** + // Task Adaptation Layer: + // + + // + // _AsyncTaskThunkBase provides a bridge between IAsync and task. + // + template + class _AsyncTaskThunkBase abstract : public _AsyncBaseProgressLayer<_Attributes> + { + public: + + //AsyncAction* + virtual STDMETHODIMP GetResults() + { + HRESULT hr = _CheckValidStateForResultsCall(); + if (FAILED(hr)) return hr; + _M_task.get(); + return S_OK; + } + public: + typedef task<_ReturnType> _TaskType; + + _AsyncTaskThunkBase(const _TaskType& _Task) + : _M_task(_Task) + { + } + + _AsyncTaskThunkBase() + { + } +#if _MSC_VER < 1800 + void _SetTaskCreationAddressHint(void* _SourceAddressHint) + { + if (!(std::is_same<_Attributes::_AsyncKind, _TypeSelectorAsyncTask>::value)) + { + // Overwrite the creation address with the return address of create_async unless the + // lambda returned a task. If the create async lambda returns a task, that task is reused and + // we want to preserve its creation address hint. + _M_task._SetTaskCreationAddressHint(_SourceAddressHint); + } + } +#endif + protected: + virtual void _OnStart() override + { + _M_task.then([=](_TaskType _Antecedent) -> HRESULT { + try + { + _Antecedent.get(); + } + catch (Concurrency::task_canceled&) + { + _TryTransitionToCancelled(); + } + catch (IRestrictedErrorInfo*& _Ex) + { + HRESULT hr; + HRESULT _hr; + hr = _Ex->GetErrorDetails(NULL, &_hr, NULL, NULL); + if (SUCCEEDED(hr)) hr = _hr; + _TryTransitionToError(hr); + } + catch (...) + { + _TryTransitionToError(E_FAIL); + } + return _FireCompletion(); + }); + } + + protected: + _TaskType _M_task; + Concurrency::cancellation_token_source _M_cts; + }; + + template + class _AsyncTaskReturn abstract : public _AsyncTaskThunkBase<_Attributes, _Return> + { + public: + //AsyncOperation* + virtual STDMETHODIMP GetResults(_ReturnType* results) + { + HRESULT hr = _CheckValidStateForResultsCall(); + if (FAILED(hr)) return hr; + _M_task.get(); + *results = _M_results; + return S_OK; + } + template +#if _MSC_VER >= 1800 + void DoCreateTask(_Function _func, const _TaskCreationCallstack & _callstack) + { + _M_task = _Attributes::_Generate_Task(_func, this, _M_cts, &_M_results, _callstack); + } +#else + void DoCreateTask(_Function _func) + { + _M_task = _Attributes::_Generate_Task(_func, this, _M_cts, &_M_results); + } +#endif + protected: + _ReturnType _M_results; + }; + + template + class _AsyncTaskReturn<_Attributes, _ReturnType, void> abstract : public _AsyncTaskThunkBase<_Attributes, void> + { + public: + template +#if _MSC_VER >= 1800 + void DoCreateTask(_Function _func, const _TaskCreationCallstack & _callstack) + { + _M_task = _Attributes::_Generate_Task(_func, this, _M_cts, _callstack); + } +#else + void DoCreateTask(_Function _func) + { + _M_task = _Attributes::_Generate_Task(_func, this, _M_cts); + } +#endif + }; + + template + class _AsyncTaskReturn<_Attributes, void, task> abstract : public _AsyncTaskThunkBase<_Attributes, task> + { + public: + template +#if _MSC_VER >= 1800 + void DoCreateTask(_Function _func, const _TaskCreationCallstack & _callstack) + { + _M_task = _Attributes::_Generate_Task(_func, this, _M_cts, &_M_results, _callstack); + } +#else + void DoCreateTask(_Function _func) + { + _M_task = _Attributes::_Generate_Task(_func, this, _M_cts, &_M_results); + } +#endif + protected: + task _M_results; + }; + + template + class _AsyncTaskThunk : public _AsyncTaskReturn<_Attributes, typename _Attributes::_ReturnType_abi, typename _Attributes::_ReturnType> + { + public: + + _AsyncTaskThunk(const _TaskType& _Task) : + _AsyncTaskThunkBase(_Task) + { + } + + _AsyncTaskThunk() + { + } + + protected: + + virtual void _OnClose() override + { + } + + virtual void _OnCancel() override + { + _M_cts.cancel(); + } + }; + + // *************************************************************************** + // Async Creation Layer: + // + template + class _AsyncTaskGeneratorThunk : public _AsyncTaskThunk::_AsyncAttributes> + { + public: + + typedef typename _AsyncLambdaTypeTraits<_Function>::_AsyncAttributes _Attributes; + typedef typename _AsyncTaskThunk<_Attributes> _Base; + typedef typename _Attributes::_AsyncBaseType _AsyncBaseType; + +#if _MSC_VER >= 1800 + _AsyncTaskGeneratorThunk(const _Function& _Func, const _TaskCreationCallstack &_callstack) : _M_func(_Func), _M_creationCallstack(_callstack) +#else + _AsyncTaskGeneratorThunk(const _Function& _Func) : _M_func(_Func) +#endif + { + // Virtual call here is safe as the class is declared 'sealed' + _Start(); + } + + protected: + + // + // The only thing we must do different from the base class is we must spin the hot task on transition from Created->Started. Otherwise, + // let the base thunk handle everything. + // + + virtual void _OnStart() override + { + // + // Call the appropriate task generator to actually produce a task of the expected type. This might adapt the user lambda for progress reports, + // wrap the return result in a task, or allow for direct return of a task depending on the form of the lambda. + // +#if _MSC_VER >= 1800 + DoCreateTask<_Function>(_M_func, _M_creationCallstack); +#else + DoCreateTask<_Function>(_M_func); +#endif + _Base::_OnStart(); + } + + virtual void _OnCancel() override + { + _Base::_OnCancel(); + } + + private: +#if _MSC_VER >= 1800 + _TaskCreationCallstack _M_creationCallstack; +#endif + _Function _M_func; + }; +} // namespace details + +/// +/// Creates a Windows Runtime asynchronous construct based on a user supplied lambda or function object. The return type of create_async is +/// one of either IAsyncAction^, IAsyncActionWithProgress<TProgress>^, IAsyncOperation<TResult>^, or +/// IAsyncOperationWithProgress<TResult, TProgress>^ based on the signature of the lambda passed to the method. +/// +/// +/// The lambda or function object from which to create a Windows Runtime asynchronous construct. +/// +/// +/// An asynchronous construct represented by an IAsyncAction^, IAsyncActionWithProgress<TProgress>^, IAsyncOperation<TResult>^, or an +/// IAsyncOperationWithProgress<TResult, TProgress>^. The interface returned depends on the signature of the lambda passed into the function. +/// +/// +/// The return type of the lambda determines whether the construct is an action or an operation. +/// Lambdas that return void cause the creation of actions. Lambdas that return a result of type TResult cause the creation of +/// operations of TResult. +/// The lambda may also return a task<TResult> which encapsulates the aysnchronous work within itself or is the continuation of +/// a chain of tasks that represent the asynchronous work. In this case, the lambda itself is executed inline, since the tasks are the ones that +/// execute asynchronously, and the return type of the lambda is unwrapped to produce the asynchronous construct returned by create_async. +/// This implies that a lambda that returns a task<void> will cause the creation of actions, and a lambda that returns a task<TResult> will +/// cause the creation of operations of TResult. +/// The lambda may take either zero, one or two arguments. The valid arguments are progress_reporter<TProgress> and +/// cancellation_token, in that order if both are used. A lambda without arguments causes the creation of an asynchronous construct without +/// the capability for progress reporting. A lambda that takes a progress_reporter<TProgress> will cause create_async to return an asynchronous +/// construct which reports progress of type TProgress each time the report method of the progress_reporter object is called. A lambda that +/// takes a cancellation_token may use that token to check for cancellation, or pass it to tasks that it creates so that cancellation of the +/// asynchronous construct causes cancellation of those tasks. +/// If the body of the lambda or function object returns a result (and not a task<TResult>), the lamdba will be executed +/// asynchronously within the process MTA in the context of a task the Runtime implicitly creates for it. The IAsyncInfo::Cancel method will +/// cause cancellation of the implicit task. +/// If the body of the lambda returns a task, the lamba executes inline, and by declaring the lambda to take an argument of type +/// cancellation_token you can trigger cancellation of any tasks you create within the lambda by passing that token in when you create them. +/// You may also use the register_callback method on the token to cause the Runtime to invoke a callback when you call IAsyncInfo::Cancel on +/// the async operation or action produced.. +/// This function is only available to Windows Store apps. +/// +/// +/// +/// +/**/ +template +__declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result +details::_AsyncTaskGeneratorThunk<_Function>* create_async(const _Function& _Func) +{ + static_assert(std::is_same(_Func, 0, 0, 0, 0, 0, 0, 0, 0)), std::true_type>::value, + "argument to create_async must be a callable object taking zero, one, two or three arguments"); +#if _MSC_VER >= 1800 + Microsoft::WRL::ComPtr> _AsyncInfo = Microsoft::WRL::Make>(_Func, _CAPTURE_CALLSTACK()); +#else + Microsoft::WRL::ComPtr> _AsyncInfo = Microsoft::WRL::Make>(_Func); + _AsyncInfo->_SetTaskCreationAddressHint(_ReturnAddress()); +#endif + return _AsyncInfo.Detach(); +} + +namespace details +{ +#if _MSC_VER < 1800 + // Internal API which retrieves the next async id. + _CRTIMP2 unsigned int __cdecl _GetNextAsyncId(); +#endif + // Helper struct for when_all operators to know when tasks have completed + template + struct _RunAllParam + { + _RunAllParam() : _M_completeCount(0), _M_numTasks(0) + { + } + + void _Resize(size_t _Len, bool _SkipVector = false) + { + _M_numTasks = _Len; + if (!_SkipVector) +#if _MSC_VER >= 1800 + { + _M_vector._Result.resize(_Len); + } +#else + _M_vector.resize(_Len); + _M_contexts.resize(_Len); +#endif + } + + task_completion_event<_Unit_type> _M_completed; + atomic_size_t _M_completeCount; +#if _MSC_VER >= 1800 + _ResultHolder > _M_vector; + _ResultHolder<_Type> _M_mergeVal; +#else + std::vector<_Type> _M_vector; + std::vector<_ContextCallback> _M_contexts; + _Type _M_mergeVal; +#endif + size_t _M_numTasks; + }; + +#if _MSC_VER >= 1800 + template + struct _RunAllParam > + { + _RunAllParam() : _M_completeCount(0), _M_numTasks(0) + { + } + + void _Resize(size_t _Len, bool _SkipVector = false) + { + _M_numTasks = _Len; + + if (!_SkipVector) + { + _M_vector.resize(_Len); + } + } + + task_completion_event<_Unit_type> _M_completed; + std::vector<_ResultHolder > > _M_vector; + atomic_size_t _M_completeCount; + size_t _M_numTasks; + }; +#endif + + // Helper struct specialization for void + template<> +#if _MSC_VER >= 1800 + struct _RunAllParam<_Unit_type> +#else + struct _RunAllParam +#endif + { + _RunAllParam() : _M_completeCount(0), _M_numTasks(0) + { + } + + void _Resize(size_t _Len) + { + _M_numTasks = _Len; + } + + task_completion_event<_Unit_type> _M_completed; + atomic_size_t _M_completeCount; + size_t _M_numTasks; + }; + + inline void _JoinAllTokens_Add(const Concurrency::cancellation_token_source& _MergedSrc, Concurrency::details::_CancellationTokenState *_PJoinedTokenState) + { + if (_PJoinedTokenState != nullptr && _PJoinedTokenState != Concurrency::details::_CancellationTokenState::_None()) + { + Concurrency::cancellation_token _T = Concurrency::cancellation_token::_FromImpl(_PJoinedTokenState); + _T.register_callback([=](){ + _MergedSrc.cancel(); + }); + } + } + + template + void _WhenAllContinuationWrapper(_RunAllParam<_ElementType>* _PParam, _Function _Func, task<_TaskType>& _Task) + { + if (_Task._GetImpl()->_IsCompleted()) + { + _Func(); +#if _MSC_VER >= 1800 + if (Concurrency::details::atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) +#else + if (_InterlockedIncrementSizeT(&_PParam->_M_completeCount) == _PParam->_M_numTasks) +#endif + { + // Inline execute its direct continuation, the _ReturnTask + _PParam->_M_completed.set(_Unit_type()); + // It's safe to delete it since all usage of _PParam in _ReturnTask has been finished. + delete _PParam; + } + } + else + { + _CONCRT_ASSERT(_Task._GetImpl()->_IsCanceled()); + if (_Task._GetImpl()->_HasUserException()) + { + // _Cancel will return false if the TCE is already canceled with or without exception + _PParam->_M_completed._Cancel(_Task._GetImpl()->_GetExceptionHolder()); + } + else + { + _PParam->_M_completed._Cancel(); + } +#if _MSC_VER >= 1800 + if (Concurrency::details::atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) +#else + if (_InterlockedIncrementSizeT(&_PParam->_M_completeCount) == _PParam->_M_numTasks) +#endif + { + delete _PParam; + } + } + } + + template + struct _WhenAllImpl + { +#if _MSC_VER >= 1800 + static task> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) +#else + static task> _Perform(Concurrency::details::_CancellationTokenState *_PTokenState, _Iterator _Begin, _Iterator _End) +#endif + { +#if _MSC_VER >= 1800 + Concurrency::details::_CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; +#endif + auto _PParam = new _RunAllParam<_ElementType>(); + Concurrency::cancellation_token_source _MergedSource; + + // Step1: Create task completion event. +#if _MSC_VER >= 1800 + task_options _Options(_TaskOptions); + _Options.set_cancellation_token(_MergedSource.get_token()); + task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); +#else + task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _MergedSource.get_token()); +#endif + // The return task must be created before step 3 to enforce inline execution. + auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type, std::vector<_ElementType>* retVal) -> HRESULT { +#if _MSC_VER >= 1800 + * retVal = _PParam->_M_vector.Get(); +#else + auto _Result = _PParam->_M_vector; // copy by value + + size_t _Index = 0; + for (auto _It = _Result.begin(); _It != _Result.end(); ++_It) + { + *_It = _ResultContext<_ElementType>::_GetValue(*_It, _PParam->_M_contexts[_Index++], false); + } + *retVal = _Result; +#endif + return S_OK; +#if _MSC_VER >= 1800 + }, nullptr); +#else + }, nullptr, true); +#endif + // Step2: Combine and check tokens, and count elements in range. + if (_PTokenState) + { + details::_JoinAllTokens_Add(_MergedSource, _PTokenState); + _PParam->_Resize(static_cast(std::distance(_Begin, _End))); + } + else + { + size_t _TaskNum = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + _TaskNum++; + details::_JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); + } + _PParam->_Resize(_TaskNum); + } + + // Step3: Check states of previous tasks. + if (_Begin == _End) + { + _PParam->_M_completed.set(_Unit_type()); + delete _PParam; + } + else + { + size_t _Index = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + if (_PTask->is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PTask->_Then([_PParam, _Index](task<_ElementType> _ResultTask) -> HRESULT { + +#if _MSC_VER >= 1800 + // Dev10 compiler bug + typedef _ElementType _ElementTypeDev10; + auto _PParamCopy = _PParam; + auto _IndexCopy = _Index; + auto _Func = [_PParamCopy, _IndexCopy, &_ResultTask](){ + _PParamCopy->_M_vector._Result[_IndexCopy] = _ResultTask._GetImpl()->_GetResult(); + }; +#else + auto _Func = [_PParam, _Index, &_ResultTask](){ + _PParam->_M_vector[_Index] = _ResultTask._GetImpl()->_GetResult(); + _PParam->_M_contexts[_Index] = _ResultContext<_ElementType>::_GetContext(false); + }; +#endif + _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); + return S_OK; +#if _MSC_VER >= 1800 + }, Concurrency::details::_CancellationTokenState::_None()); +#else + }, Concurrency::details::_CancellationTokenState::_None(), false); +#endif + + _Index++; + } + } + + return _ReturnTask; + } + }; + + template + struct _WhenAllImpl, _Iterator> + { +#if _MSC_VER >= 1800 + static task> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) +#else + static task> _Perform(Concurrency::details::_CancellationTokenState *_PTokenState, _Iterator _Begin, _Iterator _End) +#endif + { +#if _MSC_VER >= 1800 + Concurrency::details::_CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; +#endif + auto _PParam = new _RunAllParam>(); + Concurrency::cancellation_token_source _MergedSource; + + // Step1: Create task completion event. +#if _MSC_VER >= 1800 + task_options _Options(_TaskOptions); + _Options.set_cancellation_token(_MergedSource.get_token()); + task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); +#else + task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _MergedSource.get_token()); +#endif + // The return task must be created before step 3 to enforce inline execution. + auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type, std::vector<_ElementType>* retVal) -> HRESULT { + _CONCRT_ASSERT(_PParam->_M_completeCount == _PParam->_M_numTasks); + std::vector<_ElementType> _Result; + for (size_t _I = 0; _I < _PParam->_M_numTasks; _I++) + { +#if _MSC_VER >= 1800 + const std::vector<_ElementType>& _Vec = _PParam->_M_vector[_I].Get(); +#else + std::vector<_ElementType>& _Vec = _PParam->_M_vector[_I]; + + for (auto _It = _Vec.begin(); _It != _Vec.end(); ++_It) + { + *_It = _ResultContext<_ElementType>::_GetValue(*_It, _PParam->_M_contexts[_I], false); + } +#endif + _Result.insert(_Result.end(), _Vec.begin(), _Vec.end()); + } + *retVal = _Result; + return S_OK; +#if _MSC_VER >= 1800 + }, nullptr); +#else + }, nullptr, true); +#endif + + // Step2: Combine and check tokens, and count elements in range. + if (_PTokenState) + { + details::_JoinAllTokens_Add(_MergedSource, _PTokenState); + _PParam->_Resize(static_cast(std::distance(_Begin, _End))); + } + else + { + size_t _TaskNum = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + _TaskNum++; + details::_JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); + } + _PParam->_Resize(_TaskNum); + } + + // Step3: Check states of previous tasks. + if (_Begin == _End) + { + _PParam->_M_completed.set(_Unit_type()); + delete _PParam; + } + else + { + size_t _Index = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + if (_PTask->is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PTask->_Then([_PParam, _Index](task> _ResultTask) -> HRESULT { +#if _MSC_VER >= 1800 + // Dev10 compiler bug + typedef _ElementType _ElementTypeDev10; + auto _PParamCopy = _PParam; + auto _IndexCopy = _Index; + auto _Func = [_PParamCopy, _IndexCopy, &_ResultTask]() { + _PParamCopy->_M_vector[_IndexCopy].Set(_ResultTask._GetImpl()->_GetResult()); + }; +#else + auto _Func = [_PParam, _Index, &_ResultTask]() { + _PParam->_M_vector[_Index] = _ResultTask._GetImpl()->_GetResult(); + _PParam->_M_contexts[_Index] = _ResultContext<_ElementType>::_GetContext(false); + }; +#endif + _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); + return S_OK; +#if _MSC_VER >= 1800 + }, Concurrency::details::_CancellationTokenState::_None()); +#else + }, Concurrency::details::_CancellationTokenState::_None(), false); +#endif + + _Index++; + } + } + + return _ReturnTask; + } + }; + + template + struct _WhenAllImpl + { +#if _MSC_VER >= 1800 + static task _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) +#else + static task _Perform(Concurrency::details::_CancellationTokenState *_PTokenState, _Iterator _Begin, _Iterator _End) +#endif + { +#if _MSC_VER >= 1800 + Concurrency::details::_CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; +#endif + auto _PParam = new _RunAllParam<_Unit_type>(); + Concurrency::cancellation_token_source _MergedSource; + + // Step1: Create task completion event. +#if _MSC_VER >= 1800 + task_options _Options(_TaskOptions); + _Options.set_cancellation_token(_MergedSource.get_token()); + task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _Options); +#else + task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _MergedSource.get_token()); +#endif + // The return task must be created before step 3 to enforce inline execution. + auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type) -> HRESULT { return S_OK; +#if _MSC_VER >= 1800 + }, nullptr); +#else + }, nullptr, false); +#endif + + // Step2: Combine and check tokens, and count elements in range. + if (_PTokenState) + { + details::_JoinAllTokens_Add(_MergedSource, _PTokenState); + _PParam->_Resize(static_cast(std::distance(_Begin, _End))); + } + else + { + size_t _TaskNum = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + _TaskNum++; + details::_JoinAllTokens_Add(_MergedSource, _PTask->_GetImpl()->_M_pTokenState); + } + _PParam->_Resize(_TaskNum); + } + + // Step3: Check states of previous tasks. + if (_Begin == _End) + { + _PParam->_M_completed.set(_Unit_type()); + delete _PParam; + } + else + { + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + if (_PTask->is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PTask->_Then([_PParam](task _ResultTask) -> HRESULT { + + auto _Func = []() -> HRESULT { return S_OK; }; + _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); + return S_OK; +#if _MSC_VER >= 1800 + }, Concurrency::details::_CancellationTokenState::_None()); +#else + }, Concurrency::details::_CancellationTokenState::_None(), false); +#endif + } + } + + return _ReturnTask; + } + }; + + template + task> _WhenAllVectorAndValue(const task>& _VectorTask, const task<_ReturnType>& _ValueTask, + bool _OutputVectorFirst) + { + auto _PParam = new _RunAllParam<_ReturnType>(); + Concurrency::cancellation_token_source _MergedSource; + + // Step1: Create task completion event. + task<_Unit_type> _All_tasks_completed(_PParam->_M_completed, _MergedSource.get_token()); + // The return task must be created before step 3 to enforce inline execution. + auto _ReturnTask = _All_tasks_completed._Then([=](_Unit_type, std::vector<_ReturnType>* retVal) -> HRESULT { + _CONCRT_ASSERT(_PParam->_M_completeCount == 2); +#if _MSC_VER >= 1800 + auto _Result = _PParam->_M_vector.Get(); // copy by value + auto _mergeVal = _PParam->_M_mergeVal.Get(); +#else + auto _Result = _PParam->_M_vector; // copy by value + for (auto _It = _Result.begin(); _It != _Result.end(); ++_It) + { + *_It = _ResultContext<_ReturnType>::_GetValue(*_It, _PParam->_M_contexts[0], false); + } +#endif + + if (_OutputVectorFirst == true) + { +#if _MSC_VER >= 1800 + _Result.push_back(_mergeVal); +#else + _Result.push_back(_ResultContext<_ReturnType>::_GetValue(_PParam->_M_mergeVal, _PParam->_M_contexts[1], false)); +#endif + } + else + { +#if _MSC_VER >= 1800 + _Result.insert(_Result.begin(), _mergeVal); +#else + _Result.insert(_Result.begin(), _ResultContext<_ReturnType>::_GetValue(_PParam->_M_mergeVal, _PParam->_M_contexts[1], false)); +#endif + } + *retVal = _Result; + return S_OK; + }, nullptr, true); + + // Step2: Combine and check tokens. + _JoinAllTokens_Add(_MergedSource, _VectorTask._GetImpl()->_M_pTokenState); + _JoinAllTokens_Add(_MergedSource, _ValueTask._GetImpl()->_M_pTokenState); + + // Step3: Check states of previous tasks. + _PParam->_Resize(2, true); + + if (_VectorTask.is_apartment_aware() || _ValueTask.is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + _VectorTask._Then([_PParam](task> _ResultTask) -> HRESULT { +#if _MSC_VER >= 1800 + // Dev10 compiler bug + typedef _ReturnType _ReturnTypeDev10; + auto _PParamCopy = _PParam; + auto _Func = [_PParamCopy, &_ResultTask]() { + auto _ResultLocal = _ResultTask._GetImpl()->_GetResult(); + _PParamCopy->_M_vector.Set(_ResultLocal); + }; +#else + auto _Func = [_PParam, &_ResultTask]() { + _PParam->_M_vector = _ResultTask._GetImpl()->_GetResult(); + _PParam->_M_contexts[0] = _ResultContext<_ReturnType>::_GetContext(false); + }; +#endif + + _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); + return S_OK; +#if _MSC_VER >= 1800 + }, _CancellationTokenState::_None()); +#else + }, _CancellationTokenState::_None(), false); +#endif + _ValueTask._Then([_PParam](task<_ReturnType> _ResultTask) -> HRESULT { +#if _MSC_VER >= 1800 + // Dev10 compiler bug + typedef _ReturnType _ReturnTypeDev10; + auto _PParamCopy = _PParam; + auto _Func = [_PParamCopy, &_ResultTask]() { + auto _ResultLocal = _ResultTask._GetImpl()->_GetResult(); + _PParamCopy->_M_mergeVal.Set(_ResultLocal); + }; +#else + auto _Func = [_PParam, &_ResultTask]() { + _PParam->_M_mergeVal = _ResultTask._GetImpl()->_GetResult(); + _PParam->_M_contexts[1] = _ResultContext<_ReturnType>::_GetContext(false); + }; +#endif + _WhenAllContinuationWrapper(_PParam, _Func, _ResultTask); + return S_OK; +#if _MSC_VER >= 1800 + }, _CancellationTokenState::_None()); +#else + }, _CancellationTokenState::_None(), false); +#endif + + return _ReturnTask; + } +} // namespace details + +#if _MSC_VER < 1800 +/// +/// Creates a task that will complete successfully when all of the tasks supplied as arguments complete successfully. +/// +/// +/// The type of the input iterator. +/// +/// +/// The position of the first element in the range of elements to be combined into the resulting task. +/// +/// +/// The position of the first element beyond the range of elements to be combined into the resulting task. +/// +/// +/// A task that completes sucessfully when all of the input tasks have completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output +/// task will also be a task<void>. +/// +/// +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encoutered, will be thrown if you call get() or wait() on that task. +/// +/// +/**/ +template +auto when_all(_Iterator _Begin, _Iterator _End) +-> decltype (details::_WhenAllImpl::value_type::result_type, _Iterator>::_Perform(nullptr, _Begin, _End)) +{ + typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; + return details::_WhenAllImpl<_ElementType, _Iterator>::_Perform(nullptr, _Begin, _End); +} +#endif + +/// +/// Creates a task that will complete successfully when all of the tasks supplied as arguments complete successfully. +/// +/// +/// The type of the input iterator. +/// +/// +/// The position of the first element in the range of elements to be combined into the resulting task. +/// +/// +/// The position of the first element beyond the range of elements to be combined into the resulting task. +/// +/// +/// The cancellation token which controls cancellation of the returned task. If you do not provide a cancellation token, the resulting +/// task will be created with a token that is a combination of all the cancelable tokens (tokens created by methods other than +/// cancellation_token::none()of the tasks supplied. +/// +/// +/// A task that completes sucessfully when all of the input tasks have completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output +/// task will also be a task<void>. +/// +/// +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encoutered, will be thrown if you call get() or wait() on that task. +/// +/// +/**/ +template +#if _MSC_VER >= 1800 +auto when_all(_Iterator _Begin, _Iterator _End, const task_options& _TaskOptions = task_options()) +-> decltype (details::_WhenAllImpl::value_type::result_type, _Iterator>::_Perform(_TaskOptions, _Begin, _End)) +{ + typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; + return details::_WhenAllImpl<_ElementType, _Iterator>::_Perform(_TaskOptions, _Begin, _End); +} +#else +auto when_all(_Iterator _Begin, _Iterator _End, Concurrency::cancellation_token _CancellationToken) +-> decltype (details::_WhenAllImpl::value_type::result_type, _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End)) +{ + typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; + return details::_WhenAllImpl<_ElementType, _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End); +} +#endif + +/// +/// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output +/// task will also be a task<void>. +/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator +/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. +/// +/// +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encoutered, will be thrown if you call get() or wait() on that task. +/// +/// +/**/ +template +task> operator&&(const task<_ReturnType> & _Lhs, const task<_ReturnType> & _Rhs) +{ + task<_ReturnType> _PTasks[2] = { _Lhs, _Rhs }; + return when_all(_PTasks, _PTasks + 2); +} + +/// +/// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output +/// task will also be a task<void>. +/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator +/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. +/// +/// +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encoutered, will be thrown if you call get() or wait() on that task. +/// +/// +/**/ +template +task> operator&&(const task> & _Lhs, const task<_ReturnType> & _Rhs) +{ + return details::_WhenAllVectorAndValue(_Lhs, _Rhs, true); +} + +/// +/// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output +/// task will also be a task<void>. +/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator +/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. +/// +/// +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encoutered, will be thrown if you call get() or wait() on that task. +/// +/// +/**/ +template +task> operator&&(const task<_ReturnType> & _Lhs, const task> & _Rhs) +{ + return details::_WhenAllVectorAndValue(_Rhs, _Lhs, false); +} + +/// +/// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output +/// task will also be a task<void>. +/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator +/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. +/// +/// +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encoutered, will be thrown if you call get() or wait() on that task. +/// +/// +/**/ +template +task> operator&&(const task> & _Lhs, const task> & _Rhs) +{ + task> _PTasks[2] = { _Lhs, _Rhs }; + return when_all(_PTasks, _PTasks + 2); +} + +/// +/// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>>. If the input tasks are of type void the output +/// task will also be a task<void>. +/// To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator +/// produces a task<std::vector<T>> if either one or both of the tasks are of type task<std::vector<T>>. +/// +/// +/// If one of the tasks is canceled or throws an exception, the returned task will complete early, in the canceled state, and the exception, +/// if one is encoutered, will be thrown if you call get() or wait() on that task. +/// +/// +/**/ +inline task operator&&(const task & _Lhs, const task & _Rhs) +{ + task _PTasks[2] = { _Lhs, _Rhs }; + return when_all(_PTasks, _PTasks + 2); +} + +namespace details +{ + // Helper struct for when_any operators to know when tasks have completed + template + struct _RunAnyParam + { + _RunAnyParam() : _M_completeCount(0), _M_numTasks(0), _M_exceptionRelatedToken(nullptr), _M_fHasExplicitToken(false) + { + } + ~_RunAnyParam() + { + if (Concurrency::details::_CancellationTokenState::_IsValid(_M_exceptionRelatedToken)) + _M_exceptionRelatedToken->_Release(); + } + task_completion_event<_CompletionType> _M_Completed; + Concurrency::cancellation_token_source _M_cancellationSource; + Concurrency::details::_CancellationTokenState* _M_exceptionRelatedToken; + atomic_size_t _M_completeCount; + size_t _M_numTasks; + bool _M_fHasExplicitToken; + }; + + template + void _WhenAnyContinuationWrapper(_RunAnyParam<_CompletionType> * _PParam, const _Function & _Func, task<_TaskType>& _Task) + { + bool _IsTokenCancled = !_PParam->_M_fHasExplicitToken && _Task._GetImpl()->_M_pTokenState != Concurrency::details::_CancellationTokenState::_None() && _Task._GetImpl()->_M_pTokenState->_IsCanceled(); + if (_Task._GetImpl()->_IsCompleted() && !_IsTokenCancled) + { + _Func(); +#if _MSC_VER >= 1800 + if (Concurrency::details::atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) +#else + if (_InterlockedIncrementSizeT(&_PParam->_M_completeCount) == _PParam->_M_numTasks) +#endif + { + delete _PParam; + } + } + else + { + _CONCRT_ASSERT(_Task._GetImpl()->_IsCanceled() || _IsTokenCancled); + if (_Task._GetImpl()->_HasUserException() && !_IsTokenCancled) + { + if (_PParam->_M_Completed._StoreException(_Task._GetImpl()->_GetExceptionHolder())) + { + // This can only enter once. + _PParam->_M_exceptionRelatedToken = _Task._GetImpl()->_M_pTokenState; + _CONCRT_ASSERT(_PParam->_M_exceptionRelatedToken); + // Deref token will be done in the _PParam destructor. + if (_PParam->_M_exceptionRelatedToken != Concurrency::details::_CancellationTokenState::_None()) + { + _PParam->_M_exceptionRelatedToken->_Reference(); + } + } + } + +#if _MSC_VER >= 1800 + if (Concurrency::details::atomic_increment(_PParam->_M_completeCount) == _PParam->_M_numTasks) +#else + if (_InterlockedIncrementSizeT(&_PParam->_M_completeCount) == _PParam->_M_numTasks) +#endif + { + // If no one has be completed so far, we need to make some final cancellation decision. + if (!_PParam->_M_Completed._IsTriggered()) + { + // If we already explicit token, we can skip the token join part. + if (!_PParam->_M_fHasExplicitToken) + { + if (_PParam->_M_exceptionRelatedToken) + { + details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, _PParam->_M_exceptionRelatedToken); + } + else + { + // If haven't captured any exception token yet, there was no exception for all those tasks, + // so just pick a random token (current one) for normal cancellation. + details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, _Task._GetImpl()->_M_pTokenState); + } + } + // Do exception cancellation or normal cancellation based on whether it has stored exception. + _PParam->_M_Completed._Cancel(); + } + delete _PParam; + } + } + } + + template + struct _WhenAnyImpl + { +#if _MSC_VER >= 1800 + static task> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) +#else + static task> _Perform(Concurrency::details::_CancellationTokenState *_PTokenState, _Iterator _Begin, _Iterator _End) +#endif + { + if (_Begin == _End) + { + throw Concurrency::invalid_operation("when_any(begin, end) cannot be called on an empty container."); + } +#if _MSC_VER >= 1800 + Concurrency::details::_CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; +#endif + auto _PParam = new _RunAnyParam, Concurrency::details::_CancellationTokenState *>>(); + + if (_PTokenState) + { + details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, _PTokenState); + _PParam->_M_fHasExplicitToken = true; + } +#if _MSC_VER >= 1800 + task_options _Options(_TaskOptions); + _Options.set_cancellation_token(_PParam->_M_cancellationSource.get_token()); + task, Concurrency::details::_CancellationTokenState *>> _Any_tasks_completed(_PParam->_M_Completed, _Options); +#else + task, Concurrency::details::_CancellationTokenState *>> _Any_tasks_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); + _Any_tasks_completed._GetImpl()->_M_fRuntimeAggregate = true; +#endif + // Keep a copy ref to the token source + auto _CancellationSource = _PParam->_M_cancellationSource; + + _PParam->_M_numTasks = static_cast(std::distance(_Begin, _End)); + size_t index = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + if (_PTask->is_apartment_aware()) + { + _Any_tasks_completed._SetAsync(); + } + + _PTask->_Then([_PParam, index](task<_ElementType> _ResultTask) -> HRESULT { +#if _MSC_VER >= 1800 + auto _PParamCopy = _PParam; // Dev10 + auto _IndexCopy = index; // Dev10 + auto _Func = [&_ResultTask, _PParamCopy, _IndexCopy]() { + _PParamCopy->_M_Completed.set(std::make_pair(std::make_pair(_ResultTask._GetImpl()->_GetResult(), _IndexCopy), _ResultTask._GetImpl()->_M_pTokenState)); + }; +#else + auto _Func = [&_ResultTask, _PParam, index]() { + _PParam->_M_Completed.set(std::make_pair(std::make_pair(_ResultTask._GetImpl()->_GetResult(), index), _ResultTask._GetImpl()->_M_pTokenState)); + }; +#endif + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + return S_OK; +#if _MSC_VER >= 1800 + }, Concurrency::details::_CancellationTokenState::_None()); +#else + }, Concurrency::details::_CancellationTokenState::_None(), false); +#endif + index++; + } + + // All _Any_tasks_completed._SetAsync() must be finished before this return continuation task being created. + return _Any_tasks_completed._Then([=](std::pair, Concurrency::details::_CancellationTokenState *> _Result, std::pair<_ElementType, size_t>* retVal) -> HRESULT { + _CONCRT_ASSERT(_Result.second); + if (!_PTokenState) + { + details::_JoinAllTokens_Add(_CancellationSource, _Result.second); + } + *retVal = _Result.first; + return S_OK; +#if _MSC_VER >= 1800 + }, nullptr); +#else + }, nullptr, true); +#endif + } + }; + + template + struct _WhenAnyImpl + { +#if _MSC_VER >= 1800 + static task _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End) +#else + static task _Perform(Concurrency::details::_CancellationTokenState *_PTokenState, _Iterator _Begin, _Iterator _End) +#endif + { + if (_Begin == _End) + { + throw Concurrency::invalid_operation("when_any(begin, end) cannot be called on an empty container."); + } +#if _MSC_VER >= 1800 + Concurrency::details::_CancellationTokenState *_PTokenState = _TaskOptions.has_cancellation_token() ? _TaskOptions.get_cancellation_token()._GetImplValue() : nullptr; +#endif + auto _PParam = new _RunAnyParam>(); + + if (_PTokenState) + { + details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, _PTokenState); + _PParam->_M_fHasExplicitToken = true; + } + +#if _MSC_VER >= 1800 + task_options _Options(_TaskOptions); + _Options.set_cancellation_token(_PParam->_M_cancellationSource.get_token()); + task> _Any_tasks_completed(_PParam->_M_Completed, _Options); +#else + task> _Any_tasks_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); +#endif + // Keep a copy ref to the token source + auto _CancellationSource = _PParam->_M_cancellationSource; + + _PParam->_M_numTasks = static_cast(std::distance(_Begin, _End)); + size_t index = 0; + for (auto _PTask = _Begin; _PTask != _End; ++_PTask) + { + if (_PTask->is_apartment_aware()) + { + _Any_tasks_completed._SetAsync(); + } + + _PTask->_Then([_PParam, index](task _ResultTask) -> HRESULT { +#if _MSC_VER >= 1800 + auto _PParamCopy = _PParam; // Dev10 + auto _IndexCopy = index; // Dev10 + auto _Func = [&_ResultTask, _PParamCopy, _IndexCopy]() { + _PParamCopy->_M_Completed.set(std::make_pair(_IndexCopy, _ResultTask._GetImpl()->_M_pTokenState)); + }; +#else + auto _Func = [&_ResultTask, _PParam, index]() { + _PParam->_M_Completed.set(std::make_pair(index, _ResultTask._GetImpl()->_M_pTokenState)); + }; +#endif + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + return S_OK; +#if _MSC_VER >= 1800 + }, Concurrency::details::_CancellationTokenState::_None()); +#else + }, Concurrency::details::_CancellationTokenState::_None(), false); +#endif + + index++; + } + + // All _Any_tasks_completed._SetAsync() must be finished before this return continuation task being created. + return _Any_tasks_completed._Then([=](std::pair _Result, size_t* retVal) -> HRESULT { + _CONCRT_ASSERT(_Result.second); + if (!_PTokenState) + { + details::_JoinAllTokens_Add(_CancellationSource, _Result.second); + } + *retVal = _Result.first; + return S_OK; +#if _MSC_VER >= 1800 + }, nullptr); +#else + }, nullptr, false); +#endif + } + }; +} // namespace details + +/// +/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. +/// +/// +/// The type of the input iterator. +/// +/// +/// The position of the first element in the range of elements to be combined into the resulting task. +/// +/// +/// The position of the first element beyond the range of elements to be combined into the resulting task. +/// +/// +/// A task that completes successfully when any one of the input tasks has completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::pair<T, size_t>>>, where the first element of the pair is the result +/// of the completing task, and the second element is the index of the task that finished. If the input tasks are of type void +/// the output is a task<size_t>, where the result is the index of the completing task. +/// +/// +/**/ +template +#if _MSC_VER >= 1800 +auto when_any(_Iterator _Begin, _Iterator _End, const task_options& _TaskOptions = task_options()) +-> decltype (details::_WhenAnyImpl::value_type::result_type, _Iterator>::_Perform(_TaskOptions, _Begin, _End)) +{ + typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; + return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(_TaskOptions, _Begin, _End); +} +#else +auto when_any(_Iterator _Begin, _Iterator _End) +-> decltype (details::_WhenAnyImpl::value_type::result_type, _Iterator>::_Perform(nullptr, _Begin, _End)) +{ + typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; + return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(nullptr, _Begin, _End); +} +#endif + +/// +/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. +/// +/// +/// The type of the input iterator. +/// +/// +/// The position of the first element in the range of elements to be combined into the resulting task. +/// +/// +/// The position of the first element beyond the range of elements to be combined into the resulting task. +/// +/// +/// The cancellation token which controls cancellation of the returned task. If you do not provide a cancellation token, the resulting +/// task will receive the cancellation token of the task that causes it to complete. +/// +/// +/// A task that completes successfully when any one of the input tasks has completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::pair<T, size_t>>>, where the first element of the pair is the result +/// of the completing task, and the second element is the index of the task that finished. If the input tasks are of type void +/// the output is a task<size_t>, where the result is the index of the completing task. +/// +/// +/**/ +template +auto when_any(_Iterator _Begin, _Iterator _End, Concurrency::cancellation_token _CancellationToken) +-> decltype (details::_WhenAnyImpl::value_type::result_type, _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End)) +{ + typedef typename std::iterator_traits<_Iterator>::value_type::result_type _ElementType; + return details::_WhenAnyImpl<_ElementType, _Iterator>::_Perform(_CancellationToken._GetImplValue(), _Begin, _End); +} + +/// +/// Creates a task that will complete successfully when either of the tasks supplied as arguments completes successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task +/// will also be a task<void>. +/// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence +/// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> +/// and the other one is of type task<T>. +/// +/// +/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, +/// if any are encountered, will be thrown when you call get() or wait() on that task. +/// +/// +/**/ +template +task<_ReturnType> operator||(const task<_ReturnType> & _Lhs, const task<_ReturnType> & _Rhs) +{ +#if _MSC_VER >= 1800 + auto _PParam = new details::_RunAnyParam>(); + + task> _Any_tasks_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); + // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, + // So that _PParam can be used before it getting deleted. + auto _ReturnTask = _Any_tasks_completed._Then([=](std::pair<_ReturnType, size_t> _Ret, _ReturnType* retVal) -> HRESULT { + _CONCRT_ASSERT(_Ret.second); + details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, reinterpret_cast(_Ret.second)); + *retVal = _Ret.first; + return S_OK; + }, nullptr); +#else + auto _PParam = new details::_RunAnyParam>(); + + task> _Any_tasks_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); + // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, + // So that _PParam can be used before it getting deleted. + auto _ReturnTask = _Any_tasks_completed._Then([=](std::pair<_ReturnType, Concurrency::details::_CancellationTokenState *> _Ret, _ReturnType* retVal) -> HRESULT { + _CONCRT_ASSERT(_Ret.second); + details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, _Ret.second); + *retVal = _Ret.first; + return S_OK; + }, nullptr, false); +#endif + if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PParam->_M_numTasks = 2; + auto _Continuation = [_PParam](task<_ReturnType> _ResultTask) -> HRESULT { +#if _MSC_VER >= 1800 + // Dev10 compiler bug + auto _PParamCopy = _PParam; + auto _Func = [&_ResultTask, _PParamCopy]() { + _PParamCopy->_M_Completed.set(std::make_pair(_ResultTask._GetImpl()->_GetResult(), reinterpret_cast(_ResultTask._GetImpl()->_M_pTokenState))); + }; +#else + auto _Func = [&_ResultTask, _PParam]() { + _PParam->_M_Completed.set(std::make_pair(_ResultTask._GetImpl()->_GetResult(), _ResultTask._GetImpl()->_M_pTokenState)); + }; +#endif + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + return S_OK; + }; + +#if _MSC_VER >= 1800 + _Lhs._Then(_Continuation, Concurrency::details::_CancellationTokenState::_None()); + _Rhs._Then(_Continuation, Concurrency::details::_CancellationTokenState::_None()); +#else + _Lhs._Then(_Continuation, Concurrency::details::_CancellationTokenState::_None(), false); + _Rhs._Then(_Continuation, Concurrency::details::_CancellationTokenState::_None(), false); +#endif + return _ReturnTask; +} + +/// +/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task +/// will also be a task<void>. +/// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence +/// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> +/// and the other one is of type task<T>. +/// +/// +/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, +/// if any are encountered, will be thrown when you call get() or wait() on that task. +/// +/// +/**/ +template +task> operator||(const task> & _Lhs, const task<_ReturnType> & _Rhs) +{ + auto _PParam = new details::_RunAnyParam, Concurrency::details::_CancellationTokenState *>>(); + + task, Concurrency::details::_CancellationTokenState *>> _Any_tasks_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); +#if _MSC_VER < 1800 + _Any_tasks_completed._GetImpl()->_M_fRuntimeAggregate = true; +#endif + // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, + // So that _PParam can be used before it getting deleted. + auto _ReturnTask = _Any_tasks_completed._Then([=](std::pair, Concurrency::details::_CancellationTokenState *> _Ret, std::vector<_ReturnType>* retVal) -> HRESULT { + _CONCRT_ASSERT(_Ret.second); + details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, _Ret.second); + *retVal = _Ret.first; + return S_OK; + }, nullptr, true); + + if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PParam->_M_numTasks = 2; + _Lhs._Then([_PParam](task> _ResultTask) -> HRESULT { +#if _MSC_VER >= 1800 + // Dev10 compiler bug + auto _PParamCopy = _PParam; + auto _Func = [&_ResultTask, _PParamCopy]() { + auto _Result = _ResultTask._GetImpl()->_GetResult(); + _PParamCopy->_M_Completed.set(std::make_pair(_Result, _ResultTask._GetImpl()->_M_pTokenState)); + }; +#else + auto _Func = [&_ResultTask, _PParam]() { + std::vector<_ReturnType> _Result = _ResultTask._GetImpl()->_GetResult(); + _PParam->_M_Completed.set(std::make_pair(_Result, _ResultTask._GetImpl()->_M_pTokenState)); + }; +#endif + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + return S_OK; +#if _MSC_VER >= 1800 + }, Concurrency::details::_CancellationTokenState::_None()); +#else + }, Concurrency::details::_CancellationTokenState::_None(), false); +#endif + _Rhs._Then([_PParam](task<_ReturnType> _ResultTask) -> HRESULT { +#if _MSC_VER >= 1800 + // Dev10 compiler bug + typedef _ReturnType _ReturnTypeDev10; + auto _PParamCopy = _PParam; + auto _Func = [&_ResultTask, _PParamCopy]() { + auto _Result = _ResultTask._GetImpl()->_GetResult(); + + std::vector<_ReturnTypeDev10> _Vec; + _Vec.push_back(_Result); + _PParamCopy->_M_Completed.set(std::make_pair(_Vec, _ResultTask._GetImpl()->_M_pTokenState)); + }; +#else + auto _Func = [&_ResultTask, _PParam]() { + _ReturnType _Result = _ResultTask._GetImpl()->_GetResult(); + + std::vector<_ReturnType> _Vec; + _Vec.push_back(_Result); + _PParam->_M_Completed.set(std::make_pair(_Vec, _ResultTask._GetImpl()->_M_pTokenState)); + }; +#endif + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + return S_OK; +#if _MSC_VER >= 1800 + }, Concurrency::details::_CancellationTokenState::_None()); +#else + }, Concurrency::details::_CancellationTokenState::_None(), false); +#endif + return _ReturnTask; +} + +/// +/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task +/// will also be a task<void>. +/// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence +/// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> +/// and the other one is of type task<T>. +/// +/// +/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, +/// if any are encountered, will be thrown when you call get() or wait() on that task. +/// +/// +/**/ +template +task> operator||(const task<_ReturnType> & _Lhs, const task> & _Rhs) +{ + return _Rhs || _Lhs; +} + +/// +/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully. +/// +/// +/// The type of the returned task. +/// +/// +/// The first task to combine into the resulting task. +/// +/// +/// The second task to combine into the resulting task. +/// +/// +/// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type T, +/// the output of this function will be a task<std::vector<T>. If the input tasks are of type void the output task +/// will also be a task<void>. +/// To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence +/// over ||, the operator|| produces a task<std::vector<T>> if one of the tasks is of type task<std::vector<T>> +/// and the other one is of type task<T>. +/// +/// +/// If both of the tasks are canceled or throw exceptions, the returned task will complete in the canceled state, and one of the exceptions, +/// if any are encountered, will be thrown when you call get() or wait() on that task. +/// +/// +/**/ +inline task operator||(const task & _Lhs, const task & _Rhs) +{ + auto _PParam = new details::_RunAnyParam>(); + + task> _Any_task_completed(_PParam->_M_Completed, _PParam->_M_cancellationSource.get_token()); + // Chain the return continuation task here to ensure it will get inline execution when _M_Completed.set is called, + // So that _PParam can be used before it getting deleted. + auto _ReturnTask = _Any_task_completed._Then([=](std::pair _Ret) -> HRESULT { + _CONCRT_ASSERT(_Ret.second); + details::_JoinAllTokens_Add(_PParam->_M_cancellationSource, _Ret.second); + return S_OK; +#if _MSC_VER >= 1800 + }, nullptr); +#else + }, nullptr, false); +#endif + + if (_Lhs.is_apartment_aware() || _Rhs.is_apartment_aware()) + { + _ReturnTask._SetAsync(); + } + + _PParam->_M_numTasks = 2; + auto _Continuation = [_PParam](task _ResultTask) mutable -> HRESULT { + // Dev10 compiler needs this. + auto _PParam1 = _PParam; + auto _Func = [&_ResultTask, _PParam1]() { + _PParam1->_M_Completed.set(std::make_pair(details::_Unit_type(), _ResultTask._GetImpl()->_M_pTokenState)); + }; + _WhenAnyContinuationWrapper(_PParam, _Func, _ResultTask); + return S_OK; + }; + +#if _MSC_VER >= 1800 + _Lhs._Then(_Continuation, Concurrency::details::_CancellationTokenState::_None()); + _Rhs._Then(_Continuation, Concurrency::details::_CancellationTokenState::_None()); +#else + _Lhs._Then(_Continuation, Concurrency::details::_CancellationTokenState::_None(), false); + _Rhs._Then(_Continuation, Concurrency::details::_CancellationTokenState::_None(), false); +#endif + + return _ReturnTask; +} + +#if _MSC_VER >= 1800 +template +task<_Ty> task_from_result(_Ty _Param, const task_options& _TaskOptions = task_options()) +{ + task_completion_event<_Ty> _Tce; + _Tce.set(_Param); + return create_task<_Ty>(_Tce, _TaskOptions); +} + +// Work around VS 2010 compiler bug +#if _MSC_VER == 1600 +inline task task_from_result(bool _Param) +{ + task_completion_event _Tce; + _Tce.set(_Param); + return create_task(_Tce, task_options()); +} +#endif +inline task task_from_result(const task_options& _TaskOptions = task_options()) +{ + task_completion_event _Tce; + _Tce.set(); + return create_task(_Tce, _TaskOptions); +} + +template +task<_TaskType> task_from_exception(_ExType _Exception, const task_options& _TaskOptions = task_options()) +{ + task_completion_event<_TaskType> _Tce; + _Tce.set_exception(_Exception); + return create_task<_TaskType>(_Tce, _TaskOptions); +} + +namespace details +{ + /// + /// A convenient extension to Concurrency: loop until a condition is no longer met + /// + /// + /// A function representing the body of the loop. It will be invoked at least once and + /// then repetitively as long as it returns true. + /// + inline + task do_while(std::function(void)> func) + { + task first = func(); + return first.then([=](bool guard, task* retVal) -> HRESULT { + if (guard) + *retVal = do_while(func); + else + *retVal = first; + return S_OK; + }); + } + +} // namespace details +#endif + +} // namespace Concurrency_winrt + +namespace concurrency_winrt = Concurrency_winrt; + +#pragma pop_macro("new") +#pragma warning(pop) +#pragma pack(pop) +#endif + +#endif