diff --git a/modules/highgui/CMakeLists.txt b/modules/highgui/CMakeLists.txt index b9908cbad..98090b6a9 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 000000000..99fbf4185 --- /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 df52f04bd..d08829eb3 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,45 +366,12 @@ 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); - - const HANDLE ig_hFrameReady; - const HANDLE ig_hFrameGrabbed; - const HANDLE ig_hFinish; - -private: - bool ig_RIE; - bool ig_Close; - bool ig_Synchronous; - long m_cRef; - 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); @@ -222,6 +384,89 @@ private: LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer, DWORD dwSampleSize); STDMETHODIMP OnShutdown(); + + const HANDLE ig_hFrameReady; + const HANDLE ig_hFrameGrabbed; + const HANDLE ig_hFinish; +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; + 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); + + 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); } -HRESULT ImageGrabber::initImageGrabber(IMFMediaSource *pSource, GUID VideoFormat) +#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) { - ComPtr pSinkActivate = NULL; - ComPtr pType = NULL; - ComPtr pPD = NULL; - ComPtr pSD = NULL; - ComPtr pHandler = NULL; - ComPtr pCurrentType = NULL; 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 ImageGrabberWinRT::startGrabbing(MAKE_WRL_REF(_AsyncAction)* action) +{ + 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; } -unsigned int videoInput::getCountFormats(int deviceID) +#ifdef HAVE_WINRT +void videoInput::waitForDevice(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) + { + 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) +{ + if (deviceID < 0) + { + 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 000000000..c03f1ecc3 --- /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 000000000..1243baea9 --- /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