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 <windows.h>
#include <guiddef.h>
#include <mfidl.h>
#include <Mfapi.h>
#include <mfplay.h>
#include <mfobjects.h>
+#include <tchar.h>
#include <strsafe.h>
#include <Mfreadwrite.h>
#include <new>
#pragma comment(lib, "Mfreadwrite")
#pragma comment(lib, "MinCore_Downlevel")
-// for ComPtr usage
+#include <mferror.h>
+
+#ifdef HAVE_WINRT
+ // for ComPtr usage
#include <wrl/client.h>
-using namespace Microsoft::WRL;
+#ifdef __cplusplus_winrt
+#include <agile.h>
+#include <vccorlib.h>
+#endif
+
+#include <wrl\async.h>
+#include <wrl\implements.h>
+#include <wrl\module.h>
+#include <wrl\wrappers\corewrappers.h>
+#include <windows.media.capture.h>
+#include <windows.devices.enumeration.h>
+#ifdef HAVE_CONCURRENCY
+#include <concrt.h>
+#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<const __s_GUID&>(__guid).Data1),
+__b(reinterpret_cast<const __s_GUID&>(__guid).Data2),
+__c(reinterpret_cast<const __s_GUID&>(__guid).Data3),
+__d(reinterpret_cast<const __s_GUID&>(__guid).Data4[0]),
+__e(reinterpret_cast<const __s_GUID&>(__guid).Data4[1]),
+__f(reinterpret_cast<const __s_GUID&>(__guid).Data4[2]),
+__g(reinterpret_cast<const __s_GUID&>(__guid).Data4[3]),
+__h(reinterpret_cast<const __s_GUID&>(__guid).Data4[4]),
+__i(reinterpret_cast<const __s_GUID&>(__guid).Data4[5]),
+__j(reinterpret_cast<const __s_GUID&>(__guid).Data4[6]),
+__k(reinterpret_cast<const __s_GUID&>(__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<unsigned long*>(&__aArg);
+ auto __b = reinterpret_cast<unsigned long*>(&__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<unsigned long*>(&__aArg);
+ auto __b = reinterpret_cast<unsigned long*>(&__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 <atlbase.h>
+#endif
struct IMFMediaType;
+#ifndef HAVE_WINRT
struct IMFActivate;
struct IMFMediaSource;
+#endif
struct IMFAttributes;
namespace
}
}
-/// 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
RawImage(unsigned int size);
};
-// Class for grabbing image from video stream
-class ImageGrabber : public IMFSampleGrabberSinkCallback
+class ImageGrabberCallback : public IMFSampleGrabberSinkCallback
{
public:
- ~ImageGrabber(void);
- HRESULT initImageGrabber(IMFMediaSource *pSource, GUID VideoFormat);
- HRESULT startGrabbing(void);
void pauseGrabbing();
void resumeGrabbing();
- void stopGrabbing();
RawImage *getRawImage();
- // Function of creation of the instance of the class
- static HRESULT CreateInstance(ImageGrabber **ppIG, unsigned int deviceID, bool synchronous = false);
+ // IMFClockStateSink methods
+ STDMETHODIMP OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset);
+ STDMETHODIMP OnClockStop(MFTIME hnsSystemTime);
+ STDMETHODIMP OnClockPause(MFTIME hnsSystemTime);
+ STDMETHODIMP OnClockRestart(MFTIME hnsSystemTime);
+ STDMETHODIMP OnClockSetRate(MFTIME hnsSystemTime, float flRate);
+ // IMFSampleGrabberSinkCallback methods
+ STDMETHODIMP OnSetPresentationClock(IMFPresentationClock* pClock);
+ STDMETHODIMP OnProcessSample(REFGUID guidMajorMediaType, DWORD dwSampleFlags,
+ LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer,
+ DWORD dwSampleSize);
+ STDMETHODIMP OnShutdown();
const HANDLE ig_hFrameReady;
const HANDLE ig_hFrameGrabbed;
const HANDLE ig_hFinish;
-
-private:
+protected:
+ ImageGrabberCallback(bool synchronous);
bool ig_RIE;
bool ig_Close;
bool ig_Synchronous;
long m_cRef;
+
+ RawImage *ig_RIFirst;
+ RawImage *ig_RISecond;
+ RawImage *ig_RIOut;
+private:
+ ImageGrabberCallback& operator=(const ImageGrabberCallback&); // Declared to fix compilation warning.
+ };
+
+#ifdef HAVE_WINRT
+extern const __declspec(selectany) WCHAR RuntimeClass_CV_ImageGrabberWinRT[] = L"cv.ImageGrabberWinRT";
+
+class ImageGrabberWinRT :
+ public Microsoft::WRL::RuntimeClass<
+ Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::RuntimeClassType::WinRtClassicComMix>,
+ IMFSampleGrabberSinkCallback>, public ImageGrabberCallback
+{
+ InspectableClass(RuntimeClass_CV_ImageGrabberWinRT, BaseTrust)
+public:
+ ImageGrabberWinRT(bool synchronous);
+ ~ImageGrabberWinRT(void);
+
+ HRESULT initImageGrabber(MAKE_WRL_REF(_MediaCapture) pSource,
+ GUID VideoFormat);
+ HRESULT startGrabbing(MAKE_WRL_REF(_AsyncAction)* action);
+ HRESULT stopGrabbing(MAKE_WRL_REF(_AsyncAction)* action);
+ // IMFClockStateSink methods
+ STDMETHODIMP OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset) { return ImageGrabberCallback::OnClockStart(hnsSystemTime, llClockStartOffset); }
+ STDMETHODIMP OnClockStop(MFTIME hnsSystemTime) { return ImageGrabberCallback::OnClockStop(hnsSystemTime); }
+ STDMETHODIMP OnClockPause(MFTIME hnsSystemTime) { return ImageGrabberCallback::OnClockPause(hnsSystemTime); }
+ STDMETHODIMP OnClockRestart(MFTIME hnsSystemTime) { return ImageGrabberCallback::OnClockRestart(hnsSystemTime); }
+ STDMETHODIMP OnClockSetRate(MFTIME hnsSystemTime, float flRate) { return ImageGrabberCallback::OnClockSetRate(hnsSystemTime, flRate); }
+ // IMFSampleGrabberSinkCallback methods
+ STDMETHODIMP OnSetPresentationClock(IMFPresentationClock* pClock) { return ImageGrabberCallback::OnSetPresentationClock(pClock); }
+ STDMETHODIMP OnProcessSample(REFGUID guidMajorMediaType, DWORD dwSampleFlags,
+ LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer,
+ DWORD dwSampleSize) { return ImageGrabberCallback::OnProcessSample(guidMajorMediaType, dwSampleFlags, llSampleTime, llSampleDuration, pSampleBuffer, dwSampleSize); }
+ STDMETHODIMP OnShutdown() { return ImageGrabberCallback::OnShutdown(); }
+ // Function of creation of the instance of the class
+ static HRESULT CreateInstance(ImageGrabberWinRT **ppIG, bool synchronous = false);
+private:
+ MAKE_WRL_AGILE_REF(_MediaCapture) ig_pMedCapSource;
+ MediaSink* ig_pMediaSink;
+};
+#endif
+
+// Class for grabbing image from video stream
+class ImageGrabber : public ImageGrabberCallback
+{
+public:
+ ~ImageGrabber(void);
+ HRESULT initImageGrabber(IMFMediaSource *pSource);
+ HRESULT startGrabbing(void);
+ void stopGrabbing();
+ // IUnknown methods
+ STDMETHODIMP QueryInterface(REFIID iid, void** ppv);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+ // Function of creation of the instance of the class
+ static HRESULT CreateInstance(ImageGrabber **ppIG, unsigned int deviceID, bool synchronous = false);
+
+private:
unsigned int ig_DeviceID;
+
IMFMediaSource *ig_pSource;
IMFMediaSession *ig_pSession;
IMFTopology *ig_pTopology;
- RawImage *ig_RIFirst;
- RawImage *ig_RISecond;
- RawImage *ig_RIOut;
ImageGrabber(unsigned int deviceID, bool synchronous);
HRESULT CreateTopology(IMFMediaSource *pSource, IMFActivate *pSinkActivate, IMFTopology **ppTopo);
HRESULT AddSourceNode(IMFTopology *pTopology, IMFMediaSource *pSource,
IMFPresentationDescriptor *pPD, IMFStreamDescriptor *pSD, IMFTopologyNode **ppNode);
HRESULT AddOutputNode(IMFTopology *pTopology, IMFActivate *pActivate, DWORD dwId, IMFTopologyNode **ppNode);
- // IUnknown methods
- STDMETHODIMP QueryInterface(REFIID iid, void** ppv);
- STDMETHODIMP_(ULONG) AddRef();
- STDMETHODIMP_(ULONG) Release();
- // IMFClockStateSink methods
- STDMETHODIMP OnClockStart(MFTIME hnsSystemTime, LONGLONG llClockStartOffset);
- STDMETHODIMP OnClockStop(MFTIME hnsSystemTime);
- STDMETHODIMP OnClockPause(MFTIME hnsSystemTime);
- STDMETHODIMP OnClockRestart(MFTIME hnsSystemTime);
- STDMETHODIMP OnClockSetRate(MFTIME hnsSystemTime, float flRate);
- // IMFSampleGrabberSinkCallback methods
- STDMETHODIMP OnSetPresentationClock(IMFPresentationClock* pClock);
- STDMETHODIMP OnProcessSample(REFGUID guidMajorMediaType, DWORD dwSampleFlags,
- LONGLONG llSampleTime, LONGLONG llSampleDuration, const BYTE * pSampleBuffer,
- DWORD dwSampleSize);
- STDMETHODIMP OnShutdown();
+
+ ImageGrabber& operator=(const ImageGrabber&); // Declared to fix comiplation error.
};
/// Class for controlling of thread of the grabbing raw data from video device
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);
CamParametrs vd_PrevParametrs;
unsigned int vd_Width;
unsigned int vd_Height;
+ unsigned int vd_FrameRate;
unsigned int vd_CurrentNumber;
bool vd_IsSetuped;
std::map<UINT64, FrameRateMap> vd_CaptureFormats;
std::vector<MediaType> vd_CurrentFormats;
IMFMediaSource *vd_pSource;
+#ifdef HAVE_WINRT
+ MAKE_WRL_AGILE_REF(_MediaCapture) vd_pMedCap;
+ EventRegistrationToken vd_cookie;
+ ImageGrabberWinRT *vd_pImGr;
+ DEFINE_TASK<void> 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<void>* 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
{
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<void> vds_enumTask;
+#endif
std::vector<videoDevice *> vds_Devices;
videoDevices(void);
};
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
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);
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)
{
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);
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;
}
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;
}
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;
}
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;
#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),
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)
SafeRelease(&ig_pSession);
SafeRelease(&ig_pTopology);
- DebugPrintOut *DPO = &DebugPrintOut::getInstance();
- DPO->printOut(L"IMAGEGRABBER VIDEODEVICE %i: Destroing instance of the ImageGrabber class\n", ig_DeviceID);
+ DebugPrintOut(L"IMAGEGRABBER VIDEODEVICE %i: Destroying instance of the ImageGrabber class\n", ig_DeviceID);
+}
+
+#ifdef HAVE_WINRT
+
+ImageGrabberWinRT::ImageGrabberWinRT(bool synchronous):
+ ImageGrabberCallback(synchronous),
+ ig_pMediaSink(NULL)
+{
+ ig_pMedCapSource = nullptr;
+}
+
+ImageGrabberWinRT::~ImageGrabberWinRT(void)
+{
+ //stop must already be performed and complete by object owner
+ if (ig_pMediaSink != NULL) {
+ ((IMFMediaSink*)ig_pMediaSink)->Shutdown();
+ }
+ SafeRelease(&ig_pMediaSink);
+ RELEASE_AGILE_WRL(ig_pMedCapSource)
+
+ CloseHandle(ig_hFinish);
+
+ if (ig_Synchronous)
+ {
+ CloseHandle(ig_hFrameReady);
+ CloseHandle(ig_hFrameGrabbed);
+ }
+
+ DebugPrintOut(L"IMAGEGRABBER VIDEODEVICE: Destroying instance of the ImageGrabberWinRT class\n");
+}
+
+HRESULT ImageGrabberWinRT::initImageGrabber(MAKE_WRL_REF(_MediaCapture) pSource,
+ GUID VideoFormat)
+{
+ HRESULT hr;
+ MAKE_WRL_OBJ(_VideoDeviceController) pDevCont;
+ WRL_PROP_GET(pSource, VideoDeviceController, pDevCont, hr)
+ if (FAILED(hr)) return hr;
+ GET_WRL_OBJ_FROM_OBJ(_MediaDeviceController, pMedDevCont, pDevCont, hr)
+ if (FAILED(hr)) return hr;
+ MAKE_WRL_OBJ(_MediaEncodingProperties) pMedEncProps;
+ WRL_METHOD(pMedDevCont, GetMediaStreamProperties, pMedEncProps, hr, WRL_ENUM_GET(_MediaStreamType, MediaStreamType, VideoPreview))
+ if (FAILED(hr)) return hr;
+ GET_WRL_OBJ_FROM_OBJ(_VideoEncodingProperties, pVidProps, pMedEncProps, hr);
+ if (FAILED(hr)) return hr;
+ _ComPtr<IMFMediaType> pType = NULL;
+ hr = MediaSink::ConvertPropertiesToMediaType(DEREF_AS_NATIVE_WRL_OBJ(ABI::Windows::Media::MediaProperties::IMediaEncodingProperties, pMedEncProps), &pType);
+ if (FAILED(hr)) return hr;
+ MediaType MT = FormatReader::Read(pType.Get());
+ unsigned int sizeRawImage = 0;
+ if(VideoFormat == MFVideoFormat_RGB24)
+ {
+ sizeRawImage = MT.MF_MT_FRAME_SIZE * 3;
+ }
+ else if(VideoFormat == MFVideoFormat_RGB32)
+ {
+ sizeRawImage = MT.MF_MT_FRAME_SIZE * 4;
+ }
+ sizeRawImage = MT.MF_MT_SAMPLE_SIZE;
+ CHECK_HR(hr = RawImage::CreateInstance(&ig_RIFirst, sizeRawImage));
+ CHECK_HR(hr = RawImage::CreateInstance(&ig_RISecond, sizeRawImage));
+ ig_RIOut = ig_RISecond;
+ ig_pMedCapSource = pSource;
+done:
+ return hr;
+}
+
+HRESULT ImageGrabberWinRT::stopGrabbing(MAKE_WRL_REF(_AsyncAction)* action)
+{
+ HRESULT hr = S_OK;
+ if (ig_pMedCapSource != nullptr) {
+ GET_WRL_OBJ_FROM_REF(_MediaCaptureVideoPreview, imedPrevCap, DEREF_AGILE_WRL_OBJ(ig_pMedCapSource), hr)
+ if (FAILED(hr)) return hr;
+ MAKE_WRL_REF(_AsyncAction) pAction;
+ WRL_METHOD_BASE(imedPrevCap, StopPreviewAsync, pAction, hr)
+ if (SUCCEEDED(hr)) {
+#ifdef HAVE_CONCURRENCY
+ DEFINE_TASK<void> _task = CREATE_TASK DEFINE_RET_TYPE(void)(pAction);
+ *action = BEGIN_CREATE_ASYNC(void, _task, this)
+ HRESULT hr = S_OK;
+ _task.wait();
+ SafeRelease(&ig_pMediaSink);
+ SetEvent(ig_hFinish);
+ END_CREATE_ASYNC(hr);
+#else
+ *action = nullptr;
+#endif
+ }
+ }
+ return hr;
}
-HRESULT ImageGrabber::initImageGrabber(IMFMediaSource *pSource, GUID VideoFormat)
+HRESULT ImageGrabberWinRT::startGrabbing(MAKE_WRL_REF(_AsyncAction)* action)
{
- ComPtr<IMFActivate> pSinkActivate = NULL;
- ComPtr<IMFMediaType> pType = NULL;
- ComPtr<IMFPresentationDescriptor> pPD = NULL;
- ComPtr<IMFStreamDescriptor> pSD = NULL;
- ComPtr<IMFMediaTypeHandler> pHandler = NULL;
- ComPtr<IMFMediaType> pCurrentType = NULL;
HRESULT hr = S_OK;
+ GET_WRL_OBJ_FROM_REF(_MediaCaptureVideoPreview, imedPrevCap, DEREF_AGILE_WRL_OBJ(ig_pMedCapSource), hr)
+ if (FAILED(hr)) return hr;
+ ACTIVATE_OBJ(RuntimeClass_Windows_Foundation_Collections_PropertySet, _PropertySet, pSet, hr)
+ if (FAILED(hr)) return hr;
+ GET_WRL_OBJ_FROM_OBJ(_Map, spSetting, pSet, hr)
+ if (FAILED(hr)) return hr;
+ ACTIVATE_STATIC_OBJ(RuntimeClass_Windows_Foundation_PropertyValue, MAKE_WRL_OBJ(_PropertyValueStatics), spPropVal, hr)
+ if (FAILED(hr)) return hr;
+ _ObjectObj pVal;
+ boolean bReplaced;
+ WRL_METHOD(spPropVal, CreateUInt32, pVal, hr, (unsigned int)WRL_ENUM_GET(_MediaStreamType, MediaStreamType, VideoPreview))
+ if (FAILED(hr)) return hr;
+ WRL_METHOD(spSetting, Insert, bReplaced, hr, DEREF_WRL_OBJ(_StringReference(MF_PROP_VIDTYPE)), DEREF_WRL_OBJ(pVal))
+ if (FAILED(hr)) return hr;
+ WRL_METHOD(spSetting, Insert, bReplaced, hr, DEREF_WRL_OBJ(_StringReference(MF_PROP_SAMPLEGRABBERCALLBACK)), reinterpret_cast<_Object>(this))
+ if (FAILED(hr)) return hr;
+ MAKE_WRL_OBJ(_VideoDeviceController) pDevCont;
+ WRL_PROP_GET(ig_pMedCapSource, VideoDeviceController, pDevCont, hr)
+ if (FAILED(hr)) return hr;
+ GET_WRL_OBJ_FROM_OBJ(_MediaDeviceController, pMedDevCont, pDevCont, hr)
+ if (FAILED(hr)) return hr;
+ MAKE_WRL_OBJ(_MediaEncodingProperties) pMedEncProps;
+ WRL_METHOD(pMedDevCont, GetMediaStreamProperties, pMedEncProps, hr, WRL_ENUM_GET(_MediaStreamType, MediaStreamType, VideoPreview))
+ if (FAILED(hr)) return hr;
+ GET_WRL_OBJ_FROM_OBJ(_VideoEncodingProperties, pVidProps, pMedEncProps, hr);
+ if (FAILED(hr)) return hr;
+ ACTIVATE_OBJ(RuntimeClass_Windows_Media_MediaProperties_MediaEncodingProfile, _MediaEncodingProfile, pEncProps, hr)
+ if (FAILED(hr)) return hr;
+ WRL_PROP_PUT(pEncProps, Video, DEREF_WRL_OBJ(pVidProps), hr)
+ if (FAILED(hr)) return hr;
+ WRL_METHOD(spSetting, Insert, bReplaced, hr, DEREF_WRL_OBJ(_StringReference(MF_PROP_VIDENCPROPS)), DEREF_WRL_OBJ(pVidProps))
+ if (SUCCEEDED(hr)) {
+ //can start/stop multiple times with same MediaCapture object if using activatable class
+ WRL_METHOD(imedPrevCap, _StartPreviewToCustomSinkIdAsync, *action, hr, DEREF_WRL_OBJ(pEncProps), DEREF_WRL_OBJ(_StringReference(RuntimeClass_CV_MediaSink)), DEREF_WRL_OBJ(pSet))
+ if (FAILED(hr) && hr == REGDB_E_CLASSNOTREG) {
+ hr = Microsoft::WRL::Make<MediaSink>().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<MAKE_WRL_REF(_MediaExtension)>(ig_pMediaSink))
+ }
+ }
+ return hr;
+}
+
+HRESULT ImageGrabberWinRT::CreateInstance(ImageGrabberWinRT **ppIG, bool synchronous)
+{
+ *ppIG = Microsoft::WRL::Make<ImageGrabberWinRT>(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<IMFActivate> pSinkActivate = NULL;
+ _ComPtr<IMFMediaType> pType = NULL;
+ _ComPtr<IMFPresentationDescriptor> pPD = NULL;
+ _ComPtr<IMFStreamDescriptor> pSD = NULL;
+ _ComPtr<IMFMediaTypeHandler> pHandler = NULL;
+ _ComPtr<IMFMediaType> pCurrentType = NULL;
MediaType MT;
// Clean up.
if (ig_pSession)
SafeRelease(&ig_pSession);
SafeRelease(&ig_pTopology);
ig_pSource = pSource;
- hr = pSource->CreatePresentationDescriptor(&pPD);
+ HRESULT hr = pSource->CreatePresentationDescriptor(&pPD);
if (FAILED(hr))
{
goto err;
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):
SafeRelease(&ig_pSession);
SafeRelease(&ig_pTopology);
}
-
return hr;
}
{
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<IMFMediaEvent> pEvent = NULL;
+ _ComPtr<IMFMediaEvent> 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(;;)
{
}
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);
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<IMFPresentationDescriptor> pPD = NULL;
- ComPtr<IMFStreamDescriptor> pSD = NULL;
- ComPtr<IMFMediaTypeHandler> pHandler = NULL;
- ComPtr<IMFTopologyNode> pNode1 = NULL;
- ComPtr<IMFTopologyNode> pNode2 = NULL;
+ _ComPtr<IMFPresentationDescriptor> pPD = NULL;
+ _ComPtr<IMFStreamDescriptor> pSD = NULL;
+ _ComPtr<IMFMediaTypeHandler> pHandler = NULL;
+ _ComPtr<IMFTopologyNode> pNode1 = NULL;
+ _ComPtr<IMFTopologyNode> pNode2 = NULL;
HRESULT hr = S_OK;
DWORD cStreams = 0;
CHECK_HR(hr = MFCreateTopology(&pTopology));
IMFStreamDescriptor *pSD, // Stream descriptor.
IMFTopologyNode **ppNode) // Receives the node pointer.
{
- ComPtr<IMFTopologyNode> pNode = NULL;
+ _ComPtr<IMFTopologyNode> 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));
DWORD dwId, // Identifier of the stream sink.
IMFTopologyNode **ppNode) // Receives the node pointer.
{
- ComPtr<IMFTopologyNode> pNode = NULL;
+ _ComPtr<IMFTopologyNode> pNode = NULL;
HRESULT hr = S_OK;
CHECK_HR(hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, pNode.GetAddressOf()));
CHECK_HR(hr = pNode->SetObject(pActivate));
{
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;
}
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)
{
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;
}
return S_OK;
}
-STDMETHODIMP ImageGrabber::OnShutdown()
+STDMETHODIMP ImageGrabberCallback::OnShutdown()
{
SetEvent(ig_hFinish);
return S_OK;
}
-RawImage *ImageGrabber::getRawImage()
+RawImage *ImageGrabberCallback::getRawImage()
{
return ig_RIOut;
}
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);
}
}
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;
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()
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");
}
}
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<IMFAttributes> pAttributes = NULL;
+#ifdef HAVE_WINRT
+ videoDevices *vDs = &videoDevices::getInstance();
+ hr = vDs->initDevices(WRL_ENUM_GET(_DeviceClass, DeviceClass, VideoCapture));
+#else
+ _ComPtr<IMFAttributes> pAttributes = NULL;
CoInitialize(NULL);
hr = MFCreateAttributes(pAttributes.GetAddressOf(), 1);
if (SUCCEEDED(hr))
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));
}
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)
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<HSTRING>(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<void> 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;
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<void>* 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<MAKE_WRL_REF(_DeviceInformationCollection)>) 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<MAKE_WRL_REF(_DeviceInformation)>) pVector =
+ CREATE_TASK DEFINE_RET_TYPE(MAKE_WRL_REF(_VectorView<MAKE_WRL_REF(_DeviceInformation)>))(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<HSTRING>(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)
{
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
}
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++)
{
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<IMFAttributes> 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<void> 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<void> _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<ABI::Windows::Media::Capture::IMediaCapture*>(DEREF_AGILE_WRL_OBJ(vd_pMedCap))->add_Failed(Microsoft::WRL::Callback<ABI::Windows::Media::Capture::IMediaCaptureFailedEventHandler>([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<IMFAttributes> pAttributes = NULL;
+ IMFActivate *vd_pActivate = NULL;
hr = MFCreateAttributes(pAttributes.GetAddressOf(), 1);
if (SUCCEEDED(hr))
{
}
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;
}
}
int videoDevice::getCountFormats()
{
- return vd_CurrentFormats.size();
+ return (int)vd_CurrentFormats.size();
}
void videoDevice::setEmergencyStopEvent(void *userData, void(*func)(int, void *))
{
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<ABI::Windows::Media::Capture::IMediaCapture*>(DEREF_AGILE_WRL_OBJ(vd_pMedCap))->remove_Failed(vd_cookie);
+ vd_cookie.value = 0;
+ DEFINE_TASK<void> 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)
{
}
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()
else
return 0;
}
+
+unsigned int videoDevice::getFrameRate() const
+{
+ if(vd_IsSetuped)
+ return vd_FrameRate;
+ else
+ return 0;
+}
+
IMFMediaSource *videoDevice::getMediaSource()
{
IMFMediaSource *out = NULL;
}
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<UINT64, FrameRateMap>::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<UINT64, SUBTYPEMap>::iterator f = FRM.begin();
for(; f != FRM.end(); f++)
{
+ // Looking for highest possible frame rate.
if((*f).first >= frameRateMax)
{
frameRateMax = (*f).first;
std::map<UINT64, SUBTYPEMap>::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<String, vectorNum>::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];
}
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<MAKE_WRL_REF(_MediaEncodingProperties)>) 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<IMFPresentationDescriptor> pPD = NULL;
- ComPtr<IMFStreamDescriptor> pSD = NULL;
- ComPtr<IMFMediaTypeHandler> pHandler = NULL;
- ComPtr<IMFMediaType> pType = NULL;
+ _ComPtr<IMFPresentationDescriptor> pPD = NULL;
+ _ComPtr<IMFStreamDescriptor> pSD = NULL;
+ _ComPtr<IMFMediaTypeHandler> pHandler = NULL;
+ _ComPtr<IMFMediaType> pType = NULL;
HRESULT hr = pSource->CreatePresentationDescriptor(pPD.GetAddressOf());
if (FAILED(hr))
{
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;
}
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<void> 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();
}
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<void> _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()
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<MAKE_WRL_REF(_MediaEncodingProperties)>) 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<IMFMediaType> 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<IMFPresentationDescriptor> pPD = NULL;
- ComPtr<IMFStreamDescriptor> pSD = NULL;
- ComPtr<IMFMediaTypeHandler> pHandler = NULL;
- ComPtr<IMFMediaType> pType = NULL;
+ _ComPtr<IMFPresentationDescriptor> pPD = NULL;
+ _ComPtr<IMFStreamDescriptor> pSD = NULL;
+ _ComPtr<IMFMediaTypeHandler> pHandler = NULL;
+ _ComPtr<IMFMediaType> pType = NULL;
HRESULT hr = pSource->CreatePresentationDescriptor(pPD.GetAddressOf());
if (FAILED(hr))
{
}
videoDevices::videoDevices(void): count(0)
-{}
+{
+}
void videoDevices::clearDevices()
{
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<MAKE_WRL_REF(_DeviceInformationCollection)>) 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<MAKE_WRL_REF(_DeviceInformation)>) pVector =
+ CREATE_TASK DEFINE_RET_TYPE(MAKE_WRL_REF(_VectorView<MAKE_WRL_REF(_DeviceInformation)>))(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)
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()
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)
{
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)
{
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)
}
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)
}
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)
}
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)
}
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)
}
else
{
- DPO->printOut(L"VIDEODEVICE(s): There is not any suitable video device\n");
+ DebugPrintOut(L"VIDEODEVICE(s): There is not any suitable video device\n");
}
return false;
}
+#ifdef HAVE_WINRT
+void videoInput::waitForDevice(int deviceID)
+{
+ if (deviceID < 0)
+ {
+ DebugPrintOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID);
+ return;
+ }
+ if(accessToDevices)
+ {
+ if(!isDeviceSetup(deviceID))
+ {
+ if(isDeviceMediaSource(deviceID))
+ return;
+ }
+ videoDevices *VDS = &videoDevices::getInstance();
+ videoDevice * VD = VDS->getDevice(deviceID);
+ if(VD)
+ {
+ VD->waitForDevice();
+ }
+ }
+ else
+ {
+ DebugPrintOut(L"VIDEODEVICE(s): There is not any suitable video device\n");
+ }
+ return;
+}
+#endif
+
unsigned int videoInput::getCountFormats(int deviceID)
{
- DebugPrintOut *DPO = &DebugPrintOut::getInstance();
if (deviceID < 0)
{
- DPO->printOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID);
+ DebugPrintOut(L"VIDEODEVICE %i: Invalid device ID\n", deviceID);
return 0;
}
if(accessToDevices)
}
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;
}
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
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;
}
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)
}
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)
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);
}
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;
}
int index, width, height, fourcc;
IplImage* frame;
videoInput VI;
+#ifdef HAVE_WINRT
+#ifdef HAVE_CONCURRENCY
+ DEFINE_TASK<bool> 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),
// 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();
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;
}
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 );
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;
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<IMFSourceResolver> pSourceResolver = NULL;
+ _ComPtr<IMFSourceResolver> pSourceResolver = NULL;
IUnknown* pUnkSource = NULL;
- hr = MFCreateSourceResolver(pSourceResolver.GetAddressOf());
+ HRESULT hr = MFCreateSourceResolver(pSourceResolver.GetAddressOf());
if (SUCCEEDED(hr))
{
HRESULT CvCaptureFile_MSMF::enumerateCaptureFormats(IMFMediaSource *pSource)
{
- ComPtr<IMFPresentationDescriptor> pPD = NULL;
- ComPtr<IMFStreamDescriptor> pSD = NULL;
- ComPtr<IMFMediaTypeHandler> pHandler = NULL;
- ComPtr<IMFMediaType> pType = NULL;
+ _ComPtr<IMFPresentationDescriptor> pPD = NULL;
+ _ComPtr<IMFStreamDescriptor> pSD = NULL;
+ _ComPtr<IMFMediaTypeHandler> pHandler = NULL;
+ _ComPtr<IMFMediaType> pType = NULL;
HRESULT hr = pSource->CreatePresentationDescriptor(pPD.GetAddressOf());
if (FAILED(hr))
{
GUID inputFormat;
DWORD streamIndex;
- ComPtr<IMFSinkWriter> sinkWriter;
+ _ComPtr<IMFSinkWriter> sinkWriter;
bool initiated;
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'):
HRESULT CvVideoWriter_MSMF::InitializeSinkWriter(const char* filename)
{
- ComPtr<IMFAttributes> spAttr;
- ComPtr<IMFMediaType> mediaTypeOut;
- ComPtr<IMFMediaType> mediaTypeIn;
- ComPtr<IMFByteStream> spByteStream;
+ _ComPtr<IMFAttributes> spAttr;
+ _ComPtr<IMFMediaType> mediaTypeOut;
+ _ComPtr<IMFMediaType> mediaTypeIn;
+ _ComPtr<IMFByteStream> 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);
HRESULT CvVideoWriter_MSMF::WriteFrame(DWORD *videoFrameBuffer, const LONGLONG& Start, const LONGLONG& Duration)
{
- ComPtr<IMFSample> sample;
- ComPtr<IMFMediaBuffer> buffer;
+ _ComPtr<IMFSample> sample;
+ _ComPtr<IMFMediaBuffer> buffer;
const LONG cbWidth = 4 * videoWidth;
const DWORD cbBuffer = cbWidth * videoHeight;
--- /dev/null
+#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<typename _Type, bool bUnknown = std::is_base_of<IUnknown, _Type>::value>
+struct winrt_type
+{
+};
+template<typename _Type>
+struct winrt_type<_Type, true>
+{
+ static IUnknown* create(_Type* _ObjInCtx) {
+ return reinterpret_cast<IUnknown*>(_ObjInCtx);
+ }
+ static IID getuuid() { return __uuidof(_Type); }
+ static const ABI::Windows::Foundation::PropertyType _PropType = ABI::Windows::Foundation::PropertyType::PropertyType_OtherType;
+};
+template <typename _Type>
+struct winrt_type<_Type, false>
+{
+ static IUnknown* create(_Type* _ObjInCtx) {
+ Microsoft::WRL::ComPtr<IInspectable> _PObj;
+ Microsoft::WRL::ComPtr<IActivationFactory> 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<ABI::Windows::Foundation::IPropertyValueStatics> 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<IUnknown*>(_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<void>
+{
+ 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<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<typename _Type>
+struct winrt_array_type
+{
+ static IUnknown* create(_Type* _ObjInCtx, size_t N) {
+ Microsoft::WRL::ComPtr<IInspectable> _PObj;
+ Microsoft::WRL::ComPtr<IActivationFactory> 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<ABI::Windows::Foundation::IPropertyValueStatics> 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<IUnknown*>(_PObj.Detach());
+ }
+ return nullptr;
+ }
+ static const ABI::Windows::Foundation::PropertyType _PropType = ABI::Windows::Foundation::PropertyType::PropertyType_OtherTypeArray;
+};
+template<int>
+struct winrt_prop_type {};
+
+template <>
+struct winrt_prop_type<ABI::Windows::Foundation::PropertyType_Empty> {
+ typedef void _Type;
+};
+
+template <>
+struct winrt_prop_type<ABI::Windows::Foundation::PropertyType_OtherType> {
+ typedef void _Type;
+};
+
+template <>
+struct winrt_prop_type<ABI::Windows::Foundation::PropertyType_OtherTypeArray> {
+ typedef void _Type;
+};
+
+#define MAKE_PROP(Prop, Type) template <>\
+struct winrt_prop_type<ABI::Windows::Foundation::PropertyType_##Prop> {\
+ 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<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<Type> PropertyValueToVector(ABI::Windows::Foundation::IPropertyValue* propValue)\
+{\
+ UINT32 uLen = 0;\
+ Type* pArray = nullptr;\
+ propValue->Get##Name##Array(&uLen, &pArray);\
+ return std::vector<Type>(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<T*>
+{
+ 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<Microsoft::WRL::Details::RuntimeClassBase, _Type>::value || \
+ std::is_base_of<IInspectable, typename DerefHelper<_Type>::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 <initguid.h>
+
+// 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 <utility>
+#ifdef _UNICODE
+#define MAKE_MAP(e) std::map<e, std::wstring>
+#define MAKE_ENUM(e) std::pair<e, std::wstring>
+#define MAKE_ENUM_PAIR(e, str) std::pair<e, std::wstring>(str, L#str)
+#else
+#define MAKE_MAP(e) std::map<e, std::string>
+#define MAKE_ENUM(e) std::pair<e, std::string>
+#define MAKE_ENUM_PAIR(e, str) std::pair<e, std::string>(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<rettype>()) ? Concurrency::create_task(func) : _task.then([func](rettype) -> rettype { return func(); });
+#define CREATE_OR_CONTINUE_TASK_RET(_task, rettype, func) _task = (_task == Concurrency::task<rettype>()) ? 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<x^>
+#define MAKE_WRL_AGILE_OBJ(x) Platform::Agile<x^>
+#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<objtype^>(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<type*>(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<IInspectable>
+#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<HSTRING, IInspectable *>
+#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<type>([__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<rettype>()) ? Concurrency_winrt::create_task<rettype>(func) : _task.then(func);
+#define CREATE_OR_CONTINUE_TASK_RET(_task, rettype, func) _task = (_task == Concurrency_winrt::task<rettype>()) ? Concurrency_winrt::create_task<rettype>(func) : _task.then([func](rettype, rettype* retVal) -> HRESULT { return func(retVal); });
+#define DEFINE_RET_VAL(x) x* retVal
+#define DEFINE_RET_TYPE(x) <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<x>
+#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<x>
+#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<objtype> obj;\
+hr = orig->QueryInterface(__uuidof(objtype), &obj);
+#define GET_WRL_OBJ_FROM_OBJ(objtype, obj, orig, hr) Microsoft::WRL::ComPtr<objtype> 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<objtype>()
+#define ACTIVATE_LOCAL_OBJ(objtype, ...) Microsoft::WRL::Make<objtype>(__VA_ARGS__)
+#define ACTIVATE_EVENT_HANDLER(objtype, ...) Microsoft::WRL::Callback<objtype>(__VA_ARGS__).Get()
+#define ACTIVATE_OBJ(rtclass, objtype, obj, hr) MAKE_WRL_OBJ(objtype) obj;\
+{\
+ Microsoft::WRL::ComPtr<IActivationFactory> objFactory;\
+ hr = Windows::Foundation::GetActivationFactory(Microsoft::WRL::Wrappers::HStringReference(rtclass).Get(), objFactory.ReleaseAndGetAddressOf());\
+ if (SUCCEEDED(hr)) {\
+ Microsoft::WRL::ComPtr<IInspectable> 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<IActivationFactory> 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 T>
+class ComPtr : public ATL::CComPtr<T>
+{
+public:
+ ComPtr() throw()
+ {
+ }
+ ComPtr(int nNull) throw() :
+ CComPtr<T>((T*)nNull)
+ {
+ }
+ ComPtr(T* lp) throw() :
+ CComPtr<T>(lp)
+
+ {
+ }
+ ComPtr(_In_ const CComPtr<T>& lp) throw() :
+ CComPtr<T>(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<typename U>
+ HRESULT As(_Inout_ U** lp) const throw()
+ {
+ return p->QueryInterface(__uuidof(U), (void**)lp);
+ }
+ // query for U interface
+ template<typename U>
+ HRESULT As(_Out_ ComPtr<U>* lp) const throw()
+ {
+ return p->QueryInterface(__uuidof(U), reinterpret_cast<void**>(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 TBase=IMFAttributes>
+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<IMFAttributes> _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<IMFStreamSink*>(this);
+ AddRef();
+ } else if (riid == IID_IMFMediaEventGenerator) {
+ *ppv = static_cast<IMFMediaEventGenerator*>(this);
+ AddRef();
+ } else if (riid == IID_IMFMediaTypeHandler) {
+ *ppv = static_cast<IMFMediaTypeHandler*>(this);
+ AddRef();
+ } else if (riid == IID_IMFAttributes) {
+ *ppv = static_cast<IMFAttributes*>(this);
+ AddRef();
+ } else if (riid == IID_ICustomStreamSink) {
+ *ppv = static_cast<ICustomStreamSink*>(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<IMFMediaSink> 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<IMFSampleGrabberSinkCallback> 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<IMFMediaSink> 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<IMFMediaBuffer> pInput;
+ _ComPtr<IMFSampleGrabberSinkCallback> 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<IMFMediaEventQueue> 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<IMFMediaType> 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<IMFMediaType> 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<IMFMediaType> 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<IMFMediaType> m_spCurrentType;
+ _ComPtr<IMFMediaEventQueue> m_spEventQueue; // Event queue
+
+ _ComPtr<IUnknown> 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<T>::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 <class T>
+struct NoOp
+{
+ void operator()(T& /*t*/)
+ {
+ }
+};
+
+template <class T>
+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<T>;
+
+ 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 <class FN>
+ 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<T> 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<Node*>(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<IUnknown>
+//
+// 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 T, bool NULLABLE = FALSE>
+class ComPtrList : public List<T*>
+{
+public:
+
+ typedef T* Ptr;
+
+ void Clear()
+ {
+ ComAutoRelease car;
+ List<Ptr>::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<Ptr>::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<Ptr>::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<Ptr>::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):
+ <Extensions>
+ <Extension Category="windows.activatableClass.inProcessServer">
+ <InProcessServer>
+ <Path>modulename</Path>
+ <ActivatableClass ActivatableClassId="cv.MediaSink" ThreadingModel="both" />
+ </InProcessServer>
+ </Extension>
+ </Extensions>*/
+
+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<ABI::Windows::Media::IMediaExtension>,
+ 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<IMFMediaSink*>(this);
+ AddRef();
+ } else if (riid == IID_IMFClockStateSink) {
+ (*ppv) = static_cast<IMFClockStateSink*>(this);
+ AddRef();
+ } else if (riid == IID_IMFAttributes) {
+ (*ppv) = static_cast<IMFAttributes*>(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<IInspectable> spInsp;
+ Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IMap<HSTRING, IInspectable *>> spSetting;
+ Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IPropertyValue> spPropVal;
+ Microsoft::WRL::ComPtr<ABI::Windows::Media::MediaProperties::IMediaEncodingProperties> 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<IInspectable> 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<IMFMediaType> spMT;
+ Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IMap<GUID, IInspectable*>> spMap;
+ Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IIterable<ABI::Windows::Foundation::Collections::IKeyValuePair<GUID, IInspectable*>*>> spIterable;
+ Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Collections::IIterator<ABI::Windows::Foundation::Collections::IKeyValuePair<GUID, IInspectable*>*>> 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<ABI::Windows::Foundation::Collections::IKeyValuePair<GUID, IInspectable*> > spKeyValuePair;
+ Microsoft::WRL::ComPtr<IInspectable> spValue;
+ Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IPropertyValue> 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<IInspectable> spValue;
+ Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IPropertyValue> 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<IMFMediaType> 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<IMFStreamSink> 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<IMFStreamSink> spMFStream;
+ _ComPtr<ICustomStreamSink> 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<StreamSink>();
+ if (pStream == nullptr) {
+ hr = E_OUTOFMEMORY;
+ }
+ if (SUCCEEDED(hr))
+ hr = pStream.As<IMFStreamSink>(&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<IMFAttributes> 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<IMFStreamSink>::POSITION pos = m_streams.FrontPosition();
+ ComPtrList<IMFStreamSink>::POSITION posEnd = m_streams.EndPosition();
+
+ // Insert in proper position
+ for (; pos != posEnd; pos = m_streams.Next(pos))
+ {
+ DWORD dwCurrId;
+ _ComPtr<IMFStreamSink> 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<IMFStreamSink>::POSITION pos = m_streams.FrontPosition();
+ ComPtrList<IMFStreamSink>::POSITION endPos = m_streams.EndPosition();
+ _ComPtr<IMFStreamSink> 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<ICustomStreamSink> spCustomSink;
+#ifdef HAVE_WINRT
+ spCustomSink = static_cast<StreamSink*>(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<IMFStreamSink> spStream;
+ EnterCriticalSection(&m_critSec);
+ DWORD cStreams = m_streams.GetCount();
+
+ if (dwIndex >= cStreams)
+ {
+ return MF_E_INVALIDINDEX;
+ }
+
+ HRESULT hr = CheckShutdown();
+
+ if (SUCCEEDED(hr))
+ {
+ ComPtrList<IMFStreamSink>::POSITION pos = m_streams.FrontPosition();
+ ComPtrList<IMFStreamSink>::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<IMFStreamSink> spResult;
+
+ if (SUCCEEDED(hr))
+ {
+ ComPtrList<IMFStreamSink>::POSITION pos = m_streams.FrontPosition();
+ ComPtrList<IMFStreamSink>::POSITION endPos = m_streams.EndPosition();
+
+ for (; pos != endPos; pos = m_streams.Next(pos))
+ {
+ _ComPtr<IMFStreamSink> 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<IMFSampleGrabberSinkCallback> 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<IMFMediaType> 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<ICustomStreamSink> spCustomSink;
+ HRESULT hr;
+#ifdef HAVE_WINRT
+ spCustomSink = static_cast<StreamSink*>(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<ICustomStreamSink> spCustomSink;
+ HRESULT hr;
+#ifdef HAVE_WINRT
+ spCustomSink = static_cast<StreamSink*>(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<ICustomStreamSink> spCustomSink;
+ HRESULT hr;
+#ifdef HAVE_WINRT
+ spCustomSink = static_cast<StreamSink*>(pStream);
+#else
+ hr = pStream->QueryInterface(IID_PPV_ARGS(spCustomSink.GetAddressOf()));
+ if (FAILED(hr)) return hr;
+#endif
+ hr = spCustomSink->Stop();
+ return hr;
+ }
+ };
+
+ template <class T, class TFunc>
+ HRESULT ForEach(ComPtrList<T> &col, TFunc fn)
+ {
+ ComPtrList<T>::POSITION pos = col.FrontPosition();
+ ComPtrList<T>::POSITION endPos = col.EndPosition();
+ HRESULT hr = S_OK;
+
+ for (; pos != endPos; pos = col.Next(pos))
+ {
+ _ComPtr<T> 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<IMFSampleGrabberSinkCallback> 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<IMFSampleGrabberSinkCallback> 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<IMFSampleGrabberSinkCallback> 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<IMFSampleGrabberSinkCallback> 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<IMFSampleGrabberSinkCallback> 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<IMFStreamSink> m_streams;
+ _ComPtr<IMFPresentationClock> m_spClock;
+ LONGLONG m_llStartTime;
+};
+
+#ifdef HAVE_WINRT
+ActivatableClass(MediaSink);
+#endif
--- /dev/null
+/***
+* ==++==
+*
+* 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 <concrt.h>
+#include <ppltasks.h>
+#if _MSC_VER >= 1800
+#include <pplconcrt.h>
+
+// 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 <ppl.h>
+#endif
+#include <functional>
+#include <vector>
+#include <utility>
+#include <exception>
+#if _MSC_VER >= 1800
+#include <algorithm>
+#endif
+
+#ifndef __cplusplus_winrt
+
+#include <wrl\implements.h>
+#include <wrl\async.h>
+#if _MSC_VER >= 1800
+#include "agile_wrl.h"
+#endif
+#include <windows.foundation.h>
+#include <ctxtcall.h>
+
+#ifndef _UITHREADCTXT_SUPPORT
+
+#ifdef WINAPI_FAMILY /*IFSTRIP=IGN*/
+
+// It is safe to include winapifamily as WINAPI_FAMILY was defined by the user
+#include <winapifamily.h>
+
+#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 <uithreadctxt.h>
+#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<class _E> 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<class _T>
+ _T&& declval();
+}
+
+/// <summary>
+/// The <c>Concurrency_winrt</c> namespace provides classes and functions that give you access to the Concurrency Runtime,
+/// a concurrent programming framework for C++. For more information, see <see cref="Concurrency Runtime"/>.
+/// </summary>
+/**/
+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
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ /// <ramarks>
+ /// 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.
+ /// </remarks>
+#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
+/// <summary>
+
+/// A type that represents the terminal state of a task. Valid values are <c>completed</c> and <c>canceled</c>.
+/// </summary>
+/// <seealso cref="task Class"/>
+/**/
+typedef Concurrency::task_group_status task_status;
+
+template <typename _Type> class task;
+template <> class task<void>;
+
+/// <summary>
+/// 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.
+/// </summary>
+/// <returns>
+/// <c>true</c> if the currently executing task has received a request for cancellation, <c>false</c> otherwise.
+/// </returns>
+/// <remarks>
+/// If you call this method in the body of a task and it returns <c>true</c>, you must respond with a call to
+/// <see cref="cancel_current_task Function">cancel_current_task</see> 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 <c>canceled</c> state. If you do not respond and continue execution, or return instead of calling
+/// <c>cancel_current_task</c>, the task will enter the <c>completed</c> state when it is done.
+/// state.
+/// <para>A task is not cancellable if it was created without a cancellation token.</para>
+/// </remarks>
+/// <seealso cref="task Class"/>
+/// <seealso cref="cancellation_token_source Class"/>
+/// <seealso cref="cancellation_token Class"/>
+/// <seealso cref="cancel_current_task Function"/>
+/**/
+#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
+
+/// <summary>
+/// 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 <c>canceled</c> state. While it may be used in response to
+/// the <see cref="is_task_cancellation_requested Function">is_task_cancellation_requested</see> function, you may
+/// also use it by itself, to initiate cancellation of the task that is currently executing.
+/// <para>It is not a supported scenario to call this function if you are not within the body of a <c>task</c>.
+/// Doing so will result in undefined behavior such as a crash or a hang in your application.</para>
+/// </summary>
+/// <seealso cref="task Class"/>
+/// <seealso cref="is_task_cancellation_requested"/>
+/**/
+//#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
+ /// <summary>
+ /// 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.
+ /// </summary>
+ 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<void *> _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<typename _Ty>
+ struct _NormalizeVoidToUnitType
+ {
+ typedef _Ty _Type;
+ };
+
+ template<>
+ struct _NormalizeVoidToUnitType<void>
+ {
+ typedef _Unit_type _Type;
+ };
+
+ template<typename _T>
+ struct _IsUnwrappedAsyncSelector
+ {
+ static const bool _Value = true;
+ };
+
+ template<>
+ struct _IsUnwrappedAsyncSelector<_TypeSelectorNoAsync>
+ {
+ static const bool _Value = false;
+ };
+
+ template <typename _Ty>
+ struct _UnwrapTaskType
+ {
+ typedef _Ty _Type;
+ };
+
+ template <typename _Ty>
+ struct _UnwrapTaskType<task<_Ty>>
+ {
+ typedef _Ty _Type;
+ };
+
+ template <typename _T>
+ _TypeSelectorAsyncTask _AsyncOperationKindSelector(task<_T>);
+
+ _TypeSelectorNoAsync _AsyncOperationKindSelector(...);
+
+ template <typename _Type>
+ struct _Unhat
+ {
+ typedef _Type _Value;
+ };
+
+ template <typename _Type>
+ struct _Unhat<_Type*>
+ {
+ typedef _Type _Value;
+ };
+
+ //struct _NonUserType { public: int _Dummy; };
+
+ template <typename _Type, bool _IsValueTypeOrRefType = __is_valid_winrt_type(_Type)>
+ struct _ValueTypeOrRefType
+ {
+ typedef _Unit_type _Value;
+ };
+
+ template <typename _Type>
+ struct _ValueTypeOrRefType<_Type, true>
+ {
+ typedef _Type _Value;
+ };
+
+ template <typename _Ty>
+ _Ty _UnwrapAsyncActionWithProgressSelector(ABI::Windows::Foundation::IAsyncActionWithProgress_impl<_Ty>*);
+
+ template <typename _Ty>
+ _Ty _UnwrapAsyncActionWithProgressSelector(...);
+
+ template <typename _Ty, typename _Progress>
+ _Progress _UnwrapAsyncOperationWithProgressProgressSelector(ABI::Windows::Foundation::IAsyncOperationWithProgress_impl<_Ty, _Progress>*);
+
+ template <typename _Ty, typename _Progress>
+ _Progress _UnwrapAsyncOperationWithProgressProgressSelector(...);
+
+ template <typename _T1, typename _T2>
+ _T2 _ProgressTypeSelector(ABI::Windows::Foundation::IAsyncOperationWithProgress<_T1, _T2>*);
+
+ template <typename _T1>
+ _T1 _ProgressTypeSelector(ABI::Windows::Foundation::IAsyncActionWithProgress<_T1>*);
+
+ template <typename _Type>
+ struct _GetProgressType
+ {
+ typedef decltype(_ProgressTypeSelector(stdx::declval<_Type>())) _Value;
+ };
+
+ template <typename _T>
+ _TypeSelectorAsyncOperation _AsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncOperation<_T>*);
+
+ _TypeSelectorAsyncAction _AsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncAction*);
+
+ template <typename _T1, typename _T2>
+ _TypeSelectorAsyncOperationWithProgress _AsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncOperationWithProgress<_T1, _T2>*);
+
+ template <typename _T>
+ _TypeSelectorAsyncActionWithProgress _AsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncActionWithProgress<_T>*);
+
+ template <typename _Type>
+ struct _IsIAsyncInfo
+ {
+ static const bool _Value = std::is_base_of<ABI::Windows::Foundation::IAsyncInfo, typename _Unhat<_Type>::_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<void>
+ {
+ static const bool _Value = false;
+ };
+
+ template <typename _Ty>
+ _Ty _UnwrapAsyncOperationSelector(ABI::Windows::Foundation::IAsyncOperation_impl<_Ty>*);
+
+ template <typename _Ty>
+ _Ty _UnwrapAsyncOperationSelector(...);
+
+ template <typename _Ty, typename _Progress>
+ _Ty _UnwrapAsyncOperationWithProgressSelector(ABI::Windows::Foundation::IAsyncOperationWithProgress_impl<_Ty, _Progress>*);
+
+ template <typename _Ty, typename _Progress>
+ _Ty _UnwrapAsyncOperationWithProgressSelector(...);
+
+ // Unwrap functions for asyncOperations
+ template<typename _Ty>
+ auto _GetUnwrappedType(ABI::Windows::Foundation::IAsyncOperation<_Ty>*) -> typename ABI::Windows::Foundation::Internal::GetAbiType<decltype(_UnwrapAsyncOperationSelector(stdx::declval<ABI::Windows::Foundation::IAsyncOperation<_Ty>*>()))>::type;
+
+ void _GetUnwrappedType(ABI::Windows::Foundation::IAsyncAction*);
+
+ template<typename _Ty, typename _Progress>
+ auto _GetUnwrappedType(ABI::Windows::Foundation::IAsyncOperationWithProgress<_Ty, _Progress>*) -> typename ABI::Windows::Foundation::Internal::GetAbiType<decltype(_UnwrapAsyncOperationWithProgressSelector(stdx::declval<ABI::Windows::Foundation::IAsyncOperationWithProgress<_Ty, _Progress>*>()))>::type;
+
+ template<typename _Progress>
+ void _GetUnwrappedType(ABI::Windows::Foundation::IAsyncActionWithProgress<_Progress>*);
+
+ template <typename _T>
+ _T _ReturnAsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncOperation<_T>*);
+
+ void _ReturnAsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncAction*);
+
+ template <typename _T1, typename _T2>
+ _T1 _ReturnAsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncOperationWithProgress<_T1, _T2>*);
+
+ template <typename _T>
+ void _ReturnAsyncOperationKindSelector(ABI::Windows::Foundation::IAsyncActionWithProgress<_T>*);
+
+ class _ProgressReporterCtorArgType{};
+
+ template <typename _Type, bool _IsAsync = _IsIAsyncInfo<_Type>::_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<typename _Type>
+ 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 <typename _ReturnType, typename _Function> auto _IsCallable(_Function _Func, int, int, int) -> decltype(_Func(stdx::declval<task<_ReturnType>*>()), std::true_type()) { (void)_Func; return std::true_type(); }
+ template <typename _ReturnType, typename _Function> auto _IsCallable(_Function _Func, int, int, ...) -> decltype(_Func(stdx::declval<_ReturnType*>()), std::true_type()) { (void)_Func; return std::true_type(); }
+ template <typename _ReturnType, typename _Function> auto _IsCallable(_Function _Func, int, ...) -> decltype(_Func(), std::true_type()) { (void)_Func; return std::true_type(); }
+ template <typename _ReturnType, typename _Function> std::false_type _IsCallable(_Function, ...) { return std::false_type(); }
+
+ template <>
+ struct _TaskTypeTraits<void>
+ {
+ 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<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _Arg1 _Arg1ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3));
+
+ // non-void arg:
+ template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _Arg2 _Arg2ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3));
+
+ // non-void arg:
+ template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _Arg3 _Arg3ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3));
+
+ template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3));
+
+ template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _ThreeArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3));
+
+ // ********************
+ // TWO ARGUMENTS:
+
+ // non-void arg:
+ template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2>
+ _Arg1 _Arg1ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2));
+
+ // non-void arg:
+ template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2>
+ _Arg2 _Arg2ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2));
+
+ // non-void arg:
+ template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2>
+ void _Arg3ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2));
+
+ template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2>
+ _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2));
+
+ template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2>
+ _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)(_Arg1, _Arg2));
+
+ // ********************
+ // ONE ARGUMENT:
+
+ // non-void arg:
+ template<typename _Class, typename _ReturnType, typename _Arg1>
+ _Arg1 _Arg1ClassHelperThunk(_ReturnType(_Class::*)(_Arg1));
+
+ // non-void arg:
+ template<typename _Class, typename _ReturnType, typename _Arg1>
+ void _Arg2ClassHelperThunk(_ReturnType(_Class::*)(_Arg1));
+
+ // non-void arg:
+ template<typename _Class, typename _ReturnType, typename _Arg1>
+ void _Arg3ClassHelperThunk(_ReturnType(_Class::*)(_Arg1));
+
+ template<typename _Class, typename _ReturnType, typename _Arg1>
+ _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)(_Arg1));
+
+ template<typename _Class, typename _ReturnType, typename _Arg1>
+ _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)(_Arg1));
+
+ // ********************
+ // ZERO ARGUMENT:
+
+ // void arg:
+ template<typename _Class, typename _ReturnType>
+ void _Arg1ClassHelperThunk(_ReturnType(_Class::*)());
+
+ // void arg:
+ template<typename _Class, typename _ReturnType>
+ void _Arg2ClassHelperThunk(_ReturnType(_Class::*)());
+
+ // void arg:
+ template<typename _Class, typename _ReturnType>
+ void _Arg3ClassHelperThunk(_ReturnType(_Class::*)());
+
+ // void arg:
+ template<typename _Class, typename _ReturnType>
+ _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)());
+
+ template<typename _Class, typename _ReturnType>
+ _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)());
+
+ // ********************
+ // THREE ARGUMENTS:
+
+ // non-void arg:
+ template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _Arg1 _Arg1ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3) const);
+
+ // non-void arg:
+ template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _Arg2 _Arg2ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3) const);
+
+ // non-void arg:
+ template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _Arg3 _Arg3ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3) const);
+
+ template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3) const);
+
+ template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _ThreeArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)(_Arg1, _Arg2, _Arg3) const);
+
+ // ********************
+ // TWO ARGUMENTS:
+
+ // non-void arg:
+ template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2>
+ _Arg1 _Arg1ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2) const);
+
+ // non-void arg:
+ template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2>
+ _Arg2 _Arg2ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2) const);
+
+ // non-void arg:
+ template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2>
+ void _Arg3ClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2) const);
+
+ template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2>
+ _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)(_Arg1, _Arg2) const);
+
+ template<typename _Class, typename _ReturnType, typename _Arg1, typename _Arg2>
+ _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)(_Arg1, _Arg2) const);
+
+ // ********************
+ // ONE ARGUMENT:
+
+ // non-void arg:
+ template<typename _Class, typename _ReturnType, typename _Arg1>
+ _Arg1 _Arg1ClassHelperThunk(_ReturnType(_Class::*)(_Arg1) const);
+
+ // non-void arg:
+ template<typename _Class, typename _ReturnType, typename _Arg1>
+ void _Arg2ClassHelperThunk(_ReturnType(_Class::*)(_Arg1) const);
+
+ // non-void arg:
+ template<typename _Class, typename _ReturnType, typename _Arg1>
+ void _Arg3ClassHelperThunk(_ReturnType(_Class::*)(_Arg1) const);
+
+ template<typename _Class, typename _ReturnType, typename _Arg1>
+ _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)(_Arg1) const);
+
+ template<typename _Class, typename _ReturnType, typename _Arg1>
+ _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)(_Arg1) const);
+
+ // ********************
+ // ZERO ARGUMENT:
+
+ // void arg:
+ template<typename _Class, typename _ReturnType>
+ void _Arg1ClassHelperThunk(_ReturnType(_Class::*)() const);
+
+ // void arg:
+ template<typename _Class, typename _ReturnType>
+ void _Arg2ClassHelperThunk(_ReturnType(_Class::*)() const);
+
+ // void arg:
+ template<typename _Class, typename _ReturnType>
+ void _Arg3ClassHelperThunk(_ReturnType(_Class::*)() const);
+
+ // void arg:
+ template<typename _Class, typename _ReturnType>
+ _ReturnType _ReturnTypeClassHelperThunk(_ReturnType(_Class::*)() const);
+
+ template<typename _Class, typename _ReturnType>
+ _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(_Class::*)() const);
+
+ // ****************************************
+ // POINTER TYPES:
+
+ // ********************
+ // THREE ARGUMENTS:
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2, _Arg3));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _Arg2 _Arg2PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2, _Arg3));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _Arg3 _Arg3PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2, _Arg3));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2, _Arg3));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _ThreeArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)(_Arg1, _Arg2, _Arg3));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2, _Arg3));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _Arg2 _Arg2PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2, _Arg3));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _Arg3 _Arg3PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2, _Arg3));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2, _Arg3));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _ThreeArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)(_Arg1, _Arg2, _Arg3));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2, _Arg3));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _Arg2 _Arg2PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2, _Arg3));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _Arg3 _Arg3PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2, _Arg3));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2, _Arg3));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2, typename _Arg3>
+ _ThreeArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)(_Arg1, _Arg2, _Arg3));
+
+ // ********************
+ // TWO ARGUMENTS:
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2>
+ _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2>
+ _Arg2 _Arg2PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2>
+ void _Arg3PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2>
+ _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)(_Arg1, _Arg2));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2>
+ _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)(_Arg1, _Arg2));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2>
+ _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2>
+ _Arg2 _Arg2PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2>
+ void _Arg3PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2>
+ _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)(_Arg1, _Arg2));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2>
+ _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)(_Arg1, _Arg2));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2>
+ _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2>
+ _Arg2 _Arg2PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2>
+ void _Arg3PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2>
+ _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)(_Arg1, _Arg2));
+
+ template<typename _ReturnType, typename _Arg1, typename _Arg2>
+ _TwoArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)(_Arg1, _Arg2));
+
+ // ********************
+ // ONE ARGUMENT:
+
+ template<typename _ReturnType, typename _Arg1>
+ _Arg1 _Arg1PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1));
+
+ template<typename _ReturnType, typename _Arg1>
+ void _Arg2PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1));
+
+ template<typename _ReturnType, typename _Arg1>
+ void _Arg3PFNHelperThunk(_ReturnType(__cdecl *)(_Arg1));
+
+ template<typename _ReturnType, typename _Arg1>
+ _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)(_Arg1));
+
+ template<typename _ReturnType, typename _Arg1>
+ _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)(_Arg1));
+
+ template<typename _ReturnType, typename _Arg1>
+ _Arg1 _Arg1PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1));
+
+ template<typename _ReturnType, typename _Arg1>
+ void _Arg2PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1));
+
+ template<typename _ReturnType, typename _Arg1>
+ void _Arg3PFNHelperThunk(_ReturnType(__stdcall *)(_Arg1));
+
+ template<typename _ReturnType, typename _Arg1>
+ _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)(_Arg1));
+
+ template<typename _ReturnType, typename _Arg1>
+ _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)(_Arg1));
+
+ template<typename _ReturnType, typename _Arg1>
+ _Arg1 _Arg1PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1));
+
+ template<typename _ReturnType, typename _Arg1>
+ void _Arg2PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1));
+
+ template<typename _ReturnType, typename _Arg1>
+ void _Arg3PFNHelperThunk(_ReturnType(__fastcall *)(_Arg1));
+
+ template<typename _ReturnType, typename _Arg1>
+ _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)(_Arg1));
+
+ template<typename _ReturnType, typename _Arg1>
+ _OneArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)(_Arg1));
+
+ // ********************
+ // ZERO ARGUMENT:
+
+ template<typename _ReturnType>
+ void _Arg1PFNHelperThunk(_ReturnType(__cdecl *)());
+
+ template<typename _ReturnType>
+ void _Arg2PFNHelperThunk(_ReturnType(__cdecl *)());
+
+ template<typename _ReturnType>
+ void _Arg3PFNHelperThunk(_ReturnType(__cdecl *)());
+
+ template<typename _ReturnType>
+ _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__cdecl *)());
+
+ template<typename _ReturnType>
+ _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__cdecl *)());
+
+ template<typename _ReturnType>
+ void _Arg1PFNHelperThunk(_ReturnType(__stdcall *)());
+
+ template<typename _ReturnType>
+ void _Arg2PFNHelperThunk(_ReturnType(__stdcall *)());
+
+ template<typename _ReturnType>
+ void _Arg3PFNHelperThunk(_ReturnType(__stdcall *)());
+
+ template<typename _ReturnType>
+ _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__stdcall *)());
+
+ template<typename _ReturnType>
+ _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__stdcall *)());
+
+ template<typename _ReturnType>
+ void _Arg1PFNHelperThunk(_ReturnType(__fastcall *)());
+
+ template<typename _ReturnType>
+ void _Arg2PFNHelperThunk(_ReturnType(__fastcall *)());
+
+ template<typename _ReturnType>
+ void _Arg3PFNHelperThunk(_ReturnType(__fastcall *)());
+
+ template<typename _ReturnType>
+ _ReturnType _ReturnTypePFNHelperThunk(_ReturnType(__fastcall *)());
+
+ template<typename _ReturnType>
+ _ZeroArgumentFunctor _ArgumentCountHelper(_ReturnType(__fastcall *)());
+
+ template<typename _T>
+ 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<typename _T>
+ 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<typename _T>
+ 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<void> _To_task();
+
+ template <typename _Function> auto _IsVoidConversionHelper(_Function _Func, int) -> typename decltype(_Func(_To_task()), std::true_type());
+ template <typename _Function> std::false_type _IsVoidConversionHelper(_Function _Func, ...);
+
+ template <typename T> std::true_type _VoidIsTaskHelper(task<T> _Arg, int);
+ template <typename T> std::false_type _VoidIsTaskHelper(T _Arg, ...);
+
+ template<typename _Function, typename _ExpectedParameterType, const bool _IsVoidConversion = std::is_same<decltype(_IsVoidConversionHelper(stdx::declval<_Function>(), 0)), std::true_type>::value, const size_t _Count = _FunctorTypeTraits<_Function>::_ArgumentCount>
+ struct _FunctionTypeTraits
+ {
+ typedef typename _Unhat<typename _FunctorTypeTraits<_Function>::_Argument2Type>::_Value _FuncRetType;
+ static_assert(std::is_same<typename _FunctorTypeTraits<_Function>::_Argument1Type, _ExpectedParameterType>::value ||
+ std::is_same<typename _FunctorTypeTraits<_Function>::_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<typename _Function, typename _ExpectedParameterType, const bool _IsVoidConversion>
+ struct _FunctionTypeTraits<_Function, _ExpectedParameterType, _IsVoidConversion, 1>
+ {
+ typedef void _FuncRetType;
+ static_assert(std::is_same<typename _FunctorTypeTraits<_Function>::_Argument1Type, _ExpectedParameterType>::value ||
+ std::is_same<typename _FunctorTypeTraits<_Function>::_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<typename _Function>
+ struct _FunctionTypeTraits<_Function, void, true, 1>
+ {
+ typedef void _FuncRetType;
+ static_assert(std::is_same<typename _FunctorTypeTraits<_Function>::_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<typename _Function>
+ struct _FunctionTypeTraits<_Function, void, false, 1>
+ {
+ typedef typename _Unhat<typename _FunctorTypeTraits<_Function>::_Argument1Type>::_Value _FuncRetType;
+
+ typedef std::false_type _Takes_task;
+ };
+
+ template<typename _Function, typename _ExpectedParameterType, const bool _IsVoidConversion>
+ struct _FunctionTypeTraits<_Function, _ExpectedParameterType, _IsVoidConversion, 0>
+ {
+ typedef void _FuncRetType;
+
+ typedef std::false_type _Takes_task;
+ };
+
+ template<typename _Function, typename _ReturnType>
+ struct _ContinuationTypeTraits
+ {
+ typedef typename task<typename _TaskTypeTraits<typename _FunctionTypeTraits<_Function, _ReturnType>::_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<task<void>> t1([]() -> task<void> {
+ // task<void> t2([]() {});
+ // return t2;
+ // });
+ //
+ // This declaration SHOULD cause unwrapping
+ // task<void>> t1([]() -> task<void> {
+ // task<void> 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 <typename _TaskType, typename _FuncRetType>
+ struct _InitFunctorTypeTraits
+ {
+ typedef typename _TaskTypeTraits<_FuncRetType>::_AsyncKind _AsyncKind;
+ static const bool _IsAsyncTask = _TaskTypeTraits<_FuncRetType>::_IsAsyncTask;
+ static const bool _IsUnwrappedTaskOrAsync = _TaskTypeTraits<_FuncRetType>::_IsUnwrappedTaskOrAsync;
+ };
+
+ template<typename T>
+ struct _InitFunctorTypeTraits<T, T>
+ {
+ typedef _TypeSelectorNoAsync _AsyncKind;
+ static const bool _IsAsyncTask = false;
+ static const bool _IsUnwrappedTaskOrAsync = false;
+ };
+ /// <summary>
+ /// Helper object used for LWT invocation.
+ /// </summary>
+ struct _TaskProcThunk
+ {
+ _TaskProcThunk(const std::function<HRESULT(void)> & _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<HRESULT(void)> _M_func;
+ _TaskProcThunk& operator=(const _TaskProcThunk&);
+ };
+
+ /// <summary>
+ /// 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 <paramref value="_InliningMode"/>.
+ /// </summary>
+ /// <param name="_Func">
+ /// The user functor need to be scheduled.
+ /// </param>
+ /// <param name="_InliningMode">
+ /// The inlining scheduling policy for current functor.
+ /// </param>
+#if _MSC_VER >= 1800
+ typedef Concurrency::details::_TaskInliningMode_t _TaskInliningMode;
+#else
+ typedef Concurrency::details::_TaskInliningMode _TaskInliningMode;
+#endif
+ static void _ScheduleFuncWithAutoInline(const std::function<HRESULT(void)> & _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<Concurrency::TaskProc>(&_TaskProcThunk::_Bridge), new _TaskProcThunk(_Func));
+ }
+#endif
+ }
+ class _ContextCallback
+ {
+ typedef std::function<HRESULT(void)> _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<void **>(&_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<void *>(&_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<typename _Type>
+ struct _ResultHolder
+ {
+ void Set(const _Type& _type)
+ {
+ _Result = _type;
+ }
+
+ _Type Get()
+ {
+ return _Result;
+ }
+
+ _Type _Result;
+ };
+
+ template<typename _Type>
+ 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<typename _Type>
+ struct _ResultHolder<std::vector<_Type*>>
+ {
+ 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<T^> 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<typename _Type>
+ struct _ResultHolder<std::pair<_Type*, void*> >
+ {
+ 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<Agile<_Type*>, size_t> _M_Result;
+ };
+#else
+ template<typename _Type>
+ struct _ResultContext
+ {
+ static _ContextCallback _GetContext(bool /* _RuntimeAggregate */)
+ {
+ return _ContextCallback();
+ }
+
+ static _Type _GetValue(_Type _ObjInCtx, const _ContextCallback & /* _Ctx */, bool /* _RuntimeAggregate */)
+ {
+ return _ObjInCtx;
+ }
+ };
+
+ template<typename _Type, size_t N = 0, bool bIsArray = std::is_array<_Type>::value>
+ struct _MarshalHelper
+ {
+ };
+ template<typename _Type, size_t N>
+ 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<void**>(&_Proxy));
+ if (FAILED(_Hr))
+ {
+ throw std::make_exception_ptr(_Hr);
+ }
+ return _Proxy;
+ }
+ };
+ template<typename _Type>
+ struct _MarshalHelper<_Type, 0, false>
+ {
+ static _Type* _Perform(_Type* _ObjInCtx, const _ContextCallback& _Ctx)
+ {
+ static_assert(std::is_base_of<IUnknown, _Type>::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<void**>(&_Proxy));
+ if (FAILED(_Hr))
+ {
+ throw std::make_exception_ptr(_Hr);
+ }
+ return _Proxy;
+ }
+ };
+
+ // Arrays must be converted to IPropertyValue objects.
+
+ template<>
+ struct _MarshalHelper<HSTRING__>
+ {
+ static HSTRING _Perform(HSTRING _ObjInCtx, const _ContextCallback& _Ctx)
+ {
+ return _ObjInCtx;
+ }
+ };
+
+ template<typename _Type>
+ _Type* _Marshal(_Type* _ObjInCtx, const _ContextCallback& _Ctx)
+ {
+ return _MarshalHelper<_Type>::_Perform(_ObjInCtx, _Ctx);
+ }
+
+ template<typename _Type>
+ struct _InContext
+ {
+ static _Type _Get(_Type _ObjInCtx, const _ContextCallback& _Ctx)
+ {
+ return _ObjInCtx;
+ }
+ };
+
+ template<typename _Type>
+ 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<typename _Type>
+ 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<typename _Type>
+ struct _ResultContext<std::vector<_Type*>>
+ {
+ 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<typename _Type>
+ struct _ResultContext<std::pair<_Type*, size_t>>
+ {
+ 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<IRestrictedErrorInfo> _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
+
+ /// <summary>
+ /// Base converter class for converting asynchronous interfaces to IAsyncOperation
+ /// </summary>
+ template<typename _AsyncOperationType, typename _CompletionHandlerType, typename _Result_abi>
+ struct _AsyncInfoImpl abstract : public Microsoft::WRL::RuntimeClass<
+ Microsoft::WRL::RuntimeClassFlags< Microsoft::WRL::RuntimeClassType::WinRt>,
+ Microsoft::WRL::Implements<Microsoft::WRL::AsyncBase<_CompletionHandlerType>>>
+ {
+ 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<ABI::Windows::Foundation::IAsyncInfo> pAsyncInfo;
+ HRESULT hr;
+#if _MSC_VER >= 1800
+ if (SUCCEEDED(hr = _M_asyncInfo.Get()->QueryInterface<ABI::Windows::Foundation::IAsyncInfo>(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<ABI::Windows::Foundation::IAsyncInfo> pAsyncInfo;
+ HRESULT hr;
+#if _MSC_VER >= 1800
+ if (SUCCEEDED(hr = _M_asyncInfo.Get()->QueryInterface<ABI::Windows::Foundation::IAsyncInfo>(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<ABI::Windows::Foundation::IAsyncInfo> pAsyncInfo;
+ HRESULT hr;
+#if _MSC_VER >= 1800
+ if (SUCCEEDED(hr = _M_asyncInfo.Get()->QueryInterface<ABI::Windows::Foundation::IAsyncInfo>(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<ABI::Windows::Foundation::IAsyncInfo> pAsyncInfo;
+ HRESULT hr;
+#if _MSC_VER >= 1800
+ if (SUCCEEDED(hr = _M_asyncInfo.Get()->QueryInterface<ABI::Windows::Foundation::IAsyncInfo>(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<ABI::Windows::Foundation::IAsyncInfo> pAsyncInfo;
+ HRESULT hr;
+#if _MSC_VER >= 1800
+ if (SUCCEEDED(hr = _M_asyncInfo.Get()->QueryInterface<ABI::Windows::Foundation::IAsyncInfo>(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";
+
+ /// <summary>
+ /// Class _IAsyncOperationToAsyncOperationConverter is used to convert an instance of IAsyncOperationWithProgress<T> into IAsyncOperation<T>
+ /// </summary>
+ template<typename _Result>
+ struct _IAsyncOperationToAsyncOperationConverter :
+ _AsyncInfoImpl<ABI::Windows::Foundation::IAsyncOperation<_Result>,
+ ABI::Windows::Foundation::IAsyncOperationCompletedHandler<_Result>,
+ typename ABI::Windows::Foundation::Internal::GetAbiType<decltype(_UnwrapAsyncOperationSelector(stdx::declval<ABI::Windows::Foundation::IAsyncOperation<_Result>*>()))>::type>
+ {
+ typedef typename ABI::Windows::Foundation::Internal::GetAbiType<decltype(_UnwrapAsyncOperationSelector(stdx::declval<ABI::Windows::Foundation::IAsyncOperation<_Result>*>()))>::type _Result_abi;
+
+ InspectableClass(RuntimeClass_IAsyncOperationToAsyncOperationConverter, BaseTrust)
+ public:
+ _IAsyncOperationToAsyncOperationConverter(ABI::Windows::Foundation::IAsyncOperation<_Result>* _Operation) :
+ _AsyncInfoImpl<ABI::Windows::Foundation::IAsyncOperation<_Result>,
+ 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";
+
+ /// <summary>
+ /// Class _IAsyncOperationWithProgressToAsyncOperationConverter is used to convert an instance of IAsyncOperationWithProgress<T> into IAsyncOperation<T>
+ /// </summary>
+ template<typename _Result, typename _Progress>
+ struct _IAsyncOperationWithProgressToAsyncOperationConverter :
+ _AsyncInfoImpl<ABI::Windows::Foundation::IAsyncOperationWithProgress<_Result, _Progress>,
+ ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler<_Result, _Progress>,
+ typename ABI::Windows::Foundation::Internal::GetAbiType<decltype(_UnwrapAsyncOperationWithProgressSelector(stdx::declval<ABI::Windows::Foundation::IAsyncOperationWithProgress<_Result, _Progress>*>()))>::type>
+ {
+ typedef typename ABI::Windows::Foundation::Internal::GetAbiType<decltype(_UnwrapAsyncOperationWithProgressSelector(stdx::declval<ABI::Windows::Foundation::IAsyncOperationWithProgress<_Result, _Progress>*>()))>::type _Result_abi;
+
+ InspectableClass(RuntimeClass_IAsyncOperationWithProgressToAsyncOperationConverter, BaseTrust)
+ public:
+ _IAsyncOperationWithProgressToAsyncOperationConverter(ABI::Windows::Foundation::IAsyncOperationWithProgress<_Result, _Progress>* _Operation) :
+ _AsyncInfoImpl<ABI::Windows::Foundation::IAsyncOperationWithProgress<_Result, _Progress>,
+ 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";
+
+ /// <summary>
+ /// Class _IAsyncActionToAsyncOperationConverter is used to convert an instance of IAsyncAction into IAsyncOperation<_Unit_type>
+ /// </summary>
+ struct _IAsyncActionToAsyncOperationConverter :
+ _AsyncInfoImpl<ABI::Windows::Foundation::IAsyncAction,
+ ABI::Windows::Foundation::IAsyncActionCompletedHandler,
+ _Unit_type>
+ {
+ InspectableClass(RuntimeClass_IAsyncActionToAsyncOperationConverter, BaseTrust)
+ public:
+ _IAsyncActionToAsyncOperationConverter(ABI::Windows::Foundation::IAsyncAction* _Operation) :
+ _AsyncInfoImpl<ABI::Windows::Foundation::IAsyncAction,
+ ABI::Windows::Foundation::IAsyncActionCompletedHandler,
+ _Unit_type>(_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";
+
+ /// <summary>
+ /// Class _IAsyncActionWithProgressToAsyncOperationConverter is used to convert an instance of IAsyncActionWithProgress into IAsyncOperation<_Unit_type>
+ /// </summary>
+ template<typename _Progress>
+ struct _IAsyncActionWithProgressToAsyncOperationConverter :
+ _AsyncInfoImpl<ABI::Windows::Foundation::IAsyncActionWithProgress<_Progress>,
+ ABI::Windows::Foundation::IAsyncActionWithProgressCompletedHandler<_Progress>,
+ _Unit_type>
+ {
+ InspectableClass(RuntimeClass_IAsyncActionWithProgressToAsyncOperationConverter, BaseTrust)
+ public:
+ _IAsyncActionWithProgressToAsyncOperationConverter(ABI::Windows::Foundation::IAsyncActionWithProgress<_Progress>* _Action) :
+ _AsyncInfoImpl<ABI::Windows::Foundation::IAsyncActionWithProgress<_Progress>,
+ 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;
+ }
+ };
+}
+
+/// <summary>
+/// The <c>task_continuation_context</c> 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.
+/// </summary>
+/// <seealso cref="task Class"/>
+/**/
+class task_continuation_context : public details::_ContextCallback
+{
+public:
+
+ /// <summary>
+ /// Creates the default task continuation context.
+ /// </summary>
+ /// <returns>
+ /// The default continuation context.
+ /// </returns>
+ /// <remarks>
+ /// The default context is used if you don't specifiy a continuation context when you call the <c>then</c> 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 <c>then</c> is invoked.
+ /// <para>An apartment aware task is a task that unwraps a Windows Runtime <c>IAsyncInfo</c> 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.</para>
+ /// <para>A continuation on a non-apartment aware task will execute in a context the Runtime chooses.</para>
+ /// </remarks>
+ /**/
+ 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
+ }
+
+ /// <summary>
+ /// Creates a task continuation context which allows the Runtime to choose the execution context for a continuation.
+ /// </summary>
+ /// <returns>
+ /// A task continuation context that represents an arbitrary location.
+ /// </returns>
+ /// <remarks>
+ /// When this continuation context is used the continuation will execute in a context the runtime chooses even if the antecedent task
+ /// is apartment aware.
+ /// <para><c>use_arbitrary</c> can be used to turn off the default behavior for a continuation on an apartment
+ /// aware task created in an STA. </para>
+ /// <para>This method is only available to Windows Store apps.</para>
+ /// </remarks>
+ /**/
+ static task_continuation_context use_arbitrary()
+ {
+ task_continuation_context _Arbitrary(true);
+ _Arbitrary._Resolve(false);
+ return _Arbitrary;
+ }
+
+ /// <summary>
+ /// Returns a task continuation context object that represents the current execution context.
+ /// </summary>
+ /// <returns>
+ /// The current execution context.
+ /// </returns>
+ /// <remarks>
+ /// This method captures the caller's Windows Runtime context so that continuations can be executed in the right apartment.
+ /// <para>The value returned by <c>use_current</c> 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 <c>IAsyncInfo</c> interface, or a task that is descended from such a task. </para>
+ /// <para>This method is only available to Windows Store apps.</para>
+ /// </remarks>
+ /**/
+ 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);
+}
+/// <summary>
+/// Represents the allowed options for creating a task
+/// </summary>
+class task_options
+{
+public:
+
+
+ /// <summary>
+ /// Default list of task creation options
+ /// </summary>
+ 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)
+ {
+ }
+
+ /// <summary>
+ /// Task option that specify a cancellation token
+ /// </summary>
+ 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)
+ {
+ }
+
+ /// <summary>
+ /// Task option that specify a continuation context. This is valid only for continuations (then)
+ /// </summary>
+ 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)
+ {
+ }
+
+ /// <summary>
+ /// Task option that specify a cancellation token and a continuation context. This is valid only for continuations (then)
+ /// </summary>
+ 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)
+ {
+ }
+
+ /// <summary>
+ /// Task option that specify a scheduler with shared lifetime
+ /// </summary>
+ template<typename _SchedType>
+ 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)
+ {
+ }
+
+ /// <summary>
+ /// Task option that specify a scheduler reference
+ /// </summary>
+ 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)
+ {
+ }
+
+ /// <summary>
+ /// Task option that specify a scheduler
+ /// </summary>
+ 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)
+ {
+ }
+
+ /// <summary>
+ /// Task option copy constructor
+ /// </summary>
+ 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())
+ {
+ }
+
+ /// <summary>
+ /// Sets the given token in the options
+ /// </summary>
+ void set_cancellation_token(Concurrency::cancellation_token _Token)
+ {
+ _M_CancellationToken = _Token;
+ _M_HasCancellationToken = true;
+ }
+
+ /// <summary>
+ /// Sets the given continuation context in the options
+ /// </summary>
+ void set_continuation_context(task_continuation_context _ContinuationContext)
+ {
+ _M_ContinuationContext = _ContinuationContext;
+ }
+
+ /// <summary>
+ /// Indicates whether a cancellation token was specified by the user
+ /// </summary>
+ bool has_cancellation_token() const
+ {
+ return _M_HasCancellationToken;
+ }
+
+ /// <summary>
+ /// Returns the cancellation token
+ /// </summary>
+ Concurrency::cancellation_token get_cancellation_token() const
+ {
+ return _M_CancellationToken;
+ }
+
+ /// <summary>
+ /// Returns the continuation context
+ /// </summary>
+ task_continuation_context get_continuation_context() const
+ {
+ return _M_ContinuationContext;
+ }
+
+ /// <summary>
+ /// Indicates whether a scheduler n was specified by the user
+ /// </summary>
+ bool has_scheduler() const
+ {
+ return _M_HasScheduler;
+ }
+
+ /// <summary>
+ /// Returns the scheduler
+ /// </summary>
+ 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<typename _ReturnType> struct _Task_impl;
+
+ template<typename _ReturnType>
+ 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<unsigned long long>(_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<unsigned long long>(_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<unsigned long long>(_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<unsigned long long>(_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
+ /// <summary>
+ /// 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.
+ /// </summary>
+ /// <typeparam name="_ReturnType">
+ /// The result type of the _Task_impl.
+ /// </typeparam>
+ /// <typeparam name="_DerivedTaskHandle">
+ /// The derived task handle class. The <c>operator ()</c> needs to be implemented.
+ /// </typeparam>
+ /// <typeparam name="_BaseTaskHandle">
+ /// The base class from which _PPLTaskHandle should be derived. This is either _UnrealizedChore or _ContinuationTaskHandleBase.
+ /// </typeparam>
+ template<typename _ReturnType, typename _DerivedTaskHandle, typename _BaseTaskHandle>
+ struct _PPLTaskHandle : _BaseTaskHandle
+ {
+ _PPLTaskHandle(const typename _Task_ptr<_ReturnType>::_Type & _PTask) : _M_pTask(_PTask)
+ {
+#if _MSC_VER < 1800
+ m_pFunction = reinterpret_cast <Concurrency::TaskProc> (&_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<const _DerivedTaskHandle *>(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<const _DerivedTaskHandle *>(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
+ };
+
+ /// <summary>
+ /// The base implementation of a first-class task. This class contains all the non-type specific
+ /// implementation details of the task.
+ /// </summary>
+ /**/
+ 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;
+ }
+ /// <summary>
+ /// Requests cancellation on the task and schedules continuations if the task can be transitioned to a terminal state.
+ /// </summary>
+ /// <param name="_SynchronousCancel">
+ /// 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.
+ /// </param>
+ /// <param name="_UserException">
+ /// Whether an exception other than the internal runtime cancellation exceptions caused this cancellation.
+ /// </param>
+ /// <param name="_PropagatedFromAncestor">
+ /// 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.
+ /// </param>
+ /// <param name="_ExHolder">
+ /// The exception holder that represents the exception. Only valid when _UserException is set to true.
+ /// </param>
+ 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<decltype(_CancellationCallback)>(_CancellationCallback);
+ _M_pTokenState->_RegisterCallback(_M_pRegistration);
+#else
+ _M_pRegistration = _M_pTokenState->_RegisterCallback(reinterpret_cast<Concurrency::TaskProc>(&_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<bool>(_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
+ /// <summary>
+ /// Helper function to schedule the task on the Task Collection.
+ /// </summary>
+ /// <param name="_PTaskHandle">
+ /// The task chore handle that need to be executed.
+ /// </param>
+ /// <param name="_InliningMode">
+ /// The inlining scheduling policy for current _PTaskHandle.
+ /// </param>
+ 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
+ }
+
+ /// <summary>
+ /// Function executes a continuation. This function is recorded by a parent task implementation
+ /// when a continuation is created in order to execute later.
+ /// </summary>
+ /// <param name="_PTaskHandle">
+ /// The continuation task chore handle that need to be executed.
+ /// </param>
+ /**/
+ 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);
+ }
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ /// <typeparam name="_FuncInputType">
+ /// The input type of the task.
+ /// </typeparam>
+ /// <typeparam name="_FuncOutputType">
+ /// The output type of the task.
+ /// </typeparam>
+ /**/
+ 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<typename _ReturnType, typename _Result, typename _OpType, typename _CompHandlerType, typename _ResultType>
+ static void _AsyncInit(const typename _Task_ptr<_ReturnType>::_Type & _OuterTask,
+ _AsyncInfoImpl<_OpType, _CompHandlerType, _ResultType>* _AsyncOp)
+ {
+ typedef typename ABI::Windows::Foundation::Internal::GetAbiType<decltype(_GetUnwrappedType(stdx::declval<_OpType*>()))>::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<ABI::Windows::Foundation::IAsyncInfo> pAsyncInfo;
+ if (SUCCEEDED(hr = _Operation->QueryInterface<ABI::Windows::Foundation::IAsyncInfo>(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<typename _ReturnType, typename _InternalReturnType>
+ 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<T^> or std::pair<T^, size_t>. 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<unsigned long long>(_M_task), _State);
+ }
+ }
+ }
+#endif
+#endif
+
+ template<typename _ReturnType>
+ 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<typename _ResultType>
+ 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<typename _Task_ptr<_ResultType>::_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<HRESULT(_Unit_type*)> _MakeVoidToUnitFunc(const std::function<HRESULT(void)>& _Func)
+ {
+ return [=](_Unit_type* retVal) -> HRESULT { HRESULT hr = _Func(); *retVal = _Unit_type(); return hr; };
+ }
+
+ template <typename _Type>
+ std::function<HRESULT(_Unit_type, _Type*)> _MakeUnitToTFunc(const std::function<HRESULT(_Type*)>& _Func)
+ {
+ return [=](_Unit_type, _Type* retVal) -> HRESULT { HRESULT hr = _Func(retVal); return hr; };
+ }
+
+ template <typename _Type>
+ std::function<HRESULT(_Type, _Unit_type*)> _MakeTToUnitFunc(const std::function<HRESULT(_Type)>& _Func)
+ {
+ return[=](_Type t, _Unit_type* retVal) -> HRESULT { HRESULT hr = _Func(t); *retVal = _Unit_type(); return hr; };
+ }
+
+ inline std::function<HRESULT(_Unit_type, _Unit_type*)> _MakeUnitToUnitFunc(const std::function<HRESULT(void)>& _Func)
+ {
+ return [=](_Unit_type, _Unit_type* retVal) -> HRESULT { HRESULT hr = _Func(); *retVal = _Unit_type(); return hr; };
+ }
+}
+
+
+/// <summary>
+/// The <c>task_completion_event</c> 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.
+/// </summary>
+/// <typeparam name="_ResultType">
+/// The result type of this <c>task_completion_event</c> class.
+/// </typeparam>
+/// <remarks>
+/// 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 <c>task_completion_event</c> 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.
+/// <para>If the task completion event is never signaled, any tasks created from it will be canceled when it is destructed.</para>
+/// <para><c>task_completion_event</c> behaves like a smart pointer, and should be passed by value.</para>
+/// </remarks>
+/// <seealso cref="task Class"/>
+/**/
+template<typename _ResultType>
+class task_completion_event
+{
+public:
+ /// <summary>
+ /// Constructs a <c>task_completion_event</c> object.
+ /// </summary>
+ /**/
+ task_completion_event() : _M_Impl(std::make_shared<details::_Task_completion_event_impl<_ResultType>>())
+ {
+ }
+
+ /// <summary>
+ /// Sets the task completion event.
+ /// </summary>
+ /// <param name="_Result">
+ /// The result to set this event with.
+ /// </param>
+ /// <returns>
+ /// The method returns <c>true</c> if it was successful in setting the event. It returns <c>false</c> if the event is already set.
+ /// </returns>
+ /// <remarks>
+ /// In the presence of multiple or concurrent calls to <c>set</c>, 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 <typeparamref name="_ResultType"/> other than <c>void</c> will pass the value <paramref value="_Result"/> to their continuations.
+ /// </remarks>
+ /**/
+ 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<typename _E>
+ __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
+
+ /// <summary>
+ /// Propagates an exception to all tasks associated with this event.
+ /// </summary>
+ /// <param>
+ /// The exception_ptr that indicates the exception to set this event with.
+ /// </param>
+ /**/
+ __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
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ bool _Cancel() const
+ {
+ // Cancel with the stored exception if one exists.
+ return _CancelInternal();
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ template<typename _ExHolderType>
+#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;
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ template<typename _ExHolderType>
+#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;
+ }
+
+ /// <summary>
+ /// Tests whether current event has been either Set, or Canceled.
+ /// </summary>
+ bool _IsTriggered() const
+ {
+ return _M_Impl->_M_fHasValue || _M_Impl->_M_fIsCanceled;
+ }
+
+private:
+
+#if _MSC_VER >= 1800
+ static std::shared_ptr<details::_ExceptionHolder> _ToExceptionHolder(const std::shared_ptr<details::_ExceptionHolder>& _ExHolder, const details::_TaskCreationCallstack&)
+#else
+ static std::shared_ptr<details::_ExceptionHolder> _ToExceptionHolder(const std::shared_ptr<details::_ExceptionHolder>& _ExHolder, void*)
+#endif
+ {
+ return _ExHolder;
+ }
+
+#if _MSC_VER >= 1800
+ static std::shared_ptr<details::_ExceptionHolder> _ToExceptionHolder(std::exception_ptr _ExceptionPtr, const details::_TaskCreationCallstack &_SetExceptionAddressHint)
+#else
+ static std::shared_ptr<details::_ExceptionHolder> _ToExceptionHolder(std::exception_ptr _ExceptionPtr, void* _SetExceptionAddressHint)
+#endif
+ {
+ return std::make_shared<details::_ExceptionHolder>(_ExceptionPtr, _SetExceptionAddressHint);
+ }
+
+ template <typename T> friend class task; // task can register itself with the event by calling the private _RegisterTask
+ template <typename T> friend class task_completion_event;
+
+ typedef typename details::_Task_completion_event_impl<_ResultType>::_TaskList _TaskList;
+
+ /// <summary>
+ /// Cancels the task_completion_event.
+ /// </summary>
+ 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;
+ }
+
+ /// <summary>
+ /// Register a task with this event. This function is called when a task is constructed using
+ /// a task_completion_event.
+ /// </summary>
+ 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<details::_Task_completion_event_impl<_ResultType>> _M_Impl;
+};
+
+/// <summary>
+/// The <c>task_completion_event</c> 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.
+/// </summary>
+/// <remarks>
+/// 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 <c>task_completion_event</c> 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.
+/// <para>If the task completion event is never signaled, any tasks created from it will be canceled when it is destructed.</para>
+/// <para><c>task_completion_event</c> behaves like a smart pointer, and should be passed by value.</para>
+/// </remarks>
+/// <seealso cref="task Class"/>
+/**/
+template<>
+class task_completion_event<void>
+{
+public:
+ /// <summary>
+ /// Sets the task completion event.
+ /// </summary>
+ /// <returns>
+ /// The method returns <c>true</c> if it was successful in setting the event. It returns <c>false</c> if the event is already set.
+ /// </returns>
+ /// <remarks>
+ /// In the presence of multiple or concurrent calls to <c>set</c>, 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 <typeparamref name="_ResultType"/> other than <c>void</c> will pass the value <paramref value="_Result"/> to their continuations.
+ /// </remarks>
+ /**/
+ 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<typename _E>
+ __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
+
+ /// <summary>
+ /// Propagates an exception to all tasks associated with this event.
+ /// </summary>
+ /// <param>
+ /// The exception_ptr that indicates the exception to set this event with.
+ /// </param>
+ /**/
+ __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
+ }
+
+ /// <summary>
+ /// Cancel the task_completion_event. Any task created using this event will be marked as canceled if it has
+ /// not already been set.
+ /// </summary>
+ void _Cancel() const // 'const' (even though it's not deep) allows to safely pass events by value into lambdas
+ {
+ _M_unitEvent._Cancel();
+ }
+
+ /// <summary>
+ /// Cancel the task_completion_event with the exception holder provided. Any task created using this event will be canceled
+ /// with the same exception.
+ /// </summary>
+ void _Cancel(const std::shared_ptr<details::_ExceptionHolder>& _ExHolder) const
+ {
+ _M_unitEvent._Cancel(_ExHolder);
+ }
+
+ /// <summary>
+ /// 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.
+ /// </summary>
+ bool _StoreException(const std::shared_ptr<details::_ExceptionHolder>& _ExHolder) const
+ {
+ return _M_unitEvent._StoreException(_ExHolder);
+ }
+
+ /// <summary>
+ /// Test whether current event has been either Set, or Canceled.
+ /// </summary>
+ bool _IsTriggered() const
+ {
+ return _M_unitEvent._IsTriggered();
+ }
+
+private:
+ template <typename T> friend class task; // task can register itself with the event by calling the private _RegisterTask
+
+ /// <summary>
+ /// Register a task with this event. This function is called when a task is constructed using
+ /// a task_completion_event.
+ /// </summary>
+ void _RegisterTask(details::_Task_ptr<details::_Unit_type>::_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<details::_Unit_type> _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<typename _ReturnType, typename _Ty>
+ 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<typename _ReturnType, typename _Ty>
+ auto _IsValidTaskCtor(_Ty _Param, int, int, int, int, int, int, ...) -> typename decltype(_Param(stdx::declval<task<_ReturnType>*>()), std::true_type());
+
+ // Anything callable with a return value is fine
+ template<typename _ReturnType, typename _Ty>
+ 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<typename _ReturnType, typename _Ty>
+ 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<typename _ReturnType, typename _Ty>
+ auto _IsValidTaskCtor(_Ty _Param, int, int, int, ...) -> typename decltype(_Param->GetResults(stdx::declval<decltype(_GetUnwrappedType(stdx::declval<_Ty>()))*>()), std::true_type());
+
+ // Allow parameters with set: this covers task_completion_event
+ template<typename _ReturnType, typename _Ty>
+ auto _IsValidTaskCtor(_Ty _Param, int, int, ...) -> typename decltype(_Param.set(stdx::declval<_ReturnType>()), std::true_type());
+
+ template<typename _ReturnType, typename _Ty>
+ auto _IsValidTaskCtor(_Ty _Param, int, ...) -> typename decltype(_Param.set(), std::true_type());
+
+ // All else is invalid
+ template<typename _ReturnType, typename _Ty>
+ std::false_type _IsValidTaskCtor(_Ty _Param, ...);
+
+ template<typename _ReturnType, typename _Ty>
+ void _ValidateTaskConstructorArgs(_Ty _Param)
+ {
+ (void)_Param;
+ static_assert(std::is_same<decltype(details::_IsValidTaskCtor<_ReturnType>(_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<typename _ReturnType, typename _Ty>
+ 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<typename _ReturnType, typename _Ty>
+ 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<typename _ReturnType, typename _Ty>
+ 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<typename _ReturnType, typename _Ty>
+ 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<typename _ReturnType, typename _Ty>
+ 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<typename _ReturnType, typename _Ty>
+ 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<typename _ReturnType, typename _Ty>
+ 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<typename _ReturnType, typename _Ty>
+ 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<typename _ReturnType, typename _Ty>
+ static std::false_type _IsValidCreateAsync(_Ty _Param, ...);
+}
+
+/// <summary>
+/// The Parallel Patterns Library (PPL) <c>task</c> class. A <c>task</c> 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 <typeparamref name="_ResultType"/> on successful completion. Tasks of type <c>task<void></c> 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(<c>then</c>), and join(<c>when_all</c>) and choice(<c>when_any</c>) patterns.
+/// </summary>
+/// <typeparam name="_ReturnType">
+/// The result type of this task.
+/// </typeparam>
+/// <remarks>
+/// For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.
+/// </remarks>
+/**/
+template<typename _ReturnType>
+class task
+{
+public:
+ /// <summary>
+ /// The type of the result an object of this class produces.
+ /// </summary>
+ /**/
+ typedef _ReturnType result_type;
+
+ /// <summary>
+ /// Constructs a <c>task</c> object.
+ /// </summary>
+ /// <remarks>
+ /// The default constructor for a <c>task</c> 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 <c>get</c>, <c>wait</c> or <c>then</c>
+ /// will throw an <see cref="invalid_argument Class">invalid_argument</see> exception when called on a default constructed task.
+ /// <para>A task that is created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the task
+ /// completion event is set.</para>
+ /// <para>The version of the constructor that takes a cancellation token creates a task that can be canceled using the
+ /// <c>cancellation_token_source</c> the token was obtained from. Tasks created without a cancellation token are not cancelable.</para>
+ /// <para>Tasks created from a <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface
+ /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created
+ /// from a lamda that returns a <c>task<result_type></c> reach their terminal state when the inner task reaches its terminal state,
+ /// and not when the lamda returns.</para>
+ /// <para><c>task</c> 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.</para>
+ /// <para>The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available
+ /// to Windows Store apps.</para>
+ /// <para>For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
+ /// </remarks>
+ /**/
+ 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.
+ }
+
+ /// <summary>
+ /// Constructs a <c>task</c> object.
+ /// </summary>
+ /// <typeparam name="_Ty">
+ /// The type of the parameter from which the task is to be constructed.
+ /// </typeparam>
+ /// <param name="_Param">
+ /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a <c>task_completion_event<result_type></c>
+ /// 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 <c>std::function<X(void)></c>, where X can be a variable of type <c>result_type</c>,
+ /// <c>task<result_type></c>, or a Windows::Foundation::IAsyncInfo in Windows Store apps.
+ /// </param>
+ /// <param name="_Token">
+ /// The cancellation token to associate with this task. A task created without a cancellation token cannot be canceled. It implicitly receives
+ /// the token <c>cancellation_token::none()</c>.
+ /// </param>
+ /// <remarks>
+ /// The default constructor for a <c>task</c> 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 <c>get</c>, <c>wait</c> or <c>then</c>
+ /// will throw an <see cref="invalid_argument Class">invalid_argument</see> exception when called on a default constructed task.
+ /// <para>A task that is created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the task
+ /// completion event is set.</para>
+ /// <para>The version of the constructor that takes a cancellation token creates a task that can be canceled using the
+ /// <c>cancellation_token_source</c> the token was obtained from. Tasks created without a cancellation token are not cancelable.</para>
+ /// <para>Tasks created from a <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface
+ /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created
+ /// from a lamda that returns a <c>task<result_type></c> reach their terminal state when the inner task reaches its terminal state,
+ /// and not when the lamda returns.</para>
+ /// <para><c>task</c> 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.</para>
+ /// <para>The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available
+ /// to Windows Store apps.</para>
+ /// <para>For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
+ /// </remarks>
+ /**/
+ template<typename _Ty>
+ __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));
+ }
+
+ /// <summary>
+ /// Constructs a <c>task</c> object.
+ /// </summary>
+ /// <typeparam name="_Ty">
+ /// The type of the parameter from which the task is to be constructed.
+ /// </typeparam>
+ /// <param name="_Param">
+ /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a <c>task_completion_event<result_type></c>
+ /// 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 <c>std::function<X(void)></c>, where X can be a variable of type <c>result_type</c>,
+ /// <c>task<result_type></c>, or a Windows::Foundation::IAsyncInfo in Windows Store apps.
+ /// </param>
+ /// <param name="_Token">
+ /// The cancellation token to associate with this task. A task created without a cancellation token cannot be canceled. It implicitly receives
+ /// the token <c>cancellation_token::none()</c>.
+ /// </param>
+ /// <remarks>
+ /// The default constructor for a <c>task</c> 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 <c>get</c>, <c>wait</c> or <c>then</c>
+ /// will throw an <see cref="invalid_argument Class">invalid_argument</see> exception when called on a default constructed task.
+ /// <para>A task that is created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the task
+ /// completion event is set.</para>
+ /// <para>The version of the constructor that takes a cancellation token creates a task that can be canceled using the
+ /// <c>cancellation_token_source</c> the token was obtained from. Tasks created without a cancellation token are not cancelable.</para>
+ /// <para>Tasks created from a <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface
+ /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created
+ /// from a lamda that returns a <c>task<result_type></c> reach their terminal state when the inner task reaches its terminal state,
+ /// and not when the lamda returns.</para>
+ /// <para><c>task</c> 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.</para>
+ /// <para>The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available
+ /// to Windows Store apps.</para>
+ /// <para>For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
+ /// </remarks>
+ /**/
+ template<typename _Ty>
+ __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));
+ }
+
+ /// <summary>
+ /// Constructs a <c>task</c> object.
+ /// </summary>
+ /// <param name="_Other">
+ /// The source <c>task</c> object.
+ /// </param>
+ /// <remarks>
+ /// The default constructor for a <c>task</c> 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 <c>get</c>, <c>wait</c> or <c>then</c>
+ /// will throw an <see cref="invalid_argument Class">invalid_argument</see> exception when called on a default constructed task.
+ /// <para>A task that is created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the task
+ /// completion event is set.</para>
+ /// <para>The version of the constructor that takes a cancellation token creates a task that can be canceled using the
+ /// <c>cancellation_token_source</c> the token was obtained from. Tasks created without a cancellation token are not cancelable.</para>
+ /// <para>Tasks created from a <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface
+ /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created
+ /// from a lamda that returns a <c>task<result_type></c> reach their terminal state when the inner task reaches its terminal state,
+ /// and not when the lamda returns.</para>
+ /// <para><c>task</c> 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.</para>
+ /// <para>The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available
+ /// to Windows Store apps.</para>
+ /// <para>For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
+ /// </remarks>
+ /**/
+ task(const task& _Other) : _M_Impl(_Other._M_Impl) {}
+
+ /// <summary>
+ /// Constructs a <c>task</c> object.
+ /// </summary>
+ /// <param name="_Other">
+ /// The source <c>task</c> object.
+ /// </param>
+ /// <remarks>
+ /// The default constructor for a <c>task</c> 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 <c>get</c>, <c>wait</c> or <c>then</c>
+ /// will throw an <see cref="invalid_argument Class">invalid_argument</see> exception when called on a default constructed task.
+ /// <para>A task that is created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the task
+ /// completion event is set.</para>
+ /// <para>The version of the constructor that takes a cancellation token creates a task that can be canceled using the
+ /// <c>cancellation_token_source</c> the token was obtained from. Tasks created without a cancellation token are not cancelable.</para>
+ /// <para>Tasks created from a <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface
+ /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created
+ /// from a lamda that returns a <c>task<result_type></c> reach their terminal state when the inner task reaches its terminal state,
+ /// and not when the lamda returns.</para>
+ /// <para><c>task</c> 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.</para>
+ /// <para>The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available
+ /// to Windows Store apps.</para>
+ /// <para>For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
+ /// </remarks>
+ /**/
+ task(task&& _Other) : _M_Impl(std::move(_Other._M_Impl)) {}
+
+ /// <summary>
+ /// Replaces the contents of one <c>task</c> object with another.
+ /// </summary>
+ /// <param name="_Other">
+ /// The source <c>task</c> object.
+ /// </param>
+ /// <remarks>
+ /// As <c>task</c> behaves like a smart pointer, after a copy assignment, this <c>task</c> objects represents the same
+ /// actual task as <paramref name="_Other"/> does.
+ /// </remarks>
+ /**/
+ task& operator=(const task& _Other)
+ {
+ if (this != &_Other)
+ {
+ _M_Impl = _Other._M_Impl;
+ }
+ return *this;
+ }
+
+ /// <summary>
+ /// Replaces the contents of one <c>task</c> object with another.
+ /// </summary>
+ /// <param name="_Other">
+ /// The source <c>task</c> object.
+ /// </param>
+ /// <remarks>
+ /// As <c>task</c> behaves like a smart pointer, after a copy assignment, this <c>task</c> objects represents the same
+ /// actual task as <paramref name="_Other"/> does.
+ /// </remarks>
+ /**/
+ task& operator=(task&& _Other)
+ {
+ if (this != &_Other)
+ {
+ _M_Impl = std::move(_Other._M_Impl);
+ }
+ return *this;
+ }
+
+ /// <summary>
+ /// Adds a continuation task to this task.
+ /// </summary>
+ /// <typeparam name="_Function">
+ /// The type of the function object that will be invoked by this task.
+ /// </typeparam>
+ /// <param name="_Func">
+ /// The continuation function to execute when this task completes. This continuation function must take as input
+ /// a variable of either <c>result_type</c> or <c>task<result_type></c>, where <c>result_type</c> is the type
+ /// of the result this task produces.
+ /// </param>
+ /// <returns>
+ /// The newly created continuation task. The result type of the returned task is determined by what <paramref name="_Func"/> returns.
+ /// </returns>
+ /// <remarks>
+ /// The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available
+ /// to Windows Store apps.
+ /// <para>For more information on how to use task continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
+ /// </remarks>
+ /**/
+ template<typename _Function>
+ __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
+ }
+
+ /// <summary>
+ /// Adds a continuation task to this task.
+ /// </summary>
+ /// <typeparam name="_Function">
+ /// The type of the function object that will be invoked by this task.
+ /// </typeparam>
+ /// <param name="_Func">
+ /// The continuation function to execute when this task completes. This continuation function must take as input
+ /// a variable of either <c>result_type</c> or <c>task<result_type></c>, where <c>result_type</c> is the type
+ /// of the result this task produces.
+ /// </param>
+ /// <param name="_CancellationToken">
+ /// 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.
+ /// </param>
+ /// <returns>
+ /// The newly created continuation task. The result type of the returned task is determined by what <paramref name="_Func"/> returns.
+ /// </returns>
+ /// <remarks>
+ /// The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available
+ /// to Windows Store apps.
+ /// <para>For more information on how to use task continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
+ /// </remarks>
+ /**/
+ template<typename _Function>
+ __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
+ /// <summary>
+ /// Adds a continuation task to this task.
+ /// </summary>
+ /// <typeparam name="_Function">
+ /// The type of the function object that will be invoked by this task.
+ /// </typeparam>
+ /// <param name="_Func">
+ /// The continuation function to execute when this task completes. This continuation function must take as input
+ /// a variable of either <c>result_type</c> or <c>task<result_type></c>, where <c>result_type</c> is the type
+ /// of the result this task produces.
+ /// </param>
+ /// <param name="_ContinuationContext">
+ /// 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 <see cref="task_continuation_context Class">task_continuation_context</see>
+ /// </param>
+ /// <returns>
+ /// The newly created continuation task. The result type of the returned task is determined by what <paramref name="_Func"/> returns.
+ /// </returns>
+ /// <remarks>
+ /// The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available
+ /// to Windows Store apps.
+ /// <para>For more information on how to use task continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
+ /// </remarks>
+ /**/
+ template<typename _Function>
+ __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
+ /// <summary>
+ /// Adds a continuation task to this task.
+ /// </summary>
+ /// <typeparam name="_Function">
+ /// The type of the function object that will be invoked by this task.
+ /// </typeparam>
+ /// <param name="_Func">
+ /// The continuation function to execute when this task completes. This continuation function must take as input
+ /// a variable of either <c>result_type</c> or <c>task<result_type></c>, where <c>result_type</c> is the type
+ /// of the result this task produces.
+ /// </param>
+ /// <param name="_CancellationToken">
+ /// 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.
+ /// </param>
+ /// <param name="_ContinuationContext">
+ /// 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 <see cref="task_continuation_context Class">task_continuation_context</see>
+ /// </param>
+ /// <returns>
+ /// The newly created continuation task. The result type of the returned task is determined by what <paramref name="_Func"/> returns.
+ /// </returns>
+ /// <remarks>
+ /// The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available
+ /// to Windows Store apps.
+ /// <para>For more information on how to use task continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
+ /// </remarks>
+ /**/
+ template<typename _Function>
+ __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
+ }
+
+ /// <summary>
+ /// Waits for this task to reach a terminal state. It is possible for <c>wait</c> 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.
+ /// </summary>
+ /// <returns>
+ /// A <c>task_status</c> value which could be either <c>completed</c> or <c>canceled</c>. If the task encountered an exception
+ /// during execution, or an exception was propagated to it from an antecedent task, <c>wait</c> will throw that exception.
+ /// </returns>
+ /**/
+ 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();
+ }
+
+ /// <summary>
+ /// Returns the result this task produced. If the task is not in a terminal state, a call to <c>get</c> will wait for the task to
+ /// finish. This method does not return a value when called on a task with a <c>result_type</c> of <c>void</c>.
+ /// </summary>
+ /// <returns>
+ /// The result of the task.
+ /// </returns>
+ /// <remarks>
+ /// If the task is canceled, a call to <c>get</c> will throw a <see cref="task_canceled Class">task_canceled</see> exception. If the task
+ /// encountered an different exception or an exception was propagated to it from an antecedent task, a call to <c>get</c> will throw that exception.
+ /// </remarks>
+ /**/
+ _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
+ /// <summary>
+ /// Determines if the task is completed.
+ /// </summary>
+ /// <returns>
+ /// True if the task has completed, false otherwise.
+ /// </returns>
+ /// <remarks>
+ /// The function returns true if the task is completed or canceled (with or without user exception).
+ /// </remarks>
+ 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();
+ }
+
+ /// <summary>
+ /// Returns the scheduler for this task
+ /// </summary>
+ /// <returns>
+ /// A pointer to the scheduler
+ /// </returns>
+ 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
+ /// <summary>
+ /// Determines whether the task unwraps a Windows Runtime <c>IAsyncInfo</c> interface or is descended from such a task.
+ /// </summary>
+ /// <returns>
+ /// <c>true</c> if the task unwraps an <c>IAsyncInfo</c> interface or is descended from such a task, <c>false</c> otherwise.
+ /// </returns>
+ /**/
+ 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();
+ }
+
+ /// <summary>
+ /// Determines whether two <c>task</c> objects represent the same internal task.
+ /// </summary>
+ /// <returns>
+ /// <c>true</c> if the objects refer to the same underlying task, and <c>false</c> otherwise.
+ /// </returns>
+ /**/
+ bool operator==(const task<_ReturnType>& _Rhs) const
+ {
+ return (_M_Impl == _Rhs._M_Impl);
+ }
+
+ /// <summary>
+ /// Determines whether two <c>task</c> objects represent different internal tasks.
+ /// </summary>
+ /// <returns>
+ /// <c>true</c> if the objects refer to different underlying tasks, and <c>false</c> otherwise.
+ /// </returns>
+ /**/
+ bool operator!=(const task<_ReturnType>& _Rhs) const
+ {
+ return !operator==(_Rhs);
+ }
+
+ /// <summary>
+ /// Create an underlying task implementation.
+ /// </summary>
+#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
+ }
+ }
+
+ /// <summary>
+ /// Return the underlying implementation for this task.
+ /// </summary>
+ const typename details::_Task_ptr<_ReturnType>::_Type & _GetImpl() const
+ {
+ return _M_Impl;
+ }
+
+ /// <summary>
+ /// Set the implementation of the task to be the supplied implementaion.
+ /// </summary>
+ void _SetImpl(const typename details::_Task_ptr<_ReturnType>::_Type & _Impl)
+ {
+ _CONCRT_ASSERT(_M_Impl == nullptr);
+ _M_Impl = _Impl;
+ }
+
+ /// <summary>
+ /// Set the implementation of the task to be the supplied implementaion using a move instead of a copy.
+ /// </summary>
+ void _SetImpl(typename details::_Task_ptr<_ReturnType>::_Type && _Impl)
+ {
+ _CONCRT_ASSERT(_M_Impl == nullptr);
+ _M_Impl = std::move(_Impl);
+ }
+
+ /// <summary>
+ /// Sets a property determining whether the task is apartment aware.
+ /// </summary>
+ void _SetAsync(bool _Async = true)
+ {
+ _GetImpl()->_SetAsync(_Async);
+ }
+
+ /// <summary>
+ /// Sets a field in the task impl to the return address for calls to the task constructors and the then method.
+ /// </summary>
+#if _MSC_VER >= 1800
+ void _SetTaskCreationCallstack(const details::_TaskCreationCallstack &_callstack)
+ {
+ _GetImpl()->_SetTaskCreationCallstack(_callstack);
+ }
+#else
+ void _SetTaskCreationAddressHint(void* _Address)
+ {
+ _GetImpl()->_SetTaskCreationAddressHint(_Address);
+ }
+#endif
+ /// <summary>
+ /// 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.
+ /// </summary>
+ template<typename _Function>
+#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 <typename T> 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<typename _RetType>
+ class _Init_func_transformer
+ {
+ public:
+ static auto _Perform(std::function<HRESULT(_RetType*)> _Func) -> decltype(_Func)
+ {
+ return _Func;
+ }
+ };
+
+ template<>
+ class _Init_func_transformer<void>
+ {
+ public:
+ static auto _Perform(std::function<HRESULT(void)> _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 <typename _InternalReturnType, typename _Function, typename _TypeSelection>
+ 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 <typename _Func, typename _RetArg>
+ 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<details::_IAsyncOperationToAsyncOperationConverter<_InternalReturnType>>(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<details::_IAsyncActionToAsyncOperationConverter>(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<decltype(_M_function())>::_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<details::_IAsyncOperationWithProgressToAsyncOperationConverter<_InternalReturnType, _ProgressType>>(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<decltype(_M_function())>::_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<details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>>(retVal).Get());
+ }
+ };
+
+ /// <summary>
+ /// 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'.
+ /// </summary>
+ template<typename _InpType, typename _OutType>
+ class _Continuation_func_transformer
+ {
+ public:
+ static auto _Perform(std::function<HRESULT(_InpType, _OutType*)> _Func) -> decltype(_Func)
+ {
+ return _Func;
+ }
+ };
+
+ template<typename _OutType>
+ class _Continuation_func_transformer<void, _OutType>
+ {
+ public:
+ static auto _Perform(std::function<HRESULT(_OutType*)> _Func) -> decltype(details::_MakeUnitToTFunc<_OutType>(_Func))
+ {
+ return details::_MakeUnitToTFunc<_OutType>(_Func);
+ }
+ };
+
+ template<typename _InType>
+ class _Continuation_func_transformer<_InType, void>
+ {
+ public:
+ static auto _Perform(std::function<HRESULT(_InType)> _Func) -> decltype(details::_MakeTToUnitFunc<_InType>(_Func))
+ {
+ return details::_MakeTToUnitFunc<_InType>(_Func);
+ }
+ };
+
+ template<>
+ class _Continuation_func_transformer<void, void>
+ {
+ public:
+ static auto _Perform(std::function<HRESULT(void)> _Func) -> decltype(details::_MakeUnitToUnitFunc(_Func))
+ {
+ return details::_MakeUnitToUnitFunc(_Func);
+ }
+ };
+ /// <summary>
+ /// The task handle type used to create a 'continuation task'.
+ /// </summary>
+ template <typename _InternalReturnType, typename _ContinuationReturnType, typename _Function, typename _IsTaskBased, typename _TypeSelection>
+ struct _ContinuationTaskHandle :
+ details::_PPLTaskHandle<typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_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<typename details::_NormalizeVoidToUnitType<_ContinuationReturnType>::_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 <typename _Func, typename _Arg, typename _RetArg>
+ 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<details::_IAsyncOperationToAsyncOperationConverter<_ContinuationReturnType>>(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<details::_IAsyncActionToAsyncOperationConverter>(
+ 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<decltype(_OpWithProgress)>::_Value _ProgressType;
+
+ if (FAILED(hr)) throw std::make_exception_ptr(hr);
+ details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(
+ _M_pTask,
+ Microsoft::WRL::Make<details::_IAsyncOperationWithProgressToAsyncOperationConverter<_ContinuationReturnType, _ProgressType>>(_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<decltype(_OpWithProgress)>::_Value _ProgressType;
+
+ if (FAILED(hr)) throw std::make_exception_ptr(hr);
+ details::_Task_impl_base::_AsyncInit<_NormalizedContinuationReturnType, _ContinuationReturnType>(
+ _M_pTask,
+ Microsoft::WRL::Make<details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>>(_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<details::_IAsyncOperationToAsyncOperationConverter<_ContinuationReturnType>>(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<details::_IAsyncActionToAsyncOperationConverter>(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<decltype(_M_function(_ResultTask))>::_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<details::_IAsyncOperationWithProgressToAsyncOperationConverter<_ContinuationReturnType, _ProgressType>>(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<decltype(_M_function(_ResultTask))>::_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<details::_IAsyncActionWithProgressToAsyncOperationConverter<_ProgressType>>(retVal));
+ }
+ };
+ /// <summary>
+ /// Initializes a task using a lambda, function pointer or function object.
+ /// </summary>
+ template<typename _InternalReturnType, typename _Function>
+ 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);
+ }
+
+ /// <summary>
+ /// Initializes a task using a task completion event.
+ /// </summary>
+ void _TaskInitNoFunctor(task_completion_event<_ReturnType>& _Event)
+ {
+ _Event._RegisterTask(_M_Impl);
+ }
+
+ /// <summary>
+ /// Initializes a task using an asynchronous operation IAsyncOperation<T>*
+ /// </summary>
+ template<typename _Result, typename _OpType, typename _CompHandlerType, typename _ResultType>
+ 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);
+ }
+
+ /// <summary>
+ /// Initializes a task using an asynchronous operation IAsyncOperation<T>*
+ /// </summary>
+ template<typename _Result>
+ void _TaskInitNoFunctor(ABI::Windows::Foundation::IAsyncOperation<_Result>* _AsyncOp)
+ {
+ _TaskInitAsyncOp<_Result>(Microsoft::WRL::Make<details::_IAsyncOperationToAsyncOperationConverter<_Result>>(_AsyncOp).Get());
+ }
+
+ /// <summary>
+ /// Initializes a task using an asynchronous operation with progress IAsyncOperationWithProgress<T, P>*
+ /// </summary>
+ template<typename _Result, typename _Progress>
+ void _TaskInitNoFunctor(ABI::Windows::Foundation::IAsyncOperationWithProgress<_Result, _Progress>* _AsyncOp)
+ {
+ _TaskInitAsyncOp<_Result>(Microsoft::WRL::Make<details::_IAsyncOperationWithProgressToAsyncOperationConverter<_Result, _Progress>>(_AsyncOp).Get());
+ }
+ /// <summary>
+ /// Initializes a task using a callable object.
+ /// </summary>
+ template<typename _Function>
+ void _TaskInitMaybeFunctor(_Function & _Func, std::true_type)
+ {
+ _TaskInitWithFunctor<_ReturnType, _Function>(_Func);
+ }
+
+ /// <summary>
+ /// Initializes a task using a non-callable object.
+ /// </summary>
+ template<typename _Ty>
+ void _TaskInitMaybeFunctor(_Ty & _Param, std::false_type)
+ {
+ _TaskInitNoFunctor(_Param);
+ }
+#if _MSC_VER >= 1800
+ template<typename _InternalReturnType, typename _Function>
+ 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
+ /// <summary>
+ /// The one and only implementation of then for void and non-void tasks.
+ /// </summary>
+ template<typename _InternalReturnType, typename _Function>
+#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<typename _Function_type_traits::_FuncRetType> _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;
+};
+
+/// <summary>
+/// The Parallel Patterns Library (PPL) <c>task</c> class. A <c>task</c> 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 <typeparamref name="_ResultType"/> on successful completion. Tasks of type <c>task<void></c> 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(<c>then</c>), and join(<c>when_all</c>) and choice(<c>when_any</c>) patterns.
+/// </summary>
+/// <remarks>
+/// For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.
+/// </remarks>
+/**/
+template<>
+class task<void>
+{
+public:
+ /// <summary>
+ /// The type of the result an object of this class produces.
+ /// </summary>
+ /**/
+ typedef void result_type;
+
+ /// <summary>
+ /// Constructs a <c>task</c> object.
+ /// </summary>
+ /// <remarks>
+ /// The default constructor for a <c>task</c> 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 <c>get</c>, <c>wait</c> or <c>then</c>
+ /// will throw an <see cref="invalid_argument Class">invalid_argument</see> exception when called on a default constructed task.
+ /// <para>A task that is created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the task
+ /// completion event is set.</para>
+ /// <para>The version of the constructor that takes a cancellation token creates a task that can be canceled using the
+ /// <c>cancellation_token_source</c> the token was obtained from. Tasks created without a cancellation token are not cancelable.</para>
+ /// <para>Tasks created from a <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface
+ /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created
+ /// from a lamda that returns a <c>task<result_type></c> reach their terminal state when the inner task reaches its terminal state,
+ /// and not when the lamda returns.</para>
+ /// <para><c>task</c> 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.</para>
+ /// <para>The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available
+ /// to Windows Store apps.</para>
+ /// <para>For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
+ /// </remarks>
+ /**/
+ 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
+ /// <summary>
+ /// Constructs a <c>task</c> object.
+ /// </summary>
+ /// <typeparam name="_Ty">
+ /// The type of the parameter from which the task is to be constructed.
+ /// </typeparam>
+ /// <param name="_Param">
+ /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a <c>task_completion_event<result_type></c>
+ /// 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 <c>std::function<X(void)></c>, where X can be a variable of type <c>result_type</c>,
+ /// <c>task<result_type></c>, or a Windows::Foundation::IAsyncInfo in Windows Store apps.
+ /// </param>
+ /// <remarks>
+ /// The default constructor for a <c>task</c> 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 <c>get</c>, <c>wait</c> or <c>then</c>
+ /// will throw an <see cref="invalid_argument Class">invalid_argument</see> exception when called on a default constructed task.
+ /// <para>A task that is created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the task
+ /// completion event is set.</para>
+ /// <para>The version of the constructor that takes a cancellation token creates a task that can be canceled using the
+ /// <c>cancellation_token_source</c> the token was obtained from. Tasks created without a cancellation token are not cancelable.</para>
+ /// <para>Tasks created from a <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface
+ /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created
+ /// from a lamda that returns a <c>task<result_type></c> reach their terminal state when the inner task reaches its terminal state,
+ /// and not when the lamda returns.</para>
+ /// <para><c>task</c> 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.</para>
+ /// <para>The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available
+ /// to Windows Store apps.</para>
+ /// <para>For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
+ /// </remarks>
+ /**/
+ template<typename _Ty>
+ __declspec(noinline) // Ask for no inlining so that the _ReturnAddress intrinsic gives us the expected result
+ explicit task(_Ty _Param)
+ {
+ details::_ValidateTaskConstructorArgs<void, _Ty>(_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<void>(_Param, 0, 0, 0));
+ }
+#endif
+ /// <summary>
+ /// Constructs a <c>task</c> object.
+ /// </summary>
+ /// <typeparam name="_Ty">
+ /// The type of the parameter from which the task is to be constructed.
+ /// </typeparam>
+ /// <param name="_Param">
+ /// The parameter from which the task is to be constructed. This could be a lambda, a function object, a <c>task_completion_event<result_type></c>
+ /// 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 <c>std::function<X(void)></c>, where X can be a variable of type <c>result_type</c>,
+ /// <c>task<result_type></c>, or a Windows::Foundation::IAsyncInfo in Windows Store apps.
+ /// </param>
+ /// <param name="_Token">
+ /// The cancellation token to associate with this task. A task created without a cancellation token cannot be canceled. It implicitly receives
+ /// the token <c>cancellation_token::none()</c>.
+ /// </param>
+ /// <remarks>
+ /// The default constructor for a <c>task</c> 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 <c>get</c>, <c>wait</c> or <c>then</c>
+ /// will throw an <see cref="invalid_argument Class">invalid_argument</see> exception when called on a default constructed task.
+ /// <para>A task that is created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the task
+ /// completion event is set.</para>
+ /// <para>The version of the constructor that takes a cancellation token creates a task that can be canceled using the
+ /// <c>cancellation_token_source</c> the token was obtained from. Tasks created without a cancellation token are not cancelable.</para>
+ /// <para>Tasks created from a <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface
+ /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created
+ /// from a lamda that returns a <c>task<result_type></c> reach their terminal state when the inner task reaches its terminal state,
+ /// and not when the lamda returns.</para>
+ /// <para><c>task</c> 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.</para>
+ /// <para>The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available
+ /// to Windows Store apps.</para>
+ /// <para>For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
+ /// </remarks>
+ /**/
+ template<typename _Ty>
+ __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<void, _Ty>(_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<void>(_Param, 0, 0, 0));
+ }
+
+ /// <summary>
+ /// Constructs a <c>task</c> object.
+ /// </summary>
+ /// <param name="_Other">
+ /// The source <c>task</c> object.
+ /// </param>
+ /// <remarks>
+ /// The default constructor for a <c>task</c> 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 <c>get</c>, <c>wait</c> or <c>then</c>
+ /// will throw an <see cref="invalid_argument Class">invalid_argument</see> exception when called on a default constructed task.
+ /// <para>A task that is created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the task
+ /// completion event is set.</para>
+ /// <para>The version of the constructor that takes a cancellation token creates a task that can be canceled using the
+ /// <c>cancellation_token_source</c> the token was obtained from. Tasks created without a cancellation token are not cancelable.</para>
+ /// <para>Tasks created from a <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface
+ /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created
+ /// from a lamda that returns a <c>task<result_type></c> reach their terminal state when the inner task reaches its terminal state,
+ /// and not when the lamda returns.</para>
+ /// <para><c>task</c> 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.</para>
+ /// <para>The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available
+ /// to Windows Store apps.</para>
+ /// <para>For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
+ /// </remarks>
+ /**/
+ task(const task& _Other) : _M_unitTask(_Other._M_unitTask){}
+
+ /// <summary>
+ /// Constructs a <c>task</c> object.
+ /// </summary>
+ /// <param name="_Other">
+ /// The source <c>task</c> object.
+ /// </param>
+ /// <remarks>
+ /// The default constructor for a <c>task</c> 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 <c>get</c>, <c>wait</c> or <c>then</c>
+ /// will throw an <see cref="invalid_argument Class">invalid_argument</see> exception when called on a default constructed task.
+ /// <para>A task that is created from a <c>task_completion_event</c> will complete (and have its continuations scheduled) when the task
+ /// completion event is set.</para>
+ /// <para>The version of the constructor that takes a cancellation token creates a task that can be canceled using the
+ /// <c>cancellation_token_source</c> the token was obtained from. Tasks created without a cancellation token are not cancelable.</para>
+ /// <para>Tasks created from a <c>Windows::Foundation::IAsyncInfo</c> interface or a lambda that returns an <c>IAsyncInfo</c> interface
+ /// reach their terminal state when the enclosed Windows Runtime asynchronous operation or action completes. Similarly, tasks created
+ /// from a lamda that returns a <c>task<result_type></c> reach their terminal state when the inner task reaches its terminal state,
+ /// and not when the lamda returns.</para>
+ /// <para><c>task</c> 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.</para>
+ /// <para>The constructor overloads that take a Windows::Foundation::IAsyncInfo interface or a lambda returning such an interface, are only available
+ /// to Windows Store apps.</para>
+ /// <para>For more information, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
+ /// </remarks>
+ /**/
+ task(task&& _Other) : _M_unitTask(std::move(_Other._M_unitTask)) {}
+
+ /// <summary>
+ /// Replaces the contents of one <c>task</c> object with another.
+ /// </summary>
+ /// <param name="_Other">
+ /// The source <c>task</c> object.
+ /// </param>
+ /// <remarks>
+ /// As <c>task</c> behaves like a smart pointer, after a copy assignment, this <c>task</c> objects represents the same
+ /// actual task as <paramref name="_Other"/> does.
+ /// </remarks>
+ /**/
+ task& operator=(const task& _Other)
+ {
+ if (this != &_Other)
+ {
+ _M_unitTask = _Other._M_unitTask;
+ }
+ return *this;
+ }
+
+ /// <summary>
+ /// Replaces the contents of one <c>task</c> object with another.
+ /// </summary>
+ /// <param name="_Other">
+ /// The source <c>task</c> object.
+ /// </param>
+ /// <remarks>
+ /// As <c>task</c> behaves like a smart pointer, after a copy assignment, this <c>task</c> objects represents the same
+ /// actual task as <paramref name="_Other"/> does.
+ /// </remarks>
+ /**/
+ task& operator=(task&& _Other)
+ {
+ if (this != &_Other)
+ {
+ _M_unitTask = std::move(_Other._M_unitTask);
+ }
+ return *this;
+ }
+#if _MSC_VER < 1800
+ /// <summary>
+ /// Adds a continuation task to this task.
+ /// </summary>
+ /// <typeparam name="_Function">
+ /// The type of the function object that will be invoked by this task.
+ /// </typeparam>
+ /// <param name="_Func">
+ /// The continuation function to execute when this task completes. This continuation function must take as input
+ /// a variable of either <c>result_type</c> or <c>task<result_type></c>, where <c>result_type</c> is the type
+ /// of the result this task produces.
+ /// </param>
+ /// <returns>
+ /// The newly created continuation task. The result type of the returned task is determined by what <paramref name="_Func"/> returns.
+ /// </returns>
+ /// <remarks>
+ /// The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available
+ /// to Windows Store apps.
+ /// <para>For more information on how to use task continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
+ /// </remarks>
+ /**/
+ template<typename _Function>
+ __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<void, _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
+ /// <summary>
+ /// Adds a continuation task to this task.
+ /// </summary>
+ /// <typeparam name="_Function">
+ /// The type of the function object that will be invoked by this task.
+ /// </typeparam>
+ /// <param name="_Func">
+ /// The continuation function to execute when this task completes. This continuation function must take as input
+ /// a variable of either <c>result_type</c> or <c>task<result_type></c>, where <c>result_type</c> is the type
+ /// of the result this task produces.
+ /// </param>
+ /// <param name="_CancellationToken">
+ /// 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.
+ /// </param>
+ /// <returns>
+ /// The newly created continuation task. The result type of the returned task is determined by what <paramref name="_Func"/> returns.
+ /// </returns>
+ /// <remarks>
+ /// The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available
+ /// to Windows Store apps.
+ /// <para>For more information on how to use task continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
+ /// </remarks>
+ /**/
+ template<typename _Function>
+ __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<void, _Function>(_Func, _TaskOptions);
+ }
+#else
+ auto then(const _Function& _Func, Concurrency::cancellation_token _CancellationToken) const -> typename details::_ContinuationTypeTraits<_Function, void>::_TaskOfType
+ {
+ auto _ContinuationTask = _M_unitTask._ThenImpl<void, _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;
+ }
+ /// <summary>
+ /// Adds a continuation task to this task.
+ /// </summary>
+ /// <typeparam name="_Function">
+ /// The type of the function object that will be invoked by this task.
+ /// </typeparam>
+ /// <param name="_Func">
+ /// The continuation function to execute when this task completes. This continuation function must take as input
+ /// a variable of either <c>result_type</c> or <c>task<result_type></c>, where <c>result_type</c> is the type
+ /// of the result this task produces.
+ /// </param>
+ /// <param name="_ContinuationContext">
+ /// 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 <see cref="task_continuation_context Class">task_continuation_context</see>
+ /// </param>
+ /// <returns>
+ /// The newly created continuation task. The result type of the returned task is determined by what <paramref name="_Func"/> returns.
+ /// </returns>
+ /// <remarks>
+ /// The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available
+ /// to Windows Store apps.
+ /// <para>For more information on how to use task continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
+ /// </remarks>
+ /**/
+ template<typename _Function>
+ __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<void, _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
+ /// <summary>
+ /// Adds a continuation task to this task.
+ /// </summary>
+ /// <typeparam name="_Function">
+ /// The type of the function object that will be invoked by this task.
+ /// </typeparam>
+ /// <param name="_Func">
+ /// The continuation function to execute when this task completes. This continuation function must take as input
+ /// a variable of either <c>result_type</c> or <c>task<result_type></c>, where <c>result_type</c> is the type
+ /// of the result this task produces.
+ /// </param>
+ /// <param name="_CancellationToken">
+ /// 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.
+ /// </param>
+ /// <param name="_ContinuationContext">
+ /// 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 <see cref="task_continuation_context Class">task_continuation_context</see>
+ /// </param>
+ /// <returns>
+ /// The newly created continuation task. The result type of the returned task is determined by what <paramref name="_Func"/> returns.
+ /// </returns>
+ /// <remarks>
+ /// The overloads of <c>then</c> that take a lambda or functor that returns a Windows::Foundation::IAsyncInfo interface, are only available
+ /// to Windows Store apps.
+ /// <para>For more information on how to use task continuations to compose asynchronous work, see <see cref="Task Parallelism (Concurrency Runtime)"/>.</para>
+ /// </remarks>
+ /**/
+ template<typename _Function>
+ __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<void, _Function>(_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<void, _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
+
+ /// <summary>
+ /// Waits for this task to reach a terminal state. It is possible for <c>wait</c> 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.
+ /// </summary>
+ /// <returns>
+ /// A <c>task_status</c> value which could be either <c>completed</c> or <c>canceled</c>. If the task encountered an exception
+ /// during execution, or an exception was propagated to it from an antecedent task, <c>wait</c> will throw that exception.
+ /// </returns>
+ /**/
+ task_status wait() const
+ {
+ return _M_unitTask.wait();
+ }
+
+ /// <summary>
+ /// Returns the result this task produced. If the task is not in a terminal state, a call to <c>get</c> will wait for the task to
+ /// finish. This method does not return a value when called on a task with a <c>result_type</c> of <c>void</c>.
+ /// </summary>
+ /// <remarks>
+ /// If the task is canceled, a call to <c>get</c> will throw a <see cref="task_canceled Class">task_canceled</see> exception. If the task
+ /// encountered an different exception or an exception was propagated to it from an antecedent task, a call to <c>get</c> will throw that exception.
+ /// </remarks>
+ /**/
+ void get() const
+ {
+ _M_unitTask.get();
+ }
+#if _MSC_VER >= 1800
+
+ /// <summary>
+ /// Determines if the task is completed.
+ /// </summary>
+ /// <returns>
+ /// True if the task has completed, false otherwise.
+ /// </returns>
+ /// <remarks>
+ /// The function returns true if the task is completed or canceled (with or without user exception).
+ /// </remarks>
+ bool is_done() const
+ {
+ return _M_unitTask.is_done();
+ }
+
+ /// <summary>
+ /// Returns the scheduler for this task
+ /// </summary>
+ /// <returns>
+ /// A pointer to the scheduler
+ /// </returns>
+ Concurrency::scheduler_ptr scheduler() const
+ {
+ return _M_unitTask.scheduler();
+ }
+#endif
+ /// <summary>
+ /// Determines whether the task unwraps a Windows Runtime <c>IAsyncInfo</c> interface or is descended from such a task.
+ /// </summary>
+ /// <returns>
+ /// <c>true</c> if the task unwraps an <c>IAsyncInfo</c> interface or is descended from such a task, <c>false</c> otherwise.
+ /// </returns>
+ /**/
+ bool is_apartment_aware() const
+ {
+ return _M_unitTask.is_apartment_aware();
+ }
+
+ /// <summary>
+ /// Determines whether two <c>task</c> objects represent the same internal task.
+ /// </summary>
+ /// <returns>
+ /// <c>true</c> if the objects refer to the same underlying task, and <c>false</c> otherwise.
+ /// </returns>
+ /**/
+ bool operator==(const task<void>& _Rhs) const
+ {
+ return (_M_unitTask == _Rhs._M_unitTask);
+ }
+
+ /// <summary>
+ /// Determines whether two <c>task</c> objects represent different internal tasks.
+ /// </summary>
+ /// <returns>
+ /// <c>true</c> if the objects refer to different underlying tasks, and <c>false</c> otherwise.
+ /// </returns>
+ /**/
+ bool operator!=(const task<void>& _Rhs) const
+ {
+ return !operator==(_Rhs);
+ }
+
+ /// <summary>
+ /// Create an underlying task implementation.
+ /// </summary>
+#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
+
+ /// <summary>
+ /// Return the underlying implementation for this task.
+ /// </summary>
+ const details::_Task_ptr<details::_Unit_type>::_Type & _GetImpl() const
+ {
+ return _M_unitTask._M_Impl;
+ }
+
+ /// <summary>
+ /// Set the implementation of the task to be the supplied implementaion.
+ /// </summary>
+ void _SetImpl(const details::_Task_ptr<details::_Unit_type>::_Type & _Impl)
+ {
+ _M_unitTask._SetImpl(_Impl);
+ }
+
+ /// <summary>
+ /// Set the implementation of the task to be the supplied implementaion using a move instead of a copy.
+ /// </summary>
+ void _SetImpl(details::_Task_ptr<details::_Unit_type>::_Type && _Impl)
+ {
+ _M_unitTask._SetImpl(std::move(_Impl));
+ }
+
+ /// <summary>
+ /// Sets a property determining whether the task is apartment aware.
+ /// </summary>
+ void _SetAsync(bool _Async = true)
+ {
+ _M_unitTask._SetAsync(_Async);
+ }
+
+ /// <summary>
+ /// Sets a field in the task impl to the return address for calls to the task constructors and the then method.
+ /// </summary>
+#if _MSC_VER >= 1800
+ void _SetTaskCreationCallstack(const details::_TaskCreationCallstack &_callstack)
+ {
+ _M_unitTask._SetTaskCreationCallstack(_callstack);
+ }
+#else
+ void _SetTaskCreationAddressHint(void* _Address)
+ {
+ _M_unitTask._SetTaskCreationAddressHint(_Address);
+ }
+#endif
+
+ /// <summary>
+ /// An internal version of then that takes additional flags and executes the continuation inline. Used for runtime internal continuations only.
+ /// </summary>
+ template<typename _Function>
+#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<void, _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, void>::_TaskOfType
+ {
+ return _M_unitTask._ThenImpl<void, _Function>(_Func, _PTokenState, task_continuation_context::use_default(), _Aggregating, _InliningMode);
+ }
+#endif
+
+private:
+ template <typename T> friend class task;
+ template <typename T> friend class task_completion_event;
+
+ /// <summary>
+ /// Initializes a task using a task completion event.
+ /// </summary>
+ void _TaskInitNoFunctor(task_completion_event<void>& _Event)
+ {
+ _M_unitTask._TaskInitNoFunctor(_Event._M_unitEvent);
+ }
+ /// <summary>
+ /// Initializes a task using an asynchronous action IAsyncAction*
+ /// </summary>
+ void _TaskInitNoFunctor(ABI::Windows::Foundation::IAsyncAction* _AsyncAction)
+ {
+ _M_unitTask._TaskInitAsyncOp<details::_Unit_type>(Microsoft::WRL::Make<details::_IAsyncActionToAsyncOperationConverter>(_AsyncAction).Get());
+ }
+
+ /// <summary>
+ /// Initializes a task using an asynchronous action with progress IAsyncActionWithProgress<_P>*
+ /// </summary>
+ template<typename _P>
+ void _TaskInitNoFunctor(ABI::Windows::Foundation::IAsyncActionWithProgress<_P>* _AsyncActionWithProgress)
+ {
+ _M_unitTask._TaskInitAsyncOp<details::_Unit_type>(Microsoft::WRL::Make<details::_IAsyncActionWithProgressToAsyncOperationConverter<_P>>(_AsyncActionWithProgress).Get());
+ }
+ /// <summary>
+ /// Initializes a task using a callable object.
+ /// </summary>
+ template<typename _Function>
+ void _TaskInitMaybeFunctor(_Function & _Func, std::true_type)
+ {
+ _M_unitTask._TaskInitWithFunctor<void, _Function>(_Func);
+ }
+
+ /// <summary>
+ /// Initializes a task using a non-callable object.
+ /// </summary>
+ template<typename _T>
+ 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<details::_Unit_type> _M_unitTask;
+};
+
+namespace details
+{
+
+ /// <summary>
+ /// The following type traits are used for the create_task function.
+ /// </summary>
+
+ // Unwrap task<T>
+ template<typename _Ty>
+ _Ty _GetUnwrappedType(task<_Ty>);
+
+ // Unwrap all supported types
+ template<typename _Ty>
+ auto _GetUnwrappedReturnType(_Ty _Arg, int) -> decltype(_GetUnwrappedType(_Arg));
+ // fallback
+ template<typename _Ty>
+ _Ty _GetUnwrappedReturnType(_Ty, ...);
+
+ /// <summary>
+ /// <c>_GetTaskType</c> functions will retrieve task type <c>T</c> in <c>task[T](Arg)</c>,
+ /// for given constructor argument <c>Arg</c> and its property "callable".
+ /// It will automatically unwrap argument to get the final return type if necessary.
+ /// </summary>
+
+ // Non-Callable
+ template<typename _Ty>
+ _Ty _GetTaskType(task_completion_event<_Ty>, std::false_type);
+
+ // Non-Callable
+ template<typename _Ty>
+ auto _GetTaskType(_Ty _NonFunc, std::false_type) -> decltype(_GetUnwrappedType(_NonFunc));
+
+ // Callable
+ template<typename _Ty>
+ auto _GetTaskType(_Ty _Func, std::true_type) -> decltype(_GetUnwrappedReturnType(stdx::declval<_FunctionTypeTraits<_Ty, void>::_FuncRetType>(), 0));
+
+ // Special callable returns void
+ void _GetTaskType(std::function<HRESULT()>, std::true_type);
+ struct _BadArgType{};
+
+ template<typename _ReturnType, typename _Ty>
+ auto _FilterValidTaskType(_Ty _Param, int) -> decltype(_GetTaskType(_Param, _IsCallable<_ReturnType>(_Param, 0, 0, 0)));
+
+ template<typename _ReturnType, typename _Ty>
+ _BadArgType _FilterValidTaskType(_Ty _Param, ...);
+
+ template<typename _ReturnType, typename _Ty>
+ struct _TaskTypeFromParam
+ {
+ typedef decltype(_FilterValidTaskType<_ReturnType>(stdx::declval<_Ty>(), 0)) _Type;
+ };
+}
+
+
+/// <summary>
+/// Creates a PPL <see cref="task Class">task</c> object. <c>create_task</c> can be used anywhere you would have used a task constructor.
+/// It is provided mainly for convenience, because it allows use of the <c>auto</c> keyword while creating tasks.
+/// </summary>
+/// <typeparam name="_Ty">
+/// The type of the parameter from which the task is to be constructed.
+/// </typeparam>
+/// <param name="_Param">
+/// The parameter from which the task is to be constructed. This could be a lambda or function object, a <c>task_completion_event</c>
+/// object, a different <c>task</c> object, or a Windows::Foundation::IAsyncInfo interface if you are using tasks in your Windows Store app.
+/// </param>
+/// <returns>
+/// A new task of type <c>T</c>, that is inferred from <paramref name="_Param"/>.
+/// </returns>
+/// <remarks>
+/// The first overload behaves like a task constructor that takes a single parameter.
+/// <para>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 <c>task</c> object as the first parameter.</para>
+/// <para>The type of the returned task is inferred from the first parameter to the function. If <paramref name="_Param"/> is a <c>task_completion_event<T></c>,
+/// a <c>task<T></c>, or a functor that returns either type <c>T</c> or <c>task<T></c>, the type of the created task is <c>task<T></c>.
+/// <para>In a Windows Store app, if <paramref name="_Param"/> 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 <c>task<T></c>.
+/// If <paramref name="_Param"/> 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 <c>task<void></c>.</para>
+/// </remarks>
+/// <seealso cref="task Class"/>
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
+/**/
+template<typename _ReturnType, typename _Ty>
+__declspec(noinline)
+#if _MSC_VER >= 1800
+auto create_task(_Ty _Param, task_options _TaskOptions = task_options()) -> task<typename details::_TaskTypeFromParam<_ReturnType, _Ty>::_Type>
+#else
+auto create_task(_Ty _Param) -> task<typename details::_TaskTypeFromParam<_ReturnType, _Ty>::_Type>
+#endif
+{
+ static_assert(!std::is_same<typename details::_TaskTypeFromParam<_ReturnType, _Ty>::_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<typename details::_TaskTypeFromParam<_ReturnType, _Ty>::_Type> _CreatedTask(_Param, _TaskOptions);
+#else
+ task<typename details::_TaskTypeFromParam<_ReturnType, _Ty>::_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;
+}
+
+/// <summary>
+/// Creates a PPL <see cref="task Class">task</c> object. <c>create_task</c> can be used anywhere you would have used a task constructor.
+/// It is provided mainly for convenience, because it allows use of the <c>auto</c> keyword while creating tasks.
+/// </summary>
+/// <typeparam name="_Ty">
+/// The type of the parameter from which the task is to be constructed.
+/// </typeparam>
+/// <param name="_Param">
+/// The parameter from which the task is to be constructed. This could be a lambda or function object, a <c>task_completion_event</c>
+/// object, a different <c>task</c> object, or a Windows::Foundation::IAsyncInfo interface if you are using tasks in your Windows Store app.
+/// </param>
+/// <param name="_Token">
+/// The cancellation token to associate with the task. When the source for this token is canceled, cancellation will be requested on the task.
+/// </param>
+/// <returns>
+/// A new task of type <c>T</c>, that is inferred from <paramref name="_Param"/>.
+/// </returns>
+/// <remarks>
+/// The first overload behaves like a task constructor that takes a single parameter.
+/// <para>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 <c>task</c> object as the first parameter.</para>
+/// <para>The type of the returned task is inferred from the first parameter to the function. If <paramref name="_Param"/> is a <c>task_completion_event<T></c>,
+/// a <c>task<T></c>, or a functor that returns either type <c>T</c> or <c>task<T></c>, the type of the created task is <c>task<T></c>.
+/// <para>In a Windows Store app, if <paramref name="_Param"/> 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 <c>task<T></c>.
+/// If <paramref name="_Param"/> 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 <c>task<void></c>.</para>
+/// </remarks>
+/// <seealso cref="task Class"/>
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
+/**/
+#if _MSC_VER >= 1800
+template<typename _ReturnType>
+__declspec(noinline)
+task<_ReturnType> create_task(const task<_ReturnType>& _Task)
+{
+ task<_ReturnType> _CreatedTask(_Task);
+ return _CreatedTask;
+}
+#else
+template<typename _ReturnType, typename _Ty>
+__declspec(noinline)
+auto create_task(_Ty _Param, Concurrency::cancellation_token _Token) -> task<typename details::_TaskTypeFromParam<_ReturnType, _Ty>::_Type>
+{
+ static_assert(!std::is_same<typename details::_TaskTypeFromParam<_ReturnType, _Ty>::_Type, details::_BadArgType>::value,
+ "incorrect argument for create_task; can be a callable object, an asynchronous operation, or a task_completion_event"
+ );
+ task<typename details::_TaskTypeFromParam<_ReturnType, _Ty>::_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<typename _T>
+ task<typename ABI::Windows::Foundation::Internal::GetAbiType<decltype(_UnwrapAsyncOperationSelector(stdx::declval<ABI::Windows::Foundation::IAsyncOperation<_T>*>()))>::type> _To_task_helper(ABI::Windows::Foundation::IAsyncOperation<_T>* op)
+ {
+ return task<_T>(op);
+ }
+
+ template<typename _T, typename _Progress>
+ task<typename ABI::Windows::Foundation::Internal::GetAbiType<decltype(_UnwrapAsyncOperationWithProgressSelector(stdx::declval<ABI::Windows::Foundation::IAsyncOperationWithProgress<_T, _Progress>*>()))>::type> _To_task_helper(ABI::Windows::Foundation::IAsyncOperationWithProgress<_T, _Progress>* op)
+ {
+ return task<_T>(op);
+ }
+
+ inline task<void> _To_task_helper(ABI::Windows::Foundation::IAsyncAction* op)
+ {
+ return task<void>(op);
+ }
+
+ template<typename _Progress>
+ task<void> _To_task_helper(ABI::Windows::Foundation::IAsyncActionWithProgress<_Progress>* op)
+ {
+ return task<void>(op);
+ }
+
+ template<typename _ProgressType>
+ class _ProgressDispatcherBase
+ {
+ public:
+
+ virtual ~_ProgressDispatcherBase()
+ {
+ }
+
+ virtual void _Report(const _ProgressType& _Val) = 0;
+ };
+
+ template<typename _ProgressType, typename _ClassPtrType>
+ 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
+
+
+/// <summary>
+/// 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.
+/// </summary>
+/// <typeparam name="_ProgressType">
+/// The payload type of each progress notification reported through the progress reporter.
+/// </typeparam>
+/// <remarks>
+/// This type is only available to Windows Store apps.
+/// </remarks>
+/// <seealso cref="create_async Function"/>
+/**/
+template<typename _ProgressType>
+class progress_reporter
+{
+ typedef std::shared_ptr<details::_ProgressDispatcherBase<_ProgressType>> _PtrType;
+
+public:
+
+ /// <summary>
+ /// Sends a progress report to the asynchronous action or operation to which this progress reporter is bound.
+ /// </summary>
+ /// <param name="_Val">
+ /// The payload to report through a progress notification.
+ /// </param>
+ /**/
+ void report(const _ProgressType& _Val) const
+ {
+ _M_dispatcher->_Report(_Val);
+ }
+
+ template<typename _ClassPtrType>
+ 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<typename _T>
+ struct _ProgressTypeTraits
+ {
+ static const bool _TakesProgress = false;
+ typedef void _ProgressType;
+ };
+
+ template<typename _T>
+ struct _ProgressTypeTraits<progress_reporter<_T>>
+ {
+ static const bool _TakesProgress = true;
+ typedef typename _T _ProgressType;
+ };
+
+ template<typename _T, bool bTakesToken = std::is_same<_T, Concurrency::cancellation_token>::value, bool bTakesProgress = _ProgressTypeTraits<_T>::_TakesProgress>
+ struct _TokenTypeTraits
+ {
+ static const bool _TakesToken = false;
+ typedef typename _T _ReturnType;
+ };
+
+ template<typename _T>
+ struct _TokenTypeTraits<_T, false, true>
+ {
+ static const bool _TakesToken = false;
+ typedef void _ReturnType;
+ };
+
+ template<typename _T>
+ struct _TokenTypeTraits<_T, true, false>
+ {
+ static const bool _TakesToken = true;
+ typedef void _ReturnType;
+ };
+
+ template<typename _T, size_t count = _FunctorTypeTraits<_T>::_ArgumentCount>
+ struct _CAFunctorOptions
+ {
+ static const bool _TakesProgress = false;
+ static const bool _TakesToken = false;
+ typedef void _ProgressType;
+ typedef void _ReturnType;
+ };
+
+ template<typename _T>
+ 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<typename _T>
+ 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<typename _T>
+ 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<typename _AsyncSelector, typename _ReturnType>
+ struct _SelectorTaskGenerator
+ {
+#if _MSC_VER >= 1800
+ template<typename _Function>
+ 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<typename _Function>
+ 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<typename _Function, typename _ProgressObject>
+ 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<typename _Function, typename _ProgressObject>
+ 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<typename _Function>
+ static task<_ReturnType> _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts, _ReturnType* _pRet)
+ {
+ return task<_ReturnType>(_Func(_pRet), _Cts.get_token());
+ }
+
+ template<typename _Function>
+ 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<typename _Function, typename _ProgressObject>
+ 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<typename _Function, typename _ProgressObject>
+ 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<typename _AsyncSelector>
+ struct _SelectorTaskGenerator<_AsyncSelector, void>
+ {
+#if _MSC_VER >= 1800
+ template<typename _Function>
+ static task<void> _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<void>(_Func(), _taskOptinos);
+ }
+
+ template<typename _Function>
+ static task<void> _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<void>(_Func(_Cts.get_token()), _taskOptinos);
+ }
+
+ template<typename _Function, typename _ProgressObject>
+ static task<void> _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<void>(_Func(_Progress), _taskOptinos);
+ }
+
+ template<typename _Function, typename _ProgressObject>
+ static task<void> _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<void>(_Func(_Progress, _Cts.get_token()), _taskOptinos);
+ }
+#else
+ template<typename _Function>
+ static task<void> _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts)
+ {
+ return task<void>(_Func(), _Cts.get_token());
+ }
+
+ template<typename _Function>
+ static task<void> _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts)
+ {
+ return task<void>(_Func(_Cts.get_token()), _Cts.get_token());
+ }
+
+ template<typename _Function, typename _ProgressObject>
+ static task<void> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts)
+ {
+ return task<void>(_Func(_Progress), _Cts.get_token());
+ }
+
+ template<typename _Function, typename _ProgressObject>
+ static task<void> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts)
+ {
+ return task<void>(_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<typename _ReturnType>
+ struct _SelectorTaskGenerator<details::_TypeSelectorNoAsync, _ReturnType>
+ {
+#if _MSC_VER >= 1800
+
+#pragma warning(push)
+#pragma warning(disable: 4702)
+ template<typename _Function>
+ 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<typename _Function>
+ 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<typename _Function, typename _ProgressObject>
+ 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<typename _Function, typename _ProgressObject>
+ 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<typename _Function>
+ 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<typename _Function>
+ 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<typename _Function, typename _ProgressObject>
+ 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<typename _Function, typename _ProgressObject>
+ 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<details::_TypeSelectorNoAsync, void>
+ {
+#if _MSC_VER >= 1800
+ template<typename _Function>
+ static task<void> _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<void>([=]() -> HRESULT {
+ Concurrency::details::_Task_generator_oversubscriber_t _Oversubscriber;
+ (_Oversubscriber);
+ return _Func();
+ }, _taskOptinos);
+ }
+
+ template<typename _Function>
+ static task<void> _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<void>([=]() -> HRESULT {
+ Concurrency::details::_Task_generator_oversubscriber_t _Oversubscriber;
+ (_Oversubscriber);
+ return _Func(_Cts.get_token());
+ }, _taskOptinos);
+ }
+
+ template<typename _Function, typename _ProgressObject>
+ static task<void> _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<void>([=]() -> HRESULT {
+ Concurrency::details::_Task_generator_oversubscriber_t _Oversubscriber;
+ (_Oversubscriber);
+ return _Func(_Progress);
+ }, _taskOptinos);
+ }
+
+ template<typename _Function, typename _ProgressObject>
+ static task<void> _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<void>([=]() -> HRESULT {
+ Concurrency::details::_Task_generator_oversubscriber_t _Oversubscriber;
+ (_Oversubscriber);
+ return _Func(_Progress, _Cts.get_token());
+ }, _taskOptinos);
+ }
+#else
+ template<typename _Function>
+ static task<void> _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts)
+ {
+ return task<void>([=]() -> HRESULT {
+ _Task_generator_oversubscriber _Oversubscriber;
+ return _Func();
+ }, _Cts.get_token());
+ }
+
+ template<typename _Function>
+ static task<void> _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts)
+ {
+ return task<void>([=]() -> HRESULT {
+ _Task_generator_oversubscriber _Oversubscriber;
+ return _Func(_Cts.get_token());
+ }, _Cts.get_token());
+ }
+
+ template<typename _Function, typename _ProgressObject>
+ static task<void> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts)
+ {
+ return task<void>([=]() -> HRESULT {
+ _Task_generator_oversubscriber _Oversubscriber;
+ return _Func(_Progress);
+ }, _Cts.get_token());
+ }
+
+ template<typename _Function, typename _ProgressObject>
+ static task<void> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts)
+ {
+ return task<void>([=]() -> 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<typename _ReturnType>
+ struct _SelectorTaskGenerator<details::_TypeSelectorAsyncTask, _ReturnType>
+ {
+ template<typename _Function>
+#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<typename _Function>
+#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<typename _Function, typename _ProgressObject>
+#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<typename _Function, typename _ProgressObject>
+#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<details::_TypeSelectorAsyncTask, void>
+ {
+ template<typename _Function>
+#if _MSC_VER >= 1800
+ static task<void> _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack)
+#else
+ static task<void> _GenerateTask_0(const _Function& _Func, Concurrency::cancellation_token_source _Cts)
+#endif
+ {
+ task<void> _task;
+ _Func(&_task);
+ return _task;
+ }
+
+ template<typename _Function>
+#if _MSC_VER >= 1800
+ static task<void> _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack)
+#else
+ static task<void> _GenerateTask_1C(const _Function& _Func, Concurrency::cancellation_token_source _Cts)
+#endif
+ {
+ task<void> _task;
+ _Func(_Cts.get_token(), &_task);
+ return _task;
+ }
+
+ template<typename _Function, typename _ProgressObject>
+#if _MSC_VER >= 1800
+ static task<void> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack)
+#else
+ static task<void> _GenerateTask_1P(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts)
+#endif
+ {
+ task<void> _task;
+ _Func(_Progress, &_task);
+ return _task;
+ }
+
+ template<typename _Function, typename _ProgressObject>
+#if _MSC_VER >= 1800
+ static task<void> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts, const _TaskCreationCallstack & _callstack)
+#else
+ static task<void> _GenerateTask_2PC(const _Function& _Func, const _ProgressObject& _Progress, Concurrency::cancellation_token_source _Cts)
+#endif
+ {
+ task<void> _task;
+ _Func(_Progress, _Cts.get_token(), &_task);
+ return _task;
+ }
+ };
+
+ template<typename _Generator, bool _TakesToken, bool TakesProgress>
+ struct _TaskGenerator
+ {
+ };
+
+ template<typename _Generator>
+ struct _TaskGenerator<_Generator, false, false>
+ {
+#if _MSC_VER >= 1800
+ template<typename _Function, typename _ClassPtr, typename _ProgressType>
+ 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<typename _Function, typename _ClassPtr, typename _ProgressType, typename _ReturnType>
+ 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<typename _Function, typename _ClassPtr, typename _ProgressType>
+ 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<typename _Function, typename _ClassPtr, typename _ProgressType, typename _ReturnType>
+ 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<typename _Generator>
+ struct _TaskGenerator<_Generator, true, false>
+ {
+#if _MSC_VER >= 1800
+ template<typename _Function, typename _ClassPtr, typename _ProgressType>
+ 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<typename _Function, typename _ClassPtr, typename _ProgressType, typename _ReturnType>
+ 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<typename _Function, typename _ClassPtr, typename _ProgressType>
+ 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<typename _Function, typename _ClassPtr, typename _ProgressType, typename _ReturnType>
+ 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<typename _Generator>
+ struct _TaskGenerator<_Generator, false, true>
+ {
+#if _MSC_VER >= 1800
+ template<typename _Function, typename _ClassPtr, typename _ProgressType>
+ 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<typename _Function, typename _ClassPtr, typename _ProgressType, typename _ReturnType>
+ 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<typename _Function, typename _ClassPtr, typename _ProgressType>
+ 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<typename _Function, typename _ClassPtr, typename _ProgressType, typename _ReturnType>
+ 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<typename _Generator>
+ struct _TaskGenerator<_Generator, true, true>
+ {
+#if _MSC_VER >= 1800
+ template<typename _Function, typename _ClassPtr, typename _ProgressType>
+ 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<typename _Function, typename _ClassPtr, typename _ProgressType, typename _ReturnType>
+ 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<typename _Function, typename _ClassPtr, typename _ProgressType>
+ 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<typename _Function, typename _ClassPtr, typename _ProgressType, typename _ReturnType>
+ 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<typename _Function, typename _ProgressType, typename _ReturnType, typename _TaskTraits, bool _TakesToken, bool _TakesProgress>
+ struct _AsyncAttributes
+ {
+ };
+
+ template<typename _Function, typename _ProgressType, typename _ReturnType, typename _TaskTraits, bool _TakesToken>
+ 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<decltype(_UnwrapAsyncOperationWithProgressSelector(stdx::declval<_AsyncBaseType*>()))>::type _ReturnType_abi;
+ typedef typename _ProgressType _ProgressType;
+ typedef typename ABI::Windows::Foundation::Internal::GetAbiType<decltype(_UnwrapAsyncOperationWithProgressProgressSelector(stdx::declval<_AsyncBaseType*>()))>::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<typename _Function, typename _ClassPtr>
+#if _MSC_VER >= 1800
+ static task<typename _TaskTraits::_TaskRetType> _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<typename _TaskTraits::_TaskRetType> _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<typename _Function, typename _ProgressType, typename _ReturnType, typename _TaskTraits, bool _TakesToken>
+ 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<decltype(_UnwrapAsyncOperationSelector(stdx::declval<_AsyncBaseType*>()))>::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<typename _Function, typename _ClassPtr>
+#if _MSC_VER >= 1800
+ static task<typename _TaskTraits::_TaskRetType> _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<typename _TaskTraits::_TaskRetType> _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<typename _Function, typename _ProgressType, typename _TaskTraits, bool _TakesToken>
+ 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<decltype(_UnwrapAsyncActionWithProgressSelector(stdx::declval<_AsyncBaseType*>()))>::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<typename _Function, typename _ClassPtr>
+ static task<void> _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<typename _Function, typename _ClassPtr>
+ static task<task<void>> _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<typename _Function, typename _ClassPtr>
+ static task<void> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts)
+ {
+ return _TaskGenerator::_GenerateTaskNoRet<_Function, _ClassPtr, _ProgressType_abi>(_Func, _Ptr, _Cts);
+ }
+ template<typename _Function, typename _ClassPtr>
+ static task<task<void>> _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<typename _Function, typename _ProgressType, typename _TaskTraits, bool _TakesToken>
+ 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<typename _Function, typename _ClassPtr>
+ static task<void> _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<typename _Function, typename _ClassPtr>
+ static task<task<void>> _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<typename _Function, typename _ClassPtr>
+ static task<void> _Generate_Task(const _Function& _Func, _ClassPtr _Ptr, Concurrency::cancellation_token_source _Cts)
+ {
+ return _TaskGenerator::_GenerateTaskNoRet<_Function, _ClassPtr, _ProgressType>(_Func, _Ptr, _Cts);
+ }
+ template<typename _Function, typename _ClassPtr>
+ static task<task<void>> _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<typename _Function>
+ struct _AsyncLambdaTypeTraits
+ {
+ typedef typename _Unhat<typename _CAFunctorOptions<_Function>::_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<typename _Attributes::_AsyncBaseType, ABI::Windows::Foundation::IAsyncInfo>>
+ {
+ 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<ABI::Windows::Foundation::AsyncStatus>(_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<volatile LONG*>(&_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<volatile LONG*>(&_M_currentStatus),
+ _NewState,
+ static_cast<LONG>(_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<typename _Attributes::_CompletionDelegateType> _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<typename _Attributes, _AsyncResultType _ResultType = SingleResult>
+ class _AsyncBaseProgressLayer abstract : public _AsyncProgressBase<_Attributes, _Attributes::_TakesProgress, _ResultType>
+ {
+ };
+
+ // ***************************************************************************
+ // Task Adaptation Layer:
+ //
+
+ //
+ // _AsyncTaskThunkBase provides a bridge between IAsync<Action/Operation> and task.
+ //
+ template<typename _Attributes, typename _ReturnType>
+ 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<typename _Attributes, typename _ReturnType, typename _Return>
+ 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 <typename _Function>
+#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<typename _Attributes, typename _ReturnType>
+ class _AsyncTaskReturn<_Attributes, _ReturnType, void> abstract : public _AsyncTaskThunkBase<_Attributes, void>
+ {
+ public:
+ template <typename _Function>
+#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<typename _Attributes>
+ class _AsyncTaskReturn<_Attributes, void, task<void>> abstract : public _AsyncTaskThunkBase<_Attributes, task<void>>
+ {
+ public:
+ template <typename _Function>
+#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<void> _M_results;
+ };
+
+ template<typename _Attributes>
+ 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<typename _Function>
+ class _AsyncTaskGeneratorThunk : public _AsyncTaskThunk<typename _AsyncLambdaTypeTraits<_Function>::_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
+
+/// <summary>
+/// Creates a Windows Runtime asynchronous construct based on a user supplied lambda or function object. The return type of <c>create_async</c> is
+/// one of either <c>IAsyncAction^</c>, <c>IAsyncActionWithProgress<TProgress>^</c>, <c>IAsyncOperation<TResult>^</c>, or
+/// <c>IAsyncOperationWithProgress<TResult, TProgress>^</c> based on the signature of the lambda passed to the method.
+/// </summary>
+/// <param name="_Func">
+/// The lambda or function object from which to create a Windows Runtime asynchronous construct.
+/// </param>
+/// <returns>
+/// 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.
+/// </returns>
+/// <remarks>
+/// The return type of the lambda determines whether the construct is an action or an operation.
+/// <para>Lambdas that return void cause the creation of actions. Lambdas that return a result of type <c>TResult</c> cause the creation of
+/// operations of TResult.</para>
+/// <para>The lambda may also return a <c>task<TResult></c> 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 <c>create_async</c>.
+/// 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.</para>
+/// <para>The lambda may take either zero, one or two arguments. The valid arguments are <c>progress_reporter<TProgress></c> and
+/// <c>cancellation_token</c>, 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 <c>create_async</c> to return an asynchronous
+/// construct which reports progress of type TProgress each time the <c>report</c> 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.</para>
+/// <para>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 <c>IAsyncInfo::Cancel</c> method will
+/// cause cancellation of the implicit task.</para>
+/// <para>If the body of the lambda returns a task, the lamba executes inline, and by declaring the lambda to take an argument of type
+/// <c>cancellation_token</c> 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 <c>register_callback</c> method on the token to cause the Runtime to invoke a callback when you call <c>IAsyncInfo::Cancel</c> on
+/// the async operation or action produced..</para>
+/// <para>This function is only available to Windows Store apps.</para>
+/// </remarks>
+/// <seealso cref="task Class"/>
+/// <seealso cref="progress_reporter Class"/>
+/// <seealso cref="cancelation_token Class"/>
+/**/
+template<typename _ReturnType, typename _Function>
+__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<decltype(details::_IsValidCreateAsync<_ReturnType>(_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<details::_AsyncTaskGeneratorThunk<_Function>> _AsyncInfo = Microsoft::WRL::Make<details::_AsyncTaskGeneratorThunk<_Function>>(_Func, _CAPTURE_CALLSTACK());
+#else
+ Microsoft::WRL::ComPtr<details::_AsyncTaskGeneratorThunk<_Function>> _AsyncInfo = Microsoft::WRL::Make<details::_AsyncTaskGeneratorThunk<_Function>>(_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<typename _Type>
+ 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<std::vector<_Type> > _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<typename _Type>
+ struct _RunAllParam<std::vector<_Type> >
+ {
+ _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<std::vector<_Type> > > _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<void>
+#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<typename _ElementType, typename _Function, typename _TaskType>
+ 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<typename _ElementType, typename _Iterator>
+ struct _WhenAllImpl
+ {
+#if _MSC_VER >= 1800
+ static task<std::vector<_ElementType>> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End)
+#else
+ static task<std::vector<_ElementType>> _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<size_t>(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<typename _ElementType, typename _Iterator>
+ struct _WhenAllImpl<std::vector<_ElementType>, _Iterator>
+ {
+#if _MSC_VER >= 1800
+ static task<std::vector<_ElementType>> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End)
+#else
+ static task<std::vector<_ElementType>> _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<std::vector<_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 {
+ _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<size_t>(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<std::vector<_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[_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<typename _Iterator>
+ struct _WhenAllImpl<void, _Iterator>
+ {
+#if _MSC_VER >= 1800
+ static task<void> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End)
+#else
+ static task<void> _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<size_t>(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<void> _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<typename _ReturnType>
+ task<std::vector<_ReturnType>> _WhenAllVectorAndValue(const task<std::vector<_ReturnType>>& _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<std::vector<_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_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
+/// <summary>
+/// Creates a task that will complete successfully when all of the tasks supplied as arguments complete successfully.
+/// </summary>
+/// <typeparam name="_Iterator">
+/// The type of the input iterator.
+/// </typeparam>
+/// <param name="_Begin">
+/// The position of the first element in the range of elements to be combined into the resulting task.
+/// </param>
+/// <param name="_End">
+/// The position of the first element beyond the range of elements to be combined into the resulting task.
+/// </param>
+/// <returns>
+/// A task that completes sucessfully when all of the input tasks have completed successfully. If the input tasks are of type <c>T</c>,
+/// the output of this function will be a <c>task<std::vector<T>></c>. If the input tasks are of type <c>void</c> the output
+/// task will also be a <c>task<void></c>.
+/// </returns>
+/// <remarks>
+/// 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 <c>get()</c> or <c>wait()</c> on that task.
+/// </remarks>
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
+/**/
+template <typename _Iterator>
+auto when_all(_Iterator _Begin, _Iterator _End)
+-> decltype (details::_WhenAllImpl<typename std::iterator_traits<_Iterator>::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
+
+/// <summary>
+/// Creates a task that will complete successfully when all of the tasks supplied as arguments complete successfully.
+/// </summary>
+/// <typeparam name="_Iterator">
+/// The type of the input iterator.
+/// </typeparam>
+/// <param name="_Begin">
+/// The position of the first element in the range of elements to be combined into the resulting task.
+/// </param>
+/// <param name="_End">
+/// The position of the first element beyond the range of elements to be combined into the resulting task.
+/// </param>
+/// <param name="_CancellationToken">
+/// 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
+/// <c>cancellation_token::none()</c>of the tasks supplied.
+/// </param>
+/// <returns>
+/// A task that completes sucessfully when all of the input tasks have completed successfully. If the input tasks are of type <c>T</c>,
+/// the output of this function will be a <c>task<std::vector<T>></c>. If the input tasks are of type <c>void</c> the output
+/// task will also be a <c>task<void></c>.
+/// </returns>
+/// <remarks>
+/// 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 <c>get()</c> or <c>wait()</c> on that task.
+/// </remarks>
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
+/**/
+template <typename _Iterator>
+#if _MSC_VER >= 1800
+auto when_all(_Iterator _Begin, _Iterator _End, const task_options& _TaskOptions = task_options())
+-> decltype (details::_WhenAllImpl<typename std::iterator_traits<_Iterator>::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<typename std::iterator_traits<_Iterator>::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
+
+/// <summary>
+/// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully.
+/// </summary>
+/// <typeparam name="_ReturnType">
+/// The type of the returned task.
+/// </typeparam>
+/// <param name="_Lhs">
+/// The first task to combine into the resulting task.
+/// </param>
+/// <param name="_Rhs">
+/// The second task to combine into the resulting task.
+/// </param>
+/// <returns>
+/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type <c>T</c>,
+/// the output of this function will be a <c>task<std::vector<T>></c>. If the input tasks are of type <c>void</c> the output
+/// task will also be a <c>task<void></c>.
+/// <para> To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator
+/// produces a <c>task<std::vector<T>></c> if either one or both of the tasks are of type <c>task<std::vector<T>></c>.</para>
+/// </returns>
+/// <remarks>
+/// 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 <c>get()</c> or <c>wait()</c> on that task.
+/// </remarks>
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
+/**/
+template<typename _ReturnType>
+task<std::vector<_ReturnType>> operator&&(const task<_ReturnType> & _Lhs, const task<_ReturnType> & _Rhs)
+{
+ task<_ReturnType> _PTasks[2] = { _Lhs, _Rhs };
+ return when_all(_PTasks, _PTasks + 2);
+}
+
+/// <summary>
+/// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully.
+/// </summary>
+/// <typeparam name="_ReturnType">
+/// The type of the returned task.
+/// </typeparam>
+/// <param name="_Lhs">
+/// The first task to combine into the resulting task.
+/// </param>
+/// <param name="_Rhs">
+/// The second task to combine into the resulting task.
+/// </param>
+/// <returns>
+/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type <c>T</c>,
+/// the output of this function will be a <c>task<std::vector<T>></c>. If the input tasks are of type <c>void</c> the output
+/// task will also be a <c>task<void></c>.
+/// <para> To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator
+/// produces a <c>task<std::vector<T>></c> if either one or both of the tasks are of type <c>task<std::vector<T>></c>.</para>
+/// </returns>
+/// <remarks>
+/// 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 <c>get()</c> or <c>wait()</c> on that task.
+/// </remarks>
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
+/**/
+template<typename _ReturnType>
+task<std::vector<_ReturnType>> operator&&(const task<std::vector<_ReturnType>> & _Lhs, const task<_ReturnType> & _Rhs)
+{
+ return details::_WhenAllVectorAndValue(_Lhs, _Rhs, true);
+}
+
+/// <summary>
+/// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully.
+/// </summary>
+/// <typeparam name="_ReturnType">
+/// The type of the returned task.
+/// </typeparam>
+/// <param name="_Lhs">
+/// The first task to combine into the resulting task.
+/// </param>
+/// <param name="_Rhs">
+/// The second task to combine into the resulting task.
+/// </param>
+/// <returns>
+/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type <c>T</c>,
+/// the output of this function will be a <c>task<std::vector<T>></c>. If the input tasks are of type <c>void</c> the output
+/// task will also be a <c>task<void></c>.
+/// <para> To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator
+/// produces a <c>task<std::vector<T>></c> if either one or both of the tasks are of type <c>task<std::vector<T>></c>.</para>
+/// </returns>
+/// <remarks>
+/// 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 <c>get()</c> or <c>wait()</c> on that task.
+/// </remarks>
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
+/**/
+template<typename _ReturnType>
+task<std::vector<_ReturnType>> operator&&(const task<_ReturnType> & _Lhs, const task<std::vector<_ReturnType>> & _Rhs)
+{
+ return details::_WhenAllVectorAndValue(_Rhs, _Lhs, false);
+}
+
+/// <summary>
+/// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully.
+/// </summary>
+/// <typeparam name="_ReturnType">
+/// The type of the returned task.
+/// </typeparam>
+/// <param name="_Lhs">
+/// The first task to combine into the resulting task.
+/// </param>
+/// <param name="_Rhs">
+/// The second task to combine into the resulting task.
+/// </param>
+/// <returns>
+/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type <c>T</c>,
+/// the output of this function will be a <c>task<std::vector<T>></c>. If the input tasks are of type <c>void</c> the output
+/// task will also be a <c>task<void></c>.
+/// <para> To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator
+/// produces a <c>task<std::vector<T>></c> if either one or both of the tasks are of type <c>task<std::vector<T>></c>.</para>
+/// </returns>
+/// <remarks>
+/// 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 <c>get()</c> or <c>wait()</c> on that task.
+/// </remarks>
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
+/**/
+template<typename _ReturnType>
+task<std::vector<_ReturnType>> operator&&(const task<std::vector<_ReturnType>> & _Lhs, const task<std::vector<_ReturnType>> & _Rhs)
+{
+ task<std::vector<_ReturnType>> _PTasks[2] = { _Lhs, _Rhs };
+ return when_all(_PTasks, _PTasks + 2);
+}
+
+/// <summary>
+/// Creates a task that will complete succesfully when both of the tasks supplied as arguments complete successfully.
+/// </summary>
+/// <typeparam name="_ReturnType">
+/// The type of the returned task.
+/// </typeparam>
+/// <param name="_Lhs">
+/// The first task to combine into the resulting task.
+/// </param>
+/// <param name="_Rhs">
+/// The second task to combine into the resulting task.
+/// </param>
+/// <returns>
+/// A task that completes successfully when both of the input tasks have completed successfully. If the input tasks are of type <c>T</c>,
+/// the output of this function will be a <c>task<std::vector<T>></c>. If the input tasks are of type <c>void</c> the output
+/// task will also be a <c>task<void></c>.
+/// <para> To allow for a construct of the sort taskA && taskB && taskC, which are combined in pairs, the && operator
+/// produces a <c>task<std::vector<T>></c> if either one or both of the tasks are of type <c>task<std::vector<T>></c>.</para>
+/// </returns>
+/// <remarks>
+/// 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 <c>get()</c> or <c>wait()</c> on that task.
+/// </remarks>
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
+/**/
+inline task<void> operator&&(const task<void> & _Lhs, const task<void> & _Rhs)
+{
+ task<void> _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 <typename _CompletionType>
+ 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<typename _CompletionType, typename _Function, typename _TaskType>
+ 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<typename _ElementType, typename _Iterator>
+ struct _WhenAnyImpl
+ {
+#if _MSC_VER >= 1800
+ static task<std::pair<_ElementType, size_t>> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End)
+#else
+ static task<std::pair<_ElementType, size_t>> _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<std::pair<std::pair<_ElementType, size_t>, 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<std::pair<std::pair<_ElementType, size_t>, Concurrency::details::_CancellationTokenState *>> _Any_tasks_completed(_PParam->_M_Completed, _Options);
+#else
+ task<std::pair<std::pair<_ElementType, size_t>, 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<size_t>(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<std::pair<_ElementType, size_t>, 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<typename _Iterator>
+ struct _WhenAnyImpl<void, _Iterator>
+ {
+#if _MSC_VER >= 1800
+ static task<size_t> _Perform(const task_options& _TaskOptions, _Iterator _Begin, _Iterator _End)
+#else
+ static task<size_t> _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<std::pair<size_t, 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<std::pair<size_t, _CancellationTokenState *>> _Any_tasks_completed(_PParam->_M_Completed, _Options);
+#else
+ task<std::pair<size_t, Concurrency::details::_CancellationTokenState *>> _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<size_t>(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<void> _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<size_t, Concurrency::details::_CancellationTokenState *> _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
+
+/// <summary>
+/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully.
+/// </summary>
+/// <typeparam name="_Iterator">
+/// The type of the input iterator.
+/// </typeparam>
+/// <param name="_Begin">
+/// The position of the first element in the range of elements to be combined into the resulting task.
+/// </param>
+/// <param name="_End">
+/// The position of the first element beyond the range of elements to be combined into the resulting task.
+/// </param>
+/// <returns>
+/// A task that completes successfully when any one of the input tasks has completed successfully. If the input tasks are of type <c>T</c>,
+/// the output of this function will be a <c>task<std::pair<T, size_t>>></c>, 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 <c>void</c>
+/// the output is a <c>task<size_t></c>, where the result is the index of the completing task.
+/// </returns>
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
+/**/
+template<typename _Iterator>
+#if _MSC_VER >= 1800
+auto when_any(_Iterator _Begin, _Iterator _End, const task_options& _TaskOptions = task_options())
+-> decltype (details::_WhenAnyImpl<typename std::iterator_traits<_Iterator>::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<typename std::iterator_traits<_Iterator>::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
+
+/// <summary>
+/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully.
+/// </summary>
+/// <typeparam name="_Iterator">
+/// The type of the input iterator.
+/// </typeparam>
+/// <param name="_Begin">
+/// The position of the first element in the range of elements to be combined into the resulting task.
+/// </param>
+/// <param name="_End">
+/// The position of the first element beyond the range of elements to be combined into the resulting task.
+/// </param>
+/// <param name="_CancellationToken">
+/// 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.
+/// </param>
+/// <returns>
+/// A task that completes successfully when any one of the input tasks has completed successfully. If the input tasks are of type <c>T</c>,
+/// the output of this function will be a <c>task<std::pair<T, size_t>>></c>, 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 <c>void</c>
+/// the output is a <c>task<size_t></c>, where the result is the index of the completing task.
+/// </returns>
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
+/**/
+template<typename _Iterator>
+auto when_any(_Iterator _Begin, _Iterator _End, Concurrency::cancellation_token _CancellationToken)
+-> decltype (details::_WhenAnyImpl<typename std::iterator_traits<_Iterator>::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);
+}
+
+/// <summary>
+/// Creates a task that will complete successfully when either of the tasks supplied as arguments completes successfully.
+/// </summary>
+/// <typeparam name="_ReturnType">
+/// The type of the returned task.
+/// </typeparam>
+/// <param name="_Lhs">
+/// The first task to combine into the resulting task.
+/// </param>
+/// <param name="_Rhs">
+/// The second task to combine into the resulting task.
+/// </param>
+/// <returns>
+/// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type <c>T</c>,
+/// the output of this function will be a <c>task<std::vector<T></c>. If the input tasks are of type <c>void</c> the output task
+/// will also be a <c>task<void></c>.
+/// <para> To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence
+/// over ||, the operator|| produces a <c>task<std::vector<T>></c> if one of the tasks is of type <c>task<std::vector<T>></c>
+/// and the other one is of type <c>task<T>.</para>
+/// </returns>
+/// <remarks>
+/// 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 <c>get()</c> or <c>wait()</c> on that task.
+/// </remarks>
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
+/**/
+template<typename _ReturnType>
+task<_ReturnType> operator||(const task<_ReturnType> & _Lhs, const task<_ReturnType> & _Rhs)
+{
+#if _MSC_VER >= 1800
+ auto _PParam = new details::_RunAnyParam<std::pair<_ReturnType, size_t>>();
+
+ task<std::pair<_ReturnType, size_t>> _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<Concurrency::details::_CancellationTokenState *>(_Ret.second));
+ *retVal = _Ret.first;
+ return S_OK;
+ }, nullptr);
+#else
+ auto _PParam = new details::_RunAnyParam<std::pair<_ReturnType, Concurrency::details::_CancellationTokenState *>>();
+
+ task<std::pair<_ReturnType, Concurrency::details::_CancellationTokenState *>> _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<size_t>(_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;
+}
+
+/// <summary>
+/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully.
+/// </summary>
+/// <typeparam name="_ReturnType">
+/// The type of the returned task.
+/// </typeparam>
+/// <param name="_Lhs">
+/// The first task to combine into the resulting task.
+/// </param>
+/// <param name="_Rhs">
+/// The second task to combine into the resulting task.
+/// </param>
+/// <returns>
+/// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type <c>T</c>,
+/// the output of this function will be a <c>task<std::vector<T></c>. If the input tasks are of type <c>void</c> the output task
+/// will also be a <c>task<void></c>.
+/// <para> To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence
+/// over ||, the operator|| produces a <c>task<std::vector<T>></c> if one of the tasks is of type <c>task<std::vector<T>></c>
+/// and the other one is of type <c>task<T>.</para>
+/// </returns>
+/// <remarks>
+/// 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 <c>get()</c> or <c>wait()</c> on that task.
+/// </remarks>
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
+/**/
+template<typename _ReturnType>
+task<std::vector<_ReturnType>> operator||(const task<std::vector<_ReturnType>> & _Lhs, const task<_ReturnType> & _Rhs)
+{
+ auto _PParam = new details::_RunAnyParam<std::pair<std::vector<_ReturnType>, Concurrency::details::_CancellationTokenState *>>();
+
+ task<std::pair<std::vector<_ReturnType>, 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<std::vector<_ReturnType>, 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<std::vector<_ReturnType>> _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;
+}
+
+/// <summary>
+/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully.
+/// </summary>
+/// <typeparam name="_ReturnType">
+/// The type of the returned task.
+/// </typeparam>
+/// <param name="_Lhs">
+/// The first task to combine into the resulting task.
+/// </param>
+/// <param name="_Rhs">
+/// The second task to combine into the resulting task.
+/// </param>
+/// <returns>
+/// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type <c>T</c>,
+/// the output of this function will be a <c>task<std::vector<T></c>. If the input tasks are of type <c>void</c> the output task
+/// will also be a <c>task<void></c>.
+/// <para> To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence
+/// over ||, the operator|| produces a <c>task<std::vector<T>></c> if one of the tasks is of type <c>task<std::vector<T>></c>
+/// and the other one is of type <c>task<T>.</para>
+/// </returns>
+/// <remarks>
+/// 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 <c>get()</c> or <c>wait()</c> on that task.
+/// </remarks>
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
+/**/
+template<typename _ReturnType>
+task<std::vector<_ReturnType>> operator||(const task<_ReturnType> & _Lhs, const task<std::vector<_ReturnType>> & _Rhs)
+{
+ return _Rhs || _Lhs;
+}
+
+/// <summary>
+/// Creates a task that will complete successfully when any of the tasks supplied as arguments completes successfully.
+/// </summary>
+/// <typeparam name="_ReturnType">
+/// The type of the returned task.
+/// </typeparam>
+/// <param name="_Lhs">
+/// The first task to combine into the resulting task.
+/// </param>
+/// <param name="_Rhs">
+/// The second task to combine into the resulting task.
+/// </param>
+/// <returns>
+/// A task that completes sucessfully when either of the input tasks has completed successfully. If the input tasks are of type <c>T</c>,
+/// the output of this function will be a <c>task<std::vector<T></c>. If the input tasks are of type <c>void</c> the output task
+/// will also be a <c>task<void></c>.
+/// <para> To allow for a construct of the sort taskA || taskB && taskC, which are combined in pairs, with && taking precedence
+/// over ||, the operator|| produces a <c>task<std::vector<T>></c> if one of the tasks is of type <c>task<std::vector<T>></c>
+/// and the other one is of type <c>task<T>.</para>
+/// </returns>
+/// <remarks>
+/// 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 <c>get()</c> or <c>wait()</c> on that task.
+/// </remarks>
+/// <seealso cref="Task Parallelism (Concurrency Runtime)"/>
+/**/
+inline task<void> operator||(const task<void> & _Lhs, const task<void> & _Rhs)
+{
+ auto _PParam = new details::_RunAnyParam<std::pair<details::_Unit_type, Concurrency::details::_CancellationTokenState *>>();
+
+ task<std::pair<details::_Unit_type, Concurrency::details::_CancellationTokenState *>> _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<details::_Unit_type, Concurrency::details::_CancellationTokenState *> _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<void> _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<typename _Ty>
+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<bool> task_from_result(bool _Param)
+{
+ task_completion_event<bool> _Tce;
+ _Tce.set(_Param);
+ return create_task<bool>(_Tce, task_options());
+}
+#endif
+inline task<void> task_from_result(const task_options& _TaskOptions = task_options())
+{
+ task_completion_event<void> _Tce;
+ _Tce.set();
+ return create_task<void>(_Tce, _TaskOptions);
+}
+
+template<typename _TaskType, typename _ExType>
+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
+{
+ /// <summary>
+ /// A convenient extension to Concurrency: loop until a condition is no longer met
+ /// </summary>
+ /// <param name="func">
+ /// A function representing the body of the loop. It will be invoked at least once and
+ /// then repetitively as long as it returns true.
+ /// </param>
+ inline
+ task<bool> do_while(std::function<task<bool>(void)> func)
+ {
+ task<bool> first = func();
+ return first.then([=](bool guard, task<bool>* 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