diff --git a/.gitignore b/.gitignore index e257658..dc47b49 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ *.out *.app +.vs/* + diff --git a/ApplicationLoopback.cpp b/ApplicationLoopback.cpp new file mode 100644 index 0000000..6eadd97 --- /dev/null +++ b/ApplicationLoopback.cpp @@ -0,0 +1,78 @@ +#include +#include +#include +#include "LoopbackCapture.h" // Assuming this is where CLoopbackCapture is defined + +void usage() { + std::wcout << L"Usage: AudioCapture \n"; +} + +volatile bool keepRunning = true; + +void signalHandler(int signum) { + keepRunning = false; +} + +int wmain(int argc, wchar_t* argv[]) +{ + if (argc != 4) + { + usage(); + return 0; + } + + DWORD processId = wcstoul(argv[1], nullptr, 0); + if (processId == 0) + { + usage(); + return 0; + } + + bool includeProcessTree; + if (wcscmp(argv[2], L"includetree") == 0) + { + includeProcessTree = true; + } + else if (wcscmp(argv[2], L"excludetree") == 0) + { + includeProcessTree = false; + } + else + { + usage(); + return 0; + } + + PCWSTR outputFile = argv[3]; + + // Set up signal handling + signal(SIGINT, signalHandler); + signal(SIGTERM, signalHandler); + + CLoopbackCapture loopbackCapture; + HRESULT hr = loopbackCapture.StartCaptureAsync(processId, includeProcessTree, outputFile); + if (FAILED(hr)) + { + wil::unique_hlocal_string message; + FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, hr, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (PWSTR)&message, 0, nullptr); + std::wcout << L"Failed to start capture\n0x" << std::hex << hr << L": " << message.get() << L"\n"; + } + else + { + std::wcout << L"Capturing audio. Terminate the process to stop." << std::endl; + + // Run until a termination signal is received + while (keepRunning) + { + Sleep(100); // Sleep to reduce CPU usage + } + + // Stop the capture + loopbackCapture.StopCaptureAsync(); + + std::wcout << L"Capture stopped." << std::endl; + } + + return 0; +} diff --git a/ApplicationLoopback.sln b/ApplicationLoopback.sln new file mode 100644 index 0000000..41c65b5 --- /dev/null +++ b/ApplicationLoopback.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30204.135 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ApplicationLoopback", "ApplicationLoopback.vcxproj", "{6E745655-513E-4713-B3AB-D6D3F62D7734}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6E745655-513E-4713-B3AB-D6D3F62D7734}.Debug|x64.ActiveCfg = Debug|x64 + {6E745655-513E-4713-B3AB-D6D3F62D7734}.Debug|x64.Build.0 = Debug|x64 + {6E745655-513E-4713-B3AB-D6D3F62D7734}.Debug|x86.ActiveCfg = Debug|Win32 + {6E745655-513E-4713-B3AB-D6D3F62D7734}.Debug|x86.Build.0 = Debug|Win32 + {6E745655-513E-4713-B3AB-D6D3F62D7734}.Release|x64.ActiveCfg = Release|x64 + {6E745655-513E-4713-B3AB-D6D3F62D7734}.Release|x64.Build.0 = Release|x64 + {6E745655-513E-4713-B3AB-D6D3F62D7734}.Release|x86.ActiveCfg = Release|Win32 + {6E745655-513E-4713-B3AB-D6D3F62D7734}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {64406148-8C03-42D2-8A2A-C14A03CFF8E3} + EndGlobalSection +EndGlobal diff --git a/ApplicationLoopback.vcxproj b/ApplicationLoopback.vcxproj new file mode 100644 index 0000000..5f8c07e --- /dev/null +++ b/ApplicationLoopback.vcxproj @@ -0,0 +1,169 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {6e745655-513e-4713-b3ab-d6d3f62d7734} + ApplicationLoopback + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + false + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + false + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;mfplat.lib;mmdevapi.lib;mfuuid.lib;mfreadwrite.lib;windowsapp.lib;userenv.lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Disabled + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;mfplat.lib;mmdevapi.lib;mfuuid.lib;mfreadwrite.lib;windowsapp.lib;userenv.lib;%(AdditionalDependencies) + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;mfplat.lib;mmdevapi.lib;mfuuid.lib;mfreadwrite.lib;windowsapp.lib;userenv.lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;mfplat.lib;mmdevapi.lib;mfuuid.lib;mfreadwrite.lib;windowsapp.lib;userenv.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/ApplicationLoopback.vcxproj.filters b/ApplicationLoopback.vcxproj.filters new file mode 100644 index 0000000..320849d --- /dev/null +++ b/ApplicationLoopback.vcxproj.filters @@ -0,0 +1,36 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + + + + \ No newline at end of file diff --git a/ApplicationLoopback.vcxproj.user b/ApplicationLoopback.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/ApplicationLoopback.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Common.h b/Common.h new file mode 100644 index 0000000..a221449 --- /dev/null +++ b/Common.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include + +#ifndef METHODASYNCCALLBACK +#define METHODASYNCCALLBACK(Parent, AsyncCallback, pfnCallback) \ +class Callback##AsyncCallback :\ + public IMFAsyncCallback \ +{ \ +public: \ + Callback##AsyncCallback() : \ + _parent(((Parent*)((BYTE*)this - offsetof(Parent, m_x##AsyncCallback)))), \ + _dwQueueID( MFASYNC_CALLBACK_QUEUE_MULTITHREADED ) \ + { \ + } \ +\ + STDMETHOD_( ULONG, AddRef )() \ + { \ + return _parent->AddRef(); \ + } \ + STDMETHOD_( ULONG, Release )() \ + { \ + return _parent->Release(); \ + } \ + STDMETHOD( QueryInterface )( REFIID riid, void **ppvObject ) \ + { \ + if (riid == IID_IMFAsyncCallback || riid == IID_IUnknown) \ + { \ + (*ppvObject) = this; \ + AddRef(); \ + return S_OK; \ + } \ + *ppvObject = NULL; \ + return E_NOINTERFACE; \ + } \ + STDMETHOD( GetParameters )( \ + /* [out] */ __RPC__out DWORD *pdwFlags, \ + /* [out] */ __RPC__out DWORD *pdwQueue) \ + { \ + *pdwFlags = 0; \ + *pdwQueue = _dwQueueID; \ + return S_OK; \ + } \ + STDMETHOD( Invoke )( /* [out] */ __RPC__out IMFAsyncResult * pResult ) \ + { \ + _parent->pfnCallback( pResult ); \ + return S_OK; \ + } \ + void SetQueueID( DWORD dwQueueID ) { _dwQueueID = dwQueueID; } \ +\ +protected: \ + Parent* _parent; \ + DWORD _dwQueueID; \ + \ +} m_x##AsyncCallback; +#endif diff --git a/LoopbackCapture.cpp b/LoopbackCapture.cpp new file mode 100644 index 0000000..c7a8901 --- /dev/null +++ b/LoopbackCapture.cpp @@ -0,0 +1,417 @@ +#include +#include +#include +#include + +#include "LoopbackCapture.h" + +#define BITS_PER_BYTE 8 + +HRESULT CLoopbackCapture::SetDeviceStateErrorIfFailed(HRESULT hr) +{ + if (FAILED(hr)) + { + m_DeviceState = DeviceState::Error; + } + return hr; +} + +HRESULT CLoopbackCapture::InitializeLoopbackCapture() +{ + // Create events for sample ready or user stop + RETURN_IF_FAILED(m_SampleReadyEvent.create(wil::EventOptions::None)); + + // Initialize MF + RETURN_IF_FAILED(MFStartup(MF_VERSION, MFSTARTUP_LITE)); + + // Register MMCSS work queue + DWORD dwTaskID = 0; + RETURN_IF_FAILED(MFLockSharedWorkQueue(L"Capture", 0, &dwTaskID, &m_dwQueueID)); + + // Set the capture event work queue to use the MMCSS queue + m_xSampleReady.SetQueueID(m_dwQueueID); + + // Create the completion event as auto-reset + RETURN_IF_FAILED(m_hActivateCompleted.create(wil::EventOptions::None)); + + // Create the capture-stopped event as auto-reset + RETURN_IF_FAILED(m_hCaptureStopped.create(wil::EventOptions::None)); + + return S_OK; +} + +CLoopbackCapture::~CLoopbackCapture() +{ + if (m_dwQueueID != 0) + { + MFUnlockWorkQueue(m_dwQueueID); + } +} + +HRESULT CLoopbackCapture::ActivateAudioInterface(DWORD processId, bool includeProcessTree) +{ + return SetDeviceStateErrorIfFailed([&]() -> HRESULT + { + AUDIOCLIENT_ACTIVATION_PARAMS audioclientActivationParams = {}; + audioclientActivationParams.ActivationType = AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK; + audioclientActivationParams.ProcessLoopbackParams.ProcessLoopbackMode = includeProcessTree ? + PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE : PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE; + audioclientActivationParams.ProcessLoopbackParams.TargetProcessId = processId; + + PROPVARIANT activateParams = {}; + activateParams.vt = VT_BLOB; + activateParams.blob.cbSize = sizeof(audioclientActivationParams); + activateParams.blob.pBlobData = (BYTE*)&audioclientActivationParams; + + wil::com_ptr_nothrow asyncOp; + RETURN_IF_FAILED(ActivateAudioInterfaceAsync(VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK, __uuidof(IAudioClient), &activateParams, this, &asyncOp)); + + // Wait for activation completion + m_hActivateCompleted.wait(); + + return m_activateResult; + }()); +} + +// +// ActivateCompleted() +// +// Callback implementation of ActivateAudioInterfaceAsync function. This will be called on MTA thread +// when results of the activation are available. +// +HRESULT CLoopbackCapture::ActivateCompleted(IActivateAudioInterfaceAsyncOperation* operation) +{ + m_activateResult = SetDeviceStateErrorIfFailed([&]()->HRESULT + { + // Check for a successful activation result + HRESULT hrActivateResult = E_UNEXPECTED; + wil::com_ptr_nothrow punkAudioInterface; + RETURN_IF_FAILED(operation->GetActivateResult(&hrActivateResult, &punkAudioInterface)); + RETURN_IF_FAILED(hrActivateResult); + + // Get the pointer for the Audio Client + RETURN_IF_FAILED(punkAudioInterface.copy_to(&m_AudioClient)); + + // The app can also call m_AudioClient->GetMixFormat instead to get the capture format. + // 16 - bit PCM format. + m_CaptureFormat.wFormatTag = WAVE_FORMAT_PCM; + m_CaptureFormat.nChannels = 2; + m_CaptureFormat.nSamplesPerSec = 44100; + m_CaptureFormat.wBitsPerSample = 16; + m_CaptureFormat.nBlockAlign = m_CaptureFormat.nChannels * m_CaptureFormat.wBitsPerSample / BITS_PER_BYTE; + m_CaptureFormat.nAvgBytesPerSec = m_CaptureFormat.nSamplesPerSec * m_CaptureFormat.nBlockAlign; + + // Initialize the AudioClient in Shared Mode with the user specified buffer + RETURN_IF_FAILED(m_AudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + 200000, + AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM, + &m_CaptureFormat, + nullptr)); + + // Get the maximum size of the AudioClient Buffer + RETURN_IF_FAILED(m_AudioClient->GetBufferSize(&m_BufferFrames)); + + // Get the capture client + RETURN_IF_FAILED(m_AudioClient->GetService(IID_PPV_ARGS(&m_AudioCaptureClient))); + + // Create Async callback for sample events + RETURN_IF_FAILED(MFCreateAsyncResult(nullptr, &m_xSampleReady, nullptr, &m_SampleReadyAsyncResult)); + + // Tell the system which event handle it should signal when an audio buffer is ready to be processed by the client + RETURN_IF_FAILED(m_AudioClient->SetEventHandle(m_SampleReadyEvent.get())); + + // Creates the WAV file. + RETURN_IF_FAILED(CreateWAVFile()); + + // Everything is ready. + m_DeviceState = DeviceState::Initialized; + + return S_OK; + }()); + + // Let ActivateAudioInterface know that m_activateResult has the result of the activation attempt. + m_hActivateCompleted.SetEvent(); + return S_OK; +} + +// +// CreateWAVFile() +// +// Creates a WAV file in music folder +// +HRESULT CLoopbackCapture::CreateWAVFile() +{ + return SetDeviceStateErrorIfFailed([&]()->HRESULT + { + m_hFile.reset(CreateFile(m_outputFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)); + RETURN_LAST_ERROR_IF(!m_hFile); + + // Create and write the WAV header + + // 1. RIFF chunk descriptor + DWORD header[] = { + FCC('RIFF'), // RIFF header + 0, // Total size of WAV (will be filled in later) + FCC('WAVE'), // WAVE FourCC + FCC('fmt '), // Start of 'fmt ' chunk + sizeof(m_CaptureFormat) // Size of fmt chunk + }; + DWORD dwBytesWritten = 0; + RETURN_IF_WIN32_BOOL_FALSE(WriteFile(m_hFile.get(), header, sizeof(header), &dwBytesWritten, NULL)); + + m_cbHeaderSize += dwBytesWritten; + + // 2. The fmt sub-chunk + WI_ASSERT(m_CaptureFormat.cbSize == 0); + RETURN_IF_WIN32_BOOL_FALSE(WriteFile(m_hFile.get(), &m_CaptureFormat, sizeof(m_CaptureFormat), &dwBytesWritten, NULL)); + m_cbHeaderSize += dwBytesWritten; + + // 3. The data sub-chunk + DWORD data[] = { FCC('data'), 0 }; // Start of 'data' chunk + RETURN_IF_WIN32_BOOL_FALSE(WriteFile(m_hFile.get(), data, sizeof(data), &dwBytesWritten, NULL)); + m_cbHeaderSize += dwBytesWritten; + + return S_OK; + }()); +} + + +// +// FixWAVHeader() +// +// The size values were not known when we originally wrote the header, so now go through and fix the values +// +HRESULT CLoopbackCapture::FixWAVHeader() +{ + // Write the size of the 'data' chunk first + DWORD dwPtr = SetFilePointer(m_hFile.get(), m_cbHeaderSize - sizeof(DWORD), NULL, FILE_BEGIN); + RETURN_LAST_ERROR_IF(INVALID_SET_FILE_POINTER == dwPtr); + + DWORD dwBytesWritten = 0; + RETURN_IF_WIN32_BOOL_FALSE(WriteFile(m_hFile.get(), &m_cbDataSize, sizeof(DWORD), &dwBytesWritten, NULL)); + + // Write the total file size, minus RIFF chunk and size + // sizeof(DWORD) == sizeof(FOURCC) + RETURN_LAST_ERROR_IF(INVALID_SET_FILE_POINTER == SetFilePointer(m_hFile.get(), sizeof(DWORD), NULL, FILE_BEGIN)); + + DWORD cbTotalSize = m_cbDataSize + m_cbHeaderSize - 8; + RETURN_IF_WIN32_BOOL_FALSE(WriteFile(m_hFile.get(), &cbTotalSize, sizeof(DWORD), &dwBytesWritten, NULL)); + + RETURN_IF_WIN32_BOOL_FALSE(FlushFileBuffers(m_hFile.get())); + + return S_OK; +} + +HRESULT CLoopbackCapture::StartCaptureAsync(DWORD processId, bool includeProcessTree, PCWSTR outputFileName) +{ + m_outputFileName = outputFileName; + auto resetOutputFileName = wil::scope_exit([&] { m_outputFileName = nullptr; }); + + RETURN_IF_FAILED(InitializeLoopbackCapture()); + RETURN_IF_FAILED(ActivateAudioInterface(processId, includeProcessTree)); + + // We should be in the initialzied state if this is the first time through getting ready to capture. + if (m_DeviceState == DeviceState::Initialized) + { + m_DeviceState = DeviceState::Starting; + return MFPutWorkItem2(MFASYNC_CALLBACK_QUEUE_MULTITHREADED, 0, &m_xStartCapture, nullptr); + } + + return S_OK; +} + +// +// OnStartCapture() +// +// Callback method to start capture +// +HRESULT CLoopbackCapture::OnStartCapture(IMFAsyncResult* pResult) +{ + return SetDeviceStateErrorIfFailed([&]()->HRESULT + { + // Start the capture + RETURN_IF_FAILED(m_AudioClient->Start()); + + m_DeviceState = DeviceState::Capturing; + MFPutWaitingWorkItem(m_SampleReadyEvent.get(), 0, m_SampleReadyAsyncResult.get(), &m_SampleReadyKey); + + return S_OK; + }()); +} + + +// +// StopCaptureAsync() +// +// Stop capture asynchronously via MF Work Item +// +HRESULT CLoopbackCapture::StopCaptureAsync() +{ + RETURN_HR_IF(E_NOT_VALID_STATE, (m_DeviceState != DeviceState::Capturing) && + (m_DeviceState != DeviceState::Error)); + + m_DeviceState = DeviceState::Stopping; + + RETURN_IF_FAILED(MFPutWorkItem2(MFASYNC_CALLBACK_QUEUE_MULTITHREADED, 0, &m_xStopCapture, nullptr)); + + // Wait for capture to stop + m_hCaptureStopped.wait(); + + return S_OK; +} + +// +// OnStopCapture() +// +// Callback method to stop capture +// +HRESULT CLoopbackCapture::OnStopCapture(IMFAsyncResult* pResult) +{ + // Stop capture by cancelling Work Item + // Cancel the queued work item (if any) + if (0 != m_SampleReadyKey) + { + MFCancelWorkItem(m_SampleReadyKey); + m_SampleReadyKey = 0; + } + + m_AudioClient->Stop(); + m_SampleReadyAsyncResult.reset(); + + return FinishCaptureAsync(); +} + +// +// FinishCaptureAsync() +// +// Finalizes WAV file on a separate thread via MF Work Item +// +HRESULT CLoopbackCapture::FinishCaptureAsync() +{ + // We should be flushing when this is called + return MFPutWorkItem2(MFASYNC_CALLBACK_QUEUE_MULTITHREADED, 0, &m_xFinishCapture, nullptr); +} + +// +// OnFinishCapture() +// +// Because of the asynchronous nature of the MF Work Queues and the DataWriter, there could still be +// a sample processing. So this will get called to finalize the WAV header. +// +HRESULT CLoopbackCapture::OnFinishCapture(IMFAsyncResult* pResult) +{ + // FixWAVHeader will set the DeviceStateStopped when all async tasks are complete + HRESULT hr = FixWAVHeader(); + + m_DeviceState = DeviceState::Stopped; + + m_hCaptureStopped.SetEvent(); + + return hr; +} + +// +// OnSampleReady() +// +// Callback method when ready to fill sample buffer +// +HRESULT CLoopbackCapture::OnSampleReady(IMFAsyncResult* pResult) +{ + if (SUCCEEDED(OnAudioSampleRequested())) + { + // Re-queue work item for next sample + if (m_DeviceState == DeviceState::Capturing) + { + // Re-queue work item for next sample + return MFPutWaitingWorkItem(m_SampleReadyEvent.get(), 0, m_SampleReadyAsyncResult.get(), &m_SampleReadyKey); + } + } + else + { + m_DeviceState = DeviceState::Error; + } + + return S_OK; +} + +// +// OnAudioSampleRequested() +// +// Called when audio device fires m_SampleReadyEvent +// +HRESULT CLoopbackCapture::OnAudioSampleRequested() +{ + UINT32 FramesAvailable = 0; + BYTE* Data = nullptr; + DWORD dwCaptureFlags; + UINT64 u64DevicePosition = 0; + UINT64 u64QPCPosition = 0; + DWORD cbBytesToCapture = 0; + + auto lock = m_CritSec.lock(); + + // If this flag is set, we have already queued up the async call to finialize the WAV header + // So we don't want to grab or write any more data that would possibly give us an invalid size + if (m_DeviceState == DeviceState::Stopping) + { + return S_OK; + } + + // A word on why we have a loop here; + // Suppose it has been 10 milliseconds or so since the last time + // this routine was invoked, and that we're capturing 48000 samples per second. + // + // The audio engine can be reasonably expected to have accumulated about that much + // audio data - that is, about 480 samples. + // + // However, the audio engine is free to accumulate this in various ways: + // a. as a single packet of 480 samples, OR + // b. as a packet of 80 samples plus a packet of 400 samples, OR + // c. as 48 packets of 10 samples each. + // + // In particular, there is no guarantee that this routine will be + // run once for each packet. + // + // So every time this routine runs, we need to read ALL the packets + // that are now available; + // + // We do this by calling IAudioCaptureClient::GetNextPacketSize + // over and over again until it indicates there are no more packets remaining. + while (SUCCEEDED(m_AudioCaptureClient->GetNextPacketSize(&FramesAvailable)) && FramesAvailable > 0) + { + cbBytesToCapture = FramesAvailable * m_CaptureFormat.nBlockAlign; + + // WAV files have a 4GB (0xFFFFFFFF) size limit, so likely we have hit that limit when we + // overflow here. Time to stop the capture + if ((m_cbDataSize + cbBytesToCapture) < m_cbDataSize) + { + StopCaptureAsync(); + break; + } + + // Get sample buffer + RETURN_IF_FAILED(m_AudioCaptureClient->GetBuffer(&Data, &FramesAvailable, &dwCaptureFlags, &u64DevicePosition, &u64QPCPosition)); + + + // Write File + if (m_DeviceState != DeviceState::Stopping) + { + DWORD dwBytesWritten = 0; + RETURN_IF_WIN32_BOOL_FALSE(WriteFile( + m_hFile.get(), + Data, + cbBytesToCapture, + &dwBytesWritten, + NULL)); + } + + // Release buffer back + m_AudioCaptureClient->ReleaseBuffer(FramesAvailable); + + // Increase the size of our 'data' chunk. m_cbDataSize needs to be accurate + m_cbDataSize += cbBytesToCapture; + } + + return S_OK; +} diff --git a/LoopbackCapture.h b/LoopbackCapture.h new file mode 100644 index 0000000..936856c --- /dev/null +++ b/LoopbackCapture.h @@ -0,0 +1,86 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "Common.h" + +using namespace Microsoft::WRL; + +class CLoopbackCapture : + public RuntimeClass< RuntimeClassFlags< ClassicCom >, FtmBase, IActivateAudioInterfaceCompletionHandler > +{ +public: + CLoopbackCapture() = default; + ~CLoopbackCapture(); + + HRESULT StartCaptureAsync(DWORD processId, bool includeProcessTree, PCWSTR outputFileName); + HRESULT StopCaptureAsync(); + + METHODASYNCCALLBACK(CLoopbackCapture, StartCapture, OnStartCapture); + METHODASYNCCALLBACK(CLoopbackCapture, StopCapture, OnStopCapture); + METHODASYNCCALLBACK(CLoopbackCapture, SampleReady, OnSampleReady); + METHODASYNCCALLBACK(CLoopbackCapture, FinishCapture, OnFinishCapture); + + // IActivateAudioInterfaceCompletionHandler + STDMETHOD(ActivateCompleted)(IActivateAudioInterfaceAsyncOperation* operation); + +private: + // NB: All states >= Initialized will allow some methods + // to be called successfully on the Audio Client + enum class DeviceState + { + Uninitialized, + Error, + Initialized, + Starting, + Capturing, + Stopping, + Stopped, + }; + + HRESULT OnStartCapture(IMFAsyncResult* pResult); + HRESULT OnStopCapture(IMFAsyncResult* pResult); + HRESULT OnFinishCapture(IMFAsyncResult* pResult); + HRESULT OnSampleReady(IMFAsyncResult* pResult); + + HRESULT InitializeLoopbackCapture(); + HRESULT CreateWAVFile(); + HRESULT FixWAVHeader(); + HRESULT OnAudioSampleRequested(); + + HRESULT ActivateAudioInterface(DWORD processId, bool includeProcessTree); + HRESULT FinishCaptureAsync(); + + HRESULT SetDeviceStateErrorIfFailed(HRESULT hr); + + wil::com_ptr_nothrow m_AudioClient; + WAVEFORMATEX m_CaptureFormat{}; + UINT32 m_BufferFrames = 0; + wil::com_ptr_nothrow m_AudioCaptureClient; + wil::com_ptr_nothrow m_SampleReadyAsyncResult; + + wil::unique_event_nothrow m_SampleReadyEvent; + MFWORKITEM_KEY m_SampleReadyKey = 0; + wil::unique_hfile m_hFile; + wil::critical_section m_CritSec; + DWORD m_dwQueueID = 0; + DWORD m_cbHeaderSize = 0; + DWORD m_cbDataSize = 0; + + // These two members are used to communicate between the main thread + // and the ActivateCompleted callback. + PCWSTR m_outputFileName = nullptr; + HRESULT m_activateResult = E_UNEXPECTED; + + DeviceState m_DeviceState{ DeviceState::Uninitialized }; + wil::unique_event_nothrow m_hActivateCompleted; + wil::unique_event_nothrow m_hCaptureStopped; +}; diff --git a/packages.config b/packages.config new file mode 100644 index 0000000..26065b1 --- /dev/null +++ b/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/.signature.p7s b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/.signature.p7s new file mode 100644 index 0000000..b7d7276 Binary files /dev/null and b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/.signature.p7s differ diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/LICENSE b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/LICENSE new file mode 100644 index 0000000..2107107 --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/LICENSE @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/Microsoft.Windows.ImplementationLibrary.1.0.210204.1.nupkg b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/Microsoft.Windows.ImplementationLibrary.1.0.210204.1.nupkg new file mode 100644 index 0000000..cb3ece6 Binary files /dev/null and b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/Microsoft.Windows.ImplementationLibrary.1.0.210204.1.nupkg differ diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/ThirdPartyNotices.txt b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/ThirdPartyNotices.txt new file mode 100644 index 0000000..40457fe --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/ThirdPartyNotices.txt @@ -0,0 +1,60 @@ +THIRD PARTY SOFTWARE NOTICES AND INFORMATION +Do Not Translate or Localize + +This software incorporates material from third parties. Microsoft makes certain open source code available at http://3rdpartysource.microsoft.com, or you may send a check or money order for US $5.00, including the product name, the open source component name, and version number, to: + +Source Code Compliance Team +Microsoft Corporation +One Microsoft Way +Redmond, WA 98052 +USA + +Notwithstanding any other terms, you may reverse engineer this software to the extent required to debug changes to any libraries licensed under the GNU Lesser General Public License. + +Libc++ + +Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Catch2 + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/build/native/Microsoft.Windows.ImplementationLibrary.targets b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/build/native/Microsoft.Windows.ImplementationLibrary.targets new file mode 100644 index 0000000..6587053 --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/build/native/Microsoft.Windows.ImplementationLibrary.targets @@ -0,0 +1,8 @@ + + + + + $(MSBuildThisFileDirectory)..\..\include\;%(AdditionalIncludeDirectories) + + + diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/com.h b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/com.h new file mode 100644 index 0000000..b8688b7 --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/com.h @@ -0,0 +1,2830 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_COM_INCLUDED +#define __WIL_COM_INCLUDED + +#include +#include +#include "result.h" +#include "resource.h" // last to ensure _COMBASEAPI_H_ protected definitions are available + +// Forward declaration within WIL (see https://msdn.microsoft.com/en-us/library/br244983.aspx) +/// @cond +namespace Microsoft +{ + namespace WRL + { + template + class ComPtr; + } +} +/// @endcond + +namespace wil +{ + /// @cond + namespace details + { + // We can't directly use wistd::is_convertible as it returns TRUE for an ambiguous conversion. + // Adding is_abstract to the mix, enables us to allow conversion for interfaces, but deny it for + // classes (where the multiple inheritance causes ambiguity). + // NOTE: I've reached out to vcsig on this topic and it turns out that __is_convertible_to should NEVER + // return true for ambiguous conversions. This was a bug in our compiler that has since been fixed. + // Eventually, once that fix propagates we can move to a more efficient __is_convertible_to without + // the added complexity. + template + struct is_com_convertible : + wistd::bool_constant<__is_convertible_to(TFrom, TTo) && (__is_abstract(TFrom) || wistd::is_same::value)> + { + }; + + typedef wistd::integral_constant tag_com_query; + typedef wistd::integral_constant tag_try_com_query; + typedef wistd::integral_constant tag_com_copy; + typedef wistd::integral_constant tag_try_com_copy; + + class default_query_policy + { + public: + template + inline static HRESULT query(_In_ T* ptr, REFIID riid, _COM_Outptr_ void** result) + { + return ptr->QueryInterface(riid, result); + } + + template + inline static HRESULT query(_In_ T* ptr, _COM_Outptr_ TResult** result) + { + return query_dispatch(ptr, typename details::is_com_convertible::type(), result); + } + + private: + template + inline static HRESULT query_dispatch(_In_ T* ptr, wistd::true_type, _COM_Outptr_ TResult** result) // convertible + { + *result = ptr; + (*result)->AddRef(); + return S_OK; + } + + template + inline static HRESULT query_dispatch(_In_ T* ptr, wistd::false_type, _COM_Outptr_ TResult** result) // not convertible + { + auto hr = ptr->QueryInterface(IID_PPV_ARGS(result)); + __analysis_assume(SUCCEEDED(hr) || (*result == nullptr)); + return hr; + } + }; + + template + struct query_policy_helper + { + typedef default_query_policy type; + }; + + class weak_query_policy + { + public: + inline static HRESULT query(_In_ IWeakReference* ptr, REFIID riid, _COM_Outptr_ void** result) + { + WI_ASSERT_MSG(riid != __uuidof(IWeakReference), "Cannot resolve a weak reference to IWeakReference"); + *result = nullptr; + + IInspectable* temp; + HRESULT hr = ptr->Resolve(__uuidof(IInspectable), reinterpret_cast(&temp)); + if (SUCCEEDED(hr)) + { + if (temp == nullptr) + { + return E_NOT_SET; + } + hr = temp->QueryInterface(riid, result); + __analysis_assume(SUCCEEDED(hr) || (*result == nullptr)); + temp->Release(); + } + + return hr; + } + + template + inline static HRESULT query(_In_ IWeakReference* ptr, _COM_Outptr_ TResult** result) + { + static_assert(!wistd::is_same::value, "Cannot resolve a weak reference to IWeakReference"); + return query_dispatch(ptr, wistd::is_base_of(), result); + } + + private: + template + static HRESULT query_dispatch(_In_ IWeakReference* ptr, wistd::true_type, _COM_Outptr_ TResult** result) + { + auto hr = ptr->Resolve(__uuidof(TResult), reinterpret_cast(result)); + if (SUCCEEDED(hr) && (*result == nullptr)) + { + hr = E_NOT_SET; + } + __analysis_assume(SUCCEEDED(hr) || (*result == nullptr)); + return hr; + } + + template + static HRESULT query_dispatch(_In_ IWeakReference* ptr, wistd::false_type, _COM_Outptr_ TResult** result) + { + return query(ptr, IID_PPV_ARGS(result)); + } + }; + + template <> + struct query_policy_helper + { + typedef weak_query_policy type; + }; + +#if (NTDDI_VERSION >= NTDDI_WINBLUE) + class agile_query_policy + { + public: + inline static HRESULT query(_In_ IAgileReference* ptr, REFIID riid, _COM_Outptr_ void** result) + { + WI_ASSERT_MSG(riid != __uuidof(IAgileReference), "Cannot resolve a agile reference to IAgileReference"); + auto hr = ptr->Resolve(riid, result); + __analysis_assume(SUCCEEDED(hr) || (*result == nullptr)); // IAgileReference::Resolve not annotated correctly + return hr; + } + + template + static HRESULT query(_In_ IAgileReference* ptr, _COM_Outptr_ TResult** result) + { + static_assert(!wistd::is_same::value, "Cannot resolve a agile reference to IAgileReference"); + return query(ptr, __uuidof(TResult), reinterpret_cast(result)); + } + }; + + template <> + struct query_policy_helper + { + typedef agile_query_policy type; + }; +#endif + + template + using query_policy_t = typename query_policy_helper::type>::type; + + } // details + /// @endcond + + //! Represents the base template type that implements com_ptr, com_weak_ref, and com_agile_ref. + //! See @ref page_comptr for more background. See @ref page_query for more information on querying with WIL. + //! @tparam T Represents the type being held by the com_ptr_t. + //! For com_ptr, this will always be the interface being represented. For com_weak_ref, this will always be + //! IWeakReference. For com_agile_ref, this will always be IAgileReference. + //! @tparam err_policy Represents the error policy for the class (error codes, exceptions, or fail fast; see @ref page_errors) + template + class com_ptr_t + { + private: + typedef typename wistd::add_lvalue_reference::type element_type_reference; + typedef details::query_policy_t query_policy; + public: + //! The function return result (HRESULT or void) for the given err_policy (see @ref page_errors). + typedef typename err_policy::result result; + //! The template type `T` being held by the com_ptr_t. + typedef T element_type; + //! A pointer to the template type `T` being held by the com_ptr_t (what `get()` returns). + typedef T* pointer; + + //! @name Constructors + //! @{ + + //! Default constructor (holds nullptr). + com_ptr_t() WI_NOEXCEPT : + m_ptr(nullptr) + { + } + + //! Implicit construction from nullptr_t (holds nullptr). + com_ptr_t(wistd::nullptr_t) WI_NOEXCEPT : + com_ptr_t() + { + } + + //! Implicit construction from a compatible raw interface pointer (AddRef's the parameter). + com_ptr_t(pointer ptr) WI_NOEXCEPT : + m_ptr(ptr) + { + if (m_ptr) + { + m_ptr->AddRef(); + } + } + + //! Copy-construction from a like `com_ptr_t` (copies and AddRef's the parameter). + com_ptr_t(const com_ptr_t& other) WI_NOEXCEPT : + com_ptr_t(other.get()) + { + } + + //! Copy-construction from a convertible `com_ptr_t` (copies and AddRef's the parameter). + template > + com_ptr_t(const com_ptr_t& other) WI_NOEXCEPT : + com_ptr_t(static_cast(other.get())) + { + } + + //! Move construction from a like `com_ptr_t` (avoids AddRef/Release by moving from the parameter). + com_ptr_t(com_ptr_t&& other) WI_NOEXCEPT : + m_ptr(other.detach()) + { + } + + //! Move construction from a compatible `com_ptr_t` (avoids AddRef/Release by moving from the parameter). + template > + com_ptr_t(com_ptr_t&& other) WI_NOEXCEPT : + m_ptr(other.detach()) + { + } + //! @} + + //! Destructor (releases the pointer). + ~com_ptr_t() WI_NOEXCEPT + { + if (m_ptr) + { + m_ptr->Release(); + } + } + + //! @name Assignment operators + //! @{ + + //! Assign to nullptr (releases the current pointer, holds nullptr). + com_ptr_t& operator=(wistd::nullptr_t) WI_NOEXCEPT + { + reset(); + return *this; + } + + //! Assign a compatible raw interface pointer (releases current pointer, copies and AddRef's the parameter). + com_ptr_t& operator=(pointer other) WI_NOEXCEPT + { + auto ptr = m_ptr; + m_ptr = other; + if (m_ptr) + { + m_ptr->AddRef(); + } + if (ptr) + { + ptr->Release(); + } + return *this; + } + + //! Assign a like `com_ptr_t` (releases current pointer, copies and AddRef's the parameter). + com_ptr_t& operator=(const com_ptr_t& other) WI_NOEXCEPT + { + return operator=(other.get()); + } + + //! Assign a convertible `com_ptr_t` (releases current pointer, copies and AddRef's the parameter). + template > + com_ptr_t& operator=(const com_ptr_t& other) WI_NOEXCEPT + { + return operator=(static_cast(other.get())); + } + + //! Move assign from a like `com_ptr_t` (releases current pointer, avoids AddRef/Release by moving the parameter). + com_ptr_t& operator=(com_ptr_t&& other) WI_NOEXCEPT + { + attach(other.detach()); + return *this; + } + + //! Move assignment from a compatible `com_ptr_t` (releases current pointer, avoids AddRef/Release by moving from the parameter). + template > + com_ptr_t& operator=(com_ptr_t&& other) WI_NOEXCEPT + { + attach(other.detach()); + return *this; + } + //! @} + + //! @name Modifiers + //! @{ + + //! Swap pointers with an another named com_ptr_t object. + template + void swap(com_ptr_t& other) WI_NOEXCEPT + { + auto ptr = m_ptr; + m_ptr = other.m_ptr; + other.m_ptr = ptr; + } + + //! Swap pointers with a rvalue reference to another com_ptr_t object. + template + void swap(com_ptr_t&& other) WI_NOEXCEPT + { + swap(other); + } + + //! Releases the pointer and sets it to nullptr. + void reset() WI_NOEXCEPT + { + auto ptr = m_ptr; + m_ptr = nullptr; + if (ptr) + { + ptr->Release(); + } + } + + //! Releases the pointer and sets it to nullptr. + void reset(wistd::nullptr_t) WI_NOEXCEPT + { + reset(); + } + + //! Takes ownership of a compatible raw interface pointer (releases pointer, copies but DOES NOT AddRef the parameter). + void attach(pointer other) WI_NOEXCEPT + { + auto ptr = m_ptr; + m_ptr = other; + if (ptr) + { + ULONG ref; + ref = ptr->Release(); + WI_ASSERT_MSG(((other != ptr) || (ref > 0)), "Bug: Attaching the same already assigned, destructed pointer"); + } + } + + //! Relinquishes ownership and returns the internal interface pointer (DOES NOT release the detached pointer, sets class pointer to null). + WI_NODISCARD pointer detach() WI_NOEXCEPT + { + auto temp = m_ptr; + m_ptr = nullptr; + return temp; + } + + //! Returns the address of the internal pointer (releases ownership of the pointer BEFORE returning the address). + //! The pointer is explicitly released to prevent accidental leaks of the pointer. Coding standards generally indicate that + //! there is little valid `_Inout_` use of `IInterface**`, making this safe to do under typical use. + //! @see addressof + //! ~~~~ + //! STDAPI GetMuffin(IMuffin **muffin); + //! wil::com_ptr myMuffin; + //! THROW_IF_FAILED(GetMuffin(myMuffin.put())); + //! ~~~~ + pointer* put() WI_NOEXCEPT + { + reset(); + return &m_ptr; + } + + //! Returns the address of the internal pointer casted to void** (releases ownership of the pointer BEFORE returning the address). + //! @see put + void** put_void() WI_NOEXCEPT + { + return reinterpret_cast(put()); + } + + //! Returns the address of the internal pointer casted to IUnknown** (releases ownership of the pointer BEFORE returning the address). + //! @see put + ::IUnknown** put_unknown() WI_NOEXCEPT + { + return reinterpret_cast<::IUnknown**>(put()); + } + + //! Returns the address of the internal pointer (releases ownership of the pointer BEFORE returning the address). + //! The pointer is explicitly released to prevent accidental leaks of the pointer. Coding standards generally indicate that + //! there is little valid `_Inout_` use of `IInterface**`, making this safe to do under typical use. Since this behavior is not always immediately + //! apparent, prefer to scope variables as close to use as possible (generally avoiding use of the same com_ptr variable in successive calls to + //! receive an output interface). + //! @see addressof + pointer* operator&() WI_NOEXCEPT + { + return put(); + } + + //! Returns the address of the internal pointer (does not release the pointer; should not be used for `_Out_` parameters) + pointer* addressof() WI_NOEXCEPT + { + return &m_ptr; + } + //! @} + + //! @name Inspection + //! @{ + + //! Returns the address of the const internal pointer (does not release the pointer) + const pointer* addressof() const WI_NOEXCEPT + { + return &m_ptr; + } + + //! Returns 'true' if the pointer is assigned (NOT nullptr) + explicit operator bool() const WI_NOEXCEPT + { + return (m_ptr != nullptr); + } + + //! Returns the pointer + pointer get() const WI_NOEXCEPT + { + return m_ptr; + } + + //! Allows direct calls against the pointer (AV on internal nullptr) + pointer operator->() const WI_NOEXCEPT + { + return m_ptr; + } + + //! Dereferences the pointer (AV on internal nullptr) + element_type_reference operator*() const WI_NOEXCEPT + { + return *m_ptr; + } + //! @} + + //! @name Query helpers + //! * Retrieves the requested interface + //! * AV if the pointer is null + //! * Produce an error if the requested interface is unsupported + //! + //! See @ref page_query for more information + //! @{ + + //! Query and return a smart pointer matching the interface specified by 'U': `auto foo = m_ptr.query();`. + //! See @ref page_query for more information. + //! + //! This method is the primary method that should be used to query a com_ptr in exception-based or fail-fast based code. + //! Error-code returning code should use @ref query_to so that the returned HRESULT can be examined. In the following + //! examples, `m_ptr` is an exception-based or fail-fast based com_ptr, com_weak_ref, or com_agile_ref: + //! ~~~~ + //! auto foo = ptr.query(); + //! foo->Method1(); + //! foo->Method2(); + //! ~~~~ + //! For simple single-method calls, this method allows removing the temporary that holds the com_ptr: + //! ~~~~ + //! ptr.query()->Method1(); + //! ~~~~ + //! @tparam U Represents the interface being queried + //! @return A `com_ptr_t` pointer to the given interface `U`. The pointer is guaranteed not null. The returned + //! `com_ptr_t` type will be @ref com_ptr or @ref com_ptr_failfast (matching the error handling form of the + //! pointer being queried (exception based or fail-fast). + template + inline com_ptr_t query() const + { + static_assert(wistd::is_same::value, "query requires exceptions or fail fast; use try_query or query_to"); + return com_ptr_t(m_ptr, details::tag_com_query()); + } + + //! Query for the interface of the given out parameter `U`: `ptr.query_to(&foo);`. + //! See @ref page_query for more information. + //! + //! For fail-fast and exception-based behavior this routine should primarily be used to write to out parameters and @ref query should + //! be used to perform most queries. For error-code based code, this routine is the primary method that should be used to query a com_ptr. + //! + //! Error-code based samples: + //! ~~~~ + //! // class member being queried: + //! wil::com_ptr_nothrow m_ptr; + //! + //! // simple query example: + //! wil::com_ptr_nothrow foo; + //! RETURN_IF_FAILED(m_ptr.query_to(&foo)); + //! foo->FooMethod1(); + //! + //! // output parameter example: + //! HRESULT GetFoo(_COM_Outptr_ IFoo** fooPtr) + //! { + //! RETURN_IF_FAILED(m_ptr.query_to(fooPtr)); + //! return S_OK; + //! } + //! ~~~~ + //! Exception or fail-fast samples: + //! ~~~~ + //! // class member being queried + //! wil::com_ptr m_ptr; + //! + //! void GetFoo(_COM_Outptr_ IFoo** fooPtr) + //! { + //! m_ptr.query_to(fooPtr); + //! } + //! ~~~~ + //! @tparam U Represents the interface being queried (type of the output parameter). This interface does not need to + //! be specified directly. Rely upon template type deduction to pick up the type from the output parameter. + //! @param ptrResult The output pointer that will receive the newly queried interface. This pointer will be assigned null on failure. + //! @return For the nothrow (error code-based) classes (@ref com_ptr_nothrow, @ref com_weak_ref_nothrow, @ref com_agile_ref_nothrow) this + //! method returns an `HRESULT` indicating whether the query was successful. Exception-based and fail-fast based classes + //! do not return a value (void). + template + result query_to(_COM_Outptr_ U** ptrResult) const + { + // Prefast cannot see through the error policy + query_policy mapping and as a result fires 6388 and 28196 for this function. + // Suppression is also not working. Wrapping this entire function in #pragma warning(disable: 6388 28196) does not stop all of the prefast errors + // from being emitted. +#if defined(_PREFAST_) + *ptrResult = nullptr; + return err_policy::HResult(E_NOINTERFACE); +#else + return err_policy::HResult(query_policy::query(m_ptr, ptrResult)); +#endif + } + + //! Query for the requested interface using the iid, ppv pattern: `ptr.query_to(riid, ptr);`. + //! See @ref page_query for more information. + //! + //! This method is built to implement an API boundary that exposes a returned pointer to a caller through the REFIID and void** pointer + //! pattern (like QueryInterface). This pattern should not be used outside of that pattern (through IID_PPV_ARGS) as it is less efficient + //! than the typed version of @ref query_to which can elide the QueryInterface in favor of AddRef when the types are convertible. + //! ~~~~ + //! // class member being queried: + //! wil::com_ptr_nothrow m_ptr; + //! + //! // output parameter example: + //! HRESULT GetFoo(REFIID riid, _COM_Outptr_ void** ptrResult) + //! { + //! RETURN_IF_FAILED(m_ptr.query_to(riid, ptrResult)); + //! return S_OK; + //! } + //! ~~~~ + //! @param riid The interface to query for. + //! @param ptrResult The output pointer that will receive the newly queried interface. This pointer will be assigned null on failure. + //! @return For the nothrow (error code-based) classes (@ref com_ptr_nothrow, @ref com_weak_ref_nothrow, @ref com_agile_ref_nothrow) this + //! method returns an `HRESULT` indicating whether the query was successful. Exception-based and fail-fast based classes + //! do not return a value (void). + result query_to(REFIID riid, _COM_Outptr_ void** ptrResult) const + { + // Prefast cannot see through the error policy + query_policy mapping and as a result and as a result fires 6388 and 28196 for this function. + // Suppression is also not working. Wrapping this entire function in #pragma warning(disable: 6388 28196) does not stop the prefast errors + // from being emitted. +#if defined(_PREFAST_) + *ptrResult = nullptr; + return err_policy::HResult(E_NOINTERFACE); +#else + return err_policy::HResult(query_policy::query(m_ptr, riid, ptrResult)); +#endif + } + //! @} + + //! @name Try query helpers + //! * Attempts to retrieves the requested interface + //! * AV if the pointer is null + //! * Produce null if the requested interface is unsupported + //! * bool returns 'true' when query was successful + //! + //! See @ref page_query for more information. + //! @{ + + //! Attempt a query and return a smart pointer matching the interface specified by 'U': `auto foo = m_ptr.try_query();` (null result when interface is unsupported). + //! See @ref page_query for more information. + //! + //! This method can be used to query a com_ptr for an interface when it's known that support for that interface is + //! optional (failing the query should not produce an error). The caller must examine the returned pointer to see + //! if it's null before using it: + //! ~~~~ + //! auto foo = ptr.try_query(); + //! if (foo) + //! { + //! foo->Method1(); + //! foo->Method2(); + //! } + //! ~~~~ + //! @tparam U Represents the interface being queried + //! @return A `com_ptr_t` pointer to the given interface `U`. The returned pointer will be null if the interface is + //! not supported. The returned `com_ptr_t` will have the same error handling policy (exceptions, failfast or error codes) as + //! the pointer being queried. + template + inline com_ptr_t try_query() const + { + return com_ptr_t(m_ptr, details::tag_try_com_query()); + } + + //! Attempts to query for the interface matching the given output parameter; returns a bool indicating if the query was successful (non-null). + //! See @ref page_query for more information. + //! + //! This method can be used to perform a query against a non-null interface when it's known that support for that interface is + //! optional (failing the query should not produce an error). The caller must examine the returned bool before using the returned pointer. + //! ~~~~ + //! wil::com_ptr_nothrow foo; + //! if (ptr.try_query_to(&foo)) + //! { + //! foo->Method1(); + //! foo->Method2(); + //! } + //! ~~~~ + //! @param ptrResult The pointer to query for. The interface to query is deduced from the type of this out parameter; do not specify + //! the type directly to the template. + //! @return A `bool` indicating `true` of the query was successful (the returned parameter is non-null). + template + _Success_return_ bool try_query_to(_COM_Outptr_ U** ptrResult) const + { + return SUCCEEDED(query_policy::query(m_ptr, ptrResult)); + } + + //! Attempts a query for the requested interface using the iid, ppv pattern: `ptr.try_query_to(riid, ptr);`. + //! See @ref page_query for more information. + //! + //! This method is built to implement an API boundary that exposes a returned pointer to a caller through the REFIID and void** pointer + //! pattern (like QueryInterface). The key distinction is that this routine does not produce an error if the request isn't fulfilled, so + //! it's appropriate for `_COM_Outptr_result_maybenull_` cases. This pattern should not be used outside of that pattern (through IID_PPV_ARGS) as + //! it is less efficient than the typed version of @ref try_query_to which can elide the QueryInterface in favor of AddRef when the types are convertible. + //! The caller must examine the returned bool before using the returned pointer. + //! ~~~~ + //! // class member being queried: + //! wil::com_ptr_nothrow m_ptr; + //! + //! // output parameter example (result may be null): + //! HRESULT GetFoo(REFIID riid, _COM_Outptr_result_maybenull_ void** ptrResult) + //! { + //! m_ptr.try_query_to(riid, ptrResult); + //! return S_OK; + //! } + //! ~~~~ + //! @param riid The interface to query for. + //! @param ptrResult The output pointer that will receive the newly queried interface. This pointer will be assigned null on failure. + //! @return A `bool` indicating `true` of the query was successful (the returned parameter is non-null). + _Success_return_ bool try_query_to(REFIID riid, _COM_Outptr_ void** ptrResult) const + { + return SUCCEEDED(query_policy::query(m_ptr, riid, ptrResult)); + } + //! @} + + //! @name Copy helpers + //! * Retrieves the requested interface + //! * Succeeds with null if the pointer is null + //! * Produce an error if the requested interface is unsupported + //! + //! See @ref page_query for more information. + //! @{ + + //! Query and return a smart pointer matching the interface specified by 'U': `auto foo = m_ptr.copy();` (succeeds and returns a null ptr if the queried pointer is null). + //! See @ref page_query for more information. + //! + //! This method is identical to @ref query with the exception that it can be used when the pointer is null. When used + //! against a null pointer, the returned pointer will always be null and an error will not be produced. Like query it will + //! produce an error for a non-null pointer that does not support the requested interface. + //! @tparam U Represents the interface being queried + //! @return A `com_ptr_t` pointer to the given interface `U`. The pointer will be null ONLY if the pointer being queried is null. The returned + //! `com_ptr_t` type will be @ref com_ptr or @ref com_ptr_failfast (matching the error handling form of the + //! pointer being queried (exception based or fail-fast). + template + inline com_ptr_t copy() const + { + static_assert(wistd::is_same::value, "copy requires exceptions or fail fast; use the try_copy or copy_to method"); + return com_ptr_t(m_ptr, details::tag_com_copy()); + } + + //! Query for the interface of the given out parameter `U`: `ptr.copy_to(&foo);` (succeeds and returns null ptr if the queried pointer is null). + //! See @ref page_query for more information. + //! + //! This method is identical to @ref query_to with the exception that it can be used when the pointer is null. When used + //! against a null pointer, the returned pointer will always be null and an error will not be produced. Like query_to it will + //! produce an error for a non-null pointer that does not support the requested interface. + //! @tparam U Represents the interface being queried (type of the output parameter). This interface does not need to + //! be specified directly. Rely upon template type deduction to pick up the type from the output parameter. + //! @param ptrResult The output pointer that will receive the newly queried interface. This pointer will be assigned null on failure OR assigned null + //! when the source pointer is null. + //! @return For the nothrow (error code-based) classes (@ref com_ptr_nothrow, @ref com_weak_ref_nothrow, @ref com_agile_ref_nothrow) this + //! method returns an `HRESULT` indicating whether the query was successful. Copying a null value is considered success. Exception-based + //! and fail-fast based classes do not return a value (void). + template + result copy_to(_COM_Outptr_result_maybenull_ U** ptrResult) const + { + if (m_ptr) + { + // Prefast cannot see through the error policy + query_policy mapping and as a result and as a result fires 6388 and 28196 for this function. + // Suppression is also not working. Wrapping this entire function in #pragma warning(disable: 6388 28196) does not stop the prefast errors + // from being emitted. +#if defined(_PREFAST_) + *ptrResult = nullptr; + return err_policy::HResult(E_NOINTERFACE); +#else + return err_policy::HResult(query_policy::query(m_ptr, ptrResult)); +#endif + } + *ptrResult = nullptr; + return err_policy::OK(); + } + + //! Query for the requested interface using the iid, ppv pattern: `ptr.copy_to(riid, ptr);`. (succeeds and returns null ptr if the queried pointer is null). + //! See @ref page_query for more information. + //! + //! Identical to the corresponding @ref query_to method with the exception that it can be used when the pointer is null. When used + //! against a null pointer, the returned pointer will always be null and an error will not be produced. Like query_to it will + //! produce an error for a non-null pointer that does not support the requested interface. + //! @param riid The interface to query for. + //! @param ptrResult The output pointer that will receive the newly queried interface. This pointer will be assigned null on failure OR assigned null + //! when the source pointer is null. + //! @return For the nothrow (error code-based) classes (@ref com_ptr_nothrow, @ref com_weak_ref_nothrow, @ref com_agile_ref_nothrow) this + //! method returns an `HRESULT` indicating whether the query was successful. Copying a null value is considered success. Exception-based + //! and fail-fast based classes do not return a value (void). + result copy_to(REFIID riid, _COM_Outptr_result_maybenull_ void** ptrResult) const + { + if (m_ptr) + { + // Prefast cannot see through the error policy + query_policy mapping and as a result and as a result fires 6388 and 28196 for this function. + // Suppression is also not working. Wrapping this entire function in #pragma warning(disable: 6388 28196) does not stop the prefast errors + // from being emitted. +#if defined(_PREFAST_) + *ptrResult = nullptr; + return err_policy::HResult(E_NOINTERFACE); +#else + return err_policy::HResult(query_policy::query(m_ptr, riid, ptrResult)); +#endif + } + *ptrResult = nullptr; + return err_policy::OK(); + } + //! @} + + //! @name Try copy helpers + //! * Attempts to retrieves the requested interface + //! * Successfully produces null if the queried pointer is already null + //! * Produce null if the requested interface is unsupported + //! * bool returns 'false' ONLY when the queried pointer is not null and the requested interface is unsupported + //! + //! See @ref page_query for more information. + //! @{ + + //! Attempt a query and return a smart pointer matching the interface specified by 'U': `auto foo = m_ptr.try_query();` (null result when interface is unsupported or queried pointer is null). + //! See @ref page_query for more information. + //! + //! Identical to the corresponding @ref try_query method with the exception that it can be used when the pointer is null. When used + //! against a null pointer, the returned pointer will always be null and an error will not be produced. + //! @tparam U Represents the interface being queried + //! @return A `com_ptr_t` pointer to the given interface `U`. The returned pointer will be null if the interface was + //! not supported or the pointer being queried is null. The returned `com_ptr_t` will have the same error handling + //! policy (exceptions, failfast or error codes) as the pointer being queried. + template + inline com_ptr_t try_copy() const + { + return com_ptr_t(m_ptr, details::tag_try_com_copy()); + } + + //! Attempts to query for the interface matching the given output parameter; returns a bool indicating if the query was successful (returns `false` if the pointer is null). + //! See @ref page_query for more information. + //! + //! Identical to the corresponding @ref try_query_to method with the exception that it can be used when the pointer is null. When used + //! against a null pointer, the returned pointer will be null and the return value will be `false`. + //! @param ptrResult The pointer to query for. The interface to query is deduced from the type of this out parameter; do not specify + //! the type directly to the template. + //! @return A `bool` indicating `true` of the query was successful (the returned parameter is non-null). + template + _Success_return_ bool try_copy_to(_COM_Outptr_result_maybenull_ U** ptrResult) const + { + if (m_ptr) + { + return SUCCEEDED(query_policy::query(m_ptr, ptrResult)); + } + *ptrResult = nullptr; + return false; + } + + //! Attempts a query for the requested interface using the iid, ppv pattern: `ptr.try_query_to(riid, ptr);` (returns `false` if the pointer is null) + //! See @ref page_query for more information. + //! + //! Identical to the corresponding @ref try_query_to method with the exception that it can be used when the pointer is null. When used + //! against a null pointer, the returned pointer will be null and the return value will be `false`. + //! @param riid The interface to query for. + //! @param ptrResult The output pointer that will receive the newly queried interface. This pointer will be assigned null on failure or + //! if the source pointer being queried is null. + //! @return A `bool` indicating `true` of the query was successful (the returned parameter is non-null). Querying a null + //! pointer will return `false` with a null result. + _Success_return_ bool try_copy_to(REFIID riid, _COM_Outptr_result_maybenull_ void** ptrResult) const + { + if (m_ptr) + { + return SUCCEEDED(query_policy::query(m_ptr, riid, ptrResult)); + } + *ptrResult = nullptr; + return false; + } + //! @} + + //! @name WRL compatibility + //! @{ + + //! Copy construct from a compatible WRL ComPtr. + template > + com_ptr_t(const Microsoft::WRL::ComPtr& other) WI_NOEXCEPT : + com_ptr_t(static_cast(other.Get())) + { + } + + //! Move construct from a compatible WRL ComPtr. + template > + com_ptr_t(Microsoft::WRL::ComPtr&& other) WI_NOEXCEPT : + m_ptr(other.Detach()) + { + } + + //! Assign from a compatible WRL ComPtr. + template > + com_ptr_t& operator=(const Microsoft::WRL::ComPtr& other) WI_NOEXCEPT + { + return operator=(static_cast(other.Get())); + } + + //! Move assign from a compatible WRL ComPtr. + template > + com_ptr_t& operator=(Microsoft::WRL::ComPtr&& other) WI_NOEXCEPT + { + attach(other.Detach()); + return *this; + } + + //! Swap pointers with a WRL ComPtr to the same interface. + void swap(Microsoft::WRL::ComPtr& other) WI_NOEXCEPT + { + auto ptr = m_ptr; + m_ptr = other.Detach(); + other.Attach(ptr); + } + + //! Swap pointers with a rvalue reference to a WRL ComPtr to the same interface. + void swap(Microsoft::WRL::ComPtr&& other) WI_NOEXCEPT + { + swap(other); + } + //! @} // WRL compatibility + + public: + // Internal Helpers + /// @cond + template + inline com_ptr_t(_In_ U* ptr, details::tag_com_query) : m_ptr(nullptr) + { + err_policy::HResult(details::query_policy_t::query(ptr, &m_ptr)); + } + + template + inline com_ptr_t(_In_ U* ptr, details::tag_try_com_query) WI_NOEXCEPT : m_ptr(nullptr) + { + details::query_policy_t::query(ptr, &m_ptr); + } + + template + inline com_ptr_t(_In_opt_ U* ptr, details::tag_com_copy) : m_ptr(nullptr) + { + if (ptr) + { + err_policy::HResult(details::query_policy_t::query(ptr, &m_ptr)); + } + } + + template + inline com_ptr_t(_In_opt_ U* ptr, details::tag_try_com_copy) WI_NOEXCEPT : m_ptr(nullptr) + { + if (ptr) + { + details::query_policy_t::query(ptr, &m_ptr); + } + } + /// @endcond + + private: + pointer m_ptr; + }; + + // Error-policy driven forms of com_ptr + +#ifdef WIL_ENABLE_EXCEPTIONS + //! COM pointer, errors throw exceptions (see @ref com_ptr_t for details) + template + using com_ptr = com_ptr_t; +#endif + + //! COM pointer, errors return error codes (see @ref com_ptr_t for details) + template + using com_ptr_nothrow = com_ptr_t; + + //! COM pointer, errors fail-fast (see @ref com_ptr_t for details) + template + using com_ptr_failfast = com_ptr_t; + + + // Global operators / swap + + //! Swaps the given com pointers that have different error handling. + //! Note that there are also corresponding versions to allow you to swap any wil com_ptr with a WRL ComPtr. + template + inline void swap(com_ptr_t& left, com_ptr_t& right) WI_NOEXCEPT + { + left.swap(right); + } + + //! Swaps the given com pointers that have the same error handling. + template + inline void swap(com_ptr_t& left, com_ptr_t& right) WI_NOEXCEPT + { + left.swap(right); + } + + //! Compare two com pointers. + //! Compares the two raw com pointers for equivalence. Does NOT compare object identity with a QI for IUnknown. + //! + //! Note that documentation for all of the various comparators has not been generated to reduce global function + //! clutter, but ALL standard comparison operators are supported between wil com_ptr objects, nullptr_t, and + //! WRL ComPtr. + template + inline bool operator==(const com_ptr_t& left, const com_ptr_t& right) WI_NOEXCEPT + { + static_assert(__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*), "comparison operator requires left and right pointers to be compatible"); + return (left.get() == right.get()); + } + + // We don't document all of the global comparison operators (reduce clutter) + /// @cond + template + inline bool operator<(const com_ptr_t& left, const com_ptr_t& right) WI_NOEXCEPT + { + static_assert(__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*), "comparison operator requires left and right pointers to be compatible"); + return (left.get() < right.get()); + } + + template + inline bool operator==(const com_ptr_t& left, wistd::nullptr_t) WI_NOEXCEPT + { + return (left.get() == nullptr); + } + + template + inline bool operator!=(const com_ptr_t& left, const com_ptr_t& right) WI_NOEXCEPT + { return (!(left == right)); } + + template + inline bool operator>=(const com_ptr_t& left, const com_ptr_t& right) WI_NOEXCEPT + { return (!(left < right)); } + + template + inline bool operator>(const com_ptr_t& left, const com_ptr_t& right) WI_NOEXCEPT + { return (right < left); } + + template + inline bool operator<=(const com_ptr_t& left, const com_ptr_t& right) WI_NOEXCEPT + { return (!(right < left)); } + + template + inline bool operator==(wistd::nullptr_t, const com_ptr_t& right) WI_NOEXCEPT + { + return (right.get() == nullptr); + } + + template + inline bool operator!=(const com_ptr_t& left, wistd::nullptr_t) WI_NOEXCEPT + { return (!(left == nullptr)); } + + template + inline bool operator!=(wistd::nullptr_t, const com_ptr_t& right) WI_NOEXCEPT + { return (!(right == nullptr)); } + + // WRL ComPtr support + + template + inline void swap(com_ptr_t& left, Microsoft::WRL::ComPtr& right) WI_NOEXCEPT + { + left.swap(right); + } + + template + inline bool operator==(const com_ptr_t& left, const Microsoft::WRL::ComPtr& right) WI_NOEXCEPT + { + static_assert(__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*), "comparison operator requires left and right pointers to be compatible"); + return (left.get() == right.Get()); + } + + template + inline bool operator<(const com_ptr_t& left, const Microsoft::WRL::ComPtr& right) WI_NOEXCEPT + { + static_assert(__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*), "comparison operator requires left and right pointers to be compatible"); + return (left.get() < right.Get()); + } + + template + inline bool operator!=(const com_ptr_t& left, const Microsoft::WRL::ComPtr& right) WI_NOEXCEPT + { return (!(left == right)); } + + template + inline bool operator>=(const com_ptr_t& left, const Microsoft::WRL::ComPtr& right) WI_NOEXCEPT + { return (!(left < right)); } + + template + inline bool operator>(const com_ptr_t& left, const Microsoft::WRL::ComPtr& right) WI_NOEXCEPT + { return (right < left); } + + template + inline bool operator<=(const com_ptr_t& left, const Microsoft::WRL::ComPtr& right) WI_NOEXCEPT + { return (!(right < left)); } + + template + inline void swap(Microsoft::WRL::ComPtr& left, com_ptr_t& right) WI_NOEXCEPT + { + right.swap(left); + } + + template + inline bool operator==(const Microsoft::WRL::ComPtr& left, const com_ptr_t& right) WI_NOEXCEPT + { + static_assert(__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*), "comparison operator requires left and right pointers to be compatible"); + return (left.Get() == right.get()); + } + + template + inline bool operator<(const Microsoft::WRL::ComPtr& left, const com_ptr_t& right) WI_NOEXCEPT + { + static_assert(__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*), "comparison operator requires left and right pointers to be compatible"); + return (left.Get() < right.get()); + } + + template + inline bool operator!=(const Microsoft::WRL::ComPtr& left, const com_ptr_t& right) WI_NOEXCEPT + { return (!(left == right)); } + + template + inline bool operator>=(const Microsoft::WRL::ComPtr& left, const com_ptr_t& right) WI_NOEXCEPT + { return (!(left < right)); } + + template + inline bool operator>(const Microsoft::WRL::ComPtr& left, const com_ptr_t& right) WI_NOEXCEPT + { return (right < left); } + + template + inline bool operator<=(const Microsoft::WRL::ComPtr& left, const com_ptr_t& right) WI_NOEXCEPT + { return (!(right < left)); } + + // raw COM pointer support + // + // Use these for convenience and to avoid unnecessary AddRef/Release cyles when using raw + // pointers to access STL containers. Specify std::less<> to benefit from operator<. + // + // Example: std::set, std::less<>> set; + + template + inline bool operator==(const com_ptr_t& left, TRight* right) WI_NOEXCEPT + { + static_assert(__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*), "comparison operator requires left and right pointers to be compatible"); + return (left.get() == right); + } + + template + inline bool operator<(const com_ptr_t& left, TRight* right) WI_NOEXCEPT + { + static_assert(__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*), "comparison operator requires left and right pointers to be compatible"); + return (left.get() < right); + } + + template + inline bool operator!=(const com_ptr_t& left, TRight* right) WI_NOEXCEPT + { return (!(left == right)); } + + template + inline bool operator>=(const com_ptr_t& left, TRight* right) WI_NOEXCEPT + { return (!(left < right)); } + + template + inline bool operator>(const com_ptr_t& left, TRight* right) WI_NOEXCEPT + { return (right < left); } + + template + inline bool operator<=(const com_ptr_t& left, TRight* right) WI_NOEXCEPT + { return (!(right < left)); } + + template + inline bool operator==(TLeft* left, const com_ptr_t& right) WI_NOEXCEPT + { + static_assert(__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*), "comparison operator requires left and right pointers to be compatible"); + return (left == right.get()); + } + + template + inline bool operator<(TLeft* left, const com_ptr_t& right) WI_NOEXCEPT + { + static_assert(__is_convertible_to(TLeft*, TRight*) || __is_convertible_to(TRight*, TLeft*), "comparison operator requires left and right pointers to be compatible"); + return (left < right.get()); + } + + template + inline bool operator!=(TLeft* left, const com_ptr_t& right) WI_NOEXCEPT + { return (!(left == right)); } + + template + inline bool operator>=(TLeft* left, const com_ptr_t& right) WI_NOEXCEPT + { return (!(left < right)); } + + template + inline bool operator>(TLeft* left, const com_ptr_t& right) WI_NOEXCEPT + { return (right < left); } + + template + inline bool operator<=(TLeft* left, const com_ptr_t& right) WI_NOEXCEPT + { return (!(right < left)); } + + // suppress documentation of every single comparison operator + /// @endcond + + + //! An overloaded function that retrieves the raw com pointer from a raw pointer, wil::com_ptr_t, WRL ComPtr, or Platform::Object^. + //! This function is primarily useful by library or helper code. It allows code to be written to accept a forwarding reference + //! template that can be used as an input com pointer. That input com pointer is allowed to be any of: + //! * Raw Pointer: `T* com_raw_ptr(T* ptr)` + //! * Wil com_ptr: `T* com_raw_ptr(const wil::com_ptr_t& ptr)` + //! * WRL ComPtr: `T* com_raw_ptr(const Microsoft::WRL::ComPtr& ptr)` + //! * C++/CX hat: `IInspectable* com_raw_ptr(Platform::Object^ ptr)` + //! + //! Which in turn allows code like the following to be written: + //! ~~~~ + //! template + //! void com_query_to(T&& ptrSource, _COM_Outptr_ U** ptrResult) + //! { + //! auto raw = com_raw_ptr(wistd::forward(ptrSource)); + //! // decltype(raw) has the type of the inner pointer and raw is guaranteed to be a raw com pointer + //! ~~~~ + template + T* com_raw_ptr(T* ptr) + { + return ptr; + } + + /// @cond + template + T* com_raw_ptr(const wil::com_ptr_t& ptr) + { + return ptr.get(); + } + + template + T* com_raw_ptr(const Microsoft::WRL::ComPtr& ptr) + { + return ptr.Get(); + } + +#ifdef __cplusplus_winrt + + template + inline IInspectable* com_raw_ptr(T^ ptr) + { + return reinterpret_cast(static_cast<::Platform::Object^>(ptr)); + } + +#endif + /// @endcond + + + //! @name Stand-alone query helpers + //! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr + //! * Retrieves the requested interface + //! * AV if the source pointer is null + //! * Produce an error if the requested interface is unsupported + //! + //! See @ref page_query for more information + //! @{ + +#ifdef WIL_ENABLE_EXCEPTIONS + //! Queries for the specified interface and returns an exception-based wil::com_ptr to that interface (exception if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null + //! @tparam U Represents the interface being queried + //! @return A `wil::com_ptr` pointer to the given interface `U`. The returned pointer is guaranteed not null. + template + inline com_ptr com_query(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return com_ptr(raw, details::tag_com_query()); + } +#endif + + //! Queries for the specified interface and returns a fail-fast-based wil::com_ptr_failfast to that interface (fail-fast if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null + //! @tparam U Represents the interface being queried + //! @return A `wil::com_ptr` pointer to the given interface `U`. The returned pointer is guaranteed not null. + template + inline com_ptr_failfast com_query_failfast(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return com_ptr_failfast(raw, details::tag_com_query()); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + //! Queries for the interface specified by the type of the output parameter (throws an exception if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null + //! @param ptrResult Represents the output pointer to populate. The returned pointer is guaranteed not null. + template + _Success_true_ void com_query_to(T&& ptrSource, _COM_Outptr_ U** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + THROW_IF_FAILED(details::query_policy_t::query(raw, ptrResult)); + __analysis_assume(*ptrResult != nullptr); + } +#endif + + //! Queries for the interface specified by the type of the output parameter (fail-fast if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null + //! @param ptrResult Represents the output pointer to populate. The returned pointer is guaranteed not null. + template + _Success_true_ void com_query_to_failfast(T&& ptrSource, _COM_Outptr_ U** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + FAIL_FAST_IF_FAILED(details::query_policy_t::query(raw, ptrResult)); + __analysis_assume(*ptrResult != nullptr); + } + + //! Queries for the interface specified by the type of the output parameter (returns an error if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null + //! @param ptrResult Represents the output pointer to populate. The returned pointer will be null upon failure. + //! @return Returns an HRESULT representing whether the query succeeded. + template + HRESULT com_query_to_nothrow(T&& ptrSource, _COM_Outptr_ U** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + auto hr = details::query_policy_t::query(raw, ptrResult); + __analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr)); + RETURN_HR(hr); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + //! Queries for the interface specified by the given REFIID parameter (throws an exception if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null + //! @param riid The interface to query for + //! @param ptrResult Represents the output pointer to populate. The returned pointer is guaranteed not null. + template + _Success_true_ void com_query_to(T&& ptrSource, REFIID riid, _COM_Outptr_ void** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + THROW_IF_FAILED(details::query_policy_t::query(raw, riid, ptrResult)); + __analysis_assume(*ptrResult != nullptr); + } +#endif + + //! Queries for the interface specified by the given REFIID parameter (fail-fast if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null + //! @param riid The interface to query for + //! @param ptrResult Represents the output pointer to populate. The returned pointer is guaranteed not null. + template + _Success_true_ void com_query_to_failfast(T&& ptrSource, REFIID riid, _COM_Outptr_ void** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + FAIL_FAST_IF_FAILED(details::query_policy_t::query(raw, riid, ptrResult)); + __analysis_assume(*ptrResult != nullptr); + } + + //! Queries for the interface specified by the given REFIID parameter (returns an error if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null + //! @param riid The interface to query for + //! @param ptrResult Represents the output pointer to populate. The returned pointer will be null upon failure. + template + HRESULT com_query_to_nothrow(T&& ptrSource, REFIID riid, _COM_Outptr_ void** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + auto hr = details::query_policy_t::query(raw, riid, ptrResult); + __analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr)); + RETURN_HR(hr); + } + //! @} + + //! @name Stand-alone try query helpers + //! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr + //! * Attempts to retrieves the requested interface + //! * AV if the source pointer is null + //! * Produce null if the requested interface is unsupported + //! * bool returns 'true' when query was successful (non-null return result) + //! + //! See @ref page_query for more information. + //! @{ + +#ifdef WIL_ENABLE_EXCEPTIONS + //! Attempts a query for the specified interface and returns an exception-based wil::com_ptr to that interface (returns null if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null + //! @tparam U Represents the interface being queried + //! @return A `wil::com_ptr` pointer to the given interface `U`. The returned pointer is null if the requested interface was not supported. + template + inline com_ptr try_com_query(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return com_ptr(raw, details::tag_try_com_query()); + } +#endif + + //! Attempts a query for the specified interface and returns an fail-fast wil::com_ptr_failfast to that interface (returns null if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null + //! @tparam U Represents the interface being queried + //! @return A `wil::com_ptr_failfast` pointer to the given interface `U`. The returned pointer is null if the requested interface was not supported. + template + inline com_ptr_failfast try_com_query_failfast(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return com_ptr_failfast(raw, details::tag_try_com_query()); + } + + //! Attempts a query for the specified interface and returns an error-code-based wil::com_ptr_nothrow to that interface (returns null if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null + //! @tparam U Represents the interface being queried + //! @return A `wil::com_ptr_nothrow` pointer to the given interface `U`. The returned pointer is null if the requested interface was not supported. + template + inline com_ptr_nothrow try_com_query_nothrow(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return com_ptr_nothrow(raw, details::tag_try_com_query()); + } + + //! Attempts a query for the interface specified by the type of the output parameter (returns `false` if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null. + //! @param ptrResult Represents the output pointer to populate. If the interface is unsupported, the returned pointer will be null. + //! @return A bool value representing whether the query was successful (non-null return result). + template + _Success_return_ bool try_com_query_to(T&& ptrSource, _COM_Outptr_ U** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return (SUCCEEDED(details::query_policy_t::query(raw, ptrResult))); + } + + //! Attempts a query for the interface specified by the type of the output parameter (returns `false` if unsupported). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), should not be null. + //! @param riid The interface to query for + //! @param ptrResult Represents the output pointer to populate. If the interface is unsupported, the returned pointer will be null. + //! @return A bool value representing whether the query was successful (non-null return result). + template + _Success_return_ bool try_com_query_to(T&& ptrSource, REFIID riid, _COM_Outptr_ void** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return (SUCCEEDED(details::query_policy_t::query(raw, riid, ptrResult))); + } + //! @} + + + //! @name Stand-alone copy helpers + //! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr + //! * Retrieves the requested interface + //! * Succeeds with null if the source pointer is null + //! * Produce an error if the requested interface is unsupported + //! + //! See @ref page_query for more information + //! @{ + +#ifdef WIL_ENABLE_EXCEPTIONS + //! Queries for the specified interface and returns an exception-based wil::com_ptr to that interface (exception if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null + //! @tparam U Represents the interface being queried + //! @return A `wil::com_ptr` pointer to the given interface `U`. The returned pointer will be null only if the source is null. + template + inline com_ptr com_copy(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return com_ptr(raw, details::tag_com_copy()); + } +#endif + + //! Queries for the specified interface and returns a fail-fast-based wil::com_ptr_failfast to that interface (fail-fast if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null + //! @tparam U Represents the interface being queried + //! @return A `wil::com_ptr` pointer to the given interface `U`. The returned pointer will be null only if the source is null. + template + inline com_ptr_failfast com_copy_failfast(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return com_ptr_failfast(raw, details::tag_com_copy()); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + //! Queries for the interface specified by the type of the output parameter (throws an exception if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null + //! @param ptrResult Represents the output pointer to populate. The returned pointer will be null only if the source is null. + template + _Success_true_ void com_copy_to(T&& ptrSource, _COM_Outptr_result_maybenull_ U** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + if (raw) + { + THROW_IF_FAILED(details::query_policy_t::query(raw, ptrResult)); + return; + } + *ptrResult = nullptr; + } +#endif + + //! Queries for the interface specified by the type of the output parameter (fail-fast if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null + //! @param ptrResult Represents the output pointer to populate. The returned pointer will be null only if the source is null. + template + _Success_true_ void com_copy_to_failfast(T&& ptrSource, _COM_Outptr_result_maybenull_ U** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + if (raw) + { + FAIL_FAST_IF_FAILED(details::query_policy_t::query(raw, ptrResult)); + return; + } + *ptrResult = nullptr; + } + + //! Queries for the interface specified by the type of the output parameter (returns an error if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null + //! @param ptrResult Represents the output pointer to populate. The returned pointer will be null upon failure or if the source is null. + //! @return Returns an HRESULT representing whether the query succeeded (returns S_OK if the source is null). + template + HRESULT com_copy_to_nothrow(T&& ptrSource, _COM_Outptr_result_maybenull_ U** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + if (raw) + { + RETURN_HR(details::query_policy_t::query(raw, ptrResult)); + } + *ptrResult = nullptr; + return S_OK; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + //! Queries for the interface specified by the given REFIID parameter (throws an exception if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null + //! @param riid The interface to query for + //! @param ptrResult Represents the output pointer to populate. The returned pointer will be null only if the source is null. + template + _Success_true_ void com_copy_to(T&& ptrSource, REFIID riid, _COM_Outptr_result_maybenull_ void** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + if (raw) + { + THROW_IF_FAILED(details::query_policy_t::query(raw, riid, ptrResult)); + return; + } + *ptrResult = nullptr; + } +#endif + + //! Queries for the interface specified by the given REFIID parameter (fail-fast if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null + //! @param riid The interface to query for + //! @param ptrResult Represents the output pointer to populate. The returned pointer will be null only if the source is null. + template + _Success_true_ void com_copy_to_failfast(T&& ptrSource, REFIID riid, _COM_Outptr_result_maybenull_ void** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + if (raw) + { + FAIL_FAST_IF_FAILED(details::query_policy_t::query(raw, riid, ptrResult)); + return; + } + *ptrResult = nullptr; + } + + //! Queries for the interface specified by the given REFIID parameter (returns an error if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null + //! @param riid The interface to query for + //! @param ptrResult Represents the output pointer to populate. The returned pointer will be null upon failure or if the source is null. + //! @return Returns an HRESULT representing whether the query succeeded (returns S_OK if the source is null). + template + HRESULT com_copy_to_nothrow(T&& ptrSource, REFIID riid, _COM_Outptr_result_maybenull_ void** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + if (raw) + { + RETURN_HR(details::query_policy_t::query(raw, riid, ptrResult)); + } + *ptrResult = nullptr; + return S_OK; + } + //! @} + + + //! @name Stand-alone try copy helpers + //! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr + //! * Attempts to retrieves the requested interface + //! * Succeeds with null if the source pointer is null + //! * Produce null if the requested interface is unsupported + //! * bool returns 'true' when query was successful (non-null return result) + //! + //! See @ref page_query for more information. + //! @{ + +#ifdef WIL_ENABLE_EXCEPTIONS + //! Attempts a query for the specified interface and returns an exception-based wil::com_ptr to that interface (returns null if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null + //! @tparam U Represents the interface being queried + //! @return A `wil::com_ptr` pointer to the given interface `U`. The returned pointer is null if the requested interface was not supported. + template + inline com_ptr try_com_copy(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return com_ptr(raw, details::tag_try_com_copy()); + } +#endif + + //! Attempts a query for the specified interface and returns an fail-fast wil::com_ptr_failfast to that interface (returns null if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null + //! @tparam U Represents the interface being queried + //! @return A `wil::com_ptr_failfast` pointer to the given interface `U`. The returned pointer is null if the requested interface was not supported. + template + inline com_ptr_failfast try_com_copy_failfast(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return com_ptr_failfast(raw, details::tag_try_com_copy()); + } + + //! Attempts a query for the specified interface and returns an error-code-based wil::com_ptr_nothrow to that interface (returns null if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null + //! @tparam U Represents the interface being queried + //! @return A `wil::com_ptr_nothrow` pointer to the given interface `U`. The returned pointer is null if the requested interface was not supported. + template + inline com_ptr_nothrow try_com_copy_nothrow(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + return com_ptr_nothrow(raw, details::tag_try_com_copy()); + } + + //! Attempts a query for the interface specified by the type of the output parameter (returns `false` if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null. + //! @param ptrResult Represents the output pointer to populate. If the interface is unsupported, the returned pointer will be null. + //! @return A bool value representing whether the query was successful (non-null return result). + template + _Success_return_ bool try_com_copy_to(T&& ptrSource, _COM_Outptr_result_maybenull_ U** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + if (raw) + { + return SUCCEEDED(details::query_policy_t::query(raw, ptrResult)); + } + *ptrResult = nullptr; + return false; + } + + //! Attempts a query for the interface specified by the type of the output parameter (returns `false` if unsupported, preserves null). + //! See @ref page_query for more information. + //! @param ptrSource The pointer to query (may be a raw interface pointer, wil com_ptr, or WRL ComPtr), may be null. + //! @param riid The interface to query for + //! @param ptrResult Represents the output pointer to populate. If the interface is unsupported, the returned pointer will be null. + //! @return A bool value representing whether the query was successful (non-null return result). + template + _Success_return_ bool try_com_copy_to(T&& ptrSource, REFIID riid, _COM_Outptr_result_maybenull_ void** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + if (raw) + { + return SUCCEEDED(details::query_policy_t::query(raw, riid, ptrResult)); + } + *ptrResult = nullptr; + return false; + } + //! @} + +#ifdef __cplusplus_winrt + //! @name Stand-alone helpers to query for CX ref ("hat") types from ABI COM types. + //! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr + //! * Retrieves the requested C++/CX interface or ref class. + //! * Preserves null if the source pointer is null + //! * Produce an error if the requested interface is unsupported + //! + //! See @ref page_query for more information + //! @{ + + template + ::Platform::Object^ cx_object_from_abi(T&& ptr) WI_NOEXCEPT + { + IInspectable* const inspectable = com_raw_ptr(wistd::forward(ptr)); + return reinterpret_cast<::Platform::Object^>(inspectable); + } + + template + inline U^ cx_safe_cast(T&& ptrSource) + { + return safe_cast(cx_object_from_abi(wistd::forward(ptrSource))); + } + + template + inline U^ cx_dynamic_cast(T&& ptrSource) WI_NOEXCEPT + { + return dynamic_cast(cx_object_from_abi(wistd::forward(ptrSource))); + } + //! @} +#endif + + + //***************************************************************************** + // Agile References + //***************************************************************************** + +#if (NTDDI_VERSION >= NTDDI_WINBLUE) +#ifdef WIL_ENABLE_EXCEPTIONS + //! Agile reference to a COM interface, errors throw exceptions (see @ref com_ptr_t and @ref com_agile_query for details) + using com_agile_ref = com_ptr; +#endif + //! Agile reference to a COM interface, errors return error codes (see @ref com_ptr_t and @ref com_agile_query_nothrow for details) + using com_agile_ref_nothrow = com_ptr_nothrow; + //! Agile reference to a COM interface, errors fail fast (see @ref com_ptr_t and @ref com_agile_query_failfast for details) + using com_agile_ref_failfast = com_ptr_failfast; + + //! @name Create agile reference helpers + //! * Attempts to retrieve an agile reference to the requested interface (see [RoGetAgileReference](https://msdn.microsoft.com/en-us/library/dn269839.aspx)) + //! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr + //! * `query` methods AV if the source pointer is null + //! * `copy` methods succeed with null if the source pointer is null + //! * Accept optional [AgileReferenceOptions](https://msdn.microsoft.com/en-us/library/dn269836.aspx) + //! + //! See @ref page_query for more information on resolving an agile ref + //! @{ + +#ifdef WIL_ENABLE_EXCEPTIONS + //! return a com_agile_ref representing the given source pointer (throws an exception on failure) + template + com_agile_ref com_agile_query(T&& ptrSource, AgileReferenceOptions options = AGILEREFERENCE_DEFAULT) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + com_agile_ref agileRef; + THROW_IF_FAILED(::RoGetAgileReference(options, __uuidof(raw), raw, &agileRef)); + return agileRef; + } +#endif + + //! return a com_agile_ref_failfast representing the given source pointer (fail-fast on failure) + template + com_agile_ref_failfast com_agile_query_failfast(T&& ptrSource, AgileReferenceOptions options = AGILEREFERENCE_DEFAULT) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + com_agile_ref_failfast agileRef; + FAIL_FAST_IF_FAILED(::RoGetAgileReference(options, __uuidof(raw), raw, &agileRef)); + return agileRef; + } + + //! return a com_agile_ref_nothrow representing the given source pointer (returns an HRESULT on failure) + template + HRESULT com_agile_query_nothrow(T&& ptrSource, _COM_Outptr_ IAgileReference** ptrResult, AgileReferenceOptions options = AGILEREFERENCE_DEFAULT) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + auto hr = ::RoGetAgileReference(options, __uuidof(raw), raw, ptrResult); + __analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr)); + RETURN_HR(hr); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + //! return a com_agile_ref representing the given source pointer (throws an exception on failure, source maybe null) + template + com_agile_ref com_agile_copy(T&& ptrSource, AgileReferenceOptions options = AGILEREFERENCE_DEFAULT) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + com_agile_ref agileRef; + if (raw) + { + THROW_IF_FAILED(::RoGetAgileReference(options, __uuidof(raw), raw, &agileRef)); + } + return agileRef; + } +#endif + + //! return a com_agile_ref_failfast representing the given source pointer (fail-fast on failure, source maybe null) + template + com_agile_ref_failfast com_agile_copy_failfast(T&& ptrSource, AgileReferenceOptions options = AGILEREFERENCE_DEFAULT) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + com_agile_ref_failfast agileRef; + if (raw) + { + FAIL_FAST_IF_FAILED(::RoGetAgileReference(options, __uuidof(raw), raw, &agileRef)); + } + return agileRef; + } + + //! return an agile ref (com_agile_ref_XXX or other representation) representing the given source pointer (return error on failure, source maybe null) + template + HRESULT com_agile_copy_nothrow(T&& ptrSource, _COM_Outptr_result_maybenull_ IAgileReference** ptrResult, AgileReferenceOptions options = AGILEREFERENCE_DEFAULT) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + if (raw) + { + RETURN_HR(::RoGetAgileReference(options, __uuidof(raw), raw, ptrResult)); + } + *ptrResult = nullptr; + return S_OK; + } + //! @} +#endif + + //***************************************************************************** + // Weak References + //***************************************************************************** + + namespace details + { + template + HRESULT GetWeakReference(T* ptr, _COM_Outptr_ IWeakReference** weakReference) + { + static_assert(!wistd::is_same::value, "Cannot get an IWeakReference to an IWeakReference"); + + *weakReference = nullptr; + com_ptr_nothrow source; + HRESULT hr = ptr->QueryInterface(IID_PPV_ARGS(&source)); + if (SUCCEEDED(hr)) + { + hr = source->GetWeakReference(weakReference); + } + return hr; + } + } + +#ifdef WIL_ENABLE_EXCEPTIONS + //! Weak reference to a COM interface, errors throw exceptions (see @ref com_ptr_t and @ref com_weak_query for details) + using com_weak_ref = com_ptr; +#endif + //! Weak reference to a COM interface, errors return error codes (see @ref com_ptr_t and @ref com_weak_query_nothrow for details) + using com_weak_ref_nothrow = com_ptr_nothrow; + //! Weak reference to a COM interface, errors fail fast (see @ref com_ptr_t and @ref com_weak_query_failfast for details) + using com_weak_ref_failfast = com_ptr_failfast; + + //! @name Create weak reference helpers + //! * Attempts to retrieve a weak reference to the requested interface (see WRL's similar [WeakRef](https://msdn.microsoft.com/en-us/library/br244853.aspx)) + //! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr + //! * `query` methods AV if the source pointer is null + //! * `copy` methods succeed with null if the source pointer is null + //! + //! See @ref page_query for more information on resolving a weak ref + //! @{ + +#ifdef WIL_ENABLE_EXCEPTIONS + //! return a com_weak_ref representing the given source pointer (throws an exception on failure) + template + com_weak_ref com_weak_query(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + com_weak_ref weakRef; + THROW_IF_FAILED(details::GetWeakReference(raw, &weakRef)); + return weakRef; + } +#endif + + //! return a com_weak_ref_failfast representing the given source pointer (fail-fast on failure) + template + com_weak_ref_failfast com_weak_query_failfast(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + com_weak_ref_failfast weakRef; + FAIL_FAST_IF_FAILED(details::GetWeakReference(raw, &weakRef)); + return weakRef; + } + + //! return a com_weak_ref_nothrow representing the given source pointer (returns an HRESULT on failure) + template + HRESULT com_weak_query_nothrow(T&& ptrSource, _COM_Outptr_ IWeakReference** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + auto hr = details::GetWeakReference(raw, ptrResult); + __analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr)); + RETURN_HR(hr); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + //! return a com_weak_ref representing the given source pointer (throws an exception on failure, source maybe null) + template + com_weak_ref com_weak_copy(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + com_weak_ref weakRef; + if (raw) + { + THROW_IF_FAILED(details::GetWeakReference(raw, &weakRef)); + } + return weakRef; + } +#endif + + //! return a com_weak_ref_failfast representing the given source pointer (fail-fast on failure, source maybe null) + template + com_weak_ref_failfast com_weak_copy_failfast(T&& ptrSource) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + com_weak_ref_failfast weakRef; + if (raw) + { + FAIL_FAST_IF_FAILED(details::GetWeakReference(raw, &weakRef)); + } + return weakRef; + } + + //! return a com_weak_ref_failfast representing the given source pointer (fail-fast on failure, source maybe null) + template + HRESULT com_weak_copy_nothrow(T&& ptrSource, _COM_Outptr_result_maybenull_ IWeakReference** ptrResult) + { + auto raw = com_raw_ptr(wistd::forward(ptrSource)); + if (raw) + { + RETURN_HR(details::GetWeakReference(raw, ptrResult)); + } + *ptrResult = nullptr; + return S_OK; + } + +#pragma region COM Object Helpers + + template + inline bool is_agile(T&& ptrSource) + { + wil::com_ptr_nothrow agileObject; + return SUCCEEDED(com_raw_ptr(wistd::forward(ptrSource))->QueryInterface(IID_PPV_ARGS(&agileObject))); + } + + /** constructs a COM object using an CLSID on a specific interface or IUnknown.*/ + template + wil::com_ptr_t CoCreateInstance(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER) + { + wil::com_ptr_t result; + error_policy::HResult(::CoCreateInstance(rclsid, nullptr, dwClsContext, IID_PPV_ARGS(&result))); + return result; + } + + /** constructs a COM object using the class as the identifier (that has an associated CLSID) on a specific interface or IUnknown. */ + template + wil::com_ptr_t CoCreateInstance(DWORD dwClsContext = CLSCTX_INPROC_SERVER) + { + return CoCreateInstance(__uuidof(Class), dwClsContext); + } + + /** constructs a COM object using an CLSID on a specific interface or IUnknown. */ + template + wil::com_ptr_failfast CoCreateInstanceFailFast(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER) WI_NOEXCEPT + { + return CoCreateInstance(rclsid, dwClsContext); + } + + /** constructs a COM object using the class as the identifier (that has an associated CLSID) on a specific interface or IUnknown. */ + template + wil::com_ptr_failfast CoCreateInstanceFailFast(DWORD dwClsContext = CLSCTX_INPROC_SERVER) WI_NOEXCEPT + { + return CoCreateInstanceFailFast(__uuidof(Class), dwClsContext); + } + + /** constructs a COM object using an CLSID on a specific interface or IUnknown. + Note, failures are reported as a null result, the HRESULT is lost. */ + template + wil::com_ptr_nothrow CoCreateInstanceNoThrow(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER) WI_NOEXCEPT + { + return CoCreateInstance(rclsid, dwClsContext); + } + + /** constructs a COM object using the class as the identifier (that has an associated CLSID) on a specific interface or IUnknown. + Note, failures are reported as a null result, the HRESULT is lost. */ + template + wil::com_ptr_nothrow CoCreateInstanceNoThrow(DWORD dwClsContext = CLSCTX_INPROC_SERVER) WI_NOEXCEPT + { + return CoCreateInstanceNoThrow(__uuidof(Class), dwClsContext); + } + + /** constructs a COM object class factory using an CLSID on IClassFactory or a specific interface. */ + template + wil::com_ptr_t CoGetClassObject(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER) + { + wil::com_ptr_t result; + error_policy::HResult(CoGetClassObject(rclsid, dwClsContext, nullptr, IID_PPV_ARGS(&result))); + return result; + } + + /** constructs a COM object class factory using the class as the identifier (that has an associated CLSID) + on IClassFactory or a specific interface. */ + template + wil::com_ptr_t CoGetClassObject(DWORD dwClsContext = CLSCTX_INPROC_SERVER) + { + return CoGetClassObject(__uuidof(Class), dwClsContext); + } + + /** constructs a COM object class factory using an CLSID on IClassFactory or a specific interface. */ + template + wil::com_ptr_failfast CoGetClassObjectFailFast(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER) + { + return CoGetClassObject(rclsid, dwClsContext); + } + + /** constructs a COM object class factory using the class as the identifier (that has an associated CLSID) + on IClassFactory or a specific interface. */ + template + wil::com_ptr_failfast CoGetClassObjectFailFast(DWORD dwClsContext = CLSCTX_INPROC_SERVER) + { + return CoGetClassObjectFailFast(__uuidof(Class), dwClsContext); + } + + /** constructs a COM object class factory using an CLSID on IClassFactory or a specific interface. + Note, failures are reported as a null result, the HRESULT is lost. */ + template + wil::com_ptr_nothrow CoGetClassObjectNoThrow(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER) + { + return CoGetClassObject(rclsid, dwClsContext); + } + + /** constructs a COM object class factory using the class as the identifier (that has an associated CLSID) + on IClassFactory or a specific interface. + Note, failures are reported as a null result, the HRESULT is lost. */ + template + wil::com_ptr_nothrow CoGetClassObjectNoThrow(DWORD dwClsContext = CLSCTX_INPROC_SERVER) + { + return CoGetClassObjectNoThrow(__uuidof(Class), dwClsContext); + } +#pragma endregion + +#pragma region Stream helpers + + /** Read data from a stream into a buffer. + Reads up to a certain number of bytes into a buffer. Returns the amount of data written, which + may be less than the amount requested if the stream ran out. + ~~~~ + IStream* source = // ... + ULONG dataBlob = 0; + size_t read = 0; + RETURN_IF_FAILED(wil::stream_read_partial_nothrow(source, &dataBlob, sizeof(dataBlob), &read)); + if (read != sizeof(dataBlob)) + { + // end of stream, probably + } + else if (dataBlob == 0x8675309) + { + DoThing(dataBlob); + } + ~~~~ + @param stream The stream from which to read at most `size` bytes. + @param data A buffer into which up to `size` bytes will be read + @param size The size, in bytes, of the buffer pointed to by `data` + @param wrote The amount, in bytes, of data read from `stream` into `data` + */ + inline HRESULT stream_read_partial_nothrow(_In_ ISequentialStream* stream, _Out_writes_bytes_to_(size, *wrote) void* data, unsigned long size, unsigned long *wrote) + { + RETURN_HR(stream->Read(data, size, wrote)); + } + + /** Read an exact number of bytes from a stream into a buffer. + Fails if the stream didn't read all the bytes requested. + ~~~~ + IStream* source = // ... + ULONG dataBlob = 0; + RETURN_IF_FAILED(wil::stream_read_nothrow(source, &dataBlob, sizeof(dataBlob))); + if (dataBlob == 0x8675309) + { + DoThing(dataBlob); + } + ~~~~ + @param stream The stream from which to read at most `size` bytes. + @param data A buffer into which up to `size` bytes will be read + @param size The size, in bytes, of the buffer pointed to by `data` + @return The underlying stream read result, or HRESULT_FROM_WIN32(ERROR_INVALID_DATA) if the stream + did not read the complete buffer. + */ + inline HRESULT stream_read_nothrow(_In_ ISequentialStream* stream, _Out_writes_bytes_all_(size) void* data, unsigned long size) + { + unsigned long didRead; + RETURN_IF_FAILED(stream_read_partial_nothrow(stream, data, size, &didRead)); + RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), didRead != size); + + return S_OK; + } + + /** Read from a stream into a POD type. + Fails if the stream didn't have enough bytes. + ~~~~ + IStream* source = // ... + MY_HEADER header{}; + RETURN_IF_FAILED(wil::stream_read_nothrow(source, &header)); + if (header.Version == 0x8675309) + { + ConsumeOldHeader(stream, header); + } + ~~~~ + @param stream The stream from which to read at most `size` bytes. + @param pThing The POD data type to read from the stream. + @return The underlying stream read result, or HRESULT_FROM_WIN32(ERROR_INVALID_DATA) if the stream + did not read the complete buffer. + */ + template HRESULT stream_read_nothrow(_In_ ISequentialStream* stream, _Out_ T* pThing) + { + static_assert(__is_pod(T), "Type must be POD."); + return stream_read_nothrow(stream, pThing, sizeof(T)); + } + + /** Write an exact number of bytes to a stream from a buffer. + Fails if the stream didn't read write the bytes requested. + ~~~~ + IStream* source = // ... + ULONG dataBlob = 0x8675309; + RETURN_IF_FAILED(wil::stream_write_nothrow(source, &dataBlob, sizeof(dataBlob))); + ~~~~ + @param stream The stream to which to write at most `size` bytes. + @param data A buffer from which up to `size` bytes will be read + @param size The size, in bytes, of the buffer pointed to by `data` + */ + inline HRESULT stream_write_nothrow(_In_ ISequentialStream* stream, _In_reads_bytes_(size) const void* data, unsigned long size) + { + unsigned long wrote; + RETURN_IF_FAILED(stream->Write(data, size, &wrote)); + RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), wrote != size); + + return S_OK; + } + + /** Write a POD type to a stream. + Fails if not all the bytes were written. + ~~~~ + IStream* source = // ... + MY_HEADER header { 0x8675309, HEADER_FLAG_1 | HEADER_FLAG_2 }; + RETURN_IF_FAILED(wil::stream_write_nothrow(source, header)); + + ULONGLONG value = 16; + RETURN_IF_FAILED(wil::stream_write_nothrow(source, value)); + ~~~~ + @param stream The stream to which to write `thing` + @param thing The POD data type to write to the stream. + */ + template inline HRESULT stream_write_nothrow(_In_ ISequentialStream* stream, const T& thing) + { + return stream_write_nothrow(stream, wistd::addressof(thing), sizeof(thing)); + } + + /** Retrieve the size of this stream, in bytes + ~~~~ + IStream* source = // ... + ULONGLONG size; + RETURN_IF_FAILED(wil::stream_size_nothrow(source, &size)); + RETURN_HR_IF(E_INVALIDARG, size > ULONG_MAX); + ~~~~ + @param stream The stream whose size is to be returned in `value` + @param value The size, in bytes, reported by `stream` + */ + inline HRESULT stream_size_nothrow(_In_ IStream* stream, _Out_ unsigned long long* value) + { + STATSTG st{}; + RETURN_IF_FAILED(stream->Stat(&st, STATFLAG_NONAME)); + *value = st.cbSize.QuadPart; + + return S_OK; + } + + /** Seek a stream to a relative offset or absolute position + ~~~~ + IStream* source = // ... + unsigned long long landed; + RETURN_IF_FAILED(wil::stream_seek_nothrow(source, 16, STREAM_SEEK_CUR, &landed)); + RETURN_IF_FAILED(wil::stream_seek_nothrow(source, -5, STREAM_SEEK_END)); + RETURN_IF_FAILED(wil::stream_seek_nothrow(source, LLONG_MAX, STREAM_SEEK_CUR)); + ~~~~ + @param stream The stream to seek + @param offset The position, in bytes from the current position, to seek + @param from The starting point from which to seek, from the STREAM_SEEK_* set of values + @param value Optionally recieves the new absolute position from the stream + */ + inline HRESULT stream_seek_nothrow(_In_ IStream* stream, long long offset, unsigned long from, _Out_opt_ unsigned long long* value = nullptr) + { + LARGE_INTEGER amount; + ULARGE_INTEGER landed{}; + amount.QuadPart = offset; + RETURN_IF_FAILED(stream->Seek(amount, from, value ? &landed : nullptr)); + assign_to_opt_param(value, landed.QuadPart); + + return S_OK; + } + + /** Seek a stream to an absolute offset + ~~~~ + IStream* source = // ... + RETURN_HR(wil::stream_set_position_nothrow(source, 16)); + ~~~~ + @param stream The stream whose size is to be returned in `value` + @param offset The position, in bytes from the start of the stream, to seek to + @param value Optionally recieves the new absolute position from the stream + */ + inline HRESULT stream_set_position_nothrow(_In_ IStream* stream, unsigned long long offset, _Out_opt_ unsigned long long* value = nullptr) + { + // IStream::Seek(..., _SET) interprets the first parameter as an unsigned value. + return stream_seek_nothrow(stream, static_cast(offset), STREAM_SEEK_SET, value); + } + + /** Seek a relative amount in a stream + ~~~~ + IStream* source = // ... + RETURN_IF_FAILED(wil::stream_seek_from_current_position_nothrow(source, -16)); + + ULONGLONG newPosition; + RETURN_IF_FAILED(wil::stream_seek_from_current_position_nothrow(source, 16, &newPosition)); + ~~~~ + @param stream The stream whose location is to be moved + @param amount The offset, in bytes, to seek the stream. + @param value Set to the new absolute steam position, in bytes + */ + inline HRESULT stream_seek_from_current_position_nothrow(_In_ IStream* stream, long long amount, _Out_opt_ unsigned long long* value = nullptr) + { + return stream_seek_nothrow(stream, amount, STREAM_SEEK_CUR, value); + } + + /** Determine the current byte position in the stream + ~~~~ + IStream* source = // ... + ULONGLONG currentPos; + RETURN_IF_FAILED(wil::stream_get_position_nothrow(source, ¤tPos)); + ~~~~ + @param stream The stream whose location is to be moved + @param position Set to the current absolute steam position, in bytes + */ + inline HRESULT stream_get_position_nothrow(_In_ IStream* stream, _Out_ unsigned long long* position) + { + return stream_seek_from_current_position_nothrow(stream, 0, position); + } + + /** Moves the stream to absolute position 0 + ~~~~ + IStream* source = // ... + RETURN_IF_FAILED(wil::stream_reset_nothrow(source)); + ~~~~ + @param stream The stream whose location is to be moved + */ + inline HRESULT stream_reset_nothrow(_In_ IStream* stream) + { + return stream_set_position_nothrow(stream, 0); + } + + /** Copy data from one stream to another, returning the final amount copied. + ~~~~ + IStream* source = // ... + IStream* target = // ... + ULONGLONG copied; + RETURN_IF_FAILED(wil::stream_copy_bytes_nothrow(source, target, sizeof(MyType), &copied)); + if (copied < sizeof(MyType)) + { + DoSomethingAboutPartialCopy(); + } + ~~~~ + @param source The stream from which to copy at most `amount` bytes + @param target The steam to which to copy at most `amount` bytes + @param amount The maximum number of bytes to copy from `source` to `target` + @param pCopied If non-null, set to the number of bytes copied between the two. + */ + inline HRESULT stream_copy_bytes_nothrow(_In_ IStream* source, _In_ IStream* target, unsigned long long amount, _Out_opt_ unsigned long long* pCopied = nullptr) + { + ULARGE_INTEGER toCopy; + ULARGE_INTEGER copied; + toCopy.QuadPart = amount; + RETURN_IF_FAILED(source->CopyTo(target, toCopy, nullptr, &copied)); + assign_to_opt_param(pCopied, copied.QuadPart); + + return S_OK; + } + + /** Copy all data from one stream to another, returning the final amount copied. + ~~~~ + IStream* source = // ... + IStream* target = // ... + ULONGLONG copied; + RETURN_IF_FAILED(wil::stream_copy_all_nothrow(source, target, &copied)); + if (copied < 8) + { + DoSomethingAboutPartialCopy(); + } + ~~~~ + @param source The stream from which to copy all content + @param target The steam to which to copy all content + @param pCopied If non-null, set to the number of bytes copied between the two. + */ + inline HRESULT stream_copy_all_nothrow(_In_ IStream* source, _In_ IStream* target, _Out_opt_ unsigned long long* pCopied = nullptr) + { + return stream_copy_bytes_nothrow(source, target, ULLONG_MAX, pCopied); + } + + /** Copies an exact amount of data from one stream to another, failing otherwise + ~~~~ + IStream* source = // ... + IStream* target = // ... + RETURN_IF_FAILED(wil::stream_copy_all_nothrow(source, target, 16)); + ~~~~ + @param source The stream from which to copy at most `amount` bytes + @param target The steam to which to copy at most `amount` bytes + @param amount The number of bytes to copy from `source` to `target` + */ + inline HRESULT stream_copy_exact_nothrow(_In_ IStream* source, _In_ IStream* target, unsigned long long amount) + { + unsigned long long copied; + RETURN_IF_FAILED(stream_copy_bytes_nothrow(source, target, ULLONG_MAX, &copied)); + RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), copied != amount); + + return S_OK; + } + + //! Controls behavior when reading a zero-length string from a stream + enum class empty_string_options + { + //! Zero-length strings are returned as nullptr + returns_null, + + //! Zero-length strings are allocated and returned with zero characters + returns_empty, + }; + +#ifdef __WIL_OBJBASE_H_ + + /** Read a string from a stream and returns an allocated copy + Deserializes strings in streams written by both IStream_WriteStr and wil::stream_write_string[_nothrow]. The format + is a single 16-bit quantity, followed by that many wchar_ts. The returned string is allocated with CoTaskMemAlloc. + Returns a zero-length (but non-null) string if the stream contained a zero-length string. + ~~~~ + IStream* source = // ... + wil::unique_cotaskmem_string content; + RETURN_IF_FAILED(wil::stream_read_string_nothrow(source, &content)); + if (wcscmp(content.get(), L"waffles") == 0) + { + // Waffles! + } + ~~~~ + @param source The stream from which to read a string + @param value Set to point to the allocated result of reading a string from `source` + */ + inline HRESULT stream_read_string_nothrow( + _In_ ISequentialStream* source, + _When_(options == empty_string_options::returns_empty, _Outptr_result_z_) _When_(options == empty_string_options::returns_null, _Outptr_result_maybenull_z_) wchar_t** value, + empty_string_options options = empty_string_options::returns_empty) + { + unsigned short cch; + RETURN_IF_FAILED(stream_read_nothrow(source, &cch)); + + if ((cch == 0) && (options == empty_string_options::returns_null)) + { + *value = nullptr; + } + else + { + auto allocated = make_unique_cotaskmem_nothrow(static_cast(cch) + 1); + RETURN_IF_NULL_ALLOC(allocated); + RETURN_IF_FAILED(stream_read_nothrow(source, allocated.get(), static_cast(cch) * sizeof(wchar_t))); + allocated[cch] = 0; + + *value = allocated.release(); + } + + return S_OK; + } + +#endif // __WIL_OBJBASE_H + + /** Write a string to a stream + Serializes a string into a stream by putting its length and then the wchar_ts in the string + into the stream. Zero-length strings have their length but no data written. This is the + form expected by IStream_ReadStr and wil::string_read_stream. + ~~~~ + IStream* target = // ... + RETURN_IF_FAILED(wil::stream_write_string_nothrow(target, L"Waffles", 3)); + // Produces wchar_t[] { 0x3, L'W', L'a', L'f' }; + ~~~~ + @param target The stream to which to write a string + @param source The string to write. Can be null if `writeLength` is zero + @param writeLength The number of characters to write from source into `target` + */ + inline HRESULT stream_write_string_nothrow(_In_ ISequentialStream* target, _In_reads_opt_(writeLength) const wchar_t* source, _In_ size_t writeLength) + { + FAIL_FAST_IF(writeLength > USHRT_MAX); + + RETURN_IF_FAILED(stream_write_nothrow(target, static_cast(writeLength))); + + if (writeLength > 0) + { + RETURN_IF_FAILED(stream_write_nothrow(target, source, static_cast(writeLength) * sizeof(wchar_t))); + } + + return S_OK; + } + + /** Write a string to a stream + Serializes a string into a stream by putting its length and then the wchar_ts in the string + into the stream. Zero-length strings have their length but no data written. This is the + form expected by IStream_ReadStr and wil::string_read_stream. + ~~~~ + IStream* target = // ... + RETURN_IF_FAILED(wil::stream_write_string_nothrow(target, L"Waffles")); + // Produces wchar_t[] { 0x3, L'W', L'a', L'f', L'f', L'l', L'e', L's' }; + ~~~~ + @param target The stream to which to write a string + @param source The string to write. When nullptr, a zero-length string is written. + */ + inline HRESULT stream_write_string_nothrow(_In_ ISequentialStream* target, _In_opt_z_ const wchar_t* source) + { + return stream_write_string_nothrow(target, source, source ? wcslen(source) : 0); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + + /** Read data from a stream into a buffer. + ~~~~ + IStream* source = // ... + ULONG dataBlob = 0; + auto read = wil::stream_read_partial(source, &dataBlob, sizeof(dataBlob)); + if (read != sizeof(dataBlob)) + { + // end of stream, probably + } + else if (dataBlob == 0x8675309) + { + DoThing(dataBlob); + } + ~~~~ + @param stream The stream from which to read at most `size` bytes. + @param data A buffer into which up to `size` bytes will be read + @param size The size, in bytes, of the buffer pointed to by `data` + @return The amount, in bytes, of data read from `stream` into `data` + */ + inline unsigned long stream_read_partial(_In_ ISequentialStream* stream, _Out_writes_bytes_to_(size, return) void* data, unsigned long size) + { + unsigned long didRead; + THROW_IF_FAILED(stream_read_partial_nothrow(stream, data, size, &didRead)); + + return didRead; + } + + /** Read an exact number of bytes from a stream into a buffer. + Fails if the stream didn't read all the bytes requested by throwing HRESULT_FROM_WIN32(ERROR_INVALID_DATA). + ~~~~ + IStream* source = // ... + ULONG dataBlob = 0; + wil::stream_read(source, &dataBlob, sizeof(dataBlob)); + if (dataBlob == 0x8675309) + { + DoThing(dataBlob); + } + ~~~~ + @param stream The stream from which to read at most `size` bytes. + @param data A buffer into which up to `size` bytes will be read + @param size The size, in bytes, of the buffer pointed to by `data` + */ + inline void stream_read(_In_ ISequentialStream* stream, _Out_writes_bytes_all_(size) void* data, unsigned long size) + { + THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), stream_read_partial(stream, data, size) != size); + } + + /** Read from a stream into a POD type. + Fails if the stream didn't have enough bytes by throwing HRESULT_FROM_WIN32(ERROR_INVALID_DATA). + ~~~~ + IStream* source = // ... + MY_HEADER header = wil::stream_read(source); + if (header.Version == 0x8675309) + { + ConsumeOldHeader(stream, header); + } + ~~~~ + @param stream The stream from which to read at most `sizeof(T)` bytes. + @return An instance of `T` read from the stream + */ + template T stream_read(_In_ ISequentialStream* stream) + { + static_assert(__is_pod(T), "Read type must be POD"); + T temp{}; + stream_read(stream, &temp, sizeof(temp)); + + return temp; + } + + /** Write an exact number of bytes to a stream from a buffer. + Fails if the stream didn't read write the bytes requested. + ~~~~ + IStream* source = // ... + ULONG dataBlob = 0; + wil::stream_write(source, dataBlob, sizeof(dataBlob)); + ~~~~ + @param stream The stream to which to write at most `size` bytes. + @param data A buffer from which up to `size` bytes will be read + @param size The size, in bytes, of the buffer pointed to by `data` + */ + inline void stream_write(_In_ ISequentialStream* stream, _In_reads_bytes_(size) const void* data, unsigned long size) + { + THROW_IF_FAILED(stream_write_nothrow(stream, data, size)); + } + + /** Write a POD type to a stream. + Fails if the stream didn't accept the entire size. + ~~~~ + IStream* target = // ... + + MY_HEADER header { 0x8675309, HEADER_FLAG_1 | HEADER_FLAG_2 }; + wil::stream_write(target, header) + + wil::stream_write(target, 16); + ~~~~ + @param stream The stream to which to write `thing` + @param thing The POD data type to write to the stream. + */ + template inline void stream_write(_In_ ISequentialStream* stream, const T& thing) + { + stream_write(stream, wistd::addressof(thing), sizeof(thing)); + } + + /** Retrieve the size of this stream, in bytes + ~~~~ + IStream* source = // ... + ULONGLONG size = wil::stream_size(source); + ~~~~ + @param stream The stream whose size is to be returned in `value` + @return The size, in bytes, reported by `stream` + */ + inline unsigned long long stream_size(_In_ IStream* stream) + { + unsigned long long size; + THROW_IF_FAILED(stream_size_nothrow(stream, &size)); + + return size; + } + + /** Seek a stream to an absolute offset + ~~~~ + IStream* source = // ... + wil::stream_set_position(source, sizeof(HEADER)); + ~~~~ + @param stream The stream whose size is to be returned in `value` + @param offset The offset, in bytes, to seek the stream. + @return The new absolute stream position, in bytes + */ + inline unsigned long long stream_set_position(_In_ IStream* stream, unsigned long long offset) + { + unsigned long long landed; + THROW_IF_FAILED(stream_set_position_nothrow(stream, offset, &landed)); + return landed; + } + + /** Seek a relative amount in a stream + ~~~~ + IStream* source = // ... + ULONGLONG newPosition = wil::stream_seek_from_current_position(source, 16); + ~~~~ + @param stream The stream whose location is to be moved + @param amount The offset, in bytes, to seek the stream. + @return The new absolute stream position, in bytes + */ + inline unsigned long long stream_seek_from_current_position(_In_ IStream* stream, long long amount) + { + unsigned long long landed; + THROW_IF_FAILED(stream_seek_from_current_position_nothrow(stream, amount, &landed)); + + return landed; + } + + /** Determine the current byte position in the stream + ~~~~ + IStream* source = // ... + ULONGLONG currentPos = wil::stream_get_position(source); + ~~~~ + @param stream The stream whose location is to be moved + @return The current position reported by `stream` + */ + inline unsigned long long stream_get_position(_In_ IStream* stream) + { + return stream_seek_from_current_position(stream, 0); + } + + /** Moves the stream to absolute position 0 + ~~~~ + IStream* source = // ... + wil::stream_reset(source); + ASSERT(wil::stream_get_position(source) == 0); + ~~~~ + @param stream The stream whose location is to be moved + */ + inline void stream_reset(_In_ IStream* stream) + { + stream_set_position(stream, 0); + } + + /** Copy data from one stream to another + ~~~~ + IStream* source = // ... + IStream* target = // ... + ULONGLONG copied = ; + if (wil::stream_copy_bytes(source, target, sizeof(Header)) < sizeof(Header)) + { + DoSomethingAboutPartialCopy(); + } + ~~~~ + @param source The stream from which to copy at most `amount` bytes + @param target The steam to which to copy at most `amount` bytes + @param amount The maximum number of bytes to copy from `source` to `target` + @return The number of bytes copied between the two streams + */ + inline unsigned long long stream_copy_bytes(_In_ IStream* source, _In_ IStream* target, unsigned long long amount) + { + unsigned long long copied; + THROW_IF_FAILED(stream_copy_bytes_nothrow(source, target, amount, &copied)); + + return copied; + } + + /** Copy all data from one stream to another + ~~~~ + IStream* source = // ... + IStream* target = // ... + ULONGLONG copied = wil::stream_copy_all(source, target); + ~~~~ + @param source The stream from which to copy all content + @param target The steam to which to copy all content + @return The number of bytes copied between the two. + */ + inline unsigned long long stream_copy_all(_In_ IStream* source, _In_ IStream* target) + { + return stream_copy_bytes(source, target, ULLONG_MAX); + } + + /** Copies an exact amount of data from one stream to another, failing otherwise + ~~~~ + IStream* source = // ... + IStream* target = // ... + wil::stream_copy_all_nothrow(source, target, sizeof(SOMETHING)); + ~~~~ + @param source The stream from which to copy at most `amount` bytes + @param target The steam to which to copy at most `amount` bytes + @param amount The number of bytes to copy from `source` to `target` + */ + inline void stream_copy_exact(_In_ IStream* source, _In_ IStream* target, unsigned long long amount) + { + THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), stream_copy_bytes(source, target, amount) != amount); + } + +#ifdef __WIL_OBJBASE_H_ + + /** Read a string from a stream and returns an allocated copy + Deserializes strings in streams written by both IStream_WriteStr and wil::stream_write_string[_nothrow]. The format + is a single 16-bit quantity, followed by that many wchar_ts. The returned string is allocated with CoTaskMemAlloc. + Returns a zero-length (but non-null) string if the stream contained a zero-length string. + ~~~~ + IStream* source = // ... + wil::unique_cotaskmem_string content = wil::stream_read_string(source); + if (wcscmp(content.get(), L"waffles") == 0) + { + // Waffles! + } + ~~~~ + @param source The stream from which to read a string + @return An non-null string (but possibly zero lengh) string read from `source` + */ + inline wil::unique_cotaskmem_string stream_read_string(_In_ ISequentialStream* source, empty_string_options options = empty_string_options::returns_empty) + { + wil::unique_cotaskmem_string result; + THROW_IF_FAILED(stream_read_string_nothrow(source, &result, options)); + + return result; + } + +#endif // __WIL_OBJBASE_H + + /** Write a string to a stream + Serializes a string into a stream by putting its length and then the wchar_ts in the string + into the stream. Zero-length strings have their length but no data written. This is the + form expected by IStream_ReadStr and wil::string_read_stream. + ~~~~ + IStream* target = // ... + wil::stream_write_string(target, L"Waffles", 3); + ~~~~ + @param target The stream to which to write a string + @param source The string to write. Can be null if `writeLength` is zero + @param writeLength The number of characters to write from source into `target` + */ + inline void stream_write_string(_In_ ISequentialStream* target, _In_reads_opt_(toWriteCch) const wchar_t* source, _In_ size_t toWriteCch) + { + THROW_IF_FAILED(stream_write_string_nothrow(target, source, toWriteCch)); + } + + /** Write a string to a stream + Serializes a string into a stream by putting its length and then the wchar_ts in the string + into the stream. Zero-length strings have their length but no data written.This is the + form expected by IStream_ReadStr and wil::string_read_stream. + ~~~~ + IStream* target = // ... + wil::stream_write_string(target, L"Waffles"); + ~~~~ + @param target The stream to which to write a string + @param source The string to write. When nullptr, a zero-length string is written. + */ + inline void stream_write_string(_In_ ISequentialStream* target, _In_opt_z_ const wchar_t* source) + { + THROW_IF_FAILED(stream_write_string_nothrow(target, source, source ? wcslen(source) : 0)); + } + + /** Saves and restores the position of a stream + Useful for potentially reading data from a stream, or being able to read ahead, then reset + back to where one left off, such as conditionally reading content from a stream. + ~~~~ + void MaybeConsumeStream(IStream* stream) + { + // On error, reset the read position in the stream to where we left off + auto saver = wil::stream_position_saver(stream); + auto header = wil::stream_read(stream); + for (ULONG i = 0; i < header.Count; ++i) + { + ProcessElement(wil::stream_read(stream)); + } + } + ~~~~ + */ + class stream_position_saver + { + public: + //! Constructs a saver from the current position of this stream + //! @param stream The stream instance whose position is to be saved. + explicit stream_position_saver(_In_opt_ IStream* stream) : + m_stream(stream), + m_position(stream ? stream_get_position(stream) : 0) + { + } + + ~stream_position_saver() + { + if (m_stream) + { + LOG_IF_FAILED(stream_set_position_nothrow(m_stream.get(), m_position)); + } + } + + /** Updates the current position in the stream + ~~~~ + // Read a size marker from the stream, then advance that much. + IStream* stream1 = // ... + auto saver = wil::stream_position_saver(stream1); + auto size = wil::stream_read(stream1); + wil::stream_seek_from_current_position(stream, size); + saver.update(); + ~~~~ + */ + void update() + { + m_position = stream_get_position(m_stream.get()); + } + + //! Returns the current position being saved for the stream + //! @returns The position, in bytes, being saved for the stream + unsigned long long position() const + { + return m_position; + } + + /** Resets the position saver to manage a new stream + Reverts the position of any stream this saver is currently holding a place for. + ~~~~ + IStream* stream1 = // ... + IStream* stream2 = // ... + auto saver = wil::stream_position_saver(stream1); + if (wil::stream_read(stream1).Flags != 0) + { + saver.reset(stream2); // position in stream1 is reverted, now holding stream2 + } + ~~~~ + @param stream The stream whose position is to be saved + */ + void reset(_In_ IStream* stream) + { + reset(); + + m_stream = stream; + m_position = wil::stream_get_position(m_stream.get()); + } + + /** Resets the position of the stream + ~~~~ + IStream* stream1 = // ... + auto saver = wil::stream_position_saver(stream1); + MyType mt = wil::stream_read(stream1); + if (mt.Flags & MyTypeFlags::Extended) + { + saver.reset(); + ProcessExtended(stream1, wil::stream_read(stream1)); + } + else + { + ProcessStandard(stream1, mt); + } + ~~~~ + */ + void reset() + { + if (m_stream) + { + wil::stream_set_position(m_stream.get(), m_position); + } + } + + /** Stops saving the position of the stream + ~~~~ + // The stream has either a standard or extended header, followed by interesting content. + // Read either one, leaving the stream after the headers have been read off. On failure, + // the stream's position is restored. + std::pair get_headers(_In_ IStream* source) + { + auto saver = wil::stream_position_saver(stream1); + MyType mt = wil::stream_read(stream1); + MyTypeExtended mte{}; + if (mt.Flags & MyTypeFlags::Extended) + { + mte = wil::stream_read(stream1); + } + saver.dismiss(); + return { mt, mte }; + } + ~~~~ + */ + void dismiss() + { + m_stream.reset(); + } + + stream_position_saver(stream_position_saver&&) = default; + stream_position_saver& operator=(stream_position_saver&&) = default; + + stream_position_saver(const stream_position_saver&) = delete; + void operator=(const stream_position_saver&) = delete; + + private: + com_ptr m_stream; + unsigned long long m_position; + }; +#endif // WIL_ENABLE_EXCEPTIONS +#pragma endregion // stream helpers + +#if defined(__IObjectWithSite_INTERFACE_DEFINED__) + /// @cond + namespace details + { + inline void __stdcall SetSiteNull(IObjectWithSite* objWithSite) + { + objWithSite->SetSite(nullptr); // break the cycle + } + } // details + /// @endcond + + using unique_set_site_null_call = wil::unique_com_call; + + /** RAII support for managing the site chain. This function sets the site pointer on an object and return an object + that resets it on destruction to break the cycle. + Note, this does not preserve the existing site if there is one (an uncommon case) so only use this when that is not required. + ~~~ + auto cleanup = wil::com_set_site(execCommand.get(), serviceProvider->GetAsSite()); + ~~~ + Include ocidl.h before wil\com.h to use this. + */ + WI_NODISCARD inline unique_set_site_null_call com_set_site(_In_opt_ IUnknown* obj, _In_opt_ IUnknown* site) + { + wil::com_ptr_nothrow objWithSite; + if (site && wil::try_com_copy_to(obj, &objWithSite)) + { + objWithSite->SetSite(site); + } + return unique_set_site_null_call(objWithSite.get()); + } + + /** Iterate over each object in a site chain. Useful for debugging site issues, here is sample use. + ~~~ + void OutputDebugSiteChainWatchWindowText(IUnknown* site) + { + OutputDebugStringW(L"Copy and paste these entries into the Visual Studio Watch Window\n"); + wil::for_each_site(site, [](IUnknown* site) + { + wchar_t msg[64]; + StringCchPrintfW(msg, ARRAYSIZE(msg), L"((IUnknown*)0x%p)->__vfptr[0]\n", site); + OutputDebugStringW(msg); + }); + } + */ + + template + void for_each_site(_In_opt_ IUnknown* siteInput, TLambda&& callback) + { + wil::com_ptr_nothrow site(siteInput); + while (site) + { + callback(site.get()); + auto objWithSite = site.try_query(); + site.reset(); + if (objWithSite) + { + objWithSite->GetSite(IID_PPV_ARGS(&site)); + } + } + } + +#endif // __IObjectWithSite_INTERFACE_DEFINED__ + +} // wil + +#endif diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/common.h b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/common.h new file mode 100644 index 0000000..99e29c5 --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/common.h @@ -0,0 +1,778 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_COMMON_INCLUDED +#define __WIL_COMMON_INCLUDED + +#if defined(_KERNEL_MODE ) && !defined(__WIL_MIN_KERNEL) +// This define indicates that the WIL usage is in a kernel mode context where +// a high degree of WIL functionality is desired. +// +// Use (sparingly) to change behavior based on whether WIL is being used in kernel +// mode or user mode. +#define WIL_KERNEL_MODE +#endif + +// Defining WIL_HIDE_DEPRECATED will hide everything deprecated. +// Each wave of deprecation will add a new WIL_HIDE_DEPRECATED_YYMM number that can be used to lock deprecation at +// a particular point, allowing components to avoid backslide and catch up to the current independently. +#ifdef WIL_HIDE_DEPRECATED +#define WIL_HIDE_DEPRECATED_1809 +#endif +#ifdef WIL_HIDE_DEPRECATED_1809 +#define WIL_HIDE_DEPRECATED_1612 +#endif +#ifdef WIL_HIDE_DEPRECATED_1612 +#define WIL_HIDE_DEPRECATED_1611 +#endif + +// Implementation side note: ideally the deprecation would be done with the function-level declspec +// as it allows you to utter the error text when used. The declspec works, but doing it selectively with +// a macro makes intellisense deprecation comments not work. So we just use the #pragma deprecation. +#ifdef WIL_WARN_DEPRECATED +#define WIL_WARN_DEPRECATED_1809 +#endif +#ifdef WIL_WARN_DEPRECATED_1809 +#define WIL_WARN_DEPRECATED_1612 +#endif +#ifdef WIL_WARN_DEPRECATED_1612 +#define WIL_WARN_DEPRECATED_1611 +#endif +#ifdef WIL_WARN_DEPRECATED_1809 +#define WIL_WARN_DEPRECATED_1809_PRAGMA(...) __pragma(deprecated(__VA_ARGS__)) +#else +#define WIL_WARN_DEPRECATED_1809_PRAGMA(...) +#endif +#ifdef WIL_WARN_DEPRECATED_1611 +#define WIL_WARN_DEPRECATED_1611_PRAGMA(...) __pragma(deprecated(__VA_ARGS__)) +#else +#define WIL_WARN_DEPRECATED_1611_PRAGMA(...) +#endif +#ifdef WIL_WARN_DEPRECATED_1612 +#define WIL_WARN_DEPRECATED_1612_PRAGMA(...) __pragma(deprecated(__VA_ARGS__)) +#else +#define WIL_WARN_DEPRECATED_1612_PRAGMA(...) +#endif + +#if defined(_MSVC_LANG) +#define __WI_SUPPRESS_4127_S __pragma(warning(push)) __pragma(warning(disable:4127)) __pragma(warning(disable:26498)) __pragma(warning(disable:4245)) +#define __WI_SUPPRESS_4127_E __pragma(warning(pop)) +#define __WI_SUPPRESS_NULLPTR_ANALYSIS __pragma(warning(suppress:28285)) __pragma(warning(suppress:6504)) +#define __WI_SUPPRESS_NONINIT_ANALYSIS __pragma(warning(suppress:26495)) +#define __WI_SUPPRESS_NOEXCEPT_ANALYSIS __pragma(warning(suppress:26439)) +#else +#define __WI_SUPPRESS_4127_S +#define __WI_SUPPRESS_4127_E +#define __WI_SUPPRESS_NULLPTR_ANALYSIS +#define __WI_SUPPRESS_NONINIT_ANALYSIS +#define __WI_SUPPRESS_NOEXCEPT_ANALYSIS +#endif + +#include + +// Some SAL remapping / decoration to better support Doxygen. Macros that look like function calls can +// confuse Doxygen when they are used to decorate a function or variable. We simplify some of these to +// basic macros without the function for common use cases. +/// @cond +#define _Success_return_ _Success_(return) +#define _Success_true_ _Success_(true) +#define __declspec_noinline_ __declspec(noinline) +#define __declspec_selectany_ __declspec(selectany) +/// @endcond + +//! @defgroup macrobuilding Macro Composition +//! The following macros are building blocks primarily intended for authoring other macros. +//! @{ + +//! Re-state a macro value (indirection for composition) +#define WI_FLATTEN(...) __VA_ARGS__ + +/// @cond +#define __WI_PASTE_imp(a, b) a##b +/// @endcond + +//! This macro is for use in other macros to paste two tokens together, such as a constant and the __LINE__ macro. +#define WI_PASTE(a, b) __WI_PASTE_imp(a, b) + +/// @cond +#define __WI_HAS_VA_OPT_IMPL(F, T, ...) T +#define __WI_HAS_VA_OPT_(...) __WI_HAS_VA_OPT_IMPL(__VA_OPT__(0,) 1, 0) +/// @endcond + +//! Evaluates to '1' when support for '__VA_OPT__' is available, else '0' +#define WI_HAS_VA_OPT __WI_HAS_VA_OPT_(unused) + +/// @cond +#define __WI_ARGS_COUNT1(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, \ + A30, A31, A32, A33, A34, A35, A36, A37, A38, A39, A40, A41, A42, A43, A44, A45, A46, A47, A48, A49, A50, A51, A52, A53, A54, A55, A56, A57, A58, A59, \ + A60, A61, A62, A63, A64, A65, A66, A67, A68, A69, A70, A71, A72, A73, A74, A75, A76, A77, A78, A79, A80, A81, A82, A83, A84, A85, A86, A87, A88, A89, \ + A90, A91, A92, A93, A94, A95, A96, A97, A98, A99, count, ...) count +#define __WI_ARGS_COUNT0(...) WI_FLATTEN(__WI_ARGS_COUNT1(__VA_ARGS__, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, \ + 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \ + 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)) +#define __WI_ARGS_COUNT_PREFIX(...) 0, __VA_ARGS__ +/// @endcond + +//! This variadic macro returns the number of arguments passed to it (up to 99). +#if WI_HAS_VA_OPT +#define WI_ARGS_COUNT(...) __WI_ARGS_COUNT0(0 __VA_OPT__(, __VA_ARGS__)) +#else +#define WI_ARGS_COUNT(...) __WI_ARGS_COUNT0(__WI_ARGS_COUNT_PREFIX(__VA_ARGS__)) +#endif + +/// @cond +#define __WI_FOR_imp0( fn) +#define __WI_FOR_imp1( fn, arg) fn(arg) +#define __WI_FOR_imp2( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp1(fn, __VA_ARGS__)) +#define __WI_FOR_imp3( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp2(fn, __VA_ARGS__)) +#define __WI_FOR_imp4( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp3(fn, __VA_ARGS__)) +#define __WI_FOR_imp5( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp4(fn, __VA_ARGS__)) +#define __WI_FOR_imp6( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp5(fn, __VA_ARGS__)) +#define __WI_FOR_imp7( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp6(fn, __VA_ARGS__)) +#define __WI_FOR_imp8( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp7(fn, __VA_ARGS__)) +#define __WI_FOR_imp9( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp8(fn, __VA_ARGS__)) +#define __WI_FOR_imp10(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp9(fn, __VA_ARGS__)) +#define __WI_FOR_imp11(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp10(fn, __VA_ARGS__)) +#define __WI_FOR_imp12(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp11(fn, __VA_ARGS__)) +#define __WI_FOR_imp13(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp12(fn, __VA_ARGS__)) +#define __WI_FOR_imp14(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp13(fn, __VA_ARGS__)) +#define __WI_FOR_imp15(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp14(fn, __VA_ARGS__)) +#define __WI_FOR_imp16(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp15(fn, __VA_ARGS__)) +#define __WI_FOR_imp17(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp16(fn, __VA_ARGS__)) +#define __WI_FOR_imp18(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp17(fn, __VA_ARGS__)) +#define __WI_FOR_imp19(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp18(fn, __VA_ARGS__)) +#define __WI_FOR_imp20(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp19(fn, __VA_ARGS__)) +#define __WI_FOR_imp21(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp20(fn, __VA_ARGS__)) +#define __WI_FOR_imp22(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp21(fn, __VA_ARGS__)) +#define __WI_FOR_imp23(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp22(fn, __VA_ARGS__)) +#define __WI_FOR_imp24(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp23(fn, __VA_ARGS__)) +#define __WI_FOR_imp25(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp24(fn, __VA_ARGS__)) +#define __WI_FOR_imp26(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp25(fn, __VA_ARGS__)) +#define __WI_FOR_imp27(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp26(fn, __VA_ARGS__)) +#define __WI_FOR_imp28(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp27(fn, __VA_ARGS__)) +#define __WI_FOR_imp29(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp28(fn, __VA_ARGS__)) +#define __WI_FOR_imp30(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp29(fn, __VA_ARGS__)) +#define __WI_FOR_imp31(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp30(fn, __VA_ARGS__)) +#define __WI_FOR_imp32(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp31(fn, __VA_ARGS__)) +#define __WI_FOR_imp33(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp32(fn, __VA_ARGS__)) +#define __WI_FOR_imp34(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp33(fn, __VA_ARGS__)) +#define __WI_FOR_imp35(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp34(fn, __VA_ARGS__)) +#define __WI_FOR_imp36(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp35(fn, __VA_ARGS__)) +#define __WI_FOR_imp37(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp36(fn, __VA_ARGS__)) +#define __WI_FOR_imp38(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp37(fn, __VA_ARGS__)) +#define __WI_FOR_imp39(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp38(fn, __VA_ARGS__)) +#define __WI_FOR_imp40(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp39(fn, __VA_ARGS__)) +#define __WI_FOR_imp41(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp40(fn, __VA_ARGS__)) +#define __WI_FOR_imp42(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp41(fn, __VA_ARGS__)) +#define __WI_FOR_imp43(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp42(fn, __VA_ARGS__)) +#define __WI_FOR_imp44(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp43(fn, __VA_ARGS__)) +#define __WI_FOR_imp45(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp44(fn, __VA_ARGS__)) +#define __WI_FOR_imp46(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp45(fn, __VA_ARGS__)) +#define __WI_FOR_imp47(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp46(fn, __VA_ARGS__)) +#define __WI_FOR_imp48(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp47(fn, __VA_ARGS__)) +#define __WI_FOR_imp49(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp48(fn, __VA_ARGS__)) +#define __WI_FOR_imp50(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp49(fn, __VA_ARGS__)) +#define __WI_FOR_imp51(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp50(fn, __VA_ARGS__)) +#define __WI_FOR_imp52(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp51(fn, __VA_ARGS__)) +#define __WI_FOR_imp53(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp52(fn, __VA_ARGS__)) +#define __WI_FOR_imp54(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp53(fn, __VA_ARGS__)) +#define __WI_FOR_imp55(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp54(fn, __VA_ARGS__)) +#define __WI_FOR_imp56(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp55(fn, __VA_ARGS__)) +#define __WI_FOR_imp57(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp56(fn, __VA_ARGS__)) +#define __WI_FOR_imp58(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp57(fn, __VA_ARGS__)) +#define __WI_FOR_imp59(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp58(fn, __VA_ARGS__)) +#define __WI_FOR_imp60(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp59(fn, __VA_ARGS__)) +#define __WI_FOR_imp61(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp60(fn, __VA_ARGS__)) +#define __WI_FOR_imp62(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp61(fn, __VA_ARGS__)) +#define __WI_FOR_imp63(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp62(fn, __VA_ARGS__)) +#define __WI_FOR_imp64(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp63(fn, __VA_ARGS__)) +#define __WI_FOR_imp65(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp64(fn, __VA_ARGS__)) +#define __WI_FOR_imp66(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp65(fn, __VA_ARGS__)) +#define __WI_FOR_imp67(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp66(fn, __VA_ARGS__)) +#define __WI_FOR_imp68(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp67(fn, __VA_ARGS__)) +#define __WI_FOR_imp69(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp68(fn, __VA_ARGS__)) +#define __WI_FOR_imp70(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp69(fn, __VA_ARGS__)) +#define __WI_FOR_imp71(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp70(fn, __VA_ARGS__)) +#define __WI_FOR_imp72(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp71(fn, __VA_ARGS__)) +#define __WI_FOR_imp73(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp72(fn, __VA_ARGS__)) +#define __WI_FOR_imp74(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp73(fn, __VA_ARGS__)) +#define __WI_FOR_imp75(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp74(fn, __VA_ARGS__)) +#define __WI_FOR_imp76(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp75(fn, __VA_ARGS__)) +#define __WI_FOR_imp77(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp76(fn, __VA_ARGS__)) +#define __WI_FOR_imp78(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp77(fn, __VA_ARGS__)) +#define __WI_FOR_imp79(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp78(fn, __VA_ARGS__)) +#define __WI_FOR_imp80(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp79(fn, __VA_ARGS__)) +#define __WI_FOR_imp81(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp80(fn, __VA_ARGS__)) +#define __WI_FOR_imp82(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp81(fn, __VA_ARGS__)) +#define __WI_FOR_imp83(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp82(fn, __VA_ARGS__)) +#define __WI_FOR_imp84(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp83(fn, __VA_ARGS__)) +#define __WI_FOR_imp85(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp84(fn, __VA_ARGS__)) +#define __WI_FOR_imp86(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp85(fn, __VA_ARGS__)) +#define __WI_FOR_imp87(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp86(fn, __VA_ARGS__)) +#define __WI_FOR_imp88(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp87(fn, __VA_ARGS__)) +#define __WI_FOR_imp89(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp88(fn, __VA_ARGS__)) +#define __WI_FOR_imp90(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp89(fn, __VA_ARGS__)) +#define __WI_FOR_imp91(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp90(fn, __VA_ARGS__)) +#define __WI_FOR_imp92(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp91(fn, __VA_ARGS__)) +#define __WI_FOR_imp93(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp92(fn, __VA_ARGS__)) +#define __WI_FOR_imp94(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp93(fn, __VA_ARGS__)) +#define __WI_FOR_imp95(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp94(fn, __VA_ARGS__)) +#define __WI_FOR_imp96(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp95(fn, __VA_ARGS__)) +#define __WI_FOR_imp97(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp96(fn, __VA_ARGS__)) +#define __WI_FOR_imp98(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp97(fn, __VA_ARGS__)) +#define __WI_FOR_imp99(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp98(fn, __VA_ARGS__)) + +#define __WI_FOR_imp(n, fnAndArgs) WI_PASTE(__WI_FOR_imp, n) fnAndArgs +/// @endcond + +//! Iterates through each of the given arguments invoking the specified macro against each one. +#define WI_FOREACH(fn, ...) __WI_FOR_imp(WI_ARGS_COUNT(__VA_ARGS__), (fn, ##__VA_ARGS__)) + +//! Dispatches a single macro name to separate macros based on the number of arguments passed to it. +#define WI_MACRO_DISPATCH(name, ...) WI_PASTE(WI_PASTE(name, WI_ARGS_COUNT(__VA_ARGS__)), (__VA_ARGS__)) + +//! @} // Macro composition helpers + +#if !defined(__cplusplus) || defined(__WIL_MIN_KERNEL) + +#define WI_ODR_PRAGMA(NAME, TOKEN) +#define WI_NOEXCEPT + +#else +#pragma warning(push) +#pragma warning(disable:4714) // __forceinline not honored + +// DO NOT add *any* further includes to this file -- there should be no dependencies from its usage +#include "wistd_type_traits.h" + +//! This macro inserts ODR violation protection; the macro allows it to be compatible with straight "C" code +#define WI_ODR_PRAGMA(NAME, TOKEN) __pragma(detect_mismatch("ODR_violation_" NAME "_mismatch", TOKEN)) + +#ifdef WIL_KERNEL_MODE +WI_ODR_PRAGMA("WIL_KERNEL_MODE", "1") +#else +WI_ODR_PRAGMA("WIL_KERNEL_MODE", "0") +#endif + +#if defined(_CPPUNWIND) && !defined(WIL_SUPPRESS_EXCEPTIONS) +/** This define is automatically set when exceptions are enabled within wil. +It is automatically defined when your code is compiled with exceptions enabled (via checking for the built-in +_CPPUNWIND flag) unless you explicitly define WIL_SUPPRESS_EXCEPTIONS ahead of including your first wil +header. All exception-based WIL methods and classes are included behind: +~~~~ +#ifdef WIL_ENABLE_EXCEPTIONS +// code +#endif +~~~~ +This enables exception-free code to directly include WIL headers without worrying about exception-based +routines suddenly becoming available. */ +#define WIL_ENABLE_EXCEPTIONS +#endif +/// @endcond + +/// @cond +#if defined(WIL_EXCEPTION_MODE) +static_assert(WIL_EXCEPTION_MODE <= 2, "Invalid exception mode"); +#elif !defined(WIL_LOCK_EXCEPTION_MODE) +#define WIL_EXCEPTION_MODE 0 // default, can link exception-based and non-exception based libraries together +#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "0") +#elif defined(WIL_ENABLE_EXCEPTIONS) +#define WIL_EXCEPTION_MODE 1 // new code optimization: ONLY support linking libraries together that have exceptions enabled +#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "1") +#else +#define WIL_EXCEPTION_MODE 2 // old code optimization: ONLY support linking libraries that are NOT using exceptions +#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "2") +#endif + +#if WIL_EXCEPTION_MODE == 1 && !defined(WIL_ENABLE_EXCEPTIONS) +#error Must enable exceptions when WIL_EXCEPTION_MODE == 1 +#endif + +// block for documentation only +#if defined(WIL_DOXYGEN) +/** This define can be explicitly set to disable exception usage within wil. +Normally this define is never needed as the WIL_ENABLE_EXCEPTIONS macro is enabled automatically by looking +at _CPPUNWIND. If your code compiles with exceptions enabled, but does not want to enable the exception-based +classes and methods from WIL, define this macro ahead of including the first WIL header. */ +#define WIL_SUPPRESS_EXCEPTIONS + +/** This define can be explicitly set to lock the process exception mode to WIL_ENABLE_EXCEPTIONS. +Locking the exception mode provides optimizations to exception barriers, staging hooks and DLL load costs as it eliminates the need to +do copy-on-write initialization of various function pointers and the necessary indirection that's done within WIL to avoid ODR violations +when linking libraries together with different exception handling semantics. */ +#define WIL_LOCK_EXCEPTION_MODE + +/** This define explicit sets the exception mode for the process to control optimizations. +Three exception modes are available: +0) This is the default. This enables a binary to link both exception-based and non-exception based libraries together that + use WIL. This adds overhead to exception barriers, DLL copy on write pages and indirection through function pointers to avoid ODR + violations when linking libraries together with different exception handling semantics. +1) Prefer this setting when it can be used. This locks the binary to only supporting libraries which were built with exceptions enabled. +2) This locks the binary to libraries built without exceptions. */ +#define WIL_EXCEPTION_MODE +#endif + +#if (__cplusplus >= 201703) || (_MSVC_LANG >= 201703) +#define WIL_HAS_CXX_17 1 +#else +#define WIL_HAS_CXX_17 0 +#endif + +// Until we'll have C++17 enabled in our code base, we're falling back to SAL +#define WI_NODISCARD __WI_LIBCPP_NODISCARD_ATTRIBUTE + +#define __R_ENABLE_IF_IS_CLASS(ptrType) wistd::enable_if_t::value, void*> = (void*)0 +#define __R_ENABLE_IF_IS_NOT_CLASS(ptrType) wistd::enable_if_t::value, void*> = (void*)0 + +//! @defgroup bitwise Bitwise Inspection and Manipulation +//! Bitwise helpers to improve readability and reduce the error rate of bitwise operations. +//! Several macros have been constructed to assist with bitwise inspection and manipulation. These macros exist +//! for two primary purposes: +//! +//! 1. To improve the readability of bitwise comparisons and manipulation. +//! +//! The macro names are the more concise, readable form of what's being done and do not require that any flags +//! or variables be specified multiple times for the comparisons. +//! +//! 2. To reduce the error rate associated with bitwise operations. +//! +//! The readability improvements naturally lend themselves to this by cutting down the number of concepts. +//! Using `WI_IsFlagSet(var, MyEnum::Flag)` rather than `((var & MyEnum::Flag) == MyEnum::Flag)` removes the comparison +//! operator and repetition in the flag value. +//! +//! Additionally, these macros separate single flag operations (which tend to be the most common) from multi-flag +//! operations so that compile-time errors are generated for bitwise operations which are likely incorrect, +//! such as: `WI_IsFlagSet(var, MyEnum::None)` or `WI_IsFlagSet(var, MyEnum::ValidMask)`. +//! +//! Note that the single flag helpers should be used when a compile-time constant single flag is being manipulated. These +//! helpers provide compile-time errors on misuse and should be preferred over the multi-flag helpers. The multi-flag helpers +//! should be used when multiple flags are being used simultaneously or when the flag values are not compile-time constants. +//! +//! Common example usage (manipulation of flag variables): +//! ~~~~ +//! WI_SetFlag(m_flags, MyFlags::Foo); // Set a single flag in the given variable +//! WI_SetAllFlags(m_flags, MyFlags::Foo | MyFlags::Bar); // Set one or more flags +//! WI_ClearFlagIf(m_flags, MyFlags::Bar, isBarClosed); // Conditionally clear a single flag based upon a bool +//! WI_ClearAllFlags(m_flags, MyFlags::Foo | MyFlags::Bar); // Clear one or more flags from the given variable +//! WI_ToggleFlag(m_flags, MyFlags::Foo); // Toggle (change to the opposite value) a single flag +//! WI_UpdateFlag(m_flags, MyFlags::Bar, isBarClosed); // Sets or Clears a single flag from the given variable based upon a bool value +//! WI_UpdateFlagsInMask(m_flags, flagsMask, newFlagValues); // Sets or Clears the flags in flagsMask to the masked values from newFlagValues +//! ~~~~ +//! Common example usage (inspection of flag variables): +//! ~~~~ +//! if (WI_IsFlagSet(m_flags, MyFlags::Foo)) // Is a single flag set in the given variable? +//! if (WI_IsAnyFlagSet(m_flags, MyFlags::Foo | MyFlags::Bar)) // Is at least one flag from the given mask set? +//! if (WI_AreAllFlagsClear(m_flags, MyFlags::Foo | MyFlags::Bar)) // Are all flags in the given list clear? +//! if (WI_IsSingleFlagSet(m_flags)) // Is *exactly* one flag set in the given variable? +//! ~~~~ +//! @{ + +//! Returns the unsigned type of the same width and numeric value as the given enum +#define WI_EnumValue(val) static_cast<::wil::integral_from_enum>(val) +//! Validates that exactly ONE bit is set in compile-time constant `flag` +#define WI_StaticAssertSingleBitSet(flag) static_cast(::wil::details::verify_single_flag_helper(WI_EnumValue(flag))>::value) + +//! @name Bitwise manipulation macros +//! @{ + +//! Set zero or more bitflags specified by `flags` in the variable `var`. +#define WI_SetAllFlags(var, flags) ((var) |= (flags)) +//! Set a single compile-time constant `flag` in the variable `var`. +#define WI_SetFlag(var, flag) WI_SetAllFlags(var, WI_StaticAssertSingleBitSet(flag)) +//! Conditionally sets a single compile-time constant `flag` in the variable `var` only if `condition` is true. +#define WI_SetFlagIf(var, flag, condition) do { if (wil::verify_bool(condition)) { WI_SetFlag(var, flag); } } while ((void)0, 0) + +//! Clear zero or more bitflags specified by `flags` from the variable `var`. +#define WI_ClearAllFlags(var, flags) ((var) &= ~(flags)) +//! Clear a single compile-time constant `flag` from the variable `var`. +#define WI_ClearFlag(var, flag) WI_ClearAllFlags(var, WI_StaticAssertSingleBitSet(flag)) +//! Conditionally clear a single compile-time constant `flag` in the variable `var` only if `condition` is true. +#define WI_ClearFlagIf(var, flag, condition) do { if (wil::verify_bool(condition)) { WI_ClearFlag(var, flag); } } while ((void)0, 0) + +//! Changes a single compile-time constant `flag` in the variable `var` to be set if `isFlagSet` is true or cleared if `isFlagSet` is false. +#define WI_UpdateFlag(var, flag, isFlagSet) (wil::verify_bool(isFlagSet) ? WI_SetFlag(var, flag) : WI_ClearFlag(var, flag)) +//! Changes only the flags specified by `flagsMask` in the variable `var` to match the corresponding flags in `newFlags`. +#define WI_UpdateFlagsInMask(var, flagsMask, newFlags) wil::details::UpdateFlagsInMaskHelper(var, flagsMask, newFlags) + +//! Toggles (XOR the value) of multiple bitflags specified by `flags` in the variable `var`. +#define WI_ToggleAllFlags(var, flags) ((var) ^= (flags)) +//! Toggles (XOR the value) of a single compile-time constant `flag` in the variable `var`. +#define WI_ToggleFlag(var, flag) WI_ToggleAllFlags(var, WI_StaticAssertSingleBitSet(flag)) +//! @} // bitwise manipulation macros + +//! @name Bitwise inspection macros +//! @{ + +//! Evaluates as true if every bitflag specified in `flags` is set within `val`. +#define WI_AreAllFlagsSet(val, flags) wil::details::AreAllFlagsSetHelper(val, flags) +//! Evaluates as true if one or more bitflags specified in `flags` are set within `val`. +#define WI_IsAnyFlagSet(val, flags) (static_cast(WI_EnumValue(val) & WI_EnumValue(flags)) != static_cast(0)) +//! Evaluates as true if a single compile-time constant `flag` is set within `val`. +#define WI_IsFlagSet(val, flag) WI_IsAnyFlagSet(val, WI_StaticAssertSingleBitSet(flag)) + +//! Evaluates as true if every bitflag specified in `flags` is clear within `val`. +#define WI_AreAllFlagsClear(val, flags) (static_cast(WI_EnumValue(val) & WI_EnumValue(flags)) == static_cast(0)) +//! Evaluates as true if one or more bitflags specified in `flags` are clear within `val`. +#define WI_IsAnyFlagClear(val, flags) (!wil::details::AreAllFlagsSetHelper(val, flags)) +//! Evaluates as true if a single compile-time constant `flag` is clear within `val`. +#define WI_IsFlagClear(val, flag) WI_AreAllFlagsClear(val, WI_StaticAssertSingleBitSet(flag)) + +//! Evaluates as true if exactly one bit (any bit) is set within `val`. +#define WI_IsSingleFlagSet(val) wil::details::IsSingleFlagSetHelper(val) +//! Evaluates as true if exactly one bit from within the specified `mask` is set within `val`. +#define WI_IsSingleFlagSetInMask(val, mask) wil::details::IsSingleFlagSetHelper((val) & (mask)) +//! Evaluates as true if exactly one bit (any bit) is set within `val` or if there are no bits set within `val`. +#define WI_IsClearOrSingleFlagSet(val) wil::details::IsClearOrSingleFlagSetHelper(val) +//! Evaluates as true if exactly one bit from within the specified `mask` is set within `val` or if there are no bits from `mask` set within `val`. +#define WI_IsClearOrSingleFlagSetInMask(val, mask) wil::details::IsClearOrSingleFlagSetHelper((val) & (mask)) +//! @} + +#if defined(WIL_DOXYGEN) +/** This macro provides a C++ header with a guaranteed initialization function. +Normally, were a global object's constructor used for this purpose, the optimizer/linker might throw +the object away if it's unreferenced (which throws away the side-effects that the initialization function +was trying to achieve). Using this macro forces linker inclusion of a variable that's initialized by the +provided function to elide that optimization. +//! +This functionality is primarily provided as a building block for header-based libraries (such as WIL) +to be able to layer additional functionality into other libraries by their mere inclusion. Alternative models +of initialization should be used whenever they are available. +~~~~ +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +WI_HEADER_INITITALIZATION_FUNCTION(InitializeDesktopFamilyApis, [] +{ + g_pfnGetModuleName = GetCurrentModuleName; + g_pfnFailFastInLoaderCallout = FailFastInLoaderCallout; + return 1; +}); +#endif +~~~~ +The above example is used within WIL to decide whether or not the library containing WIL is allowed to use +desktop APIs. Building this functionality as #IFDEFs within functions would create ODR violations, whereas +doing it with global function pointers and header initialization allows a runtime determination. */ +#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn) +#elif defined(_M_IX86) +#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn) \ + extern "C" { __declspec(selectany) unsigned char g_header_init_ ## name = static_cast(fn()); } \ + __pragma(comment(linker, "/INCLUDE:_g_header_init_" #name)) +#elif defined(_M_IA64) || defined(_M_AMD64) || defined(_M_ARM) || defined(_M_ARM64) +#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn) \ + extern "C" { __declspec(selectany) unsigned char g_header_init_ ## name = static_cast(fn()); } \ + __pragma(comment(linker, "/INCLUDE:g_header_init_" #name)) +#else + #error linker pragma must include g_header_init variation +#endif + + +/** All Windows Implementation Library classes and functions are located within the "wil" namespace. +The 'wil' namespace is an intentionally short name as the intent is for code to be able to reference +the namespace directly (example: `wil::srwlock lock;`) without a using statement. Resist adding a using +statement for wil to avoid introducing potential name collisions between wil and other namespaces. */ +namespace wil +{ + /// @cond + namespace details + { + template + class pointer_range + { + public: + pointer_range(T begin_, T end_) : m_begin(begin_), m_end(end_) {} + T begin() const { return m_begin; } + T end() const { return m_end; } + private: + T m_begin; + T m_end; + }; + } + /// @endcond + + /** Enables using range-based for between a begin and end object pointer. + ~~~~ + for (auto& obj : make_range(objPointerBegin, objPointerEnd)) { } + ~~~~ */ + template + details::pointer_range make_range(T begin, T end) + { + return details::pointer_range(begin, end); + } + + /** Enables using range-based for on a range when given the base pointer and the number of objects in the range. + ~~~~ + for (auto& obj : make_range(objPointer, objCount)) { } + ~~~~ */ + template + details::pointer_range make_range(T begin, size_t count) + { + return details::pointer_range(begin, begin + count); + } + + + //! @defgroup outparam Output Parameters + //! Improve the conciseness of assigning values to optional output parameters. + //! @{ + + /** Assign the given value to an optional output parameter. + Makes code more concise by removing trivial `if (outParam)` blocks. */ + template + inline void assign_to_opt_param(_Out_opt_ T *outParam, T val) + { + if (outParam != nullptr) + { + *outParam = val; + } + } + + /** Assign NULL to an optional output pointer parameter. + Makes code more concise by removing trivial `if (outParam)` blocks. */ + template + inline void assign_null_to_opt_param(_Out_opt_ T *outParam) + { + if (outParam != nullptr) + { + *outParam = nullptr; + } + } + //! @} // end output parameter helpers + + /** Performs a logical or of the given variadic template parameters allowing indirect compile-time boolean evaluation. + Example usage: + ~~~~ + template + struct FeatureRequiredBy + { + static const bool enabled = wil::variadic_logical_or::enabled...>::value; + }; + ~~~~ */ + template struct variadic_logical_or; + /// @cond + template <> struct variadic_logical_or<> : wistd::false_type { }; + template struct variadic_logical_or : wistd::true_type { }; + template struct variadic_logical_or : variadic_logical_or::type { }; + /// @endcond + + /// @cond + namespace details + { + template + struct verify_single_flag_helper + { + static_assert((flag != 0) && ((flag & (flag - 1)) == 0), "Single flag expected, zero or multiple flags found"); + static const unsigned long long value = flag; + }; + } + /// @endcond + + + //! @defgroup typesafety Type Validation + //! Helpers to validate variable types to prevent accidental, but allowed type conversions. + //! These helpers are most useful when building macros that accept a particular type. Putting these functions around the types accepted + //! prior to pushing that type through to a function (or using it within the macro) allows the macro to add an additional layer of type + //! safety that would ordinarily be stripped away by C++ implicit conversions. This system is extensively used in the error handling helper + //! macros to validate the types given to various macro parameters. + //! @{ + + /** Verify that `val` can be evaluated as a logical bool. + Other types will generate an intentional compilation error. Allowed types for a logical bool are bool, BOOL, + boolean, BOOLEAN, and classes with an explicit bool cast. + @param val The logical bool expression + @return A C++ bool representing the evaluation of `val`. */ + template + _Post_satisfies_(return == static_cast(val)) + __forceinline constexpr bool verify_bool(const T& val) + { + return static_cast(val); + } + + template + __forceinline constexpr bool verify_bool(T /*val*/) + { + static_assert(!wistd::is_same::value, "Wrong Type: bool/BOOL/BOOLEAN/boolean expected"); + return false; + } + + template <> + _Post_satisfies_(return == val) + __forceinline constexpr bool verify_bool(bool val) + { + return val; + } + + template <> + _Post_satisfies_(return == (val != 0)) + __forceinline constexpr bool verify_bool(int val) + { + return (val != 0); + } + + template <> + _Post_satisfies_(return == !!val) + __forceinline constexpr bool verify_bool(unsigned char val) + { + return !!val; + } + + /** Verify that `val` is a Win32 BOOL value. + Other types (including other logical bool expressions) will generate an intentional compilation error. Note that this will + accept any `int` value as long as that is the underlying typedef behind `BOOL`. + @param val The Win32 BOOL returning expression + @return A Win32 BOOL representing the evaluation of `val`. */ + template + _Post_satisfies_(return == val) + __forceinline constexpr int verify_BOOL(T val) + { + // Note: Written in terms of 'int' as BOOL is actually: typedef int BOOL; + static_assert((wistd::is_same::value), "Wrong Type: BOOL expected"); + return val; + } + + /** Verify that `hr` is an HRESULT value. + Other types will generate an intentional compilation error. Note that this will accept any `long` value as that is the + underlying typedef behind HRESULT. + //! + Note that occasionally you might run into an HRESULT which is directly defined with a #define, such as: + ~~~~ + #define UIA_E_NOTSUPPORTED 0x80040204 + ~~~~ + Though this looks like an `HRESULT`, this is actually an `unsigned long` (the hex specification forces this). When + these are encountered and they are NOT in the public SDK (have not yet shipped to the public), then you should change + their definition to match the manner in which `HRESULT` constants are defined in winerror.h: + ~~~~ + #define E_NOTIMPL _HRESULT_TYPEDEF_(0x80004001L) + ~~~~ + When these are encountered in the public SDK, their type should not be changed and you should use a static_cast + to use this value in a macro that utilizes `verify_hresult`, for example: + ~~~~ + RETURN_HR_IF(static_cast(UIA_E_NOTSUPPORTED), (patternId != UIA_DragPatternId)); + ~~~~ + @param val The HRESULT returning expression + @return An HRESULT representing the evaluation of `val`. */ + template + _Post_satisfies_(return == hr) + inline constexpr long verify_hresult(T hr) + { + // Note: Written in terms of 'long' as HRESULT is actually: typedef _Return_type_success_(return >= 0) long HRESULT + static_assert(wistd::is_same::value, "Wrong Type: HRESULT expected"); + return hr; + } + + /** Verify that `status` is an NTSTATUS value. + Other types will generate an intentional compilation error. Note that this will accept any `long` value as that is the + underlying typedef behind NTSTATUS. + //! + Note that occasionally you might run into an NTSTATUS which is directly defined with a #define, such as: + ~~~~ + #define STATUS_NOT_SUPPORTED 0x1 + ~~~~ + Though this looks like an `NTSTATUS`, this is actually an `unsigned long` (the hex specification forces this). When + these are encountered and they are NOT in the public SDK (have not yet shipped to the public), then you should change + their definition to match the manner in which `NTSTATUS` constants are defined in ntstatus.h: + ~~~~ + #define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL) + ~~~~ + When these are encountered in the public SDK, their type should not be changed and you should use a static_cast + to use this value in a macro that utilizes `verify_ntstatus`, for example: + ~~~~ + NT_RETURN_IF_FALSE(static_cast(STATUS_NOT_SUPPORTED), (dispatch->Version == HKE_V1_0)); + ~~~~ + @param val The NTSTATUS returning expression + @return An NTSTATUS representing the evaluation of `val`. */ + template + _Post_satisfies_(return == status) + inline long verify_ntstatus(T status) + { + // Note: Written in terms of 'long' as NTSTATUS is actually: typedef _Return_type_success_(return >= 0) long NTSTATUS + static_assert(wistd::is_same::value, "Wrong Type: NTSTATUS expected"); + return status; + } + /// @} // end type validation routines + + /// @cond + // Implementation details for macros and helper functions... do not use directly. + namespace details + { + // Use size-specific casts to avoid sign extending numbers -- avoid warning C4310: cast truncates constant value + #define __WI_MAKE_UNSIGNED(val) \ + (__pragma(warning(push)) __pragma(warning(disable: 4310 4309)) (sizeof(val) == 1 ? static_cast(val) : \ + sizeof(val) == 2 ? static_cast(val) : \ + sizeof(val) == 4 ? static_cast(val) : \ + static_cast(val)) __pragma(warning(pop))) + #define __WI_IS_UNSIGNED_SINGLE_FLAG_SET(val) ((val) && !((val) & ((val) - 1))) + #define __WI_IS_SINGLE_FLAG_SET(val) __WI_IS_UNSIGNED_SINGLE_FLAG_SET(__WI_MAKE_UNSIGNED(val)) + + template + __forceinline constexpr bool AreAllFlagsSetHelper(TVal val, TFlags flags) + { + return ((val & flags) == static_cast(flags)); + } + + template + __forceinline constexpr bool IsSingleFlagSetHelper(TVal val) + { + return __WI_IS_SINGLE_FLAG_SET(val); + } + + template + __forceinline constexpr bool IsClearOrSingleFlagSetHelper(TVal val) + { + return ((val == static_cast>(0)) || IsSingleFlagSetHelper(val)); + } + + template + __forceinline constexpr void UpdateFlagsInMaskHelper(_Inout_ TVal& val, TMask mask, TFlags flags) + { + val = static_cast>((val & ~mask) | (flags & mask)); + } + + template + struct variable_size; + + template <> + struct variable_size<1> + { + typedef unsigned char type; + }; + + template <> + struct variable_size<2> + { + typedef unsigned short type; + }; + + template <> + struct variable_size<4> + { + typedef unsigned long type; + }; + + template <> + struct variable_size<8> + { + typedef unsigned long long type; + }; + + template + struct variable_size_mapping + { + typedef typename variable_size::type type; + }; + } // details + /// @endcond + + /** Defines the unsigned type of the same width (1, 2, 4, or 8 bytes) as the given type. + This allows code to generically convert any enum class to it's corresponding underlying type. */ + template + using integral_from_enum = typename details::variable_size_mapping::type; +} // wil + +#pragma warning(pop) + +#endif // __cplusplus +#endif // __WIL_COMMON_INCLUDED diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/cppwinrt.h b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/cppwinrt.h new file mode 100644 index 0000000..b873620 --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/cppwinrt.h @@ -0,0 +1,319 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_CPPWINRT_INCLUDED +#define __WIL_CPPWINRT_INCLUDED + +#include "common.h" +#include +#include +#include +#include + +// WIL and C++/WinRT use two different exception types for communicating HRESULT failures. Thus, both libraries need to +// understand how to translate these exception types into the correct HRESULT values at the ABI boundary. Prior to +// C++/WinRT "2.0" this was accomplished by injecting the WINRT_EXTERNAL_CATCH_CLAUSE macro - that WIL defines below - +// into its exception handler (winrt::to_hresult). Starting with C++/WinRT "2.0" this mechanism has shifted to a global +// function pointer - winrt_to_hresult_handler - that WIL sets automatically when this header is included and +// 'CPPWINRT_SUPPRESS_STATIC_INITIALIZERS' is not defined. + +/// @cond +namespace wil::details +{ + // Since the C++/WinRT version macro is a string... + // For example: "2.0.210122.3" + inline constexpr int version_from_string(const char* versionString) + { + int result = 0; + auto str = versionString; + while ((*str >= '0') && (*str <= '9')) + { + result = result * 10 + (*str - '0'); + ++str; + } + + return result; + } + + inline constexpr int major_version_from_string(const char* versionString) + { + return version_from_string(versionString); + } + + inline constexpr int minor_version_from_string(const char* versionString) + { + auto str = versionString; + int dotCount = 0; + while ((*str != '\0')) + { + if (*str == '.') + { + ++dotCount; + } + + ++str; + if (dotCount == 2) + { + break; + } + } + + if (*str == '\0') + { + return 0; + } + + return version_from_string(str); + } +} +/// @endcond + +#ifdef CPPWINRT_VERSION +// Prior to C++/WinRT "2.0" this header needed to be included before 'winrt/base.h' so that our definition of +// 'WINRT_EXTERNAL_CATCH_CLAUSE' would get picked up in the implementation of 'winrt::to_hresult'. This is no longer +// problematic, so only emit an error when using a version of C++/WinRT prior to 2.0 +static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2, + "Please include wil/cppwinrt.h before including any C++/WinRT headers"); +#endif + +// NOTE: Will eventually be removed once C++/WinRT 2.0 use can be assumed +#ifdef WINRT_EXTERNAL_CATCH_CLAUSE +#define __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE 1 +#else +#define WINRT_EXTERNAL_CATCH_CLAUSE \ + catch (const wil::ResultException& e) \ + { \ + return winrt::hresult_error(e.GetErrorCode(), winrt::to_hstring(e.what())).to_abi(); \ + } +#endif + +#include "result_macros.h" +#include + +#if __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE +static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2, + "C++/WinRT external catch clause already defined outside of WIL"); +#endif + +// In C++/WinRT 2.0 and beyond, this function pointer exists. In earlier versions it does not. It's much easier to avoid +// linker errors than it is to SFINAE on variable existence, so we declare the variable here, but are careful not to +// use it unless the version of C++/WinRT is high enough +extern std::int32_t(__stdcall* winrt_to_hresult_handler)(void*) noexcept; + +// The same is true with this function pointer as well, except that the version must be 2.X or higher. +extern void(__stdcall* winrt_throw_hresult_handler)(uint32_t, char const*, char const*, void*, winrt::hresult const) noexcept; + +/// @cond +namespace wil::details +{ + inline void MaybeGetExceptionString( + const winrt::hresult_error& exception, + _Out_writes_opt_(debugStringChars) PWSTR debugString, + _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars) + { + if (debugString) + { + StringCchPrintfW(debugString, debugStringChars, L"winrt::hresult_error: %ls", exception.message().c_str()); + } + } + + inline HRESULT __stdcall ResultFromCaughtException_CppWinRt( + _Inout_updates_opt_(debugStringChars) PWSTR debugString, + _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, + _Inout_ bool* isNormalized) noexcept + { + if (g_pfnResultFromCaughtException) + { + try + { + throw; + } + catch (const ResultException& exception) + { + *isNormalized = true; + MaybeGetExceptionString(exception, debugString, debugStringChars); + return exception.GetErrorCode(); + } + catch (const winrt::hresult_error& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return exception.to_abi(); + } + catch (const std::bad_alloc& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return E_OUTOFMEMORY; + } + catch (const std::out_of_range& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return E_BOUNDS; + } + catch (const std::invalid_argument& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return E_INVALIDARG; + } + catch (...) + { + auto hr = RecognizeCaughtExceptionFromCallback(debugString, debugStringChars); + if (FAILED(hr)) + { + return hr; + } + } + } + else + { + try + { + throw; + } + catch (const ResultException& exception) + { + *isNormalized = true; + MaybeGetExceptionString(exception, debugString, debugStringChars); + return exception.GetErrorCode(); + } + catch (const winrt::hresult_error& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return exception.to_abi(); + } + catch (const std::bad_alloc& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return E_OUTOFMEMORY; + } + catch (const std::out_of_range& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return E_BOUNDS; + } + catch (const std::invalid_argument& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return E_INVALIDARG; + } + catch (const std::exception& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION); + } + catch (...) + { + // Fall through to returning 'S_OK' below + } + } + + // Tell the caller that we were unable to map the exception by succeeding... + return S_OK; + } +} +/// @endcond + +namespace wil +{ + inline std::int32_t __stdcall winrt_to_hresult(void* returnAddress) noexcept + { + // C++/WinRT only gives us the return address (caller), so pass along an empty 'DiagnosticsInfo' since we don't + // have accurate file/line/etc. information + return static_cast(details::ReportFailure_CaughtException(__R_DIAGNOSTICS_RA(DiagnosticsInfo{}, returnAddress))); + } + + inline void __stdcall winrt_throw_hresult(uint32_t lineNumber, char const* fileName, char const* functionName, void* returnAddress, winrt::hresult const result) noexcept + { + void* callerReturnAddress{nullptr}; PCSTR code{nullptr}; + wil::details::ReportFailure_Hr(__R_FN_CALL_FULL __R_COMMA result); + } + + inline void WilInitialize_CppWinRT() + { + details::g_pfnResultFromCaughtException_CppWinRt = details::ResultFromCaughtException_CppWinRt; + if constexpr (details::major_version_from_string(CPPWINRT_VERSION) >= 2) + { + WI_ASSERT(winrt_to_hresult_handler == nullptr); + winrt_to_hresult_handler = winrt_to_hresult; + } + + if constexpr ((details::major_version_from_string(CPPWINRT_VERSION) >= 2) && (details::minor_version_from_string(CPPWINRT_VERSION) >= 210122)) + { + WI_ASSERT(winrt_throw_hresult_handler == nullptr); + winrt_throw_hresult_handler = winrt_throw_hresult; + } + } + + /// @cond + namespace details + { +#ifndef CPPWINRT_SUPPRESS_STATIC_INITIALIZERS + WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "0") + WI_HEADER_INITITALIZATION_FUNCTION(WilInitialize_CppWinRT, [] + { + ::wil::WilInitialize_CppWinRT(); + return 1; + }); +#else + WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "1") +#endif + } + /// @endcond + + // Provides an overload of verify_hresult so that the WIL macros can recognize winrt::hresult as a valid "hresult" type. + inline long verify_hresult(winrt::hresult hr) noexcept + { + return hr; + } + + // Provides versions of get_abi and put_abi for genericity that directly use HSTRING for convenience. + template + auto get_abi(T const& object) noexcept + { + return winrt::get_abi(object); + } + + inline auto get_abi(winrt::hstring const& object) noexcept + { + return static_cast(winrt::get_abi(object)); + } + + template + auto put_abi(T& object) noexcept + { + return winrt::put_abi(object); + } + + inline auto put_abi(winrt::hstring& object) noexcept + { + return reinterpret_cast(winrt::put_abi(object)); + } + + inline ::IUnknown* com_raw_ptr(const winrt::Windows::Foundation::IUnknown& ptr) noexcept + { + return static_cast<::IUnknown*>(winrt::get_abi(ptr)); + } + + // Needed to power wil::cx_object_from_abi that requires IInspectable + inline ::IInspectable* com_raw_ptr(const winrt::Windows::Foundation::IInspectable& ptr) noexcept + { + return static_cast<::IInspectable*>(winrt::get_abi(ptr)); + } + + // Taken from the docs.microsoft.com article + template + T convert_from_abi(::IUnknown* from) + { + T to{ nullptr }; // `T` is a projected type. + winrt::check_hresult(from->QueryInterface(winrt::guid_of(), winrt::put_abi(to))); + return to; + } +} + +#endif // __WIL_CPPWINRT_INCLUDED diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/cppwinrt_wrl.h b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/cppwinrt_wrl.h new file mode 100644 index 0000000..afd6f8e --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/cppwinrt_wrl.h @@ -0,0 +1,74 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_CPPWINRT_WRL_INCLUDED +#define __WIL_CPPWINRT_WRL_INCLUDED + +#include "cppwinrt.h" +#include + +#include "result_macros.h" +#include + +// wil::wrl_factory_for_winrt_com_class provides interopability between a +// C++/WinRT class and the WRL Module system, allowing the winrt class to be +// CoCreatable. +// +// Usage: +// - In your cpp, add: +// CoCreatableCppWinRtClass(className) +// +// - In the dll.cpp (or equivalent) for the module containing your class, add: +// CoCreatableClassWrlCreatorMapInclude(className) +// +namespace wil +{ + namespace details + { + template + class module_count_wrapper : public TCppWinRTClass + { + public: + module_count_wrapper() + { + if (auto modulePtr = ::Microsoft::WRL::GetModuleBase()) + { + modulePtr->IncrementObjectCount(); + } + } + + virtual ~module_count_wrapper() + { + if (auto modulePtr = ::Microsoft::WRL::GetModuleBase()) + { + modulePtr->DecrementObjectCount(); + } + } + }; + } + + template + class wrl_factory_for_winrt_com_class : public ::Microsoft::WRL::ClassFactory<> + { + public: + IFACEMETHODIMP CreateInstance(_In_opt_ ::IUnknown* unknownOuter, REFIID riid, _COM_Outptr_ void **object) noexcept try + { + *object = nullptr; + RETURN_HR_IF(CLASS_E_NOAGGREGATION, unknownOuter != nullptr); + + return winrt::make>().as(riid, object); + } + CATCH_RETURN() + }; +} + +#define CoCreatableCppWinRtClass(className) CoCreatableClassWithFactory(className, ::wil::wrl_factory_for_winrt_com_class) + +#endif // __WIL_CPPWINRT_WRL_INCLUDED diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/filesystem.h b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/filesystem.h new file mode 100644 index 0000000..4df11a0 --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/filesystem.h @@ -0,0 +1,1016 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_FILESYSTEM_INCLUDED +#define __WIL_FILESYSTEM_INCLUDED + +#ifdef _KERNEL_MODE +#error This header is not supported in kernel-mode. +#endif + +#include +#include // Needed for CoTaskMemFree() used in output of some helpers. +#include // LocalAlloc +#include +#include "result.h" +#include "win32_helpers.h" +#include "resource.h" + +namespace wil +{ + //! Determines if a path is an extended length path that can be used to access paths longer than MAX_PATH. + inline bool is_extended_length_path(_In_ PCWSTR path) + { + return wcsncmp(path, L"\\\\?\\", 4) == 0; + } + +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN7) + //! Find the last segment of a path. Matches the behavior of shlwapi!PathFindFileNameW() + //! note, does not support streams being specified like PathFindFileNameW(), is that a bug or a feature? + inline PCWSTR find_last_path_segment(_In_ PCWSTR path) + { + auto const pathLength = wcslen(path); + // If there is a trailing slash ignore that in the search. + auto const limitedLength = ((pathLength > 0) && (path[pathLength - 1] == L'\\')) ? (pathLength - 1) : pathLength; + + PCWSTR result; + auto const offset = FindStringOrdinal(FIND_FROMEND, path, static_cast(limitedLength), L"\\", 1, TRUE); + if (offset == -1) + { + result = path + pathLength; // null terminator + } + else + { + result = path + offset + 1; // just past the slash + } + return result; + } +#endif + + //! Determine if the file name is one of the special "." or ".." names. + inline bool path_is_dot_or_dotdot(_In_ PCWSTR fileName) + { + return ((fileName[0] == L'.') && + ((fileName[1] == L'\0') || ((fileName[1] == L'.') && (fileName[2] == L'\0')))); + } + + //! Returns the drive number, if it has one. Returns true if there is a drive number, false otherwise. Supports regular and extended length paths. + inline bool try_get_drive_letter_number(_In_ PCWSTR path, _Out_ int* driveNumber) + { + if (path[0] == L'\\' && path[1] == L'\\' && path[2] == L'?' && path[3] == L'\\') + { + path += 4; + } + if (path[0] && (path[1] == L':')) + { + if ((path[0] >= L'a') && (path[0] <= L'z')) + { + *driveNumber = path[0] - L'a'; + return true; + } + else if ((path[0] >= L'A') && (path[0] <= L'Z')) + { + *driveNumber = path[0] - L'A'; + return true; + } + } + *driveNumber = -1; + return false; + } + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && (_WIN32_WINNT >= _WIN32_WINNT_WIN7) + + // PathCch.h APIs are only in desktop API for now. + + // Compute the substring in the input value that is the parent folder path. + // returns: + // true + parentPathLength - path has a parent starting at the beginning path and of parentPathLength length. + // false, no parent path, the input is a root path. + inline bool try_get_parent_path_range(_In_ PCWSTR path, _Out_ size_t* parentPathLength) + { + *parentPathLength = 0; + bool hasParent = false; + PCWSTR rootEnd; + if (SUCCEEDED(PathCchSkipRoot(path, &rootEnd)) && (*rootEnd != L'\0')) + { + auto const lastSegment = find_last_path_segment(path); + *parentPathLength = lastSegment - path; + hasParent = (*parentPathLength != 0); + } + return hasParent; + } + + // Creates directories for the specified path, creating parent paths + // as needed. + inline HRESULT CreateDirectoryDeepNoThrow(PCWSTR path) WI_NOEXCEPT + { + if (::CreateDirectoryW(path, nullptr) == FALSE) + { + DWORD const lastError = ::GetLastError(); + if (lastError == ERROR_PATH_NOT_FOUND) + { + size_t parentLength; + if (try_get_parent_path_range(path, &parentLength)) + { + wistd::unique_ptr parent(new (std::nothrow) wchar_t[parentLength + 1]); + RETURN_IF_NULL_ALLOC(parent.get()); + RETURN_IF_FAILED(StringCchCopyNW(parent.get(), parentLength + 1, path, parentLength)); + CreateDirectoryDeepNoThrow(parent.get()); // recurs + } + RETURN_IF_WIN32_BOOL_FALSE(::CreateDirectoryW(path, nullptr)); + } + else if (lastError != ERROR_ALREADY_EXISTS) + { + RETURN_WIN32(lastError); + } + } + return S_OK; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + inline void CreateDirectoryDeep(PCWSTR path) + { + THROW_IF_FAILED(CreateDirectoryDeepNoThrow(path)); + } +#endif // WIL_ENABLE_EXCEPTIONS + + //! A strongly typed version of the Win32 API GetFullPathNameW. + //! Return a path in an allocated buffer for handling long paths. + //! Optionally return the pointer to the file name part. + template + HRESULT GetFullPathNameW(PCWSTR file, string_type& path, _Outptr_opt_ PCWSTR* filePart = nullptr) + { + wil::assign_null_to_opt_param(filePart); + const auto hr = AdaptFixedSizeToAllocatedResult(path, + [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNull) -> HRESULT + { + // Note that GetFullPathNameW() is not limited to MAX_PATH + // but it does take a fixed size buffer. + *valueLengthNeededWithNull = ::GetFullPathNameW(file, static_cast(valueLength), value, nullptr); + RETURN_LAST_ERROR_IF(*valueLengthNeededWithNull == 0); + WI_ASSERT((*value != L'\0') == (*valueLengthNeededWithNull < valueLength)); + if (*valueLengthNeededWithNull < valueLength) + { + (*valueLengthNeededWithNull)++; // it fit, account for the null + } + return S_OK; + }); + if (SUCCEEDED(hr) && filePart) + { + *filePart = wil::find_last_path_segment(details::string_maker::get(path)); + } + return hr; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + //! A strongly typed version of the Win32 API of GetFullPathNameW. + //! Return a path in an allocated buffer for handling long paths. + //! Optionally return the pointer to the file name part. + template + string_type GetFullPathNameW(PCWSTR file, _Outptr_opt_ PCWSTR* filePart = nullptr) + { + string_type result; + THROW_IF_FAILED((GetFullPathNameW(file, result, filePart))); + return result; + } +#endif + + enum class RemoveDirectoryOptions + { + None = 0, + KeepRootDirectory = 0x1, + RemoveReadOnly = 0x2, + }; + DEFINE_ENUM_FLAG_OPERATORS(RemoveDirectoryOptions); + + namespace details + { + // Reparse points should not be traversed in most recursive walks of the file system, + // unless allowed through the appropriate reparse tag. + inline bool CanRecurseIntoDirectory(const FILE_ATTRIBUTE_TAG_INFO& info) + { + return (WI_IsFlagSet(info.FileAttributes, FILE_ATTRIBUTE_DIRECTORY) && + (WI_IsFlagClear(info.FileAttributes, FILE_ATTRIBUTE_REPARSE_POINT) || + (IsReparseTagDirectory(info.ReparseTag) || (info.ReparseTag == IO_REPARSE_TAG_WCI)))); + } + } + + // Retrieve a handle to a directory only if it is safe to recurse into. + inline wil::unique_hfile TryCreateFileCanRecurseIntoDirectory(PCWSTR path, PWIN32_FIND_DATAW fileFindData) + { + wil::unique_hfile result(CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, + nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr)); + if (result) + { + FILE_ATTRIBUTE_TAG_INFO fati; + if (GetFileInformationByHandleEx(result.get(), FileAttributeTagInfo, &fati, sizeof(fati)) && + details::CanRecurseIntoDirectory(fati)) + { + if (fileFindData) + { + // Refresh the found file's data now that we have secured the directory from external manipulation. + fileFindData->dwFileAttributes = fati.FileAttributes; + fileFindData->dwReserved0 = fati.ReparseTag; + } + } + else + { + result.reset(); + } + } + + return result; + } + + // If inputPath is a non-normalized name be sure to pass an extended length form to ensure + // it can be addressed and deleted. + inline HRESULT RemoveDirectoryRecursiveNoThrow(PCWSTR inputPath, RemoveDirectoryOptions options = RemoveDirectoryOptions::None) WI_NOEXCEPT + { + wil::unique_hlocal_string path; + PATHCCH_OPTIONS combineOptions = PATHCCH_NONE; + + if (is_extended_length_path(inputPath)) + { + path = wil::make_hlocal_string_nothrow(inputPath); + RETURN_IF_NULL_ALLOC(path); + // PathAllocCombine will convert extended length paths to regular paths if shorter than + // MAX_PATH, avoid that behavior to provide access inputPath with non-normalized names. + combineOptions = PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH; + } + else + { + // For regular paths normalize here to get consistent results when searching and deleting. + RETURN_IF_FAILED(wil::GetFullPathNameW(inputPath, path)); + combineOptions = PATHCCH_ALLOW_LONG_PATHS; + } + + wil::unique_hlocal_string searchPath; + RETURN_IF_FAILED(::PathAllocCombine(path.get(), L"*", combineOptions, &searchPath)); + + WIN32_FIND_DATAW fd; + wil::unique_hfind findHandle(::FindFirstFileW(searchPath.get(), &fd)); + RETURN_LAST_ERROR_IF(!findHandle); + + for (;;) + { + // skip "." and ".." + if (!(WI_IsFlagSet(fd.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY) && path_is_dot_or_dotdot(fd.cFileName))) + { + // Need to form an extended length path to provide the ability to delete paths > MAX_PATH + // and files with non-normalized names (dots or spaces at the end). + wil::unique_hlocal_string pathToDelete; + RETURN_IF_FAILED(::PathAllocCombine(path.get(), fd.cFileName, + PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH | PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, &pathToDelete)); + if (WI_IsFlagSet(fd.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY)) + { + // Get a handle to the directory to delete, preventing it from being replaced to prevent writes which could be used + // to bypass permission checks, and verify that it is not a name surrogate (e.g. symlink, mount point, etc). + wil::unique_hfile recursivelyDeletableDirectoryHandle = TryCreateFileCanRecurseIntoDirectory(pathToDelete.get(), &fd); + if (recursivelyDeletableDirectoryHandle) + { + RemoveDirectoryOptions localOptions = options; + RETURN_IF_FAILED(RemoveDirectoryRecursiveNoThrow(pathToDelete.get(), WI_ClearFlag(localOptions, RemoveDirectoryOptions::KeepRootDirectory))); + } + else if (WI_IsFlagSet(fd.dwFileAttributes, FILE_ATTRIBUTE_REPARSE_POINT)) + { + // This is a directory reparse point that should not be recursed. Delete it without traversing into it. + RETURN_IF_WIN32_BOOL_FALSE(::RemoveDirectoryW(pathToDelete.get())); + } + else + { + // Failed to grab a handle to the file or to read its attributes. This is not safe to recurse. + RETURN_WIN32(::GetLastError()); + } + } + else + { + // Try a DeleteFile. Some errors may be recoverable. + if (!::DeleteFileW(pathToDelete.get())) + { + // Fail for anything other than ERROR_ACCESS_DENIED with option to RemoveReadOnly available + bool potentiallyFixableReadOnlyProblem = + WI_IsFlagSet(options, RemoveDirectoryOptions::RemoveReadOnly) && ::GetLastError() == ERROR_ACCESS_DENIED; + RETURN_LAST_ERROR_IF(!potentiallyFixableReadOnlyProblem); + + // Fail if the file does not have read-only set, likely just an ACL problem + DWORD fileAttr = ::GetFileAttributesW(pathToDelete.get()); + RETURN_LAST_ERROR_IF(!WI_IsFlagSet(fileAttr, FILE_ATTRIBUTE_READONLY)); + + // Remove read-only flag, setting to NORMAL if completely empty + WI_ClearFlag(fileAttr, FILE_ATTRIBUTE_READONLY); + if (fileAttr == 0) + { + fileAttr = FILE_ATTRIBUTE_NORMAL; + } + + // Set the new attributes and try to delete the file again, returning any failure + ::SetFileAttributesW(pathToDelete.get(), fileAttr); + RETURN_IF_WIN32_BOOL_FALSE(::DeleteFileW(pathToDelete.get())); + } + } + } + + if (!::FindNextFileW(findHandle.get(), &fd)) + { + auto const err = ::GetLastError(); + if (err == ERROR_NO_MORE_FILES) + { + break; + } + RETURN_WIN32(err); + } + } + + if (WI_IsFlagClear(options, RemoveDirectoryOptions::KeepRootDirectory)) + { + RETURN_IF_WIN32_BOOL_FALSE(::RemoveDirectoryW(path.get())); + } + return S_OK; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + inline void RemoveDirectoryRecursive(PCWSTR path, RemoveDirectoryOptions options = RemoveDirectoryOptions::None) + { + THROW_IF_FAILED(RemoveDirectoryRecursiveNoThrow(path, options)); + } +#endif // WIL_ENABLE_EXCEPTIONS + + // Range based for that supports Win32 structures that use NextEntryOffset as the basis of traversing + // a result buffer that contains data. This is used in the following FileIO calls: + // FileStreamInfo, FILE_STREAM_INFO + // FileIdBothDirectoryInfo, FILE_ID_BOTH_DIR_INFO + // FileFullDirectoryInfo, FILE_FULL_DIR_INFO + // FileIdExtdDirectoryInfo, FILE_ID_EXTD_DIR_INFO + // ReadDirectoryChangesW, FILE_NOTIFY_INFORMATION + + template + struct next_entry_offset_iterator + { + // Fulfill std::iterator_traits requirements + using difference_type = ptrdiff_t; + using value_type = T; + using pointer = const T*; + using reference = const T&; +#ifdef _XUTILITY_ + using iterator_category = ::std::forward_iterator_tag; +#endif + + next_entry_offset_iterator(T *iterable = __nullptr) : current_(iterable) {} + + // range based for requires operator!=, operator++ and operator* to do its work + // on the type returned from begin() and end(), provide those here. + bool operator!=(const next_entry_offset_iterator& other) const { return current_ != other.current_; } + + next_entry_offset_iterator& operator++() + { + current_ = (current_->NextEntryOffset != 0) ? + reinterpret_cast(reinterpret_cast(current_) + current_->NextEntryOffset) : + __nullptr; + return *this; + } + + next_entry_offset_iterator operator++(int) + { + auto copy = *this; + ++(*this); + return copy; + } + + reference operator*() const WI_NOEXCEPT { return *current_; } + pointer operator->() const WI_NOEXCEPT { return current_; } + + next_entry_offset_iterator begin() { return *this; } + next_entry_offset_iterator end() { return next_entry_offset_iterator(); } + + T* current_; + }; + + template + next_entry_offset_iterator create_next_entry_offset_iterator(T* p) + { + return next_entry_offset_iterator(p); + } + +#pragma region Folder Watcher + // Example use in exception based code: + // auto watcher = wil::make_folder_watcher(folder.Path().c_str(), true, wil::allChangeEvents, []() + // { + // // respond + // }); + // + // Example use in result code based code: + // wil::unique_folder_watcher watcher; + // THROW_IF_FAILED(watcher.create(folder, true, wil::allChangeEvents, []() + // { + // // respond + // })); + + enum class FolderChangeEvent : DWORD + { + ChangesLost = 0, // requies special handling, reset state as events were lost + Added = FILE_ACTION_ADDED, + Removed = FILE_ACTION_REMOVED, + Modified = FILE_ACTION_MODIFIED, + RenameOldName = FILE_ACTION_RENAMED_OLD_NAME, + RenameNewName = FILE_ACTION_RENAMED_NEW_NAME, + }; + + enum class FolderChangeEvents : DWORD + { + None = 0, + FileName = FILE_NOTIFY_CHANGE_FILE_NAME, + DirectoryName = FILE_NOTIFY_CHANGE_DIR_NAME, + Attributes = FILE_NOTIFY_CHANGE_ATTRIBUTES, + FileSize = FILE_NOTIFY_CHANGE_SIZE, + LastWriteTime = FILE_NOTIFY_CHANGE_LAST_WRITE, + Security = FILE_NOTIFY_CHANGE_SECURITY, + All = FILE_NOTIFY_CHANGE_FILE_NAME | + FILE_NOTIFY_CHANGE_DIR_NAME | + FILE_NOTIFY_CHANGE_ATTRIBUTES | + FILE_NOTIFY_CHANGE_SIZE | + FILE_NOTIFY_CHANGE_LAST_WRITE | + FILE_NOTIFY_CHANGE_SECURITY + }; + DEFINE_ENUM_FLAG_OPERATORS(FolderChangeEvents); + + /// @cond + namespace details + { + struct folder_watcher_state + { + folder_watcher_state(wistd::function &&callback) : m_callback(wistd::move(callback)) + { + } + wistd::function m_callback; + // Order is important, need to close the thread pool wait before the change handle. + unique_hfind_change m_findChangeHandle; + unique_threadpool_wait m_threadPoolWait; + }; + + inline void delete_folder_watcher_state(_In_opt_ folder_watcher_state *storage) { delete storage; } + + typedef resource_policy folder_watcher_state_resource_policy; + } + /// @endcond + + template + class folder_watcher_t : public storage_t + { + public: + // forward all base class constructors... + template + explicit folder_watcher_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} + + // HRESULT or void error handling... + typedef typename err_policy::result result; + + // Exception-based constructors + folder_watcher_t(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function &&callback) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); + create(folderToWatch, isRecursive, filter, wistd::move(callback)); + } + + result create(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function &&callback) + { + return err_policy::HResult(create_common(folderToWatch, isRecursive, filter, wistd::move(callback))); + } + private: + // Factored into a standalone function to support Clang which does not support conversion of stateless lambdas + // to __stdcall + static void __stdcall callback(PTP_CALLBACK_INSTANCE /*Instance*/, void *context, TP_WAIT *pThreadPoolWait, TP_WAIT_RESULT /*result*/) + { + auto watcherState = static_cast(context); + watcherState->m_callback(); + + // Rearm the wait. Should not fail with valid parameters. + FindNextChangeNotification(watcherState->m_findChangeHandle.get()); + SetThreadpoolWait(pThreadPoolWait, watcherState->m_findChangeHandle.get(), __nullptr); + } + + // This function exists to avoid template expansion of this code based on err_policy. + HRESULT create_common(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function &&callback) + { + wistd::unique_ptr watcherState(new(std::nothrow) details::folder_watcher_state(wistd::move(callback))); + RETURN_IF_NULL_ALLOC(watcherState); + + watcherState->m_findChangeHandle.reset(FindFirstChangeNotificationW(folderToWatch, isRecursive, static_cast(filter))); + RETURN_LAST_ERROR_IF(!watcherState->m_findChangeHandle); + + watcherState->m_threadPoolWait.reset(CreateThreadpoolWait(&folder_watcher_t::callback, watcherState.get(), __nullptr)); + RETURN_LAST_ERROR_IF(!watcherState->m_threadPoolWait); + this->reset(watcherState.release()); // no more failures after this, pass ownership + SetThreadpoolWait(this->get()->m_threadPoolWait.get(), this->get()->m_findChangeHandle.get(), __nullptr); + return S_OK; + } + }; + + typedef unique_any_t, err_returncode_policy>> unique_folder_watcher_nothrow; + + inline unique_folder_watcher_nothrow make_folder_watcher_nothrow(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function &&callback) WI_NOEXCEPT + { + unique_folder_watcher_nothrow watcher; + watcher.create(folderToWatch, isRecursive, filter, wistd::move(callback)); + return watcher; // caller must test for success using if (watcher) + } + +#ifdef WIL_ENABLE_EXCEPTIONS + typedef unique_any_t, err_exception_policy>> unique_folder_watcher; + + inline unique_folder_watcher make_folder_watcher(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function &&callback) + { + return unique_folder_watcher(folderToWatch, isRecursive, filter, wistd::move(callback)); + } +#endif // WIL_ENABLE_EXCEPTIONS + +#pragma endregion + +#pragma region Folder Reader + + // Example use for throwing: + // auto reader = wil::make_folder_change_reader(folder.Path().c_str(), true, wil::FolderChangeEvents::All, + // [](wil::FolderChangeEvent event, PCWSTR fileName) + // { + // switch (event) + // { + // case wil::FolderChangeEvent::ChangesLost: break; + // case wil::FolderChangeEvent::Added: break; + // case wil::FolderChangeEvent::Removed: break; + // case wil::FolderChangeEvent::Modified: break; + // case wil::FolderChangeEvent::RenamedOldName: break; + // case wil::FolderChangeEvent::RenamedNewName: break; + // }); + // + // Example use for non throwing: + // wil::unique_folder_change_reader_nothrow reader; + // THROW_IF_FAILED(reader.create(folder, true, wil::FolderChangeEvents::All, + // [](wil::FolderChangeEvent event, PCWSTR fileName) + // { + // // handle changes + // })); + // + + // @cond + namespace details + { + struct folder_change_reader_state + { + folder_change_reader_state(bool isRecursive, FolderChangeEvents filter, wistd::function &&callback) + : m_callback(wistd::move(callback)), m_isRecursive(isRecursive), m_filter(filter) + { + } + + ~folder_change_reader_state() + { + if (m_tpIo != __nullptr) + { + TP_IO *tpIo = m_tpIo; + + // Indicate to the callback function that this object is being torn + // down. + + { + auto autoLock = m_cancelLock.lock_exclusive(); + m_tpIo = __nullptr; + } + + // Cancel IO to terminate the file system monitoring operation. + + if (m_folderHandle) + { + CancelIoEx(m_folderHandle.get(), &m_overlapped); + } + + // Wait for callbacks to complete. + // + // N.B. This is a blocking call and must not be made within a + // callback or within a lock which is taken inside the + // callback. + + WaitForThreadpoolIoCallbacks(tpIo, TRUE); + CloseThreadpoolIo(tpIo); + } + } + + HRESULT StartIo() + { + // Unfortunately we have to handle ref-counting of IOs on behalf of the + // thread pool. + StartThreadpoolIo(m_tpIo); + HRESULT hr = ReadDirectoryChangesW(m_folderHandle.get(), m_readBuffer, sizeof(m_readBuffer), + m_isRecursive, static_cast(m_filter), __nullptr, &m_overlapped, __nullptr) ? + S_OK : HRESULT_FROM_WIN32(::GetLastError()); + if (FAILED(hr)) + { + // This operation does not have the usual semantic of returning + // ERROR_IO_PENDING. + // WI_ASSERT(hr != HRESULT_FROM_WIN32(ERROR_IO_PENDING)); + + // If the operation failed for whatever reason, ensure the TP + // ref counts are accurate. + + CancelThreadpoolIo(m_tpIo); + } + return hr; + } + + // void (wil::FolderChangeEvent event, PCWSTR fileName) + wistd::function m_callback; + unique_handle m_folderHandle; + BOOL m_isRecursive = FALSE; + FolderChangeEvents m_filter = FolderChangeEvents::None; + OVERLAPPED m_overlapped{}; + TP_IO *m_tpIo = __nullptr; + srwlock m_cancelLock; + char m_readBuffer[4096]; // Consider alternative buffer sizes. With 512 byte buffer i was not able to observe overflow. + }; + + inline void delete_folder_change_reader_state(_In_opt_ folder_change_reader_state *storage) { delete storage; } + + typedef resource_policy folder_change_reader_state_resource_policy; + } + /// @endcond + + template + class folder_change_reader_t : public storage_t + { + public: + // forward all base class constructors... + template + explicit folder_change_reader_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} + + // HRESULT or void error handling... + typedef typename err_policy::result result; + + // Exception-based constructors + folder_change_reader_t(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function &&callback) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); + create(folderToWatch, isRecursive, filter, wistd::move(callback)); + } + + result create(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function &&callback) + { + return err_policy::HResult(create_common(folderToWatch, isRecursive, filter, wistd::move(callback))); + } + + wil::unique_hfile& folder_handle() { return this->get()->m_folderHandle; } + + private: + // Factored into a standalone function to support Clang which does not support conversion of stateless lambdas + // to __stdcall + static void __stdcall callback(PTP_CALLBACK_INSTANCE /* Instance */, void *context, void * /*overlapped*/, + ULONG result, ULONG_PTR /* BytesTransferred */, TP_IO * /* Io */) + { + auto readerState = static_cast(context); + // WI_ASSERT(overlapped == &readerState->m_overlapped); + + bool requeue = true; + if (result == ERROR_SUCCESS) + { + for (auto const& info : create_next_entry_offset_iterator(reinterpret_cast(readerState->m_readBuffer))) + { + wchar_t realtiveFileName[MAX_PATH]; + StringCchCopyNW(realtiveFileName, ARRAYSIZE(realtiveFileName), info.FileName, info.FileNameLength / sizeof(info.FileName[0])); + + readerState->m_callback(static_cast(info.Action), realtiveFileName); + } + } + else if (result == ERROR_NOTIFY_ENUM_DIR) + { + readerState->m_callback(FolderChangeEvent::ChangesLost, __nullptr); + } + else + { + requeue = false; + } + + if (requeue) + { + // If the lock is held non-shared or the TP IO is nullptr, this + // structure is being torn down. Otherwise, monitor for further + // changes. + auto autoLock = readerState->m_cancelLock.try_lock_shared(); + if (autoLock && readerState->m_tpIo) + { + readerState->StartIo(); // ignoring failure here + } + } + } + + // This function exists to avoid template expansion of this code based on err_policy. + HRESULT create_common(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function &&callback) + { + wistd::unique_ptr readerState(new(std::nothrow) details::folder_change_reader_state( + isRecursive, filter, wistd::move(callback))); + RETURN_IF_NULL_ALLOC(readerState); + + readerState->m_folderHandle.reset(CreateFileW(folderToWatch, + FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE, + __nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, __nullptr)); + RETURN_LAST_ERROR_IF(!readerState->m_folderHandle); + + readerState->m_tpIo = CreateThreadpoolIo(readerState->m_folderHandle.get(), &folder_change_reader_t::callback, readerState.get(), __nullptr); + RETURN_LAST_ERROR_IF_NULL(readerState->m_tpIo); + RETURN_IF_FAILED(readerState->StartIo()); + this->reset(readerState.release()); + return S_OK; + } + }; + + typedef unique_any_t, err_returncode_policy>> unique_folder_change_reader_nothrow; + + inline unique_folder_change_reader_nothrow make_folder_change_reader_nothrow(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, + wistd::function &&callback) WI_NOEXCEPT + { + unique_folder_change_reader_nothrow watcher; + watcher.create(folderToWatch, isRecursive, filter, wistd::move(callback)); + return watcher; // caller must test for success using if (watcher) + } + +#ifdef WIL_ENABLE_EXCEPTIONS + typedef unique_any_t, err_exception_policy>> unique_folder_change_reader; + + inline unique_folder_change_reader make_folder_change_reader(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, + wistd::function &&callback) + { + return unique_folder_change_reader(folderToWatch, isRecursive, filter, wistd::move(callback)); + } +#endif // WIL_ENABLE_EXCEPTIONS +#pragma endregion + + //! Dos and VolumeGuid paths are always extended length paths with the \\?\ prefix. + enum class VolumePrefix + { + Dos = VOLUME_NAME_DOS, // Extended Dos Device path form, e.g. \\?\C:\Users\Chris\AppData\Local\Temp\wil8C31.tmp + VolumeGuid = VOLUME_NAME_GUID, // \\?\Volume{588fb606-b95b-4eae-b3cb-1e49861aaf18}\Users\Chris\AppData\Local\Temp\wil8C31.tmp + // The following are special paths which can't be used with Win32 APIs, but are useful in other scenarios. + None = VOLUME_NAME_NONE, // Path without the volume root, e.g. \Users\Chris\AppData\Local\Temp\wil8C31.tmp + NtObjectName = VOLUME_NAME_NT, // Unique name used by Object Manager, e.g. \Device\HarddiskVolume4\Users\Chris\AppData\Local\Temp\wil8C31.tmp + }; + enum class PathOptions + { + Normalized = FILE_NAME_NORMALIZED, + Opened = FILE_NAME_OPENED, + }; + DEFINE_ENUM_FLAG_OPERATORS(PathOptions); + + /** A strongly typed version of the Win32 API GetFinalPathNameByHandleW. + Get the full path name in different forms + Use this instead + VolumePrefix::None instead of GetFileInformationByHandleEx(FileNameInfo) to + get that path form. */ + template + HRESULT GetFinalPathNameByHandleW(HANDLE fileHandle, string_type& path, + wil::VolumePrefix volumePrefix = wil::VolumePrefix::Dos, wil::PathOptions options = wil::PathOptions::Normalized) + { + return AdaptFixedSizeToAllocatedResult(path, + [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNull) -> HRESULT + { + *valueLengthNeededWithNull = ::GetFinalPathNameByHandleW(fileHandle, value, static_cast(valueLength), + static_cast(volumePrefix) | static_cast(options)); + RETURN_LAST_ERROR_IF(*valueLengthNeededWithNull == 0); + WI_ASSERT((*value != L'\0') == (*valueLengthNeededWithNull < valueLength)); + if (*valueLengthNeededWithNull < valueLength) + { + (*valueLengthNeededWithNull)++; // it fit, account for the null + } + return S_OK; + }); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** A strongly typed version of the Win32 API GetFinalPathNameByHandleW. + Get the full path name in different forms. Use this + VolumePrefix::None + instead of GetFileInformationByHandleEx(FileNameInfo) to get that path form. */ + template + string_type GetFinalPathNameByHandleW(HANDLE fileHandle, + wil::VolumePrefix volumePrefix = wil::VolumePrefix::Dos, wil::PathOptions options = wil::PathOptions::Normalized) + { + string_type result; + THROW_IF_FAILED((GetFinalPathNameByHandleW(fileHandle, result, volumePrefix, options))); + return result; + } +#endif + + //! A strongly typed version of the Win32 API of GetCurrentDirectoryW. + //! Return a path in an allocated buffer for handling long paths. + template + HRESULT GetCurrentDirectoryW(string_type& path) + { + return AdaptFixedSizeToAllocatedResult(path, + [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNull) -> HRESULT + { + *valueLengthNeededWithNull = ::GetCurrentDirectoryW(static_cast(valueLength), value); + RETURN_LAST_ERROR_IF(*valueLengthNeededWithNull == 0); + WI_ASSERT((*value != L'\0') == (*valueLengthNeededWithNull < valueLength)); + if (*valueLengthNeededWithNull < valueLength) + { + (*valueLengthNeededWithNull)++; // it fit, account for the null + } + return S_OK; + }); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + //! A strongly typed version of the Win32 API of GetCurrentDirectoryW. + //! Return a path in an allocated buffer for handling long paths. + template + string_type GetCurrentDirectoryW() + { + string_type result; + THROW_IF_FAILED((GetCurrentDirectoryW(result))); + return result; + } +#endif + + // TODO: add support for these and other similar APIs. + // GetShortPathNameW() + // GetLongPathNameW() + // GetWindowsDirectory() + // GetTempDirectory() + + /// @cond + namespace details + { + template struct MapInfoClassToInfoStruct; // failure to map is a usage error caught by the compiler +#define MAP_INFOCLASS_TO_STRUCT(InfoClass, InfoStruct, IsFixed, Extra) \ + template <> struct MapInfoClassToInfoStruct \ + { \ + typedef InfoStruct type; \ + static bool const isFixed = IsFixed; \ + static size_t const extraSize = Extra; \ + }; + + MAP_INFOCLASS_TO_STRUCT(FileBasicInfo, FILE_BASIC_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileStandardInfo, FILE_STANDARD_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileNameInfo, FILE_NAME_INFO, false, 32); + MAP_INFOCLASS_TO_STRUCT(FileRenameInfo, FILE_RENAME_INFO, false, 32); + MAP_INFOCLASS_TO_STRUCT(FileDispositionInfo, FILE_DISPOSITION_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileAllocationInfo, FILE_ALLOCATION_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileEndOfFileInfo, FILE_END_OF_FILE_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileStreamInfo, FILE_STREAM_INFO, false, 32); + MAP_INFOCLASS_TO_STRUCT(FileCompressionInfo, FILE_COMPRESSION_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileAttributeTagInfo, FILE_ATTRIBUTE_TAG_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileIdBothDirectoryInfo, FILE_ID_BOTH_DIR_INFO, false, 4096); + MAP_INFOCLASS_TO_STRUCT(FileIdBothDirectoryRestartInfo, FILE_ID_BOTH_DIR_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileIoPriorityHintInfo, FILE_IO_PRIORITY_HINT_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileRemoteProtocolInfo, FILE_REMOTE_PROTOCOL_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileFullDirectoryInfo, FILE_FULL_DIR_INFO, false, 4096); + MAP_INFOCLASS_TO_STRUCT(FileFullDirectoryRestartInfo, FILE_FULL_DIR_INFO, true, 0); +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + MAP_INFOCLASS_TO_STRUCT(FileStorageInfo, FILE_STORAGE_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileAlignmentInfo, FILE_ALIGNMENT_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileIdInfo, FILE_ID_INFO, true, 0); + MAP_INFOCLASS_TO_STRUCT(FileIdExtdDirectoryInfo, FILE_ID_EXTD_DIR_INFO, false, 4096); + MAP_INFOCLASS_TO_STRUCT(FileIdExtdDirectoryRestartInfo, FILE_ID_EXTD_DIR_INFO, true, 0); +#endif + + // Type unsafe version used in the implementation to avoid template bloat. + inline HRESULT GetFileInfo(HANDLE fileHandle, FILE_INFO_BY_HANDLE_CLASS infoClass, size_t allocationSize, + _Outptr_result_nullonfailure_ void **result) + { + *result = nullptr; + + wistd::unique_ptr resultHolder(new(std::nothrow) char[allocationSize]); + RETURN_IF_NULL_ALLOC(resultHolder); + + for (;;) + { + if (GetFileInformationByHandleEx(fileHandle, infoClass, resultHolder.get(), static_cast(allocationSize))) + { + *result = resultHolder.release(); + break; + } + else + { + DWORD const lastError = ::GetLastError(); + if (lastError == ERROR_MORE_DATA) + { + allocationSize *= 2; + resultHolder.reset(new(std::nothrow) char[allocationSize]); + RETURN_IF_NULL_ALLOC(resultHolder); + } + else if (lastError == ERROR_NO_MORE_FILES) // for folder enumeration cases + { + break; + } + else if (lastError == ERROR_INVALID_PARAMETER) // operation not supported by file system + { + return HRESULT_FROM_WIN32(lastError); + } + else + { + RETURN_WIN32(lastError); + } + } + } + return S_OK; + } + } + /// @endcond + + /** Get file information for a variable sized structure, returns an HRESULT. + ~~~ + wistd::unique_ptr fileNameInfo; + RETURN_IF_FAILED(GetFileInfoNoThrow(fileHandle, fileNameInfo)); + ~~~ + */ + template ::isFixed, int>::type = 0> + HRESULT GetFileInfoNoThrow(HANDLE fileHandle, wistd::unique_ptr::type> &result) WI_NOEXCEPT + { + void *rawResult; + HRESULT hr = details::GetFileInfo(fileHandle, infoClass, + sizeof(typename details::MapInfoClassToInfoStruct::type) + details::MapInfoClassToInfoStruct::extraSize, + &rawResult); + result.reset(static_cast::type*>(rawResult)); + RETURN_HR_IF_EXPECTED(hr, hr == E_INVALIDARG); // operation not supported by file system + RETURN_IF_FAILED(hr); + return S_OK; + } + + /** Get file information for a fixed sized structure, returns an HRESULT. + ~~~ + FILE_BASIC_INFO fileBasicInfo; + RETURN_IF_FAILED(GetFileInfoNoThrow(fileHandle, &fileBasicInfo)); + ~~~ + */ + template ::isFixed, int>::type = 0> + HRESULT GetFileInfoNoThrow(HANDLE fileHandle, _Out_ typename details::MapInfoClassToInfoStruct::type *result) WI_NOEXCEPT + { + const HRESULT hr = GetFileInformationByHandleEx(fileHandle, infoClass, result, sizeof(*result)) ? + S_OK : HRESULT_FROM_WIN32(::GetLastError()); + RETURN_HR_IF_EXPECTED(hr, hr == E_INVALIDARG); // operation not supported by file system + RETURN_IF_FAILED(hr); + return S_OK; + } + + // Verifies that the given file path is not a hard or a soft link. If the file is present at the path, returns + // a handle to it without delete permissions to block an attacker from swapping the file. + inline HRESULT CreateFileAndEnsureNotLinked(PCWSTR path, wil::unique_hfile& fileHandle) + { + // Open handles to the original path and to the final path and compare each file's information + // to verify they are the same file. If they are different, the file is a soft link. + fileHandle.reset(CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr)); + RETURN_LAST_ERROR_IF(!fileHandle); + BY_HANDLE_FILE_INFORMATION fileInfo; + RETURN_IF_WIN32_BOOL_FALSE(GetFileInformationByHandle(fileHandle.get(), &fileInfo)); + + // Open a handle without the reparse point flag to get the final path in case it is a soft link. + wil::unique_hfile finalPathHandle(CreateFileW(path, 0, 0, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr)); + RETURN_LAST_ERROR_IF(!finalPathHandle); + BY_HANDLE_FILE_INFORMATION finalFileInfo; + RETURN_IF_WIN32_BOOL_FALSE(GetFileInformationByHandle(finalPathHandle.get(), &finalFileInfo)); + finalPathHandle.reset(); + + // The low and high indices and volume serial number uniquely identify a file. These must match if they are the same file. + const bool isSoftLink = + ((fileInfo.nFileIndexLow != finalFileInfo.nFileIndexLow) || + (fileInfo.nFileIndexHigh != finalFileInfo.nFileIndexHigh) || + (fileInfo.dwVolumeSerialNumber != finalFileInfo.dwVolumeSerialNumber)); + + // Return failure if it is a soft link or a hard link (number of links greater than 1). + RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME), (isSoftLink || fileInfo.nNumberOfLinks > 1)); + + return S_OK; + } + +#ifdef _CPPUNWIND + /** Get file information for a fixed sized structure, throws on failure. + ~~~ + auto fileBasicInfo = GetFileInfo(fileHandle); + ~~~ + */ + template ::isFixed, int>::type = 0> + typename details::MapInfoClassToInfoStruct::type GetFileInfo(HANDLE fileHandle) + { + typename details::MapInfoClassToInfoStruct::type result; + THROW_IF_FAILED(GetFileInfoNoThrow(fileHandle, &result)); + return result; + } + + /** Get file information for a variable sized structure, throws on failure. + ~~~ + auto fileBasicInfo = GetFileInfo(fileHandle); + ~~~ + */ + template ::isFixed, int>::type = 0> + wistd::unique_ptr::type> GetFileInfo(HANDLE fileHandle) + { + wistd::unique_ptr::type> result; + THROW_IF_FAILED(GetFileInfoNoThrow(fileHandle, result)); + return result; + } +#endif // _CPPUNWIND +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && (_WIN32_WINNT >= _WIN32_WINNT_WIN7) +} + +#endif // __WIL_FILESYSTEM_INCLUDED diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/nt_result_macros.h b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/nt_result_macros.h new file mode 100644 index 0000000..05925ea --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/nt_result_macros.h @@ -0,0 +1,168 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_NT_RESULTMACROS_INCLUDED +#define __WIL_NT_RESULTMACROS_INCLUDED + +#include "result_macros.h" + +// Helpers for return macros +#define __NT_RETURN_NTSTATUS(status, str) __WI_SUPPRESS_4127_S do { NTSTATUS __status = (status); if (FAILED_NTSTATUS(__status)) { __R_FN(Return_NtStatus)(__R_INFO(str) __status); } return __status; } while ((void)0, 0) +#define __NT_RETURN_NTSTATUS_MSG(status, str, fmt, ...) __WI_SUPPRESS_4127_S do { NTSTATUS __status = (status); if (FAILED_NTSTATUS(__status)) { __R_FN(Return_NtStatusMsg)(__R_INFO(str) __status, fmt, ##__VA_ARGS__); } return __status; } while ((void)0, 0) + +//***************************************************************************** +// Macros for returning failures as NTSTATUS +//***************************************************************************** + +// Always returns a known result (NTSTATUS) - always logs failures +#define NT_RETURN_NTSTATUS(status) __NT_RETURN_NTSTATUS(wil::verify_ntstatus(status), #status) + +// Always returns a known failure (NTSTATUS) - always logs a var-arg message on failure +#define NT_RETURN_NTSTATUS_MSG(status, fmt, ...) __NT_RETURN_NTSTATUS_MSG(wil::verify_ntstatus(status), #status, fmt, ##__VA_ARGS__) + +// Conditionally returns failures (NTSTATUS) - always logs failures +#define NT_RETURN_IF_NTSTATUS_FAILED(status) __WI_SUPPRESS_4127_S do { const auto __statusRet = wil::verify_ntstatus(status); if (FAILED_NTSTATUS(__statusRet)) { __NT_RETURN_NTSTATUS(__statusRet, #status); }} __WI_SUPPRESS_4127_E while ((void)0, 0) + +// Conditionally returns failures (NTSTATUS) - always logs a var-arg message on failure +#define NT_RETURN_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) __WI_SUPPRESS_4127_S do { const auto __statusRet = wil::verify_ntstatus(status); if (FAILED_NTSTATUS(__statusRet)) { __NT_RETURN_NTSTATUS_MSG(__statusRet, #status, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0) + +//***************************************************************************** +// Macros to catch and convert exceptions on failure +//***************************************************************************** + +// Use these macros *within* a catch (...) block to handle exceptions +#define NT_RETURN_CAUGHT_EXCEPTION() return __R_FN(Nt_Return_CaughtException)(__R_INFO_ONLY(nullptr)) +#define NT_RETURN_CAUGHT_EXCEPTION_MSG(fmt, ...) return __R_FN(Nt_Return_CaughtExceptionMsg)(__R_INFO(nullptr) fmt, ##__VA_ARGS__) + +// Use these macros in place of a catch block to handle exceptions +#define NT_CATCH_RETURN() catch (...) { NT_RETURN_CAUGHT_EXCEPTION(); } +#define NT_CATCH_RETURN_MSG(fmt, ...) catch (...) { NT_RETURN_CAUGHT_EXCEPTION_MSG(fmt, ##__VA_ARGS__); } + + +namespace wil +{ + //***************************************************************************** + // Public Helpers that catch -- mostly only enabled when exceptions are enabled + //***************************************************************************** + + // StatusFromCaughtException is a function that is meant to be called from within a catch(...) block. Internally + // it re-throws and catches the exception to convert it to an NTSTATUS. If an exception is of an unrecognized type + // the function will fail fast. + // + // try + // { + // // Code + // } + // catch (...) + // { + // status = wil::StatusFromCaughtException(); + // } + _Always_(_Post_satisfies_(return < 0)) + __declspec(noinline) inline NTSTATUS StatusFromCaughtException() WI_NOEXCEPT + { + bool isNormalized = false; + NTSTATUS status = STATUS_SUCCESS; + if (details::g_pfnResultFromCaughtExceptionInternal) + { + status = details::g_pfnResultFromCaughtExceptionInternal(nullptr, 0, &isNormalized).status; + } + if (FAILED_NTSTATUS(status)) + { + return status; + } + + // Caller bug: an unknown exception was thrown + __WIL_PRIVATE_FAIL_FAST_HR_IF(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION), g_fResultFailFastUnknownExceptions); + return wil::details::HrToNtStatus(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION)); + } + + namespace details + { + template + __declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported = SupportedExceptions::Default); + template + __declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList); + + namespace __R_NS_NAME + { +#ifdef WIL_ENABLE_EXCEPTIONS + __R_DIRECT_METHOD(NTSTATUS, Nt_Return_CaughtException)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __R_FN_LOCALS; + return wil::details::ReportStatus_CaughtException(__R_DIRECT_FN_CALL_ONLY); + } + + __R_DIRECT_METHOD(NTSTATUS, Nt_Return_CaughtExceptionMsg)(__R_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + return wil::details::ReportStatus_CaughtExceptionMsg(__R_DIRECT_FN_CALL formatString, argList); + } +#endif + } + + template + __declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported) + { + wchar_t message[2048]; + message[0] = L'\0'; + return ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status; + } + + template<> + __declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported) + { + wchar_t message[2048]; + message[0] = L'\0'; + RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status); + } + + template<> + __declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported) + { + wchar_t message[2048]; + message[0] = L'\0'; + RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status); + } + + template + __declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + // Pre-populate the buffer with our message, the exception message will be added to it... + wchar_t message[2048]; + PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); + StringCchCatW(message, ARRAYSIZE(message), L" -- "); + return ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status; + } + + template<> + __declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + // Pre-populate the buffer with our message, the exception message will be added to it... + wchar_t message[2048]; + PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); + StringCchCatW(message, ARRAYSIZE(message), L" -- "); + RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status); + } + + template<> + __declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + // Pre-populate the buffer with our message, the exception message will be added to it... + wchar_t message[2048]; + PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); + StringCchCatW(message, ARRAYSIZE(message), L" -- "); + RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status); + } + } +} + +#endif // __WIL_NT_RESULTMACROS_INCLUDED diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/registry.h b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/registry.h new file mode 100644 index 0000000..e70dd54 --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/registry.h @@ -0,0 +1,277 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_REGISTRY_INCLUDED +#define __WIL_REGISTRY_INCLUDED + +#ifdef _KERNEL_MODE +#error This header is not supported in kernel-mode. +#endif + +#include +#include // new(std::nothrow) +#include "resource.h" // unique_hkey + +namespace wil +{ + //! The key name includes the absolute path of the key in the registry, always starting at a + //! base key, for example, HKEY_LOCAL_MACHINE. + size_t const max_registry_key_name_length = 255; + + //! The maximum number of characters allowed in a registry value's name. + size_t const max_registry_value_name_length = 16383; + + // unique_registry_watcher/unique_registry_watcher_nothrow/unique_registry_watcher_failfast + // These classes make it easy to execute a provided function when a + // registry key changes (optionally recursively). Specify the key + // either as a root key + path, or an open registry handle as wil::unique_hkey + // or a raw HKEY value (that will be duplicated). + // + // Example use with exceptions base error handling: + // auto watcher = wil::make_registry_watcher(HKEY_CURRENT_USER, L"Software\\MyApp", true, wil::RegistryChangeKind changeKind[] + // { + // if (changeKind == RegistryChangeKind::Delete) + // { + // watcher.reset(); + // } + // // invalidate cached registry data here + // }); + // + // Example use with error code base error handling: + // auto watcher = wil::make_registry_watcher_nothrow(HKEY_CURRENT_USER, L"Software\\MyApp", true, wil::RegistryChangeKind[] + // { + // // invalidate cached registry data here + // }); + // RETURN_IF_NULL_ALLOC(watcher); + + enum class RegistryChangeKind + { + Modify = 0, + Delete = 1, + }; + + /// @cond + namespace details + { + struct registry_watcher_state + { + registry_watcher_state(unique_hkey &&keyToWatch, bool isRecursive, wistd::function &&callback) + : m_callback(wistd::move(callback)), m_keyToWatch(wistd::move(keyToWatch)), m_isRecursive(isRecursive) + { + } + wistd::function m_callback; + unique_hkey m_keyToWatch; + unique_event_nothrow m_eventHandle; + + // While not strictly needed since this is ref counted the thread pool wait + // should be last to ensure that the other members are valid + // when it is destructed as it will reference them. + unique_threadpool_wait m_threadPoolWait; + bool m_isRecursive; + + volatile long m_refCount = 1; + srwlock m_lock; + + // Returns true if the refcount can be increased from a non zero value, + // false it was zero impling that the object is in or on the way to the destructor. + // In this case ReleaseFromCallback() should not be called. + bool TryAddRef() + { + return ::InterlockedIncrement(&m_refCount) > 1; + } + + void Release() + { + auto lock = m_lock.lock_exclusive(); + if (0 == ::InterlockedDecrement(&m_refCount)) + { + lock.reset(); // leave the lock before deleting it. + delete this; + } + } + + void ReleaseFromCallback(bool rearm) + { + auto lock = m_lock.lock_exclusive(); + if (0 == ::InterlockedDecrement(&m_refCount)) + { + // Destroy the thread pool wait now to avoid the wait that would occur in the + // destructor. That wait would cause a deadlock since we are doing this from the callback. + ::CloseThreadpoolWait(m_threadPoolWait.release()); + lock.reset(); // leave the lock before deleting it. + delete this; + // Sleep(1); // Enable for testing to find use after free bugs. + } + else if (rearm) + { + ::SetThreadpoolWait(m_threadPoolWait.get(), m_eventHandle.get(), nullptr); + } + } + }; + + inline void delete_registry_watcher_state(_In_opt_ registry_watcher_state *watcherStorage) { watcherStorage->Release(); } + + typedef resource_policy registry_watcher_state_resource_policy; + } + /// @endcond + + template + class registry_watcher_t : public storage_t + { + public: + // forward all base class constructors... + template + explicit registry_watcher_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} + + // HRESULT or void error handling... + typedef typename err_policy::result result; + + // Exception-based constructors + registry_watcher_t(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function &&callback) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); + create(rootKey, subKey, isRecursive, wistd::move(callback)); + } + + registry_watcher_t(unique_hkey &&keyToWatch, bool isRecursive, wistd::function &&callback) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions; use the create method"); + create(wistd::move(keyToWatch), isRecursive, wistd::move(callback)); + } + + // Pass a root key, sub key pair or use an empty string to use rootKey as the key to watch. + result create(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function &&callback) + { + // Most use will want to create the key, consider adding an option for open as a future design change. + unique_hkey keyToWatch; + HRESULT hr = HRESULT_FROM_WIN32(RegCreateKeyExW(rootKey, subKey, 0, nullptr, 0, KEY_NOTIFY, nullptr, &keyToWatch, nullptr)); + if (FAILED(hr)) + { + return err_policy::HResult(hr); + } + return err_policy::HResult(create_common(wistd::move(keyToWatch), isRecursive, wistd::move(callback))); + } + + result create(unique_hkey &&keyToWatch, bool isRecursive, wistd::function &&callback) + { + return err_policy::HResult(create_common(wistd::move(keyToWatch), isRecursive, wistd::move(callback))); + } + + private: + // Factored into a standalone function to support Clang which does not support conversion of stateless lambdas + // to __stdcall + static void __stdcall callback(PTP_CALLBACK_INSTANCE, void *context, TP_WAIT *, TP_WAIT_RESULT) + { +#ifndef __WIL_REGISTRY_CHANGE_CALLBACK_TEST +#define __WIL_REGISTRY_CHANGE_CALLBACK_TEST +#endif + __WIL_REGISTRY_CHANGE_CALLBACK_TEST + auto watcherState = static_cast(context); + if (watcherState->TryAddRef()) + { + // using auto reset event so don't need to manually reset. + + // failure here is a programming error. + const LSTATUS error = RegNotifyChangeKeyValue(watcherState->m_keyToWatch.get(), watcherState->m_isRecursive, + REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_THREAD_AGNOSTIC, + watcherState->m_eventHandle.get(), TRUE); + + // Call the client before re-arming to ensure that multiple callbacks don't + // run concurrently. + switch (error) + { + case ERROR_SUCCESS: + case ERROR_ACCESS_DENIED: + // Normal modification: send RegistryChangeKind::Modify and re-arm. + watcherState->m_callback(RegistryChangeKind::Modify); + watcherState->ReleaseFromCallback(true); + break; + + case ERROR_KEY_DELETED: + // Key deleted, send RegistryChangeKind::Delete, do not re-arm. + watcherState->m_callback(RegistryChangeKind::Delete); + watcherState->ReleaseFromCallback(false); + break; + + case ERROR_HANDLE_REVOKED: + // Handle revoked. This can occur if the user session ends before + // the watcher shuts-down. Disarm silently since there is generally no way to respond. + watcherState->ReleaseFromCallback(false); + break; + + default: + FAIL_FAST_HR(HRESULT_FROM_WIN32(error)); + } + } + } + + // This function exists to avoid template expansion of this code based on err_policy. + HRESULT create_common(unique_hkey &&keyToWatch, bool isRecursive, wistd::function &&callback) + { + wistd::unique_ptr watcherState(new(std::nothrow) details::registry_watcher_state( + wistd::move(keyToWatch), isRecursive, wistd::move(callback))); + RETURN_IF_NULL_ALLOC(watcherState); + RETURN_IF_FAILED(watcherState->m_eventHandle.create()); + RETURN_IF_WIN32_ERROR(RegNotifyChangeKeyValue(watcherState->m_keyToWatch.get(), + watcherState->m_isRecursive, REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_THREAD_AGNOSTIC, + watcherState->m_eventHandle.get(), TRUE)); + + watcherState->m_threadPoolWait.reset(CreateThreadpoolWait(®istry_watcher_t::callback, watcherState.get(), nullptr)); + RETURN_LAST_ERROR_IF(!watcherState->m_threadPoolWait); + storage_t::reset(watcherState.release()); // no more failures after this, pass ownership + SetThreadpoolWait(storage_t::get()->m_threadPoolWait.get(), storage_t::get()->m_eventHandle.get(), nullptr); + return S_OK; + } + }; + + typedef unique_any_t, err_returncode_policy>> unique_registry_watcher_nothrow; + typedef unique_any_t, err_failfast_policy>> unique_registry_watcher_failfast; + + inline unique_registry_watcher_nothrow make_registry_watcher_nothrow(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function &&callback) WI_NOEXCEPT + { + unique_registry_watcher_nothrow watcher; + watcher.create(rootKey, subKey, isRecursive, wistd::move(callback)); + return watcher; // caller must test for success using if (watcher) + } + + inline unique_registry_watcher_nothrow make_registry_watcher_nothrow(unique_hkey &&keyToWatch, bool isRecursive, wistd::function &&callback) WI_NOEXCEPT + { + unique_registry_watcher_nothrow watcher; + watcher.create(wistd::move(keyToWatch), isRecursive, wistd::move(callback)); + return watcher; // caller must test for success using if (watcher) + } + + inline unique_registry_watcher_failfast make_registry_watcher_failfast(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function &&callback) + { + return unique_registry_watcher_failfast(rootKey, subKey, isRecursive, wistd::move(callback)); + } + + inline unique_registry_watcher_failfast make_registry_watcher_failfast(unique_hkey &&keyToWatch, bool isRecursive, wistd::function &&callback) + { + return unique_registry_watcher_failfast(wistd::move(keyToWatch), isRecursive, wistd::move(callback)); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + typedef unique_any_t, err_exception_policy >> unique_registry_watcher; + + inline unique_registry_watcher make_registry_watcher(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function &&callback) + { + return unique_registry_watcher(rootKey, subKey, isRecursive, wistd::move(callback)); + } + + inline unique_registry_watcher make_registry_watcher(unique_hkey &&keyToWatch, bool isRecursive, wistd::function &&callback) + { + return unique_registry_watcher(wistd::move(keyToWatch), isRecursive, wistd::move(callback)); + } +#endif // WIL_ENABLE_EXCEPTIONS +} // namespace wil + +#endif diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/resource.h b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/resource.h new file mode 100644 index 0000000..c87454a --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/resource.h @@ -0,0 +1,6473 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* + +#include "result_macros.h" +#include "wistd_functional.h" +#include "wistd_memory.h" + +#pragma warning(push) +#pragma warning(disable:26135 26110) // Missing locking annotation, Caller failing to hold lock +#pragma warning(disable:4714) // __forceinline not honored + +#ifndef __WIL_RESOURCE +#define __WIL_RESOURCE + +// stdint.h and intsafe.h have conflicting definitions, so it's not safe to include either to pick up our dependencies, +// so the definitions we need are copied below +#ifdef _WIN64 +#define __WI_SIZE_MAX 0xffffffffffffffffui64 // UINT64_MAX +#else /* _WIN64 */ +#define __WI_SIZE_MAX 0xffffffffui32 // UINT32_MAX +#endif /* _WIN64 */ + +// Forward declaration +/// @cond +namespace Microsoft +{ + namespace WRL + { + template + class ComPtr; + } +} +/// @endcond + +namespace wil +{ + //! This type copies the current value of GetLastError at construction and resets the last error + //! to that value when it is destroyed. + //! + //! This is useful in library code that runs during a value's destructor. If the library code could + //! inadvertantly change the value of GetLastError (by calling a Win32 API or similar), it should + //! instantiate a value of this type before calling the library function in order to preserve the + //! GetLastError value the user would expect. + //! + //! This construct exists to hide kernel mode/user mode differences in wil library code. + //! + //! Example usage: + //! + //! if (!CreateFile(...)) + //! { + //! auto lastError = wil::last_error_context(); + //! WriteFile(g_hlog, logdata); + //! } + //! + class last_error_context + { +#ifndef WIL_KERNEL_MODE + bool m_dismissed; + DWORD m_error; + public: + last_error_context() WI_NOEXCEPT : + m_dismissed(false), + m_error(::GetLastError()) + { + } + + auto value() const + { + return m_error; + } + + last_error_context(last_error_context&& other) WI_NOEXCEPT + { + operator=(wistd::move(other)); + } + + last_error_context & operator=(last_error_context&& other) WI_NOEXCEPT + { + m_dismissed = wistd::exchange(other.m_dismissed, true); + m_error = other.m_error; + + return *this; + } + + ~last_error_context() WI_NOEXCEPT + { + if (!m_dismissed) + { + ::SetLastError(m_error); + } + } + + //! last_error_context doesn't own a concrete resource, so therefore + //! it just disarms its destructor and returns void. + void release() WI_NOEXCEPT + { + WI_ASSERT(!m_dismissed); + m_dismissed = true; + } +#else + public: + void release() WI_NOEXCEPT { } +#endif // WIL_KERNEL_MODE + }; + + /// @cond + namespace details + { + typedef wistd::integral_constant pointer_access_all; // get(), release(), addressof(), and '&' are available + typedef wistd::integral_constant pointer_access_noaddress; // get() and release() are available + typedef wistd::integral_constant pointer_access_none; // the raw pointer is not available + + template // nullptr_t if the invalid handle value is compatible with nullptr, otherwise pointer + struct resource_policy + { + typedef pointer_storage_t pointer_storage; + typedef pointer_t pointer; + typedef pointer_invalid_t pointer_invalid; + typedef pointer_access_t pointer_access; + __forceinline static pointer_storage invalid_value() { return (pointer)invalid; } + __forceinline static bool is_valid(pointer_storage value) WI_NOEXCEPT { return (static_cast(value) != (pointer)invalid); } + __forceinline static void close(pointer_storage value) WI_NOEXCEPT { wistd::invoke(close_fn, value); } + + inline static void close_reset(pointer_storage value) WI_NOEXCEPT + { + auto preserveError = last_error_context(); + wistd::invoke(close_fn, value); + } + }; + + + // This class provides the pointer storage behind the implementation of unique_any_t utilizing the given + // resource_policy. It is separate from unique_any_t to allow a type-specific specialization class to plug + // into the inheritance chain between unique_any_t and unique_storage. This allows classes like unique_event + // to be a unique_any formed class, but also expose methods like SetEvent directly. + + template + class unique_storage + { + protected: + typedef Policy policy; + typedef typename policy::pointer_storage pointer_storage; + typedef typename policy::pointer pointer; + typedef unique_storage base_storage; + + unique_storage() WI_NOEXCEPT : + m_ptr(policy::invalid_value()) + { + } + + explicit unique_storage(pointer_storage ptr) WI_NOEXCEPT : + m_ptr(ptr) + { + } + + unique_storage(unique_storage &&other) WI_NOEXCEPT : + m_ptr(wistd::move(other.m_ptr)) + { + other.m_ptr = policy::invalid_value(); + } + + ~unique_storage() WI_NOEXCEPT + { + if (policy::is_valid(m_ptr)) + { + policy::close(m_ptr); + } + } + + void replace(unique_storage &&other) WI_NOEXCEPT + { + reset(other.m_ptr); + other.m_ptr = policy::invalid_value(); + } + + public: + bool is_valid() const WI_NOEXCEPT + { + return policy::is_valid(m_ptr); + } + + void reset(pointer_storage ptr = policy::invalid_value()) WI_NOEXCEPT + { + if (policy::is_valid(m_ptr)) + { + policy::close_reset(m_ptr); + } + m_ptr = ptr; + } + + void reset(wistd::nullptr_t) WI_NOEXCEPT + { + static_assert(wistd::is_same::value, "reset(nullptr): valid only for handle types using nullptr as the invalid value"); + reset(); + } + + pointer get() const WI_NOEXCEPT + { + return static_cast(m_ptr); + } + + pointer_storage release() WI_NOEXCEPT + { + static_assert(!wistd::is_same::value, "release(): the raw handle value is not available for this resource class"); + auto ptr = m_ptr; + m_ptr = policy::invalid_value(); + return ptr; + } + + pointer_storage *addressof() WI_NOEXCEPT + { + static_assert(wistd::is_same::value, "addressof(): the address of the raw handle is not available for this resource class"); + return &m_ptr; + } + + private: + pointer_storage m_ptr; + }; + } // details + /// @endcond + + + // This class when paired with unique_storage and an optional type-specific specialization class implements + // the same interface as STL's unique_ptr<> for resource handle types. It is a non-copyable, yet movable class + // supporting attach (reset), detach (release), retrieval (get()). + + template + class unique_any_t : public storage_t + { + public: + typedef typename storage_t::policy policy; + typedef typename policy::pointer_storage pointer_storage; + typedef typename policy::pointer pointer; + + unique_any_t(unique_any_t const &) = delete; + unique_any_t& operator=(unique_any_t const &) = delete; + + // Note that the default constructor really shouldn't be needed (taken care of by the forwarding constructor below), but + // the forwarding constructor causes an internal compiler error when the class is used in a C++ array. Defining the default + // constructor independent of the forwarding constructor removes the compiler limitation. + unique_any_t() = default; + + // forwarding constructor: forwards all 'explicit' and multi-arg constructors to the base class + template + explicit unique_any_t(arg1 && first, args_t&&... args) : // should not be WI_NOEXCEPT (may forward to a throwing constructor) + storage_t(wistd::forward(first), wistd::forward(args)...) + { + static_assert(wistd::is_same::value || + wistd::is_same::value || + wistd::is_same::value, "pointer_access policy must be a known pointer_access* integral type"); + } + + unique_any_t(wistd::nullptr_t) WI_NOEXCEPT + { + static_assert(wistd::is_same::value, "nullptr constructor: valid only for handle types using nullptr as the invalid value"); + } + + unique_any_t(unique_any_t &&other) WI_NOEXCEPT : + storage_t(wistd::move(other)) + { + } + + unique_any_t& operator=(unique_any_t &&other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + // cast to base_storage to 'skip' calling the (optional) specialization class that provides handle-specific functionality + storage_t::replace(wistd::move(static_cast(other))); + } + return (*this); + } + + unique_any_t& operator=(wistd::nullptr_t) WI_NOEXCEPT + { + static_assert(wistd::is_same::value, "nullptr assignment: valid only for handle types using nullptr as the invalid value"); + storage_t::reset(); + return (*this); + } + + void swap(unique_any_t &other) WI_NOEXCEPT + { + unique_any_t self(wistd::move(*this)); + operator=(wistd::move(other)); + other = wistd::move(self); + } + + explicit operator bool() const WI_NOEXCEPT + { + return storage_t::is_valid(); + } + + //! ~~~~ + //! BOOL OpenOrCreateWaffle(PCWSTR name, HWAFFLE* handle); + //! wil::unique_any waffle; + //! RETURN_IF_WIN32_BOOL_FALSE(OpenOrCreateWaffle(L"tasty.yum", waffle.put())); + //! ~~~~ + pointer_storage *put() WI_NOEXCEPT + { + static_assert(wistd::is_same::value, "operator & is not available for this handle"); + storage_t::reset(); + return storage_t::addressof(); + } + + pointer_storage *operator&() WI_NOEXCEPT + { + return put(); + } + + pointer get() const WI_NOEXCEPT + { + static_assert(!wistd::is_same::value, "get(): the raw handle value is not available for this resource class"); + return storage_t::get(); + } + + // The following functions are publicly exposed by their inclusion in the unique_storage base class + + // explicit unique_any_t(pointer_storage ptr) WI_NOEXCEPT + // void reset(pointer_storage ptr = policy::invalid_value()) WI_NOEXCEPT + // void reset(wistd::nullptr_t) WI_NOEXCEPT + // pointer_storage release() WI_NOEXCEPT // not exposed for some resource types + // pointer_storage *addressof() WI_NOEXCEPT // not exposed for some resource types + }; + + template + void swap(unique_any_t& left, unique_any_t& right) WI_NOEXCEPT + { + left.swap(right); + } + + template + bool operator==(const unique_any_t& left, const unique_any_t& right) WI_NOEXCEPT + { + return (left.get() == right.get()); + } + + template + bool operator==(const unique_any_t& left, wistd::nullptr_t) WI_NOEXCEPT + { + static_assert(wistd::is_same::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value"); + return !left; + } + + template + bool operator==(wistd::nullptr_t, const unique_any_t& right) WI_NOEXCEPT + { + static_assert(wistd::is_same::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value"); + return !right; + } + + template + bool operator!=(const unique_any_t& left, const unique_any_t& right) WI_NOEXCEPT + { + return (!(left.get() == right.get())); + } + + template + bool operator!=(const unique_any_t& left, wistd::nullptr_t) WI_NOEXCEPT + { + static_assert(wistd::is_same::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value"); + return !!left; + } + + template + bool operator!=(wistd::nullptr_t, const unique_any_t& right) WI_NOEXCEPT + { + static_assert(wistd::is_same::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value"); + return !!right; + } + + template + bool operator<(const unique_any_t& left, const unique_any_t& right) WI_NOEXCEPT + { + return (left.get() < right.get()); + } + + template + bool operator>=(const unique_any_t& left, const unique_any_t& right) WI_NOEXCEPT + { + return (!(left < right)); + } + + template + bool operator>(const unique_any_t& left, const unique_any_t& right) WI_NOEXCEPT + { + return (right < left); + } + + template + bool operator<=(const unique_any_t& left, const unique_any_t& right) WI_NOEXCEPT + { + return (!(right < left)); + } + + // unique_any provides a template alias for easily building a unique_any_t from a unique_storage class with the given + // template parameters for resource_policy. + + template // nullptr_t if the invalid handle value is compatible with nullptr, otherwise pointer + using unique_any = unique_any_t>>; + + /// @cond + namespace details + { + template + class lambda_call + { + public: + lambda_call(const lambda_call&) = delete; + lambda_call& operator=(const lambda_call&) = delete; + lambda_call& operator=(lambda_call&& other) = delete; + + explicit lambda_call(TLambda&& lambda) WI_NOEXCEPT : m_lambda(wistd::move(lambda)) + { + static_assert(wistd::is_same::value, "scope_exit lambdas must not have a return value"); + static_assert(!wistd::is_lvalue_reference::value && !wistd::is_rvalue_reference::value, + "scope_exit should only be directly used with a lambda"); + } + + lambda_call(lambda_call&& other) WI_NOEXCEPT : m_lambda(wistd::move(other.m_lambda)), m_call(other.m_call) + { + other.m_call = false; + } + + ~lambda_call() WI_NOEXCEPT + { + reset(); + } + + // Ensures the scope_exit lambda will not be called + void release() WI_NOEXCEPT + { + m_call = false; + } + + // Executes the scope_exit lambda immediately if not yet run; ensures it will not run again + void reset() WI_NOEXCEPT + { + if (m_call) + { + m_call = false; + m_lambda(); + } + } + + // Returns true if the scope_exit lambda is still going to be executed + explicit operator bool() const WI_NOEXCEPT + { + return m_call; + } + + protected: + TLambda m_lambda; + bool m_call = true; + }; + +#ifdef WIL_ENABLE_EXCEPTIONS + template + class lambda_call_log + { + public: + lambda_call_log(const lambda_call_log&) = delete; + lambda_call_log& operator=(const lambda_call_log&) = delete; + lambda_call_log& operator=(lambda_call_log&& other) = delete; + + explicit lambda_call_log(void* address, const DiagnosticsInfo& info, TLambda&& lambda) WI_NOEXCEPT : + m_address(address), m_info(info), m_lambda(wistd::move(lambda)) + { + static_assert(wistd::is_same::value, "scope_exit lambdas must return 'void'"); + static_assert(!wistd::is_lvalue_reference::value && !wistd::is_rvalue_reference::value, + "scope_exit should only be directly used with a lambda"); + } + + lambda_call_log(lambda_call_log&& other) WI_NOEXCEPT : + m_address(other.m_address), m_info(other.m_info), m_lambda(wistd::move(other.m_lambda)), m_call(other.m_call) + { + other.m_call = false; + } + + ~lambda_call_log() WI_NOEXCEPT + { + reset(); + } + + // Ensures the scope_exit lambda will not be called + void release() WI_NOEXCEPT + { + m_call = false; + } + + // Executes the scope_exit lambda immediately if not yet run; ensures it will not run again + void reset() WI_NOEXCEPT + { + if (m_call) + { + m_call = false; + try + { + m_lambda(); + } + catch (...) + { + ReportFailure_CaughtException(__R_DIAGNOSTICS(m_info), m_address); + } + } + } + + // Returns true if the scope_exit lambda is still going to be executed + explicit operator bool() const WI_NOEXCEPT + { + return m_call; + } + + private: + void* m_address; + DiagnosticsInfo m_info; + TLambda m_lambda; + bool m_call = true; + }; +#endif // WIL_ENABLE_EXCEPTIONS + } + /// @endcond + + /** Returns an object that executes the given lambda when destroyed. + Capture the object with 'auto'; use reset() to execute the lambda early or release() to avoid + execution. Exceptions thrown in the lambda will fail-fast; use scope_exit_log to avoid. */ + template + WI_NODISCARD inline auto scope_exit(TLambda&& lambda) WI_NOEXCEPT + { + return details::lambda_call(wistd::forward(lambda)); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Returns an object that executes the given lambda when destroyed; logs exceptions. + Capture the object with 'auto'; use reset() to execute the lambda early or release() to avoid + execution. Exceptions thrown in the lambda will be caught and logged without being propagated. */ + template + WI_NODISCARD inline __declspec(noinline) auto scope_exit_log(const DiagnosticsInfo& diagnostics, TLambda&& lambda) WI_NOEXCEPT + { + return details::lambda_call_log(_ReturnAddress(), diagnostics, wistd::forward(lambda)); + } +#endif + + // Forward declaration... + template + class com_ptr_t; + + //! Type traits class that identifies the inner type of any smart pointer. + template + struct smart_pointer_details + { + typedef typename Ptr::pointer pointer; + }; + + /// @cond + template + struct smart_pointer_details> + { + typedef T* pointer; + }; + /// @endcond + + /** Generically detaches a raw pointer from any smart pointer. + Caller takes ownership of the returned raw pointer; calls the correct release(), detach(), + or Detach() method based on the smart pointer type */ + template + WI_NODISCARD typename TSmartPointer::pointer detach_from_smart_pointer(TSmartPointer& smartPtr) + { + return smartPtr.release(); + } + + /// @cond + // Generically detaches a raw pointer from any smart pointer + template + WI_NODISCARD T* detach_from_smart_pointer(wil::com_ptr_t& smartPtr) + { + return smartPtr.detach(); + } + + // Generically detaches a raw pointer from any smart pointer + template + WI_NODISCARD T* detach_from_smart_pointer(Microsoft::WRL::ComPtr& smartPtr) + { + return smartPtr.Detach(); + } + + template class com_ptr_t; // forward + namespace details + { + // The first two attach_to_smart_pointer() overloads are ambiguous when passed a com_ptr_t. + // To solve that use this functions return type to elminate the reset form for com_ptr_t. + template wistd::false_type use_reset(wil::com_ptr_t*) { return wistd::false_type(); } + template wistd::true_type use_reset(T*) { return wistd::true_type(); } + } + /// @endcond + + /** Generically attach a raw pointer to a compatible smart pointer. + Calls the correct reset(), attach(), or Attach() method based on samrt pointer type. */ + template (nullptr)))::value>> + void attach_to_smart_pointer(TSmartPointer& smartPtr, typename TSmartPointer::pointer rawPtr) + { + smartPtr.reset(rawPtr); + } + + /// @cond + + // Generically attach a raw pointer to a compatible smart pointer. + template + void attach_to_smart_pointer(wil::com_ptr_t& smartPtr, T* rawPtr) + { + smartPtr.attach(rawPtr); + } + + // Generically attach a raw pointer to a compatible smart pointer. + template + void attach_to_smart_pointer(Microsoft::WRL::ComPtr& smartPtr, T* rawPtr) + { + smartPtr.Attach(rawPtr); + } + /// @endcond + + //! @ingroup outparam + /** Detach a smart pointer resource to an optional output pointer parameter. + Avoids cluttering code with nullptr tests; works generically for any smart pointer */ + template + inline void detach_to_opt_param(_Out_opt_ T* outParam, TSmartPointer&& smartPtr) + { + if (outParam) + { + *outParam = detach_from_smart_pointer(smartPtr); + } + } + + /// @cond + namespace details + { + template + struct out_param_t + { + typedef typename wil::smart_pointer_details::pointer pointer; + T &wrapper; + pointer pRaw; + bool replace = true; + + out_param_t(_Inout_ T &output) : + wrapper(output), + pRaw(nullptr) + { + } + + out_param_t(out_param_t&& other) : + wrapper(other.wrapper), + pRaw(other.pRaw) + { + WI_ASSERT(other.replace); + other.replace = false; + } + + operator pointer*() + { + WI_ASSERT(replace); + return &pRaw; + } + + ~out_param_t() + { + if (replace) + { + attach_to_smart_pointer(wrapper, pRaw); + } + } + + out_param_t(out_param_t const &other) = delete; + out_param_t &operator=(out_param_t const &other) = delete; + }; + + template + struct out_param_ptr_t + { + typedef typename wil::smart_pointer_details::pointer pointer; + T &wrapper; + pointer pRaw; + bool replace = true; + + out_param_ptr_t(_Inout_ T &output) : + wrapper(output), + pRaw(nullptr) + { + } + + out_param_ptr_t(out_param_ptr_t&& other) : + wrapper(other.wrapper), + pRaw(other.pRaw) + { + WI_ASSERT(other.replace); + other.replace = false; + } + + operator Tcast() + { + WI_ASSERT(replace); + return reinterpret_cast(&pRaw); + } + + ~out_param_ptr_t() + { + if (replace) + { + attach_to_smart_pointer(wrapper, pRaw); + } + } + + out_param_ptr_t(out_param_ptr_t const &other) = delete; + out_param_ptr_t &operator=(out_param_ptr_t const &other) = delete; + }; + } // details + /// @endcond + + /** Use to retrieve raw out parameter pointers into smart pointers that do not support the '&' operator. + This avoids multi-step handling of a raw resource to establish the smart pointer. + Example: `GetFoo(out_param(foo));` */ + template + details::out_param_t out_param(T& p) + { + return details::out_param_t(p); + } + + /** Use to retrieve raw out parameter pointers (with a required cast) into smart pointers that do not support the '&' operator. + Use only when the smart pointer's &handle is not equal to the output type a function requries, necessitating a cast. + Example: `wil::out_param_ptr(securityDescriptor)` */ + template + details::out_param_ptr_t out_param_ptr(T& p) + { + return details::out_param_ptr_t(p); + } + + /** Use unique_struct to define an RAII type for a trivial struct that references resources that must be cleaned up. + Unique_struct wraps a trivial struct using a custom clean up function and, optionally, custom initializer function. If no custom initialier function is defined in the template + then ZeroMemory is used. + Unique_struct is modeled off of std::unique_ptr. However, unique_struct inherits from the defined type instead of managing the struct through a private member variable. + + If the type you're wrapping is a system type, you can share the code by declaring it in this file (Resource.h). Send requests to wildisc. + Otherwise, if the type is local to your project, declare it locally. + @tparam struct_t The struct you want to manage + @tparam close_fn_t The type of the function to clean up the struct. Takes one parameter: a pointer of struct_t. Return values are ignored. + @tparam close_fn The function of type close_fn_t. This is called in the destructor and reset functions. + @tparam init_fn_t Optional:The type of the function to initialize the struct. Takes one parameter: a pointer of struct_t. Return values are ignored. + @tparam init_fn Optional:The function of type init_fn_t. This is called in the constructor, reset, and release functions. The default is ZeroMemory to initialize the struct. + + Defined using the default zero memory initializer + ~~~ + typedef wil::unique_struct unique_prop_variant_default_init; + + unique_prop_variant propvariant; + SomeFunction(&propvariant); + ~~~ + + Defined using a custom initializer + ~~~ + typedef wil::unique_struct unique_prop_variant; + + unique_prop_variant propvariant; + SomeFunction(&propvariant); + ~~~ + */ + template + class unique_struct : public struct_t + { + public: + //! Initializes the managed struct using the user-provided initialization function, or ZeroMemory if no function is specified + unique_struct() + { + call_init(use_default_init_fn()); + } + + //! Takes ownership of the struct by doing a shallow copy. Must explicitly be type struct_t + explicit unique_struct(const struct_t& other) WI_NOEXCEPT : + struct_t(other) + {} + + //! Initializes the managed struct by taking the ownership of the other managed struct + //! Then resets the other managed struct by calling the custom close function + unique_struct(unique_struct&& other) WI_NOEXCEPT : + struct_t(other.release()) + {} + + //! Resets this managed struct by calling the custom close function and takes ownership of the other managed struct + //! Then resets the other managed struct by calling the custom close function + unique_struct & operator=(unique_struct&& other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + reset(other.release()); + } + return *this; + } + + //! Calls the custom close function + ~unique_struct() WI_NOEXCEPT + { + wistd::invoke(close_fn, this); + } + + void reset(const unique_struct&) = delete; + + //! Resets this managed struct by calling the custom close function and begins management of the other struct + void reset(const struct_t& other) WI_NOEXCEPT + { + { + auto preserveError = last_error_context(); + wistd::invoke(close_fn, this); + } + struct_t::operator=(other); + } + + //! Resets this managed struct by calling the custom close function + //! Then initializes this managed struct using the user-provided initialization function, or ZeroMemory if no function is specified + void reset() WI_NOEXCEPT + { + wistd::invoke(close_fn, this); + call_init(use_default_init_fn()); + } + + void swap(struct_t&) = delete; + + //! Swaps the managed structs + void swap(unique_struct& other) WI_NOEXCEPT + { + struct_t self(*this); + struct_t::operator=(other); + *(other.addressof()) = self; + } + + //! Returns the managed struct + //! Then initializes this managed struct using the user-provided initialization function, or ZeroMemory if no function is specified + struct_t release() WI_NOEXCEPT + { + struct_t value(*this); + call_init(use_default_init_fn()); + return value; + } + + //! Returns address of the managed struct + struct_t * addressof() WI_NOEXCEPT + { + return this; + } + + //! Resets this managed struct by calling the custom close function + //! Then initializes this managed struct using the user-provided initialization function, or ZeroMemory if no function is specified + //! Returns address of the managed struct + struct_t * reset_and_addressof() WI_NOEXCEPT + { + reset(); + return this; + } + + unique_struct(const unique_struct&) = delete; + unique_struct& operator=(const unique_struct&) = delete; + unique_struct& operator=(const struct_t&) = delete; + + private: + typedef typename wistd::is_same::type use_default_init_fn; + + void call_init(wistd::true_type) + { + RtlZeroMemory(this, sizeof(*this)); + } + + void call_init(wistd::false_type) + { + init_fn(this); + } + }; + + struct empty_deleter + { + template + void operator()(_Pre_opt_valid_ _Frees_ptr_opt_ T) const + { + } + }; + + /** unique_any_array_ptr is a RAII type for managing conformant arrays that need to be freed and have elements that may need to be freed. + The intented use for this RAII type would be to capture out params from API like IPropertyValue::GetStringArray. + This class also maintains the size of the array, so it can iterate over the members and deallocate them before it deallocates the base array pointer. + + If the type you're wrapping is a system type, you can share the code by declaring it in this file (Resource.h). Send requests to wildisc. + Otherwise, if the type is local to your project, declare it locally. + + @tparam ValueType: The type of array you want to manage. + @tparam ArrayDeleter: The type of the function to clean up the array. Takes one parameter of type T[] or T*. Return values are ignored. This is called in the destructor and reset functions. + @tparam ElementDeleter: The type of the function to clean up the array elements. Takes one parameter of type T. Return values are ignored. This is called in the destructor and reset functions. + + ~~~ + void GetSomeArray(_Out_ size_t*, _Out_ NOTMYTYPE**); + + struct not_my_deleter + { + void operator()(NOTMYTYPE p) const + { + destroy(p); + } + }; + + wil::unique_any_array_ptr myArray; + GetSomeArray(myArray.size_address(), &myArray); + ~~~ */ + template + class unique_any_array_ptr + { + public: + typedef ValueType value_type; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef ValueType *pointer; + typedef const ValueType *const_pointer; + typedef ValueType& reference; + typedef const ValueType& const_reference; + + typedef ValueType* iterator; + typedef const ValueType* const_iterator; + + unique_any_array_ptr() = default; + unique_any_array_ptr(const unique_any_array_ptr&) = delete; + unique_any_array_ptr& operator=(const unique_any_array_ptr&) = delete; + + unique_any_array_ptr(wistd::nullptr_t) WI_NOEXCEPT + { + } + + unique_any_array_ptr& operator=(wistd::nullptr_t) WI_NOEXCEPT + { + reset(); + return *this; + } + + unique_any_array_ptr(pointer ptr, size_t size) WI_NOEXCEPT : m_ptr(ptr), m_size(size) + { + } + + unique_any_array_ptr(unique_any_array_ptr&& other) WI_NOEXCEPT : m_ptr(other.m_ptr), m_size(other.m_size) + { + other.m_ptr = nullptr; + other.m_size = size_type{}; + } + + unique_any_array_ptr& operator=(unique_any_array_ptr&& other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + reset(); + swap(other); + } + return *this; + } + + ~unique_any_array_ptr() WI_NOEXCEPT + { + reset(); + } + + void swap(unique_any_array_ptr& other) WI_NOEXCEPT + { + auto ptr = m_ptr; + auto size = m_size; + m_ptr = other.m_ptr; + m_size = other.m_size; + other.m_ptr = ptr; + other.m_size = size; + } + + iterator begin() WI_NOEXCEPT + { + return (iterator(m_ptr)); + } + + const_iterator begin() const WI_NOEXCEPT + { + return (const_iterator(m_ptr)); + } + + iterator end() WI_NOEXCEPT + { + return (iterator(m_ptr + m_size)); + } + + const_iterator end() const WI_NOEXCEPT + { + return (const_iterator(m_ptr + m_size)); + } + + const_iterator cbegin() const WI_NOEXCEPT + { + return (begin()); + } + + const_iterator cend() const WI_NOEXCEPT + { + return (end()); + } + + size_type size() const WI_NOEXCEPT + { + return (m_size); + } + + bool empty() const WI_NOEXCEPT + { + return (size() == size_type{}); + } + + reference operator[](size_type position) + { + WI_ASSERT(position < m_size); + _Analysis_assume_(position < m_size); + return (m_ptr[position]); + } + + const_reference operator[](size_type position) const + { + WI_ASSERT(position < m_size); + _Analysis_assume_(position < m_size); + return (m_ptr[position]); + } + + reference front() + { + WI_ASSERT(!empty()); + return (m_ptr[0]); + } + + const_reference front() const + { + WI_ASSERT(!empty()); + return (m_ptr[0]); + } + + reference back() + { + WI_ASSERT(!empty()); + return (m_ptr[m_size - 1]); + } + + const_reference back() const + { + WI_ASSERT(!empty()); + return (m_ptr[m_size - 1]); + } + + ValueType* data() WI_NOEXCEPT + { + return (m_ptr); + } + + const ValueType* data() const WI_NOEXCEPT + { + return (m_ptr); + } + + pointer get() const WI_NOEXCEPT + { + return m_ptr; + } + + explicit operator bool() const WI_NOEXCEPT + { + return (m_ptr != pointer()); + } + + pointer release() WI_NOEXCEPT + { + auto result = m_ptr; + m_ptr = nullptr; + m_size = size_type{}; + return result; + } + + void reset() WI_NOEXCEPT + { + if (m_ptr) + { + reset_array(ElementDeleter()); + ArrayDeleter()(m_ptr); + m_ptr = nullptr; + m_size = size_type{}; + } + } + + void reset(pointer ptr, size_t size) WI_NOEXCEPT + { + reset(); + m_ptr = ptr; + m_size = size; + } + + pointer* addressof() WI_NOEXCEPT + { + return &m_ptr; + } + + pointer* put() WI_NOEXCEPT + { + reset(); + return addressof(); + } + + pointer* operator&() WI_NOEXCEPT + { + return put(); + } + + size_type* size_address() WI_NOEXCEPT + { + return &m_size; + } + + template + struct size_address_ptr + { + unique_any_array_ptr& wrapper; + TSize size{}; + bool replace = true; + + size_address_ptr(_Inout_ unique_any_array_ptr& output) : + wrapper(output) + { + } + + size_address_ptr(size_address_ptr&& other) : + wrapper(other.wrapper), + size(other.size) + { + WI_ASSERT(other.replace); + other.replace = false; + } + + operator TSize*() + { + WI_ASSERT(replace); + return &size; + } + + ~size_address_ptr() + { + if (replace) + { + *wrapper.size_address() = static_cast(size); + } + } + + size_address_ptr(size_address_ptr const &other) = delete; + size_address_ptr &operator=(size_address_ptr const &other) = delete; + }; + + template + size_address_ptr size_address() WI_NOEXCEPT + { + return size_address_ptr(*this); + } + + private: + pointer m_ptr = nullptr; + size_type m_size{}; + + void reset_array(const empty_deleter&) + { + } + + template + void reset_array(const T& deleter) + { + for (auto& element : make_range(m_ptr, m_size)) + { + deleter(element); + } + } + }; + + // forward declaration + template + class com_ptr_t; + + /// @cond + namespace details + { + template + struct unique_any_array_deleter + { + template + void operator()(_Pre_opt_valid_ _Frees_ptr_opt_ T* p) const + { + UniqueAnyType::policy::close_reset(p); + } + }; + + template + struct unique_struct_array_deleter + { + template + void operator()(_Pre_opt_valid_ _Frees_ptr_opt_ T& p) const + { + wistd::invoke(close_fn, &p); + } + }; + + struct com_unknown_deleter + { + template + void operator()(_Pre_opt_valid_ _Frees_ptr_opt_ T* p) const + { + if (p) + { + p->Release(); + } + } + }; + + template + struct element_traits + { + typedef empty_deleter deleter; + typedef T type; + }; + + template + struct element_traits> + { + typedef unique_any_array_deleter> deleter; + typedef typename unique_any_t::pointer type; + }; + + template + struct element_traits> + { + typedef com_unknown_deleter deleter; + typedef T* type; + }; + + template + struct element_traits> + { + typedef unique_struct_array_deleter deleter; + typedef struct_t type; + }; + } + /// @endcond + + template + using unique_array_ptr = unique_any_array_ptr::type, ArrayDeleter, typename details::element_traits::deleter>; + + /** Adapter for single-parameter 'free memory' for `wistd::unique_ptr`. + This struct provides a standard wrapper for calling a platform function to deallocate memory held by a + `wistd::unique_ptr`, making declaring them as easy as declaring wil::unique_any<>. + + Consider this adapter in preference to `wil::unique_any<>` when the returned type is really a pointer or an + array of items; `wistd::unique_ptr<>` exposes `operator->()` and `operator[]` for array-typed things safely. + ~~~~ + EXTERN_C VOID WINAPI MyDllFreeMemory(void* p); + EXTERN_C HRESULT MyDllGetString(_Outptr_ PWSTR* pString); + EXTERN_C HRESULT MyDllGetThing(_In_ PCWSTR pString, _Outptr_ PMYSTRUCT* ppThing); + template + using unique_mydll_ptr = wistd::unique_ptr>; + HRESULT Test() + { + unique_mydll_ptr dllString; + unique_mydll_ptr thing; + RETURN_IF_FAILED(MyDllGetString(wil::out_param(dllString))); + RETURN_IF_FAILED(MyDllGetThing(dllString.get(), wil::out_param(thing))); + if (thing->Member) + { + // ... + } + return S_OK; + } + ~~~~ */ + template struct function_deleter + { + template void operator()(_Frees_ptr_opt_ T* toFree) const + { + TDeleter(toFree); + } + }; + + /** Use unique_com_token to define an RAII type for a token-based resource that is managed by a COM interface. + By comparison, unique_any_t has the requirement that the close function must be static. This works for functions + such as CloseHandle(), but for any resource cleanup function that relies on a more complex interface, + unique_com_token can be used. + + @tparam interface_t A COM interface pointer that will manage this resource type. + @tparam token_t The token type that relates to the COM interface management functions. + @tparam close_fn_t The type of the function that is called when the resource is destroyed. + @tparam close_fn The function used to destroy the associated resource. This function should have the signature void(interface_t* source, token_t token). + @tparam invalid_token Optional:An invalid token value. Defaults to default-constructed token_t(). + + Example + ~~~ + void __stdcall MyInterfaceCloseFunction(IMyInterface* source, DWORD token) + { + source->MyCloseFunction(token); + } + using unique_my_interface_token = wil::unique_com_token; + ~~~ */ + template + class unique_com_token + { + public: + unique_com_token() = default; + + unique_com_token(_In_opt_ interface_t* source, token_t token = invalid_token) WI_NOEXCEPT + { + reset(source, token); + } + + unique_com_token(unique_com_token&& other) WI_NOEXCEPT : m_source(other.m_source), m_token(other.m_token) + { + other.m_source = nullptr; + other.m_token = invalid_token; + } + + unique_com_token& operator=(unique_com_token&& other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + reset(); + m_source = other.m_source; + m_token = other.m_token; + + other.m_source = nullptr; + other.m_token = invalid_token; + } + return *this; + } + + ~unique_com_token() WI_NOEXCEPT + { + reset(); + } + + //! Determine if the underlying source and token are valid + explicit operator bool() const WI_NOEXCEPT + { + return (m_token != invalid_token) && m_source; + } + + //! Associates a new source and releases the existing token if valid + void associate(_In_opt_ interface_t* source) WI_NOEXCEPT + { + reset(source, invalid_token); + } + + //! Assigns a new source and token + void reset(_In_opt_ interface_t* source, token_t token) WI_NOEXCEPT + { + WI_ASSERT(source || (token == invalid_token)); + + // Determine if we need to call the close function on our previous token. + if (m_token != invalid_token) + { + if ((m_source != source) || (m_token != token)) + { + wistd::invoke(close_fn, m_source, m_token); + } + } + + m_token = token; + + // Assign our new source and manage the reference counts + if (m_source != source) + { + auto oldSource = m_source; + m_source = source; + + if (m_source) + { + m_source->AddRef(); + } + + if (oldSource) + { + oldSource->Release(); + } + } + } + + //! Assigns a new token without modifying the source; associate must be called first + void reset(token_t token) WI_NOEXCEPT + { + reset(m_source, token); + } + + //! Closes the token and the releases the reference to the source + void reset() WI_NOEXCEPT + { + reset(nullptr, invalid_token); + } + + //! Exchanges values with another managed token + void swap(unique_com_token& other) WI_NOEXCEPT + { + wistd::swap_wil(m_source, other.m_source); + wistd::swap_wil(m_token, other.m_token); + } + + //! Releases the held token to the caller without closing it and releases the reference to the source. + //! Requires that the associated COM interface be kept alive externally or the released token may be invalidated + token_t release() WI_NOEXCEPT + { + auto token = m_token; + m_token = invalid_token; + reset(); + return token; + } + + //! Returns address of the managed token; associate must be called first + token_t* addressof() WI_NOEXCEPT + { + WI_ASSERT(m_source); + return &m_token; + } + + //! Releases the held token and allows attaching a new token; associate must be called first + token_t* put() WI_NOEXCEPT + { + reset(invalid_token); + return addressof(); + } + + //! Releases the held token and allows attaching a new token; associate must be called first + token_t* operator&() WI_NOEXCEPT + { + return put(); + } + + //! Retrieves the token + token_t get() const WI_NOEXCEPT + { + return m_token; + } + + unique_com_token(const unique_com_token&) = delete; + unique_com_token& operator=(const unique_com_token&) = delete; + + private: + interface_t* m_source = nullptr; + token_t m_token = invalid_token; + }; + + /** Use unique_com_call to define an RAII type that demands a particular parameter-less method be called on a COM interface. + This allows implementing an RAII type that can call a Close() method (think IClosable) or a SetSite(nullptr) + method (think IObjectWithSite) or some other method when a basic interface call is required as part of the RAII contract. + see wil::com_set_site in wil\com.h for the IObjectWithSite support. + + @tparam interface_t A COM interface pointer that provides context to make the call. + @tparam close_fn_t The type of the function that is called to invoke the method. + @tparam close_fn The function used to invoke the interface method. This function should have the signature void(interface_t* source). + + Example + ~~~ + void __stdcall CloseIClosable(IClosable* source) + { + source->Close(); + } + using unique_closable_call = wil::unique_com_call; + ~~~ */ + template + class unique_com_call + { + public: + unique_com_call() = default; + + explicit unique_com_call(_In_opt_ interface_t* ptr) WI_NOEXCEPT + { + reset(ptr); + } + + unique_com_call(unique_com_call&& other) WI_NOEXCEPT + { + m_ptr = other.m_ptr; + other.m_ptr = nullptr; + } + + unique_com_call& operator=(unique_com_call&& other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + reset(); + m_ptr = other.m_ptr; + other.m_ptr = nullptr; + } + return *this; + } + + ~unique_com_call() WI_NOEXCEPT + { + reset(); + } + + //! Assigns an interface to make a given call on + void reset(_In_opt_ interface_t* ptr = nullptr) WI_NOEXCEPT + { + if (ptr != m_ptr) + { + auto oldSource = m_ptr; + m_ptr = ptr; + if (m_ptr) + { + m_ptr->AddRef(); + } + if (oldSource) + { + wistd::invoke(close_fn, oldSource); + oldSource->Release(); + } + } + } + + //! Exchanges values with another class + void swap(unique_com_call& other) WI_NOEXCEPT + { + wistd::swap_wil(m_ptr, other.m_ptr); + } + + //! Cancel the interface call that this class was expected to make + void release() WI_NOEXCEPT + { + auto ptr = m_ptr; + m_ptr = nullptr; + if (ptr) + { + ptr->Release(); + } + } + + //! Returns true if the call this class was expected to make is still outstanding + explicit operator bool() const WI_NOEXCEPT + { + return (m_ptr != nullptr); + } + + //! Returns address of the internal interface + interface_t** addressof() WI_NOEXCEPT + { + return &m_ptr; + } + + //! Releases the held interface (first performing the interface call if required) + //! and allows attaching a new interface + interface_t** put() WI_NOEXCEPT + { + reset(); + return addressof(); + } + + //! Releases the held interface (first performing the interface call if required) + //! and allows attaching a new interface + interface_t** operator&() WI_NOEXCEPT + { + return put(); + } + + unique_com_call(const unique_com_call&) = delete; + unique_com_call& operator=(const unique_com_call&) = delete; + + private: + interface_t* m_ptr = nullptr; + }; + + + /** Use unique_call to define an RAII type that demands a particular parameter-less global function be called. + This allows implementing a RAII types that can call methods like CoUninitialize. + + @tparam close_fn_t The type of the function that is called to invoke the call. + @tparam close_fn The function used to invoke the call. This function should have the signature void(). + @tparam default_value Determines whether the unique_call is active or inactive when default-constructed or reset. + + Example + ~~~ + void __stdcall CoUninitializeFunction() + { + ::CoUninitialize(); + } + using unique_couninitialize_call = wil::unique_call; + ~~~ */ + template + class unique_call + { + public: + unique_call() = default; + + explicit unique_call(bool call) WI_NOEXCEPT : m_call(call) + { + } + + unique_call(unique_call&& other) WI_NOEXCEPT + { + m_call = other.m_call; + other.m_call = false; + } + + unique_call& operator=(unique_call&& other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + reset(); + m_call = other.m_call; + other.m_call = false; + } + return *this; + } + + ~unique_call() WI_NOEXCEPT + { + reset(); + } + + //! Assigns a new ptr and token + void reset() WI_NOEXCEPT + { + auto call = m_call; + m_call = false; + if (call) + { + wistd::invoke(close_fn); + } + } + + //! Exchanges values with raii class + void swap(unique_call& other) WI_NOEXCEPT + { + wistd::swap_wil(m_call, other.m_call); + } + + //! Make the interface call that was expected of this class + void activate() WI_NOEXCEPT + { + m_call = true; + } + + //! Do not make the interface call that was expected of this class + void release() WI_NOEXCEPT + { + m_call = false; + } + + //! Returns true if the call that was expected is still outstanding + explicit operator bool() const WI_NOEXCEPT + { + return m_call; + } + + unique_call(const unique_call&) = delete; + unique_call& operator=(const unique_call&) = delete; + + private: + bool m_call = default_value; + }; + + // str_raw_ptr is an overloaded function that retrieves a const pointer to the first character in a string's buffer. + // Overloads in this file support any string that is implicitly convertible to a PCWSTR, HSTRING, and any unique_any_t + // that points to any other supported type (this covers unique_hstring, unique_cotaskmem_string, and similar). + // An overload for std::wstring is available in stl.h. + inline PCWSTR str_raw_ptr(PCWSTR str) + { + return str; + } + + template + PCWSTR str_raw_ptr(const unique_any_t& ua) + { + return str_raw_ptr(ua.get()); + } + + namespace details + { + // Forward declaration + template struct string_maker; + + // Concatenate any number of strings together and store it in an automatically allocated string. If a string is present + // in the input buffer, it is overwritten. + template + HRESULT str_build_nothrow(string_type& result, _In_reads_(strCount) PCWSTR* strList, size_t strCount) + { + size_t lengthRequiredWithoutNull{}; + for (auto& string : make_range(strList, strCount)) + { + lengthRequiredWithoutNull += string ? wcslen(string) : 0; + } + + details::string_maker maker; + RETURN_IF_FAILED(maker.make(nullptr, lengthRequiredWithoutNull)); + + auto buffer = maker.buffer(); + auto bufferEnd = buffer + lengthRequiredWithoutNull + 1; + for (auto& string : make_range(strList, strCount)) + { + if (string) + { + RETURN_IF_FAILED(StringCchCopyExW(buffer, (bufferEnd - buffer), string, &buffer, nullptr, STRSAFE_IGNORE_NULLS)); + } + } + + result = maker.release(); + return S_OK; + } + + // NOTE: 'Strings' must all be PCWSTR, or convertible to PCWSTR, but C++ doesn't allow us to express that cleanly + template + HRESULT str_build_nothrow(string_type& result, Strings... strings) + { + PCWSTR localStrings[] = { strings... }; + return str_build_nothrow(result, localStrings, sizeof...(Strings)); + } + } + + // Concatenate any number of strings together and store it in an automatically allocated string. If a string is present + // in the input buffer, the remaining strings are appended to it. + template + HRESULT str_concat_nothrow(string_type& buffer, const strings&... str) + { + static_assert(sizeof...(str) > 0, "attempting to concatenate no strings"); + return details::str_build_nothrow(buffer, details::string_maker::get(buffer), str_raw_ptr(str)...); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + // Concatenate any number of strings together and store it in an automatically allocated string. + template + string_type str_concat(arguments&&... args) + { + string_type result; + THROW_IF_FAILED(str_concat_nothrow(result, wistd::forward(args)...)); + return result; + } +#endif // WIL_ENABLE_EXCEPTIONS + + // Concatenate any number of strings together and store it in an automatically allocated string. + template + string_type str_concat_failfast(arguments&&... args) + { + string_type result; + FAIL_FAST_IF_FAILED(str_concat_nothrow(result, wistd::forward(args)...)); + return result; + } + + namespace details + { + // Wraps StringCchPrintFExW and stores it in an automatically allocated string. Takes a buffer followed by the same format arguments + // that StringCchPrintfExW takes. + template + HRESULT str_vprintf_nothrow(string_type& result, _Printf_format_string_ PCWSTR pszFormat, va_list& argsVL) + { + size_t lengthRequiredWithoutNull = _vscwprintf(pszFormat, argsVL); + + string_maker maker; + RETURN_IF_FAILED(maker.make(nullptr, lengthRequiredWithoutNull)); + + auto buffer = maker.buffer(); + RETURN_IF_FAILED(StringCchVPrintfExW(buffer, lengthRequiredWithoutNull + 1, nullptr, nullptr, STRSAFE_NULL_ON_FAILURE, pszFormat, argsVL)); + + result = maker.release(); + return S_OK; + } + } + + // Wraps StringCchPrintFExW and stores it in an automatically allocated string. Takes a buffer followed by the same format arguments + // that StringCchPrintfExW takes. + template + HRESULT str_printf_nothrow(string_type& result, _Printf_format_string_ PCWSTR pszFormat, ...) + { + va_list argsVL; + va_start(argsVL, pszFormat); + auto hr = details::str_vprintf_nothrow(result, pszFormat, argsVL); + va_end(argsVL); + return hr; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + // Wraps StringCchPrintFExW and stores it in an automatically allocated string. Takes a buffer followed by the same format arguments + // that StringCchPrintfExW takes. + template + string_type str_printf(_Printf_format_string_ PCWSTR pszFormat, ...) + { + string_type result; + va_list argsVL; + va_start(argsVL, pszFormat); + auto hr = details::str_vprintf_nothrow(result, pszFormat, argsVL); + va_end(argsVL); + THROW_IF_FAILED(hr); + return result; + } +#endif // WIL_ENABLE_EXCEPTIONS + + // Wraps StringCchPrintFExW and stores it in an automatically allocated string. Takes a buffer followed by the same format arguments + // that StringCchPrintfExW takes. + template + string_type str_printf_failfast(_Printf_format_string_ PCWSTR pszFormat, ...) + { + string_type result; + va_list argsVL; + va_start(argsVL, pszFormat); + auto hr = details::str_vprintf_nothrow(result, pszFormat, argsVL); + va_end(argsVL); + FAIL_FAST_IF_FAILED(hr); + return result; + } + +} // namespace wil +#endif // __WIL_RESOURCE + + + // Hash deferral function for unique_any_t +#if (defined(_UNORDERED_SET_) || defined(_UNORDERED_MAP_)) && !defined(__WIL_RESOURCE_UNIQUE_HASH) +#define __WIL_RESOURCE_UNIQUE_HASH +namespace std +{ + template + struct hash> + { + size_t operator()(wil::unique_any_t const &val) const + { + return (hash::pointer>()(val.get())); + } + }; +} +#endif + +// shared_any and weak_any implementation using STL header +#if defined(_MEMORY_) && defined(WIL_ENABLE_EXCEPTIONS) && !defined(WIL_RESOURCE_STL) && !defined(RESOURCE_SUPPRESS_STL) +#define WIL_RESOURCE_STL +namespace wil { + + template + class weak_any; + + /// @cond + namespace details + { + // This class provides the pointer storage behind the implementation of shared_any_t utilizing the given + // resource_policy. It is separate from shared_any_t to allow a type-specific specialization class to plug + // into the inheritance chain between shared_any_t and shared_storage. This allows classes like shared_event + // to be a shared_any formed class, but also expose methods like SetEvent directly. + + template + class shared_storage + { + protected: + typedef UniqueT unique_t; + typedef typename unique_t::policy policy; + typedef typename policy::pointer_storage pointer_storage; + typedef typename policy::pointer pointer; + typedef shared_storage base_storage; + + shared_storage() = default; + + explicit shared_storage(pointer_storage ptr) + { + if (policy::is_valid(ptr)) + { + m_ptr = std::make_shared(unique_t(ptr)); // unique_t on the stack to prevent leak on throw + } + } + + shared_storage(unique_t &&other) + { + if (other) + { + m_ptr = std::make_shared(wistd::move(other)); + } + } + + shared_storage(const shared_storage &other) WI_NOEXCEPT : + m_ptr(other.m_ptr) + { + } + + shared_storage& operator=(const shared_storage &other) WI_NOEXCEPT + { + m_ptr = other.m_ptr; + return *this; + } + + shared_storage(shared_storage &&other) WI_NOEXCEPT : + m_ptr(wistd::move(other.m_ptr)) + { + } + + shared_storage(std::shared_ptr const &ptr) : + m_ptr(ptr) + { + } + + void replace(shared_storage &&other) WI_NOEXCEPT + { + m_ptr = wistd::move(other.m_ptr); + } + + public: + bool is_valid() const WI_NOEXCEPT + { + return (m_ptr && m_ptr->is_valid()); + } + + void reset(pointer_storage ptr = policy::invalid_value()) + { + if (policy::is_valid(ptr)) + { + m_ptr = std::make_shared(unique_t(ptr)); // unique_t on the stack to prevent leak on throw + } + else + { + m_ptr = nullptr; + } + } + + void reset(unique_t &&other) + { + m_ptr = std::make_shared(wistd::move(other)); + } + + void reset(wistd::nullptr_t) WI_NOEXCEPT + { + static_assert(wistd::is_same::value, "reset(nullptr): valid only for handle types using nullptr as the invalid value"); + reset(); + } + + template ::value, int>::type = 0> + pointer get() const WI_NOEXCEPT + { + return (m_ptr ? m_ptr->get() : policy::invalid_value()); + } + + template ::value, int>::type = 0> + pointer_storage *addressof() + { + if (!m_ptr) + { + m_ptr = std::make_shared(); + } + return m_ptr->addressof(); + } + + long int use_count() const WI_NOEXCEPT + { + return m_ptr.use_count(); + } + + private: + template + friend class ::wil::weak_any; + + std::shared_ptr m_ptr; + }; + } + /// @endcond + + // This class when paired with shared_storage and an optional type-specific specialization class implements + // the same interface as STL's shared_ptr<> for resource handle types. It is both copyable and movable, supporting + // weak references and automatic closure of the handle upon release of the last shared_any. + + template + class shared_any_t : public storage_t + { + public: + typedef typename storage_t::policy policy; + typedef typename policy::pointer_storage pointer_storage; + typedef typename policy::pointer pointer; + typedef typename storage_t::unique_t unique_t; + + // default and forwarding constructor: forwards default, all 'explicit' and multi-arg constructors to the base class + template + explicit shared_any_t(args_t&&... args) : // should not be WI_NOEXCEPT (may forward to a throwing constructor) + storage_t(wistd::forward(args)...) + { + } + + shared_any_t(wistd::nullptr_t) WI_NOEXCEPT + { + static_assert(wistd::is_same::value, "nullptr constructor: valid only for handle types using nullptr as the invalid value"); + } + + shared_any_t(shared_any_t &&other) WI_NOEXCEPT : + storage_t(wistd::move(other)) + { + } + + shared_any_t(const shared_any_t &other) WI_NOEXCEPT : + storage_t(other) + { + } + + shared_any_t& operator=(shared_any_t &&other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + storage_t::replace(wistd::move(static_cast(other))); + } + return (*this); + } + + shared_any_t& operator=(const shared_any_t& other) WI_NOEXCEPT + { + storage_t::operator=(other); + return (*this); + } + + shared_any_t(unique_t &&other) : + storage_t(wistd::move(other)) + { + } + + shared_any_t& operator=(unique_t &&other) + { + storage_t::reset(wistd::move(other)); + return (*this); + } + + shared_any_t& operator=(wistd::nullptr_t) WI_NOEXCEPT + { + static_assert(wistd::is_same::value, "nullptr assignment: valid only for handle types using nullptr as the invalid value"); + storage_t::reset(); + return (*this); + } + + void swap(shared_any_t &other) WI_NOEXCEPT + { + shared_any_t self(wistd::move(*this)); + operator=(wistd::move(other)); + other = wistd::move(self); + } + + explicit operator bool() const WI_NOEXCEPT + { + return storage_t::is_valid(); + } + + pointer_storage *put() + { + static_assert(wistd::is_same::value, "operator & is not available for this handle"); + storage_t::reset(); + return storage_t::addressof(); + } + + pointer_storage *operator&() + { + return put(); + } + + pointer get() const WI_NOEXCEPT + { + static_assert(!wistd::is_same::value, "get(): the raw handle value is not available for this resource class"); + return storage_t::get(); + } + + // The following functions are publicly exposed by their inclusion in the base class + + // void reset(pointer_storage ptr = policy::invalid_value()) WI_NOEXCEPT + // void reset(wistd::nullptr_t) WI_NOEXCEPT + // pointer_storage *addressof() WI_NOEXCEPT // (note: not exposed for opaque resource types) + }; + + template + void swap(shared_any_t& left, shared_any_t& right) WI_NOEXCEPT + { + left.swap(right); + } + + template + bool operator==(const shared_any_t& left, const shared_any_t& right) WI_NOEXCEPT + { + return (left.get() == right.get()); + } + + template + bool operator==(const shared_any_t& left, wistd::nullptr_t) WI_NOEXCEPT + { + static_assert(wistd::is_same::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value"); + return !left; + } + + template + bool operator==(wistd::nullptr_t, const shared_any_t& right) WI_NOEXCEPT + { + static_assert(wistd::is_same::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value"); + return !right; + } + + template + bool operator!=(const shared_any_t& left, const shared_any_t& right) WI_NOEXCEPT + { + return (!(left.get() == right.get())); + } + + template + bool operator!=(const shared_any_t& left, wistd::nullptr_t) WI_NOEXCEPT + { + static_assert(wistd::is_same::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value"); + return !!left; + } + + template + bool operator!=(wistd::nullptr_t, const shared_any_t& right) WI_NOEXCEPT + { + static_assert(wistd::is_same::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value"); + return !!right; + } + + template + bool operator<(const shared_any_t& left, const shared_any_t& right) WI_NOEXCEPT + { + return (left.get() < right.get()); + } + + template + bool operator>=(const shared_any_t& left, const shared_any_t& right) WI_NOEXCEPT + { + return (!(left < right)); + } + + template + bool operator>(const shared_any_t& left, const shared_any_t& right) WI_NOEXCEPT + { + return (right < left); + } + + template + bool operator<=(const shared_any_t& left, const shared_any_t& right) WI_NOEXCEPT + { + return (!(right < left)); + } + + + // This class provides weak_ptr<> support for shared_any<>, bringing the same weak reference counting and lock() acquire semantics + // to shared_any. + + template + class weak_any + { + public: + typedef SharedT shared_t; + + weak_any() WI_NOEXCEPT + { + } + + weak_any(const shared_t &other) WI_NOEXCEPT : + m_weakPtr(other.m_ptr) + { + } + + weak_any(const weak_any &other) WI_NOEXCEPT : + m_weakPtr(other.m_weakPtr) + { + } + + weak_any& operator=(const weak_any &right) WI_NOEXCEPT + { + m_weakPtr = right.m_weakPtr; + return (*this); + } + + weak_any& operator=(const shared_t &right) WI_NOEXCEPT + { + m_weakPtr = right.m_ptr; + return (*this); + } + + void reset() WI_NOEXCEPT + { + m_weakPtr.reset(); + } + + void swap(weak_any &other) WI_NOEXCEPT + { + m_weakPtr.swap(other.m_weakPtr); + } + + bool expired() const WI_NOEXCEPT + { + return m_weakPtr.expired(); + } + + shared_t lock() const WI_NOEXCEPT + { + return shared_t(m_weakPtr.lock()); + } + + private: + std::weak_ptr m_weakPtr; + }; + + template + void swap(weak_any& left, weak_any& right) WI_NOEXCEPT + { + left.swap(right); + } + + template + using shared_any = shared_any_t>; + +} // namespace wil +#endif + + +#if defined(WIL_RESOURCE_STL) && (defined(_UNORDERED_SET_) || defined(_UNORDERED_MAP_)) && !defined(__WIL_RESOURCE_SHARED_HASH) +#define __WIL_RESOURCE_SHARED_HASH +namespace std +{ + template + struct hash> + { + size_t operator()(wil::shared_any_t const &val) const + { + return (hash::pointer>()(val.get())); + } + }; +} +#endif + + +namespace wil +{ + +#if defined(__NOTHROW_T_DEFINED) && !defined(__WIL__NOTHROW_T_DEFINED) +#define __WIL__NOTHROW_T_DEFINED + /** Provides `std::make_unique()` semantics for resources allocated in a context that may not throw upon allocation failure. + `wil::make_unique_nothrow()` is identical to `std::make_unique()` except for the following: + * It returns `wistd::unique_ptr`, rather than `std::unique_ptr` + * It returns an empty (null) `wistd::unique_ptr` upon allocation failure, rather than throwing an exception + + Note that `wil::make_unique_nothrow()` is not marked WI_NOEXCEPT as it may be used to create an exception-based class that may throw in its constructor. + ~~~ + auto foo = wil::make_unique_nothrow(fooConstructorParam1, fooConstructorParam2); + if (foo) + { + foo->Bar(); + } + ~~~ + */ + template + inline typename wistd::enable_if::value, wistd::unique_ptr<_Ty> >::type make_unique_nothrow(_Types&&... _Args) + { + return (wistd::unique_ptr<_Ty>(new(std::nothrow) _Ty(wistd::forward<_Types>(_Args)...))); + } + + /** Provides `std::make_unique()` semantics for array resources allocated in a context that may not throw upon allocation failure. + See the overload of `wil::make_unique_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_nothrow(size); // the default constructor will be called on each Foo object + if (foos) + { + for (auto& elem : wil::make_range(foos.get(), size)) + { + elem.Bar(); + } + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent<_Ty>::value == 0, wistd::unique_ptr<_Ty> >::type make_unique_nothrow(size_t _Size) + { + typedef typename wistd::remove_extent<_Ty>::type _Elem; + return (wistd::unique_ptr<_Ty>(new(std::nothrow) _Elem[_Size]())); + } + + template + typename wistd::enable_if::value != 0, void>::type make_unique_nothrow(_Types&&...) = delete; + +#if !defined(__WIL_MIN_KERNEL) && !defined(WIL_KERNEL_MODE) + /** Provides `std::make_unique()` semantics for resources allocated in a context that must fail fast upon allocation failure. + See the overload of `wil::make_unique_nothrow()` for non-array types for more details. + ~~~ + auto foo = wil::make_unique_failfast(fooConstructorParam1, fooConstructorParam2); + foo->Bar(); + ~~~ + */ + template + inline typename wistd::enable_if::value, wistd::unique_ptr<_Ty> >::type make_unique_failfast(_Types&&... _Args) + { +#pragma warning(suppress: 28193) // temporary must be inspected (it is within the called function) + return (wistd::unique_ptr<_Ty>(FAIL_FAST_IF_NULL_ALLOC(new(std::nothrow) _Ty(wistd::forward<_Types>(_Args)...)))); + } + + /** Provides `std::make_unique()` semantics for array resources allocated in a context that must fail fast upon allocation failure. + See the overload of `wil::make_unique_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_nothrow(size); // the default constructor will be called on each Foo object + for (auto& elem : wil::make_range(foos.get(), size)) + { + elem.Bar(); + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent<_Ty>::value == 0, wistd::unique_ptr<_Ty> >::type make_unique_failfast(size_t _Size) + { + typedef typename wistd::remove_extent<_Ty>::type _Elem; +#pragma warning(suppress: 28193) // temporary must be inspected (it is within the called function) + return (wistd::unique_ptr<_Ty>(FAIL_FAST_IF_NULL_ALLOC(new(std::nothrow) _Elem[_Size]()))); + } + + template + typename wistd::enable_if::value != 0, void>::type make_unique_failfast(_Types&&...) = delete; +#endif // !defined(__WIL_MIN_KERNEL) && !defined(WIL_KERNEL_MODE) +#endif // __WIL__NOTHROW_T_DEFINED + +#if defined(_WINBASE_) && !defined(__WIL_WINBASE_) && !defined(WIL_KERNEL_MODE) +#define __WIL_WINBASE_ + /// @cond + namespace details + { + inline void __stdcall SetEvent(HANDLE h) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(::SetEvent(h)); + } + + inline void __stdcall ResetEvent(HANDLE h) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(::ResetEvent(h)); + } + + inline void __stdcall CloseHandle(HANDLE h) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(::CloseHandle(h)); + } + + inline void __stdcall ReleaseSemaphore(_In_ HANDLE h) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(::ReleaseSemaphore(h, 1, nullptr)); + } + + inline void __stdcall ReleaseMutex(_In_ HANDLE h) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(::ReleaseMutex(h)); + } + + inline void __stdcall CloseTokenLinkedToken(_In_ TOKEN_LINKED_TOKEN* linkedToken) WI_NOEXCEPT + { + if (linkedToken->LinkedToken && (linkedToken->LinkedToken != INVALID_HANDLE_VALUE)) + { + __FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(::CloseHandle(linkedToken->LinkedToken)); + } + } + + enum class PendingCallbackCancellationBehavior + { + Cancel, + Wait, + NoWait, + }; + + template + struct DestroyThreadPoolWait + { + static void Destroy(_In_ PTP_WAIT threadPoolWait) WI_NOEXCEPT + { + ::SetThreadpoolWait(threadPoolWait, nullptr, nullptr); + ::WaitForThreadpoolWaitCallbacks(threadPoolWait, (cancellationBehavior == PendingCallbackCancellationBehavior::Cancel)); + ::CloseThreadpoolWait(threadPoolWait); + } + }; + + template <> + struct DestroyThreadPoolWait + { + static void Destroy(_In_ PTP_WAIT threadPoolWait) WI_NOEXCEPT + { + ::CloseThreadpoolWait(threadPoolWait); + } + }; + + template + struct DestroyThreadPoolWork + { + static void Destroy(_In_ PTP_WORK threadpoolWork) WI_NOEXCEPT + { + ::WaitForThreadpoolWorkCallbacks(threadpoolWork, (cancellationBehavior == PendingCallbackCancellationBehavior::Cancel)); + ::CloseThreadpoolWork(threadpoolWork); + } + }; + + template <> + struct DestroyThreadPoolWork + { + static void Destroy(_In_ PTP_WORK threadpoolWork) WI_NOEXCEPT + { + ::CloseThreadpoolWork(threadpoolWork); + } + }; + + // Non-RTL implementation for threadpool_t parameter of DestroyThreadPoolTimer<> + struct SystemThreadPoolMethods + { + static void WINAPI SetThreadpoolTimer(_Inout_ PTP_TIMER Timer, _In_opt_ PFILETIME DueTime, _In_ DWORD Period, _In_opt_ DWORD WindowLength) WI_NOEXCEPT + { + ::SetThreadpoolTimer(Timer, DueTime, Period, WindowLength); + } + static void WaitForThreadpoolTimerCallbacks(_Inout_ PTP_TIMER Timer, _In_ BOOL CancelPendingCallbacks) WI_NOEXCEPT + { + ::WaitForThreadpoolTimerCallbacks(Timer, CancelPendingCallbacks); + } + static void CloseThreadpoolTimer(_Inout_ PTP_TIMER Timer) WI_NOEXCEPT + { + ::CloseThreadpoolTimer(Timer); + } + }; + + // SetThreadpoolTimer(timer, nullptr, 0, 0) will cancel any pending callbacks, + // then CloseThreadpoolTimer will asynchronusly close the timer if a callback is running. + template + struct DestroyThreadPoolTimer + { + static void Destroy(_In_ PTP_TIMER threadpoolTimer) WI_NOEXCEPT + { + threadpool_t::SetThreadpoolTimer(threadpoolTimer, nullptr, 0, 0); +#pragma warning(suppress:4127) // conditional expression is constant + if (cancellationBehavior != PendingCallbackCancellationBehavior::NoWait) + { + threadpool_t::WaitForThreadpoolTimerCallbacks(threadpoolTimer, (cancellationBehavior == PendingCallbackCancellationBehavior::Cancel)); + } + threadpool_t::CloseThreadpoolTimer(threadpoolTimer); + } + }; + + // PendingCallbackCancellationBehavior::NoWait explicitly does not block waiting for + // callbacks when destructing. + template + struct DestroyThreadPoolTimer + { + static void Destroy(_In_ PTP_TIMER threadpoolTimer) WI_NOEXCEPT + { + threadpool_t::CloseThreadpoolTimer(threadpoolTimer); + } + }; + + template + struct DestroyThreadPoolIo + { + static void Destroy(_In_ PTP_IO threadpoolIo) WI_NOEXCEPT + { + ::WaitForThreadpoolIoCallbacks(threadpoolIo, (cancellationBehavior == PendingCallbackCancellationBehavior::Cancel)); + ::CloseThreadpoolIo(threadpoolIo); + } + }; + + template <> + struct DestroyThreadPoolIo + { + static void Destroy(_In_ PTP_IO threadpoolIo) WI_NOEXCEPT + { + ::CloseThreadpoolIo(threadpoolIo); + } + }; + + template + struct handle_invalid_resource_policy : resource_policy + { + __forceinline static bool is_valid(HANDLE ptr) WI_NOEXCEPT { return ((ptr != INVALID_HANDLE_VALUE) && (ptr != nullptr)); } + }; + + template + struct handle_null_resource_policy : resource_policy + { + __forceinline static bool is_valid(HANDLE ptr) WI_NOEXCEPT { return ((ptr != nullptr) && (ptr != INVALID_HANDLE_VALUE)); } + }; + + template + struct handle_null_only_resource_policy : resource_policy + { + __forceinline static bool is_valid(HANDLE ptr) WI_NOEXCEPT { return (ptr != nullptr); } + }; + + typedef resource_policy handle_resource_policy; + } + /// @endcond + + template + using unique_any_handle_invalid = unique_any_t>>; + + template + using unique_any_handle_null = unique_any_t>>; + + template + using unique_any_handle_null_only = unique_any_t>>; + + typedef unique_any_handle_invalid unique_hfile; + typedef unique_any_handle_null unique_handle; + typedef unique_any_handle_invalid unique_hfind; + typedef unique_any unique_hmodule; + typedef unique_any_handle_null_only unique_process_handle; + + typedef unique_struct unique_token_linked_token; + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) + typedef unique_any unique_sid; +#endif + + using unique_tool_help_snapshot = unique_hfile; + + typedef unique_any::Destroy> unique_threadpool_wait; + typedef unique_any::Destroy> unique_threadpool_wait_nocancel; + typedef unique_any::Destroy> unique_threadpool_wait_nowait; + typedef unique_any::Destroy> unique_threadpool_work; + typedef unique_any::Destroy> unique_threadpool_work_nocancel; + typedef unique_any::Destroy> unique_threadpool_work_nowait; + typedef unique_any::Destroy> unique_threadpool_timer; + typedef unique_any::Destroy> unique_threadpool_timer_nocancel; + typedef unique_any::Destroy> unique_threadpool_timer_nowait; + typedef unique_any::Destroy> unique_threadpool_io; + typedef unique_any::Destroy> unique_threadpool_io_nocancel; + typedef unique_any::Destroy> unique_threadpool_io_nowait; + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + typedef unique_any_handle_invalid unique_hfind_change; +#endif + + typedef unique_any event_set_scope_exit; + typedef unique_any event_reset_scope_exit; + + // Guarantees a SetEvent on the given event handle when the returned object goes out of scope + // Note: call SetEvent early with the reset() method on the returned object or abort the call with the release() method + WI_NODISCARD inline event_set_scope_exit SetEvent_scope_exit(HANDLE hEvent) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(hEvent != nullptr); + return event_set_scope_exit(hEvent); + } + + // Guarantees a ResetEvent on the given event handle when the returned object goes out of scope + // Note: call ResetEvent early with the reset() method on the returned object or abort the call with the release() method + WI_NODISCARD inline event_reset_scope_exit ResetEvent_scope_exit(HANDLE hEvent) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(hEvent != nullptr); + return event_reset_scope_exit(hEvent); + } + + // Checks to see if the given *manual reset* event is currently signaled. The event must not be an auto-reset event. + // Use when the event will only be set once (cancellation-style) or will only be reset by the polling thread + inline bool event_is_signaled(HANDLE hEvent) WI_NOEXCEPT + { + auto status = ::WaitForSingleObjectEx(hEvent, 0, FALSE); + // Fast fail will trip for wait failures, auto-reset events, or when the event is being both Set and Reset + // from a thread other than the polling thread (use event_wait directly for those cases). + __FAIL_FAST_ASSERT__((status == WAIT_TIMEOUT) || ((status == WAIT_OBJECT_0) && (WAIT_OBJECT_0 == ::WaitForSingleObjectEx(hEvent, 0, FALSE)))); + return (status == WAIT_OBJECT_0); + } + + // Waits on the given handle for the specified duration + inline bool handle_wait(HANDLE hEvent, DWORD dwMilliseconds = INFINITE, BOOL bAlertable = FALSE) WI_NOEXCEPT + { + DWORD status = ::WaitForSingleObjectEx(hEvent, dwMilliseconds, bAlertable); + __FAIL_FAST_ASSERT__((status == WAIT_TIMEOUT) || (status == WAIT_OBJECT_0) || (bAlertable && (status == WAIT_IO_COMPLETION))); + return (status == WAIT_OBJECT_0); + } + + enum class EventOptions + { + None = 0x0, + ManualReset = 0x1, + Signaled = 0x2 + }; + DEFINE_ENUM_FLAG_OPERATORS(EventOptions); + + template + class event_t : public storage_t + { + public: + // forward all base class constructors... + template + explicit event_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} + + // HRESULT or void error handling... + typedef typename err_policy::result result; + + // Exception-based constructor to create an unnamed event + event_t(EventOptions options) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions or fail fast; use the create method"); + create(options); + } + + void ResetEvent() const WI_NOEXCEPT + { + details::ResetEvent(storage_t::get()); + } + + void SetEvent() const WI_NOEXCEPT + { + details::SetEvent(storage_t::get()); + } + + // Guarantees a SetEvent on the given event handle when the returned object goes out of scope + // Note: call SetEvent early with the reset() method on the returned object or abort the call with the release() method + WI_NODISCARD event_set_scope_exit SetEvent_scope_exit() const WI_NOEXCEPT + { + return wil::SetEvent_scope_exit(storage_t::get()); + } + + // Guarantees a ResetEvent on the given event handle when the returned object goes out of scope + // Note: call ResetEvent early with the reset() method on the returned object or abort the call with the release() method + WI_NODISCARD event_reset_scope_exit ResetEvent_scope_exit() const WI_NOEXCEPT + { + return wil::ResetEvent_scope_exit(storage_t::get()); + } + + // Checks if a *manual reset* event is currently signaled. The event must not be an auto-reset event. + // Use when the event will only be set once (cancellation-style) or will only be reset by the polling thread + bool is_signaled() const WI_NOEXCEPT + { + return wil::event_is_signaled(storage_t::get()); + } + + // Basic WaitForSingleObject on the event handle with the given timeout + bool wait(DWORD dwMilliseconds = INFINITE, BOOL bAlertable = FALSE) const WI_NOEXCEPT + { + return wil::handle_wait(storage_t::get(), dwMilliseconds, bAlertable); + } + + // Tries to create a named event -- returns false if unable to do so (gle may still be inspected with return=false) + bool try_create(EventOptions options, PCWSTR name, _In_opt_ LPSECURITY_ATTRIBUTES pSecurity = nullptr, _Out_opt_ bool *pAlreadyExists = nullptr) + { + auto handle = ::CreateEventExW(pSecurity, name, (WI_IsFlagSet(options, EventOptions::ManualReset) ? CREATE_EVENT_MANUAL_RESET : 0) | (WI_IsFlagSet(options, EventOptions::Signaled) ? CREATE_EVENT_INITIAL_SET : 0), EVENT_ALL_ACCESS); + if (!handle) + { + assign_to_opt_param(pAlreadyExists, false); + return false; + } + assign_to_opt_param(pAlreadyExists, (::GetLastError() == ERROR_ALREADY_EXISTS)); + storage_t::reset(handle); + return true; + } + + // Returns HRESULT for unique_event_nothrow, void with exceptions for shared_event and unique_event + result create(EventOptions options = EventOptions::None, PCWSTR name = nullptr, _In_opt_ LPSECURITY_ATTRIBUTES pSecurity = nullptr, _Out_opt_ bool *pAlreadyExists = nullptr) + { + return err_policy::LastErrorIfFalse(try_create(options, name, pSecurity, pAlreadyExists)); + } + + // Tries to open the named event -- returns false if unable to do so (gle may still be inspected with return=false) + bool try_open(_In_ PCWSTR name, DWORD desiredAccess = SYNCHRONIZE | EVENT_MODIFY_STATE, bool inheritHandle = false) + { + auto handle = ::OpenEventW(desiredAccess, inheritHandle, name); + if (handle == nullptr) + { + return false; + } + storage_t::reset(handle); + return true; + } + + // Returns HRESULT for unique_event_nothrow, void with exceptions for shared_event and unique_event + result open(_In_ PCWSTR name, DWORD desiredAccess = SYNCHRONIZE | EVENT_MODIFY_STATE, bool inheritHandle = false) + { + return err_policy::LastErrorIfFalse(try_open(name, desiredAccess, inheritHandle)); + } + }; + + typedef unique_any_t, err_returncode_policy>> unique_event_nothrow; + typedef unique_any_t, err_failfast_policy>> unique_event_failfast; +#ifdef WIL_ENABLE_EXCEPTIONS + typedef unique_any_t, err_exception_policy>> unique_event; +#endif + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && (_WIN32_WINNT >= _WIN32_WINNT_WIN7) + enum class SlimEventType + { + AutoReset, + ManualReset, + }; + + /** A lean and mean event class. + This class provides a very similar API to `wil::unique_event` but doesn't require a kernel object. + + The two variants of this class are: + - `wil::slim_event_auto_reset` + - `wil::slim_event_manual_reset` + + In addition, `wil::slim_event_auto_reset` has the alias `wil::slim_event`. + + Some key differences to `wil::unique_event` include: + - There is no 'create()' function, as initialization occurs in the constructor and can't fail. + - The move functions have been deleted. + - For auto-reset events, the `is_signaled()` function doesn't reset the event. (Use `ResetEvent()` instead.) + - The `ResetEvent()` function returns the previous state of the event. + - To create a manual reset event, use `wil::slim_event_manual_reset'. + ~~~~ + wil::slim_event finished; + std::thread doStuff([&finished] () { + Sleep(10); + finished.SetEvent(); + }); + finished.wait(); + + std::shared_ptr CreateSharedEvent(bool startSignaled) + { + return std::make_shared(startSignaled); + } + ~~~~ */ + template + class slim_event_t + { + public: + slim_event_t() WI_NOEXCEPT = default; + + slim_event_t(bool isSignaled) WI_NOEXCEPT : + m_isSignaled(isSignaled ? TRUE : FALSE) + { + } + + // Cannot change memory location. + slim_event_t(const slim_event_t&) = delete; + slim_event_t(slim_event_t&&) = delete; + slim_event_t& operator=(const slim_event_t&) = delete; + slim_event_t& operator=(slim_event_t&&) = delete; + + // Returns the previous state of the event. + bool ResetEvent() WI_NOEXCEPT + { + return !!InterlockedExchange(&m_isSignaled, FALSE); + } + + void SetEvent() WI_NOEXCEPT + { + // FYI: 'WakeByAddress*' invokes a full memory barrier. + WriteRelease(&m_isSignaled, TRUE); + + #pragma warning(suppress:4127) // conditional expression is constant + if (Type == SlimEventType::AutoReset) + { + WakeByAddressSingle(&m_isSignaled); + } + else + { + WakeByAddressAll(&m_isSignaled); + } + } + + // Checks if the event is currently signaled. + // Note: Unlike Win32 auto-reset event objects, this will not reset the event. + bool is_signaled() const WI_NOEXCEPT + { + return !!ReadAcquire(&m_isSignaled); + } + + bool wait(DWORD timeoutMiliseconds) WI_NOEXCEPT + { + if (timeoutMiliseconds == 0) + { + return TryAcquireEvent(); + } + else if (timeoutMiliseconds == INFINITE) + { + return wait(); + } + + UINT64 startTime; + QueryUnbiasedInterruptTime(&startTime); + + UINT64 elapsedTimeMilliseconds = 0; + + while (!TryAcquireEvent()) + { + if (elapsedTimeMilliseconds >= timeoutMiliseconds) + { + return false; + } + + DWORD newTimeout = static_cast(timeoutMiliseconds - elapsedTimeMilliseconds); + + if (!WaitForSignal(newTimeout)) + { + return false; + } + + UINT64 currTime; + QueryUnbiasedInterruptTime(&currTime); + + elapsedTimeMilliseconds = (currTime - startTime) / (10 * 1000); + } + + return true; + } + + bool wait() WI_NOEXCEPT + { + while (!TryAcquireEvent()) + { + if (!WaitForSignal(INFINITE)) + { + return false; + } + } + + return true; + } + + private: + bool TryAcquireEvent() WI_NOEXCEPT + { + #pragma warning(suppress:4127) // conditional expression is constant + if (Type == SlimEventType::AutoReset) + { + return ResetEvent(); + } + else + { + return is_signaled(); + } + } + + bool WaitForSignal(DWORD timeoutMiliseconds) WI_NOEXCEPT + { + LONG falseValue = FALSE; + BOOL waitResult = WaitOnAddress(&m_isSignaled, &falseValue, sizeof(m_isSignaled), timeoutMiliseconds); + __FAIL_FAST_ASSERT__(waitResult || ::GetLastError() == ERROR_TIMEOUT); + return !!waitResult; + } + + LONG m_isSignaled = FALSE; + }; + + /** An event object that will atomically revert to an unsignaled state anytime a `wait()` call succeeds (i.e. returns true). */ + using slim_event_auto_reset = slim_event_t; + + /** An event object that once signaled remains that way forever, unless `ResetEvent()` is called. */ + using slim_event_manual_reset = slim_event_t; + + /** An alias for `wil::slim_event_auto_reset`. */ + using slim_event = slim_event_auto_reset; + +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && (_WIN32_WINNT >= _WIN32_WINNT_WIN7) + + typedef unique_any mutex_release_scope_exit; + + WI_NODISCARD inline mutex_release_scope_exit ReleaseMutex_scope_exit(_In_ HANDLE hMutex) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(hMutex != nullptr); + return mutex_release_scope_exit(hMutex); + } + + // For efficiency, avoid using mutexes when an srwlock or condition variable will do. + template + class mutex_t : public storage_t + { + public: + // forward all base class constructors... + template + explicit mutex_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} + + // HRESULT or void error handling... + typedef typename err_policy::result result; + + // Exception-based constructor to create a mutex (prefer unnamed (nullptr) for the name) + mutex_t(_In_opt_ PCWSTR name) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions or fail fast; use the create method"); + create(name); + } + + void ReleaseMutex() const WI_NOEXCEPT + { + details::ReleaseMutex(storage_t::get()); + } + + WI_NODISCARD mutex_release_scope_exit ReleaseMutex_scope_exit() const WI_NOEXCEPT + { + return wil::ReleaseMutex_scope_exit(storage_t::get()); + } + + WI_NODISCARD mutex_release_scope_exit acquire(_Out_opt_ DWORD *pStatus = nullptr, DWORD dwMilliseconds = INFINITE, BOOL bAlertable = FALSE) const WI_NOEXCEPT + { + auto handle = storage_t::get(); + DWORD status = ::WaitForSingleObjectEx(handle, dwMilliseconds, bAlertable); + assign_to_opt_param(pStatus, status); + __FAIL_FAST_ASSERT__((status == WAIT_TIMEOUT) || (status == WAIT_OBJECT_0) || (status == WAIT_ABANDONED) || (bAlertable && (status == WAIT_IO_COMPLETION))); + return mutex_release_scope_exit(((status == WAIT_OBJECT_0) || (status == WAIT_ABANDONED)) ? handle : nullptr); + } + + // Tries to create a named mutex -- returns false if unable to do so (gle may still be inspected with return=false) + bool try_create(_In_opt_ PCWSTR name, DWORD dwFlags = 0, DWORD desiredAccess = MUTEX_ALL_ACCESS, _In_opt_ PSECURITY_ATTRIBUTES pMutexAttributes = nullptr) + { + auto handle = ::CreateMutexExW(pMutexAttributes, name, dwFlags, desiredAccess); + if (handle == nullptr) + { + return false; + } + storage_t::reset(handle); + return true; + } + + // Returns HRESULT for unique_mutex_nothrow, void with exceptions for shared_mutex and unique_mutex + result create(_In_opt_ PCWSTR name = nullptr, DWORD dwFlags = 0, DWORD desiredAccess = MUTEX_ALL_ACCESS, _In_opt_ PSECURITY_ATTRIBUTES pMutexAttributes = nullptr) + { + return err_policy::LastErrorIfFalse(try_create(name, dwFlags, desiredAccess, pMutexAttributes)); + } + + // Tries to open a named mutex -- returns false if unable to do so (gle may still be inspected with return=false) + bool try_open(_In_ PCWSTR name, DWORD desiredAccess = SYNCHRONIZE | MUTEX_MODIFY_STATE, bool inheritHandle = false) + { + auto handle = ::OpenMutexW(desiredAccess, inheritHandle, name); + if (handle == nullptr) + { + return false; + } + storage_t::reset(handle); + return true; + } + + // Returns HRESULT for unique_mutex_nothrow, void with exceptions for shared_mutex and unique_mutex + result open(_In_ PCWSTR name, DWORD desiredAccess = SYNCHRONIZE | MUTEX_MODIFY_STATE, bool inheritHandle = false) + { + return err_policy::LastErrorIfFalse(try_open(name, desiredAccess, inheritHandle)); + } + }; + + typedef unique_any_t, err_returncode_policy>> unique_mutex_nothrow; + typedef unique_any_t, err_failfast_policy>> unique_mutex_failfast; +#ifdef WIL_ENABLE_EXCEPTIONS + typedef unique_any_t, err_exception_policy>> unique_mutex; +#endif + + typedef unique_any semaphore_release_scope_exit; + + WI_NODISCARD inline semaphore_release_scope_exit ReleaseSemaphore_scope_exit(_In_ HANDLE hSemaphore) WI_NOEXCEPT + { + __FAIL_FAST_ASSERT__(hSemaphore != nullptr); + return semaphore_release_scope_exit(hSemaphore); + } + + template + class semaphore_t : public storage_t + { + public: + // forward all base class constructors... + template + explicit semaphore_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} + + // HRESULT or void error handling... + typedef typename err_policy::result result; + + // Note that for custom-constructors the type given the constructor has to match exactly as not all implicit conversions will make it through the + // forwarding constructor. This constructor, for example, uses 'int' instead of 'LONG' as the count to ease that particular issue (const numbers are int by default). + explicit semaphore_t(int initialCount, int maximumCount, _In_opt_ PCWSTR name = nullptr, DWORD desiredAccess = SEMAPHORE_ALL_ACCESS, _In_opt_ PSECURITY_ATTRIBUTES pSemaphoreAttributes = nullptr) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions or fail fast; use the create method"); + create(initialCount, maximumCount, name, desiredAccess, pSemaphoreAttributes); + } + + void ReleaseSemaphore(long nReleaseCount = 1, _In_opt_ long *pnPreviousCount = nullptr) WI_NOEXCEPT + { + long nPreviousCount = 0; + __FAIL_FAST_ASSERT__(::ReleaseSemaphore(storage_t::get(), nReleaseCount, &nPreviousCount)); + assign_to_opt_param(pnPreviousCount, nPreviousCount); + } + + WI_NODISCARD semaphore_release_scope_exit ReleaseSemaphore_scope_exit() WI_NOEXCEPT + { + return wil::ReleaseSemaphore_scope_exit(storage_t::get()); + } + + WI_NODISCARD semaphore_release_scope_exit acquire(_Out_opt_ DWORD *pStatus = nullptr, DWORD dwMilliseconds = INFINITE, BOOL bAlertable = FALSE) WI_NOEXCEPT + { + auto handle = storage_t::get(); + DWORD status = ::WaitForSingleObjectEx(handle, dwMilliseconds, bAlertable); + assign_to_opt_param(pStatus, status); + __FAIL_FAST_ASSERT__((status == WAIT_TIMEOUT) || (status == WAIT_OBJECT_0) || (bAlertable && (status == WAIT_IO_COMPLETION))); + return semaphore_release_scope_exit((status == WAIT_OBJECT_0) ? handle : nullptr); + } + + // Tries to create a named event -- returns false if unable to do so (gle may still be inspected with return=false) + bool try_create(LONG lInitialCount, LONG lMaximumCount, _In_opt_ PCWSTR name, DWORD desiredAccess = SEMAPHORE_ALL_ACCESS, _In_opt_ PSECURITY_ATTRIBUTES pSemaphoreAttributes = nullptr) + { + auto handle = ::CreateSemaphoreExW(pSemaphoreAttributes, lInitialCount, lMaximumCount, name, 0, desiredAccess); + if (handle == nullptr) + { + return false; + } + storage_t::reset(handle); + return true; + } + + // Returns HRESULT for unique_semaphore_nothrow, void with exceptions for shared_event and unique_event + result create(LONG lInitialCount, LONG lMaximumCount, _In_opt_ PCWSTR name = nullptr, DWORD desiredAccess = SEMAPHORE_ALL_ACCESS, _In_opt_ PSECURITY_ATTRIBUTES pSemaphoreAttributes = nullptr) + { + return err_policy::LastErrorIfFalse(try_create(lInitialCount, lMaximumCount, name, desiredAccess, pSemaphoreAttributes)); + } + + // Tries to open the named semaphore -- returns false if unable to do so (gle may still be inspected with return=false) + bool try_open(_In_ PCWSTR name, DWORD desiredAccess = SYNCHRONIZE | SEMAPHORE_MODIFY_STATE, bool inheritHandle = false) + { + auto handle = ::OpenSemaphoreW(desiredAccess, inheritHandle, name); + if (handle == nullptr) + { + return false; + } + storage_t::reset(handle); + return true; + } + + // Returns HRESULT for unique_semaphore_nothrow, void with exceptions for shared_semaphore and unique_semaphore + result open(_In_ PCWSTR name, DWORD desiredAccess = SYNCHRONIZE | SEMAPHORE_MODIFY_STATE, bool inheritHandle = false) + { + return err_policy::LastErrorIfFalse(try_open(name, desiredAccess, inheritHandle)); + } + }; + + typedef unique_any_t, err_returncode_policy>> unique_semaphore_nothrow; + typedef unique_any_t, err_failfast_policy>> unique_semaphore_failfast; +#ifdef WIL_ENABLE_EXCEPTIONS + typedef unique_any_t, err_exception_policy>> unique_semaphore; +#endif + + typedef unique_any rwlock_release_exclusive_scope_exit; + typedef unique_any rwlock_release_shared_scope_exit; + + WI_NODISCARD inline rwlock_release_exclusive_scope_exit AcquireSRWLockExclusive(_Inout_ SRWLOCK *plock) WI_NOEXCEPT + { + ::AcquireSRWLockExclusive(plock); + return rwlock_release_exclusive_scope_exit(plock); + } + + WI_NODISCARD inline rwlock_release_shared_scope_exit AcquireSRWLockShared(_Inout_ SRWLOCK *plock) WI_NOEXCEPT + { + ::AcquireSRWLockShared(plock); + return rwlock_release_shared_scope_exit(plock); + } + + WI_NODISCARD inline rwlock_release_exclusive_scope_exit TryAcquireSRWLockExclusive(_Inout_ SRWLOCK *plock) WI_NOEXCEPT + { + return rwlock_release_exclusive_scope_exit(::TryAcquireSRWLockExclusive(plock) ? plock : nullptr); + } + + WI_NODISCARD inline rwlock_release_shared_scope_exit TryAcquireSRWLockShared(_Inout_ SRWLOCK *plock) WI_NOEXCEPT + { + return rwlock_release_shared_scope_exit(::TryAcquireSRWLockShared(plock) ? plock : nullptr); + } + + class srwlock + { + public: + srwlock(const srwlock&) = delete; + srwlock(srwlock&&) = delete; + srwlock& operator=(const srwlock&) = delete; + srwlock& operator=(srwlock&&) = delete; + + srwlock() = default; + + WI_NODISCARD rwlock_release_exclusive_scope_exit lock_exclusive() WI_NOEXCEPT + { + return wil::AcquireSRWLockExclusive(&m_lock); + } + + WI_NODISCARD rwlock_release_exclusive_scope_exit try_lock_exclusive() WI_NOEXCEPT + { + return wil::TryAcquireSRWLockExclusive(&m_lock); + } + + WI_NODISCARD rwlock_release_shared_scope_exit lock_shared() WI_NOEXCEPT + { + return wil::AcquireSRWLockShared(&m_lock); + } + + WI_NODISCARD rwlock_release_shared_scope_exit try_lock_shared() WI_NOEXCEPT + { + return wil::TryAcquireSRWLockShared(&m_lock); + } + + private: + SRWLOCK m_lock = SRWLOCK_INIT; + }; + + typedef unique_any cs_leave_scope_exit; + + WI_NODISCARD inline cs_leave_scope_exit EnterCriticalSection(_Inout_ CRITICAL_SECTION *pcs) WI_NOEXCEPT + { + ::EnterCriticalSection(pcs); + return cs_leave_scope_exit(pcs); + } + + WI_NODISCARD inline cs_leave_scope_exit TryEnterCriticalSection(_Inout_ CRITICAL_SECTION *pcs) WI_NOEXCEPT + { + return cs_leave_scope_exit(::TryEnterCriticalSection(pcs) ? pcs : nullptr); + } + + // Critical sections are worse than srwlocks in performance and memory usage (their only unique attribute + // being recursive acquisition). Prefer srwlocks over critical sections when you don't need recursive acquisition. + class critical_section + { + public: + critical_section(const critical_section&) = delete; + critical_section(critical_section&&) = delete; + critical_section& operator=(const critical_section&) = delete; + critical_section& operator=(critical_section&&) = delete; + + critical_section(ULONG spincount = 0) WI_NOEXCEPT + { + // Initialization will not fail without invalid params... + ::InitializeCriticalSectionEx(&m_cs, spincount, 0); + } + + ~critical_section() WI_NOEXCEPT + { + ::DeleteCriticalSection(&m_cs); + } + + WI_NODISCARD cs_leave_scope_exit lock() WI_NOEXCEPT + { + return wil::EnterCriticalSection(&m_cs); + } + + WI_NODISCARD cs_leave_scope_exit try_lock() WI_NOEXCEPT + { + return wil::TryEnterCriticalSection(&m_cs); + } + + private: + CRITICAL_SECTION m_cs; + }; + + class condition_variable + { + public: + condition_variable(const condition_variable&) = delete; + condition_variable(condition_variable&&) = delete; + condition_variable& operator=(const condition_variable&) = delete; + condition_variable& operator=(condition_variable&&) = delete; + + condition_variable() = default; + + void notify_one() WI_NOEXCEPT + { + ::WakeConditionVariable(&m_cv); + } + + void notify_all() WI_NOEXCEPT + { + ::WakeAllConditionVariable(&m_cv); + } + + void wait(const cs_leave_scope_exit& lock) WI_NOEXCEPT + { + wait_for(lock, INFINITE); + } + + void wait(const rwlock_release_exclusive_scope_exit& lock) WI_NOEXCEPT + { + wait_for(lock, INFINITE); + } + + void wait(const rwlock_release_shared_scope_exit& lock) WI_NOEXCEPT + { + wait_for(lock, INFINITE); + } + + bool wait_for(const cs_leave_scope_exit& lock, DWORD timeoutMs) WI_NOEXCEPT + { + bool result = !!::SleepConditionVariableCS(&m_cv, lock.get(), timeoutMs); + __FAIL_FAST_ASSERT__(result || ::GetLastError() == ERROR_TIMEOUT); + return result; + } + + bool wait_for(const rwlock_release_exclusive_scope_exit& lock, DWORD timeoutMs) WI_NOEXCEPT + { + bool result = !!::SleepConditionVariableSRW(&m_cv, lock.get(), timeoutMs, 0); + __FAIL_FAST_ASSERT__(result || ::GetLastError() == ERROR_TIMEOUT); + return result; + } + + bool wait_for(const rwlock_release_shared_scope_exit& lock, DWORD timeoutMs) WI_NOEXCEPT + { + bool result = !!::SleepConditionVariableSRW(&m_cv, lock.get(), timeoutMs, CONDITION_VARIABLE_LOCKMODE_SHARED); + __FAIL_FAST_ASSERT__(result || ::GetLastError() == ERROR_TIMEOUT); + return result; + } + + private: + CONDITION_VARIABLE m_cv = CONDITION_VARIABLE_INIT; + }; + + /// @cond + namespace details + { + template struct string_allocator + { + static void* allocate(size_t /*size*/) WI_NOEXCEPT + { + static_assert(!wistd::is_same::value, "This type did not provide a string_allocator, add a specialization of string_allocator to support your type."); + return nullptr; + } + }; + } + /// @endcond + + // This string helper does not support the ansi wil string helpers + template + PCWSTR string_get_not_null(const string_type& string) + { + return string ? string.get() : L""; + } + +#ifndef MAKE_UNIQUE_STRING_MAX_CCH +#define MAKE_UNIQUE_STRING_MAX_CCH 2147483647 // max buffer size, in characters, that we support (same as INT_MAX) +#endif + + /** Copies a string (up to the given length) into memory allocated with a specified allocator returning null on failure. + Use `wil::make_unique_string_nothrow()` for string resources returned from APIs that must satisfy a memory allocation contract + that requires use of a specific allocator and free function (CoTaskMemAlloc/CoTaskMemFree, LocalAlloc/LocalFree, GlobalAlloc/GlobalFree, etc.). + ~~~ + auto str = wil::make_unique_string_nothrow(L"a string of words", 8); + RETURN_IF_NULL_ALLOC(str); + std::wcout << L"This is " << str.get() << std::endl; // prints "This is a string" + + auto str = wil::make_unique_string_nothrow(L"a string"); + RETURN_IF_NULL_ALLOC(str); + std::wcout << L"This is " << str.get() << std::endl; // prints "This is a string" + + NOTE: If source is not null terminated, then length MUST be equal to or less than the size + of the buffer pointed to by source. + ~~~ + */ + template string_type make_unique_string_nothrow( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + const wchar_t* source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + // guard against invalid parameters (null source with -1 length) + FAIL_FAST_IF(!source && (length == static_cast(-1))); + + // When the source string exists, calculate the number of characters to copy up to either + // 1) the length that is given + // 2) the length of the source string. When the source does not exist, use the given length + // for calculating both the size of allocated buffer and the number of characters to copy. + size_t lengthToCopy = length; + if (source) + { + size_t maxLength = length < MAKE_UNIQUE_STRING_MAX_CCH ? length : MAKE_UNIQUE_STRING_MAX_CCH; + PCWSTR endOfSource = source; + while (maxLength && (*endOfSource != L'\0')) + { + endOfSource++; + maxLength--; + } + lengthToCopy = endOfSource - source; + } + + if (length == static_cast(-1)) + { + length = lengthToCopy; + } + const size_t allocatedBytes = (length + 1) * sizeof(*source); + auto result = static_cast(details::string_allocator::allocate(allocatedBytes)); + + if (result) + { + if (source) + { + const size_t bytesToCopy = lengthToCopy * sizeof(*source); + memcpy_s(result, allocatedBytes, source, bytesToCopy); + result[lengthToCopy] = L'\0'; // ensure the copied string is zero terminated + } + else + { + *result = L'\0'; // ensure null terminated in the "reserve space" use case. + } + result[length] = L'\0'; // ensure the final char of the buffer is zero terminated + } + return string_type(result); + } +#ifndef WIL_NO_ANSI_STRINGS + template string_type make_unique_ansistring_nothrow( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + // guard against invalid parameters (null source with -1 length) + FAIL_FAST_IF(!source && (length == static_cast(-1))); + + if (length == static_cast(-1)) + { + length = strlen(source); + } + const size_t cb = (length + 1) * sizeof(*source); + auto result = static_cast(details::string_allocator::allocate(cb)); + if (result) + { + if (source) + { + memcpy_s(result, cb, source, cb - sizeof(*source)); + } + else + { + *result = '\0'; // ensure null terminated in the "reserve space" use case. + } + result[length] = '\0'; // ensure zero terminated + } + return string_type(result); + } +#endif // WIL_NO_ANSI_STRINGS + + /** Copies a given string into memory allocated with a specified allocator that will fail fast on failure. + The use of variadic templates parameters supports the 2 forms of make_unique_string, see those for more details. + */ + template string_type make_unique_string_failfast( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + auto result(make_unique_string_nothrow(source, length)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } + +#ifndef WIL_NO_ANSI_STRINGS + template string_type make_unique_ansistring_failfast( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + auto result(make_unique_ansistring_nothrow(source, length)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } +#endif // WIL_NO_ANSI_STRINGS + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Copies a given string into memory allocated with a specified allocator that will throw on failure. + The use of variadic templates parameters supports the 2 forms of make_unique_string, see those for more details. + */ + template string_type make_unique_string( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length = static_cast(-1)) + { + auto result(make_unique_string_nothrow(source, length)); + THROW_IF_NULL_ALLOC(result); + return result; + } +#ifndef WIL_NO_ANSI_STRINGS + template string_type make_unique_ansistring( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCSTR source, size_t length = static_cast(-1)) + { + auto result(make_unique_ansistring_nothrow(source, length)); + THROW_IF_NULL_ALLOC(result); + return result; + } +#endif // WIL_NO_ANSI_STRINGS +#endif // WIL_ENABLE_EXCEPTIONS + + /// @cond + namespace details + { + // string_maker abstracts creating a string for common string types. This form supports the + // wil::unique_xxx_string types. Specializations of other types like HSTRING and std::wstring + // are found in wil\winrt.h and wil\stl.h. + // This design supports creating the string in a single step or using two phase construction. + + template struct string_maker + { + HRESULT make( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + const wchar_t* source, + size_t length) + { + m_value = make_unique_string_nothrow(source, length); + return m_value ? S_OK : E_OUTOFMEMORY; + } + + wchar_t* buffer() { WI_ASSERT(m_value.get()); return m_value.get(); } + + // By default, assume string_type is a null-terminated string and therefore does not require trimming. + HRESULT trim_at_existing_null(size_t /* length */) { return S_OK; } + + string_type release() { return wistd::move(m_value); } + + // Utility to abstract access to the null terminated m_value of all string types. + static PCWSTR get(const string_type& value) { return value.get(); } + + private: + string_type m_value; // a wil::unique_xxx_string type. + }; + + struct SecureZeroData + { + void *pointer; + size_t sizeBytes; + SecureZeroData(void *pointer_, size_t sizeBytes_ = 0) WI_NOEXCEPT { pointer = pointer_; sizeBytes = sizeBytes_; } + operator void *() const WI_NOEXCEPT { return pointer; } + static void Close(SecureZeroData data) WI_NOEXCEPT { ::SecureZeroMemory(data.pointer, data.sizeBytes); } + }; + } + /// @endcond + + typedef unique_any secure_zero_memory_scope_exit; + + WI_NODISCARD inline secure_zero_memory_scope_exit SecureZeroMemory_scope_exit(_In_reads_bytes_(sizeBytes) void* pSource, size_t sizeBytes) + { + return secure_zero_memory_scope_exit(details::SecureZeroData(pSource, sizeBytes)); + } + + WI_NODISCARD inline secure_zero_memory_scope_exit SecureZeroMemory_scope_exit(_In_ PWSTR initializedString) + { + return SecureZeroMemory_scope_exit(static_cast(initializedString), wcslen(initializedString) * sizeof(initializedString[0])); + } + + /// @cond + namespace details + { + inline void __stdcall FreeProcessHeap(_Pre_opt_valid_ _Frees_ptr_opt_ void* p) + { + ::HeapFree(::GetProcessHeap(), 0, p); + } + } + /// @endcond + + struct process_heap_deleter + { + template + void operator()(_Pre_valid_ _Frees_ptr_ T* p) const + { + details::FreeProcessHeap(p); + } + }; + + struct virtualalloc_deleter + { + template + void operator()(_Pre_valid_ _Frees_ptr_ T* p) const + { + ::VirtualFree(p, 0, MEM_RELEASE); + } + }; + + struct mapview_deleter + { + template + void operator()(_Pre_valid_ _Frees_ptr_ T* p) const + { + ::UnmapViewOfFile(p); + } + }; + + template + using unique_process_heap_ptr = wistd::unique_ptr; + + typedef unique_any unique_process_heap_string; + + /// @cond + namespace details + { + template<> struct string_allocator + { + static _Ret_opt_bytecap_(size) void* allocate(size_t size) WI_NOEXCEPT + { + return ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, size); + } + }; + } + /// @endcond + + /** Manages a typed pointer allocated with VirtualAlloc + A specialization of wistd::unique_ptr<> that frees via VirtualFree(p, 0, MEM_RELEASE). + */ + template + using unique_virtualalloc_ptr = wistd::unique_ptr; + + /** Manages a typed pointer allocated with MapViewOfFile + A specialization of wistd::unique_ptr<> that frees via UnmapViewOfFile(p). + */ + template + using unique_mapview_ptr = wistd::unique_ptr; + +#endif // __WIL_WINBASE_ + +#if defined(__WIL_WINBASE_) && defined(__NOTHROW_T_DEFINED) && !defined(__WIL_WINBASE_NOTHROW_T_DEFINED) +#define __WIL_WINBASE_NOTHROW_T_DEFINED + // unique_event_watcher, unique_event_watcher_nothrow, unique_event_watcher_failfast + // + // Clients must include or to enable use of this class as it uses new(std::nothrow). + // This is to avoid the dependency on those headers that some clients can't tolerate. + // + // These classes makes it easy to execute a provided function when an event + // is signaled. It will create the event handle for you, take ownership of one + // or duplicate a handle provided. It supports the ability to signal the + // event using SetEvent() and SetEvent_scope_exit(); + // + // This can be used to support producer-consumer pattern + // where a producer updates some state then signals the event when done. + // The consumer will consume that state in the callback provided to unique_event_watcher. + // + // Note, multiple signals may coalesce into a single callback. + // + // Example use of throwing version: + // auto globalStateWatcher = wil::make_event_watcher([] + // { + // currentState = GetGlobalState(); + // }); + // + // UpdateGlobalState(value); + // globalStateWatcher.SetEvent(); // signal observers so they can update + // + // Example use of non-throwing version: + // auto globalStateWatcher = wil::make_event_watcher_nothrow([] + // { + // currentState = GetGlobalState(); + // }); + // RETURN_IF_NULL_ALLOC(globalStateWatcher); + // + // UpdateGlobalState(value); + // globalStateWatcher.SetEvent(); // signal observers so they can update + + /// @cond + namespace details + { + struct event_watcher_state + { + event_watcher_state(unique_event_nothrow &&eventHandle, wistd::function &&callback) + : m_callback(wistd::move(callback)), m_event(wistd::move(eventHandle)) + { + } + wistd::function m_callback; + unique_event_nothrow m_event; + // The thread pool must be last to ensure that the other members are valid + // when it is destructed as it will reference them. + unique_threadpool_wait m_threadPoolWait; + }; + + inline void delete_event_watcher_state(_In_opt_ event_watcher_state *watcherStorage) { delete watcherStorage; } + + typedef resource_policy event_watcher_state_resource_policy; + } + /// @endcond + + template + class event_watcher_t : public storage_t + { + public: + // forward all base class constructors... + template + explicit event_watcher_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward(args)...) {} + + // HRESULT or void error handling... + typedef typename err_policy::result result; + + // Exception-based constructors + template + event_watcher_t(unique_any_t, from_err_policy>> &&eventHandle, wistd::function &&callback) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions or fail fast; use the create method"); + create(wistd::move(eventHandle), wistd::move(callback)); + } + + event_watcher_t(_In_ HANDLE eventHandle, wistd::function &&callback) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions or fail fast; use the create method"); + create(eventHandle, wistd::move(callback)); + } + + event_watcher_t(wistd::function &&callback) + { + static_assert(wistd::is_same::value, "this constructor requires exceptions or fail fast; use the create method"); + create(wistd::move(callback)); + } + + template + result create(unique_any_t, event_err_policy>> &&eventHandle, + wistd::function &&callback) + { + return err_policy::HResult(create_take_hevent_ownership(eventHandle.release(), wistd::move(callback))); + } + + // Creates the event that you will be watching. + result create(wistd::function &&callback) + { + unique_event_nothrow eventHandle; + HRESULT hr = eventHandle.create(EventOptions::ManualReset); // auto-reset is supported too. + if (FAILED(hr)) + { + return err_policy::HResult(hr); + } + return err_policy::HResult(create_take_hevent_ownership(eventHandle.release(), wistd::move(callback))); + } + + // Input is an event handler that is duplicated into this class. + result create(_In_ HANDLE eventHandle, wistd::function &&callback) + { + unique_event_nothrow ownedHandle; + if (!DuplicateHandle(GetCurrentProcess(), eventHandle, GetCurrentProcess(), &ownedHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + return err_policy::LastError(); + } + return err_policy::HResult(create_take_hevent_ownership(ownedHandle.release(), wistd::move(callback))); + } + + // Provide access to the inner event and the very common SetEvent() method on it. + unique_event_nothrow const& get_event() const WI_NOEXCEPT { return storage_t::get()->m_event; } + void SetEvent() const WI_NOEXCEPT { storage_t::get()->m_event.SetEvent(); } + + private: + + // Had to move this from a Lambda so it would compile in C++/CLI (which thought the Lambda should be a managed function for some reason). + static void CALLBACK wait_callback(PTP_CALLBACK_INSTANCE, void *context, TP_WAIT *pThreadPoolWait, TP_WAIT_RESULT) + { + auto pThis = static_cast(context); + // Manual events must be re-set to avoid missing the last notification. + pThis->m_event.ResetEvent(); + // Call the client before re-arming to ensure that multiple callbacks don't + // run concurrently. + pThis->m_callback(); + SetThreadpoolWait(pThreadPoolWait, pThis->m_event.get(), nullptr); // valid params ensure success + } + + // To avoid template expansion (if unique_event/unique_event_nothrow forms were used) this base + // create function takes a raw handle and assumes its ownership, even on failure. + HRESULT create_take_hevent_ownership(_In_ HANDLE rawHandleOwnershipTaken, wistd::function &&callback) + { + __FAIL_FAST_ASSERT__(rawHandleOwnershipTaken != nullptr); // invalid parameter + unique_event_nothrow eventHandle(rawHandleOwnershipTaken); + wistd::unique_ptr watcherState(new(std::nothrow) details::event_watcher_state(wistd::move(eventHandle), wistd::move(callback))); + RETURN_IF_NULL_ALLOC(watcherState); + + watcherState->m_threadPoolWait.reset(CreateThreadpoolWait(wait_callback, watcherState.get(), nullptr)); + RETURN_LAST_ERROR_IF(!watcherState->m_threadPoolWait); + storage_t::reset(watcherState.release()); // no more failures after this, pass ownership + SetThreadpoolWait(storage_t::get()->m_threadPoolWait.get(), storage_t::get()->m_event.get(), nullptr); + return S_OK; + } + }; + + typedef unique_any_t, err_returncode_policy>> unique_event_watcher_nothrow; + typedef unique_any_t, err_failfast_policy>> unique_event_watcher_failfast; + + template + unique_event_watcher_nothrow make_event_watcher_nothrow(unique_any_t, err_policy>> &&eventHandle, wistd::function &&callback) WI_NOEXCEPT + { + unique_event_watcher_nothrow watcher; + watcher.create(wistd::move(eventHandle), wistd::move(callback)); + return watcher; // caller must test for success using if (watcher) + } + + inline unique_event_watcher_nothrow make_event_watcher_nothrow(_In_ HANDLE eventHandle, wistd::function &&callback) WI_NOEXCEPT + { + unique_event_watcher_nothrow watcher; + watcher.create(eventHandle, wistd::move(callback)); + return watcher; // caller must test for success using if (watcher) + } + + inline unique_event_watcher_nothrow make_event_watcher_nothrow(wistd::function &&callback) WI_NOEXCEPT + { + unique_event_watcher_nothrow watcher; + watcher.create(wistd::move(callback)); + return watcher; // caller must test for success using if (watcher) + } + + template + unique_event_watcher_failfast make_event_watcher_failfast(unique_any_t, err_policy>> &&eventHandle, wistd::function &&callback) + { + return unique_event_watcher_failfast(wistd::move(eventHandle), wistd::move(callback)); + } + + inline unique_event_watcher_failfast make_event_watcher_failfast(_In_ HANDLE eventHandle, wistd::function &&callback) + { + return unique_event_watcher_failfast(eventHandle, wistd::move(callback)); + } + + inline unique_event_watcher_failfast make_event_watcher_failfast(wistd::function &&callback) + { + return unique_event_watcher_failfast(wistd::move(callback)); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + typedef unique_any_t, err_exception_policy>> unique_event_watcher; + + template + unique_event_watcher make_event_watcher(unique_any_t, err_policy>> &&eventHandle, wistd::function &&callback) + { + return unique_event_watcher(wistd::move(eventHandle), wistd::move(callback)); + } + + inline unique_event_watcher make_event_watcher(_In_ HANDLE eventHandle, wistd::function &&callback) + { + return unique_event_watcher(eventHandle, wistd::move(callback)); + } + + inline unique_event_watcher make_event_watcher(wistd::function &&callback) + { + return unique_event_watcher(wistd::move(callback)); + } +#endif // WIL_ENABLE_EXCEPTIONS + +#endif // __WIL_WINBASE_NOTHROW_T_DEFINED + +#if defined(__WIL_WINBASE_) && !defined(__WIL_WINBASE_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_WINBASE_STL + typedef shared_any_t>> shared_event; + typedef shared_any_t>> shared_mutex; + typedef shared_any_t>> shared_semaphore; + typedef shared_any shared_hfile; + typedef shared_any shared_handle; + typedef shared_any shared_hfind; + typedef shared_any shared_hmodule; + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + typedef shared_any shared_threadpool_wait; + typedef shared_any shared_threadpool_wait_nocancel; + typedef shared_any shared_threadpool_work; + typedef shared_any shared_threadpool_work_nocancel; + + typedef shared_any shared_hfind_change; +#endif + + typedef weak_any weak_event; + typedef weak_any weak_mutex; + typedef weak_any weak_semaphore; + typedef weak_any weak_hfile; + typedef weak_any weak_handle; + typedef weak_any weak_hfind; + typedef weak_any weak_hmodule; + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + typedef weak_any weak_threadpool_wait; + typedef weak_any weak_threadpool_wait_nocancel; + typedef weak_any weak_threadpool_work; + typedef weak_any weak_threadpool_work_nocancel; + + typedef weak_any weak_hfind_change; +#endif + +#endif // __WIL_WINBASE_STL + +#if defined(__WIL_WINBASE_) && defined(__NOTHROW_T_DEFINED) && !defined(__WIL_WINBASE_NOTHROW_T_DEFINED_STL) && defined(WIL_RESOURCE_STL) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_WINBASE_NOTHROW_T_DEFINED_STL + typedef shared_any_t>> shared_event_watcher; + typedef weak_any weak_event_watcher; +#endif // __WIL_WINBASE_NOTHROW_T_DEFINED_STL + +#if defined(__WIL_WINBASE_) && !defined(__WIL_WINBASE_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_WINBASE_DESKTOP + /// @cond + namespace details + { + inline void __stdcall DestroyPrivateObjectSecurity(_Pre_opt_valid_ _Frees_ptr_opt_ PSECURITY_DESCRIPTOR pObjectDescriptor) WI_NOEXCEPT + { + ::DestroyPrivateObjectSecurity(&pObjectDescriptor); + } + } + /// @endcond + + using hlocal_deleter = function_deleter; + + template + using unique_hlocal_ptr = wistd::unique_ptr; + + /** Provides `std::make_unique()` semantics for resources allocated with `LocalAlloc()` in a context that may not throw upon allocation failure. + Use `wil::make_unique_hlocal_nothrow()` for resources returned from APIs that must satisfy a memory allocation contract that requires the use of `LocalAlloc()` / `LocalFree()`. + Use `wil::make_unique_nothrow()` when `LocalAlloc()` is not required. + + Allocations are initialized with placement new and will call constructors (if present), but this does not guarantee initialization. + + Note that `wil::make_unique_hlocal_nothrow()` is not marked WI_NOEXCEPT as it may be used to create an exception-based class that may throw in its constructor. + ~~~ + auto foo = wil::make_unique_hlocal_nothrow(); + if (foo) + { + // initialize allocated Foo object as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_hlocal_ptr>::type make_unique_hlocal_nothrow(Args&&... args) + { + static_assert(wistd::is_trivially_destructible::value, "T has a destructor that won't be run when used with this function; use make_unique instead"); + unique_hlocal_ptr sp(static_cast(::LocalAlloc(LMEM_FIXED, sizeof(T)))); + if (sp) + { + // use placement new to initialize memory from the previous allocation + new (sp.get()) T(wistd::forward(args)...); + } + return sp; + } + + /** Provides `std::make_unique()` semantics for array resources allocated with `LocalAlloc()` in a context that may not throw upon allocation failure. + See the overload of `wil::make_unique_hlocal_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_hlocal_nothrow(size); + if (foos) + { + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_hlocal_ptr>::type make_unique_hlocal_nothrow(size_t size) + { + typedef typename wistd::remove_extent::type E; + static_assert(wistd::is_trivially_destructible::value, "E has a destructor that won't be run when used with this function; use make_unique instead"); + FAIL_FAST_IF((__WI_SIZE_MAX / sizeof(E)) < size); + size_t allocSize = sizeof(E) * size; + unique_hlocal_ptr sp(static_cast(::LocalAlloc(LMEM_FIXED, allocSize))); + if (sp) + { + // use placement new to initialize memory from the previous allocation; + // note that array placement new cannot be used as the standard allows for operator new[] + // to consume overhead in the allocation for internal bookkeeping + for (auto& elem : make_range(static_cast(sp.get()), size)) + { + new (&elem) E(); + } + } + return sp; + } + + /** Provides `std::make_unique()` semantics for resources allocated with `LocalAlloc()` in a context that must fail fast upon allocation failure. + See the overload of `wil::make_unique_hlocal_nothrow()` for non-array types for more details. + ~~~ + auto foo = wil::make_unique_hlocal_failfast(); + // initialize allocated Foo object as appropriate + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_hlocal_ptr>::type make_unique_hlocal_failfast(Args&&... args) + { + unique_hlocal_ptr result(make_unique_hlocal_nothrow(wistd::forward(args)...)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } + + /** Provides `std::make_unique()` semantics for array resources allocated with `LocalAlloc()` in a context that must fail fast upon allocation failure. + See the overload of `wil::make_unique_hlocal_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_hlocal_failfast(size); + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_hlocal_ptr>::type make_unique_hlocal_failfast(size_t size) + { + unique_hlocal_ptr result(make_unique_hlocal_nothrow(size)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Provides `std::make_unique()` semantics for resources allocated with `LocalAlloc()`. + See the overload of `wil::make_unique_hlocal_nothrow()` for non-array types for more details. + ~~~ + auto foo = wil::make_unique_hlocal(); + // initialize allocated Foo object as appropriate + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_hlocal_ptr>::type make_unique_hlocal(Args&&... args) + { + unique_hlocal_ptr result(make_unique_hlocal_nothrow(wistd::forward(args)...)); + THROW_IF_NULL_ALLOC(result); + return result; + } + + /** Provides `std::make_unique()` semantics for array resources allocated with `LocalAlloc()`. + See the overload of `wil::make_unique_hlocal_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_hlocal(size); + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_hlocal_ptr>::type make_unique_hlocal(size_t size) + { + unique_hlocal_ptr result(make_unique_hlocal_nothrow(size)); + THROW_IF_NULL_ALLOC(result); + return result; + } +#endif // WIL_ENABLE_EXCEPTIONS + + typedef unique_any unique_hlocal; + typedef unique_any unique_hlocal_string; +#ifndef WIL_NO_ANSI_STRINGS + typedef unique_any unique_hlocal_ansistring; +#endif // WIL_NO_ANSI_STRINGS + + /// @cond + namespace details + { + struct localalloc_allocator + { + static _Ret_opt_bytecap_(size) void* allocate(size_t size) WI_NOEXCEPT + { + return ::LocalAlloc(LMEM_FIXED, size); + } + }; + + template<> struct string_allocator : localalloc_allocator {}; +#ifndef WIL_NO_ANSI_STRINGS + template<> struct string_allocator : localalloc_allocator {}; +#endif // WIL_NO_ANSI_STRINGS + } + /// @endcond + + inline auto make_hlocal_string_nothrow( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + return make_unique_string_nothrow(source, length); + } + + inline auto make_hlocal_string_failfast( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + return make_unique_string_failfast(source, length); + } + +#ifndef WIL_NO_ANSI_STRINGS + inline auto make_hlocal_ansistring_nothrow( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + return make_unique_ansistring_nothrow(source, length); + } + + inline auto make_hlocal_ansistring_failfast( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + return make_unique_ansistring_failfast(source, length); + } +#endif + +#ifdef WIL_ENABLE_EXCEPTIONS + inline auto make_hlocal_string( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length = static_cast(-1)) + { + return make_unique_string(source, length); + } + +#ifndef WIL_NO_ANSI_STRINGS + inline auto make_hlocal_ansistring( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCSTR source, size_t length = static_cast(-1)) + { + return make_unique_ansistring(source, length); + } +#endif // WIL_NO_ANSI_STRINGS +#endif // WIL_ENABLE_EXCEPTIONS + + struct hlocal_secure_deleter + { + template + void operator()(_Pre_opt_valid_ _Frees_ptr_opt_ T* p) const + { + if (p) + { +#pragma warning(suppress: 26006 26007) // LocalSize() ensures proper buffer length + ::SecureZeroMemory(p, ::LocalSize(p)); // this is safe since LocalSize() returns 0 on failure + ::LocalFree(p); + } + } + }; + + template + using unique_hlocal_secure_ptr = wistd::unique_ptr; + + /** Provides `std::make_unique()` semantics for secure resources allocated with `LocalAlloc()` in a context that may not throw upon allocation failure. + See the overload of `wil::make_unique_hlocal_nothrow()` for non-array types for more details. + ~~~ + auto foo = wil::make_unique_hlocal_secure_nothrow(); + if (foo) + { + // initialize allocated Foo object as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_hlocal_secure_ptr>::type make_unique_hlocal_secure_nothrow(Args&&... args) + { + return unique_hlocal_secure_ptr(make_unique_hlocal_nothrow(wistd::forward(args)...).release()); + } + + /** Provides `std::make_unique()` semantics for secure array resources allocated with `LocalAlloc()` in a context that may not throw upon allocation failure. + See the overload of `wil::make_unique_hlocal_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_hlocal_secure_nothrow(size); + if (foos) + { + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_hlocal_secure_ptr>::type make_unique_hlocal_secure_nothrow(size_t size) + { + return unique_hlocal_secure_ptr(make_unique_hlocal_nothrow(size).release()); + } + + /** Provides `std::make_unique()` semantics for secure resources allocated with `LocalAlloc()` in a context that must fail fast upon allocation failure. + See the overload of `wil::make_unique_hlocal_nothrow()` for non-array types for more details. + ~~~ + auto foo = wil::make_unique_hlocal_secure_failfast(); + // initialize allocated Foo object as appropriate + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_hlocal_secure_ptr>::type make_unique_hlocal_secure_failfast(Args&&... args) + { + unique_hlocal_secure_ptr result(make_unique_hlocal_secure_nothrow(wistd::forward(args)...)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } + + /** Provides `std::make_unique()` semantics for secure array resources allocated with `LocalAlloc()` in a context that must fail fast upon allocation failure. + See the overload of `wil::make_unique_hlocal_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_hlocal_secure_failfast(size); + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_hlocal_secure_ptr>::type make_unique_hlocal_secure_failfast(size_t size) + { + unique_hlocal_secure_ptr result(make_unique_hlocal_secure_nothrow(size)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Provides `std::make_unique()` semantics for secure resources allocated with `LocalAlloc()`. + See the overload of `wil::make_unique_hlocal_nothrow()` for non-array types for more details. + ~~~ + auto foo = wil::make_unique_hlocal_secure(); + // initialize allocated Foo object as appropriate + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_hlocal_secure_ptr>::type make_unique_hlocal_secure(Args&&... args) + { + unique_hlocal_secure_ptr result(make_unique_hlocal_secure_nothrow(wistd::forward(args)...)); + THROW_IF_NULL_ALLOC(result); + return result; + } + + /** Provides `std::make_unique()` semantics for secure array resources allocated with `LocalAlloc()`. + See the overload of `wil::make_unique_hlocal_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_hlocal_secure(size); + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_hlocal_secure_ptr>::type make_unique_hlocal_secure(size_t size) + { + unique_hlocal_secure_ptr result(make_unique_hlocal_secure_nothrow(size)); + THROW_IF_NULL_ALLOC(result); + return result; + } +#endif // WIL_ENABLE_EXCEPTIONS + + typedef unique_hlocal_secure_ptr unique_hlocal_string_secure; + + /** Copies a given string into secure memory allocated with `LocalAlloc()` in a context that may not throw upon allocation failure. + See the overload of `wil::make_hlocal_string_nothrow()` with supplied length for more details. + ~~~ + auto str = wil::make_hlocal_string_secure_nothrow(L"a string"); + RETURN_IF_NULL_ALLOC(str); + std::wcout << L"This is " << str.get() << std::endl; // prints "This is a string" + ~~~ + */ + inline auto make_hlocal_string_secure_nothrow(_In_ PCWSTR source) WI_NOEXCEPT + { + return unique_hlocal_string_secure(make_hlocal_string_nothrow(source).release()); + } + + /** Copies a given string into secure memory allocated with `LocalAlloc()` in a context that must fail fast upon allocation failure. + See the overload of `wil::make_hlocal_string_nothrow()` with supplied length for more details. + ~~~ + auto str = wil::make_hlocal_string_secure_failfast(L"a string"); + std::wcout << L"This is " << str.get() << std::endl; // prints "This is a string" + ~~~ + */ + inline auto make_hlocal_string_secure_failfast(_In_ PCWSTR source) WI_NOEXCEPT + { + unique_hlocal_string_secure result(make_hlocal_string_secure_nothrow(source)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Copies a given string into secure memory allocated with `LocalAlloc()`. + See the overload of `wil::make_hlocal_string_nothrow()` with supplied length for more details. + ~~~ + auto str = wil::make_hlocal_string_secure(L"a string"); + std::wcout << L"This is " << str.get() << std::endl; // prints "This is a string" + ~~~ + */ + inline auto make_hlocal_string_secure(_In_ PCWSTR source) + { + unique_hlocal_string_secure result(make_hlocal_string_secure_nothrow(source)); + THROW_IF_NULL_ALLOC(result); + return result; + } +#endif + + using hglobal_deleter = function_deleter; + + template + using unique_hglobal_ptr = wistd::unique_ptr; + + typedef unique_any unique_hglobal; + typedef unique_any unique_hglobal_string; +#ifndef WIL_NO_ANSI_STRINGS + typedef unique_any unique_hglobal_ansistring; +#endif // WIL_NO_ANSI_STRINGS + + /// @cond + namespace details + { + template<> struct string_allocator + { + static _Ret_opt_bytecap_(size) void* allocate(size_t size) WI_NOEXCEPT + { + return ::GlobalAlloc(GPTR, size); + } + }; + } + /// @endcond + + inline auto make_process_heap_string_nothrow( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + return make_unique_string_nothrow(source, length); + } + + inline auto make_process_heap_string_failfast( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + return make_unique_string_failfast(source, length); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + inline auto make_process_heap_string( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length = static_cast(-1)) + { + return make_unique_string(source, length); + } +#endif // WIL_ENABLE_EXCEPTIONS + + typedef unique_any_handle_null unique_hheap; + typedef unique_any unique_tls; + typedef unique_any unique_hlocal_security_descriptor; + typedef unique_any unique_private_security_descriptor; + +#if defined(_WINUSER_) && !defined(__WIL__WINUSER_) +#define __WIL__WINUSER_ + typedef unique_any unique_haccel; + typedef unique_any unique_hcursor; + typedef unique_any unique_hwnd; +#if !defined(NOUSER) && !defined(NOWH) + typedef unique_any unique_hhook; +#endif +#if !defined(NOWINABLE) + typedef unique_any unique_hwineventhook; +#endif +#endif // __WIL__WINUSER_ + +#if !defined(NOGDI) && !defined(NODESKTOP) + typedef unique_any unique_hdesk; + typedef unique_any unique_hwinsta; +#endif // !defined(NOGDI) && !defined(NODESKTOP) + +#endif +#if defined(__WIL_WINBASE_DESKTOP) && !defined(__WIL_WINBASE_DESKTOP_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_WINBASE_DESKTOP_STL + typedef shared_any shared_hheap; + typedef shared_any shared_hlocal; + typedef shared_any shared_tls; + typedef shared_any shared_hlocal_security_descriptor; + typedef shared_any shared_private_security_descriptor; + typedef shared_any shared_haccel; + typedef shared_any shared_hcursor; +#if !defined(NOGDI) && !defined(NODESKTOP) + typedef shared_any shared_hdesk; + typedef shared_any shared_hwinsta; +#endif // !defined(NOGDI) && !defined(NODESKTOP) + typedef shared_any shared_hwnd; +#if !defined(NOUSER) && !defined(NOWH) + typedef shared_any shared_hhook; +#endif +#if !defined(NOWINABLE) + typedef shared_any shared_hwineventhook; +#endif + + typedef weak_any weak_hheap; + typedef weak_any weak_hlocal; + typedef weak_any weak_tls; + typedef weak_any weak_hlocal_security_descriptor; + typedef weak_any weak_private_security_descriptor; + typedef weak_any weak_haccel; + typedef weak_any weak_hcursor; +#if !defined(NOGDI) && !defined(NODESKTOP) + typedef weak_any weak_hdesk; + typedef weak_any weak_hwinsta; +#endif // !defined(NOGDI) && !defined(NODESKTOP) + typedef weak_any weak_hwnd; +#if !defined(NOUSER) && !defined(NOWH) + typedef weak_any weak_hhook; +#endif +#if !defined(NOWINABLE) + typedef weak_any weak_hwineventhook; +#endif +#endif // __WIL_WINBASE_DESKTOP_STL + +#if defined(_COMBASEAPI_H_) && !defined(__WIL__COMBASEAPI_H_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) && !defined(WIL_KERNEL_MODE) +#define __WIL__COMBASEAPI_H_ +#if (NTDDI_VERSION >= NTDDI_WIN8) + typedef unique_any unique_mta_usage_cookie; +#endif + + typedef unique_any unique_com_class_object_cookie; + + /// @cond + namespace details + { + inline void __stdcall MultiQiCleanup(_In_ MULTI_QI* multiQi) + { + if (multiQi->pItf) + { + multiQi->pItf->Release(); + multiQi->pItf = nullptr; + } + } + } + /// @endcond + + //! A type that calls CoRevertToSelf on destruction (or reset()). + using unique_coreverttoself_call = unique_call; + + //! Calls CoImpersonateClient and fail-fasts if it fails; returns an RAII object that reverts + WI_NODISCARD inline unique_coreverttoself_call CoImpersonateClient_failfast() + { + FAIL_FAST_IF_FAILED(::CoImpersonateClient()); + return unique_coreverttoself_call(); + } + + typedef unique_struct unique_multi_qi; +#endif // __WIL__COMBASEAPI_H_ +#if defined(__WIL__COMBASEAPI_H_) && defined(WIL_ENABLE_EXCEPTIONS) && !defined(__WIL__COMBASEAPI_H_EXCEPTIONAL) +#define __WIL__COMBASEAPI_H_EXCEPTIONAL + WI_NODISCARD inline unique_coreverttoself_call CoImpersonateClient() + { + THROW_IF_FAILED(::CoImpersonateClient()); + return unique_coreverttoself_call(); + } +#endif +#if defined(__WIL__COMBASEAPI_H_) && !defined(__WIL__COMBASEAPI_H__STL) && defined(WIL_RESOURCE_STL) && (NTDDI_VERSION >= NTDDI_WIN8) +#define __WIL__COMBASEAPI_H__STL + typedef shared_any shared_mta_usage_cookie; + typedef weak_any weak_mta_usage_cookie; +#endif // __WIL__COMBASEAPI_H__STL + +#if defined(_COMBASEAPI_H_) && !defined(__WIL__COMBASEAPI_H_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) && !defined(WIL_KERNEL_MODE) +#define __WIL__COMBASEAPI_H_APP + //! A type that calls CoUninitialize on destruction (or reset()). + using unique_couninitialize_call = unique_call; + + //! Calls CoInitializeEx and fail-fasts if it fails; returns an RAII object that reverts + WI_NODISCARD inline unique_couninitialize_call CoInitializeEx_failfast(DWORD coinitFlags = 0 /*COINIT_MULTITHREADED*/) + { + FAIL_FAST_IF_FAILED(::CoInitializeEx(nullptr, coinitFlags)); + return unique_couninitialize_call(); + } +#endif // __WIL__COMBASEAPI_H_APP +#if defined(__WIL__COMBASEAPI_H_APP) && defined(WIL_ENABLE_EXCEPTIONS) && !defined(__WIL__COMBASEAPI_H_APPEXCEPTIONAL) +#define __WIL__COMBASEAPI_H_APPEXCEPTIONAL + WI_NODISCARD inline unique_couninitialize_call CoInitializeEx(DWORD coinitFlags = 0 /*COINIT_MULTITHREADED*/) + { + THROW_IF_FAILED(::CoInitializeEx(nullptr, coinitFlags)); + return unique_couninitialize_call(); + } +#endif + +#if defined(__ROAPI_H_) && !defined(__WIL__ROAPI_H_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) && (NTDDI_VERSION >= NTDDI_WIN8) +#define __WIL__ROAPI_H_APP + + typedef unique_any unique_ro_registration_cookie; + + //! A type that calls RoUninitialize on destruction (or reset()). + //! Use as a replacement for Windows::Foundation::Uninitialize. + using unique_rouninitialize_call = unique_call; + + //! Calls RoInitialize and fail-fasts if it fails; returns an RAII object that reverts + //! Use as a replacement for Windows::Foundation::Initialize + WI_NODISCARD inline unique_rouninitialize_call RoInitialize_failfast(RO_INIT_TYPE initType = RO_INIT_MULTITHREADED) + { + FAIL_FAST_IF_FAILED(::RoInitialize(initType)); + return unique_rouninitialize_call(); + } +#endif // __WIL__ROAPI_H_APP +#if defined(__WIL__ROAPI_H_APP) && defined(WIL_ENABLE_EXCEPTIONS) && !defined(__WIL__ROAPI_H_APPEXCEPTIONAL) +#define __WIL__ROAPI_H_APPEXCEPTIONAL + //! Calls RoInitialize and throws an exception if it fails; returns an RAII object that reverts + //! Use as a replacement for Windows::Foundation::Initialize + WI_NODISCARD inline unique_rouninitialize_call RoInitialize(RO_INIT_TYPE initType = RO_INIT_MULTITHREADED) + { + THROW_IF_FAILED(::RoInitialize(initType)); + return unique_rouninitialize_call(); + } +#endif + +#if defined(__WINSTRING_H_) && !defined(__WIL__WINSTRING_H_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) +#define __WIL__WINSTRING_H_ + typedef unique_any unique_hstring; + + template<> inline unique_hstring make_unique_string_nothrow( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length) WI_NOEXCEPT + { + WI_ASSERT(source != nullptr); // the HSTRING version of this function does not suport this case + if (length == static_cast(-1)) + { + length = wcslen(source); + } + + unique_hstring result; + ::WindowsCreateString(source, static_cast(length), &result); + return result; + } + + typedef unique_any unique_hstring_buffer; + + /** Promotes an hstring_buffer to an HSTRING. + When an HSTRING_BUFFER object is promoted to a real string it must not be passed to WindowsDeleteString. The caller owns the + HSTRING afterwards. + ~~~ + HRESULT Type::MakePath(_Out_ HSTRING* path) + { + wchar_t* bufferStorage = nullptr; + wil::unique_hstring_buffer theBuffer; + RETURN_IF_FAILED(::WindowsPreallocateStringBuffer(65, &bufferStorage, &theBuffer)); + RETURN_IF_FAILED(::PathCchCombine(bufferStorage, 65, m_foo, m_bar)); + RETURN_IF_FAILED(wil::make_hstring_from_buffer_nothrow(wistd::move(theBuffer), path))); + return S_OK; + } + ~~~ + */ + inline HRESULT make_hstring_from_buffer_nothrow(unique_hstring_buffer&& source, _Out_ HSTRING* promoted) + { + HRESULT hr = ::WindowsPromoteStringBuffer(source.get(), promoted); + if (SUCCEEDED(hr)) + { + source.release(); + } + return hr; + } + + //! A fail-fast variant of `make_hstring_from_buffer_nothrow` + inline unique_hstring make_hstring_from_buffer_failfast(unique_hstring_buffer&& source) + { + unique_hstring result; + FAIL_FAST_IF_FAILED(make_hstring_from_buffer_nothrow(wistd::move(source), &result)); + return result; + } + +#if defined WIL_ENABLE_EXCEPTIONS + /** Promotes an hstring_buffer to an HSTRING. + When an HSTRING_BUFFER object is promoted to a real string it must not be passed to WindowsDeleteString. The caller owns the + HSTRING afterwards. + ~~~ + wil::unique_hstring Type::Make() + { + wchar_t* bufferStorage = nullptr; + wil::unique_hstring_buffer theBuffer; + THROW_IF_FAILED(::WindowsPreallocateStringBuffer(65, &bufferStorage, &theBuffer)); + THROW_IF_FAILED(::PathCchCombine(bufferStorage, 65, m_foo, m_bar)); + return wil::make_hstring_from_buffer(wistd::move(theBuffer)); + } + ~~~ + */ + inline unique_hstring make_hstring_from_buffer(unique_hstring_buffer&& source) + { + unique_hstring result; + THROW_IF_FAILED(make_hstring_from_buffer_nothrow(wistd::move(source), &result)); + return result; + } +#endif + + /// @cond + namespace details + { + template<> struct string_maker + { + string_maker() = default; + string_maker(const string_maker&) = delete; + void operator=(const string_maker&) = delete; + string_maker& operator=(string_maker&& source) WI_NOEXCEPT + { + m_value = wistd::move(source.m_value); + m_bufferHandle = wistd::move(source.m_bufferHandle); + m_charBuffer = wistd::exchange(source.m_charBuffer, nullptr); + return *this; + } + + HRESULT make( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + const wchar_t* source, + size_t length) + { + if (source) + { + RETURN_IF_FAILED(WindowsCreateString(source, static_cast(length), &m_value)); + m_charBuffer = nullptr; + m_bufferHandle.reset(); // do this after WindowsCreateString so we can trim_at_existing_null() from our own buffer + } + else + { + // Need to set it to the empty string to support the empty string case. + m_value.reset(); + RETURN_IF_FAILED(WindowsPreallocateStringBuffer(static_cast(length), &m_charBuffer, &m_bufferHandle)); + } + return S_OK; + } + + wchar_t* buffer() { WI_ASSERT(m_charBuffer != nullptr); return m_charBuffer; } + const wchar_t* buffer() const { return m_charBuffer; } + + HRESULT trim_at_existing_null(size_t length) { return make(buffer(), length); } + + unique_hstring release() + { + m_charBuffer = nullptr; + if (m_bufferHandle) + { + return make_hstring_from_buffer_failfast(wistd::move(m_bufferHandle)); + } + return wistd::move(m_value); + } + + static PCWSTR get(const wil::unique_hstring& value) { return WindowsGetStringRawBuffer(value.get(), nullptr); } + + private: + unique_hstring m_value; + unique_hstring_buffer m_bufferHandle; + wchar_t* m_charBuffer = nullptr; + }; + } + /// @endcond + + // str_raw_ptr is an overloaded function that retrieves a const pointer to the first character in a string's buffer. + // This is the overload for HSTRING. Other overloads available above. + inline PCWSTR str_raw_ptr(HSTRING str) + { + return WindowsGetStringRawBuffer(str, nullptr); + } + + inline PCWSTR str_raw_ptr(const unique_hstring& str) + { + return str_raw_ptr(str.get()); + } + +#endif // __WIL__WINSTRING_H_ +#if defined(__WIL__WINSTRING_H_) && !defined(__WIL__WINSTRING_H_STL) && defined(WIL_RESOURCE_STL) +#define __WIL__WINSTRING_H_STL + typedef shared_any shared_hstring; + typedef shared_any shared_hstring_buffer; + typedef weak_any weak_hstring; + typedef weak_any weak_hstring_buffer; +#endif // __WIL__WINSTRING_H_STL + + +#if defined(_WINREG_) && !defined(__WIL_WINREG_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && !defined(WIL_KERNEL_MODE) +#define __WIL_WINREG_ + typedef unique_any unique_hkey; +#endif // __WIL_WINREG_ +#if defined(__WIL_WINREG_) && !defined(__WIL_WINREG_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_WINREG_STL + typedef shared_any shared_hkey; + typedef weak_any weak_hkey; +#endif // __WIL_WINREG_STL + +#if defined(__propidl_h__) && !defined(_WIL__propidl_h__) && !defined(WIL_KERNEL_MODE) +#define _WIL__propidl_h__ + using unique_prop_variant = wil::unique_struct; +#endif // _WIL__propidl_h__ + +#if defined(_OLEAUTO_H_) && !defined(__WIL_OLEAUTO_H_) && !defined(WIL_KERNEL_MODE) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) +#define __WIL_OLEAUTO_H_ + using unique_variant = wil::unique_struct; + typedef unique_any unique_bstr; + + inline wil::unique_bstr make_bstr_nothrow(PCWSTR source) WI_NOEXCEPT + { + return wil::unique_bstr(::SysAllocString(source)); + } + + inline wil::unique_bstr make_bstr_failfast(PCWSTR source) WI_NOEXCEPT + { + return wil::unique_bstr(FAIL_FAST_IF_NULL_ALLOC(::SysAllocString(source))); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + inline wil::unique_bstr make_bstr(PCWSTR source) + { + wil::unique_bstr result(make_bstr_nothrow(source)); + THROW_IF_NULL_ALLOC(result); + return result; + } +#endif // WIL_ENABLE_EXCEPTIONS + +#endif // __WIL_OLEAUTO_H_ +#if defined(__WIL_OLEAUTO_H_) && !defined(__WIL_OLEAUTO_H_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_OLEAUTO_H_STL + typedef shared_any shared_bstr; + typedef weak_any weak_bstr; +#endif // __WIL_OLEAUTO_H_STL + + +#if (defined(_WININET_) || defined(_DUBINET_)) && !defined(__WIL_WININET_) +#define __WIL_WININET_ + typedef unique_any unique_hinternet; +#endif // __WIL_WININET_ +#if defined(__WIL_WININET_) && !defined(__WIL_WININET_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_WININET_STL + typedef shared_any shared_hinternet; + typedef weak_any weak_hinternet; +#endif // __WIL_WININET_STL + + +#if defined(_WINHTTPX_) && !defined(__WIL_WINHTTP_) +#define __WIL_WINHTTP_ + typedef unique_any unique_winhttp_hinternet; +#endif // __WIL_WINHTTP_ +#if defined(__WIL_WINHTTP_) && !defined(__WIL_WINHTTP_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_WINHTTP_STL + typedef shared_any shared_winhttp_hinternet; + typedef weak_any weak_winhttp_hinternet; +#endif // __WIL_WINHTTP_STL + + +#if defined(_WINSOCKAPI_) && !defined(__WIL_WINSOCKAPI_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_WINSOCKAPI_ + typedef unique_any unique_socket; +#endif // __WIL_WINSOCKAPI_ +#if defined(__WIL_WINSOCKAPI_) && !defined(__WIL_WINSOCKAPI_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_WINSOCKAPI_STL + typedef shared_any shared_socket; + typedef weak_any weak_socket; +#endif // __WIL_WINSOCKAPI_STL + + +#if defined(_WINGDI_) && !defined(__WIL_WINGDI_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && !defined(NOGDI) && !defined(WIL_KERNEL_MODE) +#define __WIL_WINGDI_ + struct window_dc + { + HDC dc; + HWND hwnd; + window_dc(HDC dc_, HWND hwnd_ = nullptr) WI_NOEXCEPT { dc = dc_; hwnd = hwnd_; } + operator HDC() const WI_NOEXCEPT { return dc; } + static void close(window_dc wdc) WI_NOEXCEPT { ::ReleaseDC(wdc.hwnd, wdc.dc); } + }; + typedef unique_any unique_hdc_window; + + struct paint_dc + { + HWND hwnd; + PAINTSTRUCT ps; + paint_dc(HDC hdc = nullptr) { ::ZeroMemory(this, sizeof(*this)); ps.hdc = hdc; } + operator HDC() const WI_NOEXCEPT { return ps.hdc; } + static void close(paint_dc pdc) WI_NOEXCEPT { ::EndPaint(pdc.hwnd, &pdc.ps); } + }; + typedef unique_any unique_hdc_paint; + + struct select_result + { + HGDIOBJ hgdi; + HDC hdc; + select_result(HGDIOBJ hgdi_, HDC hdc_ = nullptr) WI_NOEXCEPT { hgdi = hgdi_; hdc = hdc_; } + operator HGDIOBJ() const WI_NOEXCEPT { return hgdi; } + static void close(select_result sr) WI_NOEXCEPT { ::SelectObject(sr.hdc, sr.hgdi); } + }; + typedef unique_any unique_select_object; + + inline unique_hdc_window GetDC(HWND hwnd) WI_NOEXCEPT + { + return unique_hdc_window(window_dc(::GetDC(hwnd), hwnd)); + } + + inline unique_hdc_window GetWindowDC(HWND hwnd) WI_NOEXCEPT + { + return unique_hdc_window(window_dc(::GetWindowDC(hwnd), hwnd)); + } + + inline unique_hdc_paint BeginPaint(HWND hwnd, _Out_opt_ PPAINTSTRUCT pPaintStruct = nullptr) WI_NOEXCEPT + { + paint_dc pdc; + pdc.hwnd = hwnd; + HDC hdc = ::BeginPaint(hwnd, &pdc.ps); + assign_to_opt_param(pPaintStruct, pdc.ps); + return (hdc == nullptr) ? unique_hdc_paint() : unique_hdc_paint(pdc); + } + + inline unique_select_object SelectObject(HDC hdc, HGDIOBJ gdiobj) WI_NOEXCEPT + { + return unique_select_object(select_result(::SelectObject(hdc, gdiobj), hdc)); + } + + typedef unique_any unique_hgdiobj; + typedef unique_any unique_hpen; + typedef unique_any unique_hbrush; + typedef unique_any unique_hfont; + typedef unique_any unique_hbitmap; + typedef unique_any unique_hrgn; + typedef unique_any unique_hpalette; + typedef unique_any unique_hdc; + typedef unique_any unique_hicon; +#if !defined(NOMENUS) + typedef unique_any unique_hmenu; +#endif // !defined(NOMENUS) +#endif // __WIL_WINGDI_ +#if defined(__WIL_WINGDI_) && !defined(__WIL_WINGDI_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_WINGDI_STL + typedef shared_any shared_hgdiobj; + typedef shared_any shared_hpen; + typedef shared_any shared_hbrush; + typedef shared_any shared_hfont; + typedef shared_any shared_hbitmap; + typedef shared_any shared_hrgn; + typedef shared_any shared_hpalette; + typedef shared_any shared_hdc; + typedef shared_any shared_hicon; +#if !defined(NOMENUS) + typedef shared_any shared_hmenu; +#endif // !defined(NOMENUS) + + typedef weak_any weak_hgdiobj; + typedef weak_any weak_hpen; + typedef weak_any weak_hbrush; + typedef weak_any weak_hfont; + typedef weak_any weak_hbitmap; + typedef weak_any weak_hrgn; + typedef weak_any weak_hpalette; + typedef weak_any weak_hdc; + typedef weak_any weak_hicon; +#if !defined(NOMENUS) + typedef weak_any weak_hmenu; +#endif // !defined(NOMENUS) +#endif // __WIL_WINGDI_STL + +#if defined(_INC_WTSAPI) && !defined(__WIL_WTSAPI) +#define __WIL_WTSAPI + template + using unique_wtsmem_ptr = wistd::unique_ptr>; +#endif // __WIL_WTSAPI + +#if defined(_WINSCARD_H_) && !defined(__WIL_WINSCARD_H_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_WINSCARD_H_ + typedef unique_any unique_scardctx; +#endif // __WIL_WINSCARD_H_ +#if defined(__WIL_WINSCARD_H_) && !defined(__WIL_WINSCARD_H_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_WINSCARD_H_STL + typedef shared_any shared_scardctx; + typedef weak_any weak_scardctx; +#endif // __WIL_WINSCARD_H_STL + + +#if defined(__WINCRYPT_H__) && !defined(__WIL__WINCRYPT_H__) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL__WINCRYPT_H__ + /// @cond + namespace details + { + inline void __stdcall CertCloseStoreNoParam(_Pre_opt_valid_ _Frees_ptr_opt_ HCERTSTORE hCertStore) WI_NOEXCEPT + { + ::CertCloseStore(hCertStore, 0); + } + + inline void __stdcall CryptReleaseContextNoParam(_Pre_opt_valid_ _Frees_ptr_opt_ HCRYPTPROV hCryptCtx) WI_NOEXCEPT + { + ::CryptReleaseContext(hCryptCtx, 0); + } + } + /// @endcond + + struct cert_context_t : details::unique_storage> + { + // forward all base class constructors... + template + explicit cert_context_t(args_t&&... args) WI_NOEXCEPT : unique_storage(wistd::forward(args)...) {} + + /** A wrapper around CertEnumCertificatesInStore. + CertEnumCertificatesInStore takes ownership of its second paramter in an unclear fashion, + making it error-prone to use in combination with unique_cert_context. This wrapper helps + manage the resource correctly while ensuring the GetLastError state set by CertEnumCertificatesInStore. + is not lost. See MSDN for more information on `CertEnumCertificatesInStore`. + ~~~~ + void MyMethod(HCERTSTORE certStore) + { + wil::unique_cert_context enumCert; + while (enumCert.CertEnumCertificatesInStore(certStore)) + { + UseTheCertToDoTheThing(enumCert); + } + } + ~~~~ + @param certStore A handle of a certificate store. + @param 'true' if a certificate was enumerated by this call, false otherwise. + */ + bool CertEnumCertificatesInStore(HCERTSTORE certStore) WI_NOEXCEPT + { + reset(::CertEnumCertificatesInStore(certStore, release())); + return is_valid(); + } + }; + + // Warning - ::CertEnumCertificatesInStore takes ownership of its parameter. Prefer the + // .CertEnumCertificatesInStore method of the unique_cert_context or else use .release + // when calling ::CertEnumCertificatesInStore directly. + typedef unique_any_t unique_cert_context; + typedef unique_any unique_cert_chain_context; + typedef unique_any unique_hcertstore; + typedef unique_any unique_hcryptprov; + typedef unique_any unique_hcryptkey; + typedef unique_any unique_hcrypthash; +#endif // __WIL__WINCRYPT_H__ +#if defined(__WIL__WINCRYPT_H__) && !defined(__WIL__WINCRYPT_H__STL) && defined(WIL_RESOURCE_STL) +#define __WIL__WINCRYPT_H__STL + typedef shared_any shared_cert_context; + typedef shared_any shared_cert_chain_context; + typedef shared_any shared_hcertstore; + typedef shared_any shared_hcryptprov; + typedef shared_any shared_hcryptkey; + typedef shared_any shared_hcrypthash; + + typedef weak_any weak_cert_context; + typedef weak_any weak_cert_chain_context; + typedef weak_any weak_hcertstore; + typedef weak_any weak_hcryptprov; + typedef weak_any weak_hcryptkey; + typedef weak_any weak_hcrypthash; +#endif // __WIL__WINCRYPT_H__STL + + +#if defined(__NCRYPT_H__) && !defined(__WIL_NCRYPT_H__) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_NCRYPT_H__ + using ncrypt_deleter = function_deleter; + + template + using unique_ncrypt_ptr = wistd::unique_ptr; + + typedef unique_any unique_ncrypt_prov; + typedef unique_any unique_ncrypt_key; + typedef unique_any unique_ncrypt_secret; +#endif // __WIL_NCRYPT_H__ +#if defined(__WIL_NCRYPT_H__) && !defined(__WIL_NCRYPT_H_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_NCRYPT_H_STL + typedef shared_any shared_ncrypt_prov; + typedef shared_any shared_ncrypt_key; + typedef shared_any shared_ncrypt_secret; + + typedef weak_any weak_ncrypt_prov; + typedef weak_any weak_ncrypt_key; + typedef weak_any weak_ncrypt_secret; +#endif // __WIL_NCRYPT_H_STL + +#if defined(__BCRYPT_H__) && !defined(__WIL_BCRYPT_H__) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_BCRYPT_H__ + /// @cond + namespace details + { + inline void __stdcall BCryptCloseAlgorithmProviderNoFlags(_Pre_opt_valid_ _Frees_ptr_opt_ BCRYPT_ALG_HANDLE hAlgorithm) WI_NOEXCEPT + { + if (hAlgorithm) + { + ::BCryptCloseAlgorithmProvider(hAlgorithm, 0); + } + } + } + /// @endcond + + using bcrypt_deleter = function_deleter; + + template + using unique_bcrypt_ptr = wistd::unique_ptr; + + typedef unique_any unique_bcrypt_algorithm; + typedef unique_any unique_bcrypt_hash; + typedef unique_any unique_bcrypt_key; + typedef unique_any unique_bcrypt_secret; +#endif // __WIL_BCRYPT_H__ +#if defined(__WIL_BCRYPT_H__) && !defined(__WIL_BCRYPT_H_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_BCRYPT_H_STL + typedef shared_any shared_bcrypt_algorithm; + typedef shared_any shared_bcrypt_hash; + typedef shared_any shared_bcrypt_key; + typedef shared_any shared_bcrypt_secret; + + typedef weak_any weak_bcrypt_algorithm; + typedef weak_any weak_bcrypt_hash; + typedef weak_any weak_bcrypt_key; + typedef weak_any weak_bcrypt_secret; +#endif // __WIL_BCRYPT_H_STL + + +#if defined(__RPCNDR_H__) && !defined(__WIL__RPCNDR_H__) && !defined(WIL_KERNEL_MODE) +#define __WIL__RPCNDR_H__ + + //! Function deleter for use with pointers allocated by MIDL_user_allocate + using midl_deleter = function_deleter; + + //! Unique-ptr holding a type allocated by MIDL_user_alloc or returned from an RPC invocation + template using unique_midl_ptr = wistd::unique_ptr; + + //! Unique-ptr for strings allocated by MIDL_user_alloc + using unique_midl_string = unique_midl_ptr; +#ifndef WIL_NO_ANSI_STRINGS + using unique_midl_ansistring = unique_midl_ptr; +#endif + + namespace details + { + struct midl_allocator + { + static _Ret_opt_bytecap_(size) void* allocate(size_t size) WI_NOEXCEPT + { + return ::MIDL_user_allocate(size); + } + }; + + // Specialization to support construction of unique_midl_string instances + template<> struct string_allocator : midl_allocator {}; + +#ifndef WIL_NO_ANSI_STRINGS + template<> struct string_allocator : midl_allocator {}; +#endif + } +#endif // __WIL__RPCNDR_H__ + +#if defined(_OBJBASE_H_) && !defined(__WIL_OBJBASE_H_) && !defined(WIL_KERNEL_MODE) +#define __WIL_OBJBASE_H_ + using cotaskmem_deleter = function_deleter; + + template + using unique_cotaskmem_ptr = wistd::unique_ptr; + + template + using unique_cotaskmem_array_ptr = unique_array_ptr; + + /** Provides `std::make_unique()` semantics for resources allocated with `CoTaskMemAlloc()` in a context that may not throw upon allocation failure. + Use `wil::make_unique_cotaskmem_nothrow()` for resources returned from APIs that must satisfy a memory allocation contract that requires the use of `CoTaskMemAlloc()` / `CoTaskMemFree()`. + Use `wil::make_unique_nothrow()` when `CoTaskMemAlloc()` is not required. + + Allocations are initialized with placement new and will call constructors (if present), but this does not guarantee initialization. + + Note that `wil::make_unique_cotaskmem_nothrow()` is not marked WI_NOEXCEPT as it may be used to create an exception-based class that may throw in its constructor. + ~~~ + auto foo = wil::make_unique_cotaskmem_nothrow(); + if (foo) + { + // initialize allocated Foo object as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_cotaskmem_ptr>::type make_unique_cotaskmem_nothrow(Args&&... args) + { + static_assert(wistd::is_trivially_destructible::value, "T has a destructor that won't be run when used with this function; use make_unique instead"); + unique_cotaskmem_ptr sp(static_cast(::CoTaskMemAlloc(sizeof(T)))); + if (sp) + { + // use placement new to initialize memory from the previous allocation + new (sp.get()) T(wistd::forward(args)...); + } + return sp; + } + + /** Provides `std::make_unique()` semantics for array resources allocated with `CoTaskMemAlloc()` in a context that may not throw upon allocation failure. + See the overload of `wil::make_unique_cotaskmem_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_cotaskmem_nothrow(size); + if (foos) + { + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_cotaskmem_ptr>::type make_unique_cotaskmem_nothrow(size_t size) + { + typedef typename wistd::remove_extent::type E; + static_assert(wistd::is_trivially_destructible::value, "E has a destructor that won't be run when used with this function; use make_unique instead"); + FAIL_FAST_IF((__WI_SIZE_MAX / sizeof(E)) < size); + size_t allocSize = sizeof(E) * size; + unique_cotaskmem_ptr sp(static_cast(::CoTaskMemAlloc(allocSize))); + if (sp) + { + // use placement new to initialize memory from the previous allocation; + // note that array placement new cannot be used as the standard allows for operator new[] + // to consume overhead in the allocation for internal bookkeeping + for (auto& elem : make_range(static_cast(sp.get()), size)) + { + new (&elem) E(); + } + } + return sp; + } + + /** Provides `std::make_unique()` semantics for resources allocated with `CoTaskMemAlloc()` in a context that must fail fast upon allocation failure. + See the overload of `wil::make_unique_cotaskmem_nothrow()` for non-array types for more details. + ~~~ + auto foo = wil::make_unique_cotaskmem_failfast(); + // initialize allocated Foo object as appropriate + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_cotaskmem_ptr>::type make_unique_cotaskmem_failfast(Args&&... args) + { + unique_cotaskmem_ptr result(make_unique_cotaskmem_nothrow(wistd::forward(args)...)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } + + /** Provides `std::make_unique()` semantics for array resources allocated with `CoTaskMemAlloc()` in a context that must fail fast upon allocation failure. + See the overload of `wil::make_unique_cotaskmem_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_cotaskmem_failfast(size); + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_cotaskmem_ptr>::type make_unique_cotaskmem_failfast(size_t size) + { + unique_cotaskmem_ptr result(make_unique_cotaskmem_nothrow(size)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Provides `std::make_unique()` semantics for resources allocated with `CoTaskMemAlloc()`. + See the overload of `wil::make_unique_cotaskmem_nothrow()` for non-array types for more details. + ~~~ + auto foo = wil::make_unique_cotaskmem(); + // initialize allocated Foo object as appropriate + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_cotaskmem_ptr>::type make_unique_cotaskmem(Args&&... args) + { + unique_cotaskmem_ptr result(make_unique_cotaskmem_nothrow(wistd::forward(args)...)); + THROW_IF_NULL_ALLOC(result); + return result; + } + + /** Provides `std::make_unique()` semantics for array resources allocated with `CoTaskMemAlloc()`. + See the overload of `wil::make_unique_cotaskmem_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_cotaskmem(size); + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_cotaskmem_ptr>::type make_unique_cotaskmem(size_t size) + { + unique_cotaskmem_ptr result(make_unique_cotaskmem_nothrow(size)); + THROW_IF_NULL_ALLOC(result); + return result; + } +#endif // WIL_ENABLE_EXCEPTIONS + + typedef unique_any unique_cotaskmem; + typedef unique_any unique_cotaskmem_string; +#ifndef WIL_NO_ANSI_STRINGS + typedef unique_any unique_cotaskmem_ansistring; +#endif // WIL_NO_ANSI_STRINGS + + /// @cond + namespace details + { + struct cotaskmem_allocator + { + static _Ret_opt_bytecap_(size) void* allocate(size_t size) WI_NOEXCEPT + { + return ::CoTaskMemAlloc(size); + } + }; + + template<> struct string_allocator : cotaskmem_allocator {}; + +#ifndef WIL_NO_ANSI_STRINGS + template<> struct string_allocator : cotaskmem_allocator {}; +#endif // WIL_NO_ANSI_STRINGS + } + /// @endcond + + inline auto make_cotaskmem_string_nothrow( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + return make_unique_string_nothrow(source, length); + } + + inline auto make_cotaskmem_string_failfast( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length = static_cast(-1)) WI_NOEXCEPT + { + return make_unique_string_failfast(source, length); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + inline auto make_cotaskmem_string( + _When_((source != nullptr) && length != static_cast(-1), _In_reads_(length)) + _When_((source != nullptr) && length == static_cast(-1), _In_z_) + PCWSTR source, size_t length = static_cast(-1)) + { + return make_unique_string(source, length); + } + +#endif // WIL_ENABLE_EXCEPTIONS +#endif // __WIL_OBJBASE_H_ +#if defined(__WIL_OBJBASE_H_) && !defined(__WIL_OBJBASE_H_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_OBJBASE_H_STL + typedef shared_any shared_cotaskmem; + typedef weak_any weak_cotaskmem; + typedef shared_any shared_cotaskmem_string; + typedef weak_any weak_cotaskmem_string; +#endif // __WIL_OBJBASE_H_STL + +#if defined(__WIL_OBJBASE_H_) && defined(__WIL_WINBASE_) && !defined(__WIL_OBJBASE_AND_WINBASE_H_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_OBJBASE_AND_WINBASE_H_ + + struct cotaskmem_secure_deleter + { + template + void operator()(_Pre_opt_valid_ _Frees_ptr_opt_ T* p) const + { + if (p) + { + IMalloc* malloc; + if (SUCCEEDED(::CoGetMalloc(1, &malloc))) + { + size_t const size = malloc->GetSize(p); + if (size != static_cast(-1)) + { + ::SecureZeroMemory(p, size); + } + malloc->Release(); + } + ::CoTaskMemFree(p); + } + } + }; + + template + using unique_cotaskmem_secure_ptr = wistd::unique_ptr; + + /** Provides `std::make_unique()` semantics for secure resources allocated with `CoTaskMemAlloc()` in a context that may not throw upon allocation failure. + See the overload of `wil::make_unique_cotaskmem_nothrow()` for non-array types for more details. + ~~~ + auto foo = wil::make_unique_cotaskmem_secure_nothrow(); + if (foo) + { + // initialize allocated Foo object as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_cotaskmem_secure_ptr>::type make_unique_cotaskmem_secure_nothrow(Args&&... args) + { + return unique_cotaskmem_secure_ptr(make_unique_cotaskmem_nothrow(wistd::forward(args)...).release()); + } + + /** Provides `std::make_unique()` semantics for secure array resources allocated with `CoTaskMemAlloc()` in a context that may not throw upon allocation failure. + See the overload of `wil::make_unique_cotaskmem_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_cotaskmem_secure_nothrow(size); + if (foos) + { + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_cotaskmem_secure_ptr>::type make_unique_cotaskmem_secure_nothrow(size_t size) + { + return unique_cotaskmem_secure_ptr(make_unique_cotaskmem_nothrow(size).release()); + } + + /** Provides `std::make_unique()` semantics for secure resources allocated with `CoTaskMemAlloc()` in a context that must fail fast upon allocation failure. + See the overload of `wil::make_unique_cotaskmem_nothrow()` for non-array types for more details. + ~~~ + auto foo = wil::make_unique_cotaskmem_secure_failfast(); + // initialize allocated Foo object as appropriate + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_cotaskmem_secure_ptr>::type make_unique_cotaskmem_secure_failfast(Args&&... args) + { + unique_cotaskmem_secure_ptr result(make_unique_cotaskmem_secure_nothrow(wistd::forward(args)...)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } + + /** Provides `std::make_unique()` semantics for secure array resources allocated with `CoTaskMemAlloc()` in a context that must fail fast upon allocation failure. + See the overload of `wil::make_unique_cotaskmem_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_cotaskmem_secure_failfast(size); + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_cotaskmem_secure_ptr>::type make_unique_cotaskmem_secure_failfast(size_t size) + { + unique_cotaskmem_secure_ptr result(make_unique_cotaskmem_secure_nothrow(size)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Provides `std::make_unique()` semantics for secure resources allocated with `CoTaskMemAlloc()`. + See the overload of `wil::make_unique_cotaskmem_nothrow()` for non-array types for more details. + ~~~ + auto foo = wil::make_unique_cotaskmem_secure(); + // initialize allocated Foo object as appropriate + ~~~ + */ + template + inline typename wistd::enable_if::value, unique_cotaskmem_secure_ptr>::type make_unique_cotaskmem_secure(Args&&... args) + { + unique_cotaskmem_secure_ptr result(make_unique_cotaskmem_secure_nothrow(wistd::forward(args)...)); + THROW_IF_NULL_ALLOC(result); + return result; + } + + /** Provides `std::make_unique()` semantics for secure array resources allocated with `CoTaskMemAlloc()`. + See the overload of `wil::make_unique_cotaskmem_nothrow()` for non-array types for more details. + ~~~ + const size_t size = 42; + auto foos = wil::make_unique_cotaskmem_secure(size); + for (auto& elem : wil::make_range(foos.get(), size)) + { + // initialize allocated Foo objects as appropriate + } + ~~~ + */ + template + inline typename wistd::enable_if::value && wistd::extent::value == 0, unique_cotaskmem_secure_ptr>::type make_unique_cotaskmem_secure(size_t size) + { + unique_cotaskmem_secure_ptr result(make_unique_cotaskmem_secure_nothrow(size)); + THROW_IF_NULL_ALLOC(result); + return result; + } +#endif // WIL_ENABLE_EXCEPTIONS + + typedef unique_cotaskmem_secure_ptr unique_cotaskmem_string_secure; + + /** Copies a given string into secure memory allocated with `CoTaskMemAlloc()` in a context that may not throw upon allocation failure. + See the overload of `wil::make_cotaskmem_string_nothrow()` with supplied length for more details. + ~~~ + auto str = wil::make_cotaskmem_string_secure_nothrow(L"a string"); + if (str) + { + std::wcout << L"This is " << str.get() << std::endl; // prints "This is a string" + } + ~~~ + */ + inline unique_cotaskmem_string_secure make_cotaskmem_string_secure_nothrow(_In_ PCWSTR source) WI_NOEXCEPT + { + return unique_cotaskmem_string_secure(make_cotaskmem_string_nothrow(source).release()); + } + + /** Copies a given string into secure memory allocated with `CoTaskMemAlloc()` in a context that must fail fast upon allocation failure. + See the overload of `wil::make_cotaskmem_string_nothrow()` with supplied length for more details. + ~~~ + auto str = wil::make_cotaskmem_string_secure_failfast(L"a string"); + std::wcout << L"This is " << str.get() << std::endl; // prints "This is a string" + ~~~ + */ + inline unique_cotaskmem_string_secure make_cotaskmem_string_secure_failfast(_In_ PCWSTR source) WI_NOEXCEPT + { + unique_cotaskmem_string_secure result(make_cotaskmem_string_secure_nothrow(source)); + FAIL_FAST_IF_NULL_ALLOC(result); + return result; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Copies a given string into secure memory allocated with `CoTaskMemAlloc()`. + See the overload of `wil::make_cotaskmem_string_nothrow()` with supplied length for more details. + ~~~ + auto str = wil::make_cotaskmem_string_secure(L"a string"); + std::wcout << L"This is " << str.get() << std::endl; // prints "This is a string" + ~~~ + */ + inline unique_cotaskmem_string_secure make_cotaskmem_string_secure(_In_ PCWSTR source) + { + unique_cotaskmem_string_secure result(make_cotaskmem_string_secure_nothrow(source)); + THROW_IF_NULL_ALLOC(result); + return result; + } +#endif +#endif // __WIL_OBJBASE_AND_WINBASE_H_ + +#if defined(_OLE2_H_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && !defined(__WIL_OLE2_H_) && !defined(WIL_KERNEL_MODE) +#define __WIL_OLE2_H_ + typedef unique_struct unique_stg_medium; + struct unique_hglobal_locked : public unique_any + { + unique_hglobal_locked() = delete; + + explicit unique_hglobal_locked(HGLOBAL global) : unique_any(global) + { + // GlobalLock returns a pointer to the associated global memory block and that's what callers care about. + m_globalMemory = GlobalLock(global); + if (!m_globalMemory) + { + release(); + } + } + + explicit unique_hglobal_locked(STGMEDIUM& medium) : unique_hglobal_locked(medium.hGlobal) + { + } + + pointer get() const + { + return m_globalMemory; + } + + private: + pointer m_globalMemory; + }; + + //! A type that calls OleUninitialize on destruction (or reset()). + //! Use as a replacement for Windows::Foundation::Uninitialize. + using unique_oleuninitialize_call = unique_call; + + //! Calls RoInitialize and fail-fasts if it fails; returns an RAII object that reverts + //! Use as a replacement for Windows::Foundation::Initialize + _Check_return_ inline unique_oleuninitialize_call OleInitialize_failfast() + { + FAIL_FAST_IF_FAILED(::OleInitialize(nullptr)); + return unique_oleuninitialize_call(); + } +#endif // __WIL_OLE2_H_ + +#if defined(__WIL_OLE2_H_) && defined(WIL_ENABLE_EXCEPTIONS) && !defined(__WIL_OLE2_H_EXCEPTIONAL) +#define __WIL_OLE2_H_EXCEPTIONAL + //! Calls RoInitialize and throws an exception if it fails; returns an RAII object that reverts + //! Use as a replacement for Windows::Foundation::Initialize + _Check_return_ inline unique_oleuninitialize_call OleInitialize() + { + THROW_IF_FAILED(::OleInitialize(nullptr)); + return unique_oleuninitialize_call(); + } +#endif + +#if defined(_INC_COMMCTRL) && !defined(__WIL_INC_COMMCTRL) && !defined(WIL_KERNEL_MODE) +#define __WIL_INC_COMMCTRL + typedef unique_any unique_himagelist; +#endif // __WIL_INC_COMMCTRL +#if defined(__WIL_INC_COMMCTRL) && !defined(__WIL_INC_COMMCTRL_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_INC_COMMCTRL_STL + typedef shared_any shared_himagelist; + typedef weak_any weak_himagelist; +#endif // __WIL_INC_COMMCTRL_STL + +#if defined(_UXTHEME_H_) && !defined(__WIL_INC_UXTHEME) && !defined(WIL_KERNEL_MODE) +#define __WIL_INC_UXTHEME + typedef unique_any unique_htheme; +#endif // __WIL_INC_UXTHEME + +#if defined(_WINSVC_) && !defined(__WIL_HANDLE_H_WINSVC) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && !defined(WIL_KERNEL_MODE) +#define __WIL_HANDLE_H_WINSVC + typedef unique_any unique_schandle; +#endif // __WIL_HANDLE_H_WINSVC +#if defined(__WIL_HANDLE_H_WINSVC) && !defined(__WIL_HANDLE_H_WINSVC_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_HANDLE_H_WINSVC_STL + typedef shared_any shared_schandle; + typedef weak_any weak_schandle; +#endif // __WIL_HANDLE_H_WINSVC_STL + +#if defined(_INC_STDIO) && !defined(__WIL_INC_STDIO) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && !defined(WIL_KERNEL_MODE) +#define __WIL_INC_STDIO + typedef unique_any unique_pipe; + typedef unique_any unique_file; +#endif // __WIL_INC_STDIO +#if defined(__WIL_INC_STDIO) && !defined(__WIL__INC_STDIO_STL) && defined(WIL_RESOURCE_STL) +#define __WIL__INC_STDIO_STL + typedef shared_any shared_pipe; + typedef weak_any weak_pipe; + typedef shared_any shared_file; + typedef weak_any weak_file; +#endif // __WIL__INC_STDIO_STL + +#if defined(_NTLSA_) && !defined(__WIL_NTLSA_) && !defined(WIL_KERNEL_MODE) +#define __WIL_NTLSA_ + typedef unique_any unique_hlsa; + + using lsa_freemem_deleter = function_deleter; + + template + using unique_lsamem_ptr = wistd::unique_ptr; +#endif // _NTLSA_ +#if defined(_NTLSA_) && !defined(__WIL_NTLSA_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_NTLSA_STL + typedef shared_any shared_hlsa; + typedef weak_any weak_hlsa; +#endif // _NTLSA_ + +#if defined(_LSALOOKUP_) && !defined(__WIL_LSALOOKUP_) +#define __WIL_LSALOOKUP_ + typedef unique_any unique_hlsalookup; + + using lsalookup_freemem_deleter = function_deleter; + + template + using unique_lsalookupmem_ptr = wistd::unique_ptr; +#endif // _LSALOOKUP_ +#if defined(_LSALOOKUP_) && !defined(__WIL_LSALOOKUP_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_LSALOOKUP_STL + typedef shared_any shared_hlsalookup; + typedef weak_any weak_hlsalookup; +#endif // _LSALOOKUP_ + +#if defined(_NTLSA_IFS_) && !defined(__WIL_HANDLE_H_NTLSA_IFS_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_HANDLE_H_NTLSA_IFS_ + using lsa_deleter = function_deleter; + + template + using unique_lsa_ptr = wistd::unique_ptr; +#endif // __WIL_HANDLE_H_NTLSA_IFS_ + +#if defined(__WERAPI_H__) && !defined(__WIL_WERAPI_H__) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_WERAPI_H__ + typedef unique_any unique_wer_report; +#endif + +#if defined(__MIDLES_H__) && !defined(__WIL_MIDLES_H__) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_MIDLES_H__ + typedef unique_any unique_rpc_pickle; +#endif +#if defined(__WIL_MIDLES_H__) && !defined(__WIL_MIDLES_H_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_MIDLES_H_STL + typedef shared_any shared_rpc_pickle; + typedef weak_any weak_rpc_pickle; +#endif + +#if defined(__RPCDCE_H__) && !defined(__WIL_RPCDCE_H__) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_RPCDCE_H__ + /// @cond + namespace details + { + inline void __stdcall WpRpcBindingFree(_Pre_opt_valid_ _Frees_ptr_opt_ RPC_BINDING_HANDLE binding) + { + ::RpcBindingFree(&binding); + } + + inline void __stdcall WpRpcBindingVectorFree(_Pre_opt_valid_ _Frees_ptr_opt_ RPC_BINDING_VECTOR* bindingVector) + { + ::RpcBindingVectorFree(&bindingVector); + } + + inline void __stdcall WpRpcStringFree(_Pre_opt_valid_ _Frees_ptr_opt_ RPC_WSTR wstr) + { + ::RpcStringFreeW(&wstr); + } + } + /// @endcond + + typedef unique_any unique_rpc_binding; + typedef unique_any unique_rpc_binding_vector; + typedef unique_any unique_rpc_wstr; +#endif +#if defined(__WIL_RPCDCE_H__) && !defined(__WIL_RPCDCE_H_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_RPCDCE_H_STL + typedef shared_any shared_rpc_binding; + typedef weak_any weak_rpc_binding; + typedef shared_any shared_rpc_binding_vector; + typedef weak_any weak_rpc_binding_vector; + typedef shared_any shared_rpc_wstr; + typedef weak_any weak_rpc_wstr; +#endif + +#if defined(_WCMAPI_H) && !defined(__WIL_WCMAPI_H_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_WCMAPI_H_ + using wcm_deleter = function_deleter; + + template + using unique_wcm_ptr = wistd::unique_ptr; +#endif + +#if defined(_NETIOAPI_H_) && defined(_WS2IPDEF_) && defined(MIB_INVALID_TEREDO_PORT_NUMBER) && !defined(__WIL_NETIOAPI_H_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_NETIOAPI_H_ + typedef unique_any unique_mib_iftable; +#endif +#if defined(__WIL_NETIOAPI_H_) && !defined(__WIL_NETIOAPI_H_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_NETIOAPI_H_STL + typedef shared_any shared_mib_iftable; + typedef weak_any weak_mib_iftable; +#endif + +#if defined(_WLAN_WLANAPI_H) && !defined(__WIL_WLAN_WLANAPI_H) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_WLAN_WLANAPI_H + using wlan_deleter = function_deleter; + + template + using unique_wlan_ptr = wistd::unique_ptr < T, wlan_deleter >; + + /// @cond + namespace details + { + inline void __stdcall CloseWlanHandle(_Frees_ptr_ HANDLE hClientHandle) + { + ::WlanCloseHandle(hClientHandle, nullptr); + } + } + /// @endcond + + typedef unique_any unique_wlan_handle; +#endif +#if defined(__WIL_WLAN_WLANAPI_H) && !defined(__WIL_WLAN_WLANAPI_H_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_WLAN_WLANAPI_H_STL + typedef shared_any shared_wlan_handle; + typedef weak_any weak_wlan_handle; +#endif + +#if defined(_HPOWERNOTIFY_DEF_) && !defined(__WIL_HPOWERNOTIFY_DEF_H_) && !defined(WIL_KERNEL_MODE) +#define __WIL_HPOWERNOTIFY_DEF_H_ + typedef unique_any unique_hpowernotify; +#endif + +#if defined(__WIL_WINBASE_DESKTOP) && defined(SID_DEFINED) && !defined(__WIL_PSID_DEF_H_) +#define __WIL_PSID_DEF_H_ + typedef unique_any unique_any_psid; +#if defined(_OBJBASE_H_) + typedef unique_any unique_cotaskmem_psid; +#endif +#endif + +#if defined(_PROCESSTHREADSAPI_H_) && !defined(__WIL_PROCESSTHREADSAPI_H_DESK_SYS) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) && !defined(WIL_KERNEL_MODE) +#define __WIL_PROCESSTHREADSAPI_H_DESK_SYS + /// @cond + namespace details + { + inline void __stdcall CloseProcessInformation(_In_ PROCESS_INFORMATION* p) + { + if (p->hProcess) + { + CloseHandle(p->hProcess); + } + + if (p->hThread) + { + CloseHandle(p->hThread); + } + } + } + /// @endcond + + /** Manages the outbound parameter containing handles returned by `CreateProcess()` and related methods. + ~~~ + unique_process_information process; + CreateProcessW(..., CREATE_SUSPENDED, ..., &process); + THROW_LAST_ERROR_IF(ResumeThread(process.hThread) == -1); + THROW_LAST_ERROR_IF(WaitForSingleObject(process.hProcess, INFINITE) != WAIT_OBJECT_0); + ~~~ + */ + using unique_process_information = unique_struct; +#endif + +#if defined(_PROCESSENV_) && !defined(__WIL__PROCESSENV_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) +#define __WIL__PROCESSENV_ + /** Manages lifecycle of an environment-strings block + ~~~ + wil::unique_environstrings_ptr env { ::GetEnvironmentStringsW() }; + const wchar_t *nextVar = env.get(); + while (nextVar && *nextVar) + { + // consume 'nextVar' + nextVar += wcslen(nextVar) + 1; + } + ~~~ + */ + using unique_environstrings_ptr = wistd::unique_ptr>; + +#ifndef WIL_NO_ANSI_STRINGS + //! ANSI equivalent to unique_environstrings_ptr; + using unique_environansistrings_ptr = wistd::unique_ptr>; +#endif +#endif + +#if defined(_APPMODEL_H_) && !defined(__WIL_APPMODEL_H_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define __WIL_APPMODEL_H_ + typedef unique_any unique_package_info_reference; +#endif // __WIL_APPMODEL_H_ +#if defined(__WIL_APPMODEL_H_) && !defined(__WIL_APPMODEL_H_STL) && defined(WIL_RESOURCE_STL) +#define __WIL_APPMODEL_H_STL + typedef shared_any shared_package_info_reference; + typedef weak_any weak_package_info_reference; +#endif // __WIL_APPMODEL_H_STL + +#if defined(WDFAPI) && !defined(__WIL_WDFAPI) +#define __WIL_WDFAPI + + namespace details + { + template + using wdf_object_resource_policy = resource_policy; + } + + template + using unique_wdf_any = unique_any_t>>; + + using unique_wdf_object = unique_wdf_any; + + using unique_wdf_timer = unique_wdf_any; + using unique_wdf_work_item = unique_wdf_any; + + using unique_wdf_memory = unique_wdf_any; + + using unique_wdf_dma_enabler = unique_wdf_any; + using unique_wdf_dma_transaction = unique_wdf_any; + using unique_wdf_common_buffer = unique_wdf_any; + + using unique_wdf_key = unique_wdf_any; + using unique_wdf_string = unique_wdf_any; + using unique_wdf_collection = unique_wdf_any; + + using wdf_wait_lock_release_scope_exit = + unique_any< + WDFWAITLOCK, + decltype(&::WdfWaitLockRelease), + ::WdfWaitLockRelease, + details::pointer_access_none>; + + inline + WI_NODISCARD + _IRQL_requires_max_(PASSIVE_LEVEL) + _Acquires_lock_(lock) + wdf_wait_lock_release_scope_exit + acquire_wdf_wait_lock(WDFWAITLOCK lock) WI_NOEXCEPT + { + ::WdfWaitLockAcquire(lock, nullptr); + return wdf_wait_lock_release_scope_exit(lock); + } + + inline + WI_NODISCARD + _IRQL_requires_max_(APC_LEVEL) + _When_(return, _Acquires_lock_(lock)) + wdf_wait_lock_release_scope_exit + try_acquire_wdf_wait_lock(WDFWAITLOCK lock) WI_NOEXCEPT + { + LONGLONG timeout = 0; + NTSTATUS status = ::WdfWaitLockAcquire(lock, &timeout); + if (status == STATUS_SUCCESS) + { + return wdf_wait_lock_release_scope_exit(lock); + } + else + { + return wdf_wait_lock_release_scope_exit(); + } + } + + using wdf_spin_lock_release_scope_exit = + unique_any< + WDFSPINLOCK, + decltype(&::WdfSpinLockRelease), + ::WdfSpinLockRelease, + details::pointer_access_none>; + + inline + WI_NODISCARD + _IRQL_requires_max_(DISPATCH_LEVEL) + _IRQL_raises_(DISPATCH_LEVEL) + _Acquires_lock_(lock) + wdf_spin_lock_release_scope_exit + acquire_wdf_spin_lock(WDFSPINLOCK lock) WI_NOEXCEPT + { + ::WdfSpinLockAcquire(lock); + return wdf_spin_lock_release_scope_exit(lock); + } + + namespace details + { + template + using unique_wdf_lock_storage = unique_storage>; + + class unique_wdf_spin_lock_storage : public unique_wdf_lock_storage + { + using wdf_lock_storage_t = unique_wdf_lock_storage; + + public: + using pointer = wdf_lock_storage_t::pointer; + + // Forward all base class constructors, but have it be explicit. + template + explicit unique_wdf_spin_lock_storage(args_t&& ... args) WI_NOEXCEPT : wdf_lock_storage_t(wistd::forward(args)...) {} + + NTSTATUS create(_In_opt_ WDF_OBJECT_ATTRIBUTES* attributes = WDF_NO_OBJECT_ATTRIBUTES) + { + return ::WdfSpinLockCreate(attributes, out_param(*this)); + } + + WI_NODISCARD + _IRQL_requires_max_(DISPATCH_LEVEL) + _IRQL_raises_(DISPATCH_LEVEL) + wdf_spin_lock_release_scope_exit acquire() WI_NOEXCEPT + { + return wil::acquire_wdf_spin_lock(wdf_lock_storage_t::get()); + } + }; + + class unique_wdf_wait_lock_storage : public unique_wdf_lock_storage + { + using wdf_lock_storage_t = unique_wdf_lock_storage; + + public: + using pointer = wdf_lock_storage_t::pointer; + + // Forward all base class constructors, but have it be explicit. + template + explicit unique_wdf_wait_lock_storage(args_t&& ... args) WI_NOEXCEPT : wdf_lock_storage_t(wistd::forward(args)...) {} + + NTSTATUS create(_In_opt_ WDF_OBJECT_ATTRIBUTES* attributes = WDF_NO_OBJECT_ATTRIBUTES) + { + return ::WdfWaitLockCreate(attributes, out_param(*this)); + } + + WI_NODISCARD + _IRQL_requires_max_(PASSIVE_LEVEL) + wdf_wait_lock_release_scope_exit acquire() WI_NOEXCEPT + { + return wil::acquire_wdf_wait_lock(wdf_lock_storage_t::get()); + } + + WI_NODISCARD + _IRQL_requires_max_(APC_LEVEL) + wdf_wait_lock_release_scope_exit try_acquire() WI_NOEXCEPT + { + return wil::try_acquire_wdf_wait_lock(wdf_lock_storage_t::get()); + } + }; + } + + using unique_wdf_wait_lock = unique_any_t; + using unique_wdf_spin_lock = unique_any_t; + + template + struct wdf_object_reference + { + TWDFOBJECT wdfObject = WDF_NO_HANDLE; + PVOID tag = nullptr; + + wdf_object_reference() WI_NOEXCEPT = default; + + wdf_object_reference(TWDFOBJECT wdfObject, PVOID tag = nullptr) WI_NOEXCEPT + : wdfObject(wdfObject), tag(tag) + { + } + + operator TWDFOBJECT() const WI_NOEXCEPT + { + return wdfObject; + } + + static void close(const wdf_object_reference& wdfObjectReference) WI_NOEXCEPT + { + // We don't use WdfObjectDereferenceActual because there is no way to provide the + // correct __LINE__ and __FILE__, but if you use RAII all the way, you shouldn't have to + // worry about where it was released, only where it was acquired. + WdfObjectDereferenceWithTag(wdfObjectReference.wdfObject, wdfObjectReference.tag); + } + }; + + template + using unique_wdf_object_reference = unique_any::close), + &wdf_object_reference::close, details::pointer_access_noaddress, wdf_object_reference>; + + // Increment the ref-count on a WDF object a unique_wdf_object_reference for it. Use + // WI_WdfObjectReferenceIncrement to automatically use the call-site source location. Use this + // function only if the call-site source location is obtained from elsewhere (i.e., plumbed + // through other abstractions). + template + inline WI_NODISCARD unique_wdf_object_reference wdf_object_reference_increment( + TWDFOBJECT wdfObject, PVOID tag, LONG lineNumber, PCSTR fileName) WI_NOEXCEPT + { + // Parameter is incorrectly marked as non-const, so the const-cast is required. + ::WdfObjectReferenceActual(wdfObject, tag, lineNumber, const_cast(fileName)); + return unique_wdf_object_reference{ wdf_object_reference{ wdfObject, tag } }; + } + +// A macro so that we can capture __LINE__ and __FILE__. +#define WI_WdfObjectReferenceIncrement(wdfObject, tag) \ + wil::wdf_object_reference_increment(wdfObject, tag, __LINE__, __FILE__) + +#endif + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) && \ + defined(_CFGMGR32_H_) && \ + (WINVER >= _WIN32_WINNT_WIN8) && \ + !defined(__WIL_CFGMGR32_H_) +#define __WIL_CFGMGR32_H_ + typedef unique_any unique_hcmnotification; +#endif + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) && \ + defined(_SWDEVICE_H_) && \ + (WINVER >= _WIN32_WINNT_WIN8) && \ + !defined(__WIL_SWDEVICE_H_) +#define __WIL_SWDEVICE_H_ + typedef unique_any unique_hswdevice; +#endif + +#if defined(WIL_KERNEL_MODE) && (defined(_WDMDDK_) || defined(_NTDDK_)) && !defined(__WIL_RESOURCE_WDM) +#define __WIL_RESOURCE_WDM + + namespace details + { + struct kspin_lock_saved_irql + { + PKSPIN_LOCK spinLock = nullptr; + KIRQL savedIrql = PASSIVE_LEVEL; + + kspin_lock_saved_irql() = default; + + kspin_lock_saved_irql(PKSPIN_LOCK /* spinLock */) + { + // This constructor exists simply to allow conversion of the pointer type to + // pointer_storage type when constructing an invalid instance. The spinLock pointer + // is expected to be nullptr. + } + + // Exists to satisfy the interconvertibility requirement for pointer_storage and + // pointer. + explicit operator PKSPIN_LOCK() const + { + return spinLock; + } + + _IRQL_requires_(DISPATCH_LEVEL) + static + void Release(_In_ _IRQL_restores_ const kspin_lock_saved_irql& spinLockSavedIrql) + { + KeReleaseSpinLock(spinLockSavedIrql.spinLock, spinLockSavedIrql.savedIrql); + } + }; + + // On some architectures KeReleaseSpinLockFromDpcLevel is a macro, and we need a thunk + // function we can take the address of. + inline + _IRQL_requires_min_(DISPATCH_LEVEL) + void __stdcall ReleaseSpinLockFromDpcLevel(_Inout_ PKSPIN_LOCK spinLock) WI_NOEXCEPT + { + KeReleaseSpinLockFromDpcLevel(spinLock); + } + } + + using kspin_lock_guard = unique_any; + + using kspin_lock_at_dpc_guard = unique_any; + + WI_NODISCARD + inline + _IRQL_requires_max_(DISPATCH_LEVEL) + _IRQL_saves_ + _IRQL_raises_(DISPATCH_LEVEL) + kspin_lock_guard + acquire_kspin_lock(_In_ PKSPIN_LOCK spinLock) + { + details::kspin_lock_saved_irql spinLockSavedIrql; + KeAcquireSpinLock(spinLock, &spinLockSavedIrql.savedIrql); + spinLockSavedIrql.spinLock = spinLock; + return kspin_lock_guard(spinLockSavedIrql); + } + + WI_NODISCARD + inline + _IRQL_requires_min_(DISPATCH_LEVEL) + kspin_lock_at_dpc_guard + acquire_kspin_lock_at_dpc(_In_ PKSPIN_LOCK spinLock) + { + KeAcquireSpinLockAtDpcLevel(spinLock); + return kspin_lock_at_dpc_guard(spinLock); + } + + class kernel_spin_lock + { + public: + kernel_spin_lock() WI_NOEXCEPT + { + ::KeInitializeSpinLock(&m_kSpinLock); + } + + ~kernel_spin_lock() = default; + + // Cannot change memory location. + kernel_spin_lock(const kernel_spin_lock&) = delete; + kernel_spin_lock& operator=(const kernel_spin_lock&) = delete; + kernel_spin_lock(kernel_spin_lock&&) = delete; + kernel_spin_lock& operator=(kernel_spin_lock&&) = delete; + + WI_NODISCARD + _IRQL_requires_max_(DISPATCH_LEVEL) + _IRQL_saves_ + _IRQL_raises_(DISPATCH_LEVEL) + kspin_lock_guard acquire() WI_NOEXCEPT + { + return acquire_kspin_lock(&m_kSpinLock); + } + + WI_NODISCARD + _IRQL_requires_min_(DISPATCH_LEVEL) + kspin_lock_at_dpc_guard acquire_at_dpc() WI_NOEXCEPT + { + return acquire_kspin_lock_at_dpc(&m_kSpinLock); + } + + private: + KSPIN_LOCK m_kSpinLock; + }; + + namespace details + { + template + class kernel_event_t + { + public: + explicit kernel_event_t(bool isSignaled = false) WI_NOEXCEPT + { + ::KeInitializeEvent(&m_kernelEvent, static_cast(eventType), isSignaled ? TRUE : FALSE); + } + + // Cannot change memory location. + kernel_event_t(const kernel_event_t&) = delete; + kernel_event_t(kernel_event_t&&) = delete; + kernel_event_t& operator=(const kernel_event_t&) = delete; + kernel_event_t& operator=(kernel_event_t&&) = delete; + + // Get the underlying KEVENT structure for more advanced usages like + // KeWaitForMultipleObjects or KeWaitForSingleObject with non-default parameters. + PRKEVENT get() WI_NOEXCEPT + { + return &m_kernelEvent; + } + + void clear() WI_NOEXCEPT + { + // The most common use-case is to clear the event with no interest in its previous + // value. Hence, that is the functionality we provide by default. If the previous + // value is required, one may .get() the underlying event object and call + // ::KeResetEvent(). + ::KeClearEvent(&m_kernelEvent); + } + + // Returns the previous state of the event. + bool set(KPRIORITY increment = IO_NO_INCREMENT) WI_NOEXCEPT + { + return ::KeSetEvent(&m_kernelEvent, increment, FALSE) ? true : false; + } + + // Checks if the event is currently signaled. Does not change the state of the event. + bool is_signaled() const WI_NOEXCEPT + { + return ::KeReadStateEvent(const_cast(&m_kernelEvent)) ? true : false; + } + + // Return true if the wait was satisfied. Time is specified in 100ns units, relative + // (negative) or absolute (positive). For more details, see the documentation of + // KeWaitForSingleObject. + bool wait(LONGLONG waitTime) WI_NOEXCEPT + { + LARGE_INTEGER duration; + duration.QuadPart = waitTime; + return wait_for_single_object(&duration); + } + + // Waits indefinitely for the event to be signaled. + void wait() WI_NOEXCEPT + { + wait_for_single_object(nullptr); + } + + private: + bool wait_for_single_object(_In_opt_ LARGE_INTEGER* waitDuration) WI_NOEXCEPT + { + auto status = ::KeWaitForSingleObject(&m_kernelEvent, Executive, KernelMode, FALSE, waitDuration); + + // We specified Executive and non-alertable, which means some of the return values are + // not possible. + WI_ASSERT((status == STATUS_SUCCESS) || (status == STATUS_TIMEOUT)); + return (status == STATUS_SUCCESS); + } + + KEVENT m_kernelEvent; + }; + } + + using kernel_event_auto_reset = details::kernel_event_t; + using kernel_event_manual_reset = details::kernel_event_t; + using kernel_event = kernel_event_auto_reset; // For parity with the default for other WIL event types. + + /** + RAII class and lock-guards for a kernel FAST_MUTEX. + */ + + using fast_mutex_guard = unique_any; + + WI_NODISCARD + inline + _IRQL_requires_max_(APC_LEVEL) + fast_mutex_guard acquire_fast_mutex(FAST_MUTEX* fastMutex) WI_NOEXCEPT + { + ::ExAcquireFastMutex(fastMutex); + return fast_mutex_guard(fastMutex); + } + + WI_NODISCARD + inline + _IRQL_requires_max_(APC_LEVEL) + fast_mutex_guard try_acquire_fast_mutex(FAST_MUTEX* fastMutex) WI_NOEXCEPT + { + if (::ExTryToAcquireFastMutex(fastMutex)) + { + return fast_mutex_guard(fastMutex); + } + else + { + return fast_mutex_guard(); + } + } + + class fast_mutex + { + public: + fast_mutex() WI_NOEXCEPT + { + ::ExInitializeFastMutex(&m_fastMutex); + } + + ~fast_mutex() WI_NOEXCEPT = default; + + // Cannot change memory location. + fast_mutex(const fast_mutex&) = delete; + fast_mutex& operator=(const fast_mutex&) = delete; + fast_mutex(fast_mutex&&) = delete; + fast_mutex& operator=(fast_mutex&&) = delete; + + // Calls ExAcquireFastMutex. Returned wil::unique_any object calls ExReleaseFastMutex on + // destruction. + WI_NODISCARD + _IRQL_requires_max_(APC_LEVEL) + fast_mutex_guard acquire() WI_NOEXCEPT + { + return acquire_fast_mutex(&m_fastMutex); + } + + // Calls ExTryToAcquireFastMutex. Returned wil::unique_any may be empty. If non-empty, it + // calls ExReleaseFastMutex on destruction. + WI_NODISCARD + _IRQL_requires_max_(APC_LEVEL) + fast_mutex_guard try_acquire() WI_NOEXCEPT + { + return try_acquire_fast_mutex(&m_fastMutex); + } + + private: + FAST_MUTEX m_fastMutex; + }; + + namespace details + { + _IRQL_requires_max_(APC_LEVEL) + inline void release_fast_mutex_with_critical_region(FAST_MUTEX* fastMutex) WI_NOEXCEPT + { + ::ExReleaseFastMutexUnsafe(fastMutex); + ::KeLeaveCriticalRegion(); + } + } + + using fast_mutex_with_critical_region_guard = + unique_any; + + WI_NODISCARD + inline + _IRQL_requires_max_(APC_LEVEL) + fast_mutex_with_critical_region_guard acquire_fast_mutex_with_critical_region(FAST_MUTEX* fastMutex) WI_NOEXCEPT + { + ::KeEnterCriticalRegion(); + ::ExAcquireFastMutexUnsafe(fastMutex); + return fast_mutex_with_critical_region_guard(fastMutex); + } + + // A FAST_MUTEX lock class that calls KeEnterCriticalRegion and then ExAcquireFastMutexUnsafe. + // Returned wil::unique_any lock-guard calls ExReleaseFastMutexUnsafe and KeLeaveCriticalRegion + // on destruction. This is useful if calling code wants to stay at PASSIVE_LEVEL. + class fast_mutex_with_critical_region + { + public: + fast_mutex_with_critical_region() WI_NOEXCEPT + { + ::ExInitializeFastMutex(&m_fastMutex); + } + + ~fast_mutex_with_critical_region() WI_NOEXCEPT = default; + + // Cannot change memory location. + fast_mutex_with_critical_region(const fast_mutex_with_critical_region&) = delete; + fast_mutex_with_critical_region& operator=(const fast_mutex_with_critical_region&) = delete; + fast_mutex_with_critical_region(fast_mutex_with_critical_region&&) = delete; + fast_mutex_with_critical_region& operator=(fast_mutex_with_critical_region&&) = delete; + + WI_NODISCARD + _IRQL_requires_max_(APC_LEVEL) + fast_mutex_with_critical_region_guard acquire() WI_NOEXCEPT + { + return acquire_fast_mutex_with_critical_region(&m_fastMutex); + } + + private: + FAST_MUTEX m_fastMutex; + }; + + //! A type that calls KeLeaveCriticalRegion on destruction (or reset()). + using unique_leave_critical_region_call = unique_call; + + //! Disables user APCs and normal kernel APCs; returns an RAII object that reverts + WI_NODISCARD inline unique_leave_critical_region_call enter_critical_region() + { + KeEnterCriticalRegion(); + return{}; + } + + //! A type that calls KeLeaveGuardedRegion on destruction (or reset()). + using unique_leave_guarded_region_call = unique_call; + + //! Disables all APCs; returns an RAII object that reverts + WI_NODISCARD inline unique_leave_guarded_region_call enter_guarded_region() + { + KeEnterGuardedRegion(); + return{}; + } + + namespace details + { + _IRQL_requires_max_(APC_LEVEL) + inline void release_push_lock_exclusive(EX_PUSH_LOCK* pushLock) WI_NOEXCEPT + { + ::ExReleasePushLockExclusive(pushLock); + ::KeLeaveCriticalRegion(); + } + + _IRQL_requires_max_(APC_LEVEL) + inline void release_push_lock_shared(EX_PUSH_LOCK* pushLock) WI_NOEXCEPT + { + ::ExReleasePushLockShared(pushLock); + ::KeLeaveCriticalRegion(); + } + } + + using push_lock_exclusive_guard = + unique_any; + + using push_lock_shared_guard = + unique_any; + + WI_NODISCARD + inline + _IRQL_requires_max_(APC_LEVEL) + push_lock_exclusive_guard acquire_push_lock_exclusive(EX_PUSH_LOCK* pushLock) WI_NOEXCEPT + { + ::KeEnterCriticalRegion(); + ::ExAcquirePushLockExclusive(pushLock); + return push_lock_exclusive_guard(pushLock); + } + + WI_NODISCARD + inline + _IRQL_requires_max_(APC_LEVEL) + push_lock_shared_guard acquire_push_lock_shared(EX_PUSH_LOCK* pushLock) WI_NOEXCEPT + { + ::KeEnterCriticalRegion(); + ::ExAcquirePushLockShared(pushLock); + return push_lock_shared_guard(pushLock); + } + + class push_lock + { + public: + push_lock() WI_NOEXCEPT + { + ::ExInitializePushLock(&m_pushLock); + } + + ~push_lock() WI_NOEXCEPT = default; + + // Cannot change memory location. + push_lock(const push_lock&) = delete; + push_lock& operator=(const push_lock&) = delete; + push_lock(push_lock&&) = delete; + push_lock& operator=(push_lock&&) = delete; + + WI_NODISCARD + _IRQL_requires_max_(APC_LEVEL) + push_lock_exclusive_guard acquire_exclusive() WI_NOEXCEPT + { + return acquire_push_lock_exclusive(&m_pushLock); + } + + WI_NODISCARD + _IRQL_requires_max_(APC_LEVEL) + push_lock_shared_guard acquire_shared() WI_NOEXCEPT + { + return acquire_push_lock_shared(&m_pushLock); + } + + private: + EX_PUSH_LOCK m_pushLock; + }; + + namespace details + { + // Define a templated type for pool functions in order to satisfy overload resolution below + template + struct pool_helpers + { + static inline + _IRQL_requires_max_(DISPATCH_LEVEL) + void __stdcall FreePoolWithTag(pointer value) WI_NOEXCEPT + { + if (value) + { + ExFreePoolWithTag(value, tag); + } + } + }; + } + + template + using unique_tagged_pool_ptr = unique_any::FreePoolWithTag), &details::pool_helpers::FreePoolWithTag>; + + // For use with IRPs that need to be IoFreeIrp'ed when done, typically allocated using IoAllocateIrp. + using unique_allocated_irp = wil::unique_any; + using unique_io_workitem = wil::unique_any; + +#endif // __WIL_RESOURCE_WDM + +#if defined(WIL_KERNEL_MODE) && (defined(_WDMDDK_) || defined(_ZWAPI_)) && !defined(__WIL_RESOURCE_ZWAPI) +#define __WIL_RESOURCE_ZWAPI + + using unique_kernel_handle = wil::unique_any; + +#endif // __WIL_RESOURCE_ZWAPI + +#if defined(WINTRUST_H) && defined(SOFTPUB_H) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && !defined(__WIL_WINTRUST) +#define __WIL_WINTRUST + namespace details + { + inline void __stdcall CloseWintrustData(_Inout_ WINTRUST_DATA* wtData) WI_NOEXCEPT + { + GUID guidV2 = WINTRUST_ACTION_GENERIC_VERIFY_V2; + wtData->dwStateAction = WTD_STATEACTION_CLOSE; + WinVerifyTrust(static_cast(INVALID_HANDLE_VALUE), &guidV2, wtData); + } + } + typedef wil::unique_struct unique_wintrust_data; +#endif // __WIL_WINTRUST + +#if defined(MSCAT_H) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && !defined(__WIL_MSCAT) +#define __WIL_MSCAT + namespace details + { + inline void __stdcall CryptCATAdminReleaseContextNoFlags(_Pre_opt_valid_ _Frees_ptr_opt_ HCATADMIN handle) WI_NOEXCEPT + { + CryptCATAdminReleaseContext(handle, 0); + } + } + typedef wil::unique_any unique_hcatadmin; + +#if defined(WIL_RESOURCE_STL) + typedef shared_any shared_hcatadmin; + struct hcatinfo_deleter + { + hcatinfo_deleter(wil::shared_hcatadmin handle) WI_NOEXCEPT : m_hCatAdmin(wistd::move(handle)) {} + void operator()(_Pre_opt_valid_ _Frees_ptr_opt_ HCATINFO handle) const WI_NOEXCEPT + { + CryptCATAdminReleaseCatalogContext(m_hCatAdmin.get(), handle, 0); + } + wil::shared_hcatadmin m_hCatAdmin; + }; + // This stores HCATINFO, i.e. HANDLE (void *) + typedef wistd::unique_ptr unique_hcatinfo; + + namespace details + { + class crypt_catalog_enumerator + { + wil::unique_hcatinfo m_hCatInfo; + const BYTE * m_hash; + DWORD m_hashLen; + bool m_initialized = false; + + struct ref + { + explicit ref(crypt_catalog_enumerator &r) WI_NOEXCEPT : + m_r(r) + {} + + operator HCATINFO() const WI_NOEXCEPT + { + return m_r.current(); + } + + wil::unique_hcatinfo move_from_unique_hcatinfo() WI_NOEXCEPT + { + wil::unique_hcatinfo info(wistd::move(m_r.m_hCatInfo)); + return info; + } + + bool operator==(wistd::nullptr_t) const WI_NOEXCEPT + { + return m_r.m_hCatInfo == nullptr; + } + + bool operator!=(wistd::nullptr_t) const WI_NOEXCEPT + { + return !(*this == nullptr); + } + + private: + crypt_catalog_enumerator &m_r; + }; + + struct iterator + { +#ifdef _XUTILITY_ + // muse be input_iterator_tag as use of one instance invalidates the other. + typedef ::std::input_iterator_tag iterator_category; +#endif + + explicit iterator(crypt_catalog_enumerator *r) WI_NOEXCEPT : + m_r(r) + {} + + iterator(const iterator &) = default; + iterator(iterator &&) = default; + iterator &operator=(const iterator &) = default; + iterator &operator=(iterator &&) = default; + + bool operator==(const iterator &rhs) const WI_NOEXCEPT + { + if (rhs.m_r == m_r) + { + return true; + } + + return (*this == nullptr) && (rhs == nullptr); + } + + bool operator!=(const iterator &rhs) const WI_NOEXCEPT + { + return !(rhs == *this); + } + + bool operator==(wistd::nullptr_t) const WI_NOEXCEPT + { + return nullptr == m_r || nullptr == m_r->current(); + } + + bool operator!=(wistd::nullptr_t) const WI_NOEXCEPT + { + return !(*this == nullptr); + } + + iterator &operator++() WI_NOEXCEPT + { + if (m_r != nullptr) + { + m_r->next(); + } + + return *this; + } + + ref operator*() const WI_NOEXCEPT + { + return ref(*m_r); + } + + private: + crypt_catalog_enumerator *m_r; + }; + + shared_hcatadmin &hcatadmin() WI_NOEXCEPT + { + return m_hCatInfo.get_deleter().m_hCatAdmin; + } + + bool move_next() WI_NOEXCEPT + { + HCATINFO prevCatInfo = m_hCatInfo.release(); + m_hCatInfo.reset( + ::CryptCATAdminEnumCatalogFromHash( + hcatadmin().get(), + const_cast(m_hash), + m_hashLen, + 0, + &prevCatInfo)); + return !!m_hCatInfo; + } + + HCATINFO next() WI_NOEXCEPT + { + if (m_initialized && m_hCatInfo) + { + move_next(); + } + + return current(); + } + + HCATINFO init() WI_NOEXCEPT + { + if (!m_initialized) + { + m_initialized = true; + move_next(); + } + + return current(); + } + + HCATINFO current() WI_NOEXCEPT + { + return m_hCatInfo.get(); + } + + public: + crypt_catalog_enumerator(wil::shared_hcatadmin &hCatAdmin, + const BYTE *hash, + DWORD hashLen) WI_NOEXCEPT : + m_hCatInfo(nullptr, hCatAdmin), + m_hash(hash), + m_hashLen(hashLen) + // , m_initialized(false) // redundant + {} + + iterator begin() WI_NOEXCEPT + { + init(); + return iterator(this); + } + + iterator end() const WI_NOEXCEPT + { + return iterator(nullptr); + } + + crypt_catalog_enumerator(crypt_catalog_enumerator &&) = default; + crypt_catalog_enumerator &operator=(crypt_catalog_enumerator &&) = default; + + crypt_catalog_enumerator(const crypt_catalog_enumerator &) = delete; + crypt_catalog_enumerator &operator=(const crypt_catalog_enumerator &) = delete; + }; + } + + /** Use to enumerate catalogs containing a hash with a range-based for. + This avoids handling a raw resource to call CryptCATAdminEnumCatalogFromHash correctly. + Example: + `for (auto&& cat : wil::make_catalog_enumerator(hCatAdmin, hash, hashLen)) + { CryptCATCatalogInfoFromContext(cat, &catInfo, 0); }` */ + inline details::crypt_catalog_enumerator make_crypt_catalog_enumerator(wil::shared_hcatadmin &hCatAdmin, + _In_count_(hashLen) const BYTE *hash, DWORD hashLen) WI_NOEXCEPT + { + return details::crypt_catalog_enumerator(hCatAdmin, hash, hashLen); + } + + template + details::crypt_catalog_enumerator make_crypt_catalog_enumerator(wil::shared_hcatadmin &hCatAdmin, + const BYTE (&hash)[Size]) WI_NOEXCEPT + { + static_assert(Size <= static_cast(0xffffffffUL), "Array size truncated"); + return details::crypt_catalog_enumerator(hCatAdmin, hash, static_cast(Size)); + } + +#endif // WI_RESOURCE_STL + +#endif // __WIL_MSCAT + +} // namespace wil + +#pragma warning(pop) diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/result.h b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/result.h new file mode 100644 index 0000000..3ec62b4 --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/result.h @@ -0,0 +1,1274 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_RESULT_INCLUDED +#define __WIL_RESULT_INCLUDED + +// Most functionality is picked up from result_macros.h. This file specifically provides higher level processing of errors when +// they are encountered by the underlying macros. +#include "result_macros.h" + +// Note that we avoid pulling in STL's memory header from Result.h through Resource.h as we have +// Result.h customers who are still on older versions of STL (without std::shared_ptr<>). +#ifndef RESOURCE_SUPPRESS_STL +#define RESOURCE_SUPPRESS_STL +#include "resource.h" +#undef RESOURCE_SUPPRESS_STL +#else +#include "resource.h" +#endif + +#ifdef WIL_KERNEL_MODE +#error This header is not supported in kernel-mode. +#endif + +// The updated behavior of running init-list ctors during placement new is proper & correct, disable the warning that requests developers verify they want it +#pragma warning(push) +#pragma warning(disable : 4351) + +namespace wil +{ + // WARNING: EVERYTHING in this namespace must be handled WITH CARE as the entities defined within + // are used as an in-proc ABI contract between binaries that utilize WIL. Making changes + // that add v-tables or change the storage semantics of anything herein needs to be done + // with care and respect to versioning. + ///@cond + namespace details_abi + { + #define __WI_SEMAHPORE_VERSION L"_p0" + + // This class uses named semaphores to be able to stash a numeric value (including a pointer + // for retrieval from within any module in a process). This is a very specific need of a + // header-based library that should not be generally used. + // + // Notes for use: + // * Data members must be stable unless __WI_SEMAHPORE_VERSION is changed + // * The class must not reference module code (v-table, function pointers, etc) + // * Use of this class REQUIRES that there be a MUTEX held around the semaphore manipulation + // and tests as it doesn't attempt to handle thread contention on the semaphore while manipulating + // the count. + // * This class supports storing a 31-bit number of a single semaphore or a 62-bit number across + // two semaphores and directly supports pointers. + + class SemaphoreValue + { + public: + SemaphoreValue() = default; + SemaphoreValue(const SemaphoreValue&) = delete; + SemaphoreValue& operator=(const SemaphoreValue&) = delete; + + SemaphoreValue(SemaphoreValue&& other) WI_NOEXCEPT : + m_semaphore(wistd::move(other.m_semaphore)), + m_semaphoreHigh(wistd::move(other.m_semaphoreHigh)) + { + static_assert(sizeof(m_semaphore) == sizeof(HANDLE), "unique_any must be a direct representation of the HANDLE to be used across module"); + } + + void Destroy() + { + m_semaphore.reset(); + m_semaphoreHigh.reset(); + } + + template + HRESULT CreateFromValue(PCWSTR name, T value) + { + return CreateFromValueInternal(name, (sizeof(value) > sizeof(unsigned long)), static_cast(value)); + } + + HRESULT CreateFromPointer(PCWSTR name, void* pointer) + { + ULONG_PTR value = reinterpret_cast(pointer); + FAIL_FAST_IMMEDIATE_IF(WI_IsAnyFlagSet(value, 0x3)); + return CreateFromValue(name, value >> 2); + } + + template + static HRESULT TryGetValue(PCWSTR name, _Out_ T* value, _Out_opt_ bool *retrieved = nullptr) + { + *value = static_cast(0); + unsigned __int64 value64 = 0; + __WIL_PRIVATE_RETURN_IF_FAILED(TryGetValueInternal(name, (sizeof(T) > sizeof(unsigned long)), &value64, retrieved)); + *value = static_cast(value64); + return S_OK; + } + + static HRESULT TryGetPointer(PCWSTR name, _Outptr_result_maybenull_ void** pointer) + { + *pointer = nullptr; + ULONG_PTR value = 0; + __WIL_PRIVATE_RETURN_IF_FAILED(TryGetValue(name, &value)); + *pointer = reinterpret_cast(value << 2); + return S_OK; + } + + private: + HRESULT CreateFromValueInternal(PCWSTR name, bool is64Bit, unsigned __int64 value) + { + WI_ASSERT(!m_semaphore && !m_semaphoreHigh); // call Destroy first + + // This routine only supports 31 bits when semahporeHigh is not supplied or 62 bits when the value + // is supplied. It's a programming error to use it when either of these conditions are not true. + + FAIL_FAST_IMMEDIATE_IF((!is64Bit && WI_IsAnyFlagSet(value, 0xFFFFFFFF80000000)) || + (is64Bit && WI_IsAnyFlagSet(value, 0xC000000000000000))); + + wchar_t localName[MAX_PATH]; + WI_VERIFY_SUCCEEDED(StringCchCopyW(localName, ARRAYSIZE(localName), name)); + WI_VERIFY_SUCCEEDED(StringCchCatW(localName, ARRAYSIZE(localName), __WI_SEMAHPORE_VERSION)); + + const unsigned long highPart = static_cast(value >> 31); + const unsigned long lowPart = static_cast(value & 0x000000007FFFFFFF); + + // We set the count of the semaphore equal to the max (the value we're storing). The only exception to that + // is ZERO, where you can't create a semaphore of value ZERO, where we push the max to one and use a count of ZERO. + + __WIL_PRIVATE_RETURN_IF_FAILED(m_semaphore.create(static_cast(lowPart), static_cast((lowPart > 0) ? lowPart : 1), localName)); + if (is64Bit) + { + WI_VERIFY_SUCCEEDED(StringCchCatW(localName, ARRAYSIZE(localName), L"h")); + __WIL_PRIVATE_RETURN_IF_FAILED(m_semaphoreHigh.create(static_cast(highPart), static_cast((highPart > 0) ? highPart : 1), localName)); + } + + return S_OK; + } + + static HRESULT GetValueFromSemaphore(HANDLE semaphore, _Out_ LONG* count) + { + // First we consume a single count from the semaphore. This will work in all cases other + // than the case where the count we've recorded is ZERO which will TIMEOUT. + + DWORD result = ::WaitForSingleObject(semaphore, 0); + __WIL_PRIVATE_RETURN_LAST_ERROR_IF(result == WAIT_FAILED); + __WIL_PRIVATE_RETURN_HR_IF(E_UNEXPECTED, !((result == WAIT_OBJECT_0) || (result == WAIT_TIMEOUT))); + + LONG value = 0; + if (result == WAIT_OBJECT_0) + { + // We were able to wait. To establish our count, all we have to do is release that count + // back to the semaphore and observe the value that we released. + + __WIL_PRIVATE_RETURN_IF_WIN32_BOOL_FALSE(::ReleaseSemaphore(semaphore, 1, &value)); + value++; // we waited first, so our actual value is one more than the old value + + // Make sure the value is correct by validating that we have no more posts. + BOOL expectedFailure = ::ReleaseSemaphore(semaphore, 1, nullptr); + __WIL_PRIVATE_RETURN_HR_IF(E_UNEXPECTED, expectedFailure || (::GetLastError() != ERROR_TOO_MANY_POSTS)); + } + else + { + WI_ASSERT(result == WAIT_TIMEOUT); + + // We know at this point that the value is ZERO. We'll do some verification to ensure that + // this address is right by validating that we have one and only one more post that we could use. + + LONG expected = 0; + __WIL_PRIVATE_RETURN_IF_WIN32_BOOL_FALSE(::ReleaseSemaphore(semaphore, 1, &expected)); + __WIL_PRIVATE_RETURN_HR_IF(E_UNEXPECTED, expected != 0); + + const BOOL expectedFailure = ::ReleaseSemaphore(semaphore, 1, nullptr); + __WIL_PRIVATE_RETURN_HR_IF(E_UNEXPECTED, expectedFailure || (::GetLastError() != ERROR_TOO_MANY_POSTS)); + + result = ::WaitForSingleObject(semaphore, 0); + __WIL_PRIVATE_RETURN_LAST_ERROR_IF(result == WAIT_FAILED); + __WIL_PRIVATE_RETURN_HR_IF(E_UNEXPECTED, result != WAIT_OBJECT_0); + } + + *count = value; + return S_OK; + } + + static HRESULT TryGetValueInternal(PCWSTR name, bool is64Bit, _Out_ unsigned __int64* value, _Out_opt_ bool* retrieved) + { + assign_to_opt_param(retrieved, false); + *value = 0; + + wchar_t localName[MAX_PATH]; + WI_VERIFY_SUCCEEDED(StringCchCopyW(localName, ARRAYSIZE(localName), name)); + WI_VERIFY_SUCCEEDED(StringCchCatW(localName, ARRAYSIZE(localName), __WI_SEMAHPORE_VERSION)); + + wil::unique_semaphore_nothrow semaphoreLow(::OpenSemaphoreW(SEMAPHORE_ALL_ACCESS, FALSE, localName)); + if (!semaphoreLow) + { + __WIL_PRIVATE_RETURN_HR_IF(S_OK, (::GetLastError() == ERROR_FILE_NOT_FOUND)); + __WIL_PRIVATE_RETURN_LAST_ERROR(); + } + + LONG countLow = 0; + LONG countHigh = 0; + + __WIL_PRIVATE_RETURN_IF_FAILED(GetValueFromSemaphore(semaphoreLow.get(), &countLow)); + + if (is64Bit) + { + WI_VERIFY_SUCCEEDED(StringCchCatW(localName, ARRAYSIZE(localName), L"h")); + wil::unique_semaphore_nothrow semaphoreHigh(::OpenSemaphoreW(SEMAPHORE_ALL_ACCESS, FALSE, localName)); + __WIL_PRIVATE_RETURN_LAST_ERROR_IF_NULL(semaphoreHigh); + + __WIL_PRIVATE_RETURN_IF_FAILED(GetValueFromSemaphore(semaphoreHigh.get(), &countHigh)); + } + + WI_ASSERT((countLow >= 0) && (countHigh >= 0)); + + const unsigned __int64 newValueHigh = (static_cast(countHigh) << 31); + const unsigned __int64 newValueLow = static_cast(countLow); + + assign_to_opt_param(retrieved, true); + *value = (newValueHigh | newValueLow); + return S_OK; + } + + wil::unique_semaphore_nothrow m_semaphore; + wil::unique_semaphore_nothrow m_semaphoreHigh; + }; + + template + class ProcessLocalStorageData + { + public: + ProcessLocalStorageData(unique_mutex_nothrow&& mutex, SemaphoreValue&& value) : + m_mutex(wistd::move(mutex)), + m_value(wistd::move(value)), + m_data() + { + static_assert(sizeof(m_mutex) == sizeof(HANDLE), "unique_any must be equivalent to the handle size to safely use across module"); + } + + T* GetData() + { + WI_ASSERT(m_mutex); + return &m_data; + } + + void Release() + { + if (ProcessShutdownInProgress()) + { + // There are no other threads to contend with. + if (--m_refCount == 0) + { + m_data.ProcessShutdown(); + } + } + else + { + auto lock = m_mutex.acquire(); + if (--m_refCount == 0) + { + // We must explicitly destroy our semaphores while holding the mutex + m_value.Destroy(); + lock.reset(); + + this->~ProcessLocalStorageData(); + ::HeapFree(::GetProcessHeap(), 0, this); + } + } + } + + static HRESULT Acquire(PCSTR staticNameWithVersion, _Outptr_result_nullonfailure_ ProcessLocalStorageData** data) + { + *data = nullptr; + + // NOTE: the '0' in SM0 below is intended as the VERSION number. Changes to this class require + // that this value be revised. + + const DWORD size = static_cast(sizeof(ProcessLocalStorageData)); + wchar_t name[MAX_PATH]; + WI_VERIFY(SUCCEEDED(StringCchPrintfW(name, ARRAYSIZE(name), L"Local\\SM0:%lu:%lu:%hs", ::GetCurrentProcessId(), size, staticNameWithVersion))); + + unique_mutex_nothrow mutex; + mutex.reset(::CreateMutexExW(nullptr, name, 0, MUTEX_ALL_ACCESS)); + + // This will fail in some environments and will be fixed with deliverable 12394134 + RETURN_LAST_ERROR_IF_EXPECTED(!mutex); + auto lock = mutex.acquire(); + + void* pointer = nullptr; + __WIL_PRIVATE_RETURN_IF_FAILED(SemaphoreValue::TryGetPointer(name, &pointer)); + if (pointer) + { + *data = reinterpret_cast*>(pointer); + (*data)->m_refCount++; + } + else + { + __WIL_PRIVATE_RETURN_IF_FAILED(MakeAndInitialize(name, wistd::move(mutex), data)); // Assumes mutex handle ownership on success ('lock' will still be released) + } + + return S_OK; + } + + private: + + volatile long m_refCount = 1; + unique_mutex_nothrow m_mutex; + SemaphoreValue m_value; + T m_data; + + static HRESULT MakeAndInitialize(PCWSTR name, unique_mutex_nothrow&& mutex, ProcessLocalStorageData** data) + { + *data = nullptr; + + const DWORD size = static_cast(sizeof(ProcessLocalStorageData)); + + unique_process_heap_ptr> dataAlloc(static_cast*>(details::ProcessHeapAlloc(HEAP_ZERO_MEMORY, size))); + __WIL_PRIVATE_RETURN_IF_NULL_ALLOC(dataAlloc); + + SemaphoreValue semaphoreValue; + __WIL_PRIVATE_RETURN_IF_FAILED(semaphoreValue.CreateFromPointer(name, dataAlloc.get())); + + new(dataAlloc.get()) ProcessLocalStorageData(wistd::move(mutex), wistd::move(semaphoreValue)); + *data = dataAlloc.release(); + + return S_OK; + } + }; + + template + class ProcessLocalStorage + { + public: + ProcessLocalStorage(PCSTR staticNameWithVersion) WI_NOEXCEPT : + m_staticNameWithVersion(staticNameWithVersion) + { + } + + ~ProcessLocalStorage() WI_NOEXCEPT + { + if (m_data) + { + m_data->Release(); + } + } + + T* GetShared() WI_NOEXCEPT + { + if (!m_data) + { + ProcessLocalStorageData* localTemp = nullptr; + if (SUCCEEDED(ProcessLocalStorageData::Acquire(m_staticNameWithVersion, &localTemp)) && !m_data) + { + m_data = localTemp; + } + } + return m_data ? m_data->GetData() : nullptr; + } + + private: + PCSTR m_staticNameWithVersion = nullptr; + ProcessLocalStorageData* m_data = nullptr; + }; + + template + class ThreadLocalStorage + { + public: + ThreadLocalStorage(const ThreadLocalStorage&) = delete; + ThreadLocalStorage& operator=(const ThreadLocalStorage&) = delete; + + ThreadLocalStorage() = default; + + ~ThreadLocalStorage() WI_NOEXCEPT + { + for (auto &entry : m_hashArray) + { + Node *pNode = entry; + while (pNode != nullptr) + { + auto pCurrent = pNode; + pNode = pNode->pNext; + pCurrent->~Node(); + ::HeapFree(::GetProcessHeap(), 0, pCurrent); + } + entry = nullptr; + } + } + + // Note: Can return nullptr even when (shouldAllocate == true) upon allocation failure + T* GetLocal(bool shouldAllocate = false) WI_NOEXCEPT + { + DWORD const threadId = ::GetCurrentThreadId(); + size_t const index = (threadId % ARRAYSIZE(m_hashArray)); + for (auto pNode = m_hashArray[index]; pNode != nullptr; pNode = pNode->pNext) + { + if (pNode->threadId == threadId) + { + return &pNode->value; + } + } + + if (shouldAllocate) + { + if (auto pNewRaw = details::ProcessHeapAlloc(0, sizeof(Node))) + { + auto pNew = new (pNewRaw) Node{ threadId }; + + Node *pFirst; + do + { + pFirst = m_hashArray[index]; + pNew->pNext = pFirst; + } while (::InterlockedCompareExchangePointer(reinterpret_cast(m_hashArray + index), pNew, pFirst) != pFirst); + + return &pNew->value; + } + } + return nullptr; + } + + private: + + struct Node + { + DWORD threadId = ULONG_MAX; + Node* pNext = nullptr; + T value{}; + }; + + Node * volatile m_hashArray[10]{}; + }; + + struct ThreadLocalFailureInfo + { + // ABI contract (carry size to facilitate additive change without re-versioning) + unsigned short size; + unsigned char reserved1[2]; // packing, reserved + // When this failure was seen + unsigned int sequenceId; + + // Information about the failure + HRESULT hr; + PCSTR fileName; + unsigned short lineNumber; + unsigned char failureType; // FailureType + unsigned char reserved2; // packing, reserved + PCSTR modulePath; + void* returnAddress; + void* callerReturnAddress; + PCWSTR message; + + // The allocation (LocalAlloc) where structure strings point + void* stringBuffer; + size_t stringBufferSize; + + // NOTE: Externally Managed: Must not have constructor or destructor + + void Clear() + { + ::HeapFree(::GetProcessHeap(), 0, stringBuffer); + stringBuffer = nullptr; + stringBufferSize = 0; + } + + void Set(const FailureInfo& info, unsigned int newSequenceId) + { + sequenceId = newSequenceId; + + hr = info.hr; + fileName = nullptr; + lineNumber = static_cast(info.uLineNumber); + failureType = static_cast(info.type); + modulePath = nullptr; + returnAddress = info.returnAddress; + callerReturnAddress = info.callerReturnAddress; + message = nullptr; + + size_t neededSize = details::ResultStringSize(info.pszFile) + + details::ResultStringSize(info.pszModule) + + details::ResultStringSize(info.pszMessage); + + if (!stringBuffer || (stringBufferSize < neededSize)) + { + auto newBuffer = details::ProcessHeapAlloc(HEAP_ZERO_MEMORY, neededSize); + if (newBuffer) + { + ::HeapFree(::GetProcessHeap(), 0, stringBuffer); + stringBuffer = newBuffer; + stringBufferSize = neededSize; + } + } + + if (stringBuffer) + { + unsigned char *pBuffer = static_cast(stringBuffer); + unsigned char *pBufferEnd = pBuffer + stringBufferSize; + + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, info.pszFile, &fileName); + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, info.pszModule, &modulePath); + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, info.pszMessage, &message); + ZeroMemory(pBuffer, pBufferEnd - pBuffer); + } + } + + void Get(FailureInfo& info) + { + ::ZeroMemory(&info, sizeof(info)); + + info.failureId = sequenceId; + info.hr = hr; + info.pszFile = fileName; + info.uLineNumber = lineNumber; + info.type = static_cast(failureType); + info.pszModule = modulePath; + info.returnAddress = returnAddress; + info.callerReturnAddress = callerReturnAddress; + info.pszMessage = message; + } + }; + + struct ThreadLocalData + { + // ABI contract (carry size to facilitate additive change without re-versioning) + unsigned short size = sizeof(ThreadLocalData); + + // Subscription information + unsigned int threadId = 0; + volatile long* failureSequenceId = nullptr; // backpointer to the global ID + + // Information about thread errors + unsigned int latestSubscribedFailureSequenceId = 0; + + // The last (N) observed errors + ThreadLocalFailureInfo* errors = nullptr; + unsigned short errorAllocCount = 0; + unsigned short errorCurrentIndex = 0; + + // NOTE: Externally Managed: Must allow ZERO init construction + + ~ThreadLocalData() + { + Clear(); + } + + void Clear() + { + for (auto& error : make_range(errors, errorAllocCount)) + { + error.Clear(); + } + ::HeapFree(::GetProcessHeap(), 0, errors); + errorAllocCount = 0; + errorCurrentIndex = 0; + errors = nullptr; + } + + bool EnsureAllocated(bool create = true) + { + if (!errors && create) + { + const unsigned short errorCount = 5; + errors = reinterpret_cast(details::ProcessHeapAlloc(HEAP_ZERO_MEMORY, errorCount * sizeof(ThreadLocalFailureInfo))); + if (errors) + { + errorAllocCount = errorCount; + errorCurrentIndex = 0; + for (auto& error : make_range(errors, errorAllocCount)) + { + error.size = sizeof(ThreadLocalFailureInfo); + } + } + } + return (errors != nullptr); + } + + void SetLastError(const wil::FailureInfo& info) + { + const bool hasListener = (latestSubscribedFailureSequenceId > 0); + + if (!EnsureAllocated(hasListener)) + { + // We either couldn't allocate or we haven't yet allocated and nobody + // was listening, so we ignore. + return; + } + + if (hasListener) + { + // When we have listeners, we can throw away any updates to the last seen error + // code within the same listening context presuming it's an update of the existing + // error with the same code. + + for (auto& error : make_range(errors, errorAllocCount)) + { + if ((error.sequenceId > latestSubscribedFailureSequenceId) && (error.hr == info.hr)) + { + return; + } + } + } + + // Otherwise we create a new failure... + + errorCurrentIndex = (errorCurrentIndex + 1) % errorAllocCount; + errors[errorCurrentIndex].Set(info, ::InterlockedIncrementNoFence(failureSequenceId)); + } + + bool GetLastError(_Inout_ wil::FailureInfo& info, unsigned int minSequenceId, HRESULT matchRequirement) + { + if (!errors) + { + return false; + } + + // If the last error we saw doesn't meet the filter requirement or if the last error was never + // set, then we couldn't return a result at all... + auto& lastFailure = errors[errorCurrentIndex]; + if (minSequenceId >= lastFailure.sequenceId) + { + return false; + } + + // With no result filter, we just go to the last error and report it + if (matchRequirement == S_OK) + { + lastFailure.Get(info); + return true; + } + + // Find the oldest result matching matchRequirement and passing minSequenceId + ThreadLocalFailureInfo* find = nullptr; + for (auto& error : make_range(errors, errorAllocCount)) + { + if ((error.hr == matchRequirement) && (error.sequenceId > minSequenceId)) + { + if (!find || (error.sequenceId < find->sequenceId)) + { + find = &error; + } + } + } + if (find) + { + find->Get(info); + return true; + } + + return false; + } + + bool GetCaughtExceptionError(_Inout_ wil::FailureInfo& info, unsigned int minSequenceId, _In_opt_ const DiagnosticsInfo* diagnostics, HRESULT matchRequirement, void* returnAddress) + { + // First attempt to get the last error and then see if it matches the error returned from + // the last caught exception. If it does, then we're good to go and we return that last error. + + FailureInfo last = {}; + if (GetLastError(last, minSequenceId, matchRequirement) && (last.hr == ResultFromCaughtException())) + { + info = last; + return true; + } + + // The last error didn't match or we never had one... we need to create one -- we do so by logging + // our current request and then using the last error. + + DiagnosticsInfo source; + if (diagnostics) + { + source = *diagnostics; + } + + // NOTE: FailureType::Log as it's only informative (no action) and SupportedExceptions::All as it's not a barrier, only recognition. + wchar_t message[2048]; + message[0] = L'\0'; + const HRESULT hr = details::ReportFailure_CaughtExceptionCommon(__R_DIAGNOSTICS_RA(source, returnAddress), message, ARRAYSIZE(message), SupportedExceptions::All).hr; + + // Now that the exception was logged, we should be able to fetch it. + return GetLastError(info, minSequenceId, hr); + } + }; + + struct ProcessLocalData + { + // ABI contract (carry size to facilitate additive change without re-versioning) + unsigned short size = sizeof(ProcessLocalData); + + // Failure Information + volatile long failureSequenceId = 1; // process global variable + ThreadLocalStorage threads; // list of allocated threads + + void ProcessShutdown() {} + }; + + __declspec(selectany) ProcessLocalStorage* g_pProcessLocalData = nullptr; + + __declspec(noinline) inline ThreadLocalData* GetThreadLocalDataCache(bool allocate = true) + { + ThreadLocalData* result = nullptr; + if (g_pProcessLocalData) + { + auto processData = g_pProcessLocalData->GetShared(); + if (processData) + { + result = processData->threads.GetLocal(allocate); + if (result && !result->failureSequenceId) + { + result->failureSequenceId = &(processData->failureSequenceId); + } + } + } + return result; + } + + __forceinline ThreadLocalData* GetThreadLocalData(bool allocate = true) + { + return GetThreadLocalDataCache(allocate); + } + + } // details_abi + /// @endcond + + + /** Returns a sequence token that can be used with wil::GetLastError to limit errors to those that occur after this token was retrieved. + General usage pattern: use wil::GetCurrentErrorSequenceId to cache a token, execute your code, on failure use wil::GetLastError with the token + to provide information on the error that occurred while executing your code. Prefer to use wil::ThreadErrorContext over this approach when + possible. */ + inline long GetCurrentErrorSequenceId() + { + auto data = details_abi::GetThreadLocalData(); + if (data) + { + // someone is interested -- make sure we can store errors + data->EnsureAllocated(); + return *data->failureSequenceId; + } + + return 0; + } + + /** Caches failure information for later retrieval from GetLastError. + Most people will never need to do this explicitly as failure information is automatically made available per-thread across a process when + errors are encountered naturally through the WIL macros. */ + inline void SetLastError(const wil::FailureInfo& info) + { + static volatile unsigned int lastThread = 0; + auto threadId = ::GetCurrentThreadId(); + if (lastThread != threadId) + { + static volatile long depth = 0; + if (::InterlockedIncrementNoFence(&depth) < 4) + { + lastThread = threadId; + auto data = details_abi::GetThreadLocalData(false); // false = avoids allocation if not already present + if (data) + { + data->SetLastError(info); + } + lastThread = 0; + } + ::InterlockedDecrementNoFence(&depth); + } + } + + /** Retrieves failure information for the current thread with the given filters. + This API can be used to retrieve information about the last WIL failure that occurred on the current thread. + This error crosses DLL boundaries as long as the error occurred in the current process. Passing a minSequenceId + restricts the error returned to one that occurred after the given sequence ID. Passing matchRequirement also filters + the returned result to the given error code. */ + inline bool GetLastError(_Inout_ wil::FailureInfo& info, unsigned int minSequenceId = 0, HRESULT matchRequirement = S_OK) + { + auto data = details_abi::GetThreadLocalData(false); // false = avoids allocation if not already present + if (data) + { + return data->GetLastError(info, minSequenceId, matchRequirement); + } + return false; + } + + /** Retrieves failure information when within a catch block for the current thread with the given filters. + When unable to retrieve the exception information (when WIL hasn't yet seen it), this will attempt (best effort) to + discover information about the exception and will attribute that information to the given DiagnosticsInfo position. + See GetLastError for capabilities and filtering. */ + inline __declspec(noinline) bool GetCaughtExceptionError(_Inout_ wil::FailureInfo& info, unsigned int minSequenceId = 0, const DiagnosticsInfo* diagnostics = nullptr, HRESULT matchRequirement = S_OK) + { + auto data = details_abi::GetThreadLocalData(); + if (data) + { + return data->GetCaughtExceptionError(info, minSequenceId, diagnostics, matchRequirement, _ReturnAddress()); + } + return false; + } + + /** Use this class to manage retrieval of information about an error occurring in the requested code. + Construction of this class sets a point in time after which you can use the GetLastError class method to retrieve + the origination of the last error that occurred on this thread since the class was created. */ + class ThreadErrorContext + { + public: + ThreadErrorContext() : + m_data(details_abi::GetThreadLocalData()) + { + if (m_data) + { + m_sequenceIdLast = m_data->latestSubscribedFailureSequenceId; + m_sequenceIdStart = *m_data->failureSequenceId; + m_data->latestSubscribedFailureSequenceId = m_sequenceIdStart; + } + } + + ~ThreadErrorContext() + { + if (m_data) + { + m_data->latestSubscribedFailureSequenceId = m_sequenceIdLast; + } + } + + /** Retrieves the origination of the last error that occurred since this class was constructed. + The optional parameter allows the failure information returned to be filtered to a specific + result. */ + inline bool GetLastError(FailureInfo& info, HRESULT matchRequirement = S_OK) + { + if (m_data) + { + return m_data->GetLastError(info, m_sequenceIdStart, matchRequirement); + } + return false; + } + + /** Retrieves the origin of the current exception (within a catch block) since this class was constructed. + See @ref GetCaughtExceptionError for more information */ + inline __declspec(noinline) bool GetCaughtExceptionError(_Inout_ wil::FailureInfo& info, const DiagnosticsInfo* diagnostics = nullptr, HRESULT matchRequirement = S_OK) + { + if (m_data) + { + return m_data->GetCaughtExceptionError(info, m_sequenceIdStart, diagnostics, matchRequirement, _ReturnAddress()); + } + return false; + } + + private: + details_abi::ThreadLocalData* m_data; + unsigned long m_sequenceIdStart{}; + unsigned long m_sequenceIdLast{}; + }; + + + enum class WilInitializeCommand + { + Create, + Destroy, + }; + + + /// @cond + namespace details + { + struct IFailureCallback + { + virtual bool NotifyFailure(FailureInfo const &failure) WI_NOEXCEPT = 0; + }; + + class ThreadFailureCallbackHolder; + + __declspec(selectany) details_abi::ThreadLocalStorage* g_pThreadFailureCallbacks = nullptr; + + class ThreadFailureCallbackHolder + { + public: + ThreadFailureCallbackHolder(_In_ IFailureCallback *pCallbackParam, _In_opt_ CallContextInfo *pCallContext = nullptr, bool watchNow = true) WI_NOEXCEPT : + m_ppThreadList(nullptr), + m_pCallback(pCallbackParam), + m_pNext(nullptr), + m_threadId(0), + m_pCallContext(pCallContext) + { + if (watchNow) + { + StartWatching(); + } + } + + ThreadFailureCallbackHolder(ThreadFailureCallbackHolder &&other) WI_NOEXCEPT : + m_ppThreadList(nullptr), + m_pCallback(other.m_pCallback), + m_pNext(nullptr), + m_threadId(0), + m_pCallContext(other.m_pCallContext) + { + if (other.m_threadId != 0) + { + other.StopWatching(); + StartWatching(); + } + } + + ~ThreadFailureCallbackHolder() WI_NOEXCEPT + { + if (m_threadId != 0) + { + StopWatching(); + } + } + + void SetCallContext(_In_opt_ CallContextInfo *pCallContext) + { + m_pCallContext = pCallContext; + } + + CallContextInfo *CallContextInfo() + { + return m_pCallContext; + } + + void StartWatching() + { + // out-of balance Start/Stop calls? + __FAIL_FAST_IMMEDIATE_ASSERT__(m_threadId == 0); + + m_ppThreadList = g_pThreadFailureCallbacks ? g_pThreadFailureCallbacks->GetLocal(true) : nullptr; // true = allocate thread list if missing + if (m_ppThreadList) + { + m_pNext = *m_ppThreadList; + *m_ppThreadList = this; + m_threadId = ::GetCurrentThreadId(); + } + } + + void StopWatching() + { + if (m_threadId != ::GetCurrentThreadId()) + { + // The thread-specific failure holder cannot be stopped on a different thread than it was started on or the + // internal book-keeping list will be corrupted. To fix this change the telemetry pattern in the calling code + // to match one of the patterns available here: + // https://microsoft.sharepoint.com/teams/osg_development/Shared%20Documents/Windows%20TraceLogging%20Helpers.docx + + WI_USAGE_ERROR("MEMORY CORRUPTION: Calling code is leaking an activity thread-watcher and releasing it on another thread"); + } + + m_threadId = 0; + + while (*m_ppThreadList != nullptr) + { + if (*m_ppThreadList == this) + { + *m_ppThreadList = m_pNext; + break; + } + m_ppThreadList = &((*m_ppThreadList)->m_pNext); + } + m_ppThreadList = nullptr; + } + + bool IsWatching() + { + return (m_threadId != 0); + } + + void SetWatching(bool shouldWatch) + { + if (shouldWatch && !IsWatching()) + { + StartWatching(); + } + else if (!shouldWatch && IsWatching()) + { + StopWatching(); + } + } + + static bool GetThreadContext(_Inout_ FailureInfo *pFailure, _In_opt_ ThreadFailureCallbackHolder *pCallback, _Out_writes_(callContextStringLength) _Post_z_ PSTR callContextString, _Pre_satisfies_(callContextStringLength > 0) size_t callContextStringLength) + { + *callContextString = '\0'; + bool foundContext = false; + if (pCallback != nullptr) + { + foundContext = GetThreadContext(pFailure, pCallback->m_pNext, callContextString, callContextStringLength); + + if (pCallback->m_pCallContext != nullptr) + { + auto &context = *pCallback->m_pCallContext; + + // We generate the next telemetry ID only when we've found an error (avoid always incrementing) + if (context.contextId == 0) + { + context.contextId = ::InterlockedIncrementNoFence(&s_telemetryId); + } + + if (pFailure->callContextOriginating.contextId == 0) + { + pFailure->callContextOriginating = context; + } + + pFailure->callContextCurrent = context; + + auto callContextStringEnd = callContextString + callContextStringLength; + callContextString += strlen(callContextString); + + if ((callContextStringEnd - callContextString) > 2) // room for at least the slash + null + { + *callContextString++ = '\\'; + auto nameSizeBytes = strlen(context.contextName) + 1; + size_t remainingBytes = static_cast(callContextStringEnd - callContextString); + auto copyBytes = (nameSizeBytes < remainingBytes) ? nameSizeBytes : remainingBytes; + memcpy_s(callContextString, remainingBytes, context.contextName, copyBytes); + *(callContextString + (copyBytes - 1)) = '\0'; + } + + return true; + } + } + return foundContext; + } + + static void GetContextAndNotifyFailure(_Inout_ FailureInfo *pFailure, _Out_writes_(callContextStringLength) _Post_z_ PSTR callContextString, _Pre_satisfies_(callContextStringLength > 0) size_t callContextStringLength) WI_NOEXCEPT + { + *callContextString = '\0'; + bool reportedTelemetry = false; + + ThreadFailureCallbackHolder **ppListeners = g_pThreadFailureCallbacks ? g_pThreadFailureCallbacks->GetLocal() : nullptr; + if ((ppListeners != nullptr) && (*ppListeners != nullptr)) + { + callContextString[0] = '\0'; + if (GetThreadContext(pFailure, *ppListeners, callContextString, callContextStringLength)) + { + pFailure->pszCallContext = callContextString; + } + + auto pNode = *ppListeners; + do + { + reportedTelemetry |= pNode->m_pCallback->NotifyFailure(*pFailure); + pNode = pNode->m_pNext; + } + while (pNode != nullptr); + } + + if (g_pfnTelemetryCallback != nullptr) + { + g_pfnTelemetryCallback(reportedTelemetry, *pFailure); + } + } + + ThreadFailureCallbackHolder(ThreadFailureCallbackHolder const &) = delete; + ThreadFailureCallbackHolder& operator=(ThreadFailureCallbackHolder const &) = delete; + + private: + static long volatile s_telemetryId; + + ThreadFailureCallbackHolder **m_ppThreadList; + IFailureCallback *m_pCallback; + ThreadFailureCallbackHolder *m_pNext; + DWORD m_threadId; + wil::CallContextInfo *m_pCallContext; + }; + + __declspec(selectany) long volatile ThreadFailureCallbackHolder::s_telemetryId = 1; + + template + class ThreadFailureCallbackFn final : public IFailureCallback + { + public: + explicit ThreadFailureCallbackFn(_In_opt_ CallContextInfo *pContext, _Inout_ TLambda &&errorFunction) WI_NOEXCEPT : + m_errorFunction(wistd::move(errorFunction)), + m_callbackHolder(this, pContext) + { + } + + ThreadFailureCallbackFn(_Inout_ ThreadFailureCallbackFn && other) WI_NOEXCEPT : + m_errorFunction(wistd::move(other.m_errorFunction)), + m_callbackHolder(this, other.m_callbackHolder.CallContextInfo()) + { + } + + bool NotifyFailure(FailureInfo const &failure) WI_NOEXCEPT + { + return m_errorFunction(failure); + } + + private: + ThreadFailureCallbackFn(_In_ ThreadFailureCallbackFn const &); + ThreadFailureCallbackFn & operator=(_In_ ThreadFailureCallbackFn const &); + + TLambda m_errorFunction; + ThreadFailureCallbackHolder m_callbackHolder; + }; + + + // returns true if telemetry was reported for this error + inline void __stdcall GetContextAndNotifyFailure(_Inout_ FailureInfo *pFailure, _Out_writes_(callContextStringLength) _Post_z_ PSTR callContextString, _Pre_satisfies_(callContextStringLength > 0) size_t callContextStringLength) WI_NOEXCEPT + { + ThreadFailureCallbackHolder::GetContextAndNotifyFailure(pFailure, callContextString, callContextStringLength); + + // Update the process-wide failure cache + wil::SetLastError(*pFailure); + } + + template void InitGlobalWithStorage(WilInitializeCommand state, void* storage, T*& global, TCtorArgs&&... args) + { + if ((state == WilInitializeCommand::Create) && !global) + { + global = ::new (storage) T(wistd::forward(args)...); + } + else if ((state == WilInitializeCommand::Destroy) && global) + { + global->~T(); + global = nullptr; + } + } + } + /// @endcond + + /** Modules that cannot use CRT-based static initialization may call this method from their entrypoint + instead. Disable the use of CRT-based initializers by defining RESULT_SUPPRESS_STATIC_INITIALIZERS + while compiling this header. Linking together libraries that disagree on this setting and calling + this method will behave correctly. It may be necessary to recompile all statically linked libraries + with the RESULT_SUPPRESS_... setting to eliminate all "LNK4201 - CRT section exists, but..." errors. + */ + inline void WilInitialize_Result(WilInitializeCommand state) + { + static unsigned char s_processLocalData[sizeof(*details_abi::g_pProcessLocalData)]; + static unsigned char s_threadFailureCallbacks[sizeof(*details::g_pThreadFailureCallbacks)]; + + details::InitGlobalWithStorage(state, s_processLocalData, details_abi::g_pProcessLocalData, "WilError_03"); + details::InitGlobalWithStorage(state, s_threadFailureCallbacks, details::g_pThreadFailureCallbacks); + + if (state == WilInitializeCommand::Create) + { + details::g_pfnGetContextAndNotifyFailure = details::GetContextAndNotifyFailure; + } + } + + /// @cond + namespace details + { +#ifndef RESULT_SUPPRESS_STATIC_INITIALIZERS + __declspec(selectany) ::wil::details_abi::ProcessLocalStorage<::wil::details_abi::ProcessLocalData> g_processLocalData("WilError_03"); + __declspec(selectany) ::wil::details_abi::ThreadLocalStorage g_threadFailureCallbacks; + + WI_HEADER_INITITALIZATION_FUNCTION(InitializeResultHeader, [] + { + g_pfnGetContextAndNotifyFailure = GetContextAndNotifyFailure; + ::wil::details_abi::g_pProcessLocalData = &g_processLocalData; + g_pThreadFailureCallbacks = &g_threadFailureCallbacks; + return 1; + }); +#endif + } + /// @endcond + + + // This helper functions much like scope_exit -- give it a lambda and get back a local object that can be used to + // catch all errors happening in your module through all WIL error handling mechanisms. The lambda will be called + // once for each error throw, error return, or error catch that is handled while the returned object is still in + // scope. Usage: + // + // auto monitor = wil::ThreadFailureCallback([](wil::FailureInfo const &failure) + // { + // // Write your code that logs or cares about failure details here... + // // It has access to HRESULT, filename, line number, etc through the failure param. + // }); + // + // As long as the returned 'monitor' object remains in scope, the lambda will continue to receive callbacks for any + // failures that occur in this module on the calling thread. Note that this will guarantee that the lambda will run + // for any failure that is through any of the WIL macros (THROW_XXX, RETURN_XXX, LOG_XXX, etc). + + template + inline wil::details::ThreadFailureCallbackFn ThreadFailureCallback(_Inout_ TLambda &&fnAtExit) WI_NOEXCEPT + { + return wil::details::ThreadFailureCallbackFn(nullptr, wistd::forward(fnAtExit)); + } + + + // Much like ThreadFailureCallback, this class will receive WIL failure notifications from the time it's instantiated + // until the time that it's destroyed. At any point during that time you can ask for the last failure that was seen + // by any of the WIL macros (RETURN_XXX, THROW_XXX, LOG_XXX, etc) on the current thread. + // + // This class is most useful when utilized as a member of an RAII class that's dedicated to providing logging or + // telemetry. In the destructor of that class, if the operation had not been completed successfully (it goes out of + // scope due to early return or exception unwind before success is acknowledged) then details about the last failure + // can be retrieved and appropriately logged. + // + // Usage: + // + // class MyLogger + // { + // public: + // MyLogger() : m_fComplete(false) {} + // ~MyLogger() + // { + // if (!m_fComplete) + // { + // FailureInfo *pFailure = m_cache.GetFailure(); + // if (pFailure != nullptr) + // { + // // Log information about pFailure (pFileure->hr, pFailure->pszFile, pFailure->uLineNumber, etc) + // } + // else + // { + // // It's possible that you get stack unwind from an exception that did NOT come through WIL + // // like (std::bad_alloc from the STL). Use a reasonable default like: HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION). + // } + // } + // } + // void Complete() { m_fComplete = true; } + // private: + // bool m_fComplete; + // ThreadFailureCache m_cache; + // }; + + class ThreadFailureCache final : + public details::IFailureCallback + { + public: + ThreadFailureCache() : + m_callbackHolder(this) + { + } + + ThreadFailureCache(ThreadFailureCache && rhs) WI_NOEXCEPT : + m_failure(wistd::move(rhs.m_failure)), + m_callbackHolder(this) + { + } + + ThreadFailureCache& operator=(ThreadFailureCache && rhs) WI_NOEXCEPT + { + m_failure = wistd::move(rhs.m_failure); + return *this; + } + + void WatchCurrentThread() + { + m_callbackHolder.StartWatching(); + } + + void IgnoreCurrentThread() + { + m_callbackHolder.StopWatching(); + } + + FailureInfo const *GetFailure() + { + return (FAILED(m_failure.GetFailureInfo().hr) ? &(m_failure.GetFailureInfo()) : nullptr); + } + + bool NotifyFailure(FailureInfo const &failure) WI_NOEXCEPT + { + // When we "cache" a failure, we bias towards trying to find the origin of the last HRESULT + // generated, so we ignore subsequent failures on the same error code (assuming propagation). + + if (failure.hr != m_failure.GetFailureInfo().hr) + { + m_failure.SetFailureInfo(failure); + } + return false; + } + + private: + StoredFailureInfo m_failure; + details::ThreadFailureCallbackHolder m_callbackHolder; + }; + +} // wil + +#pragma warning(pop) + +#endif diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/result_macros.h b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/result_macros.h new file mode 100644 index 0000000..eba52d9 --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/result_macros.h @@ -0,0 +1,6147 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_RESULTMACROS_INCLUDED +#define __WIL_RESULTMACROS_INCLUDED + +// WARNING: +// Code within this scope must satisfy both C99 and C++ + +#include "common.h" + +#if !defined(__WIL_MIN_KERNEL) && !defined(WIL_KERNEL_MODE) +#include +#endif + +// Setup the debug behavior +#ifndef RESULT_DEBUG +#if (DBG || defined(DEBUG) || defined(_DEBUG)) && !defined(NDEBUG) +#define RESULT_DEBUG +#endif +#endif + +/// @cond +#if defined(_PREFAST_) +#define __WI_ANALYSIS_ASSUME(_exp) _Analysis_assume_(_exp) +#else +#ifdef RESULT_DEBUG +#define __WI_ANALYSIS_ASSUME(_exp) ((void) 0) +#else +// NOTE: Clang does not currently handle __noop correctly and will fail to compile if the argument is not copy +// constructible. Therefore, use 'sizeof' for syntax validation. We don't do this universally for all compilers +// since lambdas are not allowed in unevaluated contexts prior to C++20, which does not appear to affect __noop +#if !defined(_MSC_VER) || defined(__clang__) +#define __WI_ANALYSIS_ASSUME(_exp) ((void)sizeof(_exp)) // Validate syntax on non-debug builds +#else +#define __WI_ANALYSIS_ASSUME(_exp) __noop(_exp) +#endif +#endif +#endif // _PREFAST_ + +//***************************************************************************** +// Assert Macros +//***************************************************************************** + +#ifdef RESULT_DEBUG +#if defined(__clang__) && defined(_WIN32) +// Clang currently mis-handles '__annotation' for 32-bit - https://bugs.llvm.org/show_bug.cgi?id=41890 +#define __WI_ASSERT_FAIL_ANNOTATION(msg) (void)0 +#else +#define __WI_ASSERT_FAIL_ANNOTATION(msg) __annotation(L"Debug", L"AssertFail", msg) +#endif + +#define WI_ASSERT(condition) (__WI_ANALYSIS_ASSUME(condition), ((!(condition)) ? (__WI_ASSERT_FAIL_ANNOTATION(L"" #condition), DbgRaiseAssertionFailure(), FALSE) : TRUE)) +#define WI_ASSERT_MSG(condition, msg) (__WI_ANALYSIS_ASSUME(condition), ((!(condition)) ? (__WI_ASSERT_FAIL_ANNOTATION(L##msg), DbgRaiseAssertionFailure(), FALSE) : TRUE)) +#define WI_ASSERT_NOASSUME WI_ASSERT +#define WI_ASSERT_MSG_NOASSUME WI_ASSERT_MSG +#define WI_VERIFY WI_ASSERT +#define WI_VERIFY_MSG WI_ASSERT_MSG +#define WI_VERIFY_SUCCEEDED(condition) WI_ASSERT(SUCCEEDED(condition)) +#else +#define WI_ASSERT(condition) (__WI_ANALYSIS_ASSUME(condition), 0) +#define WI_ASSERT_MSG(condition, msg) (__WI_ANALYSIS_ASSUME(condition), 0) +#define WI_ASSERT_NOASSUME(condition) ((void) 0) +#define WI_ASSERT_MSG_NOASSUME(condition, msg) ((void) 0) +#define WI_VERIFY(condition) (__WI_ANALYSIS_ASSUME(condition), ((condition) ? TRUE : FALSE)) +#define WI_VERIFY_MSG(condition, msg) (__WI_ANALYSIS_ASSUME(condition), ((condition) ? TRUE : FALSE)) +#define WI_VERIFY_SUCCEEDED(condition) (__WI_ANALYSIS_ASSUME(SUCCEEDED(condition)), ((SUCCEEDED(condition)) ? TRUE : FALSE)) +#endif // RESULT_DEBUG + +#if !defined(_NTDEF_) +typedef _Return_type_success_(return >= 0) LONG NTSTATUS; +#endif +#ifndef STATUS_SUCCESS +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) +#endif +#ifndef STATUS_UNSUCCESSFUL +#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) +#endif +#ifndef __NTSTATUS_FROM_WIN32 +#define __NTSTATUS_FROM_WIN32(x) ((NTSTATUS)(x) <= 0 ? ((NTSTATUS)(x)) : ((NTSTATUS) (((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16) | ERROR_SEVERITY_ERROR))) +#endif + +#ifndef WIL_AllocateMemory +#ifdef _KERNEL_MODE +#define WIL_AllocateMemory(SIZE) ExAllocatePoolWithTag(NonPagedPoolNx, SIZE, 'LIW') +WI_ODR_PRAGMA("WIL_AllocateMemory", "2") +#else +#define WIL_AllocateMemory(SIZE) HeapAlloc(GetProcessHeap(), 0, SIZE) +WI_ODR_PRAGMA("WIL_AllocateMemory", "1") +#endif +#else +WI_ODR_PRAGMA("WIL_AllocateMemory", "0") +#endif + +#ifndef WIL_FreeMemory +#ifdef _KERNEL_MODE +#define WIL_FreeMemory(MEM) ExFreePoolWithTag(MEM, 'LIW') +WI_ODR_PRAGMA("WIL_FreeMemory", "2") +#else +#define WIL_FreeMemory(MEM) HeapFree(GetProcessHeap(), 0, MEM) +WI_ODR_PRAGMA("WIL_FreeMemory", "1") +#endif +#else +WI_ODR_PRAGMA("WIL_FreeMemory", "0") +#endif + +// It would appear as though the C++17 "noexcept is part of the type system" update in MSVC has "infected" the behavior +// when compiling with C++14 (the default...), however the updated behavior for decltype understanding noexcept is _not_ +// present... So, work around it +#if __WI_LIBCPP_STD_VER >= 17 +#define WI_PFN_NOEXCEPT WI_NOEXCEPT +#else +#define WI_PFN_NOEXCEPT +#endif +/// @endcond + +#if defined(__cplusplus) && !defined(__WIL_MIN_KERNEL) && !defined(WIL_KERNEL_MODE) + +#include +#include // provides the _ReturnAddress() intrinsic +#include // provides 'operator new', 'std::nothrow', etc. +#if defined(WIL_ENABLE_EXCEPTIONS) && !defined(WIL_SUPPRESS_NEW) +#include // provides std::bad_alloc in the windows and public CRT headers +#endif + +#pragma warning(push) +#pragma warning(disable:4714 6262) // __forceinline not honored, stack size + +//***************************************************************************** +// Behavioral setup (error handling macro configuration) +//***************************************************************************** +// Set any of the following macros to the values given below before including Result.h to +// control the error handling macro's trade-offs between diagnostics and performance + +// RESULT_DIAGNOSTICS_LEVEL +// This define controls the level of diagnostic instrumentation that is built into the binary as a +// byproduct of using the macros. The amount of diagnostic instrumentation that is supplied is +// a trade-off between diagnosibility of issues and code size and performance. The modes are: +// 0 - No diagnostics, smallest & fastest (subject to tail-merge) +// 1 - No diagnostics, unique call sites for each macro (defeat's tail-merge) +// 2 - Line number +// 3 - Line number + source filename +// 4 - Line number + source filename + function name +// 5 - Line number + source filename + function name + code within the macro +// By default, mode 3 is used in free builds and mode 5 is used in checked builds. Note that the +// _ReturnAddress() will always be available through all modes when possible. + +// RESULT_INCLUDE_CALLER_RETURNADDRESS +// This controls whether or not the _ReturnAddress() of the function that includes the macro will +// be reported to telemetry. Note that this is in addition to the _ReturnAddress() of the actual +// macro position (which is always reported). The values are: +// 0 - The address is not included +// 1 - The address is included +// The default value is '1'. + +// RESULT_INLINE_ERROR_TESTS +// For conditional macros (other than RETURN_XXX), this controls whether branches will be evaluated +// within the call containing the macro or will be forced into the function called by the macros. +// Pushing branching into the called function reduces code size and the number of unique branches +// evaluated, but increases the instruction count executed per macro. +// 0 - Branching will not happen inline to the macros +// 1 - Branching is pushed into the calling function via __forceinline +// The default value is '1'. Note that XXX_MSG functions are always effectively mode '0' due to the +// compiler's unwillingness to inline var-arg functions. + +// RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST +// RESULT_INCLUDE_CALLER_RETURNADDRESS_FAIL_FAST +// RESULT_INLINE_ERROR_TESTS_FAIL_FAST +// These defines are identical to those above in form/function, but only applicable to fail fast error +// handling allowing a process to have different diagnostic information and performance characteristics +// for fail fast than for other error handling given the different reporting infrastructure (Watson +// vs Telemetry). + +// Set the default diagnostic mode +// Note that RESULT_DEBUG_INFO and RESULT_SUPPRESS_DEBUG_INFO are older deprecated models of controlling mode +#ifndef RESULT_DIAGNOSTICS_LEVEL +#if (defined(RESULT_DEBUG) || defined(RESULT_DEBUG_INFO)) && !defined(RESULT_SUPPRESS_DEBUG_INFO) +#define RESULT_DIAGNOSTICS_LEVEL 5 +#else +#define RESULT_DIAGNOSTICS_LEVEL 3 +#endif +#endif +#ifndef RESULT_INCLUDE_CALLER_RETURNADDRESS +#define RESULT_INCLUDE_CALLER_RETURNADDRESS 1 +#endif +#ifndef RESULT_INLINE_ERROR_TESTS +#define RESULT_INLINE_ERROR_TESTS 1 +#endif +#ifndef RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST +#define RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST RESULT_DIAGNOSTICS_LEVEL +#endif +#ifndef RESULT_INCLUDE_CALLER_RETURNADDRESS_FAIL_FAST +#define RESULT_INCLUDE_CALLER_RETURNADDRESS_FAIL_FAST RESULT_INCLUDE_CALLER_RETURNADDRESS +#endif +#ifndef RESULT_INLINE_ERROR_TESTS_FAIL_FAST +#define RESULT_INLINE_ERROR_TESTS_FAIL_FAST RESULT_INLINE_ERROR_TESTS +#endif + + +//***************************************************************************** +// Win32 specific error macros +//***************************************************************************** + +#define FAILED_WIN32(win32err) ((win32err) != 0) +#define SUCCEEDED_WIN32(win32err) ((win32err) == 0) + + +//***************************************************************************** +// NT_STATUS specific error macros +//***************************************************************************** + +#define FAILED_NTSTATUS(status) (((NTSTATUS)(status)) < 0) +#define SUCCEEDED_NTSTATUS(status) (((NTSTATUS)(status)) >= 0) + + +//***************************************************************************** +// Testing helpers - redefine to run unit tests against fail fast +//***************************************************************************** + +#ifndef RESULT_NORETURN +#define RESULT_NORETURN __declspec(noreturn) +#endif +#ifndef RESULT_NORETURN_NULL +#define RESULT_NORETURN_NULL _Ret_notnull_ +#endif +#ifndef RESULT_NORETURN_RESULT +#define RESULT_NORETURN_RESULT(expr) (void)(expr); +#endif + +//***************************************************************************** +// Helpers to setup the macros and functions used below... do not directly use. +//***************************************************************************** + +/// @cond +#define __R_DIAGNOSTICS(diagnostics) diagnostics.returnAddress, diagnostics.line, diagnostics.file, nullptr, nullptr +#define __R_DIAGNOSTICS_RA(diagnostics, address) diagnostics.returnAddress, diagnostics.line, diagnostics.file, nullptr, nullptr, address +#define __R_FN_PARAMS_FULL _In_opt_ void* callerReturnAddress, unsigned int lineNumber, _In_opt_ PCSTR fileName, _In_opt_ PCSTR functionName, _In_opt_ PCSTR code, void* returnAddress +#define __R_FN_LOCALS_FULL_RA void* callerReturnAddress = nullptr; unsigned int lineNumber = 0; PCSTR fileName = nullptr; PCSTR functionName = nullptr; PCSTR code = nullptr; void* returnAddress = _ReturnAddress(); +// NOTE: This BEGINs the common macro handling (__R_ prefix) for non-fail fast handled cases +// This entire section will be repeated below for fail fast (__RFF_ prefix). +#define __R_COMMA , +#define __R_FN_CALL_FULL callerReturnAddress, lineNumber, fileName, functionName, code, returnAddress +#define __R_FN_CALL_FULL_RA callerReturnAddress, lineNumber, fileName, functionName, code, _ReturnAddress() +// The following macros assemble the varying amount of data we want to collect from the macros, treating it uniformly +#if (RESULT_DIAGNOSTICS_LEVEL >= 2) // line number +#define __R_IF_LINE(term) term +#define __R_IF_NOT_LINE(term) +#define __R_IF_COMMA , +#define __R_LINE_VALUE static_cast(__LINE__) +#else +#define __R_IF_LINE(term) +#define __R_IF_NOT_LINE(term) term +#define __R_IF_COMMA +#define __R_LINE_VALUE static_cast(0) +#endif +#if (RESULT_DIAGNOSTICS_LEVEL >= 3) // line number + file name +#define __R_IF_FILE(term) term +#define __R_IF_NOT_FILE(term) +#define __R_FILE_VALUE __FILE__ +#else +#define __R_IF_FILE(term) +#define __R_IF_NOT_FILE(term) term +#define __R_FILE_VALUE nullptr +#endif +#if (RESULT_DIAGNOSTICS_LEVEL >= 4) // line number + file name + function name +#define __R_IF_FUNCTION(term) term +#define __R_IF_NOT_FUNCTION(term) +#else +#define __R_IF_FUNCTION(term) +#define __R_IF_NOT_FUNCTION(term) term +#endif +#if (RESULT_DIAGNOSTICS_LEVEL >= 5) // line number + file name + function name + macro code +#define __R_IF_CODE(term) term +#define __R_IF_NOT_CODE(term) +#else +#define __R_IF_CODE(term) +#define __R_IF_NOT_CODE(term) term +#endif +#if (RESULT_INCLUDE_CALLER_RETURNADDRESS == 1) +#define __R_IF_CALLERADDRESS(term) term +#define __R_IF_NOT_CALLERADDRESS(term) +#define __R_CALLERADDRESS_VALUE _ReturnAddress() +#else +#define __R_IF_CALLERADDRESS(term) +#define __R_IF_NOT_CALLERADDRESS(term) term +#define __R_CALLERADDRESS_VALUE nullptr +#endif +#if (RESULT_INCLUDE_CALLER_RETURNADDRESS == 1) || (RESULT_DIAGNOSTICS_LEVEL >= 2) +#define __R_IF_TRAIL_COMMA , +#else +#define __R_IF_TRAIL_COMMA +#endif +// Assemble the varying amounts of data into a single macro +#define __R_INFO_ONLY(CODE) __R_IF_CALLERADDRESS(_ReturnAddress() __R_IF_COMMA) __R_IF_LINE(__R_LINE_VALUE) __R_IF_FILE(__R_COMMA __R_FILE_VALUE) __R_IF_FUNCTION(__R_COMMA __FUNCTION__) __R_IF_CODE(__R_COMMA CODE) +#define __R_INFO(CODE) __R_INFO_ONLY(CODE) __R_IF_TRAIL_COMMA +#define __R_INFO_NOFILE_ONLY(CODE) __R_IF_CALLERADDRESS(_ReturnAddress() __R_IF_COMMA) __R_IF_LINE(__R_LINE_VALUE) __R_IF_FILE(__R_COMMA "wil") __R_IF_FUNCTION(__R_COMMA __FUNCTION__) __R_IF_CODE(__R_COMMA CODE) +#define __R_INFO_NOFILE(CODE) __R_INFO_NOFILE_ONLY(CODE) __R_IF_TRAIL_COMMA +#define __R_FN_PARAMS_ONLY __R_IF_CALLERADDRESS(void* callerReturnAddress __R_IF_COMMA) __R_IF_LINE(unsigned int lineNumber) __R_IF_FILE(__R_COMMA _In_opt_ PCSTR fileName) __R_IF_FUNCTION(__R_COMMA _In_opt_ PCSTR functionName) __R_IF_CODE(__R_COMMA _In_opt_ PCSTR code) +#define __R_FN_PARAMS __R_FN_PARAMS_ONLY __R_IF_TRAIL_COMMA +#define __R_FN_CALL_ONLY __R_IF_CALLERADDRESS(callerReturnAddress __R_IF_COMMA) __R_IF_LINE(lineNumber) __R_IF_FILE(__R_COMMA fileName) __R_IF_FUNCTION(__R_COMMA functionName) __R_IF_CODE(__R_COMMA code) +#define __R_FN_CALL __R_FN_CALL_ONLY __R_IF_TRAIL_COMMA +#define __R_FN_LOCALS __R_IF_NOT_CALLERADDRESS(void* callerReturnAddress = nullptr;) __R_IF_NOT_LINE(unsigned int lineNumber = 0;) __R_IF_NOT_FILE(PCSTR fileName = nullptr;) __R_IF_NOT_FUNCTION(PCSTR functionName = nullptr;) __R_IF_NOT_CODE(PCSTR code = nullptr;) +#define __R_FN_LOCALS_RA __R_IF_NOT_CALLERADDRESS(void* callerReturnAddress = nullptr;) __R_IF_NOT_LINE(unsigned int lineNumber = 0;) __R_IF_NOT_FILE(PCSTR fileName = nullptr;) __R_IF_NOT_FUNCTION(PCSTR functionName = nullptr;) __R_IF_NOT_CODE(PCSTR code = nullptr;) void* returnAddress = _ReturnAddress(); +#define __R_FN_UNREFERENCED __R_IF_CALLERADDRESS((void)callerReturnAddress;) __R_IF_LINE((void)lineNumber;) __R_IF_FILE((void)fileName;) __R_IF_FUNCTION((void)functionName;) __R_IF_CODE((void)code;) +// 1) Direct Methods +// * Called Directly by Macros +// * Always noinline +// * May be template-driven to create unique call sites if (RESULT_DIAGNOSTICS_LEVEL == 1) +#if (RESULT_DIAGNOSTICS_LEVEL == 1) +#define __R_DIRECT_METHOD(RetType, MethodName) template inline __declspec(noinline) RetType MethodName +#define __R_DIRECT_NORET_METHOD(RetType, MethodName) template inline __declspec(noinline) RESULT_NORETURN RetType MethodName +#else +#define __R_DIRECT_METHOD(RetType, MethodName) inline __declspec(noinline) RetType MethodName +#define __R_DIRECT_NORET_METHOD(RetType, MethodName) inline __declspec(noinline) RESULT_NORETURN RetType MethodName +#endif +#define __R_DIRECT_FN_PARAMS __R_FN_PARAMS +#define __R_DIRECT_FN_PARAMS_ONLY __R_FN_PARAMS_ONLY +#define __R_DIRECT_FN_CALL __R_FN_CALL_FULL_RA __R_COMMA +#define __R_DIRECT_FN_CALL_ONLY __R_FN_CALL_FULL_RA +// 2) Internal Methods +// * Only called by Conditional routines +// * 'inline' when (RESULT_INLINE_ERROR_TESTS = 0 and RESULT_DIAGNOSTICS_LEVEL != 1), otherwise noinline (directly called by code when branching is forceinlined) +// * May be template-driven to create unique call sites if (RESULT_DIAGNOSTICS_LEVEL == 1 and RESULT_INLINE_ERROR_TESTS = 1) +#if (RESULT_DIAGNOSTICS_LEVEL == 1) +#define __R_INTERNAL_NOINLINE_METHOD(MethodName) inline __declspec(noinline) void MethodName +#define __R_INTERNAL_NOINLINE_NORET_METHOD(MethodName) inline __declspec(noinline) RESULT_NORETURN void MethodName +#define __R_INTERNAL_INLINE_METHOD(MethodName) template inline __declspec(noinline) void MethodName +#define __R_INTERNAL_INLINE_NORET_METHOD(MethodName) template inline __declspec(noinline) RESULT_NORETURN void MethodName +#define __R_CALL_INTERNAL_INLINE_METHOD(MethodName) MethodName +#else +#define __R_INTERNAL_NOINLINE_METHOD(MethodName) inline void MethodName +#define __R_INTERNAL_NOINLINE_NORET_METHOD(MethodName) inline RESULT_NORETURN void MethodName +#define __R_INTERNAL_INLINE_METHOD(MethodName) inline __declspec(noinline) void MethodName +#define __R_INTERNAL_INLINE_NORET_METHOD(MethodName) inline __declspec(noinline) RESULT_NORETURN void MethodName +#define __R_CALL_INTERNAL_INLINE_METHOD(MethodName) MethodName +#endif +#define __R_CALL_INTERNAL_NOINLINE_METHOD(MethodName) MethodName +#define __R_INTERNAL_NOINLINE_FN_PARAMS __R_FN_PARAMS void* returnAddress __R_COMMA +#define __R_INTERNAL_NOINLINE_FN_PARAMS_ONLY __R_FN_PARAMS void* returnAddress +#define __R_INTERNAL_NOINLINE_FN_CALL __R_FN_CALL_FULL __R_COMMA +#define __R_INTERNAL_NOINLINE_FN_CALL_ONLY __R_FN_CALL_FULL +#define __R_INTERNAL_INLINE_FN_PARAMS __R_FN_PARAMS +#define __R_INTERNAL_INLINE_FN_PARAMS_ONLY __R_FN_PARAMS_ONLY +#define __R_INTERNAL_INLINE_FN_CALL __R_FN_CALL_FULL_RA __R_COMMA +#define __R_INTERNAL_INLINE_FN_CALL_ONLY __R_FN_CALL_FULL_RA +#if (RESULT_INLINE_ERROR_TESTS == 0) +#define __R_INTERNAL_METHOD __R_INTERNAL_NOINLINE_METHOD +#define __R_INTERNAL_NORET_METHOD __R_INTERNAL_NOINLINE_NORET_METHOD +#define __R_CALL_INTERNAL_METHOD __R_CALL_INTERNAL_NOINLINE_METHOD +#define __R_INTERNAL_FN_PARAMS __R_INTERNAL_NOINLINE_FN_PARAMS +#define __R_INTERNAL_FN_PARAMS_ONLY __R_INTERNAL_NOINLINE_FN_PARAMS_ONLY +#define __R_INTERNAL_FN_CALL __R_INTERNAL_NOINLINE_FN_CALL +#define __R_INTERNAL_FN_CALL_ONLY __R_INTERNAL_NOINLINE_FN_CALL_ONLY +#else +#define __R_INTERNAL_METHOD __R_INTERNAL_INLINE_METHOD +#define __R_INTERNAL_NORET_METHOD __R_INTERNAL_INLINE_NORET_METHOD +#define __R_CALL_INTERNAL_METHOD __R_CALL_INTERNAL_INLINE_METHOD +#define __R_INTERNAL_FN_PARAMS __R_INTERNAL_INLINE_FN_PARAMS +#define __R_INTERNAL_FN_PARAMS_ONLY __R_INTERNAL_INLINE_FN_PARAMS_ONLY +#define __R_INTERNAL_FN_CALL __R_INTERNAL_INLINE_FN_CALL +#define __R_INTERNAL_FN_CALL_ONLY __R_INTERNAL_INLINE_FN_CALL_ONLY +#endif +// 3) Conditional Methods +// * Called Directly by Macros +// * May be noinline or __forceinline depending upon (RESULT_INLINE_ERROR_TESTS) +// * May be template-driven to create unique call sites if (RESULT_DIAGNOSTICS_LEVEL == 1) +#if (RESULT_DIAGNOSTICS_LEVEL == 1) +#define __R_CONDITIONAL_NOINLINE_METHOD(RetType, MethodName) template inline __declspec(noinline) RetType MethodName +#define __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(RetType, MethodName) inline __declspec(noinline) RetType MethodName +#define __R_CONDITIONAL_INLINE_METHOD(RetType, MethodName) template __forceinline RetType MethodName +#define __R_CONDITIONAL_INLINE_TEMPLATE_METHOD(RetType, MethodName) __forceinline RetType MethodName +#define __R_CONDITIONAL_PARTIAL_TEMPLATE unsigned int optimizerCounter __R_COMMA +#else +#define __R_CONDITIONAL_NOINLINE_METHOD(RetType, MethodName) inline __declspec(noinline) RetType MethodName +#define __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(RetType, MethodName) inline __declspec(noinline) RetType MethodName +#define __R_CONDITIONAL_INLINE_METHOD(RetType, MethodName) __forceinline RetType MethodName +#define __R_CONDITIONAL_INLINE_TEMPLATE_METHOD(RetType, MethodName) __forceinline RetType MethodName +#define __R_CONDITIONAL_PARTIAL_TEMPLATE +#endif +#define __R_CONDITIONAL_NOINLINE_FN_CALL __R_FN_CALL _ReturnAddress() __R_COMMA +#define __R_CONDITIONAL_NOINLINE_FN_CALL_ONLY __R_FN_CALL _ReturnAddress() +#define __R_CONDITIONAL_INLINE_FN_CALL __R_FN_CALL +#define __R_CONDITIONAL_INLINE_FN_CALL_ONLY __R_FN_CALL_ONLY +#if (RESULT_INLINE_ERROR_TESTS == 0) +#define __R_CONDITIONAL_METHOD __R_CONDITIONAL_NOINLINE_METHOD +#define __R_CONDITIONAL_TEMPLATE_METHOD __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD +#define __R_CONDITIONAL_FN_CALL __R_CONDITIONAL_NOINLINE_FN_CALL +#define __R_CONDITIONAL_FN_CALL_ONLY __R_CONDITIONAL_NOINLINE_FN_CALL_ONLY +#else +#define __R_CONDITIONAL_METHOD __R_CONDITIONAL_INLINE_METHOD +#define __R_CONDITIONAL_TEMPLATE_METHOD __R_CONDITIONAL_INLINE_TEMPLATE_METHOD +#define __R_CONDITIONAL_FN_CALL __R_CONDITIONAL_INLINE_FN_CALL +#define __R_CONDITIONAL_FN_CALL_ONLY __R_CONDITIONAL_INLINE_FN_CALL_ONLY +#endif +#define __R_CONDITIONAL_FN_PARAMS __R_FN_PARAMS +#define __R_CONDITIONAL_FN_PARAMS_ONLY __R_FN_PARAMS_ONLY +// Macro call-site helpers +#define __R_NS_ASSEMBLE2(ri, rd) in##ri##diag##rd // Differing internal namespaces eliminate ODR violations between modes +#define __R_NS_ASSEMBLE(ri, rd) __R_NS_ASSEMBLE2(ri, rd) +#define __R_NS_NAME __R_NS_ASSEMBLE(RESULT_INLINE_ERROR_TESTS, RESULT_DIAGNOSTICS_LEVEL) +#define __R_NS wil::details::__R_NS_NAME +#if (RESULT_DIAGNOSTICS_LEVEL == 1) +#define __R_FN(MethodName) __R_NS:: MethodName <__COUNTER__> +#else +#define __R_FN(MethodName) __R_NS:: MethodName +#endif +// NOTE: This ENDs the common macro handling (__R_ prefix) for non-fail fast handled cases +// This entire section is repeated below for fail fast (__RFF_ prefix). For ease of editing this section, the +// process is to copy/paste, and search and replace (__R_ -> __RFF_), (RESULT_DIAGNOSTICS_LEVEL -> RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST), +// (RESULT_INLINE_ERROR_TESTS -> RESULT_INLINE_ERROR_TESTS_FAIL_FAST) and (RESULT_INCLUDE_CALLER_RETURNADDRESS -> RESULT_INCLUDE_CALLER_RETURNADDRESS_FAIL_FAST) +#define __RFF_COMMA , +#define __RFF_FN_CALL_FULL callerReturnAddress, lineNumber, fileName, functionName, code, returnAddress +#define __RFF_FN_CALL_FULL_RA callerReturnAddress, lineNumber, fileName, functionName, code, _ReturnAddress() +// The following macros assemble the varying amount of data we want to collect from the macros, treating it uniformly +#if (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST >= 2) // line number +#define __RFF_IF_LINE(term) term +#define __RFF_IF_NOT_LINE(term) +#define __RFF_IF_COMMA , +#else +#define __RFF_IF_LINE(term) +#define __RFF_IF_NOT_LINE(term) term +#define __RFF_IF_COMMA +#endif +#if (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST >= 3) // line number + file name +#define __RFF_IF_FILE(term) term +#define __RFF_IF_NOT_FILE(term) +#else +#define __RFF_IF_FILE(term) +#define __RFF_IF_NOT_FILE(term) term +#endif +#if (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST >= 4) // line number + file name + function name +#define __RFF_IF_FUNCTION(term) term +#define __RFF_IF_NOT_FUNCTION(term) +#else +#define __RFF_IF_FUNCTION(term) +#define __RFF_IF_NOT_FUNCTION(term) term +#endif +#if (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST >= 5) // line number + file name + function name + macro code +#define __RFF_IF_CODE(term) term +#define __RFF_IF_NOT_CODE(term) +#else +#define __RFF_IF_CODE(term) +#define __RFF_IF_NOT_CODE(term) term +#endif +#if (RESULT_INCLUDE_CALLER_RETURNADDRESS_FAIL_FAST == 1) +#define __RFF_IF_CALLERADDRESS(term) term +#define __RFF_IF_NOT_CALLERADDRESS(term) +#else +#define __RFF_IF_CALLERADDRESS(term) +#define __RFF_IF_NOT_CALLERADDRESS(term) term +#endif +#if (RESULT_INCLUDE_CALLER_RETURNADDRESS_FAIL_FAST == 1) || (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST >= 2) +#define __RFF_IF_TRAIL_COMMA , +#else +#define __RFF_IF_TRAIL_COMMA +#endif +// Assemble the varying amounts of data into a single macro +#define __RFF_INFO_ONLY(CODE) __RFF_IF_CALLERADDRESS(_ReturnAddress() __RFF_IF_COMMA) __RFF_IF_LINE(__R_LINE_VALUE) __RFF_IF_FILE(__RFF_COMMA __R_FILE_VALUE) __RFF_IF_FUNCTION(__RFF_COMMA __FUNCTION__) __RFF_IF_CODE(__RFF_COMMA CODE) +#define __RFF_INFO(CODE) __RFF_INFO_ONLY(CODE) __RFF_IF_TRAIL_COMMA +#define __RFF_INFO_NOFILE_ONLY(CODE) __RFF_IF_CALLERADDRESS(_ReturnAddress() __RFF_IF_COMMA) __RFF_IF_LINE(__R_LINE_VALUE) __RFF_IF_FILE(__RFF_COMMA "wil") __RFF_IF_FUNCTION(__RFF_COMMA __FUNCTION__) __RFF_IF_CODE(__RFF_COMMA CODE) +#define __RFF_INFO_NOFILE(CODE) __RFF_INFO_NOFILE_ONLY(CODE) __RFF_IF_TRAIL_COMMA +#define __RFF_FN_PARAMS_ONLY __RFF_IF_CALLERADDRESS(void* callerReturnAddress __RFF_IF_COMMA) __RFF_IF_LINE(unsigned int lineNumber) __RFF_IF_FILE(__RFF_COMMA _In_opt_ PCSTR fileName) __RFF_IF_FUNCTION(__RFF_COMMA _In_opt_ PCSTR functionName) __RFF_IF_CODE(__RFF_COMMA _In_opt_ PCSTR code) +#define __RFF_FN_PARAMS __RFF_FN_PARAMS_ONLY __RFF_IF_TRAIL_COMMA +#define __RFF_FN_CALL_ONLY __RFF_IF_CALLERADDRESS(callerReturnAddress __RFF_IF_COMMA) __RFF_IF_LINE(lineNumber) __RFF_IF_FILE(__RFF_COMMA fileName) __RFF_IF_FUNCTION(__RFF_COMMA functionName) __RFF_IF_CODE(__RFF_COMMA code) +#define __RFF_FN_CALL __RFF_FN_CALL_ONLY __RFF_IF_TRAIL_COMMA +#define __RFF_FN_LOCALS __RFF_IF_NOT_CALLERADDRESS(void* callerReturnAddress = nullptr;) __RFF_IF_NOT_LINE(unsigned int lineNumber = 0;) __RFF_IF_NOT_FILE(PCSTR fileName = nullptr;) __RFF_IF_NOT_FUNCTION(PCSTR functionName = nullptr;) __RFF_IF_NOT_CODE(PCSTR code = nullptr;) +#define __RFF_FN_UNREFERENCED __RFF_IF_CALLERADDRESS(callerReturnAddress;) __RFF_IF_LINE(lineNumber;) __RFF_IF_FILE(fileName;) __RFF_IF_FUNCTION(functionName;) __RFF_IF_CODE(code;) +// 1) Direct Methods +// * Called Directly by Macros +// * Always noinline +// * May be template-driven to create unique call sites if (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST == 1) +#if (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST == 1) +#define __RFF_DIRECT_METHOD(RetType, MethodName) template inline __declspec(noinline) RetType MethodName +#define __RFF_DIRECT_NORET_METHOD(RetType, MethodName) template inline __declspec(noinline) RESULT_NORETURN RetType MethodName +#else +#define __RFF_DIRECT_METHOD(RetType, MethodName) inline __declspec(noinline) RetType MethodName +#define __RFF_DIRECT_NORET_METHOD(RetType, MethodName) inline __declspec(noinline) RESULT_NORETURN RetType MethodName +#endif +#define __RFF_DIRECT_FN_PARAMS __RFF_FN_PARAMS +#define __RFF_DIRECT_FN_PARAMS_ONLY __RFF_FN_PARAMS_ONLY +#define __RFF_DIRECT_FN_CALL __RFF_FN_CALL_FULL_RA __RFF_COMMA +#define __RFF_DIRECT_FN_CALL_ONLY __RFF_FN_CALL_FULL_RA +// 2) Internal Methods +// * Only called by Conditional routines +// * 'inline' when (RESULT_INLINE_ERROR_TESTS_FAIL_FAST = 0 and RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST != 1), otherwise noinline (directly called by code when branching is forceinlined) +// * May be template-driven to create unique call sites if (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST == 1 and RESULT_INLINE_ERROR_TESTS_FAIL_FAST = 1) +#if (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST == 1) +#define __RFF_INTERNAL_NOINLINE_METHOD(MethodName) inline __declspec(noinline) void MethodName +#define __RFF_INTERNAL_NOINLINE_NORET_METHOD(MethodName) inline __declspec(noinline) RESULT_NORETURN void MethodName +#define __RFF_INTERNAL_INLINE_METHOD(MethodName) template inline __declspec(noinline) void MethodName +#define __RFF_INTERNAL_INLINE_NORET_METHOD(MethodName) template inline __declspec(noinline) RESULT_NORETURN void MethodName +#define __RFF_CALL_INTERNAL_INLINE_METHOD(MethodName) MethodName +#else +#define __RFF_INTERNAL_NOINLINE_METHOD(MethodName) inline void MethodName +#define __RFF_INTERNAL_NOINLINE_NORET_METHOD(MethodName) inline RESULT_NORETURN void MethodName +#define __RFF_INTERNAL_INLINE_METHOD(MethodName) inline __declspec(noinline) void MethodName +#define __RFF_INTERNAL_INLINE_NORET_METHOD(MethodName) inline __declspec(noinline) RESULT_NORETURN void MethodName +#define __RFF_CALL_INTERNAL_INLINE_METHOD(MethodName) MethodName +#endif +#define __RFF_CALL_INTERNAL_NOINLINE_METHOD(MethodName) MethodName +#define __RFF_INTERNAL_NOINLINE_FN_PARAMS __RFF_FN_PARAMS void* returnAddress __RFF_COMMA +#define __RFF_INTERNAL_NOINLINE_FN_PARAMS_ONLY __RFF_FN_PARAMS void* returnAddress +#define __RFF_INTERNAL_NOINLINE_FN_CALL __RFF_FN_CALL_FULL __RFF_COMMA +#define __RFF_INTERNAL_NOINLINE_FN_CALL_ONLY __RFF_FN_CALL_FULL +#define __RFF_INTERNAL_INLINE_FN_PARAMS __RFF_FN_PARAMS +#define __RFF_INTERNAL_INLINE_FN_PARAMS_ONLY __RFF_FN_PARAMS_ONLY +#define __RFF_INTERNAL_INLINE_FN_CALL __RFF_FN_CALL_FULL_RA __RFF_COMMA +#define __RFF_INTERNAL_INLINE_FN_CALL_ONLY __RFF_FN_CALL_FULL_RA +#if (RESULT_INLINE_ERROR_TESTS_FAIL_FAST == 0) +#define __RFF_INTERNAL_METHOD __RFF_INTERNAL_NOINLINE_METHOD +#define __RFF_INTERNAL_NORET_METHOD __RFF_INTERNAL_NOINLINE_NORET_METHOD +#define __RFF_CALL_INTERNAL_METHOD __RFF_CALL_INTERNAL_NOINLINE_METHOD +#define __RFF_INTERNAL_FN_PARAMS __RFF_INTERNAL_NOINLINE_FN_PARAMS +#define __RFF_INTERNAL_FN_PARAMS_ONLY __RFF_INTERNAL_NOINLINE_FN_PARAMS_ONLY +#define __RFF_INTERNAL_FN_CALL __RFF_INTERNAL_NOINLINE_FN_CALL +#define __RFF_INTERNAL_FN_CALL_ONLY __RFF_INTERNAL_NOINLINE_FN_CALL_ONLY +#else +#define __RFF_INTERNAL_METHOD __RFF_INTERNAL_INLINE_METHOD +#define __RFF_INTERNAL_NORET_METHOD __RFF_INTERNAL_INLINE_NORET_METHOD +#define __RFF_CALL_INTERNAL_METHOD __RFF_CALL_INTERNAL_INLINE_METHOD +#define __RFF_INTERNAL_FN_PARAMS __RFF_INTERNAL_INLINE_FN_PARAMS +#define __RFF_INTERNAL_FN_PARAMS_ONLY __RFF_INTERNAL_INLINE_FN_PARAMS_ONLY +#define __RFF_INTERNAL_FN_CALL __RFF_INTERNAL_INLINE_FN_CALL +#define __RFF_INTERNAL_FN_CALL_ONLY __RFF_INTERNAL_INLINE_FN_CALL_ONLY +#endif +// 3) Conditional Methods +// * Called Directly by Macros +// * May be noinline or __forceinline depending upon (RESULT_INLINE_ERROR_TESTS_FAIL_FAST) +// * May be template-driven to create unique call sites if (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST == 1) +#if (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST == 1) +#define __RFF_CONDITIONAL_NOINLINE_METHOD(RetType, MethodName) template inline __declspec(noinline) RetType MethodName +#define __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(RetType, MethodName) inline __declspec(noinline) RetType MethodName +#define __RFF_CONDITIONAL_INLINE_METHOD(RetType, MethodName) template __forceinline RetType MethodName +#define __RFF_CONDITIONAL_INLINE_TEMPLATE_METHOD(RetType, MethodName) __forceinline RetType MethodName +#define __RFF_CONDITIONAL_PARTIAL_TEMPLATE unsigned int optimizerCounter __RFF_COMMA +#else +#define __RFF_CONDITIONAL_NOINLINE_METHOD(RetType, MethodName) inline __declspec(noinline) RetType MethodName +#define __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(RetType, MethodName) inline __declspec(noinline) RetType MethodName +#define __RFF_CONDITIONAL_INLINE_METHOD(RetType, MethodName) __forceinline RetType MethodName +#define __RFF_CONDITIONAL_INLINE_TEMPLATE_METHOD(RetType, MethodName) __forceinline RetType MethodName +#define __RFF_CONDITIONAL_PARTIAL_TEMPLATE +#endif +#define __RFF_CONDITIONAL_NOINLINE_FN_CALL __RFF_FN_CALL _ReturnAddress() __RFF_COMMA +#define __RFF_CONDITIONAL_NOINLINE_FN_CALL_ONLY __RFF_FN_CALL _ReturnAddress() +#define __RFF_CONDITIONAL_INLINE_FN_CALL __RFF_FN_CALL +#define __RFF_CONDITIONAL_INLINE_FN_CALL_ONLY __RFF_FN_CALL_ONLY +#if (RESULT_INLINE_ERROR_TESTS_FAIL_FAST == 0) +#define __RFF_CONDITIONAL_METHOD __RFF_CONDITIONAL_NOINLINE_METHOD +#define __RFF_CONDITIONAL_TEMPLATE_METHOD __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD +#define __RFF_CONDITIONAL_FN_CALL __RFF_CONDITIONAL_NOINLINE_FN_CALL +#define __RFF_CONDITIONAL_FN_CALL_ONLY __RFF_CONDITIONAL_NOINLINE_FN_CALL_ONLY +#else +#define __RFF_CONDITIONAL_METHOD __RFF_CONDITIONAL_INLINE_METHOD +#define __RFF_CONDITIONAL_TEMPLATE_METHOD __RFF_CONDITIONAL_INLINE_TEMPLATE_METHOD +#define __RFF_CONDITIONAL_FN_CALL __RFF_CONDITIONAL_INLINE_FN_CALL +#define __RFF_CONDITIONAL_FN_CALL_ONLY __RFF_CONDITIONAL_INLINE_FN_CALL_ONLY +#endif +#define __RFF_CONDITIONAL_FN_PARAMS __RFF_FN_PARAMS +#define __RFF_CONDITIONAL_FN_PARAMS_ONLY __RFF_FN_PARAMS_ONLY +// Macro call-site helpers +#define __RFF_NS_ASSEMBLE2(ri, rd) in##ri##diag##rd // Differing internal namespaces eliminate ODR violations between modes +#define __RFF_NS_ASSEMBLE(ri, rd) __RFF_NS_ASSEMBLE2(ri, rd) +#define __RFF_NS_NAME __RFF_NS_ASSEMBLE(RESULT_INLINE_ERROR_TESTS_FAIL_FAST, RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST) +#define __RFF_NS wil::details::__RFF_NS_NAME +#if (RESULT_DIAGNOSTICS_LEVEL_FAIL_FAST == 1) +#define __RFF_FN(MethodName) __RFF_NS:: MethodName <__COUNTER__> +#else +#define __RFF_FN(MethodName) __RFF_NS:: MethodName +#endif +// end-of-repeated fail-fast handling macros + +// Helpers for return macros +#define __RETURN_HR_MSG(hr, str, fmt, ...) __WI_SUPPRESS_4127_S do { const HRESULT __hr = (hr); if (FAILED(__hr)) { __R_FN(Return_HrMsg)(__R_INFO(str) __hr, fmt, ##__VA_ARGS__); } return __hr; } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_HR_MSG_FAIL(hr, str, fmt, ...) __WI_SUPPRESS_4127_S do { const HRESULT __hr = (hr); __R_FN(Return_HrMsg)(__R_INFO(str) __hr, fmt, ##__VA_ARGS__); return __hr; } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_WIN32_MSG(err, str, fmt, ...) __WI_SUPPRESS_4127_S do { const DWORD __err = (err); if (FAILED_WIN32(__err)) { return __R_FN(Return_Win32Msg)(__R_INFO(str) __err, fmt, ##__VA_ARGS__); } return S_OK; } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_WIN32_MSG_FAIL(err, str, fmt, ...) __WI_SUPPRESS_4127_S do { const DWORD __err = (err); return __R_FN(Return_Win32Msg)(__R_INFO(str) __err, fmt, ##__VA_ARGS__); } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_GLE_MSG_FAIL(str, fmt, ...) return __R_FN(Return_GetLastErrorMsg)(__R_INFO(str) fmt, ##__VA_ARGS__) +#define __RETURN_NTSTATUS_MSG(status, str, fmt, ...) __WI_SUPPRESS_4127_S do { const NTSTATUS __status = (status); if (FAILED_NTSTATUS(__status)) { return __R_FN(Return_NtStatusMsg)(__R_INFO(str) __status, fmt, ##__VA_ARGS__); } return S_OK; } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_NTSTATUS_MSG_FAIL(status, str, fmt, ...) __WI_SUPPRESS_4127_S do { const NTSTATUS __status = (status); return __R_FN(Return_NtStatusMsg)(__R_INFO(str) __status, fmt, ##__VA_ARGS__); } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_HR(hr, str) __WI_SUPPRESS_4127_S do { const HRESULT __hr = (hr); if (FAILED(__hr)) { __R_FN(Return_Hr)(__R_INFO(str) __hr); } return __hr; } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_HR_NOFILE(hr, str) __WI_SUPPRESS_4127_S do { const HRESULT __hr = (hr); if (FAILED(__hr)) { __R_FN(Return_Hr)(__R_INFO_NOFILE(str) __hr); } return __hr; } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_HR_FAIL(hr, str) __WI_SUPPRESS_4127_S do { const HRESULT __hr = (hr); __R_FN(Return_Hr)(__R_INFO(str) __hr); return __hr; } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_HR_FAIL_NOFILE(hr, str) __WI_SUPPRESS_4127_S do { const HRESULT __hr = (hr); __R_FN(Return_Hr)(__R_INFO_NOFILE(str) __hr); return __hr; } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_WIN32(err, str) __WI_SUPPRESS_4127_S do { const DWORD __err = (err); if (FAILED_WIN32(__err)) { return __R_FN(Return_Win32)(__R_INFO(str) __err); } return S_OK; } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_WIN32_FAIL(err, str) __WI_SUPPRESS_4127_S do { const DWORD __err = (err); return __R_FN(Return_Win32)(__R_INFO(str) __err); } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_GLE_FAIL(str) return __R_FN(Return_GetLastError)(__R_INFO_ONLY(str)) +#define __RETURN_GLE_FAIL_NOFILE(str) return __R_FN(Return_GetLastError)(__R_INFO_NOFILE_ONLY(str)) +#define __RETURN_NTSTATUS(status, str) __WI_SUPPRESS_4127_S do { const NTSTATUS __status = (status); if (FAILED_NTSTATUS(__status)) { return __R_FN(Return_NtStatus)(__R_INFO(str) __status); } return S_OK; } __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __RETURN_NTSTATUS_FAIL(status, str) __WI_SUPPRESS_4127_S do { const NTSTATUS __status = (status); return __R_FN(Return_NtStatus)(__R_INFO(str) __status); } __WI_SUPPRESS_4127_E while ((void)0, 0) +/// @endcond + +//***************************************************************************** +// Macros for returning failures as HRESULTs +//***************************************************************************** + +// Always returns a known result (HRESULT) - always logs failures +#define RETURN_HR(hr) __RETURN_HR(wil::verify_hresult(hr), #hr) +#define RETURN_LAST_ERROR() __RETURN_GLE_FAIL(nullptr) +#define RETURN_WIN32(win32err) __RETURN_WIN32(win32err, #win32err) +#define RETURN_NTSTATUS(status) __RETURN_NTSTATUS(status, #status) + +// Conditionally returns failures (HRESULT) - always logs failures +#define RETURN_IF_FAILED(hr) __WI_SUPPRESS_4127_S do { const auto __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { __RETURN_HR_FAIL(__hrRet, #hr); }} __WI_SUPPRESS_4127_E while ((void)0, 0) +#define RETURN_IF_WIN32_BOOL_FALSE(win32BOOL) __WI_SUPPRESS_4127_S do { const auto __boolRet = wil::verify_BOOL(win32BOOL); if (!__boolRet) { __RETURN_GLE_FAIL(#win32BOOL); }} __WI_SUPPRESS_4127_E while ((void)0, 0) +#define RETURN_IF_WIN32_ERROR(win32err) __WI_SUPPRESS_4127_S do { const DWORD __errRet = (win32err); if (FAILED_WIN32(__errRet)) { __RETURN_WIN32_FAIL(__errRet, #win32err); }} __WI_SUPPRESS_4127_E while ((void)0, 0) +#define RETURN_IF_NULL_ALLOC(ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { __RETURN_HR_FAIL(E_OUTOFMEMORY, #ptr); }} __WI_SUPPRESS_4127_E while ((void)0, 0) +#define RETURN_HR_IF(hr, condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { __RETURN_HR(wil::verify_hresult(hr), #condition); }} __WI_SUPPRESS_4127_E while ((void)0, 0) +#define RETURN_HR_IF_NULL(hr, ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { __RETURN_HR(wil::verify_hresult(hr), #ptr); }} __WI_SUPPRESS_4127_E while ((void)0, 0) +#define RETURN_LAST_ERROR_IF(condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { __RETURN_GLE_FAIL(#condition); }} __WI_SUPPRESS_4127_E while ((void)0, 0) +#define RETURN_LAST_ERROR_IF_NULL(ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { __RETURN_GLE_FAIL(#ptr); }} __WI_SUPPRESS_4127_E while ((void)0, 0) +#define RETURN_IF_NTSTATUS_FAILED(status) __WI_SUPPRESS_4127_S do { const NTSTATUS __statusRet = (status); if (FAILED_NTSTATUS(__statusRet)) { __RETURN_NTSTATUS_FAIL(__statusRet, #status); }} __WI_SUPPRESS_4127_E while ((void)0, 0) + +// Always returns a known failure (HRESULT) - always logs a var-arg message on failure +#define RETURN_HR_MSG(hr, fmt, ...) __RETURN_HR_MSG(wil::verify_hresult(hr), #hr, fmt, ##__VA_ARGS__) +#define RETURN_LAST_ERROR_MSG(fmt, ...) __RETURN_GLE_MSG_FAIL(nullptr, fmt, ##__VA_ARGS__) +#define RETURN_WIN32_MSG(win32err, fmt, ...) __RETURN_WIN32_MSG(win32err, #win32err, fmt, ##__VA_ARGS__) +#define RETURN_NTSTATUS_MSG(status, fmt, ...) __RETURN_NTSTATUS_MSG(status, #status, fmt, ##__VA_ARGS__) + +// Conditionally returns failures (HRESULT) - always logs a var-arg message on failure +#define RETURN_IF_FAILED_MSG(hr, fmt, ...) __WI_SUPPRESS_4127_S do { const auto __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { __RETURN_HR_MSG_FAIL(__hrRet, #hr, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_IF_WIN32_BOOL_FALSE_MSG(win32BOOL, fmt, ...) __WI_SUPPRESS_4127_S do { if (!wil::verify_BOOL(win32BOOL)) { __RETURN_GLE_MSG_FAIL(#win32BOOL, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_IF_WIN32_ERROR_MSG(win32err, fmt, ...) __WI_SUPPRESS_4127_S do { const DWORD __errRet = (win32err); if (FAILED_WIN32(__errRet)) { __RETURN_WIN32_MSG_FAIL(__errRet, #win32err, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_IF_NULL_ALLOC_MSG(ptr, fmt, ...) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { __RETURN_HR_MSG_FAIL(E_OUTOFMEMORY, #ptr, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_HR_IF_MSG(hr, condition, fmt, ...) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { __RETURN_HR_MSG(wil::verify_hresult(hr), #condition, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_HR_IF_NULL_MSG(hr, ptr, fmt, ...) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { __RETURN_HR_MSG(wil::verify_hresult(hr), #ptr, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_LAST_ERROR_IF_MSG(condition, fmt, ...) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { __RETURN_GLE_MSG_FAIL(#condition, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_LAST_ERROR_IF_NULL_MSG(ptr, fmt, ...) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { __RETURN_GLE_MSG_FAIL(#ptr, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) __WI_SUPPRESS_4127_S do { const NTSTATUS __statusRet = (status); if (FAILED_NTSTATUS(__statusRet)) { __RETURN_NTSTATUS_MSG_FAIL(__statusRet, #status, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0) + +// Conditionally returns failures (HRESULT) - use for failures that are expected in common use - failures are not logged - macros are only for control flow pattern +#define RETURN_IF_FAILED_EXPECTED(hr) __WI_SUPPRESS_4127_S do { const auto __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { return __hrRet; }} __WI_SUPPRESS_4127_E while ((void)0, 0) +#define RETURN_IF_WIN32_BOOL_FALSE_EXPECTED(win32BOOL) __WI_SUPPRESS_4127_S do { if (!wil::verify_BOOL(win32BOOL)) { return wil::details::GetLastErrorFailHr(); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_IF_WIN32_ERROR_EXPECTED(win32err) __WI_SUPPRESS_4127_S do { const DWORD __errRet = (win32err); if (FAILED_WIN32(__errRet)) { return __HRESULT_FROM_WIN32(__errRet); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_IF_NULL_ALLOC_EXPECTED(ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { return E_OUTOFMEMORY; }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_HR_IF_EXPECTED(hr, condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { return wil::verify_hresult(hr); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_HR_IF_NULL_EXPECTED(hr, ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { return wil::verify_hresult(hr); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_LAST_ERROR_IF_EXPECTED(condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { return wil::details::GetLastErrorFailHr(); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_LAST_ERROR_IF_NULL_EXPECTED(ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { return wil::details::GetLastErrorFailHr(); }} __WI_SUPPRESS_4127_E while((void)0, 0) +#define RETURN_IF_NTSTATUS_FAILED_EXPECTED(status) __WI_SUPPRESS_4127_S do { const NTSTATUS __statusRet = (status); if (FAILED_NTSTATUS(__statusRet)) { return wil::details::NtStatusToHr(__statusRet); }} __WI_SUPPRESS_4127_E while((void)0, 0) + +#define __WI_OR_IS_EXPECTED_HRESULT(e) || (__hrRet == wil::verify_hresult(e)) +#define RETURN_IF_FAILED_WITH_EXPECTED(hr, hrExpected, ...) \ + do \ + { \ + const auto __hrRet = wil::verify_hresult(hr); \ + if (FAILED(__hrRet)) \ + { \ + if ((__hrRet == wil::verify_hresult(hrExpected)) WI_FOREACH(__WI_OR_IS_EXPECTED_HRESULT, ##__VA_ARGS__)) \ + { \ + return __hrRet; \ + } \ + __RETURN_HR_FAIL(__hrRet, #hr); \ + } \ + } \ + while ((void)0, 0) + +//***************************************************************************** +// Macros for logging failures (ignore or pass-through) +//***************************************************************************** + +// Always logs a known failure +#define LOG_HR(hr) __R_FN(Log_Hr)(__R_INFO(#hr) wil::verify_hresult(hr)) +#define LOG_LAST_ERROR() __R_FN(Log_GetLastError)(__R_INFO_ONLY(nullptr)) +#define LOG_WIN32(win32err) __R_FN(Log_Win32)(__R_INFO(#win32err) win32err) +#define LOG_NTSTATUS(status) __R_FN(Log_NtStatus)(__R_INFO(#status) status) + +// Conditionally logs failures - returns parameter value +#define LOG_IF_FAILED(hr) __R_FN(Log_IfFailed)(__R_INFO(#hr) wil::verify_hresult(hr)) +#define LOG_IF_WIN32_BOOL_FALSE(win32BOOL) __R_FN(Log_IfWin32BoolFalse)(__R_INFO(#win32BOOL) wil::verify_BOOL(win32BOOL)) +#define LOG_IF_WIN32_ERROR(win32err) __R_FN(Log_IfWin32Error)(__R_INFO(#win32err) win32err) +#define LOG_IF_NULL_ALLOC(ptr) __R_FN(Log_IfNullAlloc)(__R_INFO(#ptr) ptr) +#define LOG_HR_IF(hr, condition) __R_FN(Log_HrIf)(__R_INFO(#condition) wil::verify_hresult(hr), wil::verify_bool(condition)) +#define LOG_HR_IF_NULL(hr, ptr) __R_FN(Log_HrIfNull)(__R_INFO(#ptr) wil::verify_hresult(hr), ptr) +#define LOG_LAST_ERROR_IF(condition) __R_FN(Log_GetLastErrorIf)(__R_INFO(#condition) wil::verify_bool(condition)) +#define LOG_LAST_ERROR_IF_NULL(ptr) __R_FN(Log_GetLastErrorIfNull)(__R_INFO(#ptr) ptr) +#define LOG_IF_NTSTATUS_FAILED(status) __R_FN(Log_IfNtStatusFailed)(__R_INFO(#status) status) + +// Alternatives for SUCCEEDED(hr) and FAILED(hr) that conditionally log failures +#define SUCCEEDED_LOG(hr) SUCCEEDED(LOG_IF_FAILED(hr)) +#define FAILED_LOG(hr) FAILED(LOG_IF_FAILED(hr)) +#define SUCCEEDED_WIN32_LOG(win32err) SUCCEEDED_WIN32(LOG_IF_WIN32_ERROR(win32err)) +#define FAILED_WIN32_LOG(win32err) FAILED_WIN32(LOG_IF_WIN32_ERROR(win32err)) +#define SUCCEEDED_NTSTATUS_LOG(status) SUCCEEDED_NTSTATUS(LOG_IF_NTSTATUS_FAILED(status)) +#define FAILED_NTSTATUS_LOG(status) FAILED_NTSTATUS(LOG_IF_NTSTATUS_FAILED(status)) + +// Alternatives for NT_SUCCESS(x) that conditionally logs failures +#define NT_SUCCESS_LOG(status) NT_SUCCESS(LOG_IF_NTSTATUS_FAILED(status)) + +// Always logs a known failure - logs a var-arg message on failure +#define LOG_HR_MSG(hr, fmt, ...) __R_FN(Log_HrMsg)(__R_INFO(#hr) wil::verify_hresult(hr), fmt, ##__VA_ARGS__) +#define LOG_LAST_ERROR_MSG(fmt, ...) __R_FN(Log_GetLastErrorMsg)(__R_INFO(nullptr) fmt, ##__VA_ARGS__) +#define LOG_WIN32_MSG(win32err, fmt, ...) __R_FN(Log_Win32Msg)(__R_INFO(#win32err) win32err, fmt, ##__VA_ARGS__) +#define LOG_NTSTATUS_MSG(status, fmt, ...) __R_FN(Log_NtStatusMsg)(__R_INFO(#status) status, fmt, ##__VA_ARGS__) + +// Conditionally logs failures - returns parameter value - logs a var-arg message on failure +#define LOG_IF_FAILED_MSG(hr, fmt, ...) __R_FN(Log_IfFailedMsg)(__R_INFO(#hr) wil::verify_hresult(hr), fmt, ##__VA_ARGS__) +#define LOG_IF_WIN32_BOOL_FALSE_MSG(win32BOOL, fmt, ...) __R_FN(Log_IfWin32BoolFalseMsg)(__R_INFO(#win32BOOL) wil::verify_BOOL(win32BOOL), fmt, ##__VA_ARGS__) +#define LOG_IF_WIN32_ERROR_MSG(win32err, fmt, ...) __R_FN(Log_IfWin32ErrorMsg)(__R_INFO(#win32err) win32err, fmt, ##__VA_ARGS__) +#define LOG_IF_NULL_ALLOC_MSG(ptr, fmt, ...) __R_FN(Log_IfNullAllocMsg)(__R_INFO(#ptr) ptr, fmt, ##__VA_ARGS__) +#define LOG_HR_IF_MSG(hr, condition, fmt, ...) __R_FN(Log_HrIfMsg)(__R_INFO(#condition) wil::verify_hresult(hr), wil::verify_bool(condition), fmt, ##__VA_ARGS__) +#define LOG_HR_IF_NULL_MSG(hr, ptr, fmt, ...) __R_FN(Log_HrIfNullMsg)(__R_INFO(#ptr) wil::verify_hresult(hr), ptr, fmt, ##__VA_ARGS__) +#define LOG_LAST_ERROR_IF_MSG(condition, fmt, ...) __R_FN(Log_GetLastErrorIfMsg)(__R_INFO(#condition) wil::verify_bool(condition), fmt, ##__VA_ARGS__) +#define LOG_LAST_ERROR_IF_NULL_MSG(ptr, fmt, ...) __R_FN(Log_GetLastErrorIfNullMsg)(__R_INFO(#ptr) ptr, fmt, ##__VA_ARGS__) +#define LOG_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) __R_FN(Log_IfNtStatusFailedMsg)(__R_INFO(#status) status, fmt, ##__VA_ARGS__) + +#define __WI_COMMA_EXPECTED_HRESULT(e) , wil::verify_hresult(e) +#define LOG_IF_FAILED_WITH_EXPECTED(hr, hrExpected, ...) __R_FN(Log_IfFailedWithExpected)(__R_INFO(#hr) wil::verify_hresult(hr), WI_ARGS_COUNT(__VA_ARGS__) + 1, wil::verify_hresult(hrExpected) WI_FOREACH(__WI_COMMA_EXPECTED_HRESULT, ##__VA_ARGS__)) + +//***************************************************************************** +// Macros to fail fast the process on failures +//***************************************************************************** + +// Always fail fast a known failure +#define FAIL_FAST_HR(hr) __RFF_FN(FailFast_Hr)(__RFF_INFO(#hr) wil::verify_hresult(hr)) +#define FAIL_FAST_LAST_ERROR() __RFF_FN(FailFast_GetLastError)(__RFF_INFO_ONLY(nullptr)) +#define FAIL_FAST_WIN32(win32err) __RFF_FN(FailFast_Win32)(__RFF_INFO(#win32err) win32err) +#define FAIL_FAST_NTSTATUS(status) __RFF_FN(FailFast_NtStatus)(__RFF_INFO(#status) status) + +// Conditionally fail fast failures - returns parameter value +#define FAIL_FAST_IF_FAILED(hr) __RFF_FN(FailFast_IfFailed)(__RFF_INFO(#hr) wil::verify_hresult(hr)) +#define FAIL_FAST_IF_WIN32_BOOL_FALSE(win32BOOL) __RFF_FN(FailFast_IfWin32BoolFalse)(__RFF_INFO(#win32BOOL) wil::verify_BOOL(win32BOOL)) +#define FAIL_FAST_IF_WIN32_ERROR(win32err) __RFF_FN(FailFast_IfWin32Error)(__RFF_INFO(#win32err) win32err) +#define FAIL_FAST_IF_NULL_ALLOC(ptr) __RFF_FN(FailFast_IfNullAlloc)(__RFF_INFO(#ptr) ptr) +#define FAIL_FAST_HR_IF(hr, condition) __RFF_FN(FailFast_HrIf)(__RFF_INFO(#condition) wil::verify_hresult(hr), wil::verify_bool(condition)) +#define FAIL_FAST_HR_IF_NULL(hr, ptr) __RFF_FN(FailFast_HrIfNull)(__RFF_INFO(#ptr) wil::verify_hresult(hr), ptr) +#define FAIL_FAST_LAST_ERROR_IF(condition) __RFF_FN(FailFast_GetLastErrorIf)(__RFF_INFO(#condition) wil::verify_bool(condition)) +#define FAIL_FAST_LAST_ERROR_IF_NULL(ptr) __RFF_FN(FailFast_GetLastErrorIfNull)(__RFF_INFO(#ptr) ptr) +#define FAIL_FAST_IF_NTSTATUS_FAILED(status) __RFF_FN(FailFast_IfNtStatusFailed)(__RFF_INFO(#status) status) + +// Always fail fast a known failure - fail fast a var-arg message on failure +#define FAIL_FAST_HR_MSG(hr, fmt, ...) __RFF_FN(FailFast_HrMsg)(__RFF_INFO(#hr) wil::verify_hresult(hr), fmt, ##__VA_ARGS__) +#define FAIL_FAST_LAST_ERROR_MSG(fmt, ...) __RFF_FN(FailFast_GetLastErrorMsg)(__RFF_INFO(nullptr) fmt, ##__VA_ARGS__) +#define FAIL_FAST_WIN32_MSG(win32err, fmt, ...) __RFF_FN(FailFast_Win32Msg)(__RFF_INFO(#win32err) win32err, fmt, ##__VA_ARGS__) +#define FAIL_FAST_NTSTATUS_MSG(status, fmt, ...) __RFF_FN(FailFast_NtStatusMsg)(__RFF_INFO(#status) status, fmt, ##__VA_ARGS__) + +// Conditionally fail fast failures - returns parameter value - fail fast a var-arg message on failure +#define FAIL_FAST_IF_FAILED_MSG(hr, fmt, ...) __RFF_FN(FailFast_IfFailedMsg)(__RFF_INFO(#hr) wil::verify_hresult(hr), fmt, ##__VA_ARGS__) +#define FAIL_FAST_IF_WIN32_BOOL_FALSE_MSG(win32BOOL, fmt, ...) __RFF_FN(FailFast_IfWin32BoolFalseMsg)(__RFF_INFO(#win32BOOL) wil::verify_BOOL(win32BOOL), fmt, ##__VA_ARGS__) +#define FAIL_FAST_IF_WIN32_ERROR_MSG(win32err, fmt, ...) __RFF_FN(FailFast_IfWin32ErrorMsg)(__RFF_INFO(#win32err) win32err, fmt, ##__VA_ARGS__) +#define FAIL_FAST_IF_NULL_ALLOC_MSG(ptr, fmt, ...) __RFF_FN(FailFast_IfNullAllocMsg)(__RFF_INFO(#ptr) ptr, fmt, ##__VA_ARGS__) +#define FAIL_FAST_HR_IF_MSG(hr, condition, fmt, ...) __RFF_FN(FailFast_HrIfMsg)(__RFF_INFO(#condition) wil::verify_hresult(hr), wil::verify_bool(condition), fmt, ##__VA_ARGS__) +#define FAIL_FAST_HR_IF_NULL_MSG(hr, ptr, fmt, ...) __RFF_FN(FailFast_HrIfNullMsg)(__RFF_INFO(#ptr) wil::verify_hresult(hr), ptr, fmt, ##__VA_ARGS__) +#define FAIL_FAST_LAST_ERROR_IF_MSG(condition, fmt, ...) __RFF_FN(FailFast_GetLastErrorIfMsg)(__RFF_INFO(#condition) wil::verify_bool(condition), fmt, ##__VA_ARGS__) +#define FAIL_FAST_LAST_ERROR_IF_NULL_MSG(ptr, fmt, ...) __RFF_FN(FailFast_GetLastErrorIfNullMsg)(__RFF_INFO(#ptr) ptr, fmt, ##__VA_ARGS__) +#define FAIL_FAST_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) __RFF_FN(FailFast_IfNtStatusFailedMsg)(__RFF_INFO(#status) status, fmt, ##__VA_ARGS__) + +// Always fail fast a known failure +#ifndef FAIL_FAST +#define FAIL_FAST() __RFF_FN(FailFast_Unexpected)(__RFF_INFO_ONLY(nullptr)) +#endif + +// Conditionally fail fast failures - returns parameter value +#define FAIL_FAST_IF(condition) __RFF_FN(FailFast_If)(__RFF_INFO(#condition) wil::verify_bool(condition)) +#define FAIL_FAST_IF_NULL(ptr) __RFF_FN(FailFast_IfNull)(__RFF_INFO(#ptr) ptr) + +// Always fail fast a known failure - fail fast a var-arg message on failure +#define FAIL_FAST_MSG(fmt, ...) __RFF_FN(FailFast_UnexpectedMsg)(__RFF_INFO(nullptr) fmt, ##__VA_ARGS__) + +// Conditionally fail fast failures - returns parameter value - fail fast a var-arg message on failure +#define FAIL_FAST_IF_MSG(condition, fmt, ...) __RFF_FN(FailFast_IfMsg)(__RFF_INFO(#condition) wil::verify_bool(condition), fmt, ##__VA_ARGS__) +#define FAIL_FAST_IF_NULL_MSG(ptr, fmt, ...) __RFF_FN(FailFast_IfNullMsg)(__RFF_INFO(#ptr) ptr, fmt, ##__VA_ARGS__) + +// Immediate fail fast (no telemetry - use rarely / only when *already* in an undefined state) +#define FAIL_FAST_IMMEDIATE() __RFF_FN(FailFastImmediate_Unexpected)() + +// Conditional immediate fail fast (no telemetry - use rarely / only when *already* in an undefined state) +#define FAIL_FAST_IMMEDIATE_IF_FAILED(hr) __RFF_FN(FailFastImmediate_IfFailed)(wil::verify_hresult(hr)) +#define FAIL_FAST_IMMEDIATE_IF(condition) __RFF_FN(FailFastImmediate_If)(wil::verify_bool(condition)) +#define FAIL_FAST_IMMEDIATE_IF_NULL(ptr) __RFF_FN(FailFastImmediate_IfNull)(ptr) +#define FAIL_FAST_IMMEDIATE_IF_NTSTATUS_FAILED(status) __RFF_FN(FailFastImmediate_IfNtStatusFailed)(status) + +// Specializations +#define FAIL_FAST_IMMEDIATE_IF_IN_LOADER_CALLOUT() do { if (wil::details::g_pfnFailFastInLoaderCallout != nullptr) { wil::details::g_pfnFailFastInLoaderCallout(); } } while ((void)0, 0) + + +//***************************************************************************** +// Macros to throw exceptions on failure +//***************************************************************************** + +#ifdef WIL_ENABLE_EXCEPTIONS + +// Always throw a known failure +#define THROW_HR(hr) __R_FN(Throw_Hr)(__R_INFO(#hr) wil::verify_hresult(hr)) +#define THROW_LAST_ERROR() __R_FN(Throw_GetLastError)(__R_INFO_ONLY(nullptr)) +#define THROW_WIN32(win32err) __R_FN(Throw_Win32)(__R_INFO(#win32err) win32err) +#define THROW_EXCEPTION(exception) wil::details::ReportFailure_CustomException(__R_INFO(#exception) exception) +#define THROW_NTSTATUS(status) __R_FN(Throw_NtStatus)(__R_INFO(#status) status) + +// Conditionally throw failures - returns parameter value +#define THROW_IF_FAILED(hr) __R_FN(Throw_IfFailed)(__R_INFO(#hr) wil::verify_hresult(hr)) +#define THROW_IF_WIN32_BOOL_FALSE(win32BOOL) __R_FN(Throw_IfWin32BoolFalse)(__R_INFO(#win32BOOL) wil::verify_BOOL(win32BOOL)) +#define THROW_IF_WIN32_ERROR(win32err) __R_FN(Throw_IfWin32Error)(__R_INFO(#win32err) win32err) +#define THROW_IF_NULL_ALLOC(ptr) __R_FN(Throw_IfNullAlloc)(__R_INFO(#ptr) ptr) +#define THROW_HR_IF(hr, condition) __R_FN(Throw_HrIf)(__R_INFO(#condition) wil::verify_hresult(hr), wil::verify_bool(condition)) +#define THROW_HR_IF_NULL(hr, ptr) __R_FN(Throw_HrIfNull)(__R_INFO(#ptr) wil::verify_hresult(hr), ptr) +#define THROW_LAST_ERROR_IF(condition) __R_FN(Throw_GetLastErrorIf)(__R_INFO(#condition) wil::verify_bool(condition)) +#define THROW_LAST_ERROR_IF_NULL(ptr) __R_FN(Throw_GetLastErrorIfNull)(__R_INFO(#ptr) ptr) +#define THROW_IF_NTSTATUS_FAILED(status) __R_FN(Throw_IfNtStatusFailed)(__R_INFO(#status) status) + +// Always throw a known failure - throw a var-arg message on failure +#define THROW_HR_MSG(hr, fmt, ...) __R_FN(Throw_HrMsg)(__R_INFO(#hr) wil::verify_hresult(hr), fmt, ##__VA_ARGS__) +#define THROW_LAST_ERROR_MSG(fmt, ...) __R_FN(Throw_GetLastErrorMsg)(__R_INFO(nullptr) fmt, ##__VA_ARGS__) +#define THROW_WIN32_MSG(win32err, fmt, ...) __R_FN(Throw_Win32Msg)(__R_INFO(#win32err) win32err, fmt, ##__VA_ARGS__) +#define THROW_EXCEPTION_MSG(exception, fmt, ...) wil::details::ReportFailure_CustomExceptionMsg(__R_INFO(#exception) exception, fmt, ##__VA_ARGS__) +#define THROW_NTSTATUS_MSG(status, fmt, ...) __R_FN(Throw_NtStatusMsg)(__R_INFO(#status) status, fmt, ##__VA_ARGS__) + +// Conditionally throw failures - returns parameter value - throw a var-arg message on failure +#define THROW_IF_FAILED_MSG(hr, fmt, ...) __R_FN(Throw_IfFailedMsg)(__R_INFO(#hr) wil::verify_hresult(hr), fmt, ##__VA_ARGS__) +#define THROW_IF_WIN32_BOOL_FALSE_MSG(win32BOOL, fmt, ...) __R_FN(Throw_IfWin32BoolFalseMsg)(__R_INFO(#win32BOOL) wil::verify_BOOL(win32BOOL), fmt, ##__VA_ARGS__) +#define THROW_IF_WIN32_ERROR_MSG(win32err, fmt, ...) __R_FN(Throw_IfWin32ErrorMsg)(__R_INFO(#win32err) win32err, fmt, ##__VA_ARGS__) +#define THROW_IF_NULL_ALLOC_MSG(ptr, fmt, ...) __R_FN(Throw_IfNullAllocMsg)(__R_INFO(#ptr) ptr, fmt, ##__VA_ARGS__) +#define THROW_HR_IF_MSG(hr, condition, fmt, ...) __R_FN(Throw_HrIfMsg)(__R_INFO(#condition) wil::verify_hresult(hr), wil::verify_bool(condition), fmt, ##__VA_ARGS__) +#define THROW_HR_IF_NULL_MSG(hr, ptr, fmt, ...) __R_FN(Throw_HrIfNullMsg)(__R_INFO(#ptr) wil::verify_hresult(hr), ptr, fmt, ##__VA_ARGS__) +#define THROW_LAST_ERROR_IF_MSG(condition, fmt, ...) __R_FN(Throw_GetLastErrorIfMsg)(__R_INFO(#condition) wil::verify_bool(condition), fmt, ##__VA_ARGS__) +#define THROW_LAST_ERROR_IF_NULL_MSG(ptr, fmt, ...) __R_FN(Throw_GetLastErrorIfNullMsg)(__R_INFO(#ptr) ptr, fmt, ##__VA_ARGS__) +#define THROW_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) __R_FN(Throw_IfNtStatusFailedMsg)(__R_INFO(#status) status, fmt, ##__VA_ARGS__) + + +//***************************************************************************** +// Macros to catch and convert exceptions on failure +//***************************************************************************** + +// Use these macros *within* a catch (...) block to handle exceptions +#define RETURN_CAUGHT_EXCEPTION() return __R_FN(Return_CaughtException)(__R_INFO_ONLY(nullptr)) +#define RETURN_CAUGHT_EXCEPTION_MSG(fmt, ...) return __R_FN(Return_CaughtExceptionMsg)(__R_INFO(nullptr) fmt, ##__VA_ARGS__) +#define RETURN_CAUGHT_EXCEPTION_EXPECTED() return wil::ResultFromCaughtException() +#define LOG_CAUGHT_EXCEPTION() __R_FN(Log_CaughtException)(__R_INFO_ONLY(nullptr)) +#define LOG_CAUGHT_EXCEPTION_MSG(fmt, ...) __R_FN(Log_CaughtExceptionMsg)(__R_INFO(nullptr) fmt, ##__VA_ARGS__) +#define FAIL_FAST_CAUGHT_EXCEPTION() __R_FN(FailFast_CaughtException)(__R_INFO_ONLY(nullptr)) +#define FAIL_FAST_CAUGHT_EXCEPTION_MSG(fmt, ...) __R_FN(FailFast_CaughtExceptionMsg)(__R_INFO(nullptr) fmt, ##__VA_ARGS__) +#define THROW_NORMALIZED_CAUGHT_EXCEPTION() __R_FN(Throw_CaughtException)(__R_INFO_ONLY(nullptr)) +#define THROW_NORMALIZED_CAUGHT_EXCEPTION_MSG(fmt, ...) __R_FN(Throw_CaughtExceptionMsg)(__R_INFO(nullptr) fmt, ##__VA_ARGS__) + +// Use these macros in place of a catch block to handle exceptions +#define CATCH_RETURN() catch (...) { RETURN_CAUGHT_EXCEPTION(); } +#define CATCH_RETURN_MSG(fmt, ...) catch (...) { RETURN_CAUGHT_EXCEPTION_MSG(fmt, ##__VA_ARGS__); } +#define CATCH_RETURN_EXPECTED() catch (...) { RETURN_CAUGHT_EXCEPTION_EXPECTED(); } +#define CATCH_LOG() catch (...) { LOG_CAUGHT_EXCEPTION(); } +// Use CATCH_LOG_RETURN instead of CATCH_LOG in a function-try block around a destructor. CATCH_LOG in this specific case has an implicit throw at the end of scope. +// Due to a bug (DevDiv 441931), Warning 4297 (function marked noexcept throws exception) is detected even when the throwing code is unreachable, such as the end of scope after a return, in function-level catch. +#define CATCH_LOG_RETURN() catch (...) { __pragma(warning(suppress : 4297)); LOG_CAUGHT_EXCEPTION(); return; } +#define CATCH_LOG_MSG(fmt, ...) catch (...) { LOG_CAUGHT_EXCEPTION_MSG(fmt, ##__VA_ARGS__); } +// Likewise use CATCH_LOG_RETURN_MSG instead of CATCH_LOG_MSG in function-try blocks around destructors. +#define CATCH_LOG_RETURN_MSG(fmt, ...) catch (...) { __pragma(warning(suppress : 4297)); LOG_CAUGHT_EXCEPTION_MSG(fmt, ##__VA_ARGS__); return; } +#define CATCH_FAIL_FAST() catch (...) { FAIL_FAST_CAUGHT_EXCEPTION(); } +#define CATCH_FAIL_FAST_MSG(fmt, ...) catch (...) { FAIL_FAST_CAUGHT_EXCEPTION_MSG(fmt, ##__VA_ARGS__); } +#define CATCH_THROW_NORMALIZED() catch (...) { THROW_NORMALIZED_CAUGHT_EXCEPTION(); } +#define CATCH_THROW_NORMALIZED_MSG(fmt, ...) catch (...) { THROW_NORMALIZED_CAUGHT_EXCEPTION_MSG(fmt, ##__VA_ARGS__); } +#define CATCH_LOG_RETURN_HR(hr) catch (...) { LOG_CAUGHT_EXCEPTION(); return hr; } + +#endif // WIL_ENABLE_EXCEPTIONS + +// Use this macro to supply diagnostics information to wil::ResultFromException +#define WI_DIAGNOSTICS_INFO wil::DiagnosticsInfo(__R_CALLERADDRESS_VALUE, __R_LINE_VALUE, __R_FILE_VALUE) +#define WI_DIAGNOSTICS_NAME(name) wil::DiagnosticsInfo(__R_CALLERADDRESS_VALUE, __R_LINE_VALUE, __R_FILE_VALUE, name) + + + +//***************************************************************************** +// Usage Error Macros +//***************************************************************************** + +#ifndef WI_USAGE_ASSERT_STOP +#define WI_USAGE_ASSERT_STOP(condition) WI_ASSERT(condition) +#endif +#ifdef RESULT_DEBUG +#define WI_USAGE_ERROR(msg, ...) do { LOG_HR_MSG(HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE), msg, ##__VA_ARGS__); WI_USAGE_ASSERT_STOP(false); } while ((void)0, 0) +#define WI_USAGE_ERROR_FORWARD(msg, ...) do { ReportFailure_ReplaceMsg(__R_FN_CALL_FULL, HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE), msg, ##__VA_ARGS__); WI_USAGE_ASSERT_STOP(false); } while ((void)0, 0) +#else +#define WI_USAGE_ERROR(msg, ...) do { LOG_HR(HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE)); WI_USAGE_ASSERT_STOP(false); } while ((void)0, 0) +#define WI_USAGE_ERROR_FORWARD(msg, ...) do { ReportFailure_Hr(__R_FN_CALL_FULL, HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE)); WI_USAGE_ASSERT_STOP(false); } while ((void)0, 0) +#endif +#define WI_USAGE_VERIFY(condition, msg, ...) do { const auto __passed = wil::verify_bool(condition); if (!__passed) { WI_USAGE_ERROR(msg, ##__VA_ARGS__); }} while ((void)0, 0) +#define WI_USAGE_VERIFY_FORWARD(condition, msg, ...) do { const auto __passed = wil::verify_bool(condition); if (!__passed) { WI_USAGE_ERROR_FORWARD(msg, ##__VA_ARGS__); }} while ((void)0, 0) +#ifdef RESULT_DEBUG +#define WI_USAGE_ASSERT(condition, msg, ...) WI_USAGE_VERIFY(condition, msg, ##__VA_ARGS__) +#else +#define WI_USAGE_ASSERT(condition, msg, ...) +#endif + +//***************************************************************************** +// Internal Error Macros - DO NOT USE - these are for internal WIL use only to reduce sizes of binaries that use WIL +//***************************************************************************** +#ifdef RESULT_DEBUG +#define __WIL_PRIVATE_RETURN_IF_FAILED(hr) RETURN_IF_FAILED(hr) +#define __WIL_PRIVATE_RETURN_HR_IF(hr, cond) RETURN_HR_IF(hr, cond) +#define __WIL_PRIVATE_RETURN_LAST_ERROR_IF(cond) RETURN_LAST_ERROR_IF(cond) +#define __WIL_PRIVATE_RETURN_IF_WIN32_BOOL_FALSE(win32BOOL) RETURN_IF_WIN32_BOOL_FALSE(win32BOOL) +#define __WIL_PRIVATE_RETURN_LAST_ERROR_IF_NULL(ptr) RETURN_LAST_ERROR_IF_NULL(ptr) +#define __WIL_PRIVATE_RETURN_IF_NULL_ALLOC(ptr) RETURN_IF_NULL_ALLOC(ptr) +#define __WIL_PRIVATE_RETURN_LAST_ERROR() RETURN_LAST_ERROR() +#define __WIL_PRIVATE_FAIL_FAST_HR_IF(hr, condition) FAIL_FAST_HR_IF(hr, condition) +#define __WIL_PRIVATE_FAIL_FAST_HR(hr) FAIL_FAST_HR(hr) +#define __WIL_PRIVATE_LOG_HR(hr) LOG_HR(hr) +#else +#define __WIL_PRIVATE_RETURN_IF_FAILED(hr) do { const auto __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { __RETURN_HR_FAIL_NOFILE(__hrRet, #hr); }} while ((void)0, 0) +#define __WIL_PRIVATE_RETURN_HR_IF(hr, cond) do { if (wil::verify_bool(cond)) { __RETURN_HR_NOFILE(wil::verify_hresult(hr), #cond); }} while ((void)0, 0) +#define __WIL_PRIVATE_RETURN_LAST_ERROR_IF(cond) do { if (wil::verify_bool(cond)) { __RETURN_GLE_FAIL_NOFILE(#cond); }} while ((void)0, 0) +#define __WIL_PRIVATE_RETURN_IF_WIN32_BOOL_FALSE(win32BOOL) do { const BOOL __boolRet = wil::verify_BOOL(win32BOOL); if (!__boolRet) { __RETURN_GLE_FAIL_NOFILE(#win32BOOL); }} while ((void)0, 0) +#define __WIL_PRIVATE_RETURN_LAST_ERROR_IF_NULL(ptr) do { if ((ptr) == nullptr) { __RETURN_GLE_FAIL_NOFILE(#ptr); }} while ((void)0, 0) +#define __WIL_PRIVATE_RETURN_IF_NULL_ALLOC(ptr) do { if ((ptr) == nullptr) { __RETURN_HR_FAIL_NOFILE(E_OUTOFMEMORY, #ptr); }} while ((void)0, 0) +#define __WIL_PRIVATE_RETURN_LAST_ERROR() __RETURN_GLE_FAIL_NOFILE(nullptr) +#define __WIL_PRIVATE_FAIL_FAST_HR_IF(hr, condition) __RFF_FN(FailFast_HrIf)(__RFF_INFO_NOFILE(#condition) wil::verify_hresult(hr), wil::verify_bool(condition)) +#define __WIL_PRIVATE_FAIL_FAST_HR(hr) __RFF_FN(FailFast_Hr)(__RFF_INFO_NOFILE(#hr) wil::verify_hresult(hr)) +#define __WIL_PRIVATE_LOG_HR(hr) __R_FN(Log_Hr)(__R_INFO_NOFILE(#hr) wil::verify_hresult(hr)) +#endif + +namespace wil +{ + // Indicates the kind of message / failure type that was used to produce a given error + enum class FailureType + { + Exception, // THROW_... + Return, // RETURN_..._LOG or RETURN_..._MSG + Log, // LOG_... + FailFast // FAIL_FAST_... + }; + + enum class FailureFlags + { + None = 0x00, + RequestFailFast = 0x01, + RequestSuppressTelemetry = 0x02, + RequestDebugBreak = 0x04, + NtStatus = 0x08, + }; + DEFINE_ENUM_FLAG_OPERATORS(FailureFlags); + + /** Use with functions and macros that allow customizing which kinds of exceptions are handled. + This is used with methods like wil::ResultFromException and wil::ResultFromExceptionDebug. */ + enum class SupportedExceptions + { + Default, //!< [Default] all well known exceptions (honors g_fResultFailFastUnknownExceptions). + Known, //!< [Known] all well known exceptions (including std::exception). + All, //!< [All] all exceptions, known or otherwise. + None, //!< [None] no exceptions at all, an exception will fail-fast where thrown. + Thrown, //!< [Thrown] exceptions thrown by wil only (Platform::Exception^ or ResultException). + ThrownOrAlloc //!< [ThrownOrAlloc] exceptions thrown by wil (Platform::Exception^ or ResultException) or std::bad_alloc. + }; + + // Represents the call context information about a given failure + // No constructors, destructors or virtual members should be contained within + struct CallContextInfo + { + long contextId; // incrementing ID for this call context (unique across an individual module load within process) + PCSTR contextName; // the explicit name given to this context + PCWSTR contextMessage; // [optional] Message that can be associated with the call context + }; + + // Represents all context information about a given failure + // No constructors, destructors or virtual members should be contained within + struct FailureInfo + { + FailureType type; + FailureFlags flags; + HRESULT hr; + NTSTATUS status; + long failureId; // incrementing ID for this specific failure (unique across an individual module load within process) + PCWSTR pszMessage; // Message is only present for _MSG logging (it's the Sprintf message) + DWORD threadId; // the thread this failure was originally encountered on + PCSTR pszCode; // [debug only] Capture code from the macro + PCSTR pszFunction; // [debug only] The function name + PCSTR pszFile; + unsigned int uLineNumber; + int cFailureCount; // How many failures of 'type' have been reported in this module so far + PCSTR pszCallContext; // General breakdown of the call context stack that generated this failure + CallContextInfo callContextOriginating; // The outermost (first seen) call context + CallContextInfo callContextCurrent; // The most recently seen call context + PCSTR pszModule; // The module where the failure originated + void* returnAddress; // The return address to the point that called the macro + void* callerReturnAddress; // The return address of the function that includes the macro + }; + + //! Created automatically from using WI_DIAGNOSTICS_INFO to provide diagnostics to functions. + //! Note that typically wil hides diagnostics from users under the covers by passing them automatically to functions as + //! parameters hidden behind a macro. In some cases, the user needs to directly supply these, so this class provides + //! the mechanism for that. We only use this for user-passed content as it can't be directly controlled by RESULT_DIAGNOSTICS_LEVEL + //! to ensure there are no ODR violations (though that variable still controls what parameters within this structure would be available). + struct DiagnosticsInfo + { + void* returnAddress = nullptr; + PCSTR file = nullptr; + PCSTR name = nullptr; + unsigned short line = 0; + + DiagnosticsInfo() = default; + + __forceinline DiagnosticsInfo(void* returnAddress_, unsigned short line_, PCSTR file_) : + returnAddress(returnAddress_), + file(file_), + line(line_) + { + } + + __forceinline DiagnosticsInfo(void* returnAddress_, unsigned short line_, PCSTR file_, PCSTR name_) : + returnAddress(returnAddress_), + file(file_), + name(name_), + line(line_) + { + } + }; + + enum class ErrorReturn + { + Auto, + None + }; + + // [optionally] Plug in error logging + // Note: This callback is deprecated. Please use SetResultTelemetryFallback for telemetry or + // SetResultLoggingCallback for observation. + extern "C" __declspec(selectany) void(__stdcall *g_pfnResultLoggingCallback)(_Inout_ wil::FailureInfo *pFailure, _Inout_updates_opt_z_(cchDebugMessage) PWSTR pszDebugMessage, _Pre_satisfies_(cchDebugMessage > 0) size_t cchDebugMessage) WI_PFN_NOEXCEPT = nullptr; + + // [optional] + // This can be explicitly set to control whether or not error messages will be output to OutputDebugString. It can also + // be set directly from within the debugger to force console logging for debugging purposes. + __declspec(selectany) bool g_fResultOutputDebugString = true; + + // [optionally] Allows application to specify a debugger to detect whether a debugger is present. + // Useful for processes that can only be debugged under kernel debuggers where IsDebuggerPresent returns + // false. + __declspec(selectany) bool(__stdcall *g_pfnIsDebuggerPresent)() WI_PFN_NOEXCEPT = nullptr; + + // [optionally] Allows forcing WIL to believe a debugger is present. Useful for when a kernel debugger is attached and ::IsDebuggerPresent returns false + __declspec(selectany) bool g_fIsDebuggerPresent = false; + + // [optionally] Plug in additional exception-type support (return S_OK when *unable* to remap the exception) + __declspec(selectany) HRESULT(__stdcall *g_pfnResultFromCaughtException)() WI_PFN_NOEXCEPT = nullptr; + + // [optionally] Use to configure fast fail of unknown exceptions (turn them off). + __declspec(selectany) bool g_fResultFailFastUnknownExceptions = true; + + // [optionally] Set to false to a configure all THROW_XXX macros in C++/CX to throw ResultException rather than Platform::Exception^ + __declspec(selectany) bool g_fResultThrowPlatformException = true; + + // [optionally] Set to false to a configure all CATCH_ and CAUGHT_ macros to NOT support (fail-fast) std::exception based exceptions (other than std::bad_alloc and wil::ResultException) + __declspec(selectany) bool g_fResultSupportStdException = true; + + // [optionally] Set to true to cause a debug break to occur on a result failure + __declspec(selectany) bool g_fBreakOnFailure = false; + + // [optionally] customize failfast behavior + __declspec(selectany) bool(__stdcall *g_pfnWilFailFast)(const wil::FailureInfo& info) WI_PFN_NOEXCEPT = nullptr; + + /// @cond + namespace details + { + // True if g_pfnResultLoggingCallback is set (allows cutting off backwards compat calls to the function) + __declspec(selectany) bool g_resultMessageCallbackSet = false; + + // On Desktop/System WINAPI family: convert NTSTATUS error codes to friendly name strings. + __declspec(selectany) void(__stdcall *g_pfnFormatNtStatusMsg)(NTSTATUS, PWSTR, DWORD) = nullptr; + + _Success_(true) _Ret_range_(dest, destEnd) + inline PWSTR LogStringPrintf(_Out_writes_to_ptr_(destEnd) _Always_(_Post_z_) PWSTR dest, _Pre_satisfies_(destEnd >= dest) PCWSTR destEnd, _In_ _Printf_format_string_ PCWSTR format, ...) + { + va_list argList; + va_start(argList, format); + StringCchVPrintfW(dest, (destEnd - dest), format, argList); + return (destEnd == dest) ? dest : (dest + wcslen(dest)); + } + } + /// @endcond + + // This call generates the default logging string that makes its way to OutputDebugString for + // any particular failure. This string is also used to associate a failure with a PlatformException^ which + // only allows a single string to be associated with the exception. + inline HRESULT GetFailureLogString(_Out_writes_(cchDest) _Always_(_Post_z_) PWSTR pszDest, _Pre_satisfies_(cchDest > 0) _In_ size_t cchDest, _In_ FailureInfo const &failure) WI_NOEXCEPT + { + // This function was lenient to empty strings at one point and some callers became dependent on this beahvior + if ((cchDest == 0) || (pszDest == nullptr)) + { + return S_OK; + } + + pszDest[0] = L'\0'; + + // Call the logging callback (if present) to allow them to generate the debug string that will be pushed to the console + // or the platform exception object if the caller desires it. + if ((g_pfnResultLoggingCallback != nullptr) && details::g_resultMessageCallbackSet) + { + // older-form callback was a non-const FailureInfo*; conceptually this is const as callers should not be modifying + g_pfnResultLoggingCallback(const_cast(&failure), pszDest, cchDest); + } + + // The callback only optionally needs to supply the debug string -- if the callback didn't populate it, yet we still want + // it for OutputDebugString or exception message, then generate the default string. + if (pszDest[0] == L'\0') + { + PCSTR pszType = ""; + switch (failure.type) + { + case FailureType::Exception: + pszType = "Exception"; + break; + case FailureType::Return: + if (WI_IsFlagSet(failure.flags, FailureFlags::NtStatus)) + { + pszType = "ReturnNt"; + } + else + { + pszType = "ReturnHr"; + } + break; + case FailureType::Log: + if (WI_IsFlagSet(failure.flags, FailureFlags::NtStatus)) + { + pszType = "LogNt"; + } + else + { + pszType = "LogHr"; + } + break; + case FailureType::FailFast: + pszType = "FailFast"; + break; + } + + wchar_t szErrorText[256]; + szErrorText[0] = L'\0'; + LONG errorCode = 0; + + if (WI_IsFlagSet(failure.flags, FailureFlags::NtStatus)) + { + errorCode = failure.status; + if (wil::details::g_pfnFormatNtStatusMsg) + { + wil::details::g_pfnFormatNtStatusMsg(failure.status, szErrorText, ARRAYSIZE(szErrorText)); + } + } + else + { + errorCode = failure.hr; + FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, failure.hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), szErrorText, ARRAYSIZE(szErrorText), nullptr); + } + + // %FILENAME(%LINE): %TYPE(%count) tid(%threadid) %HRESULT %SystemMessage + // %Caller_MSG [%CODE(%FUNCTION)] + + PWSTR dest = pszDest; + PCWSTR destEnd = (pszDest + cchDest); + + if (failure.pszFile != nullptr) + { + dest = details::LogStringPrintf(dest, destEnd, L"%hs(%u)\\%hs!%p: ", failure.pszFile, failure.uLineNumber, failure.pszModule, failure.returnAddress); + } + else + { + dest = details::LogStringPrintf(dest, destEnd, L"%hs!%p: ", failure.pszModule, failure.returnAddress); + } + + if (failure.callerReturnAddress != nullptr) + { + dest = details::LogStringPrintf(dest, destEnd, L"(caller: %p) ", failure.callerReturnAddress); + } + + dest = details::LogStringPrintf(dest, destEnd, L"%hs(%d) tid(%x) %08X %ws", pszType, failure.cFailureCount, ::GetCurrentThreadId(), errorCode, szErrorText); + + if ((failure.pszMessage != nullptr) || (failure.pszCallContext != nullptr) || (failure.pszFunction != nullptr)) + { + dest = details::LogStringPrintf(dest, destEnd, L" "); + if (failure.pszMessage != nullptr) + { + dest = details::LogStringPrintf(dest, destEnd, L"Msg:[%ws] ", failure.pszMessage); + } + if (failure.pszCallContext != nullptr) + { + dest = details::LogStringPrintf(dest, destEnd, L"CallContext:[%hs] ", failure.pszCallContext); + } + + if (failure.pszCode != nullptr) + { + dest = details::LogStringPrintf(dest, destEnd, L"[%hs(%hs)]\n", failure.pszFunction, failure.pszCode); + } + else if (failure.pszFunction != nullptr) + { + dest = details::LogStringPrintf(dest, destEnd, L"[%hs]\n", failure.pszFunction); + } + else + { + dest = details::LogStringPrintf(dest, destEnd, L"\n"); + } + } + } + + // Explicitly choosing to return success in the event of truncation... Current callers + // depend upon it or it would be eliminated. + return S_OK; + } + + /// @cond + namespace details + { + //! Interface used to wrap up code (generally a lambda or other functor) to run in an exception-managed context where + //! exceptions or errors can be observed and logged. + struct IFunctor + { + virtual HRESULT Run() = 0; + }; + + //! Used to provide custom behavior when an exception is encountered while executing IFunctor + struct IFunctorHost + { + virtual HRESULT Run(IFunctor& functor) = 0; + virtual HRESULT ExceptionThrown(void* returnAddress) = 0; + }; + + __declspec(noinline) inline HRESULT NtStatusToHr(NTSTATUS status) WI_NOEXCEPT; + __declspec(noinline) inline NTSTATUS HrToNtStatus(HRESULT) WI_NOEXCEPT; + + struct ResultStatus + { + static ResultStatus FromResult(const HRESULT _hr) + { + return { _hr, wil::details::HrToNtStatus(_hr), false }; + } + static ResultStatus FromStatus(const NTSTATUS _status) + { + return { wil::details::NtStatusToHr(_status), _status, true }; + } + static ResultStatus FromFailureInfo(const FailureInfo& _failure) + { + return { _failure.hr, _failure.status, WI_IsFlagSet(_failure.flags, FailureFlags::NtStatus) }; + } + HRESULT hr = S_OK; + NTSTATUS status = STATUS_SUCCESS; + bool isNtStatus = false; + }; + + // Fallback telemetry provider callback (set with wil::SetResultTelemetryFallback) + __declspec(selectany) void(__stdcall *g_pfnTelemetryCallback)(bool alreadyReported, wil::FailureInfo const &failure) WI_PFN_NOEXCEPT = nullptr; + + // Result.h plug-in (WIL use only) + __declspec(selectany) void(__stdcall *g_pfnGetContextAndNotifyFailure)(_Inout_ FailureInfo *pFailure, _Out_writes_(callContextStringLength) _Post_z_ PSTR callContextString, _Pre_satisfies_(callContextStringLength > 0) size_t callContextStringLength) WI_PFN_NOEXCEPT = nullptr; + + // Observe all errors flowing through the system with this callback (set with wil::SetResultLoggingCallback); use with custom logging + __declspec(selectany) void(__stdcall *g_pfnLoggingCallback)(wil::FailureInfo const &failure) WI_PFN_NOEXCEPT = nullptr; + + // Desktop/System Only: Module fetch function (automatically setup) + __declspec(selectany) PCSTR(__stdcall *g_pfnGetModuleName)() WI_PFN_NOEXCEPT = nullptr; + + // Desktop/System Only: Retrieve address offset and modulename + __declspec(selectany) bool(__stdcall *g_pfnGetModuleInformation)(void* address, _Out_opt_ unsigned int* addressOffset, _Out_writes_bytes_opt_(size) char* name, size_t size) WI_PFN_NOEXCEPT = nullptr; + + // Called with the expectation that the program will terminate when called inside of a loader callout. + // Desktop/System Only: Automatically setup when building Windows (BUILD_WINDOWS defined) + __declspec(selectany) void(__stdcall *g_pfnFailFastInLoaderCallout)() WI_PFN_NOEXCEPT = nullptr; + + // Called to translate an NTSTATUS value to a Win32 error code + // Desktop/System Only: Automatically setup when building Windows (BUILD_WINDOWS defined) + __declspec(selectany) ULONG(__stdcall *g_pfnRtlNtStatusToDosErrorNoTeb)(NTSTATUS) WI_PFN_NOEXCEPT = nullptr; + + // Desktop/System Only: Call to DebugBreak + __declspec(selectany) void(__stdcall *g_pfnDebugBreak)() WI_PFN_NOEXCEPT = nullptr; + + // Called to determine whether or not termination is happening + // Desktop/System Only: Automatically setup when building Windows (BUILD_WINDOWS defined) + __declspec(selectany) BOOLEAN(__stdcall *g_pfnDllShutdownInProgress)() WI_PFN_NOEXCEPT = nullptr; + __declspec(selectany) bool g_processShutdownInProgress = false; + + // On Desktop/System WINAPI family: dynalink RaiseFailFastException because we may encounter modules + // that do not have RaiseFailFastException in kernelbase. UWP apps will directly link. + __declspec(selectany) void (__stdcall *g_pfnRaiseFailFastException)(PEXCEPTION_RECORD,PCONTEXT,DWORD) = nullptr; + + // Exception-based compiled additions + __declspec(selectany) HRESULT(__stdcall *g_pfnRunFunctorWithExceptionFilter)(IFunctor& functor, IFunctorHost& host, void* returnAddress) = nullptr; + __declspec(selectany) void(__stdcall *g_pfnRethrow)() = nullptr; + __declspec(selectany) void(__stdcall *g_pfnThrowResultException)(const FailureInfo& failure) = nullptr; + extern "C" __declspec(selectany) ResultStatus(__stdcall *g_pfnResultFromCaughtExceptionInternal)(_Out_writes_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, _Out_ bool* isNormalized) WI_PFN_NOEXCEPT = nullptr; + + // C++/WinRT additions + extern "C" __declspec(selectany) HRESULT(__stdcall *g_pfnResultFromCaughtException_CppWinRt)(_Out_writes_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, _Out_ bool* isNormalized) WI_PFN_NOEXCEPT = nullptr; + + // C++/cx compiled additions + extern "C" __declspec(selectany) void(__stdcall *g_pfnThrowPlatformException)(FailureInfo const &failure, PCWSTR debugString) = nullptr; + extern "C" __declspec(selectany) _Always_(_Post_satisfies_(return < 0)) HRESULT(__stdcall *g_pfnResultFromCaughtException_WinRt)(_Inout_updates_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, _Out_ bool* isNormalized) WI_PFN_NOEXCEPT = nullptr; + __declspec(selectany) _Always_(_Post_satisfies_(return < 0)) HRESULT(__stdcall *g_pfnResultFromKnownExceptions_WinRt)(const DiagnosticsInfo& diagnostics, void* returnAddress, SupportedExceptions supported, IFunctor& functor) = nullptr; + + // Plugin to call RoOriginateError (WIL use only) + __declspec(selectany) void(__stdcall *g_pfnOriginateCallback)(wil::FailureInfo const& failure) WI_PFN_NOEXCEPT = nullptr; + + // Plugin to call RoFailFastWithErrorContext (WIL use only) + __declspec(selectany) void(__stdcall* g_pfnFailfastWithContextCallback)(wil::FailureInfo const& failure) WI_PFN_NOEXCEPT = nullptr; + + // Called to tell Appverifier to ignore a particular allocation from leak tracking + // If AppVerifier is not enabled, this is a no-op + // Desktop/System Only: Automatically setup when building Windows (BUILD_WINDOWS defined) + __declspec(selectany) NTSTATUS(__stdcall *g_pfnRtlDisownModuleHeapAllocation)(_In_ HANDLE heapHandle, _In_ PVOID address) WI_PFN_NOEXCEPT = nullptr; + + // Allocate and disown the allocation so that Appverifier does not complain about a false leak + inline PVOID ProcessHeapAlloc(_In_ DWORD flags, _In_ size_t size) + { + PVOID allocation = ::HeapAlloc(::GetProcessHeap(), flags, size); + + if (g_pfnRtlDisownModuleHeapAllocation) + { + (void)g_pfnRtlDisownModuleHeapAllocation(::GetProcessHeap(), allocation); + } + + return allocation; + } + + enum class ReportFailureOptions + { + None = 0x00, + ForcePlatformException = 0x01, + MayRethrow = 0x02, + }; + DEFINE_ENUM_FLAG_OPERATORS(ReportFailureOptions); + + template + using functor_return_type = decltype((*static_cast(nullptr))()); + + template + struct functor_wrapper_void : public IFunctor + { + TFunctor&& functor; + functor_wrapper_void(TFunctor&& functor_) : functor(wistd::forward(functor_)) { } + #pragma warning(push) + #pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2 + HRESULT Run() override + { + functor(); + return S_OK; + } + #pragma warning(pop) + }; + + template + struct functor_wrapper_HRESULT : public IFunctor + { + TFunctor&& functor; + functor_wrapper_HRESULT(TFunctor& functor_) : functor(wistd::forward(functor_)) { } + HRESULT Run() override + { + return functor(); + } + }; + + template + struct functor_wrapper_other : public IFunctor + { + TFunctor&& functor; + TReturn& retVal; + functor_wrapper_other(TFunctor& functor_, TReturn& retval_) : functor(wistd::forward(functor_)), retVal(retval_) { } + #pragma warning(push) + #pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2 + HRESULT Run() override + { + retVal = functor(); + return S_OK; + } + #pragma warning(pop) + }; + + struct tag_return_void : public wistd::integral_constant + { + template + using functor_wrapper = functor_wrapper_void; + }; + + struct tag_return_HRESULT : public wistd::integral_constant + { + template + using functor_wrapper = functor_wrapper_HRESULT; + }; + + struct tag_return_other : public wistd::integral_constant + { + template + using functor_wrapper = functor_wrapper_other; + }; + + // type-trait to help discover the return type of a functor for tag/dispatch. + + template + struct return_type + { + typedef tag_return_other type; + }; + + template <> + struct return_type + { + typedef tag_return_HRESULT type; + }; + + template <> + struct return_type + { + typedef tag_return_void type; + }; + + template <> + struct return_type + { + typedef tag_return_void type; + }; + + template + using functor_tag = typename return_type>::type; + + // Forward declarations to enable use of fail fast and reporting internally... + namespace __R_NS_NAME + { + _Post_satisfies_(return == hr) __R_DIRECT_METHOD(HRESULT, Log_Hr)(__R_DIRECT_FN_PARAMS HRESULT hr) WI_NOEXCEPT; + _Post_satisfies_(return == hr) __R_DIRECT_METHOD(HRESULT, Log_HrMsg)(__R_DIRECT_FN_PARAMS HRESULT hr, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT; + _Post_satisfies_(return == err) __R_DIRECT_METHOD(DWORD, Log_Win32Msg)(__R_DIRECT_FN_PARAMS DWORD err, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT; + } + namespace __RFF_NS_NAME + { + __RFF_DIRECT_NORET_METHOD(void, FailFast_Unexpected)(__RFF_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT; + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) __RFF_CONDITIONAL_METHOD(bool, FailFast_If)(__RFF_CONDITIONAL_FN_PARAMS bool condition) WI_NOEXCEPT; + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) __RFF_CONDITIONAL_METHOD(bool, FailFast_HrIf)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition) WI_NOEXCEPT; + _Post_satisfies_(return == condition) _When_(!condition, _Analysis_noreturn_) __RFF_CONDITIONAL_METHOD(bool, FailFast_IfFalse)(__RFF_CONDITIONAL_FN_PARAMS bool condition) WI_NOEXCEPT; + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) __RFF_CONDITIONAL_METHOD(bool, FailFastImmediate_If)(bool condition) WI_NOEXCEPT; + } + + RESULT_NORETURN inline void __stdcall WilFailFast(const FailureInfo& info); + inline void LogFailure(__R_FN_PARAMS_FULL, FailureType type, const ResultStatus& resultPair, _In_opt_ PCWSTR message, + bool fWantDebugString, _Out_writes_(debugStringSizeChars) _Post_z_ PWSTR debugString, _Pre_satisfies_(debugStringSizeChars > 0) size_t debugStringSizeChars, + _Out_writes_(callContextStringSizeChars) _Post_z_ PSTR callContextString, _Pre_satisfies_(callContextStringSizeChars > 0) size_t callContextStringSizeChars, + _Out_ FailureInfo *failure) WI_NOEXCEPT; + + __declspec(noinline) inline void ReportFailure(__R_FN_PARAMS_FULL, FailureType type, const ResultStatus& resultPair, _In_opt_ PCWSTR message = nullptr, ReportFailureOptions options = ReportFailureOptions::None); + template + __declspec(noinline) inline void ReportFailure_Base(__R_FN_PARAMS_FULL, const ResultStatus& resultPair, _In_opt_ PCWSTR message = nullptr, ReportFailureOptions options = ReportFailureOptions::None); + template + inline void ReportFailure_ReplaceMsg(__R_FN_PARAMS_FULL, HRESULT hr, _Printf_format_string_ PCSTR formatString, ...); + __declspec(noinline) inline void ReportFailure_Hr(__R_FN_PARAMS_FULL, FailureType type, HRESULT hr); + template + __declspec(noinline) inline void ReportFailure_Hr(__R_FN_PARAMS_FULL, HRESULT hr); + template + __declspec(noinline) inline HRESULT ReportFailure_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported = SupportedExceptions::Default); + + //***************************************************************************** + // Fail fast helpers (for use only internally to WIL) + //***************************************************************************** + + /// @cond + #define __FAIL_FAST_ASSERT__(condition) do { if (!(condition)) { __RFF_FN(FailFast_Unexpected)(__RFF_INFO_ONLY(#condition)); } } while ((void)0, 0) + #define __FAIL_FAST_IMMEDIATE_ASSERT__(condition) do { if (!(condition)) { wil::FailureInfo failure {}; wil::details::WilFailFast(failure); } } while ((void)0, 0) + #define __FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(condition) __RFF_FN(FailFast_IfWin32BoolFalse)(__RFF_INFO(#condition) wil::verify_BOOL(condition)) + + // A simple ref-counted buffer class. The interface is very similar to shared_ptr<>, only it manages + // an allocated buffer and maintains the size. + + class shared_buffer + { + public: + shared_buffer() WI_NOEXCEPT : m_pCopy(nullptr), m_size(0) + { + } + + shared_buffer(shared_buffer const &other) WI_NOEXCEPT : m_pCopy(nullptr), m_size(0) + { + assign(other.m_pCopy, other.m_size); + } + + shared_buffer(shared_buffer &&other) WI_NOEXCEPT : + m_pCopy(other.m_pCopy), + m_size(other.m_size) + { + other.m_pCopy = nullptr; + other.m_size = 0; + } + + ~shared_buffer() WI_NOEXCEPT + { + reset(); + } + + shared_buffer& operator=(shared_buffer const &other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + assign(other.m_pCopy, other.m_size); + } + return *this; + } + + shared_buffer& operator=(shared_buffer &&other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + reset(); + m_pCopy = other.m_pCopy; + m_size = other.m_size; + other.m_pCopy = nullptr; + other.m_size = 0; + } + return *this; + } + + void reset() WI_NOEXCEPT + { + if (m_pCopy != nullptr) + { + if (0 == ::InterlockedDecrementRelease(m_pCopy)) + { + WIL_FreeMemory(m_pCopy); + } + m_pCopy = nullptr; + m_size = 0; + } + } + + bool create(_In_reads_bytes_opt_(cbData) void const *pData, size_t cbData) WI_NOEXCEPT + { + if (cbData == 0) + { + reset(); + return true; + } + + long *pCopyRefCount = reinterpret_cast(WIL_AllocateMemory(sizeof(long)+cbData)); + if (pCopyRefCount == nullptr) + { + return false; + } + + *pCopyRefCount = 0; + if (pData != nullptr) + { + memcpy_s(pCopyRefCount + 1, cbData, pData, cbData); // +1 to advance past sizeof(long) counter + } + assign(pCopyRefCount, cbData); + return true; + } + + bool create(size_t cbData) WI_NOEXCEPT + { + return create(nullptr, cbData); + } + + void* get(_Out_opt_ size_t *pSize = nullptr) const WI_NOEXCEPT + { + if (pSize != nullptr) + { + *pSize = m_size; + } + return (m_pCopy == nullptr) ? nullptr : (m_pCopy + 1); + } + + size_t size() const WI_NOEXCEPT + { + return m_size; + } + + explicit operator bool() const WI_NOEXCEPT + { + return (m_pCopy != nullptr); + } + + bool unique() const WI_NOEXCEPT + { + return ((m_pCopy != nullptr) && (*m_pCopy == 1)); + } + + private: + long *m_pCopy; // pointer to allocation: refcount + data + size_t m_size; // size of the data from m_pCopy + + void assign(_In_opt_ long *pCopy, size_t cbSize) WI_NOEXCEPT + { + reset(); + if (pCopy != nullptr) + { + m_pCopy = pCopy; + m_size = cbSize; + ::InterlockedIncrementNoFence(m_pCopy); + } + } + }; + + inline shared_buffer make_shared_buffer_nothrow(_In_reads_bytes_opt_(countBytes) void *pData, size_t countBytes) WI_NOEXCEPT + { + shared_buffer buffer; + buffer.create(pData, countBytes); + return buffer; + } + + inline shared_buffer make_shared_buffer_nothrow(size_t countBytes) WI_NOEXCEPT + { + shared_buffer buffer; + buffer.create(countBytes); + return buffer; + } + + // A small mimic of the STL shared_ptr class, but unlike shared_ptr, a pointer is not attached to the class, but is + // always simply contained within (it cannot be attached or detached). + + template + class shared_object + { + public: + shared_object() WI_NOEXCEPT : m_pCopy(nullptr) + { + } + + shared_object(shared_object const &other) WI_NOEXCEPT : + m_pCopy(other.m_pCopy) + { + if (m_pCopy != nullptr) + { + ::InterlockedIncrementNoFence(&m_pCopy->m_refCount); + } + } + + shared_object(shared_object &&other) WI_NOEXCEPT : + m_pCopy(other.m_pCopy) + { + other.m_pCopy = nullptr; + } + + ~shared_object() WI_NOEXCEPT + { + reset(); + } + + shared_object& operator=(shared_object const &other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + reset(); + m_pCopy = other.m_pCopy; + if (m_pCopy != nullptr) + { + ::InterlockedIncrementNoFence(&m_pCopy->m_refCount); + } + } + return *this; + } + + shared_object& operator=(shared_object &&other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + reset(); + m_pCopy = other.m_pCopy; + other.m_pCopy = nullptr; + } + return *this; + } + + void reset() WI_NOEXCEPT + { + if (m_pCopy != nullptr) + { + if (0 == ::InterlockedDecrementRelease(&m_pCopy->m_refCount)) + { + delete m_pCopy; + } + m_pCopy = nullptr; + } + } + + bool create() + { + RefAndObject *pObject = new(std::nothrow) RefAndObject(); + if (pObject == nullptr) + { + return false; + } + reset(); + m_pCopy = pObject; + return true; + } + + template + bool create(param_t &¶m1) + { + RefAndObject *pObject = new(std::nothrow) RefAndObject(wistd::forward(param1)); + if (pObject == nullptr) + { + return false; + } + reset(); + m_pCopy = pObject; + return true; + } + + object_t* get() const WI_NOEXCEPT + { + return (m_pCopy == nullptr) ? nullptr : &m_pCopy->m_object; + } + + explicit operator bool() const WI_NOEXCEPT + { + return (m_pCopy != nullptr); + } + + bool unique() const WI_NOEXCEPT + { + return ((m_pCopy != nullptr) && (m_pCopy->m_refCount == 1)); + } + + object_t *operator->() const WI_NOEXCEPT + { + return get(); + } + + private: + struct RefAndObject + { + long m_refCount; + object_t m_object; + + RefAndObject() : + m_refCount(1), + m_object() + { + } + + template + RefAndObject(param_t &¶m1) : + m_refCount(1), + m_object(wistd::forward(param1)) + { + } + }; + + RefAndObject *m_pCopy; + }; + + // The following functions are basically the same, but are kept separated to: + // 1) Provide a unique count and last error code per-type + // 2) Avoid merging the types to allow easy debugging (breakpoints, conditional breakpoints based + // upon count of errors from a particular type, etc) + + __declspec(noinline) inline int RecordException(HRESULT hr) WI_NOEXCEPT + { + static HRESULT volatile s_hrErrorLast = S_OK; + static long volatile s_cErrorCount = 0; + s_hrErrorLast = hr; + return ::InterlockedIncrementNoFence(&s_cErrorCount); + } + + __declspec(noinline) inline int RecordReturn(HRESULT hr) WI_NOEXCEPT + { + static HRESULT volatile s_hrErrorLast = S_OK; + static long volatile s_cErrorCount = 0; + s_hrErrorLast = hr; + return ::InterlockedIncrementNoFence(&s_cErrorCount); + } + + __declspec(noinline) inline int RecordLog(HRESULT hr) WI_NOEXCEPT + { + static HRESULT volatile s_hrErrorLast = S_OK; + static long volatile s_cErrorCount = 0; + s_hrErrorLast = hr; + return ::InterlockedIncrementNoFence(&s_cErrorCount); + } + + __declspec(noinline) inline int RecordFailFast(HRESULT hr) WI_NOEXCEPT + { + static HRESULT volatile s_hrErrorLast = S_OK; + s_hrErrorLast = hr; + return 1; + } + + inline RESULT_NORETURN void __stdcall WilRaiseFailFastException(_In_ PEXCEPTION_RECORD er, _In_opt_ PCONTEXT cr, _In_ DWORD flags) + { + // if we managed to load the pointer either through WilDynamicRaiseFailFastException (PARTITION_DESKTOP etc.) + // or via direct linkage (e.g. UWP apps), then use it. + if (g_pfnRaiseFailFastException) + { + g_pfnRaiseFailFastException(er, cr, flags); + } + // if not, as a best effort, we are just going to call the intrinsic. + __fastfail(FAST_FAIL_FATAL_APP_EXIT); + } + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + inline bool __stdcall GetModuleInformation(_In_opt_ void* address, _Out_opt_ unsigned int* addressOffset, _Out_writes_bytes_opt_(size) char* name, size_t size) WI_NOEXCEPT + { + HMODULE hModule = nullptr; + if (address && !GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast(address), &hModule)) + { + assign_to_opt_param(addressOffset, 0U); + return false; + } + if (addressOffset) + { + *addressOffset = address ? static_cast(static_cast(address) - reinterpret_cast(hModule)) : 0; + } + if (name) + { + char modulePath[MAX_PATH]; + if (!GetModuleFileNameA(hModule, modulePath, ARRAYSIZE(modulePath))) + { + return false; + } + + PCSTR start = modulePath + strlen(modulePath); + while ((start > modulePath) && (*(start - 1) != '\\')) + { + start--; + } + StringCchCopyA(name, size, start); + } + return true; + } + + inline PCSTR __stdcall GetCurrentModuleName() WI_NOEXCEPT + { + static char s_szModule[64] = {}; + static volatile bool s_fModuleValid = false; + if (!s_fModuleValid) // Races are acceptable + { + GetModuleInformation(reinterpret_cast(&RecordFailFast), nullptr, s_szModule, ARRAYSIZE(s_szModule)); + s_fModuleValid = true; + } + return s_szModule; + } + + inline void __stdcall DebugBreak() WI_NOEXCEPT + { + ::DebugBreak(); + } + + inline void __stdcall WilDynamicLoadRaiseFailFastException(_In_ PEXCEPTION_RECORD er, _In_ PCONTEXT cr, _In_ DWORD flags) + { + auto k32handle = GetModuleHandleW(L"kernelbase.dll"); + _Analysis_assume_(k32handle != nullptr); + auto pfnRaiseFailFastException = reinterpret_cast(GetProcAddress(k32handle, "RaiseFailFastException")); + if (pfnRaiseFailFastException) + { + pfnRaiseFailFastException(er, cr, flags); + } + } +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + + inline bool __stdcall GetModuleInformationFromAddress(_In_opt_ void* address, _Out_opt_ unsigned int* addressOffset, _Out_writes_bytes_opt_(size) char* buffer, size_t size) WI_NOEXCEPT + { + if (size > 0) + { + assign_to_opt_param(buffer, '\0'); + } + if (addressOffset) + { + *addressOffset = 0; + } + if (g_pfnGetModuleInformation) + { + return g_pfnGetModuleInformation(address, addressOffset, buffer, size); + } + return false; + } + + __declspec(noinline) inline HRESULT NtStatusToHr(NTSTATUS status) WI_NOEXCEPT + { + // The following conversions are the only known incorrect mappings in RtlNtStatusToDosErrorNoTeb + if (SUCCEEDED_NTSTATUS(status)) + { + // All successful status codes have only one hresult equivalent, S_OK + return S_OK; + } + if (status == static_cast(STATUS_NO_MEMORY)) + { + // RtlNtStatusToDosErrorNoTeb maps STATUS_NO_MEMORY to the less popular of two Win32 no memory error codes resulting in an unexpected mapping + return E_OUTOFMEMORY; + } + + if (g_pfnRtlNtStatusToDosErrorNoTeb != nullptr) + { + DWORD err = g_pfnRtlNtStatusToDosErrorNoTeb(status); + + // ERROR_MR_MID_NOT_FOUND indicates a bug in the originator of the error (failure to add a mapping to the Win32 error codes). + // There are known instances of this bug which are unlikely to be fixed soon, and it's always possible that additional instances + // could be added in the future. In these cases, it's better to use HRESULT_FROM_NT rather than returning a meaningless error. + if ((err != 0) && (err != ERROR_MR_MID_NOT_FOUND)) + { + return __HRESULT_FROM_WIN32(err); + } + } + + return HRESULT_FROM_NT(status); + } + + __declspec(noinline) inline NTSTATUS HrToNtStatus(HRESULT hr) WI_NOEXCEPT + { + // Constants taken from ntstatus.h + static constexpr NTSTATUS WIL_STATUS_INVALID_PARAMETER = 0xC000000D; + static constexpr NTSTATUS WIL_STATUS_INTERNAL_ERROR = 0xC00000E5; + static constexpr NTSTATUS WIL_STATUS_INTEGER_OVERFLOW = 0xC0000095; + static constexpr NTSTATUS WIL_STATUS_OBJECT_PATH_NOT_FOUND = 0xC000003A; + static constexpr NTSTATUS WIL_STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034; + static constexpr NTSTATUS WIL_STATUS_NOT_IMPLEMENTED = 0xC0000002; + static constexpr NTSTATUS WIL_STATUS_BUFFER_OVERFLOW = 0x80000005; + static constexpr NTSTATUS WIL_STATUS_IMPLEMENTATION_LIMIT = 0xC000042B; + static constexpr NTSTATUS WIL_STATUS_NO_MORE_MATCHES = 0xC0000273; + static constexpr NTSTATUS WIL_STATUS_ILLEGAL_CHARACTER = 0xC0000161; + static constexpr NTSTATUS WIL_STATUS_UNDEFINED_CHARACTER = 0xC0000163; + static constexpr NTSTATUS WIL_STATUS_BUFFER_TOO_SMALL = 0xC0000023; + static constexpr NTSTATUS WIL_STATUS_DISK_FULL = 0xC000007F; + static constexpr NTSTATUS WIL_STATUS_OBJECT_NAME_INVALID = 0xC0000033; + static constexpr NTSTATUS WIL_STATUS_DLL_NOT_FOUND = 0xC0000135; + static constexpr NTSTATUS WIL_STATUS_REVISION_MISMATCH = 0xC0000059; + static constexpr NTSTATUS WIL_STATUS_XML_PARSE_ERROR = 0xC000A083; + static constexpr HRESULT WIL_E_FAIL = 0x80004005; + + NTSTATUS status = STATUS_SUCCESS; + + switch (hr) + { + case S_OK: + status = STATUS_SUCCESS; + break; + case E_INVALIDARG: + status = WIL_STATUS_INVALID_PARAMETER; + break; + case __HRESULT_FROM_WIN32(ERROR_INTERNAL_ERROR): + status = WIL_STATUS_INTERNAL_ERROR; + break; + case E_OUTOFMEMORY: + status = STATUS_NO_MEMORY; + break; + case __HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW): + status = WIL_STATUS_INTEGER_OVERFLOW; + break; + case __HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND): + status = WIL_STATUS_OBJECT_PATH_NOT_FOUND; + break; + case __HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND): + status = WIL_STATUS_OBJECT_NAME_NOT_FOUND; + break; + case __HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION): + status = WIL_STATUS_NOT_IMPLEMENTED; + break; + case __HRESULT_FROM_WIN32(ERROR_MORE_DATA): + status = WIL_STATUS_BUFFER_OVERFLOW; + break; + case __HRESULT_FROM_WIN32(ERROR_IMPLEMENTATION_LIMIT): + status = WIL_STATUS_IMPLEMENTATION_LIMIT; + break; + case __HRESULT_FROM_WIN32(ERROR_NO_MORE_MATCHES): + status = WIL_STATUS_NO_MORE_MATCHES; + break; + case __HRESULT_FROM_WIN32(ERROR_ILLEGAL_CHARACTER): + status = WIL_STATUS_ILLEGAL_CHARACTER; + break; + case __HRESULT_FROM_WIN32(ERROR_UNDEFINED_CHARACTER): + status = WIL_STATUS_UNDEFINED_CHARACTER; + break; + case __HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER): + status = WIL_STATUS_BUFFER_TOO_SMALL; + break; + case __HRESULT_FROM_WIN32(ERROR_DISK_FULL): + status = WIL_STATUS_DISK_FULL; + break; + case __HRESULT_FROM_WIN32(ERROR_INVALID_NAME): + status = WIL_STATUS_OBJECT_NAME_INVALID; + break; + case __HRESULT_FROM_WIN32(ERROR_MOD_NOT_FOUND): + status = WIL_STATUS_DLL_NOT_FOUND; + break; + case __HRESULT_FROM_WIN32(ERROR_OLD_WIN_VERSION): + status = WIL_STATUS_REVISION_MISMATCH; + break; + case WIL_E_FAIL: + status = STATUS_UNSUCCESSFUL; + break; + case __HRESULT_FROM_WIN32(ERROR_XML_PARSE_ERROR): + status = WIL_STATUS_XML_PARSE_ERROR; + break; + case __HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION): + status = STATUS_NONCONTINUABLE_EXCEPTION; + break; + default: + if ((hr & FACILITY_NT_BIT) != 0) + { + status = (hr & ~FACILITY_NT_BIT); + } + else if (HRESULT_FACILITY(hr) == FACILITY_WIN32) + { + status = __NTSTATUS_FROM_WIN32(HRESULT_CODE(hr)); + } + else if (HRESULT_FACILITY(hr) == FACILITY_SSPI) + { + status = ((NTSTATUS)(hr) <= 0 ? ((NTSTATUS)(hr)) : ((NTSTATUS)(((hr) & 0x0000FFFF) | (FACILITY_SSPI << 16) | ERROR_SEVERITY_ERROR))); + } + else + { + status = WIL_STATUS_INTERNAL_ERROR; + } + break; + } + return status; + } + + // The following set of functions all differ only based upon number of arguments. They are unified in their handling + // of data from each of the various error-handling types (fast fail, exceptions, etc.). + _Post_equals_last_error_ + inline DWORD GetLastErrorFail(__R_FN_PARAMS_FULL) WI_NOEXCEPT + { + __R_FN_UNREFERENCED; + auto err = ::GetLastError(); + if (SUCCEEDED_WIN32(err)) + { + // This function should only be called when GetLastError() is set to a FAILURE. + // If you hit this assert (or are reviewing this failure telemetry), then there are one of three issues: + // 1) Your code is using a macro (such as RETURN_IF_WIN32_BOOL_FALSE()) on a function that does not actually + // set the last error (consult MSDN). + // 2) Your macro check against the error is not immediately after the API call. Pushing it later can result + // in another API call between the previous one and the check resetting the last error. + // 3) The API you're calling has a bug in it and does not accurately set the last error (there are a few + // examples here, such as SendMessageTimeout() that don't accurately set the last error). For these, + // please send mail to 'wildisc' when found and work-around with win32errorhelpers. + + WI_USAGE_ERROR_FORWARD("CALLER BUG: Macro usage error detected. GetLastError() does not have an error."); + return ERROR_ASSERTION_FAILURE; + } + return err; + } + + _Translates_last_error_to_HRESULT_ + inline HRESULT GetLastErrorFailHr(__R_FN_PARAMS_FULL) WI_NOEXCEPT + { + return HRESULT_FROM_WIN32(GetLastErrorFail(__R_FN_CALL_FULL)); + } + + _Translates_last_error_to_HRESULT_ + inline __declspec(noinline) HRESULT GetLastErrorFailHr() WI_NOEXCEPT + { + __R_FN_LOCALS_FULL_RA; + return GetLastErrorFailHr(__R_FN_CALL_FULL); + } + + inline void PrintLoggingMessage(_Out_writes_(cchDest) _Post_z_ PWSTR pszDest, _Pre_satisfies_(cchDest > 0) size_t cchDest, _In_opt_ _Printf_format_string_ PCSTR formatString, _In_opt_ va_list argList) WI_NOEXCEPT + { + if (formatString == nullptr) + { + pszDest[0] = L'\0'; + } + else if (argList == nullptr) + { + StringCchPrintfW(pszDest, cchDest, L"%hs", formatString); + } + else + { + wchar_t szFormatWide[2048]; + StringCchPrintfW(szFormatWide, ARRAYSIZE(szFormatWide), L"%hs", formatString); + StringCchVPrintfW(pszDest, cchDest, szFormatWide, argList); + } + } + +#pragma warning(push) +#pragma warning(disable:__WARNING_RETURNING_BAD_RESULT) + // NOTE: The following two functions are unfortunate copies of strsafe.h functions that have been copied to reduce the friction associated with using + // Result.h and ResultException.h in a build that does not have WINAPI_PARTITION_DESKTOP defined (where these are conditionally enabled). + + static STRSAFEAPI WilStringLengthWorkerA(_In_reads_or_z_(cchMax) STRSAFE_PCNZCH psz, _In_ _In_range_(<= , STRSAFE_MAX_CCH) size_t cchMax, _Out_opt_ _Deref_out_range_(< , cchMax) _Deref_out_range_(<= , _String_length_(psz)) size_t* pcchLength) + { + HRESULT hr = S_OK; + size_t cchOriginalMax = cchMax; + while (cchMax && (*psz != '\0')) + { + psz++; + cchMax--; + } + if (cchMax == 0) + { + // the string is longer than cchMax + hr = STRSAFE_E_INVALID_PARAMETER; + } + if (pcchLength) + { + if (SUCCEEDED(hr)) + { + *pcchLength = cchOriginalMax - cchMax; + } + else + { + *pcchLength = 0; + } + } + return hr; + } + + _Must_inspect_result_ STRSAFEAPI StringCchLengthA(_In_reads_or_z_(cchMax) STRSAFE_PCNZCH psz, _In_ _In_range_(1, STRSAFE_MAX_CCH) size_t cchMax, _Out_opt_ _Deref_out_range_(<, cchMax) _Deref_out_range_(<= , _String_length_(psz)) size_t* pcchLength) + { + HRESULT hr; + if ((psz == NULL) || (cchMax > STRSAFE_MAX_CCH)) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + else + { + hr = WilStringLengthWorkerA(psz, cchMax, pcchLength); + } + if (FAILED(hr) && pcchLength) + { + *pcchLength = 0; + } + return hr; + } +#pragma warning(pop) + + _Post_satisfies_(cchDest > 0 && cchDest <= cchMax) static STRSAFEAPI WilStringValidateDestA(_In_reads_opt_(cchDest) STRSAFE_PCNZCH /*pszDest*/, _In_ size_t cchDest, _In_ const size_t cchMax) + { + HRESULT hr = S_OK; + if ((cchDest == 0) || (cchDest > cchMax)) + { + hr = STRSAFE_E_INVALID_PARAMETER; + } + return hr; + } + + static STRSAFEAPI WilStringVPrintfWorkerA(_Out_writes_(cchDest) _Always_(_Post_z_) STRSAFE_LPSTR pszDest, _In_ _In_range_(1, STRSAFE_MAX_CCH) size_t cchDest, _Always_(_Out_opt_ _Deref_out_range_(<=, cchDest - 1)) size_t* pcchNewDestLength, _In_ _Printf_format_string_ STRSAFE_LPCSTR pszFormat, _In_ va_list argList) + { + HRESULT hr = S_OK; + int iRet; + size_t cchMax; + size_t cchNewDestLength = 0; + + // leave the last space for the null terminator + cchMax = cchDest - 1; +#undef STRSAFE_USE_SECURE_CRT +#define STRSAFE_USE_SECURE_CRT 1 + #if (STRSAFE_USE_SECURE_CRT == 1) && !defined(STRSAFE_LIB_IMPL) + iRet = _vsnprintf_s(pszDest, cchDest, cchMax, pszFormat, argList); + #else + #pragma warning(push) + #pragma warning(disable: __WARNING_BANNED_API_USAGE)// "STRSAFE not included" + iRet = _vsnprintf(pszDest, cchMax, pszFormat, argList); + #pragma warning(pop) + #endif + // ASSERT((iRet < 0) || (((size_t)iRet) <= cchMax)); + + if ((iRet < 0) || (((size_t)iRet) > cchMax)) + { + // need to null terminate the string + pszDest += cchMax; + *pszDest = '\0'; + + cchNewDestLength = cchMax; + + // we have truncated pszDest + hr = STRSAFE_E_INSUFFICIENT_BUFFER; + } + else if (((size_t)iRet) == cchMax) + { + // need to null terminate the string + pszDest += cchMax; + *pszDest = '\0'; + + cchNewDestLength = cchMax; + } + else + { + cchNewDestLength = (size_t)iRet; + } + + if (pcchNewDestLength) + { + *pcchNewDestLength = cchNewDestLength; + } + + return hr; + } + + __inline HRESULT StringCchPrintfA( _Out_writes_(cchDest) _Always_(_Post_z_) STRSAFE_LPSTR pszDest, _In_ size_t cchDest, _In_ _Printf_format_string_ STRSAFE_LPCSTR pszFormat, ...) + { + HRESULT hr; + hr = wil::details::WilStringValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH); + if (SUCCEEDED(hr)) + { + va_list argList; + va_start(argList, pszFormat); + hr = wil::details::WilStringVPrintfWorkerA(pszDest, cchDest, NULL, pszFormat, argList); + va_end(argList); + } + else if (cchDest > 0) + { + *pszDest = '\0'; + } + return hr; + } + + _Ret_range_(sizeof(char), (psz == nullptr) ? sizeof(char) : (_String_length_(psz) + sizeof(char))) + inline size_t ResultStringSize(_In_opt_ PCSTR psz) + { return (psz == nullptr) ? sizeof(char) : (strlen(psz) + sizeof(char)); } + + _Ret_range_(sizeof(wchar_t), (psz == nullptr) ? sizeof(wchar_t) : ((_String_length_(psz) + 1) * sizeof(wchar_t))) + inline size_t ResultStringSize(_In_opt_ PCWSTR psz) + { return (psz == nullptr) ? sizeof(wchar_t) : (wcslen(psz) + 1) * sizeof(wchar_t); } + + template + _Ret_range_(pStart, pEnd) inline unsigned char* WriteResultString( + _Pre_satisfies_(pStart <= pEnd) + _When_((pStart == pEnd) || (pszString == nullptr) || (pszString[0] == 0), _In_opt_) + _When_((pStart != pEnd) && (pszString != nullptr) && (pszString[0] != 0), _Out_writes_bytes_opt_(_String_length_(pszString) * sizeof(pszString[0]))) + unsigned char* pStart, _Pre_satisfies_(pEnd >= pStart) unsigned char* pEnd, _In_opt_z_ TString pszString, _Outptr_result_maybenull_z_ TString* ppszBufferString) + { + // No space? Null string? Do nothing. + if ((pStart == pEnd) || !pszString || !*pszString) + { + assign_null_to_opt_param(ppszBufferString); + return pStart; + } + + // Treats the range pStart--pEnd as a memory buffer into which pszString is copied. A pointer to + // the start of the copied string is placed into ppszStringBuffer. If the buffer isn't big enough, + // do nothing, and tell the caller nothing was written. + size_t const stringSize = ResultStringSize(pszString); + size_t const bufferSize = pEnd - pStart; + if (bufferSize < stringSize) + { + assign_null_to_opt_param(ppszBufferString); + return pStart; + } + + memcpy_s(pStart, bufferSize, pszString, stringSize); + assign_to_opt_param(ppszBufferString, reinterpret_cast(pStart)); + return pStart + stringSize; + } + + _Ret_range_(0, (cchMax > 0) ? cchMax - 1 : 0) inline size_t UntrustedStringLength(_In_ PCSTR psz, _In_ size_t cchMax) { size_t cbLength; return SUCCEEDED(wil::details::StringCchLengthA(psz, cchMax, &cbLength)) ? cbLength : 0; } + _Ret_range_(0, (cchMax > 0) ? cchMax - 1 : 0) inline size_t UntrustedStringLength(_In_ PCWSTR psz, _In_ size_t cchMax) { size_t cbLength; return SUCCEEDED(::StringCchLengthW(psz, cchMax, &cbLength)) ? cbLength : 0; } + + template + _Ret_range_(pStart, pEnd) inline unsigned char *GetResultString(_In_reads_to_ptr_opt_(pEnd) unsigned char *pStart, _Pre_satisfies_(pEnd >= pStart) unsigned char *pEnd, _Out_ TString *ppszBufferString) + { + size_t cchLen = UntrustedStringLength(reinterpret_cast(pStart), (pEnd - pStart) / sizeof((*ppszBufferString)[0])); + *ppszBufferString = (cchLen > 0) ? reinterpret_cast(pStart) : nullptr; + auto pReturn = min(pEnd, pStart + ((cchLen + 1) * sizeof((*ppszBufferString)[0]))); + __analysis_assume((pReturn >= pStart) && (pReturn <= pEnd)); + return pReturn; + } + } // details namespace + /// @endcond + + //***************************************************************************** + // WIL result handling initializers + // + // Generally, callers do not need to manually initialize WIL. This header creates + // the appropriate .CRT init section pieces through global objects to ensure that + // WilInitialize... is called before DllMain or main(). + // + // Certain binaries do not link with the CRT or do not support .CRT-section based + // initializers. Those binaries must link only with other static libraries that + // also set RESULT_SUPPRESS_STATIC_INITIALIZERS to ensure no .CRT inits are left, + // and they should call one of the WilInitialize_ResultMacros_??? methods during + // their initialization phase. Skipping this initialization path is OK as well, + // but results in a slightly degraded experience with result reporting. + // + // Calling WilInitialize_ResultMacros_DesktopOrSystem_SuppressPrivateApiUse provides: + // - The name of the current module in wil::FailureInfo::pszModule + // - The name of the returning-to module during wil\staging.h failures + //***************************************************************************** + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + //! Call this method to initialize WIL manually in a module where RESULT_SUPPRESS_STATIC_INITIALIZERS is required. WIL will + //! only use publicly documented APIs. + inline void WilInitialize_ResultMacros_DesktopOrSystem_SuppressPrivateApiUse() + { + details::g_pfnGetModuleName = details::GetCurrentModuleName; + details::g_pfnGetModuleInformation = details::GetModuleInformation; + details::g_pfnDebugBreak = details::DebugBreak; + details::g_pfnRaiseFailFastException = wil::details::WilDynamicLoadRaiseFailFastException; + } + + /// @cond + namespace details + { +#ifndef RESULT_SUPPRESS_STATIC_INITIALIZERS +#if !defined(BUILD_WINDOWS) || defined(WIL_SUPPRESS_PRIVATE_API_USE) + WI_HEADER_INITITALIZATION_FUNCTION(WilInitialize_ResultMacros_DesktopOrSystem_SuppressPrivateApiUse, [] + { + ::wil::WilInitialize_ResultMacros_DesktopOrSystem_SuppressPrivateApiUse(); + return 1; + }); +#endif +#endif + } + /// @endcond +#else // !WINAPI_PARTITION_DESKTOP, !WINAPI_PARTITION_SYSTEM, explicitly assume these modules can direct link + namespace details + { + WI_HEADER_INITITALIZATION_FUNCTION(WilInitialize_ResultMacros_AppOnly, [] + { + g_pfnRaiseFailFastException = ::RaiseFailFastException; + return 1; + }); + } +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + + //***************************************************************************** + // Public Error Handling Helpers + //***************************************************************************** + + //! Call this method to determine if process shutdown is in progress (allows avoiding work during dll unload). + inline bool ProcessShutdownInProgress() + { + return (details::g_processShutdownInProgress || (details::g_pfnDllShutdownInProgress ? details::g_pfnDllShutdownInProgress() : false)); + } + + /** Use this object to wrap an object that wants to prevent its destructor from being run when the process is shutting down, + but the hosting DLL doesn't support CRT initializers (such as kernelbase.dll). The hosting DLL is responsible for calling + Construct() and Destroy() to manually run the constructor and destructor during DLL load & unload. + Upon process shutdown a method (ProcessShutdown()) is called that must be implemented on the object, otherwise the destructor is + called as is typical. */ + template + class manually_managed_shutdown_aware_object + { + public: + void construct() + { + void* var = &m_raw; + ::new(var) T(); + } + + void destroy() + { + if (ProcessShutdownInProgress()) + { + get().ProcessShutdown(); + } + else + { + (&get())->~T(); + } + } + + //! Retrieves a reference to the contained object + T& get() WI_NOEXCEPT + { + return *reinterpret_cast(&m_raw); + } + + private: + alignas(T) unsigned char m_raw[sizeof(T)]; + }; + + /** Use this object to wrap an object that wants to prevent its destructor from being run when the process is shutting down. + Upon process shutdown a method (ProcessShutdown()) is called that must be implemented on the object, otherwise the destructor is + called as is typical. */ + template + class shutdown_aware_object + { + public: + shutdown_aware_object() + { + m_object.construct(); + } + + ~shutdown_aware_object() + { + m_object.destroy(); + } + + //! Retrieves a reference to the contained object + T& get() WI_NOEXCEPT + { + return m_object.get(); + } + + private: + manually_managed_shutdown_aware_object m_object; + }; + + /** Use this object to wrap an object that wants to prevent its destructor from being run when the process is shutting down. */ + template + class object_without_destructor_on_shutdown + { + public: + object_without_destructor_on_shutdown() + { + void* var = &m_raw; + ::new(var) T(); + } + + ~object_without_destructor_on_shutdown() + { + if (!ProcessShutdownInProgress()) + { + get().~T(); + } + } + + //! Retrieves a reference to the contained object + T& get() WI_NOEXCEPT + { + return *reinterpret_cast(&m_raw); + } + + private: + alignas(T) unsigned char m_raw[sizeof(T)]{}; + }; + + /** Forward your DLLMain to this function so that WIL can have visibility into whether a DLL unload is because + of termination or normal unload. Note that when g_pfnDllShutdownInProgress is set, WIL attempts to make this + determination on its own without this callback. Suppressing private APIs requires use of this. */ + inline void DLLMain(HINSTANCE, DWORD reason, _In_opt_ LPVOID reserved) + { + if (!details::g_processShutdownInProgress) + { + if ((reason == DLL_PROCESS_DETACH) && (reserved != nullptr)) + { + details::g_processShutdownInProgress = true; + } + } + } + + // [optionally] Plug in fallback telemetry reporting + // Normally, the callback is owned by including ResultLogging.h in the including module. Alternatively a module + // could re-route fallback telemetry to any ONE specific provider by calling this method. + inline void SetResultTelemetryFallback(_In_opt_ decltype(details::g_pfnTelemetryCallback) callbackFunction) + { + // Only ONE telemetry provider can own the fallback telemetry callback. + __FAIL_FAST_IMMEDIATE_ASSERT__((details::g_pfnTelemetryCallback == nullptr) || (callbackFunction == nullptr) || (details::g_pfnTelemetryCallback == callbackFunction)); + details::g_pfnTelemetryCallback = callbackFunction; + } + + // [optionally] Plug in result logging (do not use for telemetry) + // This provides the ability for a module to hook all failures flowing through the system for inspection + // and/or logging. + inline void SetResultLoggingCallback(_In_opt_ decltype(details::g_pfnLoggingCallback) callbackFunction) + { + // Only ONE function can own the result logging callback + __FAIL_FAST_IMMEDIATE_ASSERT__((details::g_pfnLoggingCallback == nullptr) || (callbackFunction == nullptr) || (details::g_pfnLoggingCallback == callbackFunction)); + details::g_pfnLoggingCallback = callbackFunction; + } + + // [optionally] Plug in custom result messages + // There are some purposes that require translating the full information that is known about a failure + // into a message to be logged (either through the console for debugging OR as the message attached + // to a Platform::Exception^). This callback allows a module to format the string itself away from the + // default. + inline void SetResultMessageCallback(_In_opt_ decltype(wil::g_pfnResultLoggingCallback) callbackFunction) + { + // Only ONE function can own the result message callback + __FAIL_FAST_IMMEDIATE_ASSERT__((g_pfnResultLoggingCallback == nullptr) || (callbackFunction == nullptr) || (g_pfnResultLoggingCallback == callbackFunction)); + details::g_resultMessageCallbackSet = true; + g_pfnResultLoggingCallback = callbackFunction; + } + + // [optionally] Plug in exception remapping + // A module can plug a callback in using this function to setup custom exception handling to allow any + // exception type to be converted into an HRESULT from exception barriers. + inline void SetResultFromCaughtExceptionCallback(_In_opt_ decltype(wil::g_pfnResultFromCaughtException) callbackFunction) + { + // Only ONE function can own the exception conversion + __FAIL_FAST_IMMEDIATE_ASSERT__((g_pfnResultFromCaughtException == nullptr) || (callbackFunction == nullptr) || (g_pfnResultFromCaughtException == callbackFunction)); + g_pfnResultFromCaughtException = callbackFunction; + } + + // [optionally] Plug in exception remapping + // This provides the ability for a module to call RoOriginateError in case of a failure. + // Normally, the callback is owned by including result_originate.h in the including module. Alternatively a module + // could re-route error origination callback to its own implementation. + inline void SetOriginateErrorCallback(_In_opt_ decltype(details::g_pfnOriginateCallback) callbackFunction) + { + // Only ONE function can own the error origination callback + __FAIL_FAST_IMMEDIATE_ASSERT__((details::g_pfnOriginateCallback == nullptr) || (callbackFunction == nullptr) || (details::g_pfnOriginateCallback == callbackFunction)); + details::g_pfnOriginateCallback = callbackFunction; + } + + // [optionally] Plug in failfast callback + // This provides the ability for a module to call RoFailFastWithErrorContext in the failfast handler -if- there is stowed + // exception data available. Normally, the callback is owned by including result_originate.h in the including module. + // Alternatively a module could re-route to its own implementation. + inline void SetFailfastWithContextCallback(_In_opt_ decltype(details::g_pfnFailfastWithContextCallback) callbackFunction) + { + // Only ONE function can own the failfast with context callback + __FAIL_FAST_IMMEDIATE_ASSERT__((details::g_pfnFailfastWithContextCallback == nullptr) || (callbackFunction == nullptr) || (details::g_pfnFailfastWithContextCallback == callbackFunction)); + details::g_pfnFailfastWithContextCallback = callbackFunction; + } + + // A RAII wrapper around the storage of a FailureInfo struct (which is normally meant to be consumed + // on the stack or from the caller). The storage of FailureInfo needs to copy some data internally + // for lifetime purposes. + + class StoredFailureInfo + { + public: + StoredFailureInfo() WI_NOEXCEPT + { + ::ZeroMemory(&m_failureInfo, sizeof(m_failureInfo)); + } + + StoredFailureInfo(FailureInfo const &other) WI_NOEXCEPT + { + SetFailureInfo(other); + } + + FailureInfo const & GetFailureInfo() const WI_NOEXCEPT + { + return m_failureInfo; + } + + void SetFailureInfo(FailureInfo const &failure) WI_NOEXCEPT + { + m_failureInfo = failure; + + size_t const cbNeed = details::ResultStringSize(failure.pszMessage) + + details::ResultStringSize(failure.pszCode) + + details::ResultStringSize(failure.pszFunction) + + details::ResultStringSize(failure.pszFile) + + details::ResultStringSize(failure.pszCallContext) + + details::ResultStringSize(failure.pszModule) + + details::ResultStringSize(failure.callContextCurrent.contextName) + + details::ResultStringSize(failure.callContextCurrent.contextMessage) + + details::ResultStringSize(failure.callContextOriginating.contextName) + + details::ResultStringSize(failure.callContextOriginating.contextMessage); + + if (!m_spStrings.unique() || (m_spStrings.size() < cbNeed)) + { + m_spStrings.reset(); + m_spStrings.create(cbNeed); + } + + size_t cbAlloc; + unsigned char *pBuffer = static_cast(m_spStrings.get(&cbAlloc)); + unsigned char *pBufferEnd = (pBuffer != nullptr) ? pBuffer + cbAlloc : nullptr; + + if (pBuffer) + { + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, failure.pszMessage, &m_failureInfo.pszMessage); + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, failure.pszCode, &m_failureInfo.pszCode); + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, failure.pszFunction, &m_failureInfo.pszFunction); + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, failure.pszFile, &m_failureInfo.pszFile); + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, failure.pszCallContext, &m_failureInfo.pszCallContext); + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, failure.pszModule, &m_failureInfo.pszModule); + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, failure.callContextCurrent.contextName, &m_failureInfo.callContextCurrent.contextName); + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, failure.callContextCurrent.contextMessage, &m_failureInfo.callContextCurrent.contextMessage); + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, failure.callContextOriginating.contextName, &m_failureInfo.callContextOriginating.contextName); + pBuffer = details::WriteResultString(pBuffer, pBufferEnd, failure.callContextOriginating.contextMessage, &m_failureInfo.callContextOriginating.contextMessage); + ZeroMemory(pBuffer, pBufferEnd - pBuffer); + } + } + + // Relies upon generated copy constructor and assignment operator + + protected: + FailureInfo m_failureInfo; + details::shared_buffer m_spStrings; + }; + +#if defined(WIL_ENABLE_EXCEPTIONS) || defined(WIL_FORCE_INCLUDE_RESULT_EXCEPTION) + + //! This is WIL's default exception class thrown from all THROW_XXX macros (outside of c++/cx). + //! This class stores all of the FailureInfo context that is available when the exception is thrown. It's also caught by + //! exception guards for automatic conversion to HRESULT. + //! + //! In c++/cx, Platform::Exception^ is used instead of this class (unless @ref wil::g_fResultThrowPlatformException has been changed). + class ResultException : public std::exception + { + public: + //! Constructs a new ResultException from an existing FailureInfo. + ResultException(const FailureInfo& failure) WI_NOEXCEPT : + m_failure(failure) + { + } + + //! Constructs a new exception type from a given HRESULT (use only for constructing custom exception types). + ResultException(_Pre_satisfies_(hr < 0) HRESULT hr) WI_NOEXCEPT : + m_failure(CustomExceptionFailureInfo(hr)) + { + } + + //! Returns the failed HRESULT that this exception represents. + _Always_(_Post_satisfies_(return < 0)) HRESULT GetErrorCode() const WI_NOEXCEPT + { + HRESULT const hr = m_failure.GetFailureInfo().hr; + __analysis_assume(hr < 0); + return hr; + } + + //! Returns the failed NTSTATUS that this exception represents. + _Always_(_Post_satisfies_(return < 0)) NTSTATUS GetStatusCode() const WI_NOEXCEPT + { + NTSTATUS const status = m_failure.GetFailureInfo().status; + __analysis_assume(status < 0); + return status; + } + + //! Get a reference to the stored FailureInfo. + FailureInfo const & GetFailureInfo() const WI_NOEXCEPT + { + return m_failure.GetFailureInfo(); + } + + //! Sets the stored FailureInfo (use primarily only when constructing custom exception types). + void SetFailureInfo(FailureInfo const &failure) WI_NOEXCEPT + { + m_failure.SetFailureInfo(failure); + } + + //! Provides a string representing the FailureInfo from this exception. + inline const char * __CLR_OR_THIS_CALL what() const WI_NOEXCEPT override + { + if (!m_what) + { + wchar_t message[2048]; + GetFailureLogString(message, ARRAYSIZE(message), m_failure.GetFailureInfo()); + + char messageA[1024]; + wil::details::StringCchPrintfA(messageA, ARRAYSIZE(messageA), "%ws", message); + m_what.create(messageA, strlen(messageA) + sizeof(*messageA)); + } + return static_cast(m_what.get()); + } + + // Relies upon auto-generated copy constructor and assignment operator + protected: + StoredFailureInfo m_failure; //!< The failure information for this exception + mutable details::shared_buffer m_what; //!< The on-demand generated what() string + + //! Use to produce a custom FailureInfo from an HRESULT (use only when constructing custom exception types). + static FailureInfo CustomExceptionFailureInfo(HRESULT hr) WI_NOEXCEPT + { + FailureInfo fi = {}; + fi.type = FailureType::Exception; + fi.hr = hr; + return fi; + } + }; +#endif + + + //***************************************************************************** + // Public Helpers that catch -- mostly only enabled when exceptions are enabled + //***************************************************************************** + + // ResultFromCaughtException is a function that is meant to be called from within a catch(...) block. Internally + // it re-throws and catches the exception to convert it to an HRESULT. If an exception is of an unrecognized type + // the function will fail fast. + // + // try + // { + // // Code + // } + // catch (...) + // { + // hr = wil::ResultFromCaughtException(); + // } + _Always_(_Post_satisfies_(return < 0)) + __declspec(noinline) inline HRESULT ResultFromCaughtException() WI_NOEXCEPT + { + bool isNormalized = false; + HRESULT hr = S_OK; + if (details::g_pfnResultFromCaughtExceptionInternal) + { + hr = details::g_pfnResultFromCaughtExceptionInternal(nullptr, 0, &isNormalized).hr; + } + if (FAILED(hr)) + { + return hr; + } + + // Caller bug: an unknown exception was thrown + __WIL_PRIVATE_FAIL_FAST_HR_IF(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION), g_fResultFailFastUnknownExceptions); + return __HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION); + } + + //! Identical to 'throw;', but can be called from error-code neutral code to rethrow in code that *may* be running under an exception context + inline void RethrowCaughtException() + { + // We always want to rethrow the exception under normal circumstances. Ordinarily, we could actually guarantee + // this as we should be able to rethrow if we caught an exception, but if we got here in the middle of running + // dynamic initializers, then it's possible that we haven't yet setup the rethrow function pointer, thus the + // runtime check without the noreturn annotation. + + if (details::g_pfnRethrow) + { + details::g_pfnRethrow(); + } + } + + //! Identical to 'throw ResultException(failure);', but can be referenced from error-code neutral code + inline void ThrowResultException(const FailureInfo& failure) + { + if (details::g_pfnThrowResultException) + { + details::g_pfnThrowResultException(failure); + } + } + + //! @cond + namespace details + { +#ifdef WIL_ENABLE_EXCEPTIONS + //***************************************************************************** + // Private helpers to catch and propagate exceptions + //***************************************************************************** + + RESULT_NORETURN inline void TerminateAndReportError(_In_opt_ PEXCEPTION_POINTERS) + { + // This is an intentional fail-fast that was caught by an exception guard with WIL. Look back up the callstack to determine + // the source of the actual exception being thrown. The exception guard used by the calling code did not expect this + // exception type to be thrown or is specifically requesting fail-fast for this class of exception. + + FailureInfo failure{}; + WilFailFast(failure); + } + + inline void MaybeGetExceptionString(const ResultException& exception, _Out_writes_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars) + { + if (debugString) + { + GetFailureLogString(debugString, debugStringChars, exception.GetFailureInfo()); + } + } + + inline void MaybeGetExceptionString(const std::exception& exception, _Out_writes_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars) + { + if (debugString) + { + StringCchPrintfW(debugString, debugStringChars, L"std::exception: %hs", exception.what()); + } + } + + inline HRESULT ResultFromKnownException(const ResultException& exception, const DiagnosticsInfo& diagnostics, void* returnAddress) + { + wchar_t message[2048]; + message[0] = L'\0'; + MaybeGetExceptionString(exception, message, ARRAYSIZE(message)); + auto hr = exception.GetErrorCode(); + wil::details::ReportFailure_Base(__R_DIAGNOSTICS_RA(diagnostics, returnAddress), ResultStatus::FromResult(hr), message); + return hr; + } + + inline HRESULT ResultFromKnownException(const std::bad_alloc& exception, const DiagnosticsInfo& diagnostics, void* returnAddress) + { + wchar_t message[2048]; + message[0] = L'\0'; + MaybeGetExceptionString(exception, message, ARRAYSIZE(message)); + constexpr auto hr = E_OUTOFMEMORY; + wil::details::ReportFailure_Base(__R_DIAGNOSTICS_RA(diagnostics, returnAddress), ResultStatus::FromResult(hr), message); + return hr; + } + + inline HRESULT ResultFromKnownException(const std::exception& exception, const DiagnosticsInfo& diagnostics, void* returnAddress) + { + wchar_t message[2048]; + message[0] = L'\0'; + MaybeGetExceptionString(exception, message, ARRAYSIZE(message)); + constexpr auto hr = __HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION); + ReportFailure_Base(__R_DIAGNOSTICS_RA(diagnostics, returnAddress), ResultStatus::FromResult(hr), message); + return hr; + } + + inline HRESULT ResultFromKnownException_CppWinRT(const DiagnosticsInfo& diagnostics, void* returnAddress) + { + if (g_pfnResultFromCaughtException_CppWinRt) + { + wchar_t message[2048]; + message[0] = L'\0'; + bool ignored; + auto hr = g_pfnResultFromCaughtException_CppWinRt(message, ARRAYSIZE(message), &ignored); + if (FAILED(hr)) + { + ReportFailure_Base(__R_DIAGNOSTICS_RA(diagnostics, returnAddress), ResultStatus::FromResult(hr), message); + return hr; + } + } + + // Indicate that this either isn't a C++/WinRT exception or a handler isn't configured by returning success + return S_OK; + } + + inline HRESULT RecognizeCaughtExceptionFromCallback(_Inout_updates_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars) + { + HRESULT hr = g_pfnResultFromCaughtException(); + + // If we still don't know the error -- or we would like to get the debug string for the error (if possible) we + // rethrow and catch std::exception. + + if (SUCCEEDED(hr) || debugString) + { + try + { + throw; + } + catch (std::exception& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + if (SUCCEEDED(hr)) + { + hr = __HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION); + } + } + catch (...) + { + // Fall through to returning 'hr' below + } + } + + return hr; + } + +#ifdef __cplusplus_winrt + inline Platform::String^ GetPlatformExceptionMessage(Platform::Exception^ exception) + { + struct RawExceptionData_Partial + { + PCWSTR description; + PCWSTR restrictedErrorString; + }; + + auto exceptionPtr = reinterpret_cast(static_cast<::Platform::Object^>(exception)); + auto exceptionInfoPtr = reinterpret_cast(exceptionPtr) - 1; + auto partial = reinterpret_cast(*exceptionInfoPtr); + + Platform::String^ message = exception->Message; + + PCWSTR errorString = partial->restrictedErrorString; + PCWSTR messageString = reinterpret_cast(message ? message->Data() : nullptr); + + // An old Platform::Exception^ bug that did not actually expose the error string out of the exception + // message. We do it by hand here if the message associated with the strong does not contain the + // message that was originally attached to the string (in the fixed version it will). + + if ((errorString && *errorString && messageString) && + (wcsstr(messageString, errorString) == nullptr)) + { + return ref new Platform::String(reinterpret_cast<_Null_terminated_ const __wchar_t *>(errorString)); + } + return message; + } + + inline void MaybeGetExceptionString(_In_ Platform::Exception^ exception, _Out_writes_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars) + { + if (debugString) + { + auto message = GetPlatformExceptionMessage(exception); + auto messageString = !message ? L"(null Message)" : reinterpret_cast(message->Data()); + StringCchPrintfW(debugString, debugStringChars, L"Platform::Exception^: %ws", messageString); + } + } + + inline HRESULT ResultFromKnownException(Platform::Exception^ exception, const DiagnosticsInfo& diagnostics, void* returnAddress) + { + wchar_t message[2048]; + message[0] = L'\0'; + MaybeGetExceptionString(exception, message, ARRAYSIZE(message)); + auto hr = exception->HResult; + wil::details::ReportFailure_Base(__R_DIAGNOSTICS_RA(diagnostics, returnAddress), ResultStatus::FromResult(hr), message); + return hr; + } + + inline HRESULT __stdcall ResultFromCaughtException_WinRt(_Inout_updates_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, _Inout_ bool* isNormalized) WI_NOEXCEPT + { + if (g_pfnResultFromCaughtException) + { + try + { + throw; + } + catch (const ResultException& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return exception.GetErrorCode(); + } + catch (Platform::Exception^ exception) + { + *isNormalized = true; + // We need to call __abi_translateCurrentException so that the CX runtime will pull the originated error information + // out of the exception object and place it back into thread-local storage. + __abi_translateCurrentException(false); + MaybeGetExceptionString(exception, debugString, debugStringChars); + return exception->HResult; + } + catch (const std::bad_alloc& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return E_OUTOFMEMORY; + } + catch (...) + { + auto hr = RecognizeCaughtExceptionFromCallback(debugString, debugStringChars); + if (FAILED(hr)) + { + return hr; + } + } + } + else + { + try + { + throw; + } + catch (const ResultException& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return exception.GetErrorCode(); + } + catch (Platform::Exception^ exception) + { + *isNormalized = true; + // We need to call __abi_translateCurrentException so that the CX runtime will pull the originated error information + // out of the exception object and place it back into thread-local storage. + __abi_translateCurrentException(false); + MaybeGetExceptionString(exception, debugString, debugStringChars); + return exception->HResult; + } + catch (const std::bad_alloc& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return E_OUTOFMEMORY; + } + catch (std::exception& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION); + } + catch (...) + { + // Fall through to returning 'S_OK' below + } + } + + // Tell the caller that we were unable to map the exception by succeeding... + return S_OK; + } + + // WinRT supporting version to execute a functor and catch known exceptions. + inline HRESULT __stdcall ResultFromKnownExceptions_WinRt(const DiagnosticsInfo& diagnostics, void* returnAddress, SupportedExceptions supported, IFunctor& functor) + { + WI_ASSERT(supported != SupportedExceptions::Default); + + switch (supported) + { + case SupportedExceptions::Known: + try + { + return functor.Run(); + } + catch (const ResultException& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + catch (Platform::Exception^ exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + catch (const std::bad_alloc& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + catch (std::exception& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + catch (...) + { + auto hr = ResultFromKnownException_CppWinRT(diagnostics, returnAddress); + if (FAILED(hr)) + { + return hr; + } + + // Unknown exception + throw; + } + break; + + case SupportedExceptions::ThrownOrAlloc: + try + { + return functor.Run(); + } + catch (const ResultException& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + catch (Platform::Exception^ exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + catch (const std::bad_alloc& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + break; + + case SupportedExceptions::Thrown: + try + { + return functor.Run(); + } + catch (const ResultException& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + catch (Platform::Exception^ exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + break; + } + + WI_ASSERT(false); + return S_OK; + } + + inline void __stdcall ThrowPlatformException(FailureInfo const &failure, LPCWSTR debugString) + { + throw Platform::Exception::CreateException(failure.hr, ref new Platform::String(reinterpret_cast<_Null_terminated_ const __wchar_t *>(debugString))); + } + +#if !defined(RESULT_SUPPRESS_STATIC_INITIALIZERS) + WI_HEADER_INITITALIZATION_FUNCTION(InitializeWinRt, [] + { + g_pfnResultFromCaughtException_WinRt = ResultFromCaughtException_WinRt; + g_pfnResultFromKnownExceptions_WinRt = ResultFromKnownExceptions_WinRt; + g_pfnThrowPlatformException = ThrowPlatformException; + return 1; + }); +#endif +#endif + + inline void __stdcall Rethrow() + { + throw; + } + + inline void __stdcall ThrowResultExceptionInternal(const FailureInfo& failure) + { + throw ResultException(failure); + } + + __declspec(noinline) inline ResultStatus __stdcall ResultFromCaughtExceptionInternal(_Out_writes_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, _Out_ bool* isNormalized) WI_NOEXCEPT + { + if (debugString) + { + *debugString = L'\0'; + } + *isNormalized = false; + + if (details::g_pfnResultFromCaughtException_CppWinRt != nullptr) + { + const auto hr = details::g_pfnResultFromCaughtException_CppWinRt(debugString, debugStringChars, isNormalized); + if (FAILED(hr)) + { + return ResultStatus::FromResult(hr); + } + } + + if (details::g_pfnResultFromCaughtException_WinRt != nullptr) + { + const auto hr = details::g_pfnResultFromCaughtException_WinRt(debugString, debugStringChars, isNormalized); + return ResultStatus::FromResult(hr); + } + + if (g_pfnResultFromCaughtException) + { + try + { + throw; + } + catch (const ResultException& exception) + { + *isNormalized = true; + MaybeGetExceptionString(exception, debugString, debugStringChars); + return ResultStatus::FromFailureInfo(exception.GetFailureInfo()); + } + catch (const std::bad_alloc& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return ResultStatus::FromResult(E_OUTOFMEMORY); + } + catch (...) + { + auto hr = RecognizeCaughtExceptionFromCallback(debugString, debugStringChars); + if (FAILED(hr)) + { + return ResultStatus::FromResult(hr); + } + } + } + else + { + try + { + throw; + } + catch (const ResultException& exception) + { + *isNormalized = true; + MaybeGetExceptionString(exception, debugString, debugStringChars); + return ResultStatus::FromFailureInfo(exception.GetFailureInfo()); + } + catch (const std::bad_alloc& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return ResultStatus::FromResult(E_OUTOFMEMORY); + } + catch (std::exception& exception) + { + MaybeGetExceptionString(exception, debugString, debugStringChars); + return ResultStatus::FromResult(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION)); + } + catch (...) + { + // Fall through to returning 'S_OK' below + } + } + + // Tell the caller that we were unable to map the exception by succeeding... + return ResultStatus::FromResult(S_OK); + } + + // Runs the given functor, converting any exceptions of the supported types that are known to HRESULTs and returning + // that HRESULT. Does NOT attempt to catch unknown exceptions (which propagate). Primarily used by SEH exception + // handling techniques to stop at the point the exception is thrown. + inline HRESULT ResultFromKnownExceptions(const DiagnosticsInfo& diagnostics, void* returnAddress, SupportedExceptions supported, IFunctor& functor) + { + if (supported == SupportedExceptions::Default) + { + supported = g_fResultSupportStdException ? SupportedExceptions::Known : SupportedExceptions::ThrownOrAlloc; + } + + if ((details::g_pfnResultFromKnownExceptions_WinRt != nullptr) && + ((supported == SupportedExceptions::Known) || (supported == SupportedExceptions::Thrown) || (supported == SupportedExceptions::ThrownOrAlloc))) + { + return details::g_pfnResultFromKnownExceptions_WinRt(diagnostics, returnAddress, supported, functor); + } + + switch (supported) + { + case SupportedExceptions::Known: + try + { + return functor.Run(); + } + catch (const ResultException& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + catch (const std::bad_alloc& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + catch (std::exception& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + catch (...) + { + auto hr = ResultFromKnownException_CppWinRT(diagnostics, returnAddress); + if (FAILED(hr)) + { + return hr; + } + + // Unknown exception + throw; + } + + case SupportedExceptions::ThrownOrAlloc: + try + { + return functor.Run(); + } + catch (const ResultException& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + catch (const std::bad_alloc& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + + case SupportedExceptions::Thrown: + try + { + return functor.Run(); + } + catch (const ResultException& exception) + { + return ResultFromKnownException(exception, diagnostics, returnAddress); + } + + case SupportedExceptions::All: + try + { + return functor.Run(); + } + catch (...) + { + return wil::details::ReportFailure_CaughtException(__R_DIAGNOSTICS_RA(diagnostics, returnAddress), supported); + } + + case SupportedExceptions::None: + return functor.Run(); + + case SupportedExceptions::Default: + WI_ASSERT(false); + } + + WI_ASSERT(false); + return S_OK; + } + + inline HRESULT ResultFromExceptionSeh(const DiagnosticsInfo& diagnostics, void* returnAddress, SupportedExceptions supported, IFunctor& functor) WI_NOEXCEPT + { + __try + { + return wil::details::ResultFromKnownExceptions(diagnostics, returnAddress, supported, functor); + } + __except (wil::details::TerminateAndReportError(GetExceptionInformation()), EXCEPTION_CONTINUE_SEARCH) + { + WI_ASSERT(false); + RESULT_NORETURN_RESULT(HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION)); + } + } + + __declspec(noinline) inline HRESULT ResultFromException(const DiagnosticsInfo& diagnostics, SupportedExceptions supported, IFunctor& functor) WI_NOEXCEPT + { +#ifdef RESULT_DEBUG + // We can't do debug SEH handling if the caller also wants a shot at mapping the exceptions + // themselves or if the caller doesn't want to fail-fast unknown exceptions + if ((g_pfnResultFromCaughtException == nullptr) && g_fResultFailFastUnknownExceptions) + { + return wil::details::ResultFromExceptionSeh(diagnostics, _ReturnAddress(), supported, functor); + } +#endif + try + { + return functor.Run(); + } + catch (...) + { + return wil::details::ReportFailure_CaughtException(__R_DIAGNOSTICS(diagnostics), _ReturnAddress(), supported); + } + } + + __declspec(noinline) inline HRESULT ResultFromExceptionDebug(const DiagnosticsInfo& diagnostics, SupportedExceptions supported, IFunctor& functor) WI_NOEXCEPT + { + return wil::details::ResultFromExceptionSeh(diagnostics, _ReturnAddress(), supported, functor); + } + + // Exception guard -- catch exceptions and log them (or handle them with a custom callback) + // WARNING: may throw an exception... + inline HRESULT __stdcall RunFunctorWithExceptionFilter(IFunctor& functor, IFunctorHost& host, void* returnAddress) + { + try + { + return host.Run(functor); + } + catch (...) + { + // Note that the host may choose to re-throw, throw a normalized exception, return S_OK and eat the exception or + // return the remapped failure. + return host.ExceptionThrown(returnAddress); + } + } + + WI_HEADER_INITITALIZATION_FUNCTION(InitializeResultExceptions, [] + { + g_pfnRunFunctorWithExceptionFilter = RunFunctorWithExceptionFilter; + g_pfnRethrow = Rethrow; + g_pfnThrowResultException = ThrowResultExceptionInternal; + g_pfnResultFromCaughtExceptionInternal = ResultFromCaughtExceptionInternal; + return 1; + }); + + } + + //! A lambda-based exception guard that can vary the supported exception types. + //! This function accepts a lambda and diagnostics information as its parameters and executes that lambda + //! under a try/catch(...) block. All exceptions are caught and the function reports the exception information + //! and diagnostics to telemetry on failure. An HRESULT is returned that maps to the exception. + //! + //! Note that an overload exists that does not report failures to telemetry at all. This version should be preferred + //! to that version. Also note that neither of these versions are preferred over using try catch blocks to accomplish + //! the same thing as they will be more efficient. + //! + //! See @ref page_exception_guards for more information and examples on exception guards. + //! ~~~~ + //! return wil::ResultFromException(WI_DIAGNOSTICS_INFO, [&] + //! { + //! // exception-based code + //! // telemetry is reported with full exception information + //! }); + //! ~~~~ + //! @param diagnostics Always pass WI_DIAGNOSTICS_INFO as the first parameter + //! @param supported What kind of exceptions you want to support + //! @param functor A lambda that accepts no parameters; any return value is ignored + //! @return S_OK on success (no exception thrown) or an error based upon the exception thrown + template + __forceinline HRESULT ResultFromException(const DiagnosticsInfo& diagnostics, SupportedExceptions supported, Functor&& functor) WI_NOEXCEPT + { + static_assert(details::functor_tag::value != details::tag_return_other::value, "Functor must return void or HRESULT"); + typename details::functor_tag::template functor_wrapper functorObject(wistd::forward(functor)); + + return wil::details::ResultFromException(diagnostics, supported, functorObject); + } + + //! A lambda-based exception guard. + //! This overload uses SupportedExceptions::Known by default. See @ref ResultFromException for more detailed information. + template + __forceinline HRESULT ResultFromException(const DiagnosticsInfo& diagnostics, Functor&& functor) WI_NOEXCEPT + { + return ResultFromException(diagnostics, SupportedExceptions::Known, wistd::forward(functor)); + } + + //! A lambda-based exception guard that does not report failures to telemetry. + //! This function accepts a lambda as it's only parameter and executes that lambda under a try/catch(...) block. + //! All exceptions are caught and the function returns an HRESULT mapping to the exception. + //! + //! This version (taking only a lambda) does not report failures to telemetry. An overload with the same name + //! can be utilized by passing `WI_DIAGNOSTICS_INFO` as the first parameter and the lambda as the second parameter + //! to report failure information to telemetry. + //! + //! See @ref page_exception_guards for more information and examples on exception guards. + //! ~~~~ + //! hr = wil::ResultFromException([&] + //! { + //! // exception-based code + //! // the conversion of exception to HRESULT doesn't report telemetry + //! }); + //! + //! hr = wil::ResultFromException(WI_DIAGNOSTICS_INFO, [&] + //! { + //! // exception-based code + //! // telemetry is reported with full exception information + //! }); + //! ~~~~ + //! @param functor A lambda that accepts no parameters; any return value is ignored + //! @return S_OK on success (no exception thrown) or an error based upon the exception thrown + template + inline HRESULT ResultFromException(Functor&& functor) WI_NOEXCEPT try + { + static_assert(details::functor_tag::value == details::tag_return_void::value, "Functor must return void"); + typename details::functor_tag::template functor_wrapper functorObject(wistd::forward(functor)); + + functorObject.Run(); + return S_OK; + } + catch (...) + { + return ResultFromCaughtException(); + } + + + //! A lambda-based exception guard that can identify the origin of unknown exceptions and can vary the supported exception types. + //! Functionally this is nearly identical to the corresponding @ref ResultFromException function with the exception + //! that it utilizes structured exception handling internally to be able to terminate at the point where a unknown + //! exception is thrown, rather than after that unknown exception has been unwound. Though less efficient, this leads + //! to a better debugging experience when analyzing unknown exceptions. + //! + //! For example: + //! ~~~~ + //! hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&] + //! { + //! FunctionWhichMayThrow(); + //! }); + //! ~~~~ + //! Assume FunctionWhichMayThrow() has a bug in it where it accidentally does a `throw E_INVALIDARG;`. This ends up + //! throwing a `long` as an exception object which is not what the caller intended. The normal @ref ResultFromException + //! would fail-fast when this is encountered, but it would do so AFTER FunctionWhichMayThrow() is already off of the + //! stack and has been unwound. Because SEH is used for ResultFromExceptionDebug, the fail-fast occurs with everything + //! leading up to and including the `throw INVALIDARG;` still on the stack (and easily debuggable). + //! + //! The penalty paid for using this, however, is efficiency. It's far less efficient as a general pattern than either + //! using ResultFromException directly or especially using try with CATCH_ macros directly. Still it's helpful to deploy + //! selectively to isolate issues a component may be having with unknown/unhandled exceptions. + //! + //! The ability to vary the SupportedExceptions that this routine provides adds the ability to track down unexpected + //! exceptions not falling into the supported category easily through fail-fast. For example, by not supporting any + //! exception, you can use this function to quickly add an exception guard that will fail-fast any exception at the point + //! the exception occurs (the throw) in a codepath where the origination of unknown exceptions need to be tracked down. + //! + //! Also see @ref ResultFromExceptionDebugNoStdException. It functions almost identically, but also will fail-fast and stop + //! on std::exception based exceptions (but not Platform::Exception^ or wil::ResultException). Using this can help isolate + //! where an unexpected exception is being generated from. + //! @param diagnostics Always pass WI_DIAGNOSTICS_INFO as the first parameter + //! @param supported What kind of exceptions you want to support + //! @param functor A lambda that accepts no parameters; any return value is ignored + //! @return S_OK on success (no exception thrown) or an error based upon the exception thrown + template + __forceinline HRESULT ResultFromExceptionDebug(const DiagnosticsInfo& diagnostics, SupportedExceptions supported, Functor&& functor) WI_NOEXCEPT + { + static_assert(details::functor_tag::value == details::tag_return_void::value, "Functor must return void"); + typename details::functor_tag::template functor_wrapper functorObject(wistd::forward(functor)); + + return wil::details::ResultFromExceptionDebug(diagnostics, supported, functorObject); + } + + //! A lambda-based exception guard that can identify the origin of unknown exceptions. + //! This overload uses SupportedExceptions::Known by default. See @ref ResultFromExceptionDebug for more detailed information. + template + __forceinline HRESULT ResultFromExceptionDebug(const DiagnosticsInfo& diagnostics, Functor&& functor) WI_NOEXCEPT + { + static_assert(details::functor_tag::value == details::tag_return_void::value, "Functor must return void"); + typename details::functor_tag::template functor_wrapper functorObject(wistd::forward(functor)); + + return wil::details::ResultFromExceptionDebug(diagnostics, SupportedExceptions::Known, functorObject); + } + + //! A fail-fast based exception guard. + //! Technically this is an overload of @ref ResultFromExceptionDebug that uses SupportedExceptions::None by default. Any uncaught + //! exception that makes it back to this guard would result in a fail-fast at the point the exception is thrown. + template + __forceinline void FailFastException(const DiagnosticsInfo& diagnostics, Functor&& functor) WI_NOEXCEPT + { + static_assert(details::functor_tag::value == details::tag_return_void::value, "Functor must return void"); + typename details::functor_tag::template functor_wrapper functorObject(wistd::forward(functor)); + + wil::details::ResultFromExceptionDebug(diagnostics, SupportedExceptions::None, functorObject); + } + + namespace details { + +#endif // WIL_ENABLE_EXCEPTIONS + + // Exception guard -- catch exceptions and log them (or handle them with a custom callback) + // WARNING: may throw an exception... + inline __declspec(noinline) HRESULT RunFunctor(IFunctor& functor, IFunctorHost& host) + { + if (g_pfnRunFunctorWithExceptionFilter) + { + return g_pfnRunFunctorWithExceptionFilter(functor, host, _ReturnAddress()); + } + + return host.Run(functor); + } + + // Returns true if a debugger should be considered to be connected. + // Modules can force this on through setting g_fIsDebuggerPresent explicitly (useful for live debugging), + // they can provide a callback function by setting g_pfnIsDebuggerPresent (useful for kernel debbugging), + // and finally the user-mode check (IsDebuggerPrsent) is checked. IsDebuggerPresent is a fast call + inline bool IsDebuggerPresent() + { + return g_fIsDebuggerPresent || ((g_pfnIsDebuggerPresent != nullptr) ? g_pfnIsDebuggerPresent() : (::IsDebuggerPresent() != FALSE)); + } + + //***************************************************************************** + // Shared Reporting -- all reporting macros bubble up through this codepath + //***************************************************************************** + + inline void LogFailure(__R_FN_PARAMS_FULL, FailureType type, const ResultStatus& resultPair, _In_opt_ PCWSTR message, + bool fWantDebugString, _Out_writes_(debugStringSizeChars) _Post_z_ PWSTR debugString, _Pre_satisfies_(debugStringSizeChars > 0) size_t debugStringSizeChars, + _Out_writes_(callContextStringSizeChars) _Post_z_ PSTR callContextString, _Pre_satisfies_(callContextStringSizeChars > 0) size_t callContextStringSizeChars, + _Out_ FailureInfo *failure) WI_NOEXCEPT + { + debugString[0] = L'\0'; + callContextString[0] = L'\0'; + + static long volatile s_failureId = 0; + + failure->hr = resultPair.hr; + failure->status = resultPair.status; + + int failureCount = 0; + switch (type) + { + case FailureType::Exception: + failureCount = RecordException(failure->hr); + break; + case FailureType::Return: + failureCount = RecordReturn(failure->hr); + break; + case FailureType::Log: + if (SUCCEEDED(failure->hr)) + { + // If you hit this assert (or are reviewing this failure telemetry), then most likely you are trying to log success + // using one of the WIL macros. Example: + // LOG_HR(S_OK); + // Instead, use one of the forms that conditionally logs based upon the error condition: + // LOG_IF_FAILED(hr); + + WI_USAGE_ERROR_FORWARD("CALLER BUG: Macro usage error detected. Do not LOG_XXX success."); + failure->hr = __HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE); + failure->status = wil::details::HrToNtStatus(failure->hr); + } + failureCount = RecordLog(failure->hr); + break; + case FailureType::FailFast: + failureCount = RecordFailFast(failure->hr); + break; + }; + + failure->type = type; + failure->flags = FailureFlags::None; + WI_SetFlagIf(failure->flags, FailureFlags::NtStatus, resultPair.isNtStatus); + failure->failureId = ::InterlockedIncrementNoFence(&s_failureId); + failure->pszMessage = ((message != nullptr) && (message[0] != L'\0')) ? message : nullptr; + failure->threadId = ::GetCurrentThreadId(); + failure->pszFile = fileName; + failure->uLineNumber = lineNumber; + failure->cFailureCount = failureCount; + failure->pszCode = code; + failure->pszFunction = functionName; + failure->returnAddress = returnAddress; + failure->callerReturnAddress = callerReturnAddress; + failure->pszCallContext = nullptr; + ::ZeroMemory(&failure->callContextCurrent, sizeof(failure->callContextCurrent)); + ::ZeroMemory(&failure->callContextOriginating, sizeof(failure->callContextOriginating)); + failure->pszModule = (g_pfnGetModuleName != nullptr) ? g_pfnGetModuleName() : nullptr; + + // Completes filling out failure, notifies thread-based callbacks and the telemetry callback + if (details::g_pfnGetContextAndNotifyFailure) + { + details::g_pfnGetContextAndNotifyFailure(failure, callContextString, callContextStringSizeChars); + } + + // Allow hooks to inspect the failure before acting upon it + if (details::g_pfnLoggingCallback) + { + details::g_pfnLoggingCallback(*failure); + } + + // If the hook is enabled then it will be given the opportunity to call RoOriginateError to greatly improve the diagnostic experience + // for uncaught exceptions. In cases where we will be throwing a C++/CX Platform::Exception we should avoid originating because the + // CX runtime will be doing that for us. fWantDebugString is only set to true when the caller will be throwing a Platform::Exception. + if (details::g_pfnOriginateCallback && !fWantDebugString) + { + details::g_pfnOriginateCallback(*failure); + } + + if (SUCCEEDED(failure->hr)) + { + // Caller bug: Leaking a success code into a failure-only function + FAIL_FAST_IMMEDIATE_IF(type != FailureType::FailFast); + failure->hr = E_UNEXPECTED; + failure->status = wil::details::HrToNtStatus(failure->hr); + } + + bool const fUseOutputDebugString = IsDebuggerPresent() && g_fResultOutputDebugString; + + // We need to generate the logging message if: + // * We're logging to OutputDebugString + // * OR the caller asked us to (generally for attaching to a C++/CX exception) + if (fWantDebugString || fUseOutputDebugString) + { + // Call the logging callback (if present) to allow them to generate the debug string that will be pushed to the console + // or the platform exception object if the caller desires it. + if ((g_pfnResultLoggingCallback != nullptr) && !g_resultMessageCallbackSet) + { + g_pfnResultLoggingCallback(failure, debugString, debugStringSizeChars); + } + + // The callback only optionally needs to supply the debug string -- if the callback didn't populate it, yet we still want + // it for OutputDebugString or exception message, then generate the default string. + if (debugString[0] == L'\0') + { + GetFailureLogString(debugString, debugStringSizeChars, *failure); + } + + if (fUseOutputDebugString) + { + ::OutputDebugStringW(debugString); + } + } + else + { + // [deprecated behavior] + // This callback was at one point *always* called for all failures, so we continue to call it for failures even when we don't + // need to generate the debug string information (when the callback was supplied directly). We can avoid this if the caller + // used the explicit function (through g_resultMessageCallbackSet) + if ((g_pfnResultLoggingCallback != nullptr) && !g_resultMessageCallbackSet) + { + g_pfnResultLoggingCallback(failure, nullptr, 0); + } + } + + if (g_fBreakOnFailure && (g_pfnDebugBreak != nullptr)) + { + g_pfnDebugBreak(); + } + } + + inline RESULT_NORETURN void __stdcall WilFailFast(const wil::FailureInfo& failure) + { + if (g_pfnWilFailFast) + { + g_pfnWilFailFast(failure); + } + +#ifdef RESULT_RAISE_FAST_FAIL_EXCEPTION + // Use of this macro is an ODR violation - use the callback instead. This will be removed soon. + RESULT_RAISE_FAST_FAIL_EXCEPTION; +#endif + + // Before we fail fast in this method, give the [optional] RoFailFastWithErrorContext a try. + if (g_pfnFailfastWithContextCallback) + { + g_pfnFailfastWithContextCallback(failure); + } + + // parameter 0 is the !analyze code (FAST_FAIL_FATAL_APP_EXIT) + EXCEPTION_RECORD er{}; + er.NumberParameters = 1; // default to be safe, see below + er.ExceptionCode = static_cast(STATUS_STACK_BUFFER_OVERRUN); // 0xC0000409 + er.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + er.ExceptionInformation[0] = FAST_FAIL_FATAL_APP_EXIT; // see winnt.h, generated from minkernel\published\base\ntrtl_x.w + if (failure.returnAddress == 0) // FailureInfo does not have _ReturnAddress, have RaiseFailFastException generate it + { + // passing ExceptionCode 0xC0000409 and one param with FAST_FAIL_APP_EXIT will use existing + // !analyze functionality to crawl the stack looking for the HRESULT + // don't pass a 0 HRESULT in param 1 because that will result in worse bucketing. + WilRaiseFailFastException(&er, nullptr, FAIL_FAST_GENERATE_EXCEPTION_ADDRESS); + } + else // use FailureInfo caller address + { + // parameter 1 is the failing HRESULT + // parameter 2 is the line number. This is never used for bucketing (due to code churn causing re-bucketing) but is available in the dump's + // exception record to aid in failure locality. Putting it here prevents it from being poisoned in triage dumps. + er.NumberParameters = 3; + er.ExceptionInformation[1] = failure.hr; + er.ExceptionInformation[2] = failure.uLineNumber; + er.ExceptionAddress = failure.returnAddress; + WilRaiseFailFastException(&er, nullptr, 0 /* do not generate exception address */); + } + } + + template + inline __declspec(noinline) void ReportFailure_Return(__R_FN_PARAMS_FULL, const ResultStatus& resultPair, PCWSTR message, ReportFailureOptions options) + { + bool needPlatformException = ((T == FailureType::Exception) && + WI_IsFlagClear(options, ReportFailureOptions::MayRethrow) && + (g_pfnThrowPlatformException != nullptr) && + (g_fResultThrowPlatformException || WI_IsFlagSet(options, ReportFailureOptions::ForcePlatformException))); + + FailureInfo failure; + wchar_t debugString[2048]; + char callContextString[1024]; + + LogFailure(__R_FN_CALL_FULL, T, resultPair, message, needPlatformException, + debugString, ARRAYSIZE(debugString), callContextString, ARRAYSIZE(callContextString), &failure); + } + + template + inline __declspec(noinline) void ReportFailure_Base(__R_FN_PARAMS_FULL, const ResultStatus& resultPair, PCWSTR message, ReportFailureOptions options) + { + ReportFailure_Return(__R_FN_CALL_FULL, resultPair, message, options); + } + + template + inline __declspec(noinline) RESULT_NORETURN void ReportFailure_NoReturn(__R_FN_PARAMS_FULL, const ResultStatus& resultPair, PCWSTR message, ReportFailureOptions options) + { + bool needPlatformException = ((T == FailureType::Exception) && + WI_IsFlagClear(options, ReportFailureOptions::MayRethrow) && + (g_pfnThrowPlatformException != nullptr) && + (g_fResultThrowPlatformException || WI_IsFlagSet(options, ReportFailureOptions::ForcePlatformException))); + + FailureInfo failure; + wchar_t debugString[2048]; + char callContextString[1024]; + + LogFailure(__R_FN_CALL_FULL, T, resultPair, message, needPlatformException, + debugString, ARRAYSIZE(debugString), callContextString, ARRAYSIZE(callContextString), &failure); +__WI_SUPPRESS_4127_S + if (T == FailureType::FailFast) + { + WilFailFast(const_cast(failure)); + } + else + { + if (needPlatformException) + { + g_pfnThrowPlatformException(failure, debugString); + } + + if (WI_IsFlagSet(options, ReportFailureOptions::MayRethrow)) + { + RethrowCaughtException(); + } + + ThrowResultException(failure); + + // Wil was instructed to throw, but doesn't have any capability to do so (global function pointers are not setup) + WilFailFast(const_cast(failure)); + } +__WI_SUPPRESS_4127_E + } + + template<> + inline __declspec(noinline) RESULT_NORETURN void ReportFailure_Base(__R_FN_PARAMS_FULL, const ResultStatus& resultPair, PCWSTR message, ReportFailureOptions options) + { + ReportFailure_NoReturn(__R_FN_CALL_FULL, resultPair, message, options); + } + + template<> + inline __declspec(noinline) RESULT_NORETURN void ReportFailure_Base(__R_FN_PARAMS_FULL, const ResultStatus& resultPair, PCWSTR message, ReportFailureOptions options) + { + ReportFailure_NoReturn(__R_FN_CALL_FULL, resultPair, message, options); + } + + __declspec(noinline) inline void ReportFailure(__R_FN_PARAMS_FULL, FailureType type, const ResultStatus& resultPair, _In_opt_ PCWSTR message, ReportFailureOptions options) + { + switch(type) + { + case FailureType::Exception: + ReportFailure_Base(__R_FN_CALL_FULL, resultPair, message, options); + break; + case FailureType::FailFast: + ReportFailure_Base(__R_FN_CALL_FULL, resultPair, message, options); + break; + case FailureType::Log: + ReportFailure_Base(__R_FN_CALL_FULL, resultPair, message, options); + break; + case FailureType::Return: + ReportFailure_Base(__R_FN_CALL_FULL, resultPair, message, options); + break; + } + } + + template + inline ResultStatus ReportFailure_CaughtExceptionCommon(__R_FN_PARAMS_FULL, _Inout_updates_(debugStringChars) PWSTR debugString, _Pre_satisfies_(debugStringChars > 0) size_t debugStringChars, SupportedExceptions supported) + { + bool isNormalized = false; + auto length = wcslen(debugString); + WI_ASSERT(length < debugStringChars); + ResultStatus resultPair; + if (details::g_pfnResultFromCaughtExceptionInternal) + { + resultPair = details::g_pfnResultFromCaughtExceptionInternal(debugString + length, debugStringChars - length, &isNormalized); + } + + const bool known = (FAILED(resultPair.hr)); + if (!known) + { + resultPair = ResultStatus::FromResult(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION)); + } + + ReportFailureOptions options = ReportFailureOptions::ForcePlatformException; + WI_SetFlagIf(options, ReportFailureOptions::MayRethrow, isNormalized); + + if ((supported == SupportedExceptions::None) || + ((supported == SupportedExceptions::Known) && !known) || + ((supported == SupportedExceptions::Thrown) && !isNormalized) || + ((supported == SupportedExceptions::Default) && !known && g_fResultFailFastUnknownExceptions)) + { + // By default WIL will issue a fail fast for unrecognized exception types. Wil recognizes any std::exception or wil::ResultException based + // types and Platform::Exception^, so there aren't too many valid exception types which could cause this. Those that are valid, should be handled + // by remapping the exception callback. Those that are not valid should be found and fixed (meaningless accidents like 'throw hr;'). + // The caller may also be requesting non-default behavior to fail-fast more frequently (primarily for debugging unknown exceptions). + ReportFailure_Base(__R_FN_CALL_FULL, resultPair, debugString, options); + } + else + { + ReportFailure_Base(__R_FN_CALL_FULL, resultPair, debugString, options); + } + + return resultPair; + } + + template + inline ResultStatus RESULT_NORETURN ReportFailure_CaughtExceptionCommonNoReturnBase(__R_FN_PARAMS_FULL, _Inout_updates_(debugStringChars) PWSTR debugString, _Pre_satisfies_(debugStringChars > 0) size_t debugStringChars, SupportedExceptions supported) + { + bool isNormalized = false; + const auto length = wcslen(debugString); + WI_ASSERT(length < debugStringChars); + ResultStatus resultPair; + if (details::g_pfnResultFromCaughtExceptionInternal) + { + resultPair = details::g_pfnResultFromCaughtExceptionInternal(debugString + length, debugStringChars - length, &isNormalized); + } + + const bool known = (FAILED(resultPair.hr)); + if (!known) + { + resultPair = ResultStatus::FromResult(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION)); + } + + ReportFailureOptions options = ReportFailureOptions::ForcePlatformException; + WI_SetFlagIf(options, ReportFailureOptions::MayRethrow, isNormalized); + + if ((supported == SupportedExceptions::None) || + ((supported == SupportedExceptions::Known) && !known) || + ((supported == SupportedExceptions::Thrown) && !isNormalized) || + ((supported == SupportedExceptions::Default) && !known && g_fResultFailFastUnknownExceptions)) + { + // By default WIL will issue a fail fast for unrecognized exception types. Wil recognizes any std::exception or wil::ResultException based + // types and Platform::Exception^, so there aren't too many valid exception types which could cause this. Those that are valid, should be handled + // by remapping the exception callback. Those that are not valid should be found and fixed (meaningless accidents like 'throw hr;'). + // The caller may also be requesting non-default behavior to fail-fast more frequently (primarily for debugging unknown exceptions). + ReportFailure_Base(__R_FN_CALL_FULL, resultPair, debugString, options); + } + else + { + ReportFailure_Base(__R_FN_CALL_FULL, resultPair, debugString, options); + } + + RESULT_NORETURN_RESULT(resultPair); + } + + template<> + inline RESULT_NORETURN ResultStatus ReportFailure_CaughtExceptionCommon(__R_FN_PARAMS_FULL, _Inout_updates_(debugStringChars) PWSTR debugString, _Pre_satisfies_(debugStringChars > 0) size_t debugStringChars, SupportedExceptions supported) + { + RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommonNoReturnBase(__R_FN_CALL_FULL, debugString, debugStringChars, supported)); + } + + template<> + inline RESULT_NORETURN ResultStatus ReportFailure_CaughtExceptionCommon(__R_FN_PARAMS_FULL, _Inout_updates_(debugStringChars) PWSTR debugString, _Pre_satisfies_(debugStringChars > 0) size_t debugStringChars, SupportedExceptions supported) + { + RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommonNoReturnBase(__R_FN_CALL_FULL, debugString, debugStringChars, supported)); + } + + template + inline void ReportFailure_Msg(__R_FN_PARAMS_FULL, const ResultStatus& resultPair, _Printf_format_string_ PCSTR formatString, va_list argList) + { + wchar_t message[2048]; + PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); + ReportFailure_Base(__R_FN_CALL_FULL, resultPair, message); + } + + template<> + inline RESULT_NORETURN void ReportFailure_Msg(__R_FN_PARAMS_FULL, const ResultStatus& resultPair, _Printf_format_string_ PCSTR formatString, va_list argList) + { + wchar_t message[2048]; + PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); + ReportFailure_Base(__R_FN_CALL_FULL, resultPair, message); + } + + template<> + inline RESULT_NORETURN void ReportFailure_Msg(__R_FN_PARAMS_FULL, const ResultStatus& resultPair, _Printf_format_string_ PCSTR formatString, va_list argList) + { + wchar_t message[2048]; + PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); + ReportFailure_Base(__R_FN_CALL_FULL, resultPair, message); + } + + template + inline void ReportFailure_ReplaceMsg(__R_FN_PARAMS_FULL, HRESULT hr, PCSTR formatString, ...) + { + va_list argList; + va_start(argList, formatString); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); + } + + template + __declspec(noinline) inline void ReportFailure_Hr(__R_FN_PARAMS_FULL, HRESULT hr) + { + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); + } + + template<> + __declspec(noinline) inline RESULT_NORETURN void ReportFailure_Hr(__R_FN_PARAMS_FULL, HRESULT hr) + { + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); + } + + template<> + __declspec(noinline) inline RESULT_NORETURN void ReportFailure_Hr(__R_FN_PARAMS_FULL, HRESULT hr) + { + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); + } + + __declspec(noinline) inline void ReportFailure_Hr(__R_FN_PARAMS_FULL, FailureType type, HRESULT hr) + { + switch(type) + { + case FailureType::Exception: + ReportFailure_Hr(__R_FN_CALL_FULL, hr); + break; + case FailureType::FailFast: + ReportFailure_Hr(__R_FN_CALL_FULL, hr); + break; + case FailureType::Log: + ReportFailure_Hr(__R_FN_CALL_FULL, hr); + break; + case FailureType::Return: + ReportFailure_Hr(__R_FN_CALL_FULL, hr); + break; + } + } + + template + _Success_(true) + _Translates_Win32_to_HRESULT_(err) + __declspec(noinline) inline HRESULT ReportFailure_Win32(__R_FN_PARAMS_FULL, DWORD err) + { + const auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); + return hr; + } + + template<> + _Success_(true) + _Translates_Win32_to_HRESULT_(err) + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_Win32(__R_FN_PARAMS_FULL, DWORD err) + { + const auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); + RESULT_NORETURN_RESULT(hr); + } + + template<> + _Success_(true) + _Translates_Win32_to_HRESULT_(err) + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_Win32(__R_FN_PARAMS_FULL, DWORD err) + { + const auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); + RESULT_NORETURN_RESULT(hr); + } + + template + __declspec(noinline) inline DWORD ReportFailure_GetLastError(__R_FN_PARAMS_FULL) + { + const auto err = GetLastErrorFail(__R_FN_CALL_FULL); + const auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); + return err; + } + + template<> + __declspec(noinline) inline RESULT_NORETURN DWORD ReportFailure_GetLastError(__R_FN_PARAMS_FULL) + { + const auto err = GetLastErrorFail(__R_FN_CALL_FULL); + const auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); + RESULT_NORETURN_RESULT(err); + } + + template<> + __declspec(noinline) inline RESULT_NORETURN DWORD ReportFailure_GetLastError(__R_FN_PARAMS_FULL) + { + const auto err = GetLastErrorFail(__R_FN_CALL_FULL); + const auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); + RESULT_NORETURN_RESULT(err); + } + + template + _Success_(true) + _Translates_last_error_to_HRESULT_ + __declspec(noinline) inline HRESULT ReportFailure_GetLastErrorHr(__R_FN_PARAMS_FULL) + { + const auto hr = GetLastErrorFailHr(__R_FN_CALL_FULL); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); + return hr; + } + + template<> + _Success_(true) + _Translates_last_error_to_HRESULT_ + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_GetLastErrorHr(__R_FN_PARAMS_FULL) + { + const auto hr = GetLastErrorFailHr(__R_FN_CALL_FULL); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); + RESULT_NORETURN_RESULT(hr); + } + + template<> + _Success_(true) + _Translates_last_error_to_HRESULT_ + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_GetLastErrorHr(__R_FN_PARAMS_FULL) + { + const auto hr = GetLastErrorFailHr(__R_FN_CALL_FULL); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); + RESULT_NORETURN_RESULT(hr); + } + + template + _Success_(true) + _Translates_NTSTATUS_to_HRESULT_(status) + __declspec(noinline) inline HRESULT ReportFailure_NtStatus(__R_FN_PARAMS_FULL, NTSTATUS status) + { + const auto resultPair = ResultStatus::FromStatus(status); + ReportFailure_Base(__R_FN_CALL_FULL, resultPair); + return resultPair.hr; + } + + template<> + _Success_(true) + _Translates_NTSTATUS_to_HRESULT_(status) + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_NtStatus(__R_FN_PARAMS_FULL, NTSTATUS status) + { + const auto resultPair = ResultStatus::FromStatus(status); + ReportFailure_Base(__R_FN_CALL_FULL, resultPair); + RESULT_NORETURN_RESULT(resultPair.hr); + } + + template<> + _Success_(true) + _Translates_NTSTATUS_to_HRESULT_(status) + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_NtStatus(__R_FN_PARAMS_FULL, NTSTATUS status) + { + const auto resultPair = ResultStatus::FromStatus(status); + ReportFailure_Base(__R_FN_CALL_FULL, resultPair); + RESULT_NORETURN_RESULT(resultPair.hr); + } + + template + __declspec(noinline) inline HRESULT ReportFailure_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported) + { + wchar_t message[2048]; + message[0] = L'\0'; + return ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).hr; + } + + template<> + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported) + { + wchar_t message[2048]; + message[0] = L'\0'; + RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).hr); + } + + template<> + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported) + { + wchar_t message[2048]; + message[0] = L'\0'; + RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).hr); + } + + template + __declspec(noinline) inline void ReportFailure_HrMsg(__R_FN_PARAMS_FULL, HRESULT hr, _Printf_format_string_ PCSTR formatString, va_list argList) + { + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); + } + + template<> + __declspec(noinline) inline RESULT_NORETURN void ReportFailure_HrMsg(__R_FN_PARAMS_FULL, HRESULT hr, _Printf_format_string_ PCSTR formatString, va_list argList) + { + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); + } + + template<> + __declspec(noinline) inline RESULT_NORETURN void ReportFailure_HrMsg(__R_FN_PARAMS_FULL, HRESULT hr, _Printf_format_string_ PCSTR formatString, va_list argList) + { + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); + } + + template + _Success_(true) + _Translates_Win32_to_HRESULT_(err) + __declspec(noinline) inline HRESULT ReportFailure_Win32Msg(__R_FN_PARAMS_FULL, DWORD err, _Printf_format_string_ PCSTR formatString, va_list argList) + { + auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); + return hr; + } + + template<> + _Success_(true) + _Translates_Win32_to_HRESULT_(err) + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_Win32Msg(__R_FN_PARAMS_FULL, DWORD err, _Printf_format_string_ PCSTR formatString, va_list argList) + { + auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); + RESULT_NORETURN_RESULT(hr); + } + + template<> + _Success_(true) + _Translates_Win32_to_HRESULT_(err) + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_Win32Msg(__R_FN_PARAMS_FULL, DWORD err, _Printf_format_string_ PCSTR formatString, va_list argList) + { + auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); + RESULT_NORETURN_RESULT(hr); + } + + template + __declspec(noinline) inline DWORD ReportFailure_GetLastErrorMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + auto err = GetLastErrorFail(__R_FN_CALL_FULL); + auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); + return err; + } + + template<> + __declspec(noinline) inline RESULT_NORETURN DWORD ReportFailure_GetLastErrorMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + auto err = GetLastErrorFail(__R_FN_CALL_FULL); + auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); + RESULT_NORETURN_RESULT(err); + } + + template<> + __declspec(noinline) inline RESULT_NORETURN DWORD ReportFailure_GetLastErrorMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + auto err = GetLastErrorFail(__R_FN_CALL_FULL); + auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); + RESULT_NORETURN_RESULT(err); + } + + template + _Success_(true) + _Translates_last_error_to_HRESULT_ + __declspec(noinline) inline HRESULT ReportFailure_GetLastErrorHrMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + auto hr = GetLastErrorFailHr(__R_FN_CALL_FULL); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); + return hr; + } + + template<> + _Success_(true) + _Translates_last_error_to_HRESULT_ + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_GetLastErrorHrMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + auto hr = GetLastErrorFailHr(__R_FN_CALL_FULL); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); + RESULT_NORETURN_RESULT(hr); + } + + template<> + _Success_(true) + _Translates_last_error_to_HRESULT_ + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_GetLastErrorHrMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + auto hr = GetLastErrorFailHr(__R_FN_CALL_FULL); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); + RESULT_NORETURN_RESULT(hr); + } + + template + _Success_(true) + _Translates_NTSTATUS_to_HRESULT_(status) + __declspec(noinline) inline HRESULT ReportFailure_NtStatusMsg(__R_FN_PARAMS_FULL, NTSTATUS status, _Printf_format_string_ PCSTR formatString, va_list argList) + { + const auto resultPair = ResultStatus::FromStatus(status); + ReportFailure_Msg(__R_FN_CALL_FULL, resultPair, formatString, argList); + return resultPair.hr; + } + + template<> + _Success_(true) + _Translates_NTSTATUS_to_HRESULT_(status) + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_NtStatusMsg(__R_FN_PARAMS_FULL, NTSTATUS status, _Printf_format_string_ PCSTR formatString, va_list argList) + { + const auto resultPair = ResultStatus::FromStatus(status); + ReportFailure_Msg(__R_FN_CALL_FULL, resultPair, formatString, argList); + RESULT_NORETURN_RESULT(resultPair.hr); + } + + template<> + _Success_(true) + _Translates_NTSTATUS_to_HRESULT_(status) + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_NtStatusMsg(__R_FN_PARAMS_FULL, NTSTATUS status, _Printf_format_string_ PCSTR formatString, va_list argList) + { + const auto resultPair = ResultStatus::FromStatus(status); + ReportFailure_Msg(__R_FN_CALL_FULL, resultPair, formatString, argList); + RESULT_NORETURN_RESULT(resultPair.hr); + } + + template + __declspec(noinline) inline HRESULT ReportFailure_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + // Pre-populate the buffer with our message, the exception message will be added to it... + wchar_t message[2048]; + PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); + StringCchCatW(message, ARRAYSIZE(message), L" -- "); + return ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).hr; + } + + template<> + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + // Pre-populate the buffer with our message, the exception message will be added to it... + wchar_t message[2048]; + PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); + StringCchCatW(message, ARRAYSIZE(message), L" -- "); + RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).hr); + } + + template<> + __declspec(noinline) inline RESULT_NORETURN HRESULT ReportFailure_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) + { + // Pre-populate the buffer with our message, the exception message will be added to it... + wchar_t message[2048]; + PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); + StringCchCatW(message, ARRAYSIZE(message), L" -- "); + RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).hr); + } + + + //***************************************************************************** + // Support for throwing custom exception types + //***************************************************************************** + +#ifdef WIL_ENABLE_EXCEPTIONS + inline HRESULT GetErrorCode(_In_ ResultException &exception) WI_NOEXCEPT + { + return exception.GetErrorCode(); + } + + inline void SetFailureInfo(_In_ FailureInfo const &failure, _Inout_ ResultException &exception) WI_NOEXCEPT + { + return exception.SetFailureInfo(failure); + } + +#ifdef __cplusplus_winrt + inline HRESULT GetErrorCode(_In_ Platform::Exception^ exception) WI_NOEXCEPT + { + return exception->HResult; + } + + inline void SetFailureInfo(_In_ FailureInfo const &, _Inout_ Platform::Exception^ exception) WI_NOEXCEPT + { + // no-op -- once a PlatformException^ is created, we can't modify the message, but this function must + // exist to distinguish this from ResultException + } +#endif + + template + RESULT_NORETURN inline void ReportFailure_CustomExceptionHelper(_Inout_ T &exception, __R_FN_PARAMS_FULL, _In_opt_ PCWSTR message = nullptr) + { + // When seeing the error: "cannot convert parameter 1 from 'XXX' to 'wil::ResultException &'" + // Custom exceptions must be based upon either ResultException or Platform::Exception^ to be used with ResultException.h. + // This compilation error indicates an attempt to throw an incompatible exception type. + const HRESULT hr = GetErrorCode(exception); + + FailureInfo failure; + wchar_t debugString[2048]; + char callContextString[1024]; + + LogFailure(__R_FN_CALL_FULL, FailureType::Exception, ResultStatus::FromResult(hr), message, false, // false = does not need debug string + debugString, ARRAYSIZE(debugString), callContextString, ARRAYSIZE(callContextString), &failure); + + // push the failure info context into the custom exception class + SetFailureInfo(failure, exception); + + throw exception; + } + + template + __declspec(noreturn, noinline) inline void ReportFailure_CustomException(__R_FN_PARAMS _In_ T exception) + { + __R_FN_LOCALS_RA; + ReportFailure_CustomExceptionHelper(exception, __R_FN_CALL_FULL); + } + + template + __declspec(noreturn, noinline) inline void ReportFailure_CustomExceptionMsg(__R_FN_PARAMS _In_ T exception, _In_ _Printf_format_string_ PCSTR formatString, ...) + { + va_list argList; + va_start(argList, formatString); + wchar_t message[2048]; + PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList); + + __R_FN_LOCALS_RA; + ReportFailure_CustomExceptionHelper(exception, __R_FN_CALL_FULL, message); + } +#endif + + namespace __R_NS_NAME + { + //***************************************************************************** + // Return Macros + //***************************************************************************** + + __R_DIRECT_METHOD(void, Return_Hr)(__R_DIRECT_FN_PARAMS HRESULT hr) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_Hr(__R_DIRECT_FN_CALL hr); + } + + _Success_(true) + _Translates_Win32_to_HRESULT_(err) + __R_DIRECT_METHOD(HRESULT, Return_Win32)(__R_DIRECT_FN_PARAMS DWORD err) WI_NOEXCEPT + { + __R_FN_LOCALS; + return wil::details::ReportFailure_Win32(__R_DIRECT_FN_CALL err); + } + + _Success_(true) + _Translates_last_error_to_HRESULT_ + __R_DIRECT_METHOD(HRESULT, Return_GetLastError)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __R_FN_LOCALS; + return wil::details::ReportFailure_GetLastErrorHr(__R_DIRECT_FN_CALL_ONLY); + } + + _Success_(true) + _Translates_NTSTATUS_to_HRESULT_(status) + __R_DIRECT_METHOD(HRESULT, Return_NtStatus)(__R_DIRECT_FN_PARAMS NTSTATUS status) WI_NOEXCEPT + { + __R_FN_LOCALS; + return wil::details::ReportFailure_NtStatus(__R_DIRECT_FN_CALL status); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + __R_DIRECT_METHOD(HRESULT, Return_CaughtException)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __R_FN_LOCALS; + return wil::details::ReportFailure_CaughtException(__R_DIRECT_FN_CALL_ONLY); + } +#endif + + __R_DIRECT_METHOD(void, Return_HrMsg)(__R_DIRECT_FN_PARAMS HRESULT hr, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__R_DIRECT_FN_CALL hr, formatString, argList); + } + + _Success_(true) + _Translates_Win32_to_HRESULT_(err) + __R_DIRECT_METHOD(HRESULT, Return_Win32Msg)(__R_DIRECT_FN_PARAMS DWORD err, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + return wil::details::ReportFailure_Win32Msg(__R_DIRECT_FN_CALL err, formatString, argList); + } + + _Success_(true) + _Translates_last_error_to_HRESULT_ + __R_DIRECT_METHOD(HRESULT, Return_GetLastErrorMsg)(__R_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + return wil::details::ReportFailure_GetLastErrorHrMsg(__R_DIRECT_FN_CALL formatString, argList); + } + + _Success_(true) + _Translates_NTSTATUS_to_HRESULT_(status) + __R_DIRECT_METHOD(HRESULT, Return_NtStatusMsg)(__R_DIRECT_FN_PARAMS NTSTATUS status, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + return wil::details::ReportFailure_NtStatusMsg(__R_DIRECT_FN_CALL status, formatString, argList); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + __R_DIRECT_METHOD(HRESULT, Return_CaughtExceptionMsg)(__R_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + return wil::details::ReportFailure_CaughtExceptionMsg(__R_DIRECT_FN_CALL formatString, argList); + } +#endif + + //***************************************************************************** + // Log Macros + //***************************************************************************** + + _Post_satisfies_(return == hr) + __R_DIRECT_METHOD(HRESULT, Log_Hr)(__R_DIRECT_FN_PARAMS HRESULT hr) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_Hr(__R_DIRECT_FN_CALL hr); + return hr; + } + + _Post_satisfies_(return == err) + __R_DIRECT_METHOD(DWORD, Log_Win32)(__R_DIRECT_FN_PARAMS DWORD err) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_Win32(__R_DIRECT_FN_CALL err); + return err; + } + + __R_DIRECT_METHOD(DWORD, Log_GetLastError)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __R_FN_LOCALS; + return wil::details::ReportFailure_GetLastError(__R_DIRECT_FN_CALL_ONLY); + } + + _Post_satisfies_(return == status) + __R_DIRECT_METHOD(NTSTATUS, Log_NtStatus)(__R_DIRECT_FN_PARAMS NTSTATUS status) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_NtStatus(__R_DIRECT_FN_CALL status); + return status; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + __R_DIRECT_METHOD(HRESULT, Log_CaughtException)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __R_FN_LOCALS; + return wil::details::ReportFailure_CaughtException(__R_DIRECT_FN_CALL_ONLY); + } +#endif + + __R_INTERNAL_METHOD(_Log_Hr)(__R_INTERNAL_FN_PARAMS HRESULT hr) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_Hr(__R_INTERNAL_FN_CALL hr); + } + + __R_INTERNAL_METHOD(_Log_GetLastError)(__R_INTERNAL_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_GetLastError(__R_INTERNAL_FN_CALL_ONLY); + } + + __R_INTERNAL_METHOD(_Log_Win32)(__R_INTERNAL_FN_PARAMS DWORD err) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_Win32(__R_INTERNAL_FN_CALL err); + } + + __R_INTERNAL_METHOD(_Log_NullAlloc)(__R_INTERNAL_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_Hr(__R_INTERNAL_FN_CALL E_OUTOFMEMORY); + } + + __R_INTERNAL_METHOD(_Log_NtStatus)(__R_INTERNAL_FN_PARAMS NTSTATUS status) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_NtStatus(__R_INTERNAL_FN_CALL status); + } + + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. + _Post_satisfies_(return == hr) + __R_CONDITIONAL_METHOD(HRESULT, Log_IfFailed)(__R_CONDITIONAL_FN_PARAMS HRESULT hr) + { + if (FAILED(hr)) + { + __R_CALL_INTERNAL_METHOD(_Log_Hr)(__R_CONDITIONAL_FN_CALL hr); + } + return hr; + } + + _Post_satisfies_(return == hr) + __R_CONDITIONAL_NOINLINE_METHOD(HRESULT, Log_IfFailedWithExpected)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, unsigned int expectedCount, ...) WI_NOEXCEPT + { + va_list args; + va_start(args, expectedCount); + + if (FAILED(hr)) + { + unsigned int expectedIndex; + for (expectedIndex = 0; expectedIndex < expectedCount; ++expectedIndex) + { + if (hr == va_arg(args, HRESULT)) + { + break; + } + } + + if (expectedIndex == expectedCount) + { + __R_CALL_INTERNAL_METHOD(_Log_Hr)(__R_CONDITIONAL_FN_CALL hr); + } + } + + va_end(args); + return hr; + } + + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. + _Post_satisfies_(return == ret) + __R_CONDITIONAL_METHOD(BOOL, Log_IfWin32BoolFalse)(__R_CONDITIONAL_FN_PARAMS BOOL ret) + { + if (!ret) + { + __R_CALL_INTERNAL_METHOD(_Log_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return ret; + } + + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. + _Post_satisfies_(return == err) + __R_CONDITIONAL_METHOD(DWORD, Log_IfWin32Error)(__R_CONDITIONAL_FN_PARAMS DWORD err) + { + if (FAILED_WIN32(err)) + { + __R_CALL_INTERNAL_METHOD(_Log_Win32)(__R_CONDITIONAL_FN_CALL err); + } + return err; + } + + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. + _Post_satisfies_(return == handle) + __R_CONDITIONAL_METHOD(HANDLE, Log_IfHandleInvalid)(__R_CONDITIONAL_FN_PARAMS HANDLE handle) + { + if (handle == INVALID_HANDLE_VALUE) + { + __R_CALL_INTERNAL_METHOD(_Log_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return handle; + } + + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. + _Post_satisfies_(return == handle) + __R_CONDITIONAL_METHOD(HANDLE, Log_IfHandleNull)(__R_CONDITIONAL_FN_PARAMS HANDLE handle) + { + if (handle == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Log_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return handle; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) + __R_CONDITIONAL_TEMPLATE_METHOD(PointerT, Log_IfNullAlloc)(__R_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) WI_NOEXCEPT + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Log_NullAlloc)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __R_CONDITIONAL_TEMPLATE_METHOD(void, Log_IfNullAlloc)(__R_CONDITIONAL_FN_PARAMS const PointerT& pointer) WI_NOEXCEPT + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Log_NullAlloc)(__R_CONDITIONAL_FN_CALL_ONLY); + } + } + + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. + _Post_satisfies_(return == condition) + __R_CONDITIONAL_METHOD(bool, Log_HrIf)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition) + { + if (condition) + { + __R_CALL_INTERNAL_METHOD(_Log_Hr)(__R_CONDITIONAL_FN_CALL hr); + } + return condition; + } + + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. + _Post_satisfies_(return == condition) + __R_CONDITIONAL_METHOD(bool, Log_HrIfFalse)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition) + { + if (!condition) + { + __R_CALL_INTERNAL_METHOD(_Log_Hr)(__R_CONDITIONAL_FN_CALL hr); + } + return condition; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) + __R_CONDITIONAL_TEMPLATE_METHOD(PointerT, Log_HrIfNull)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, _Pre_maybenull_ PointerT pointer) WI_NOEXCEPT + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Log_Hr)(__R_CONDITIONAL_FN_CALL hr); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __R_CONDITIONAL_TEMPLATE_METHOD(void, Log_HrIfNull)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer) WI_NOEXCEPT + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Log_Hr)(__R_CONDITIONAL_FN_CALL hr); + } + } + + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. + _Post_satisfies_(return == condition) + __R_CONDITIONAL_METHOD(bool, Log_GetLastErrorIf)(__R_CONDITIONAL_FN_PARAMS bool condition) + { + if (condition) + { + __R_CALL_INTERNAL_METHOD(_Log_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return condition; + } + + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. + _Post_satisfies_(return == condition) + __R_CONDITIONAL_METHOD(bool, Log_GetLastErrorIfFalse)(__R_CONDITIONAL_FN_PARAMS bool condition) + { + if (!condition) + { + __R_CALL_INTERNAL_METHOD(_Log_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return condition; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) + __R_CONDITIONAL_TEMPLATE_METHOD(PointerT, Log_GetLastErrorIfNull)(__R_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) WI_NOEXCEPT + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Log_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __R_CONDITIONAL_TEMPLATE_METHOD(void, Log_GetLastErrorIfNull)(__R_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer) WI_NOEXCEPT + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Log_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + } + + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. + _Post_satisfies_(return == status) + __R_CONDITIONAL_METHOD(NTSTATUS, Log_IfNtStatusFailed)(__R_CONDITIONAL_FN_PARAMS NTSTATUS status) + { + if (FAILED_NTSTATUS(status)) + { + __R_CALL_INTERNAL_METHOD(_Log_NtStatus)(__R_CONDITIONAL_FN_CALL status); + } + return status; + } + + _Post_satisfies_(return == hr) + __R_DIRECT_METHOD(HRESULT, Log_HrMsg)(__R_DIRECT_FN_PARAMS HRESULT hr, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__R_DIRECT_FN_CALL hr, formatString, argList); + return hr; + } + + _Post_satisfies_(return == err) + __R_DIRECT_METHOD(DWORD, Log_Win32Msg)(__R_DIRECT_FN_PARAMS DWORD err, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + wil::details::ReportFailure_Win32Msg(__R_DIRECT_FN_CALL err, formatString, argList); + return err; + } + + __R_DIRECT_METHOD(DWORD, Log_GetLastErrorMsg)(__R_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + return wil::details::ReportFailure_GetLastErrorMsg(__R_DIRECT_FN_CALL formatString, argList); + } + + _Post_satisfies_(return == status) + __R_DIRECT_METHOD(NTSTATUS, Log_NtStatusMsg)(__R_DIRECT_FN_PARAMS NTSTATUS status, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + wil::details::ReportFailure_NtStatusMsg(__R_DIRECT_FN_CALL status, formatString, argList); + return status; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + __R_DIRECT_METHOD(HRESULT, Log_CaughtExceptionMsg)(__R_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + return wil::details::ReportFailure_CaughtExceptionMsg(__R_DIRECT_FN_CALL formatString, argList); + } +#endif + + __R_INTERNAL_NOINLINE_METHOD(_Log_HrMsg)(__R_INTERNAL_NOINLINE_FN_PARAMS HRESULT hr, _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__R_INTERNAL_NOINLINE_FN_CALL hr, formatString, argList); + } + + __R_INTERNAL_NOINLINE_METHOD(_Log_GetLastErrorMsg)(__R_INTERNAL_NOINLINE_FN_PARAMS _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_GetLastErrorMsg(__R_INTERNAL_NOINLINE_FN_CALL formatString, argList); + } + + __R_INTERNAL_NOINLINE_METHOD(_Log_Win32Msg)(__R_INTERNAL_NOINLINE_FN_PARAMS DWORD err, _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_Win32Msg(__R_INTERNAL_NOINLINE_FN_CALL err, formatString, argList); + } + + __R_INTERNAL_NOINLINE_METHOD(_Log_NullAllocMsg)(__R_INTERNAL_NOINLINE_FN_PARAMS _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__R_INTERNAL_NOINLINE_FN_CALL E_OUTOFMEMORY, formatString, argList); + } + + __R_INTERNAL_NOINLINE_METHOD(_Log_NtStatusMsg)(__R_INTERNAL_NOINLINE_FN_PARAMS NTSTATUS status, _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + __R_FN_LOCALS; + wil::details::ReportFailure_NtStatusMsg(__R_INTERNAL_NOINLINE_FN_CALL status, formatString, argList); + } + + _Post_satisfies_(return == hr) + __R_CONDITIONAL_NOINLINE_METHOD(HRESULT, Log_IfFailedMsg)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (FAILED(hr)) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_HrMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return hr; + } + + _Post_satisfies_(return == ret) + __R_CONDITIONAL_NOINLINE_METHOD(BOOL, Log_IfWin32BoolFalseMsg)(__R_CONDITIONAL_FN_PARAMS BOOL ret, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (!ret) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return ret; + } + + _Post_satisfies_(return == err) + __R_CONDITIONAL_NOINLINE_METHOD(DWORD, Log_IfWin32ErrorMsg)(__R_CONDITIONAL_FN_PARAMS DWORD err, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (FAILED_WIN32(err)) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_Win32Msg)(__R_CONDITIONAL_NOINLINE_FN_CALL err, formatString, argList); + } + return err; + } + + _Post_satisfies_(return == handle) + __R_CONDITIONAL_NOINLINE_METHOD(HANDLE, Log_IfHandleInvalidMsg)(__R_CONDITIONAL_FN_PARAMS HANDLE handle, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (handle == INVALID_HANDLE_VALUE) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return handle; + } + + _Post_satisfies_(return == handle) + __R_CONDITIONAL_NOINLINE_METHOD(HANDLE, Log_IfHandleNullMsg)(__R_CONDITIONAL_FN_PARAMS HANDLE handle, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (handle == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return handle; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(PointerT, Log_IfNullAllocMsg)(__R_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_NullAllocMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL_ONLY, formatString, argList); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, Log_IfNullAllocMsg)(__R_CONDITIONAL_FN_PARAMS const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_NullAllocMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL_ONLY, formatString, argList); + } + } + + _Post_satisfies_(return == condition) + __R_CONDITIONAL_NOINLINE_METHOD(bool, Log_HrIfMsg)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (condition) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_HrMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return condition; + } + + _Post_satisfies_(return == condition) + __R_CONDITIONAL_NOINLINE_METHOD(bool, Log_HrIfFalseMsg)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (!condition) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_HrMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return condition; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(PointerT, Log_HrIfNullMsg)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, _Pre_maybenull_ PointerT pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_HrMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, Log_HrIfNullMsg)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_HrMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + } + + _Post_satisfies_(return == condition) + __R_CONDITIONAL_NOINLINE_METHOD(bool, Log_GetLastErrorIfMsg)(__R_CONDITIONAL_FN_PARAMS bool condition, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (condition) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return condition; + } + + _Post_satisfies_(return == condition) + __R_CONDITIONAL_NOINLINE_METHOD(bool, Log_GetLastErrorIfFalseMsg)(__R_CONDITIONAL_FN_PARAMS bool condition, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (!condition) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return condition; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(PointerT, Log_GetLastErrorIfNullMsg)(__R_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, Log_GetLastErrorIfNullMsg)(__R_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + } + + _Post_satisfies_(return == status) + __R_CONDITIONAL_NOINLINE_METHOD(NTSTATUS, Log_IfNtStatusFailedMsg)(__R_CONDITIONAL_FN_PARAMS NTSTATUS status, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (FAILED_NTSTATUS(status)) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Log_NtStatusMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL status, formatString, argList); + } + return status; + } + } // namespace __R_NS_NAME + + namespace __RFF_NS_NAME + { + //***************************************************************************** + // FailFast Macros + //***************************************************************************** + + __RFF_DIRECT_NORET_METHOD(void, FailFast_Hr)(__RFF_DIRECT_FN_PARAMS HRESULT hr) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_Hr(__RFF_DIRECT_FN_CALL hr); + } + + __RFF_DIRECT_NORET_METHOD(void, FailFast_Win32)(__RFF_DIRECT_FN_PARAMS DWORD err) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_Win32(__RFF_DIRECT_FN_CALL err); + } + + __RFF_DIRECT_NORET_METHOD(void, FailFast_GetLastError)(__RFF_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_GetLastError(__RFF_DIRECT_FN_CALL_ONLY); + } + + __RFF_DIRECT_NORET_METHOD(void, FailFast_NtStatus)(__RFF_DIRECT_FN_PARAMS NTSTATUS status) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_NtStatus(__RFF_DIRECT_FN_CALL status); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + __RFF_DIRECT_NORET_METHOD(void, FailFast_CaughtException)(__RFF_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_CaughtException(__RFF_DIRECT_FN_CALL_ONLY); + } +#endif + + __RFF_INTERNAL_NORET_METHOD(_FailFast_Hr)(__RFF_INTERNAL_FN_PARAMS HRESULT hr) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_Hr(__RFF_INTERNAL_FN_CALL hr); + } + + __RFF_INTERNAL_NORET_METHOD(_FailFast_GetLastError)(__RFF_INTERNAL_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_GetLastError(__RFF_INTERNAL_FN_CALL_ONLY); + } + + __RFF_INTERNAL_NORET_METHOD(_FailFast_Win32)(__RFF_INTERNAL_FN_PARAMS DWORD err) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_Win32(__RFF_INTERNAL_FN_CALL err); + } + + __RFF_INTERNAL_NORET_METHOD(_FailFast_NullAlloc)(__RFF_INTERNAL_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_Hr(__RFF_INTERNAL_FN_CALL E_OUTOFMEMORY); + } + + __RFF_INTERNAL_NORET_METHOD(_FailFast_NtStatus)(__RFF_INTERNAL_FN_PARAMS NTSTATUS status) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_NtStatus(__RFF_INTERNAL_FN_CALL status); + } + + _Post_satisfies_(return == hr) _When_(FAILED(hr), _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(HRESULT, FailFast_IfFailed)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr) WI_NOEXCEPT + { + if (FAILED(hr)) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_Hr)(__RFF_CONDITIONAL_FN_CALL hr); + } + return hr; + } + + _Post_satisfies_(return == ret) _When_(!ret, _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(BOOL, FailFast_IfWin32BoolFalse)(__RFF_CONDITIONAL_FN_PARAMS BOOL ret) WI_NOEXCEPT + { + if (!ret) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_GetLastError)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + return ret; + } + + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. + _Post_satisfies_(return == err) _When_(FAILED_WIN32(err), _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(DWORD, FailFast_IfWin32Error)(__RFF_CONDITIONAL_FN_PARAMS DWORD err) + { + if (FAILED_WIN32(err)) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_Win32)(__RFF_CONDITIONAL_FN_CALL err); + } + return err; + } + + _Post_satisfies_(return == handle) _When_(handle == INVALID_HANDLE_VALUE, _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(HANDLE, FailFast_IfHandleInvalid)(__RFF_CONDITIONAL_FN_PARAMS HANDLE handle) WI_NOEXCEPT + { + if (handle == INVALID_HANDLE_VALUE) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_GetLastError)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + return handle; + } + + _Post_satisfies_(return == handle) _When_(handle == nullptr, _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(RESULT_NORETURN_NULL HANDLE, FailFast_IfHandleNull)(__RFF_CONDITIONAL_FN_PARAMS HANDLE handle) WI_NOEXCEPT + { + if (handle == nullptr) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_GetLastError)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + return handle; + } + + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_IfNullAlloc)(__RFF_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) + { + if (pointer == nullptr) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_NullAlloc)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + return pointer; + } + + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_IfNullAlloc)(__RFF_CONDITIONAL_FN_PARAMS const PointerT& pointer) + { + if (pointer == nullptr) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_NullAlloc)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + } + + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(bool, FailFast_HrIf)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition) WI_NOEXCEPT + { + if (condition) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_Hr)(__RFF_CONDITIONAL_FN_CALL hr); + } + return condition; + } + + _Post_satisfies_(return == condition) _When_(!condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(bool, FailFast_HrIfFalse)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition) WI_NOEXCEPT + { + if (!condition) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_Hr)(__RFF_CONDITIONAL_FN_CALL hr); + } + return condition; + } + + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_HrIfNull)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, _Pre_maybenull_ PointerT pointer) + { + if (pointer == nullptr) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_Hr)(__RFF_CONDITIONAL_FN_CALL hr); + } + return pointer; + } + + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_HrIfNull)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer) + { + if (pointer == nullptr) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_Hr)(__RFF_CONDITIONAL_FN_CALL hr); + } + } + + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(bool, FailFast_GetLastErrorIf)(__RFF_CONDITIONAL_FN_PARAMS bool condition) WI_NOEXCEPT + { + if (condition) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_GetLastError)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + return condition; + } + + _Post_satisfies_(return == condition) _When_(!condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(bool, FailFast_GetLastErrorIfFalse)(__RFF_CONDITIONAL_FN_PARAMS bool condition) WI_NOEXCEPT + { + if (!condition) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_GetLastError)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + return condition; + } + + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_GetLastErrorIfNull)(__RFF_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) + { + if (pointer == nullptr) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_GetLastError)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + return pointer; + } + + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_GetLastErrorIfNull)(__RFF_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer) + { + if (pointer == nullptr) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_GetLastError)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + } + + _Post_satisfies_(return == status) _When_(FAILED_NTSTATUS(status), _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(NTSTATUS, FailFast_IfNtStatusFailed)(__RFF_CONDITIONAL_FN_PARAMS NTSTATUS status) WI_NOEXCEPT + { + if (FAILED_NTSTATUS(status)) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_NtStatus)(__RFF_CONDITIONAL_FN_CALL status); + } + return status; + } + + __RFF_DIRECT_NORET_METHOD(void, FailFast_HrMsg)(__RFF_DIRECT_FN_PARAMS HRESULT hr, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __RFF_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__RFF_DIRECT_FN_CALL hr, formatString, argList); + } + + __RFF_DIRECT_NORET_METHOD(void, FailFast_Win32Msg)(__RFF_DIRECT_FN_PARAMS DWORD err, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __RFF_FN_LOCALS; + wil::details::ReportFailure_Win32Msg(__RFF_DIRECT_FN_CALL err, formatString, argList); + } + + __RFF_DIRECT_NORET_METHOD(void, FailFast_GetLastErrorMsg)(__RFF_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __RFF_FN_LOCALS; + wil::details::ReportFailure_GetLastErrorMsg(__RFF_DIRECT_FN_CALL formatString, argList); + } + + __RFF_DIRECT_NORET_METHOD(void, FailFast_NtStatusMsg)(__RFF_DIRECT_FN_PARAMS NTSTATUS status, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __RFF_FN_LOCALS; + wil::details::ReportFailure_NtStatusMsg(__RFF_DIRECT_FN_CALL status, formatString, argList); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + __RFF_DIRECT_NORET_METHOD(void, FailFast_CaughtExceptionMsg)(__RFF_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __RFF_FN_LOCALS; + wil::details::ReportFailure_CaughtExceptionMsg(__RFF_DIRECT_FN_CALL formatString, argList); + } +#endif + + __RFF_INTERNAL_NOINLINE_NORET_METHOD(_FailFast_HrMsg)(__RFF_INTERNAL_NOINLINE_FN_PARAMS HRESULT hr, _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__RFF_INTERNAL_NOINLINE_FN_CALL hr, formatString, argList); + } + + __RFF_INTERNAL_NOINLINE_NORET_METHOD(_FailFast_GetLastErrorMsg)(__RFF_INTERNAL_NOINLINE_FN_PARAMS _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_GetLastErrorMsg(__RFF_INTERNAL_NOINLINE_FN_CALL formatString, argList); + } + + __RFF_INTERNAL_NOINLINE_NORET_METHOD(_FailFast_Win32Msg)(__RFF_INTERNAL_NOINLINE_FN_PARAMS DWORD err, _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_Win32Msg(__RFF_INTERNAL_NOINLINE_FN_CALL err, formatString, argList); + } + + __RFF_INTERNAL_NOINLINE_NORET_METHOD(_FailFast_NullAllocMsg)(__RFF_INTERNAL_NOINLINE_FN_PARAMS _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__RFF_INTERNAL_NOINLINE_FN_CALL E_OUTOFMEMORY, formatString, argList); + } + + __RFF_INTERNAL_NOINLINE_NORET_METHOD(_FailFast_NtStatusMsg)(__RFF_INTERNAL_NOINLINE_FN_PARAMS NTSTATUS status, _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_NtStatusMsg(__RFF_INTERNAL_NOINLINE_FN_CALL status, formatString, argList); + } + + _Post_satisfies_(return == hr) _When_(FAILED(hr), _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(HRESULT, FailFast_IfFailedMsg)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (FAILED(hr)) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_HrMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return hr; + } + + _Post_satisfies_(return == ret) _When_(!ret, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(BOOL, FailFast_IfWin32BoolFalseMsg)(__RFF_CONDITIONAL_FN_PARAMS BOOL ret, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (!ret) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_GetLastErrorMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return ret; + } + + _Post_satisfies_(return == err) _When_(FAILED_WIN32(err), _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(DWORD, FailFast_IfWin32ErrorMsg)(__RFF_CONDITIONAL_FN_PARAMS DWORD err, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (FAILED_WIN32(err)) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_Win32Msg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL err, formatString, argList); + } + return err; + } + + _Post_satisfies_(return == handle) _When_(handle == INVALID_HANDLE_VALUE, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(HANDLE, FailFast_IfHandleInvalidMsg)(__RFF_CONDITIONAL_FN_PARAMS HANDLE handle, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (handle == INVALID_HANDLE_VALUE) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_GetLastErrorMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return handle; + } + + _Post_satisfies_(return == handle) _When_(handle == nullptr, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(RESULT_NORETURN_NULL HANDLE, FailFast_IfHandleNullMsg)(__RFF_CONDITIONAL_FN_PARAMS HANDLE handle, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (handle == nullptr) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_GetLastErrorMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return handle; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_IfNullAllocMsg)(__RFF_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_NullAllocMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL_ONLY, formatString, argList); + } + return pointer; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, FailFast_IfNullAllocMsg)(__RFF_CONDITIONAL_FN_PARAMS const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_NullAllocMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL_ONLY, formatString, argList); + } + } + + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(bool, FailFast_HrIfMsg)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (condition) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_HrMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return condition; + } + + _Post_satisfies_(return == condition) _When_(!condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(bool, FailFast_HrIfFalseMsg)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (!condition) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_HrMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return condition; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_HrIfNullMsg)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, _Pre_maybenull_ PointerT pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_HrMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return pointer; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, FailFast_HrIfNullMsg)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_HrMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + } + + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(bool, FailFast_GetLastErrorIfMsg)(__RFF_CONDITIONAL_FN_PARAMS bool condition, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (condition) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_GetLastErrorMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return condition; + } + + _Post_satisfies_(return == condition) _When_(!condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(bool, FailFast_GetLastErrorIfFalseMsg)(__RFF_CONDITIONAL_FN_PARAMS bool condition, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (!condition) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_GetLastErrorMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return condition; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_GetLastErrorIfNullMsg)(__RFF_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_GetLastErrorMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return pointer; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, FailFast_GetLastErrorIfNullMsg)(__RFF_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_GetLastErrorMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + } + + _Post_satisfies_(return == status) _When_(FAILED_NTSTATUS(status), _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(NTSTATUS, FailFast_IfNtStatusFailedMsg)(__RFF_CONDITIONAL_FN_PARAMS NTSTATUS status, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (FAILED_NTSTATUS(status)) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_NtStatusMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL status, formatString, argList); + } + return status; + } + + __RFF_DIRECT_NORET_METHOD(void, FailFast_Unexpected)(__RFF_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_Hr(__RFF_DIRECT_FN_CALL E_UNEXPECTED); + } + + __RFF_INTERNAL_NORET_METHOD(_FailFast_Unexpected)(__RFF_INTERNAL_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_Hr(__RFF_INTERNAL_FN_CALL E_UNEXPECTED); + } + + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(bool, FailFast_If)(__RFF_CONDITIONAL_FN_PARAMS bool condition) WI_NOEXCEPT + { + if (condition) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_Unexpected)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + return condition; + } + + _Post_satisfies_(return == condition) _When_(!condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(bool, FailFast_IfFalse)(__RFF_CONDITIONAL_FN_PARAMS bool condition) WI_NOEXCEPT + { + if (!condition) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_Unexpected)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + return condition; + } + + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + __WI_SUPPRESS_NULLPTR_ANALYSIS + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_IfNull)(__RFF_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) + { + if (pointer == nullptr) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_Unexpected)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + return pointer; + } + + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __WI_SUPPRESS_NULLPTR_ANALYSIS + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_IfNull)(__RFF_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer) + { + if (pointer == nullptr) + { + __RFF_CALL_INTERNAL_METHOD(_FailFast_Unexpected)(__RFF_CONDITIONAL_FN_CALL_ONLY); + } + } + + __RFF_DIRECT_NORET_METHOD(void, FailFast_UnexpectedMsg)(__RFF_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + va_list argList; + va_start(argList, formatString); + __RFF_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__RFF_DIRECT_FN_CALL E_UNEXPECTED, formatString, argList); + } + + __RFF_INTERNAL_NOINLINE_NORET_METHOD(_FailFast_UnexpectedMsg)(__RFF_INTERNAL_NOINLINE_FN_PARAMS _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + __RFF_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__RFF_INTERNAL_NOINLINE_FN_CALL E_UNEXPECTED, formatString, argList); + } + + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(bool, FailFast_IfMsg)(__RFF_CONDITIONAL_FN_PARAMS bool condition, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (condition) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_UnexpectedMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return condition; + } + + _Post_satisfies_(return == condition) _When_(!condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_METHOD(bool, FailFast_IfFalseMsg)(__RFF_CONDITIONAL_FN_PARAMS bool condition, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (!condition) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_UnexpectedMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return condition; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_IfNullMsg)(__RFF_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_UnexpectedMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return pointer; + } + + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __RFF_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, FailFast_IfNullMsg)(__RFF_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __RFF_CALL_INTERNAL_NOINLINE_METHOD(_FailFast_UnexpectedMsg)(__RFF_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + } + + //***************************************************************************** + // FailFast Immediate Macros + //***************************************************************************** + + __RFF_DIRECT_NORET_METHOD(void, FailFastImmediate_Unexpected)() WI_NOEXCEPT + { + __fastfail(FAST_FAIL_FATAL_APP_EXIT); + } + + __RFF_INTERNAL_NORET_METHOD(_FailFastImmediate_Unexpected)() WI_NOEXCEPT + { + __fastfail(FAST_FAIL_FATAL_APP_EXIT); + } + + _Post_satisfies_(return == hr) _When_(FAILED(hr), _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(HRESULT, FailFastImmediate_IfFailed)(HRESULT hr) WI_NOEXCEPT + { + if (FAILED(hr)) + { + __RFF_CALL_INTERNAL_METHOD(_FailFastImmediate_Unexpected)(); + } + return hr; + } + + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(bool, FailFastImmediate_If)(bool condition) WI_NOEXCEPT + { + if (condition) + { + __RFF_CALL_INTERNAL_METHOD(_FailFastImmediate_Unexpected)(); + } + return condition; + } + + _Post_satisfies_(return == condition) _When_(!condition, _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(bool, FailFastImmediate_IfFalse)(bool condition) WI_NOEXCEPT + { + if (!condition) + { + __RFF_CALL_INTERNAL_METHOD(_FailFastImmediate_Unexpected)(); + } + return condition; + } + + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFastImmediate_IfNull)(_Pre_maybenull_ PointerT pointer) + { + if (pointer == nullptr) + { + __RFF_CALL_INTERNAL_METHOD(_FailFastImmediate_Unexpected)(); + } + return pointer; + } + + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. + template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFastImmediate_IfNull)(_In_opt_ const PointerT& pointer) + { + if (pointer == nullptr) + { + __RFF_CALL_INTERNAL_METHOD(_FailFastImmediate_Unexpected)(); + } + } + + _Post_satisfies_(return == status) _When_(FAILED_NTSTATUS(status), _Analysis_noreturn_) + __RFF_CONDITIONAL_METHOD(NTSTATUS, FailFastImmediate_IfNtStatusFailed)(NTSTATUS status) WI_NOEXCEPT + { + if (FAILED_NTSTATUS(status)) + { + __RFF_CALL_INTERNAL_METHOD(_FailFastImmediate_Unexpected)(); + } + return status; + } + } // namespace __RFF_NS_NAME + + namespace __R_NS_NAME + { + //***************************************************************************** + // Exception Macros + //***************************************************************************** + +#ifdef WIL_ENABLE_EXCEPTIONS + __R_DIRECT_NORET_METHOD(void, Throw_Hr)(__R_DIRECT_FN_PARAMS HRESULT hr) + { + __R_FN_LOCALS; + wil::details::ReportFailure_Hr(__R_DIRECT_FN_CALL hr); + } + + __R_DIRECT_NORET_METHOD(void, Throw_Win32)(__R_DIRECT_FN_PARAMS DWORD err) + { + __R_FN_LOCALS; + wil::details::ReportFailure_Win32(__R_DIRECT_FN_CALL err); + } + + __R_DIRECT_NORET_METHOD(void, Throw_GetLastError)(__R_DIRECT_FN_PARAMS_ONLY) + { + __R_FN_LOCALS; + wil::details::ReportFailure_GetLastError(__R_DIRECT_FN_CALL_ONLY); + } + + __R_DIRECT_NORET_METHOD(void, Throw_NtStatus)(__R_DIRECT_FN_PARAMS NTSTATUS status) + { + __R_FN_LOCALS; + wil::details::ReportFailure_NtStatus(__R_DIRECT_FN_CALL status); + } + + __R_DIRECT_NORET_METHOD(void, Throw_CaughtException)(__R_DIRECT_FN_PARAMS_ONLY) + { + __R_FN_LOCALS; + wil::details::ReportFailure_CaughtException(__R_DIRECT_FN_CALL_ONLY); + } + + __R_INTERNAL_NORET_METHOD(_Throw_Hr)(__R_INTERNAL_FN_PARAMS HRESULT hr) + { + __R_FN_LOCALS; + wil::details::ReportFailure_Hr(__R_INTERNAL_FN_CALL hr); + } + + __R_INTERNAL_NORET_METHOD(_Throw_GetLastError)(__R_INTERNAL_FN_PARAMS_ONLY) + { + __R_FN_LOCALS; + wil::details::ReportFailure_GetLastError(__R_INTERNAL_FN_CALL_ONLY); + } + + __R_INTERNAL_NORET_METHOD(_Throw_Win32)(__R_INTERNAL_FN_PARAMS DWORD err) + { + __R_FN_LOCALS; + wil::details::ReportFailure_Win32(__R_INTERNAL_FN_CALL err); + } + + __R_INTERNAL_NORET_METHOD(_Throw_NullAlloc)(__R_INTERNAL_FN_PARAMS_ONLY) + { + __R_FN_LOCALS; + wil::details::ReportFailure_Hr(__R_INTERNAL_FN_CALL E_OUTOFMEMORY); + } + + __R_INTERNAL_NORET_METHOD(_Throw_NtStatus)(__R_INTERNAL_FN_PARAMS NTSTATUS status) + { + __R_FN_LOCALS; + wil::details::ReportFailure_NtStatus(__R_INTERNAL_FN_CALL status); + } + + _Post_satisfies_(return == hr) _When_(FAILED(hr), _Analysis_noreturn_) + __R_CONDITIONAL_METHOD(HRESULT, Throw_IfFailed)(__R_CONDITIONAL_FN_PARAMS HRESULT hr) + { + if (FAILED(hr)) + { + __R_CALL_INTERNAL_METHOD(_Throw_Hr)(__R_CONDITIONAL_FN_CALL hr); + } + return hr; + } + + _Post_satisfies_(return == ret) _When_(!ret, _Analysis_noreturn_) + __R_CONDITIONAL_METHOD(BOOL, Throw_IfWin32BoolFalse)(__R_CONDITIONAL_FN_PARAMS BOOL ret) + { + if (!ret) + { + __R_CALL_INTERNAL_METHOD(_Throw_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return ret; + } + + _Post_satisfies_(return == err) _When_(FAILED_WIN32(err), _Analysis_noreturn_) + __R_CONDITIONAL_METHOD(DWORD, Throw_IfWin32Error)(__R_CONDITIONAL_FN_PARAMS DWORD err) + { + if (FAILED_WIN32(err)) + { + __R_CALL_INTERNAL_METHOD(_Throw_Win32)(__R_CONDITIONAL_FN_CALL err); + } + return err; + } + + _Post_satisfies_(return == handle) _When_(handle == INVALID_HANDLE_VALUE, _Analysis_noreturn_) + __R_CONDITIONAL_METHOD(HANDLE, Throw_IfHandleInvalid)(__R_CONDITIONAL_FN_PARAMS HANDLE handle) + { + if (handle == INVALID_HANDLE_VALUE) + { + __R_CALL_INTERNAL_METHOD(_Throw_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return handle; + } + + _Post_satisfies_(return == handle) _When_(handle == nullptr, _Analysis_noreturn_) + __R_CONDITIONAL_METHOD(RESULT_NORETURN_NULL HANDLE, Throw_IfHandleNull)(__R_CONDITIONAL_FN_PARAMS HANDLE handle) + { + if (handle == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Throw_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return handle; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __R_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, Throw_IfNullAlloc)(__R_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Throw_NullAlloc)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __R_CONDITIONAL_TEMPLATE_METHOD(void, Throw_IfNullAlloc)(__R_CONDITIONAL_FN_PARAMS const PointerT& pointer) + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Throw_NullAlloc)(__R_CONDITIONAL_FN_CALL_ONLY); + } + } + + _Post_satisfies_(return == condition) + _When_(condition, _Analysis_noreturn_) + __R_CONDITIONAL_METHOD(bool, Throw_HrIf)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition) + { + if (condition) + { + __R_CALL_INTERNAL_METHOD(_Throw_Hr)(__R_CONDITIONAL_FN_CALL hr); + } + return condition; + } + + _Post_satisfies_(return == condition) + _When_(!condition, _Analysis_noreturn_) + __R_CONDITIONAL_METHOD(bool, Throw_HrIfFalse)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition) + { + if (!condition) + { + __R_CALL_INTERNAL_METHOD(_Throw_Hr)(__R_CONDITIONAL_FN_CALL hr); + } + return condition; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __R_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, Throw_HrIfNull)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, _Pre_maybenull_ PointerT pointer) + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Throw_Hr)(__R_CONDITIONAL_FN_CALL hr); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __R_CONDITIONAL_TEMPLATE_METHOD(void, Throw_HrIfNull)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer) + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Throw_Hr)(__R_CONDITIONAL_FN_CALL hr); + } + } + + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) + __R_CONDITIONAL_METHOD(bool, Throw_GetLastErrorIf)(__R_CONDITIONAL_FN_PARAMS bool condition) + { + if (condition) + { + __R_CALL_INTERNAL_METHOD(_Throw_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return condition; + } + + _Post_satisfies_(return == condition) _When_(!condition, _Analysis_noreturn_) + __R_CONDITIONAL_METHOD(bool, Throw_GetLastErrorIfFalse)(__R_CONDITIONAL_FN_PARAMS bool condition) + { + if (!condition) + { + __R_CALL_INTERNAL_METHOD(_Throw_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return condition; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __R_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, Throw_GetLastErrorIfNull)(__R_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Throw_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __R_CONDITIONAL_TEMPLATE_METHOD(void, Throw_GetLastErrorIfNull)(__R_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer) + { + if (pointer == nullptr) + { + __R_CALL_INTERNAL_METHOD(_Throw_GetLastError)(__R_CONDITIONAL_FN_CALL_ONLY); + } + } + + _Post_satisfies_(return == status) + _When_(FAILED_NTSTATUS(status), _Analysis_noreturn_) + __R_CONDITIONAL_METHOD(NTSTATUS, Throw_IfNtStatusFailed)(__R_CONDITIONAL_FN_PARAMS NTSTATUS status) + { + if (FAILED_NTSTATUS(status)) + { + __R_CALL_INTERNAL_METHOD(_Throw_NtStatus)(__R_CONDITIONAL_FN_CALL status); + } + return status; + } + + __R_DIRECT_NORET_METHOD(void, Throw_HrMsg)(__R_DIRECT_FN_PARAMS HRESULT hr, _Printf_format_string_ PCSTR formatString, ...) + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__R_DIRECT_FN_CALL hr, formatString, argList); + } + + __R_DIRECT_NORET_METHOD(void, Throw_Win32Msg)(__R_DIRECT_FN_PARAMS DWORD err, _Printf_format_string_ PCSTR formatString, ...) + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + wil::details::ReportFailure_Win32Msg(__R_DIRECT_FN_CALL err, formatString, argList); + } + + __R_DIRECT_NORET_METHOD(void, Throw_GetLastErrorMsg)(__R_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + wil::details::ReportFailure_GetLastErrorMsg(__R_DIRECT_FN_CALL formatString, argList); + } + + __R_DIRECT_NORET_METHOD(void, Throw_NtStatusMsg)(__R_DIRECT_FN_PARAMS NTSTATUS status, _Printf_format_string_ PCSTR formatString, ...) + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + wil::details::ReportFailure_NtStatusMsg(__R_DIRECT_FN_CALL status, formatString, argList); + } + + __R_DIRECT_NORET_METHOD(void, Throw_CaughtExceptionMsg)(__R_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) + { + va_list argList; + va_start(argList, formatString); + __R_FN_LOCALS; + wil::details::ReportFailure_CaughtExceptionMsg(__R_DIRECT_FN_CALL formatString, argList); + } + + __R_INTERNAL_NOINLINE_NORET_METHOD(_Throw_HrMsg)(__R_INTERNAL_NOINLINE_FN_PARAMS HRESULT hr, _Printf_format_string_ PCSTR formatString, va_list argList) + { + __R_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__R_INTERNAL_NOINLINE_FN_CALL hr, formatString, argList); + } + + __R_INTERNAL_NOINLINE_NORET_METHOD(_Throw_GetLastErrorMsg)(__R_INTERNAL_NOINLINE_FN_PARAMS _Printf_format_string_ PCSTR formatString, va_list argList) + { + __R_FN_LOCALS; + wil::details::ReportFailure_GetLastErrorMsg(__R_INTERNAL_NOINLINE_FN_CALL formatString, argList); + } + + __R_INTERNAL_NOINLINE_NORET_METHOD(_Throw_Win32Msg)(__R_INTERNAL_NOINLINE_FN_PARAMS DWORD err, _Printf_format_string_ PCSTR formatString, va_list argList) + { + __R_FN_LOCALS; + wil::details::ReportFailure_Win32Msg(__R_INTERNAL_NOINLINE_FN_CALL err, formatString, argList); + } + + __R_INTERNAL_NOINLINE_NORET_METHOD(_Throw_NullAllocMsg)(__R_INTERNAL_NOINLINE_FN_PARAMS _Printf_format_string_ PCSTR formatString, va_list argList) + { + __R_FN_LOCALS; + wil::details::ReportFailure_HrMsg(__R_INTERNAL_NOINLINE_FN_CALL E_OUTOFMEMORY, formatString, argList); + } + + __R_INTERNAL_NOINLINE_NORET_METHOD(_Throw_NtStatusMsg)(__R_INTERNAL_NOINLINE_FN_PARAMS NTSTATUS status, _Printf_format_string_ PCSTR formatString, va_list argList) + { + __R_FN_LOCALS; + wil::details::ReportFailure_NtStatusMsg(__R_INTERNAL_NOINLINE_FN_CALL status, formatString, argList); + } + + _Post_satisfies_(return == hr) _When_(FAILED(hr), _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_METHOD(HRESULT, Throw_IfFailedMsg)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, _Printf_format_string_ PCSTR formatString, ...) + { + if (FAILED(hr)) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_HrMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return hr; + } + + _Post_satisfies_(return == ret) _When_(!ret, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_METHOD(BOOL, Throw_IfWin32BoolFalseMsg)(__R_CONDITIONAL_FN_PARAMS BOOL ret, _Printf_format_string_ PCSTR formatString, ...) + { + if (!ret) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return ret; + } + + _Post_satisfies_(return == err) _When_(FAILED_WIN32(err), _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_METHOD(DWORD, Throw_IfWin32ErrorMsg)(__R_CONDITIONAL_FN_PARAMS DWORD err, _Printf_format_string_ PCSTR formatString, ...) + { + if (FAILED_WIN32(err)) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_Win32Msg)(__R_CONDITIONAL_NOINLINE_FN_CALL err, formatString, argList); + } + return err; + } + + _Post_satisfies_(return == handle) _When_(handle == INVALID_HANDLE_VALUE, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_METHOD(HANDLE, Throw_IfHandleInvalidMsg)(__R_CONDITIONAL_FN_PARAMS HANDLE handle, _Printf_format_string_ PCSTR formatString, ...) + { + if (handle == INVALID_HANDLE_VALUE) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return handle; + } + + _Post_satisfies_(return == handle) _When_(handle == 0, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_METHOD(RESULT_NORETURN_NULL HANDLE, Throw_IfHandleNullMsg)(__R_CONDITIONAL_FN_PARAMS HANDLE handle, _Printf_format_string_ PCSTR formatString, ...) + { + if (handle == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return handle; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, Throw_IfNullAllocMsg)(__R_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer, _Printf_format_string_ PCSTR formatString, ...) + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_NullAllocMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL_ONLY, formatString, argList); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __WI_SUPPRESS_NULLPTR_ANALYSIS + _When_(pointer == nullptr, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, Throw_IfNullAllocMsg)(__R_CONDITIONAL_FN_PARAMS const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_NullAllocMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL_ONLY, formatString, argList); + } + } + + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_METHOD(bool, Throw_HrIfMsg)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition, _Printf_format_string_ PCSTR formatString, ...) + { + if (condition) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_HrMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return condition; + } + + _Post_satisfies_(return == condition) _When_(!condition, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_METHOD(bool, Throw_HrIfFalseMsg)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition, _Printf_format_string_ PCSTR formatString, ...) + { + if (!condition) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_HrMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return condition; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + __WI_SUPPRESS_NULLPTR_ANALYSIS + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, Throw_HrIfNullMsg)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, _Pre_maybenull_ PointerT pointer, _Printf_format_string_ PCSTR formatString, ...) + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_HrMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __WI_SUPPRESS_NULLPTR_ANALYSIS + _When_(pointer == nullptr, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, Throw_HrIfNullMsg)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_HrMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL hr, formatString, argList); + } + } + + _Post_satisfies_(return == condition) _When_(condition, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_METHOD(bool, Throw_GetLastErrorIfMsg)(__R_CONDITIONAL_FN_PARAMS bool condition, _Printf_format_string_ PCSTR formatString, ...) + { + if (condition) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return condition; + } + + _Post_satisfies_(return == condition) _When_(!condition, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_METHOD(bool, Throw_GetLastErrorIfFalseMsg)(__R_CONDITIONAL_FN_PARAMS bool condition, _Printf_format_string_ PCSTR formatString, ...) + { + if (!condition) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return condition; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> + _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, Throw_GetLastErrorIfNullMsg)(__R_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer, _Printf_format_string_ PCSTR formatString, ...) + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + return pointer; + } + + template <__R_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> + __WI_SUPPRESS_NULLPTR_ANALYSIS + _When_(pointer == nullptr, _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_TEMPLATE_METHOD(void, Throw_GetLastErrorIfNullMsg)(__R_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer, _Printf_format_string_ PCSTR formatString, ...) + { + if (pointer == nullptr) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_GetLastErrorMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL formatString, argList); + } + } + + _Post_satisfies_(return == status) _When_(FAILED_NTSTATUS(status), _Analysis_noreturn_) + __R_CONDITIONAL_NOINLINE_METHOD(NTSTATUS, Throw_IfNtStatusFailedMsg)(__R_CONDITIONAL_FN_PARAMS NTSTATUS status, _Printf_format_string_ PCSTR formatString, ...) + { + if (FAILED_NTSTATUS(status)) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_NtStatusMsg)(__R_CONDITIONAL_NOINLINE_FN_CALL status, formatString, argList); + } + return status; + } +#endif // WIL_ENABLE_EXCEPTIONS + + } // __R_NS_NAME namespace + } // details namespace + /// @endcond + + + //***************************************************************************** + // Error Handling Policies to switch between error-handling style + //***************************************************************************** + // The following policies are used as template policies for components that can support exception, fail-fast, and + // error-code based modes. + + // Use for classes which should return HRESULTs as their error-handling policy + // Intentionally removed logging from this policy as logging is more useful at the caller. + struct err_returncode_policy + { + typedef HRESULT result; + + __forceinline static HRESULT Win32BOOL(BOOL fReturn) { RETURN_IF_WIN32_BOOL_FALSE_EXPECTED(fReturn); return S_OK; } + __forceinline static HRESULT Win32Handle(HANDLE h, _Out_ HANDLE *ph) { *ph = h; RETURN_LAST_ERROR_IF_NULL_EXPECTED(h); return S_OK; } + _Post_satisfies_(return == hr) + __forceinline static HRESULT HResult(HRESULT hr) { return hr; } + __forceinline static HRESULT LastError() { return wil::details::GetLastErrorFailHr(); } + __forceinline static HRESULT LastErrorIfFalse(bool condition) { RETURN_LAST_ERROR_IF_EXPECTED(!condition); return S_OK; } + _Post_satisfies_(return == S_OK) + __forceinline static HRESULT OK() { return S_OK; } + }; + + // Use for classes which fail-fast on errors + struct err_failfast_policy + { + typedef _Return_type_success_(true) void result; + __forceinline static result Win32BOOL(BOOL fReturn) { FAIL_FAST_IF_WIN32_BOOL_FALSE(fReturn); } + __forceinline static result Win32Handle(HANDLE h, _Out_ HANDLE *ph) { *ph = h; FAIL_FAST_LAST_ERROR_IF_NULL(h); } + _When_(FAILED(hr), _Analysis_noreturn_) + __forceinline static result HResult(HRESULT hr) { FAIL_FAST_IF_FAILED(hr); } + __forceinline static result LastError() { FAIL_FAST_LAST_ERROR(); } + __forceinline static result LastErrorIfFalse(bool condition) { if (!condition) { FAIL_FAST_LAST_ERROR(); } } + __forceinline static result OK() {} + }; + +#ifdef WIL_ENABLE_EXCEPTIONS + // Use for classes which should return through exceptions as their error-handling policy + struct err_exception_policy + { + typedef _Return_type_success_(true) void result; + __forceinline static result Win32BOOL(BOOL fReturn) { THROW_IF_WIN32_BOOL_FALSE(fReturn); } + __forceinline static result Win32Handle(HANDLE h, _Out_ HANDLE *ph) { *ph = h; THROW_LAST_ERROR_IF_NULL(h); } + _When_(FAILED(hr), _Analysis_noreturn_) + __forceinline static result HResult(HRESULT hr) { THROW_IF_FAILED(hr); } + __forceinline static result LastError() { THROW_LAST_ERROR(); } + __forceinline static result LastErrorIfFalse(bool condition) { if (!condition) { THROW_LAST_ERROR(); } } + __forceinline static result OK() {} + }; +#else + // NOTE: A lot of types use 'err_exception_policy' as a default template argument and therefore it must be defined + // (MSVC is permissive about this, but other compilers are not). This will still cause compilation errors at + // template instantiation time since this type lacks required member functions. An alternative would be to have some + // 'default_err_policy' alias that would be something like 'err_failfast_policy' when exceptions are not available, + // but that may have unexpected side effects when compiling code that expects to be using exceptions + struct err_exception_policy + { + }; +#endif + +} // namespace wil + +#pragma warning(pop) + +#endif // defined(__cplusplus) && !defined(__WIL_MIN_KERNEL) && !defined(WIL_KERNEL_MODE) +#endif // __WIL_RESULTMACROS_INCLUDED diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/result_originate.h b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/result_originate.h new file mode 100644 index 0000000..19a07f8 --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/result_originate.h @@ -0,0 +1,125 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* + +// Note: When origination is enabled by including this file, origination is done as part of the RETURN_* and THROW_* macros. Before originating +// a new error we will observe whether there is already an error payload associated with the current thread. If there is, and the HRESULTs match, +// then a new error will not be originated. Otherwise we will overwrite it with a new origination. The ABI boundary for WinRT APIs will check the +// per-thread error information. The act of checking the error clears it, so there should be minimal risk of failing to originate distinct errors +// simply because the HRESULTs match. +// +// For THROW_ macros we will examine the thread-local error storage once per throw. So typically once, with additional calls if the exception is +// caught and re-thrown. +// +// For RETURN_ macros we will have to examine the thread-local error storage once per frame as the call stack unwinds. Because error conditions +// -should- be uncommon the performance impact of checking TLS should be minimal. The more expensive part is originating the error because it must +// capture the entire stack and some additional data. + +#ifndef __WIL_RESULT_ORIGINATE_INCLUDED +#define __WIL_RESULT_ORIGINATE_INCLUDED + +#include "result.h" +#include // RestrictedErrorInfo uses BSTRs :( +#include "resource.h" +#include "com.h" +#include + +namespace wil +{ + namespace details + { + // Note: The name must begin with "Raise" so that the !analyze auto-bucketing will ignore this stack frame. Otherwise this line of code gets all the blame. + inline void __stdcall RaiseRoOriginateOnWilExceptions(wil::FailureInfo const& failure) WI_NOEXCEPT + { + if ((failure.type == FailureType::Return) || (failure.type == FailureType::Exception)) + { + bool shouldOriginate = true; + + wil::com_ptr_nothrow restrictedErrorInformation; + if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK) + { + // This thread already has an error origination payload. Don't originate again if it has the same HRESULT that we are + // observing right now. + wil::unique_bstr descriptionUnused; + HRESULT existingHr = failure.hr; + wil::unique_bstr restrictedDescriptionUnused; + wil::unique_bstr capabilitySidUnused; + if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused))) + { + shouldOriginate = (failure.hr != existingHr); + } + } + + if (shouldOriginate) + { +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + wil::unique_hmodule errorModule; + if (GetModuleHandleExW(0, L"api-ms-win-core-winrt-error-l1-1-1.dll", &errorModule)) + { + auto pfn = reinterpret_cast(GetProcAddress(errorModule.get(), "RoOriginateError")); + if (pfn != nullptr) + { + pfn(failure.hr, nullptr); + } + } +#else // DESKTOP | SYSTEM + ::RoOriginateError(failure.hr, nullptr); +#endif // DESKTOP | SYSTEM + } + else if (restrictedErrorInformation) + { + // GetRestrictedErrorInfo returns ownership of the error information. If we aren't originating, and an error was already present, + // then we need to restore the error information for later observation. + SetRestrictedErrorInfo(restrictedErrorInformation.get()); + } + } + } + + // This method will check for the presence of stowed exception data on the current thread. If such data exists, and the HRESULT + // matches the current failure, then we will call RoFailFastWithErrorContext. RoFailFastWithErrorContext in this situation will + // result in -VASTLY- improved crash bucketing. It is hard to express just how much better. In other cases we just return and + // the calling method fails fast the same way it always has. + inline void __stdcall FailfastWithContextCallback(wil::FailureInfo const& failure) WI_NOEXCEPT + { + wil::com_ptr_nothrow restrictedErrorInformation; + if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK) + { + wil::unique_bstr descriptionUnused; + HRESULT existingHr = failure.hr; + wil::unique_bstr restrictedDescriptionUnused; + wil::unique_bstr capabilitySidUnused; + if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused)) && + (existingHr == failure.hr)) + { + // GetRestrictedErrorInfo returns ownership of the error information. We want it to be available for RoFailFastWithErrorContext + // so we must restore it via SetRestrictedErrorInfo first. + SetRestrictedErrorInfo(restrictedErrorInformation.get()); + RoFailFastWithErrorContext(existingHr); + } + else + { + // The error didn't match the current failure. Put it back in thread-local storage even though we aren't failing fast + // in this method, so it is available in the debugger just-in-case. + SetRestrictedErrorInfo(restrictedErrorInformation.get()); + } + } + } + } // namespace details +} // namespace wil + +// Automatically call RoOriginateError upon error origination by including this file +WI_HEADER_INITITALIZATION_FUNCTION(ResultStowedExceptionInitialize, [] +{ + ::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions); + ::wil::SetFailfastWithContextCallback(::wil::details::FailfastWithContextCallback); + return 1; +}); + +#endif // __WIL_RESULT_ORIGINATE_INCLUDED diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/rpc_helpers.h b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/rpc_helpers.h new file mode 100644 index 0000000..63fd97b --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/rpc_helpers.h @@ -0,0 +1,206 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_RPC_HELPERS_INCLUDED +#define __WIL_RPC_HELPERS_INCLUDED + +#include "result.h" +#include "resource.h" +#include "wistd_functional.h" +#include "wistd_type_traits.h" + +namespace wil +{ + + /// @cond + namespace details + { + // This call-adapter template converts a void-returning 'wistd::invoke' into + // an HRESULT-returning 'wistd::invoke' that emits S_OK. It can be eliminated + // with 'if constexpr' when C++17 is in wide use. + template struct call_adapter + { + template static HRESULT call(TArgs&& ... args) + { + return wistd::invoke(wistd::forward(args)...); + } + }; + + template<> struct call_adapter + { + template static HRESULT call(TArgs&& ... args) + { + wistd::invoke(wistd::forward(args)...); + return S_OK; + } + }; + + // Some RPC exceptions are already HRESULTs. Others are in the regular Win32 + // error space. If the incoming exception code isn't an HRESULT, wrap it. + constexpr HRESULT map_rpc_exception(DWORD code) + { + return IS_ERROR(code) ? code : __HRESULT_FROM_WIN32(code); + } + } + /// @endcond + + /** Invokes an RPC method, mapping structured exceptions to HRESULTs + Failures encountered by the RPC infrastructure (such as server crashes, authentication + errors, client parameter issues, etc.) are emitted by raising a structured exception from + within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept, + RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual + flow control machinery to use. + + Many RPC methods are defined as returning HRESULT themselves, where the HRESULT indicates + the result of the _work_. HRESULTs returned by a successful completion of the _call_ are + returned as-is. + + RPC methods that have a return type of 'void' are mapped to returning S_OK when the _call_ + completes successfully. + + For example, consider an RPC interface method defined in idl as: + ~~~ + HRESULT GetKittenState([in, ref, string] const wchar_t* name, [out, retval] KittenState** state); + ~~~ + To call this method, use: + ~~~ + wil::unique_rpc_binding binding = // typically gotten elsewhere; + wil::unique_midl_ptr state; + HRESULT hr = wil::invoke_rpc_nothrow(GetKittenState, binding.get(), L"fluffy", state.put()); + RETURN_IF_FAILED(hr); + ~~~ + */ + template HRESULT invoke_rpc_nothrow(TCall&&... args) WI_NOEXCEPT + { + RpcTryExcept + { + // Note: this helper type can be removed with C++17 enabled via + // 'if constexpr(wistd::is_same_v)' + using result_t = typename wistd::__invoke_of::type; + RETURN_IF_FAILED(details::call_adapter::call(wistd::forward(args)...)); + return S_OK; + } + RpcExcept(RpcExceptionFilter(RpcExceptionCode())) + { + RETURN_HR(details::map_rpc_exception(RpcExceptionCode())); + } + RpcEndExcept + } + + /** Invokes an RPC method, mapping structured exceptions to HRESULTs + Failures encountered by the RPC infrastructure (such as server crashes, authentication + errors, client parameter issues, etc.) are emitted by raising a structured exception from + within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept, + RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual + flow control machinery to use. + + Some RPC methods return results (such as a state enumeration or other value) directly in + their signature. This adapter writes that result into a caller-provided object then + returns S_OK. + + For example, consider an RPC interface method defined in idl as: + ~~~ + GUID GetKittenId([in, ref, string] const wchar_t* name); + ~~~ + To call this method, use: + ~~~ + wil::unique_rpc_binding binding = // typically gotten elsewhere; + GUID id; + HRESULT hr = wil::invoke_rpc_result_nothrow(id, GetKittenId, binding.get(), L"fluffy"); + RETURN_IF_FAILED(hr); + ~~~ + */ + template HRESULT invoke_rpc_result_nothrow(TResult& result, TCall&&... args) WI_NOEXCEPT + { + RpcTryExcept + { + result = wistd::invoke(wistd::forward(args)...); + return S_OK; + } + RpcExcept(RpcExceptionFilter(RpcExceptionCode())) + { + RETURN_HR(details::map_rpc_exception(RpcExceptionCode())); + } + RpcEndExcept + } + + namespace details + { + // Provides an adapter around calling the context-handle-close method on an + // RPC interface, which itself is an RPC call. + template + struct rpc_closer_t + { + static void Close(TStorage arg) WI_NOEXCEPT + { + LOG_IF_FAILED(invoke_rpc_nothrow(close_fn, &arg)); + } + }; + } + + /** Manages explicit RPC context handles + Explicit RPC context handles are used in many RPC interfaces. Most interfaces with + context handles have an explicit `FooClose([in, out] CONTEXT*)` method that lets + the server close out the context handle. As the close method itself is an RPC call, + it can fail and raise a structured exception. + + This type routes the context-handle-specific `Close` call through the `invoke_rpc_nothrow` + helper, ensuring correct cleanup and lifecycle management. + ~~~ + // Assume the interface has two methods: + // HRESULT OpenFoo([in] handle_t binding, [out] FOO_CONTEXT*); + // HRESULT UseFoo([in] FOO_CONTEXT context; + // void CloseFoo([in, out] PFOO_CONTEXT); + using unique_foo_context = wil::unique_rpc_context_handle; + unique_foo_context context; + RETURN_IF_FAILED(wil::invoke_rpc_nothrow(OpenFoo, m_binding.get(), context.put())); + RETURN_IF_FAILED(wil::invoke_rpc_nothrow(UseFoo, context.get())); + context.reset(); + ~~~ + */ + template + using unique_rpc_context_handle = unique_any::Close), details::rpc_closer_t::Close>; + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Invokes an RPC method, mapping structured exceptions to C++ exceptions + See `wil::invoke_rpc_nothrow` for additional information. Failures during the _call_ + and those returned by the _method_ are mapped to HRESULTs and thrown inside a + wil::ResultException. Using the example RPC method provided above: + ~~~ + wil::unique_midl_ptr state; + wil::invoke_rpc(GetKittenState, binding.get(), L"fluffy", state.put()); + // use 'state' + ~~~ + */ + template void invoke_rpc(TCall&& ... args) + { + THROW_IF_FAILED(invoke_rpc_nothrow(wistd::forward(args)...)); + } + + /** Invokes an RPC method, mapping structured exceptions to C++ exceptions + See `wil::invoke_rpc_result_nothrow` for additional information. Failures during the + _call_ are mapped to HRESULTs and thrown inside a `wil::ResultException`. Using the + example RPC method provided above: + ~~~ + GUID id = wil::invoke_rpc_result(GetKittenId, binding.get()); + // use 'id' + ~~~ + */ + template auto invoke_rpc_result(TCall&& ... args) + { + using result_t = typename wistd::__invoke_of::type; + result_t result{}; + THROW_IF_FAILED(invoke_rpc_result_nothrow(result, wistd::forward(args)...)); + return result; + } +#endif +} + +#endif diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/safecast.h b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/safecast.h new file mode 100644 index 0000000..2fb1aef --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/safecast.h @@ -0,0 +1,369 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_SAFECAST_INCLUDED +#define __WIL_SAFECAST_INCLUDED + +#include "result_macros.h" +#include +#include "wistd_config.h" +#include "wistd_type_traits.h" + +namespace wil +{ + namespace details + { + // Default error case for undefined conversions in intsafe.h + template constexpr wistd::nullptr_t intsafe_conversion = nullptr; + + // is_known_safe_static_cast_v determines if a conversion is known to be safe or not. Known + // safe conversions can be handled by static_cast, this includes conversions between the same + // type, when the new type is larger than the old type but is not a signed to unsigned + // conversion, and when the two types are the same size and signed/unsigned. All other + // conversions will be assumed to be potentially unsafe, and the conversion must be handled + // by intsafe and checked. + template + constexpr bool is_known_safe_static_cast_v = + (sizeof(NewT) > sizeof(OldT) && !(wistd::is_signed_v && wistd::is_unsigned_v)) || + (sizeof(NewT) == sizeof(OldT) && ((wistd::is_signed_v && wistd::is_signed_v) || (wistd::is_unsigned_v && wistd::is_unsigned_v))); + + // Helper template to determine that NewT and OldT are both integral types. The safe_cast + // operation only supports conversions between integral types. + template + constexpr bool both_integral_v = wistd::is_integral::value && wistd::is_integral::value; + + // Note on native wchar_t (__wchar_t): + // Intsafe.h does not currently handle native wchar_t. When compiling with /Zc:wchar_t-, this is fine as wchar_t is + // typedef'd to unsigned short. However, when compiling with /Zc:wchar_t or wchar_t as a native type, the lack of + // support for native wchar_t in intsafe.h becomes an issue. To work around this, we treat native wchar_t as an + // unsigned short when passing it to intsafe.h, because the two on the Windows platform are the same size and + // share the same range according to MSDN. If the cast is to a native wchar_t, the result from intsafe.h is cast + // to a native wchar_t. + + // Intsafe does not have a defined conversion for native wchar_t + template + constexpr bool neither_native_wchar_v = !wistd::is_same::value && !wistd::is_same::value; + + // Check to see if the cast is a conversion to native wchar_t + template + constexpr bool is_cast_to_wchar_v = wistd::is_same::value && !wistd::is_same::value; + + // Check to see if the cast is a conversion from native wchar_t + template + constexpr bool is_cast_from_wchar_v = !wistd::is_same::value && wistd::is_same::value; + + // Validate the conversion to be performed has a defined mapping to an intsafe conversion + template + constexpr bool is_supported_intsafe_cast_v = intsafe_conversion != nullptr; + + // True when the conversion is between integral types and can be handled by static_cast + template + constexpr bool is_supported_safe_static_cast_v = both_integral_v && is_known_safe_static_cast_v; + + // True when the conversion is between integral types, does not involve native wchar, has + // a mapped intsafe conversion, and is unsafe. + template + constexpr bool is_supported_unsafe_cast_no_wchar_v = + both_integral_v && + !is_known_safe_static_cast_v && + neither_native_wchar_v && + is_supported_intsafe_cast_v; + + // True when the conversion is between integral types, is a cast to native wchar_t, has + // a mapped intsafe conversion, and is unsafe. + template + constexpr bool is_supported_unsafe_cast_to_wchar_v = + both_integral_v && + !is_known_safe_static_cast_v && + is_cast_to_wchar_v && + is_supported_intsafe_cast_v; + + // True when the conversion is between integral types, is a cast from native wchar_t, has + // a mapped intsafe conversion, and is unsafe. + template + constexpr bool is_supported_unsafe_cast_from_wchar_v = + both_integral_v && + !is_known_safe_static_cast_v && + is_cast_from_wchar_v && + is_supported_intsafe_cast_v; + + // True when the conversion is supported and unsafe, and may or may not involve + // native wchar_t. + template + constexpr bool is_supported_unsafe_cast_v = + is_supported_unsafe_cast_no_wchar_v || + is_supported_unsafe_cast_to_wchar_v || + is_supported_unsafe_cast_from_wchar_v; + + // True when T is any one of the primitive types that the variably sized types are defined as. + template + constexpr bool is_potentially_variably_sized_type_v = + wistd::is_same::value || + wistd::is_same::value || + wistd::is_same::value || + wistd::is_same::value || + wistd::is_same::value || + wistd::is_same::value; + + // True when either type is potentialy variably sized (e.g. size_t, ptrdiff_t) + template + constexpr bool is_potentially_variably_sized_cast_v = + is_potentially_variably_sized_type_v || + is_potentially_variably_sized_type_v; + + // Mappings of all conversions defined in intsafe.h to intsafe_conversion + // Note: Uppercase types (UINT, DWORD, SIZE_T, etc) and architecture dependent types resolve + // to the base types. The base types are used since they do not vary based on architecture. + template<> constexpr auto intsafe_conversion<__int64, char> = LongLongToChar; + template<> constexpr auto intsafe_conversion<__int64, int> = LongLongToInt; + template<> constexpr auto intsafe_conversion<__int64, long> = LongLongToLong; + template<> constexpr auto intsafe_conversion<__int64, short> = LongLongToShort; + template<> constexpr auto intsafe_conversion<__int64, signed char> = LongLongToInt8; + template<> constexpr auto intsafe_conversion<__int64, unsigned __int64> = LongLongToULongLong; + template<> constexpr auto intsafe_conversion<__int64, unsigned char> = LongLongToUChar; + template<> constexpr auto intsafe_conversion<__int64, unsigned int> = LongLongToUInt; + template<> constexpr auto intsafe_conversion<__int64, unsigned long> = LongLongToULong; + template<> constexpr auto intsafe_conversion<__int64, unsigned short> = LongLongToUShort; + template<> constexpr auto intsafe_conversion = IntToChar; + template<> constexpr auto intsafe_conversion = IntToShort; + template<> constexpr auto intsafe_conversion = IntToInt8; + template<> constexpr auto intsafe_conversion = IntToULongLong; + template<> constexpr auto intsafe_conversion = IntToUChar; + template<> constexpr auto intsafe_conversion = IntToUInt; + template<> constexpr auto intsafe_conversion = IntToULong; + template<> constexpr auto intsafe_conversion = IntToUShort; + template<> constexpr auto intsafe_conversion = LongToChar; + template<> constexpr auto intsafe_conversion = LongToInt; + template<> constexpr auto intsafe_conversion = LongToShort; + template<> constexpr auto intsafe_conversion = LongToInt8; + template<> constexpr auto intsafe_conversion = LongToULongLong; + template<> constexpr auto intsafe_conversion = LongToUChar; + template<> constexpr auto intsafe_conversion = LongToUInt; + template<> constexpr auto intsafe_conversion = LongToULong; + template<> constexpr auto intsafe_conversion = LongToUShort; + template<> constexpr auto intsafe_conversion = ShortToChar; + template<> constexpr auto intsafe_conversion = ShortToInt8; + template<> constexpr auto intsafe_conversion = ShortToULongLong; + template<> constexpr auto intsafe_conversion = ShortToUChar; + template<> constexpr auto intsafe_conversion = ShortToUInt; + template<> constexpr auto intsafe_conversion = ShortToULong; + template<> constexpr auto intsafe_conversion = ShortToUShort; + template<> constexpr auto intsafe_conversion = Int8ToULongLong; + template<> constexpr auto intsafe_conversion = Int8ToUChar; + template<> constexpr auto intsafe_conversion = Int8ToUInt; + template<> constexpr auto intsafe_conversion = Int8ToULong; + template<> constexpr auto intsafe_conversion = Int8ToUShort; + template<> constexpr auto intsafe_conversion = ULongLongToLongLong; + template<> constexpr auto intsafe_conversion = ULongLongToChar; + template<> constexpr auto intsafe_conversion = ULongLongToInt; + template<> constexpr auto intsafe_conversion = ULongLongToLong; + template<> constexpr auto intsafe_conversion = ULongLongToShort; + template<> constexpr auto intsafe_conversion = ULongLongToInt8; + template<> constexpr auto intsafe_conversion = ULongLongToUChar; + template<> constexpr auto intsafe_conversion = ULongLongToUInt; + template<> constexpr auto intsafe_conversion = ULongLongToULong; + template<> constexpr auto intsafe_conversion = ULongLongToUShort; + template<> constexpr auto intsafe_conversion = UInt8ToChar; + template<> constexpr auto intsafe_conversion = UIntToInt8; + template<> constexpr auto intsafe_conversion = UIntToChar; + template<> constexpr auto intsafe_conversion = UIntToInt; + template<> constexpr auto intsafe_conversion = UIntToLong; + template<> constexpr auto intsafe_conversion = UIntToShort; + template<> constexpr auto intsafe_conversion = UIntToInt8; + template<> constexpr auto intsafe_conversion = UIntToUChar; + template<> constexpr auto intsafe_conversion = UIntToUShort; + template<> constexpr auto intsafe_conversion = ULongToChar; + template<> constexpr auto intsafe_conversion = ULongToInt; + template<> constexpr auto intsafe_conversion = ULongToLong; + template<> constexpr auto intsafe_conversion = ULongToShort; + template<> constexpr auto intsafe_conversion = ULongToInt8; + template<> constexpr auto intsafe_conversion = ULongToUChar; + template<> constexpr auto intsafe_conversion = ULongToUInt; + template<> constexpr auto intsafe_conversion = ULongToUShort; + template<> constexpr auto intsafe_conversion = UShortToChar; + template<> constexpr auto intsafe_conversion = UShortToShort; + template<> constexpr auto intsafe_conversion = UShortToInt8; + template<> constexpr auto intsafe_conversion = UShortToUChar; + } + + // Unsafe conversion where failure results in fail fast. + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + NewT safe_cast_failfast(const OldT var) + { + NewT newVar; + FAIL_FAST_IF_FAILED((details::intsafe_conversion(var, &newVar))); + return newVar; + } + + // Unsafe conversion where failure results in fail fast. + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + NewT safe_cast_failfast(const OldT var) + { + NewT newVar; + FAIL_FAST_IF_FAILED((details::intsafe_conversion(static_cast(var), &newVar))); + return newVar; + } + + // Unsafe conversion where failure results in fail fast. + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + NewT safe_cast_failfast(const OldT var) + { + unsigned short newVar; + FAIL_FAST_IF_FAILED((details::intsafe_conversion(var, &newVar))); + return static_cast<__wchar_t>(newVar); + } + + // This conversion is always safe, therefore a static_cast is fine. + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + NewT safe_cast_failfast(const OldT var) + { + return static_cast(var); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + // Unsafe conversion where failure results in a thrown exception. + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + NewT safe_cast(const OldT var) + { + NewT newVar; + THROW_IF_FAILED((details::intsafe_conversion(var, &newVar))); + return newVar; + } + + // Unsafe conversion where failure results in a thrown exception. + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + NewT safe_cast(const OldT var) + { + NewT newVar; + THROW_IF_FAILED((details::intsafe_conversion(static_cast(var), &newVar))); + return newVar; + } + + // Unsafe conversion where failure results in a thrown exception. + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + NewT safe_cast(const OldT var) + { + unsigned short newVar; + THROW_IF_FAILED((details::intsafe_conversion(var, &newVar))); + return static_cast<__wchar_t>(newVar); + } + + // This conversion is always safe, therefore a static_cast is fine. + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + NewT safe_cast(const OldT var) + { + return static_cast(var); + } +#endif + + // This conversion is unsafe, therefore the two parameter version of safe_cast_nothrow must be used + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + NewT safe_cast_nothrow(const OldT /*var*/) + { + static_assert(!wistd::is_same_v, "This cast has the potential to fail, use the two parameter safe_cast_nothrow instead"); + } + + // This conversion is always safe, therefore a static_cast is fine. + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + NewT safe_cast_nothrow(const OldT var) + { + return static_cast(var); + } + + // Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult) + { + return details::intsafe_conversion(var, newTResult); + } + + // Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult) + { + return details::intsafe_conversion(static_cast(var), newTResult); + } + + // Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult) + { + return details::intsafe_conversion(var, reinterpret_cast(newTResult)); + } + + // This conversion is always safe, therefore a static_cast is fine. If it can be determined the conversion + // does not involve a variably sized type, then the compilation will fail and say the single parameter version + // of safe_cast_nothrow should be used instead. + template < + typename NewT, + typename OldT, + wistd::enable_if_t, int> = 0 + > + HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult) + { + static_assert(details::is_potentially_variably_sized_cast_v, "This cast is always safe; use safe_cast_nothrow(value) to avoid unnecessary error handling."); + *newTResult = static_cast(var); + return S_OK; + } +} + +#endif // __WIL_SAFECAST_INCLUDED diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/stl.h b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/stl.h new file mode 100644 index 0000000..f34deab --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/stl.h @@ -0,0 +1,116 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_STL_INCLUDED +#define __WIL_STL_INCLUDED + +#include "common.h" +#include "resource.h" +#include +#include +#include + +#if defined(WIL_ENABLE_EXCEPTIONS) + +namespace wil +{ + /** Secure allocator for STL containers. + The `wil::secure_allocator` allocator calls `SecureZeroMemory` before deallocating + memory. This provides a mechanism for secure STL containers such as `wil::secure_vector`, + `wil::secure_string`, and `wil::secure_wstring`. */ + template + struct secure_allocator + : public std::allocator + { + template + struct rebind + { + typedef secure_allocator other; + }; + + secure_allocator() + : std::allocator() + { + } + + ~secure_allocator() = default; + + secure_allocator(const secure_allocator& a) + : std::allocator(a) + { + } + + template + secure_allocator(const secure_allocator& a) + : std::allocator(a) + { + } + + T* allocate(size_t n) + { + return std::allocator::allocate(n); + } + + void deallocate(T* p, size_t n) + { + SecureZeroMemory(p, sizeof(T) * n); + std::allocator::deallocate(p, n); + } + }; + + //! `wil::secure_vector` will be securely zeroed before deallocation. + template + using secure_vector = std::vector>; + //! `wil::secure_wstring` will be securely zeroed before deallocation. + using secure_wstring = std::basic_string, wil::secure_allocator>; + //! `wil::secure_string` will be securely zeroed before deallocation. + using secure_string = std::basic_string, wil::secure_allocator>; + + /// @cond + namespace details + { + template<> struct string_maker + { + HRESULT make(_In_reads_opt_(length) PCWSTR source, size_t length) WI_NOEXCEPT try + { + m_value = source ? std::wstring(source, length) : std::wstring(length, L'\0'); + return S_OK; + } + catch (...) + { + return E_OUTOFMEMORY; + } + + wchar_t* buffer() { return &m_value[0]; } + + HRESULT trim_at_existing_null(size_t length) { m_value.erase(length); return S_OK; } + + std::wstring release() { return std::wstring(std::move(m_value)); } + + static PCWSTR get(const std::wstring& value) { return value.c_str(); } + + private: + std::wstring m_value; + }; + } + /// @endcond + + // str_raw_ptr is an overloaded function that retrieves a const pointer to the first character in a string's buffer. + // This is the overload for std::wstring. Other overloads available in resource.h. + inline PCWSTR str_raw_ptr(const std::wstring& str) + { + return str.c_str(); + } + +} // namespace wil + +#endif // WIL_ENABLE_EXCEPTIONS + +#endif // __WIL_STL_INCLUDED diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/token_helpers.h b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/token_helpers.h new file mode 100644 index 0000000..08ca28e --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/token_helpers.h @@ -0,0 +1,601 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_TOKEN_HELPERS_INCLUDED +#define __WIL_TOKEN_HELPERS_INCLUDED + +#ifdef _KERNEL_MODE +#error This header is not supported in kernel-mode. +#endif + +#include "resource.h" +#include +#include // for UNLEN and DNLEN +#include + +// for GetUserNameEx() +#define SECURITY_WIN32 +#include + +namespace wil +{ + /// @cond + namespace details + { + // Template specialization for TOKEN_INFORMATION_CLASS, add more mappings here as needed + // TODO: The mapping should be reversed to be MapTokenInfoClassToStruct since there may + // be an info class value that uses the same structure. That is the case for the file + // system information. + template struct MapTokenStructToInfoClass; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenAccessInformation; static const bool FixedSize = false; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenAppContainerSid; static const bool FixedSize = false; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenDefaultDacl; static const bool FixedSize = false; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenGroupsAndPrivileges; static const bool FixedSize = false; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenIntegrityLevel; static const bool FixedSize = false; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenOwner; static const bool FixedSize = false; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenPrimaryGroup; static const bool FixedSize = false; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenPrivileges; static const bool FixedSize = false; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenUser; static const bool FixedSize = false; }; + + // fixed size cases + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenElevationType; static const bool FixedSize = true; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenMandatoryPolicy; static const bool FixedSize = true; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenOrigin; static const bool FixedSize = true; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenSource; static const bool FixedSize = true; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenStatistics; static const bool FixedSize = true; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenType; static const bool FixedSize = true; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenImpersonationLevel; static const bool FixedSize = true; }; + template<> struct MapTokenStructToInfoClass { static const TOKEN_INFORMATION_CLASS infoClass = TokenElevation; static const bool FixedSize = true; }; + } + /// @endcond + + enum class OpenThreadTokenAs + { + Current, + Self + }; + + /** Open the active token. + Opens either the current thread token (if impersonating) or the current process token. Returns a token the caller + can use with methods like get_token_information<> below. By default, the token is opened for TOKEN_QUERY and as the + effective user. + + Consider using GetCurrentThreadEffectiveToken() instead of this method when eventually calling get_token_information. + This method returns a real handle to the effective token, but GetCurrentThreadEffectiveToken() is a Pseudo-handle + and much easier to manage. + ~~~~ + wil::unique_handle theToken; + RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken)); + ~~~~ + Callers who want more access to the token (such as to duplicate or modify the token) can pass + any mask of the token rights. + ~~~~ + wil::unique_handle theToken; + RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken, TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES)); + ~~~~ + Services impersonating their clients may need to request that the active token is opened on the + behalf of the service process to perform certain operations. Opening a token for impersonation access + or privilege-adjustment are examples of uses. + ~~~~ + wil::unique_handle callerToken; + RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken, TOKEN_QUERY | TOKEN_IMPERSONATE, true)); + ~~~~ + @param tokenHandle Receives the token opened during the operation. Must be CloseHandle'd by the caller, or + (preferably) stored in a wil::unique_handle + @param access Bits from the TOKEN_* access mask which are passed to OpenThreadToken/OpenProcessToken + @param asSelf When true, and if the thread is impersonating, the thread token is opened using the + process token's rights. + */ + inline HRESULT open_current_access_token_nothrow(_Out_ HANDLE* tokenHandle, unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current) + { + HRESULT hr = (OpenThreadToken(GetCurrentThread(), access, (openAs == OpenThreadTokenAs::Self), tokenHandle) ? S_OK : HRESULT_FROM_WIN32(::GetLastError())); + if (hr == HRESULT_FROM_WIN32(ERROR_NO_TOKEN)) + { + hr = (OpenProcessToken(GetCurrentProcess(), access, tokenHandle) ? S_OK : HRESULT_FROM_WIN32(::GetLastError())); + } + return hr; + } + + //! Current thread or process token, consider using GetCurrentThreadEffectiveToken() instead. + inline wil::unique_handle open_current_access_token_failfast(unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current) + { + HANDLE rawTokenHandle; + FAIL_FAST_IF_FAILED(open_current_access_token_nothrow(&rawTokenHandle, access, openAs)); + return wil::unique_handle(rawTokenHandle); + } + +// Exception based function to open current thread/process access token and acquire pointer to it +#ifdef WIL_ENABLE_EXCEPTIONS + //! Current thread or process token, consider using GetCurrentThreadEffectiveToken() instead. + inline wil::unique_handle open_current_access_token(unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current) + { + HANDLE rawTokenHandle; + THROW_IF_FAILED(open_current_access_token_nothrow(&rawTokenHandle, access, openAs)); + return wil::unique_handle(rawTokenHandle); + } +#endif // WIL_ENABLE_EXCEPTIONS + +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + // Returns tokenHandle or the effective thread token if tokenHandle is null. + // Note, this returns an token handle who's lifetime is managed independently + // and it may be a pseudo token, don't free it! + inline HANDLE GetCurrentThreadEffectiveTokenWithOverride(HANDLE tokenHandle) + { + return tokenHandle ? tokenHandle : GetCurrentThreadEffectiveToken(); + } + + /** Fetches information about a token. + See GetTokenInformation on MSDN for what this method can return. For variable sized structs the information + is returned to the caller as a wistd::unique_ptr (like TOKEN_ORIGIN, TOKEN_USER, TOKEN_ELEVATION, etc.). For + fixed sized, the struct is returned directly. + The caller must have access to read the information from the provided token. This method works with both real + (e.g. OpenCurrentAccessToken) and pseudo (e.g. GetCurrentThreadToken) token handles. + ~~~~ + // Retrieve the TOKEN_USER structure for the current process + wistd::unique_ptr user; + RETURN_IF_FAILED(wil::get_token_information_nothrow(user, GetCurrentProcessToken())); + RETURN_IF_FAILED(ConsumeSid(user->User.Sid)); + ~~~~ + Not specifying the token handle is the same as specifying 'nullptr' and retrieves information about the effective token. + ~~~~ + wistd::unique_ptr privileges; + RETURN_IF_FAILED(wil::get_token_information_nothrow(privileges)); + for (auto const& privilege : wil::GetRange(privileges->Privileges, privileges->PrivilegeCount)) + { + RETURN_IF_FAILED(ConsumePrivilege(privilege)); + } + ~~~~ + @param tokenInfo Receives a pointer to a structure containing the results of GetTokenInformation for the requested + type. The type of selects which TOKEN_INFORMATION_CLASS will be used. + @param tokenHandle Specifies which token will be queried. When nullptr, the thread's effective current token is used. + @return S_OK on success, a FAILED hresult containing the win32 error from querying the token otherwise. + */ + + template ::FixedSize>* = nullptr> + inline HRESULT get_token_information_nothrow(wistd::unique_ptr& tokenInfo, HANDLE tokenHandle = nullptr) + { + tokenInfo.reset(); + tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle); + + DWORD tokenInfoSize = 0; + const auto infoClass = details::MapTokenStructToInfoClass::infoClass; + RETURN_LAST_ERROR_IF(!((!GetTokenInformation(tokenHandle, infoClass, nullptr, 0, &tokenInfoSize)) && + (::GetLastError() == ERROR_INSUFFICIENT_BUFFER))); + wistd::unique_ptr tokenInfoClose( + static_cast(operator new(tokenInfoSize, std::nothrow))); + RETURN_IF_NULL_ALLOC(tokenInfoClose.get()); + RETURN_IF_WIN32_BOOL_FALSE(GetTokenInformation(tokenHandle, infoClass, tokenInfoClose.get(), tokenInfoSize, &tokenInfoSize)); + tokenInfo.reset(reinterpret_cast(tokenInfoClose.release())); + + return S_OK; + } + + template ::FixedSize>* = nullptr> + inline HRESULT get_token_information_nothrow(_Out_ T* tokenInfo, HANDLE tokenHandle = nullptr) + { + *tokenInfo = {}; + tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle); + + DWORD tokenInfoSize = sizeof(T); + const auto infoClass = details::MapTokenStructToInfoClass::infoClass; + RETURN_IF_WIN32_BOOL_FALSE(GetTokenInformation(tokenHandle, infoClass, tokenInfo, tokenInfoSize, &tokenInfoSize)); + + return S_OK; + } + + namespace details + { + template::FixedSize>* = nullptr> + wistd::unique_ptr GetTokenInfoWrap(HANDLE token = nullptr) + { + wistd::unique_ptr temp; + policy::HResult(get_token_information_nothrow(temp, token)); + return temp; + } + + template::FixedSize>* = nullptr> + T GetTokenInfoWrap(HANDLE token = nullptr) + { + T temp{}; + policy::HResult(get_token_information_nothrow(&temp, token)); + return temp; + } + } + + //! A variant of get_token_information that fails-fast on errors retrieving the token + template + inline auto get_token_information_failfast(HANDLE token = nullptr) + { + return details::GetTokenInfoWrap(token); + } + + //! Overload of GetTokenInformationNoThrow that retrieves a token linked from the provided token + inline HRESULT get_token_information_nothrow(unique_token_linked_token& tokenInfo, HANDLE tokenHandle = nullptr) + { + static_assert(sizeof(tokenInfo) == sizeof(TOKEN_LINKED_TOKEN), "confusing size mismatch"); + tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle); + + DWORD tokenInfoSize = 0; + RETURN_IF_WIN32_BOOL_FALSE(::GetTokenInformation(tokenHandle, TokenLinkedToken, + tokenInfo.reset_and_addressof(), sizeof(tokenInfo), &tokenInfoSize)); + return S_OK; + } + + /** Retrieves the linked-token information for a token. + Fails-fast if the link information cannot be retrieved. + ~~~~ + auto link = get_linked_token_information_failfast(GetCurrentThreadToken()); + auto tokenUser = get_token_information(link.LinkedToken); + ~~~~ + @param token Specifies the token to query. Pass nullptr to use the current effective thread token + @return unique_token_linked_token containing a handle to the linked token + */ + inline unique_token_linked_token get_linked_token_information_failfast(HANDLE token = nullptr) + { + unique_token_linked_token tokenInfo; + FAIL_FAST_IF_FAILED(get_token_information_nothrow(tokenInfo, token)); + return tokenInfo; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Fetches information about a token. + See get_token_information_nothrow for full details. + ~~~~ + auto user = wil::get_token_information(GetCurrentProcessToken()); + ConsumeSid(user->User.Sid); + ~~~~ + Pass 'nullptr' (or omit the parameter) as tokenHandle to retrieve information about the effective token. + ~~~~ + auto privs = wil::get_token_information(privileges); + for (auto& priv : wil::make_range(privs->Privileges, privs->Privilieges + privs->PrivilegeCount)) + { + if (priv.Attributes & SE_PRIVILEGE_ENABLED) + { + // ... + } + } + ~~~~ + @return A pointer to a structure containing the results of GetTokenInformation for the requested type. The type of + selects which TOKEN_INFORMATION_CLASS will be used. + @param token Specifies which token will be queried. When nullptr or not set, the thread's effective current token is used. + */ + template + inline auto get_token_information(HANDLE token = nullptr) + { + return details::GetTokenInfoWrap(token); + } + + /** Retrieves the linked-token information for a token. + Throws an exception if the link information cannot be retrieved. + ~~~~ + auto link = get_linked_token_information(GetCurrentThreadToken()); + auto tokenUser = get_token_information(link.LinkedToken); + ~~~~ + @param token Specifies the token to query. Pass nullptr to use the current effective thread token + @return unique_token_linked_token containing a handle to the linked token + */ + inline unique_token_linked_token get_linked_token_information(HANDLE token = nullptr) + { + unique_token_linked_token tokenInfo; + THROW_IF_FAILED(get_token_information_nothrow(tokenInfo, token)); + return tokenInfo; + } +#endif +#endif // _WIN32_WINNT >= _WIN32_WINNT_WIN8 + + /// @cond + namespace details + { + inline void RevertImpersonateToken(_In_ _Post_ptr_invalid_ HANDLE oldToken) + { + FAIL_FAST_IMMEDIATE_IF(!::SetThreadToken(nullptr, oldToken)); + + if (oldToken) + { + ::CloseHandle(oldToken); + } + } + } + /// @endcond + + using unique_token_reverter = wil::unique_any< + HANDLE, + decltype(&details::RevertImpersonateToken), + details::RevertImpersonateToken, + details::pointer_access_none, + HANDLE, + INT_PTR, + -1, + HANDLE>; + + /** Temporarily impersonates a token on this thread. + This method sets a new token on a thread, restoring the current token when the returned object + is destroyed. Useful for impersonating other tokens or running as 'self,' especially in services. + ~~~~ + HRESULT OpenFileAsSessionuser(PCWSTR filePath, DWORD session, _Out_ HANDLE* opened) + { + wil::unique_handle userToken; + RETURN_IF_WIN32_BOOL_FALSE(QueryUserToken(session, &userToken)); + + wil::unique_token_reverter reverter; + RETURN_IF_FAILED(wil::impersonate_token_nothrow(userToken.get(), reverter)); + + wil::unique_hfile userFile(::CreateFile(filePath, ...)); + RETURN_LAST_ERROR_IF(!userFile && (::GetLastError() != ERROR_FILE_NOT_FOUND)); + + *opened = userFile.release(); + return S_OK; + } + ~~~~ + @param token A token to impersonate, or 'nullptr' to run as the process identity. + */ + inline HRESULT impersonate_token_nothrow(HANDLE token, unique_token_reverter& reverter) + { + wil::unique_handle currentToken; + + // Get the token for the current thread. If there wasn't one, the reset will clear it as well + if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, ¤tToken)) + { + RETURN_LAST_ERROR_IF(::GetLastError() != ERROR_NO_TOKEN); + } + + // Update the current token + RETURN_IF_WIN32_BOOL_FALSE(::SetThreadToken(nullptr, token)); + + reverter.reset(currentToken.release()); // Ownership passed + return S_OK; + } + + /** Temporarily clears any impersonation on this thread. + This method resets the current thread's token to nullptr, indicating that it is not impersonating + any user. Useful for elevating to whatever identity a service or higher-privilege process might + be capable of running under. + ~~~~ + HRESULT DeleteFileRetryAsSelf(PCWSTR filePath) + { + if (!::DeleteFile(filePath)) + { + RETURN_LAST_ERROR_IF(::GetLastError() != ERROR_ACCESS_DENIED); + wil::unique_token_reverter reverter; + RETURN_IF_FAILED(wil::run_as_self_nothrow(reverter)); + RETURN_IF_FAILED(TakeOwnershipOfFile(filePath)); + RETURN_IF_FAILED(GrantDeleteAccess(filePath)); + RETURN_IF_WIN32_BOOL_FALSE(::DeleteFile(filePath)); + } + return S_OK; + } + ~~~~ + */ + inline HRESULT run_as_self_nothrow(unique_token_reverter& reverter) + { + return impersonate_token_nothrow(nullptr, reverter); + } + + inline unique_token_reverter impersonate_token_failfast(HANDLE token) + { + unique_token_reverter oldToken; + FAIL_FAST_IF_FAILED(impersonate_token_nothrow(token, oldToken)); + return oldToken; + } + + inline unique_token_reverter run_as_self_failfast() + { + return impersonate_token_failfast(nullptr); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Temporarily impersonates a token on this thread. + This method sets a new token on a thread, restoring the current token when the returned object + is destroyed. Useful for impersonating other tokens or running as 'self,' especially in services. + ~~~~ + wil::unique_hfile OpenFileAsSessionuser(_In_z_ const wchar_t* filePath, DWORD session) + { + wil::unique_handle userToken; + THROW_IF_WIN32_BOOL_FALSE(QueryUserToken(session, &userToken)); + + auto priorToken = wil::impersonate_token(userToken.get()); + + wil::unique_hfile userFile(::CreateFile(filePath, ...)); + THROW_LAST_ERROR_IF(::GetLastError() != ERROR_FILE_NOT_FOUND); + + return userFile; + } + ~~~~ + @param token A token to impersonate, or 'nullptr' to run as the process identity. + */ + inline unique_token_reverter impersonate_token(HANDLE token = nullptr) + { + unique_token_reverter oldToken; + THROW_IF_FAILED(impersonate_token_nothrow(token, oldToken)); + return oldToken; + } + + /** Temporarily clears any impersonation on this thread. + This method resets the current thread's token to nullptr, indicating that it is not impersonating + any user. Useful for elevating to whatever identity a service or higher-privilege process might + be capable of running under. + ~~~~ + void DeleteFileRetryAsSelf(_In_z_ const wchar_t* filePath) + { + if (!::DeleteFile(filePath) && (::GetLastError() == ERROR_ACCESS_DENIED)) + { + auto priorToken = wil::run_as_self(); + TakeOwnershipOfFile(filePath); + GrantDeleteAccess(filePath); + ::DeleteFile(filePath); + } + } + ~~~~ + */ + inline unique_token_reverter run_as_self() + { + return impersonate_token(nullptr); + } +#endif // WIL_ENABLE_EXCEPTIONS + + namespace details + { + template struct static_sid_t + { + BYTE Revision; + BYTE SubAuthorityCount; + SID_IDENTIFIER_AUTHORITY IdentifierAuthority; + DWORD SubAuthority[AuthorityCount]; + + PSID get() + { + return reinterpret_cast(this); + } + + template static_sid_t& operator=(const static_sid_t& source) + { + static_assert(other <= AuthorityCount, "Cannot assign from a larger static sid to a smaller one"); + + if (&this->Revision != &source.Revision) + { + memcpy(this, &source, sizeof(source)); + } + + return *this; + } + }; + } + + /** Returns a structure containing a Revision 1 SID initialized with the authorities provided + Replaces AllocateAndInitializeSid by constructing a structure laid out like a PSID, but + returned like a value. The resulting object is suitable for use with any method taking PSID, + passed by "&the_sid" or via "the_sid.get()" + ~~~~ + // Change the owner of the key to administrators + auto systemSid = wil::make_static_sid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS); + RETURN_IF_WIN32_ERROR(SetNamedSecurityInfo(keyPath, SE_REGISTRY_KEY, OWNER_SECURITY_INFORMATION, &systemSid, nullptr, nullptr, nullptr)); + ~~~~ + */ + template constexpr auto make_static_sid(const SID_IDENTIFIER_AUTHORITY& authority, Ts&&... subAuthorities) + { + using sid_t = details::static_sid_t; + + static_assert(sizeof...(subAuthorities) <= SID_MAX_SUB_AUTHORITIES, "too many sub authorities"); + static_assert(offsetof(sid_t, Revision) == offsetof(_SID, Revision), "layout mismatch"); + static_assert(offsetof(sid_t, SubAuthorityCount) == offsetof(_SID, SubAuthorityCount), "layout mismatch"); + static_assert(offsetof(sid_t, IdentifierAuthority) == offsetof(_SID, IdentifierAuthority), "layout mismatch"); + static_assert(offsetof(sid_t, SubAuthority) == offsetof(_SID, SubAuthority), "layout mismatch"); + + return sid_t { SID_REVISION, sizeof...(subAuthorities), authority, { static_cast(subAuthorities)... } }; + } + + //! Variant of static_sid that defaults to the NT authority + template constexpr auto make_static_nt_sid(Ts&& ... subAuthorities) + { + return make_static_sid(SECURITY_NT_AUTHORITY, wistd::forward(subAuthorities)...); + } + + /** Determines whether a specified security identifier (SID) is enabled in an access token. + This function determines whether a security identifier, described by a given set of subauthorities, is enabled + in the given access token. Note that only up to eight subauthorities can be passed to this function. + ~~~~ + bool IsGuest() + { + return wil::test_token_membership(nullptr, SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS)); + } + ~~~~ + @param result This will be set to true if and only if a security identifier described by the given set of subauthorities is enabled in the given access token. + @param token A handle to an access token. The handle must have TOKEN_QUERY access to the token, and must be an impersonation token. If token is nullptr, test_token_membership + uses the impersonation token of the calling thread. If the thread is not impersonating, the function duplicates the thread's primary token to create an impersonation token. + @param sidAuthority A reference to a SID_IDENTIFIER_AUTHORITY structure. This structure provides the top-level identifier authority value to set in the SID. + @param subAuthorities Up to 15 subauthority values to place in the SID (this is a systemwide limit) + @return S_OK on success, a FAILED hresult containing the win32 error from creating the SID or querying the token otherwise. + */ + template HRESULT test_token_membership_nothrow(_Out_ bool* result, _In_opt_ HANDLE token, + const SID_IDENTIFIER_AUTHORITY& sidAuthority, Ts&&... subAuthorities) + { + *result = false; + auto tempSid = make_static_sid(sidAuthority, wistd::forward(subAuthorities)...); + BOOL isMember; + RETURN_IF_WIN32_BOOL_FALSE(CheckTokenMembership(token, &tempSid, &isMember)); + + *result = (isMember != FALSE); + + return S_OK; + } + +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + /** Determine whether a token represents an app container + This method uses the passed in token and emits a boolean indicating that + whether TokenIsAppContainer is true. + ~~~~ + HRESULT OnlyIfAppContainer() + { + bool isAppContainer; + RETURN_IF_FAILED(wil::get_token_is_app_container_nothrow(nullptr, isAppContainer)); + RETURN_HR_IF(E_ACCESSDENIED, !isAppContainer); + RETURN_HR(...); + } + ~~~~ + @param token A token to get info about, or 'nullptr' to run as the current thread. + */ + inline HRESULT get_token_is_app_container_nothrow(_In_opt_ HANDLE token, bool& value) + { + DWORD isAppContainer = 0; + DWORD returnLength = 0; + RETURN_IF_WIN32_BOOL_FALSE(::GetTokenInformation( + token ? token : GetCurrentThreadEffectiveToken(), + TokenIsAppContainer, + &isAppContainer, + sizeof(isAppContainer), + &returnLength)); + + value = (isAppContainer != 0); + + return S_OK; + } + + //! A variant of get_token_is_app_container_nothrow that fails-fast on errors retrieving the token information + inline bool get_token_is_app_container_failfast(HANDLE token = nullptr) + { + bool value = false; + FAIL_FAST_IF_FAILED(get_token_is_app_container_nothrow(token, value)); + + return value; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + //! A variant of get_token_is_app_container_nothrow that throws on errors retrieving the token information + inline bool get_token_is_app_container(HANDLE token = nullptr) + { + bool value = false; + THROW_IF_FAILED(get_token_is_app_container_nothrow(token, value)); + + return value; + } +#endif // WIL_ENABLE_EXCEPTIONS +#endif // _WIN32_WINNT >= _WIN32_WINNT_WIN8 + + template bool test_token_membership_failfast(_In_opt_ HANDLE token, + const SID_IDENTIFIER_AUTHORITY& sidAuthority, Ts&&... subAuthorities) + { + bool result; + FAIL_FAST_IF_FAILED(test_token_membership_nothrow(&result, token, sidAuthority, wistd::forward(subAuthorities)...)); + return result; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + template bool test_token_membership(_In_opt_ HANDLE token, const SID_IDENTIFIER_AUTHORITY& sidAuthority, + Ts&&... subAuthorities) + { + bool result; + THROW_IF_FAILED(test_token_membership_nothrow(&result, token, sidAuthority, wistd::forward(subAuthorities)...)); + return result; + } +#endif + +} //namespace wil + +#endif // __WIL_TOKEN_HELPERS_INCLUDED diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/win32_helpers.h b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/win32_helpers.h new file mode 100644 index 0000000..1865c42 --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/win32_helpers.h @@ -0,0 +1,606 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_WIN32_HELPERS_INCLUDED +#define __WIL_WIN32_HELPERS_INCLUDED + +#include // FILETIME, HINSTANCE +#include // GetSystemTimeAsFileTime +#include // GetProcAddress +#include // GetModuleFileNameExW (macro), K32GetModuleFileNameExW +#include + +// detect std::bit_cast +#ifdef __has_include +# if __has_include() +# include +# endif +#endif + +#if __cpp_lib_bit_cast >= 201806L +# define __WI_CONSTEXPR_BIT_CAST constexpr +#else +# define __WI_CONSTEXPR_BIT_CAST inline +#endif + +#include "result.h" +#include "resource.h" +#include "wistd_functional.h" +#include "wistd_type_traits.h" + +namespace wil +{ + //! Strictly a function of the file system but this is the value for all known file system, NTFS, FAT. + //! CDFs has a limit of 254. + size_t const max_path_segment_length = 255; + + //! Character length not including the null, MAX_PATH (260) includes the null. + size_t const max_path_length = 259; + + //! 32743 Character length not including the null. This is a system defined limit. + //! The 24 is for the expansion of the roots from "C:" to "\Device\HarddiskVolume4" + //! It will be 25 when there are more than 9 disks. + size_t const max_extended_path_length = 0x7FFF - 24; + + //! For {guid} string form. Includes space for the null terminator. + size_t const guid_string_buffer_length = 39; + + //! For {guid} string form. Not including the null terminator. + size_t const guid_string_length = 38; + +#pragma region FILETIME helpers + // FILETIME duration values. FILETIME is in 100 nanosecond units. + namespace filetime_duration + { + long long const one_millisecond = 10000LL; + long long const one_second = 10000000LL; + long long const one_minute = 10000000LL * 60; // 600000000 or 600000000LL + long long const one_hour = 10000000LL * 60 * 60; // 36000000000 or 36000000000LL + long long const one_day = 10000000LL * 60 * 60 * 24; // 864000000000 or 864000000000LL + }; + + namespace filetime + { + constexpr unsigned long long to_int64(const FILETIME &ft) WI_NOEXCEPT + { +#if __cpp_lib_bit_cast >= 201806L + return std::bit_cast(ft); +#else + // Cannot reinterpret_cast FILETIME* to unsigned long long* + // due to alignment differences. + return (static_cast(ft.dwHighDateTime) << 32) + ft.dwLowDateTime; +#endif + } + + __WI_CONSTEXPR_BIT_CAST FILETIME from_int64(unsigned long long i64) WI_NOEXCEPT + { +#if __cpp_lib_bit_cast >= 201806L + return std::bit_cast(i64); +#else + static_assert(sizeof(i64) == sizeof(FILETIME), "sizes don't match"); + static_assert(__alignof(unsigned long long) >= __alignof(FILETIME), "alignment not compatible with type pun"); + return *reinterpret_cast(&i64); +#endif + } + + __WI_CONSTEXPR_BIT_CAST FILETIME add(_In_ FILETIME const &ft, long long delta100ns) WI_NOEXCEPT + { + return from_int64(to_int64(ft) + delta100ns); + } + + constexpr bool is_empty(const FILETIME &ft) WI_NOEXCEPT + { + return (ft.dwHighDateTime == 0) && (ft.dwLowDateTime == 0); + } + + inline FILETIME get_system_time() WI_NOEXCEPT + { + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + return ft; + } + + /// Convert time as units of 100 nanoseconds to milliseconds. Fractional milliseconds are truncated. + constexpr unsigned long long convert_100ns_to_msec(unsigned long long time100ns) WI_NOEXCEPT + { + return time100ns / filetime_duration::one_millisecond; + } + + /// Convert time as milliseconds to units of 100 nanoseconds. + constexpr unsigned long long convert_msec_to_100ns(unsigned long long timeMsec) WI_NOEXCEPT + { + return timeMsec * filetime_duration::one_millisecond; + } + +#if defined(_APISETREALTIME_) + /// Returns the current unbiased interrupt-time count, in units of 100 nanoseconds. The unbiased interrupt-time count does not include time the system spends in sleep or hibernation. + /// + /// This API avoids prematurely shortcircuiting timing loops due to system sleep/hibernation. + /// + /// This is equivalent to GetTickCount64() except it returns units of 100 nanoseconds instead of milliseconds, and it doesn't include time the system spends in sleep or hibernation. + /// For example + /// + /// start = GetTickCount64(); + /// hibernate(); + /// ...wake from hibernation 30 minutes later...; + /// elapsed = GetTickCount64() - start; + /// // elapsed = 30min + /// + /// Do the same using unbiased interrupt-time and elapsed is 0 (or nearly so). + /// + /// @note This is identical to QueryUnbiasedInterruptTime() but returns the value as a return value (rather than an out parameter). + /// @see https://msdn.microsoft.com/en-us/library/windows/desktop/ee662307(v=vs.85).aspx + inline unsigned long long QueryUnbiasedInterruptTimeAs100ns() WI_NOEXCEPT + { + ULONGLONG now{}; + QueryUnbiasedInterruptTime(&now); + return now; + } + + /// Returns the current unbiased interrupt-time count, in units of milliseconds. The unbiased interrupt-time count does not include time the system spends in sleep or hibernation. + /// @see QueryUnbiasedInterruptTimeAs100ns + inline unsigned long long QueryUnbiasedInterruptTimeAsMSec() WI_NOEXCEPT + { + return convert_100ns_to_msec(QueryUnbiasedInterruptTimeAs100ns()); + } +#endif // _APISETREALTIME_ + } +#pragma endregion + + // Use to adapt Win32 APIs that take a fixed size buffer into forms that return + // an allocated buffer. Supports many types of string representation. + // See comments below on the expected behavior of the callback. + // Adjust stackBufferLength based on typical result sizes to optimize use and + // to test the boundary cases. + template + HRESULT AdaptFixedSizeToAllocatedResult(string_type& result, wistd::function callback) WI_NOEXCEPT + { + details::string_maker maker; + + wchar_t value[stackBufferLength]; + value[0] = L'\0'; + size_t valueLengthNeededWithNull{}; // callback returns the number of characters needed including the null terminator. + RETURN_IF_FAILED_EXPECTED(callback(value, ARRAYSIZE(value), &valueLengthNeededWithNull)); + WI_ASSERT(valueLengthNeededWithNull > 0); + if (valueLengthNeededWithNull <= ARRAYSIZE(value)) + { + // Success case as described above, make() adds the space for the null. + RETURN_IF_FAILED(maker.make(value, valueLengthNeededWithNull - 1)); + } + else + { + // Did not fit in the stack allocated buffer, need to do 2 phase construction. + // May need to loop more than once if external conditions cause the value to change. + size_t bufferLength; + do + { + bufferLength = valueLengthNeededWithNull; + // bufferLength includes the null so subtract that as make() will add space for it. + RETURN_IF_FAILED(maker.make(nullptr, bufferLength - 1)); + + RETURN_IF_FAILED_EXPECTED(callback(maker.buffer(), bufferLength, &valueLengthNeededWithNull)); + WI_ASSERT(valueLengthNeededWithNull > 0); + + // If the value shrunk, then adjust the string to trim off the excess buffer. + if (valueLengthNeededWithNull < bufferLength) + { + RETURN_IF_FAILED(maker.trim_at_existing_null(valueLengthNeededWithNull - 1)); + } + } + while (valueLengthNeededWithNull > bufferLength); + } + result = maker.release(); + return S_OK; + } + + /** Expands the '%' quoted environment variables in 'input' using ExpandEnvironmentStringsW(); */ + template + HRESULT ExpandEnvironmentStringsW(_In_ PCWSTR input, string_type& result) WI_NOEXCEPT + { + return wil::AdaptFixedSizeToAllocatedResult(result, + [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT + { + *valueLengthNeededWithNul = ::ExpandEnvironmentStringsW(input, value, static_cast(valueLength)); + RETURN_LAST_ERROR_IF(*valueLengthNeededWithNul == 0); + return S_OK; + }); + } + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES) + /** Searches for a specified file in a specified path using ExpandEnvironmentStringsW(); */ + template + HRESULT SearchPathW(_In_opt_ PCWSTR path, _In_ PCWSTR fileName, _In_opt_ PCWSTR extension, string_type& result) WI_NOEXCEPT + { + return wil::AdaptFixedSizeToAllocatedResult(result, + [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT + { + *valueLengthNeededWithNul = ::SearchPathW(path, fileName, extension, static_cast(valueLength), value, nullptr); + + if (*valueLengthNeededWithNul == 0) + { + // ERROR_FILE_NOT_FOUND is an expected return value for SearchPathW + const HRESULT searchResult = HRESULT_FROM_WIN32(::GetLastError()); + RETURN_HR_IF_EXPECTED(searchResult, searchResult == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + RETURN_IF_FAILED(searchResult); + } + + // AdaptFixedSizeToAllocatedResult expects that the length will always include the NUL. + // If the result is copied to the buffer, SearchPathW returns the length of copied string, WITHOUT the NUL. + // If the buffer is too small to hold the result, SearchPathW returns the length of the required buffer WITH the nul. + if (*valueLengthNeededWithNul < valueLength) + { + (*valueLengthNeededWithNul)++; // It fit, account for the null. + } + return S_OK; + }); + } + + template + HRESULT QueryFullProcessImageNameW(HANDLE processHandle, _In_ DWORD flags, string_type& result) WI_NOEXCEPT + { + return wil::AdaptFixedSizeToAllocatedResult(result, + [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT + { + DWORD lengthToUse = static_cast(valueLength); + BOOL const success = ::QueryFullProcessImageNameW(processHandle, flags, value, &lengthToUse); + RETURN_LAST_ERROR_IF((success == FALSE) && (::GetLastError() != ERROR_INSUFFICIENT_BUFFER)); + + // On success, return the amount used; on failure, try doubling + *valueLengthNeededWithNul = success ? (lengthToUse + 1) : (lengthToUse * 2); + return S_OK; + }); + } + + /** Expands environment strings and checks path existence with SearchPathW */ + template + HRESULT ExpandEnvAndSearchPath(_In_ PCWSTR input, string_type& result) WI_NOEXCEPT + { + wil::unique_cotaskmem_string expandedName; + RETURN_IF_FAILED((wil::ExpandEnvironmentStringsW(input, expandedName))); + + // ERROR_FILE_NOT_FOUND is an expected return value for SearchPathW + const HRESULT searchResult = (wil::SearchPathW(nullptr, expandedName.get(), nullptr, result)); + RETURN_HR_IF_EXPECTED(searchResult, searchResult == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)); + RETURN_IF_FAILED(searchResult); + + return S_OK; + } +#endif + + /** Looks up the environment variable 'key' and fails if it is not found. */ + template + inline HRESULT GetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT + { + return wil::AdaptFixedSizeToAllocatedResult(result, + [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT + { + // If the function succeeds, the return value is the number of characters stored in the buffer + // pointed to by lpBuffer, not including the terminating null character. + // + // If lpBuffer is not large enough to hold the data, the return value is the buffer size, in + // characters, required to hold the string and its terminating null character and the contents of + // lpBuffer are undefined. + // + // If the function fails, the return value is zero. If the specified environment variable was not + // found in the environment block, GetLastError returns ERROR_ENVVAR_NOT_FOUND. + + ::SetLastError(ERROR_SUCCESS); + + *valueLengthNeededWithNul = ::GetEnvironmentVariableW(key, value, static_cast(valueLength)); + RETURN_LAST_ERROR_IF_EXPECTED((*valueLengthNeededWithNul == 0) && (::GetLastError() != ERROR_SUCCESS)); + if (*valueLengthNeededWithNul < valueLength) + { + (*valueLengthNeededWithNul)++; // It fit, account for the null. + } + return S_OK; + }); + } + + /** Looks up the environment variable 'key' and returns null if it is not found. */ + template + HRESULT TryGetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT + { + const auto hr = wil::GetEnvironmentVariableW(key, result); + RETURN_HR_IF(hr, FAILED(hr) && (hr != HRESULT_FROM_WIN32(ERROR_ENVVAR_NOT_FOUND))); + return S_OK; + } + + /** Retrieves the fully qualified path for the file containing the specified module loaded + by a given process. Note GetModuleFileNameExW is a macro.*/ + template + HRESULT GetModuleFileNameExW(_In_opt_ HANDLE process, _In_opt_ HMODULE module, string_type& path) WI_NOEXCEPT + { + auto adapter = [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT + { + DWORD copiedCount; + size_t valueUsedWithNul; + bool copyFailed; + bool copySucceededWithNoTruncation; + if (process != nullptr) + { + // GetModuleFileNameExW truncates and provides no error or other indication it has done so. + // The only way to be sure it didn't truncate is if it didn't need the whole buffer. The + // count copied to the buffer includes the nul-character as well. + copiedCount = ::GetModuleFileNameExW(process, module, value, static_cast(valueLength)); + valueUsedWithNul = copiedCount + 1; + copyFailed = (0 == copiedCount); + copySucceededWithNoTruncation = !copyFailed && (copiedCount < valueLength - 1); + } + else + { + // In cases of insufficient buffer, GetModuleFileNameW will return a value equal to lengthWithNull + // and set the last error to ERROR_INSUFFICIENT_BUFFER. The count returned does not include + // the nul-character + copiedCount = ::GetModuleFileNameW(module, value, static_cast(valueLength)); + valueUsedWithNul = copiedCount + 1; + copyFailed = (0 == copiedCount); + copySucceededWithNoTruncation = !copyFailed && (copiedCount < valueLength); + } + + RETURN_LAST_ERROR_IF(copyFailed); + + // When the copy truncated, request another try with more space. + *valueLengthNeededWithNul = copySucceededWithNoTruncation ? valueUsedWithNul : (valueLength * 2); + + return S_OK; + }; + + return wil::AdaptFixedSizeToAllocatedResult(path, wistd::move(adapter)); + } + + /** Retrieves the fully qualified path for the file that contains the specified module. + The module must have been loaded by the current process. The path returned will use the + same format that was specified when the module was loaded. Therefore, the path can be a + long or short file name, and can have the prefix '\\?\'. */ + template + HRESULT GetModuleFileNameW(HMODULE module, string_type& path) WI_NOEXCEPT + { + return wil::GetModuleFileNameExW(nullptr, module, path); + } + + template + HRESULT GetSystemDirectoryW(string_type& result) WI_NOEXCEPT + { + return wil::AdaptFixedSizeToAllocatedResult(result, + [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT + { + *valueLengthNeededWithNul = ::GetSystemDirectoryW(value, static_cast(valueLength)); + RETURN_LAST_ERROR_IF(*valueLengthNeededWithNul == 0); + if (*valueLengthNeededWithNul < valueLength) + { + (*valueLengthNeededWithNul)++; // it fit, account for the null + } + return S_OK; + }); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Expands the '%' quoted environment variables in 'input' using ExpandEnvironmentStringsW(); */ + template + string_type ExpandEnvironmentStringsW(_In_ PCWSTR input) + { + string_type result; + THROW_IF_FAILED((wil::ExpandEnvironmentStringsW(input, result))); + return result; + } + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES) + /** Searches for a specified file in a specified path using SearchPathW*/ + template + string_type TrySearchPathW(_In_opt_ PCWSTR path, _In_ PCWSTR fileName, PCWSTR _In_opt_ extension) + { + string_type result; + HRESULT searchHR = wil::SearchPathW(path, fileName, extension, result); + THROW_HR_IF(searchHR, FAILED(searchHR) && (searchHR != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))); + return result; + } +#endif + + /** Looks up the environment variable 'key' and fails if it is not found. */ + template + string_type GetEnvironmentVariableW(_In_ PCWSTR key) + { + string_type result; + THROW_IF_FAILED((wil::GetEnvironmentVariableW(key, result))); + return result; + } + + /** Looks up the environment variable 'key' and returns null if it is not found. */ + template + string_type TryGetEnvironmentVariableW(_In_ PCWSTR key) + { + string_type result; + THROW_IF_FAILED((wil::TryGetEnvironmentVariableW(key, result))); + return result; + } + + template + string_type GetModuleFileNameW(HMODULE module) + { + string_type result; + THROW_IF_FAILED((wil::GetModuleFileNameW(module, result))); + return result; + } + + template + string_type GetModuleFileNameExW(HANDLE process, HMODULE module) + { + string_type result; + THROW_IF_FAILED((wil::GetModuleFileNameExW(process, module, result))); + return result; + } + +#endif + + /** Retrieve the HINSTANCE for the current DLL or EXE using this symbol that + the linker provides for every module. This avoids the need for a global HINSTANCE variable + and provides access to this value for static libraries. */ + EXTERN_C IMAGE_DOS_HEADER __ImageBase; + inline HINSTANCE GetModuleInstanceHandle() WI_NOEXCEPT { return reinterpret_cast(&__ImageBase); } + + /// @cond + namespace details + { + class init_once_completer + { + INIT_ONCE& m_once; + unsigned long m_flags = INIT_ONCE_INIT_FAILED; + public: + init_once_completer(_In_ INIT_ONCE& once) WI_NOEXCEPT : m_once(once) + { + } + + #pragma warning(push) + #pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2 + void success() WI_NOEXCEPT + { + m_flags = 0; + } + #pragma warning(pop) + + ~init_once_completer() WI_NOEXCEPT + { + ::InitOnceComplete(&m_once, m_flags, nullptr); + } + }; + } + /// @endcond + + /** Performs one-time initialization + Simplifies using the Win32 INIT_ONCE structure to perform one-time initialization. The provided `func` is invoked + at most once. + ~~~~ + INIT_ONCE g_init{}; + ComPtr g_foo; + HRESULT MyMethod() + { + bool winner = false; + RETURN_IF_FAILED(wil::init_once_nothrow(g_init, [] + { + ComPtr foo; + RETURN_IF_FAILED(::CoCreateInstance(..., IID_PPV_ARGS(&foo)); + RETURN_IF_FAILED(foo->Startup()); + g_foo = foo; + }, &winner); + if (winner) + { + RETURN_IF_FAILED(g_foo->Another()); + } + return S_OK; + } + ~~~~ + See MSDN for more information on `InitOnceExecuteOnce`. + @param initOnce The INIT_ONCE structure to use as context for initialization. + @param func A function that will be invoked to perform initialization. If this fails, the init call + fails and the once-init is not marked as initialized. A later caller could attempt to + initialize it a second time. + @param callerCompleted Set to 'true' if this was the call that caused initialization, false otherwise. + */ + template HRESULT init_once_nothrow(_Inout_ INIT_ONCE& initOnce, T func, _Out_opt_ bool* callerCompleted = nullptr) WI_NOEXCEPT + { + BOOL pending = FALSE; + wil::assign_to_opt_param(callerCompleted, false); + + __WIL_PRIVATE_RETURN_IF_WIN32_BOOL_FALSE(InitOnceBeginInitialize(&initOnce, 0, &pending, nullptr)); + + if (pending) + { + details::init_once_completer completion(initOnce); + __WIL_PRIVATE_RETURN_IF_FAILED(func()); + completion.success(); + wil::assign_to_opt_param(callerCompleted, true); + } + + return S_OK; + } + + //! Similar to init_once_nothrow, but fails-fast if the initialization step failed. The 'callerComplete' value is + //! returned to the caller instead of being an out-parameter. + template bool init_once_failfast(_Inout_ INIT_ONCE& initOnce, T&& func) WI_NOEXCEPT + { + bool callerCompleted; + + FAIL_FAST_IF_FAILED(init_once_nothrow(initOnce, wistd::forward(func), &callerCompleted)); + + return callerCompleted; + }; + + //! Returns 'true' if this `init_once` structure has finished initialization, false otherwise. + inline bool init_once_initialized(_Inout_ INIT_ONCE& initOnce) WI_NOEXCEPT + { + BOOL pending = FALSE; + return ::InitOnceBeginInitialize(&initOnce, INIT_ONCE_CHECK_ONLY, &pending, nullptr) && !pending; + } + +#ifdef WIL_ENABLE_EXCEPTIONS + /** Performs one-time initialization + Simplifies using the Win32 INIT_ONCE structure to perform one-time initialization. The provided `func` is invoked + at most once. + ~~~~ + INIT_ONCE g_init{}; + ComPtr g_foo; + void MyMethod() + { + bool winner = wil::init_once(g_init, [] + { + ComPtr foo; + THROW_IF_FAILED(::CoCreateInstance(..., IID_PPV_ARGS(&foo)); + THROW_IF_FAILED(foo->Startup()); + g_foo = foo; + }); + if (winner) + { + THROW_IF_FAILED(g_foo->Another()); + } + } + ~~~~ + See MSDN for more information on `InitOnceExecuteOnce`. + @param initOnce The INIT_ONCE structure to use as context for initialization. + @param func A function that will be invoked to perform initialization. If this fails, the init call + fails and the once-init is not marked as initialized. A later caller could attempt to + initialize it a second time. + @returns 'true' if this was the call that caused initialization, false otherwise. + */ + template bool init_once(_Inout_ INIT_ONCE& initOnce, T func) + { + BOOL pending = FALSE; + + THROW_IF_WIN32_BOOL_FALSE(::InitOnceBeginInitialize(&initOnce, 0, &pending, nullptr)); + + if (pending) + { + details::init_once_completer completion(initOnce); + func(); + completion.success(); + return true; + } + else + { + return false; + } + } +#endif // WIL_ENABLE_EXCEPTIONS +} + +// Macro for calling GetProcAddress(), with type safety for C++ clients +// using the type information from the specified function. +// The return value is automatically cast to match the function prototype of the input function. +// +// Sample usage: +// +// auto sendMail = GetProcAddressByFunctionDeclaration(hinstMAPI, MAPISendMailW); +// if (sendMail) +// { +// sendMail(0, 0, pmm, MAPI_USE_DEFAULT, 0); +// } +// Declaration +#define GetProcAddressByFunctionDeclaration(hinst, fn) reinterpret_cast(GetProcAddress(hinst, #fn)) + +#endif // __WIL_WIN32_HELPERS_INCLUDED diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/winrt.h b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/winrt.h new file mode 100644 index 0000000..732189b --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/winrt.h @@ -0,0 +1,2293 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_WINRT_INCLUDED +#define __WIL_WINRT_INCLUDED + +#include +#include +#include +#include +#include +#include "result.h" +#include "com.h" +#include "resource.h" +#include +#include + +#ifdef __cplusplus_winrt +#include // bring in the CRT iterator for support for C++ CX code +#endif + +/// @cond +#if defined(WIL_ENABLE_EXCEPTIONS) && !defined(__WI_HAS_STD_LESS) +#ifdef __has_include +#if __has_include() +#define __WI_HAS_STD_LESS 1 +#include +#endif // Otherwise, not using STL; don't specialize std::less +#else +// Fall back to the old way of forward declaring std::less +#define __WI_HAS_STD_LESS 1 +#pragma warning(push) +#pragma warning(disable:4643) // Forward declaring '...' in namespace std is not permitted by the C++ Standard. +namespace std +{ + template + struct less; +} +#pragma warning(pop) +#endif +#endif +/// @endcond + +// This enables this code to be used in code that uses the ABI prefix or not. +// Code using the public SDK and C++ CX code has the ABI prefix, windows internal +// is built in a way that does not. +#if !defined(MIDL_NS_PREFIX) && !defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) +// Internal .idl files use the namespace without the ABI prefix. Macro out ABI for that case +#pragma push_macro("ABI") +#undef ABI +#define ABI +#endif + +namespace wil +{ + // time_t is the number of 1 - second intervals since January 1, 1970. + long long const SecondsToStartOf1970 = 0x2b6109100; + long long const HundredNanoSecondsInSecond = 10000000LL; + + inline __time64_t DateTime_to_time_t(ABI::Windows::Foundation::DateTime dateTime) + { + // DateTime is the number of 100 - nanosecond intervals since January 1, 1601. + return (dateTime.UniversalTime / HundredNanoSecondsInSecond - SecondsToStartOf1970); + } + + inline ABI::Windows::Foundation::DateTime time_t_to_DateTime(__time64_t timeT) + { + ABI::Windows::Foundation::DateTime dateTime; + dateTime.UniversalTime = (timeT + SecondsToStartOf1970) * HundredNanoSecondsInSecond; + return dateTime; + } + +#pragma region HSTRING Helpers + /// @cond + namespace details + { + // hstring_compare is used to assist in HSTRING comparison of two potentially non-similar string types. E.g. + // comparing a raw HSTRING with WRL's HString/HStringReference/etc. The consumer can optionally inhibit the + // deduction of array sizes by providing 'true' for the 'InhibitStringArrays' template argument. This is + // generally done in scenarios where the consumer cannot guarantee that the input argument types are perfectly + // preserved from end-to-end. E.g. if a single function in the execution path captures an array as const T&, + // then it is impossible to differentiate const arrays (where we generally do want to deduce length) from + // non-const arrays (where we generally do not want to deduce length). The consumer can also optionally choose + // to perform case-insensitive comparison by providing 'true' for the 'IgnoreCase' template argument. + template + struct hstring_compare + { + // get_buffer returns the string buffer and length for the supported string types + static const wchar_t* get_buffer(HSTRING hstr, UINT32* length) WI_NOEXCEPT + { + return ::WindowsGetStringRawBuffer(hstr, length); + } + + static const wchar_t* get_buffer(const Microsoft::WRL::Wrappers::HString& hstr, UINT32* length) WI_NOEXCEPT + { + return hstr.GetRawBuffer(length); + } + + static const wchar_t* get_buffer( + const Microsoft::WRL::Wrappers::HStringReference& hstr, + UINT32* length) WI_NOEXCEPT + { + return hstr.GetRawBuffer(length); + } + + static const wchar_t* get_buffer(const unique_hstring& str, UINT32* length) WI_NOEXCEPT + { + return ::WindowsGetStringRawBuffer(str.get(), length); + } + + template + static wistd::enable_if_t get_buffer(const wchar_t* str, UINT32* length) WI_NOEXCEPT + { + str = (str != nullptr) ? str : L""; + *length = static_cast(wcslen(str)); + return str; + } + + template + static wistd::enable_if_t< + wistd::conjunction< + wistd::is_pointer, + wistd::is_same>, wchar_t>, + wistd::bool_constant + >::value, + const wchar_t*> get_buffer(StringT str, UINT32* length) WI_NOEXCEPT + { + str = (str != nullptr) ? str : L""; + *length = static_cast(wcslen(str)); + return str; + } + + template + static wistd::enable_if_t get_buffer( + const wchar_t (&str)[Size], + UINT32* length) WI_NOEXCEPT + { + *length = Size - 1; + return str; + } + + template + static wistd::enable_if_t get_buffer(wchar_t (&str)[Size], UINT32* length) WI_NOEXCEPT + { + *length = static_cast(wcslen(str)); + return str; + } + + // Overload for std::wstring, or at least things that behave like std::wstring, without adding a dependency + // on STL headers + template + static wistd::enable_if_t().c_str())>, + wistd::is_same().length())>>, + const wchar_t*> get_buffer(const StringT& str, UINT32* length) WI_NOEXCEPT + { + *length = static_cast(str.length()); + return str.c_str(); + } + + template + static auto compare(LhsT&& lhs, RhsT&& rhs) -> + decltype(get_buffer(lhs, wistd::declval()), get_buffer(rhs, wistd::declval()), int()) + { + UINT32 lhsLength; + UINT32 rhsLength; + auto lhsBuffer = get_buffer(wistd::forward(lhs), &lhsLength); + auto rhsBuffer = get_buffer(wistd::forward(rhs), &rhsLength); + + const auto result = ::CompareStringOrdinal( + lhsBuffer, + lhsLength, + rhsBuffer, + rhsLength, + IgnoreCase ? TRUE : FALSE); + WI_ASSERT(result != 0); + + return result; + } + + template + static auto equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT -> + decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) + { + return compare(wistd::forward(lhs), wistd::forward(rhs)) == CSTR_EQUAL; + } + + template + static auto not_equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT -> + decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) + { + return compare(wistd::forward(lhs), wistd::forward(rhs)) != CSTR_EQUAL; + } + + template + static auto less(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT -> + decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) + { + return compare(wistd::forward(lhs), wistd::forward(rhs)) == CSTR_LESS_THAN; + } + + template + static auto less_equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT -> + decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) + { + return compare(wistd::forward(lhs), wistd::forward(rhs)) != CSTR_GREATER_THAN; + } + + template + static auto greater(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT -> + decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) + { + return compare(wistd::forward(lhs), wistd::forward(rhs)) == CSTR_GREATER_THAN; + } + + template + static auto greater_equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT -> + decltype(compare(wistd::forward(lhs), wistd::forward(rhs)), bool()) + { + return compare(wistd::forward(lhs), wistd::forward(rhs)) != CSTR_LESS_THAN; + } + }; + } + /// @endcond + + //! Detects if one or more embedded null is present in an HSTRING. + inline bool HasEmbeddedNull(_In_opt_ HSTRING value) + { + BOOL hasEmbeddedNull; + WindowsStringHasEmbeddedNull(value, &hasEmbeddedNull); + return hasEmbeddedNull != FALSE; + } + + /** TwoPhaseHStringConstructor help using the 2 phase constructor pattern for HSTRINGs. + ~~~ + auto stringConstructor = wil::TwoPhaseHStringConstructor::Preallocate(size); + RETURN_IF_NULL_ALLOC(stringConstructor.Get()); + + RETURN_IF_FAILED(stream->Read(stringConstructor.Get(), stringConstructor.ByteSize(), &bytesRead)); + + // Validate stream contents, sizes must match, string must be null terminated. + RETURN_IF_FAILED(stringConstructor.Validate(bytesRead)); + + wil::unique_hstring string { stringConstructor.Promote() }; + ~~~ + + See also wil::unique_hstring_buffer. + */ + struct TwoPhaseHStringConstructor + { + TwoPhaseHStringConstructor() = delete; + TwoPhaseHStringConstructor(const TwoPhaseHStringConstructor&) = delete; + void operator=(const TwoPhaseHStringConstructor&) = delete; + + TwoPhaseHStringConstructor(TwoPhaseHStringConstructor&& other) WI_NOEXCEPT + { + m_characterLength = other.m_characterLength; + other.m_characterLength = 0; + m_maker = wistd::move(other.m_maker); + } + + static TwoPhaseHStringConstructor Preallocate(UINT32 characterLength) + { + return TwoPhaseHStringConstructor{ characterLength }; + } + + //! Returns the HSTRING after it has been populated like Detatch() or release(); be sure to put this in a RAII type to manage its lifetime. + HSTRING Promote() + { + m_characterLength = 0; + return m_maker.release().release(); + } + + ~TwoPhaseHStringConstructor() = default; + + explicit operator PCWSTR() const + { + // This is set by WindowsPromoteStringBuffer() which must be called to + // construct this object via the static method Preallocate(). + return m_maker.buffer(); + } + + //! Returns a pointer for the buffer so it can be populated + wchar_t* Get() const { return const_cast(m_maker.buffer()); } + //! Used to validate range of buffer when populating. + ULONG ByteSize() const { return m_characterLength * sizeof(wchar_t); } + + /** Ensure that the size of the data provided is consistent with the pre-allocated buffer. + It seems that WindowsPreallocateStringBuffer() provides the null terminator in the buffer + (based on testing) so this can be called before populating the buffer. + */ + HRESULT Validate(ULONG bytesRead) const + { + // Null termination is required for the buffer before calling WindowsPromoteStringBuffer(). + RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_DATA), + (bytesRead != ByteSize()) || + (Get()[m_characterLength] != L'\0')); + return S_OK; + } + + private: + TwoPhaseHStringConstructor(UINT32 characterLength) : m_characterLength(characterLength) + { + (void)m_maker.make(nullptr, characterLength); + } + + UINT32 m_characterLength; + details::string_maker m_maker; + }; + + //! A transparent less-than comparison function object that enables comparison of various string types intended for + //! use with associative containers (such as `std::set`, `std::map`, etc.) that use + //! `Microsoft::WRL::Wrappers::HString` as the key type. This removes the need for the consumer to explicitly + //! create an `HString` object when using lookup functions such as `find`, `lower_bound`, etc. For example, the + //! following scenarios would all work exactly as you would expect them to: + //! ~~~ + //! std::map map; + //! const wchar_t constArray[] = L"foo"; + //! wchar_t nonConstArray[MAX_PATH] = L"foo"; + //! + //! HString key; + //! THROW_IF_FAILED(key.Set(constArray)); + //! map.emplace(std::move(key), 42); + //! + //! HString str; + //! wil::unique_hstring uniqueStr; + //! THROW_IF_FAILED(str.Set(L"foo")); + //! THROW_IF_FAILED(str.CopyTo(&uniqueStr)); + //! + //! // All of the following return an iterator to the pair { L"foo", 42 } + //! map.find(str); + //! map.find(str.Get()); + //! map.find(HStringReference(constArray)); + //! map.find(uniqueStr); + //! map.find(std::wstring(constArray)); + //! map.find(constArray); + //! map.find(nonConstArray); + //! map.find(static_cast(constArray)); + //! ~~~ + //! The first four calls in the example above use `WindowsGetStringRawBuffer` (or equivalent) to get the string + //! buffer and length for the comparison. The fifth example uses `std::wstring::c_str` and `std::wstring::length` + //! for getting these two values. The remaining three examples use only the string buffer and call `wcslen` for the + //! length. That is, the length is *not* deduced for either array. This is because argument types are not always + //! perfectly preserved by container functions and in fact are often captured as const references making it + //! impossible to differentiate const arrays - where we can safely deduce length - from non const arrays - where we + //! cannot safely deduce length since the buffer may be larger than actually needed (e.g. creating a + //! `char[MAX_PATH]` array, but only filling it with 10 characters). The implications of this behavior is that + //! string literals that contain embedded null characters will only include the part of the buffer up to the first + //! null character. For example, the following example will result in all calls to `find` returning an end + //! iterator. + //! ~~~ + //! std::map map; + //! const wchar_t constArray[] = L"foo\0bar"; + //! wchar_t nonConstArray[MAX_PATH] = L"foo\0bar"; + //! + //! // Create the key with the embedded null character + //! HString key; + //! THROW_IF_FAILED(key.Set(constArray)); + //! map.emplace(std::move(key), 42); + //! + //! // All of the following return map.end() since they look for the string "foo" + //! map.find(constArray); + //! map.find(nonConstArray); + //! map.find(static_cast(constArray)); + //! ~~~ + //! In order to search using a string literal that contains embedded null characters, a simple alternative is to + //! first create an `HStringReference` and use that for the function call: + //! ~~~ + //! // HStringReference's constructor *will* deduce the length of const arrays + //! map.find(HStringReference(constArray)); + //! ~~~ + struct hstring_less + { + using is_transparent = void; + + template + auto operator()(const LhsT& lhs, const RhsT& rhs) const WI_NOEXCEPT -> + decltype(details::hstring_compare::less(lhs, rhs)) + { + return details::hstring_compare::less(lhs, rhs); + } + }; + + //! A transparent less-than comparison function object whose behavior is equivalent to that of @ref hstring_less + //! with the one difference that comparisons are case-insensitive. That is, the following example will correctly + //! find the inserted value: + //! ~~~ + //! std::map map; + //! + //! HString key; + //! THROW_IF_FAILED(key.Set(L"foo")); + //! map.emplace(std::move(key), 42); + //! + //! // All of the following return an iterator to the pair { L"foo", 42 } + //! map.find(L"FOo"); + //! map.find(HStringReference(L"fOo")); + //! map.find(HStringReference(L"fOO").Get()); + //! ~~~ + struct hstring_insensitive_less + { + using is_transparent = void; + + template + auto operator()(const LhsT& lhs, const RhsT& rhs) const WI_NOEXCEPT -> + decltype(details::hstring_compare::less(lhs, rhs)) + { + return details::hstring_compare::less(lhs, rhs); + } + }; + +#pragma endregion + + /// @cond + namespace details + { + // MapToSmartType::type is used to map a raw type into an RAII expression + // of it. This is needed when lifetime management of the type is needed, for example + // when holding them as a value produced in an iterator. + // This type has a common set of methods used to abstract the access to the value + // that is similar to ComPtr<> and the WRL Wrappers: Get(), GetAddressOf() and other operators. + // Clients of the smart type must use those to access the value. + + // TODO: Having the base definition defined will result in creating leaks if a type + // that needs resource management (e.g. PROPVARIANT) that has not specialized is used. + // + // One fix is to use std::is_enum to cover that case and leave the base definition undefined. + // That base should use static_assert to inform clients how to fix the lack of specialization. + template struct MapToSmartType + { + #pragma warning(push) + #pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2 + struct type // T holder + { + type() {}; + type(T&& value) : m_value(wistd::forward(value)) {}; + operator T() const { return m_value; } + type& operator=(T&& value) { m_value = wistd::forward(value); return *this; } + T Get() const { return m_value; } + + // Returning T&& to support move only types + // In case of absense of T::operator=(T&&) a call to T::operator=(const T&) will happen + T&& Get() { return wistd::move(m_value); } + + HRESULT CopyTo(T* result) const { *result = m_value; return S_OK; } + T* GetAddressOf() { return &m_value; } + T* ReleaseAndGetAddressOf() { return &m_value; } + T* operator&() { return &m_value; } + T m_value{}; + }; + #pragma warning(pop) + }; + + // IUnknown * derived -> Microsoft::WRL::ComPtr<> + template + struct MapToSmartType::type>::value>::type> + { + typedef Microsoft::WRL::ComPtr::type> type; + }; + + // HSTRING -> Microsoft::WRL::Wrappers::HString + template <> struct MapToSmartType + { + class HStringWithRelease : public Microsoft::WRL::Wrappers::HString + { + public: + // Unlike all other WRL types HString does not have ReleaseAndGetAddressOf and + // GetAddressOf() has non-standard behavior, calling Release(). + HSTRING* ReleaseAndGetAddressOf() WI_NOEXCEPT + { + Release(); + return &hstr_; + } + }; + typedef HStringWithRelease type; + }; + + // WinRT interfaces like IVector<>, IAsyncOperation<> and IIterable<> can be templated + // on a runtime class (instead of an interface or primitive type). In these cases the objects + // produced by those interfaces implement an interface defined by the runtime class default interface. + // + // These templates deduce the type of the produced interface or pass through + // the type unmodified in the non runtime class case. + // + // for example: + // IAsyncOperation -> IAsyncOperation + + // For IVector, IVectorView. + template struct MapVectorResultType + { + template + static TResult PeekGetAtType(HRESULT(STDMETHODCALLTYPE TVector::*)(unsigned, TResult*)); + typedef decltype(PeekGetAtType(&VectorType::GetAt)) type; + }; + + // For IIterator. + template struct MapIteratorResultType + { + template + static TResult PeekCurrentType(HRESULT(STDMETHODCALLTYPE TIterable::*)(TResult*)); + typedef decltype(PeekCurrentType(&ABI::Windows::Foundation::Collections::IIterator::get_Current)) type; + }; + + // For IAsyncOperation. + template struct MapAsyncOpResultType + { + template + static TResult PeekGetResultsType(HRESULT(STDMETHODCALLTYPE TAsyncOperation::*)(TResult*)); + typedef decltype(PeekGetResultsType(&ABI::Windows::Foundation::IAsyncOperation::GetResults)) type; + }; + + // For IAsyncOperationWithProgress. + template struct MapAsyncOpProgressResultType + { + template + static TResult PeekGetResultsType(HRESULT(STDMETHODCALLTYPE TAsyncOperation::*)(TResult*)); + typedef decltype(PeekGetResultsType(&ABI::Windows::Foundation::IAsyncOperationWithProgress::GetResults)) type; + }; + + // No support for IAsyncActionWithProgress

none of these (currently) use + // a runtime class for the progress type. + } + /// @endcond +#pragma region C++ iterators for WinRT collections for use with range based for and STL algorithms + + /** Range base for and STL algorithms support for WinRT ABI collection types, IVector, IVectorView, IIterable + similar to support provided by for C++ CX. Three error handling policies are supported. + ~~~ + ComPtr collection = GetCollection(); // can be IVector, IVectorView or IIterable + + for (auto const& element : wil::get_range(collection.Get())) // exceptions + for (auto const& element : wil::get_range_nothrow(collection.Get(), &hr)) // error code + for (auto const& element : wil::get_range_failfast(collection.Get())) // fail fast + { + // use element + } + ~~~ + Standard algorithm example: + ~~~ + ComPtr> files = GetFiles(); + auto fileRange = wil::get_range_nothrow(files.Get()); + auto itFound = std::find_if(fileRange.begin(), fileRange.end(), [](ComPtr file) -> bool + { + return true; // first element in range + }); + ~~~ + */ +#pragma region exception and fail fast based IVector<>/IVectorView<> + + template + class vector_range + { + public: + typedef typename details::MapVectorResultType::type TResult; + typedef typename details::MapToSmartType::type TSmart; + + vector_range() = delete; + + explicit vector_range(_In_ VectorType *vector) : m_v(vector) + { + } + + class vector_iterator + { + public: +#ifdef _XUTILITY_ + // could be random_access_iterator_tag but missing some features + typedef ::std::bidirectional_iterator_tag iterator_category; +#endif + typedef TSmart value_type; + typedef ptrdiff_t difference_type; + typedef const TSmart* pointer; + typedef const TSmart& reference; + + // for begin() + vector_iterator(VectorType* v, unsigned int pos) + : m_v(v), m_i(pos) + { + } + + // for end() + vector_iterator() : m_v(nullptr), m_i(-1) {} + + vector_iterator(const vector_iterator& other) + { + m_v = other.m_v; + m_i = other.m_i; + err_policy::HResult(other.m_element.CopyTo(m_element.GetAddressOf())); + } + + vector_iterator& operator=(const vector_iterator& other) + { + if (this != wistd::addressof(other)) + { + m_v = other.m_v; + m_i = other.m_i; + err_policy::HResult(other.m_element.CopyTo(m_element.ReleaseAndGetAddressOf())); + } + return *this; + } + + reference operator*() + { + err_policy::HResult(m_v->GetAt(m_i, m_element.ReleaseAndGetAddressOf())); + return m_element; + } + + pointer operator->() + { + err_policy::HResult(m_v->GetAt(m_i, m_element.ReleaseAndGetAddressOf())); + return wistd::addressof(m_element); + } + + vector_iterator& operator++() + { + ++m_i; + return *this; + } + + vector_iterator& operator--() + { + --m_i; + return *this; + } + + vector_iterator operator++(int) + { + vector_iterator old(*this); + ++*this; + return old; + } + + vector_iterator operator--(int) + { + vector_iterator old(*this); + --*this; + return old; + } + + vector_iterator& operator+=(int n) + { + m_i += n; + return *this; + } + + vector_iterator& operator-=(int n) + { + m_i -= n; + return *this; + } + + vector_iterator operator+(int n) const + { + vector_iterator ret(*this); + ret += n; + return ret; + } + + vector_iterator operator-(int n) const + { + vector_iterator ret(*this); + ret -= n; + return ret; + } + + ptrdiff_t operator-(const vector_iterator& other) const + { + return m_i - other.m_i; + } + + bool operator==(const vector_iterator& other) const + { + return m_i == other.m_i; + } + + bool operator!=(const vector_iterator& other) const + { + return m_i != other.m_i; + } + + bool operator<(const vector_iterator& other) const + { + return m_i < other.m_i; + } + + bool operator>(const vector_iterator& other) const + { + return m_i > other.m_i; + } + + bool operator<=(const vector_iterator& other) const + { + return m_i <= other.m_i; + } + + bool operator>=(const vector_iterator& other) const + { + return m_i >= other.m_i; + } + + private: + VectorType* m_v; // weak, collection must outlive iterators. + unsigned int m_i; + TSmart m_element; + }; + + vector_iterator begin() + { + return vector_iterator(m_v, 0); + } + + vector_iterator end() + { + unsigned int size; + err_policy::HResult(m_v->get_Size(&size)); + return vector_iterator(m_v, size); + } + private: + VectorType* m_v; // weak, collection must outlive iterators. + }; +#pragma endregion + +#pragma region error code based IVector<>/IVectorView<> + + template + class vector_range_nothrow + { + public: + typedef typename details::MapVectorResultType::type TResult; + typedef typename details::MapToSmartType::type TSmart; + + vector_range_nothrow() = delete; + vector_range_nothrow(const vector_range_nothrow&) = delete; + vector_range_nothrow& operator=(const vector_range_nothrow&) = delete; + + vector_range_nothrow(vector_range_nothrow&& other) : + m_v(other.m_v), m_size(other.m_size), m_result(other.m_result), m_resultStorage(other.m_resultStorage), + m_currentElement(wistd::move(other.m_currentElement)) + { + } + + vector_range_nothrow(_In_ VectorType *vector, HRESULT* result = nullptr) + : m_v(vector), m_result(result ? result : &m_resultStorage) + { + *m_result = m_v->get_Size(&m_size); + } + + class vector_iterator_nothrow + { + public: +#ifdef _XUTILITY_ + // must be input_iterator_tag as use (via ++, --, etc.) of one invalidates the other. + typedef ::std::input_iterator_tag iterator_category; +#endif + typedef TSmart value_type; + typedef ptrdiff_t difference_type; + typedef const TSmart* pointer; + typedef const TSmart& reference; + + vector_iterator_nothrow() = delete; + vector_iterator_nothrow(vector_range_nothrow* range, unsigned int pos) + : m_range(range), m_i(pos) + { + } + + reference operator*() const + { + return m_range->m_currentElement; + } + + pointer operator->() const + { + return wistd::addressof(m_range->m_currentElement); + } + + vector_iterator_nothrow& operator++() + { + ++m_i; + m_range->get_at_current(m_i); + return *this; + } + + vector_iterator_nothrow& operator--() + { + --m_i; + m_range->get_at_current(m_i); + return *this; + } + + vector_iterator_nothrow operator++(int) + { + vector_iterator_nothrow old(*this); + ++*this; + return old; + } + + vector_iterator_nothrow operator--(int) + { + vector_iterator_nothrow old(*this); + --*this; + return old; + } + + vector_iterator_nothrow& operator+=(int n) + { + m_i += n; + m_range->get_at_current(m_i); + return *this; + } + + vector_iterator_nothrow& operator-=(int n) + { + m_i -= n; + m_range->get_at_current(m_i); + return *this; + } + + bool operator==(vector_iterator_nothrow const& other) const + { + return FAILED(*m_range->m_result) || (m_i == other.m_i); + } + + bool operator!=(vector_iterator_nothrow const& other) const + { + return !operator==(other); + } + + private: + vector_range_nothrow* m_range; + unsigned int m_i = 0; + }; + + vector_iterator_nothrow begin() + { + get_at_current(0); + return vector_iterator_nothrow(this, 0); + } + + vector_iterator_nothrow end() + { + return vector_iterator_nothrow(this, m_size); + } + + // Note, the error code is observed in operator!= and operator==, it always + // returns "equal" in the failed state to force the compare to the end + // iterator to return false and stop the loop. + // + // Is this ok for the general case? + void get_at_current(unsigned int i) + { + if (SUCCEEDED(*m_result) && (i < m_size)) + { + *m_result = m_v->GetAt(i, m_currentElement.ReleaseAndGetAddressOf()); + } + } + + private: + VectorType* m_v; // weak, collection must outlive iterators. + unsigned int m_size; + + // This state is shared by vector_iterator_nothrow instances. this means + // use of one iterator invalidates the other. + HRESULT* m_result; + HRESULT m_resultStorage = S_OK; // for the case where the caller does not provide the location to store the result + TSmart m_currentElement; + }; + +#pragma endregion + +#pragma region exception and fail fast based IIterable<> + + template + class iterable_range + { + public: + typedef typename details::MapIteratorResultType::type TResult; + typedef typename details::MapToSmartType::type TSmart; + + explicit iterable_range(_In_ ABI::Windows::Foundation::Collections::IIterable* iterable) + : m_iterable(iterable) + { + } + + class iterable_iterator + { + public: +#ifdef _XUTILITY_ + typedef ::std::forward_iterator_tag iterator_category; +#endif + typedef TSmart value_type; + typedef ptrdiff_t difference_type; + typedef const TSmart* pointer; + typedef const TSmart& reference; + + iterable_iterator() : m_i(-1) {} + + // for begin() + explicit iterable_iterator(_In_ ABI::Windows::Foundation::Collections::IIterable* iterable) + { + err_policy::HResult(iterable->First(&m_iterator)); + boolean hasCurrent; + err_policy::HResult(m_iterator->get_HasCurrent(&hasCurrent)); + m_i = hasCurrent ? 0 : -1; + } + + // for end() + iterable_iterator(int /*currentIndex*/) : m_i(-1) + { + } + + iterable_iterator(const iterable_iterator& other) + { + m_iterator = other.m_iterator; + m_i = other.m_i; + err_policy::HResult(other.m_element.CopyTo(m_element.GetAddressOf())); + } + + iterable_iterator& operator=(const iterable_iterator& other) + { + m_iterator = other.m_iterator; + m_i = other.m_i; + err_policy::HResult(other.m_element.CopyTo(m_element.ReleaseAndGetAddressOf())); + return *this; + } + + bool operator==(iterable_iterator const& other) const + { + return m_i == other.m_i; + } + + bool operator!=(iterable_iterator const& other) const + { + return !operator==(other); + } + + reference operator*() + { + err_policy::HResult(m_iterator->get_Current(m_element.ReleaseAndGetAddressOf())); + return m_element; + } + + pointer operator->() + { + err_policy::HResult(m_iterator->get_Current(m_element.ReleaseAndGetAddressOf())); + return wistd::addressof(m_element); + } + + iterable_iterator& operator++() + { + boolean hasCurrent; + err_policy::HResult(m_iterator->MoveNext(&hasCurrent)); + if (hasCurrent) + { + m_i++; + } + else + { + m_i = -1; + } + return *this; + } + + iterable_iterator operator++(int) + { + iterable_iterator old(*this); + ++*this; + return old; + } + + private: + Microsoft::WRL::ComPtr> m_iterator; + int m_i; + TSmart m_element; + }; + + iterable_iterator begin() + { + return iterable_iterator(m_iterable); + } + + iterable_iterator end() + { + return iterable_iterator(); + } + private: + // weak, collection must outlive iterators. + ABI::Windows::Foundation::Collections::IIterable* m_iterable; + }; +#pragma endregion + +#pragma region error code base IIterable<> + template + class iterable_range_nothrow + { + public: + typedef typename details::MapIteratorResultType::type TResult; + typedef typename details::MapToSmartType::type TSmart; + + iterable_range_nothrow() = delete; + iterable_range_nothrow(const iterable_range_nothrow&) = delete; + iterable_range_nothrow& operator=(const iterable_range_nothrow&) = delete; + iterable_range_nothrow& operator=(iterable_range_nothrow &&) = delete; + + iterable_range_nothrow(iterable_range_nothrow&& other) : + m_iterator(wistd::move(other.m_iterator)), m_element(wistd::move(other.m_element)), + m_resultStorage(other.m_resultStorage) + { + if (other.m_result == &other.m_resultStorage) + { + m_result = &m_resultStorage; + } + else + { + m_result = other.m_result; + } + } + + iterable_range_nothrow(_In_ ABI::Windows::Foundation::Collections::IIterable* iterable, HRESULT* result = nullptr) + : m_result(result ? result : &m_resultStorage) + { + *m_result = iterable->First(&m_iterator); + if (SUCCEEDED(*m_result)) + { + boolean hasCurrent; + *m_result = m_iterator->get_HasCurrent(&hasCurrent); + if (SUCCEEDED(*m_result) && hasCurrent) + { + *m_result = m_iterator->get_Current(m_element.ReleaseAndGetAddressOf()); + if (FAILED(*m_result)) + { + m_iterator = nullptr; // release the iterator if no elements are found + } + } + else + { + m_iterator = nullptr; // release the iterator if no elements are found + } + } + } + + class iterable_iterator_nothrow + { + public: +#ifdef _XUTILITY_ + // muse be input_iterator_tag as use of one instance invalidates the other. + typedef ::std::input_iterator_tag iterator_category; +#endif + typedef TSmart value_type; + typedef ptrdiff_t difference_type; + typedef const TSmart* pointer; + typedef const TSmart& reference; + + iterable_iterator_nothrow(_In_ iterable_range_nothrow* range, int currentIndex) : + m_range(range), m_i(currentIndex) + { + } + + bool operator==(iterable_iterator_nothrow const& other) const + { + return FAILED(*m_range->m_result) || (m_i == other.m_i); + } + + bool operator!=(iterable_iterator_nothrow const& other) const + { + return !operator==(other); + } + + reference operator*() const WI_NOEXCEPT + { + return m_range->m_element; + } + + pointer operator->() const WI_NOEXCEPT + { + return wistd::addressof(m_range->m_element); + } + + iterable_iterator_nothrow& operator++() + { + boolean hasCurrent; + *m_range->m_result = m_range->m_iterator->MoveNext(&hasCurrent); + if (SUCCEEDED(*m_range->m_result) && hasCurrent) + { + *m_range->m_result = m_range->m_iterator->get_Current(m_range->m_element.ReleaseAndGetAddressOf()); + if (SUCCEEDED(*m_range->m_result)) + { + m_i++; + } + else + { + m_i = -1; + } + } + else + { + m_i = -1; + } + return *this; + } + + iterable_range_nothrow operator++(int) + { + iterable_range_nothrow old(*this); + ++*this; + return old; + } + + private: + iterable_range_nothrow* m_range; + int m_i; + }; + + iterable_iterator_nothrow begin() + { + return iterable_iterator_nothrow(this, this->m_iterator ? 0 : -1); + } + + iterable_iterator_nothrow end() + { + return iterable_iterator_nothrow(this, -1); + } + + private: + Microsoft::WRL::ComPtr> m_iterator; + // This state is shared by all iterator instances + // so use of one iterator can invalidate another's ability to dereference + // that is allowed for input iterators. + TSmart m_element; + HRESULT* m_result; + HRESULT m_resultStorage = S_OK; + }; + +#pragma endregion + +#ifdef WIL_ENABLE_EXCEPTIONS + template vector_range> get_range(ABI::Windows::Foundation::Collections::IVector *v) + { + return vector_range>(v); + } + + template vector_range> get_range(ABI::Windows::Foundation::Collections::IVectorView *v) + { + return vector_range>(v); + } +#endif // WIL_ENABLE_EXCEPTIONS + + template vector_range, err_failfast_policy> get_range_failfast(ABI::Windows::Foundation::Collections::IVector *v) + { + return vector_range, err_failfast_policy>(v); + } + + template vector_range, err_failfast_policy> get_range_failfast(ABI::Windows::Foundation::Collections::IVectorView *v) + { + return vector_range, err_failfast_policy>(v); + } + + template vector_range_nothrow> get_range_nothrow(ABI::Windows::Foundation::Collections::IVector *v, HRESULT* result = nullptr) + { + return vector_range_nothrow>(v, result); + } + + template vector_range_nothrow> get_range_nothrow(ABI::Windows::Foundation::Collections::IVectorView *v, HRESULT* result = nullptr) + { + return vector_range_nothrow>(v, result); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + template iterable_range get_range(ABI::Windows::Foundation::Collections::IIterable *v) + { + return iterable_range(v); + } +#endif // WIL_ENABLE_EXCEPTIONS + + template iterable_range get_range_failfast(ABI::Windows::Foundation::Collections::IIterable *v) + { + return iterable_range(v); + } + + template iterable_range_nothrow get_range_nothrow(ABI::Windows::Foundation::Collections::IIterable *v, HRESULT* result = nullptr) + { + return iterable_range_nothrow(v, result); + } +} + +#pragma endregion + +#ifdef WIL_ENABLE_EXCEPTIONS + +#pragma region Global operator functions +#if defined(MIDL_NS_PREFIX) || defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) +namespace ABI { +#endif + namespace Windows { + namespace Foundation { + namespace Collections { + template typename wil::vector_range>::vector_iterator begin(IVector* v) + { + return typename wil::vector_range>::vector_iterator(v, 0); + } + + template typename wil::vector_range>::vector_iterator end(IVector* v) + { + unsigned int size; + THROW_IF_FAILED(v->get_Size(&size)); + return typename wil::vector_range>::vector_iterator(v, size); + } + + template typename wil::vector_range>::vector_iterator begin(IVectorView* v) + { + return typename wil::vector_range>::vector_iterator(v, 0); + } + + template typename wil::vector_range>::vector_iterator end(IVectorView* v) + { + unsigned int size; + THROW_IF_FAILED(v->get_Size(&size)); + return typename wil::vector_range>::vector_iterator(v, size); + } + + template typename wil::iterable_range::iterable_iterator begin(IIterable* i) + { + return typename wil::iterable_range::iterable_iterator(i); + } + + template typename wil::iterable_range::iterable_iterator end(IIterable*) + { + return typename wil::iterable_range::iterable_iterator(); + } + } // namespace Collections + } // namespace Foundation + } // namespace Windows +#if defined(MIDL_NS_PREFIX) || defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) +} // namespace ABI +#endif + +#endif // WIL_ENABLE_EXCEPTIONS + +#pragma endregion + +namespace wil +{ +#pragma region WinRT Async API helpers + +/// @cond +namespace details +{ + template ::value, int>::type = 0> + HRESULT CallAndHandleErrorsWithReturnType(TFunc&& func, Args&&... args) + { + return wistd::forward(func)(wistd::forward(args)...); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + template ::value, int>::type = 0> + HRESULT CallAndHandleErrorsWithReturnType(TFunc&& func, Args&&... args) + { + try + { + wistd::forward(func)(wistd::forward(args)...); + } + CATCH_RETURN(); + return S_OK; + } +#endif + + template + HRESULT CallAndHandleErrors(TFunc&& func, Args&&... args) + { + return CallAndHandleErrorsWithReturnType(func)(wistd::forward(args)...))>( + wistd::forward(func), wistd::forward(args)...); + } + + // Get the last type of a template parameter pack. + // usage: + // LastType::type boolValue; + template struct LastType + { + template struct LastTypeOfTs + { + typedef typename LastTypeOfTs::type type; + }; + + template struct LastTypeOfTs + { + typedef T type; + }; + + template + static typename LastTypeOfTs::type LastTypeOfTsFunc() {} + typedef decltype(LastTypeOfTsFunc()) type; + }; + + // Takes a member function that has an out param like F(..., IAsyncAction**) or F(..., IAsyncOperation**) + // and returns IAsyncAction* or IAsyncOperation*. + template + typename wistd::remove_pointer::type>::type GetReturnParamPointerType(HRESULT(STDMETHODCALLTYPE I::*)(P...)); + + // Use to determine the result type of the async action/operation interfaces or example + // decltype(GetAsyncResultType(action.get())) returns void + void GetAsyncResultType(ABI::Windows::Foundation::IAsyncAction*); + template void GetAsyncResultType(ABI::Windows::Foundation::IAsyncActionWithProgress

*); + template typename wil::details::MapAsyncOpResultType::type GetAsyncResultType(ABI::Windows::Foundation::IAsyncOperation*); + template typename wil::details::MapAsyncOpProgressResultType::type GetAsyncResultType(ABI::Windows::Foundation::IAsyncOperationWithProgress*); + + // Use to determine the result type of the async action/operation interfaces or example + // decltype(GetAsyncDelegateType(action.get())) returns void + ABI::Windows::Foundation::IAsyncActionCompletedHandler* GetAsyncDelegateType(ABI::Windows::Foundation::IAsyncAction*); + template ABI::Windows::Foundation::IAsyncActionWithProgressCompletedHandler

* GetAsyncDelegateType(ABI::Windows::Foundation::IAsyncActionWithProgress

*); + template ABI::Windows::Foundation::IAsyncOperationCompletedHandler* GetAsyncDelegateType(ABI::Windows::Foundation::IAsyncOperation*); + template ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler* GetAsyncDelegateType(ABI::Windows::Foundation::IAsyncOperationWithProgress*); + + template + HRESULT RunWhenCompleteAction(_In_ TIOperation operation, TFunction&& func) WI_NOEXCEPT + { + using namespace Microsoft::WRL; + typedef wistd::remove_pointer_t TIDelegate; + + auto callback = Callback, TIDelegate, TBaseAgility>>( + [func = wistd::forward(func)](TIOperation operation, ABI::Windows::Foundation::AsyncStatus status) mutable -> HRESULT + { + HRESULT hr = S_OK; + if (status != ABI::Windows::Foundation::AsyncStatus::Completed) // avoid a potentially costly marshaled QI / call if we completed successfully + { + // QI to the IAsyncInfo interface. While all operations implement this, it is + // possible that the stub has disconnected, causing the QI to fail. + ComPtr asyncInfo; + hr = operation->QueryInterface(IID_PPV_ARGS(&asyncInfo)); + if (SUCCEEDED(hr)) + { + // Save the error code result in a temporary variable to allow us + // to also retrieve the result of the COM call. If the stub has + // disconnected, this call may fail. + HRESULT errorCode = E_UNEXPECTED; + hr = asyncInfo->get_ErrorCode(&errorCode); + if (SUCCEEDED(hr)) + { + // Return the operations error code to the caller. + hr = errorCode; + } + } + } + + return CallAndHandleErrors(func, hr); + }); + RETURN_IF_NULL_ALLOC(callback); + return operation->put_Completed(callback.Get()); + } + + template + HRESULT RunWhenComplete(_In_ TIOperation operation, TFunction&& func) WI_NOEXCEPT + { + using namespace Microsoft::WRL; + using namespace ABI::Windows::Foundation::Internal; + + typedef wistd::remove_pointer_t TIDelegate; + + auto callback = Callback, TIDelegate, TBaseAgility>>( + [func = wistd::forward(func)](TIOperation operation, ABI::Windows::Foundation::AsyncStatus status) mutable -> HRESULT + { + typename details::MapToSmartType::type::TResult_complex>::type>::type result; + + HRESULT hr = S_OK; + // avoid a potentially costly marshaled QI / call if we completed successfully + if (status == ABI::Windows::Foundation::AsyncStatus::Completed) + { + hr = operation->GetResults(result.GetAddressOf()); + } + else + { + // QI to the IAsyncInfo interface. While all operations implement this, it is + // possible that the stub has disconnected, causing the QI to fail. + ComPtr asyncInfo; + hr = operation->QueryInterface(IID_PPV_ARGS(&asyncInfo)); + if (SUCCEEDED(hr)) + { + // Save the error code result in a temporary variable to allow us + // to also retrieve the result of the COM call. If the stub has + // disconnected, this call may fail. + HRESULT errorCode = E_UNEXPECTED; + hr = asyncInfo->get_ErrorCode(&errorCode); + if (SUCCEEDED(hr)) + { + // Return the operations error code to the caller. + hr = errorCode; + } + } + } + + return CallAndHandleErrors(func, hr, result.Get()); + }); + RETURN_IF_NULL_ALLOC(callback); + return operation->put_Completed(callback.Get()); + } + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + template + HRESULT WaitForCompletion(_In_ TIOperation operation, COWAIT_FLAGS flags, DWORD timeoutValue, _Out_opt_ bool* timedOut) WI_NOEXCEPT + { + typedef wistd::remove_pointer_t TIDelegate; + + class CompletionDelegate : public Microsoft::WRL::RuntimeClass, + TIDelegate, Microsoft::WRL::FtmBase> + { + public: + HRESULT RuntimeClassInitialize() + { + RETURN_HR(m_completedEventHandle.create()); + } + + HRESULT STDMETHODCALLTYPE Invoke(_In_ TIOperation, ABI::Windows::Foundation::AsyncStatus status) override + { + m_status = status; + m_completedEventHandle.SetEvent(); + return S_OK; + } + + HANDLE GetEvent() const + { + return m_completedEventHandle.get(); + } + + ABI::Windows::Foundation::AsyncStatus GetStatus() const + { + return m_status; + } + + private: + volatile ABI::Windows::Foundation::AsyncStatus m_status = ABI::Windows::Foundation::AsyncStatus::Started; + wil::unique_event_nothrow m_completedEventHandle; + }; + + WI_ASSERT(timedOut || (timeoutValue == INFINITE)); + assign_to_opt_param(timedOut, false); + + Microsoft::WRL::ComPtr completedDelegate; + RETURN_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&completedDelegate)); + RETURN_IF_FAILED(operation->put_Completed(completedDelegate.Get())); + + HANDLE handles[] = { completedDelegate->GetEvent() }; + DWORD dwHandleIndex; + HRESULT hr = CoWaitForMultipleHandles(flags, timeoutValue, ARRAYSIZE(handles), handles, &dwHandleIndex); + + // If the caller is listening for timedOut, and we actually timed out, set the bool and return S_OK. Otherwise, fail. + if (timedOut && (hr == RPC_S_CALLPENDING)) + { + *timedOut = true; + return S_OK; + } + RETURN_IF_FAILED(hr); + + if (completedDelegate->GetStatus() != ABI::Windows::Foundation::AsyncStatus::Completed) + { + // QI to the IAsyncInfo interface. While all operations implement this, it is + // possible that the stub has disconnected, causing the QI to fail. + Microsoft::WRL::ComPtr asyncInfo; + hr = operation->QueryInterface(IID_PPV_ARGS(&asyncInfo)); + if (SUCCEEDED(hr)) + { + // Save the error code result in a temporary variable to allow us + // to also retrieve the result of the COM call. If the stub has + // disconnected, this call may fail. + HRESULT errorCode = E_UNEXPECTED; + hr = asyncInfo->get_ErrorCode(&errorCode); + if (SUCCEEDED(hr)) + { + // Return the operations error code to the caller. + hr = errorCode; + } + } + return hr; // leave it to the caller to log failures. + } + return S_OK; + } + + template + HRESULT WaitForCompletion(_In_ TIOperation operation, _Out_ TIResults result, COWAIT_FLAGS flags, + DWORD timeoutValue, _Out_opt_ bool* timedOut) WI_NOEXCEPT + { + RETURN_IF_FAILED_EXPECTED(details::WaitForCompletion(operation, flags, timeoutValue, timedOut)); + return operation->GetResults(result); + } +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) +} +/// @endcond + +/** Set the completion callback for an async operation to run a caller provided function. +Once complete the function is called with the error code result of the operation +and the async operation result (if applicable). +The function parameter list must be (HRESULT hr) for actions, +(HRESULT hr, IResultInterface* object) for operations that produce interfaces, +and (HRESULT hr, TResult value) for operations that produce value types. +~~~ +run_when_complete(getFileOp.Get(), [](HRESULT hr, IStorageFile* file) -> void +{ + +}); +~~~ +for an agile callback use Microsoft::WRL::FtmBase +~~~ +run_when_complete(getFileOp.Get(), [](HRESULT hr, IStorageFile* file) -> void +{ + +}); +~~~ +Using the non throwing form: +~~~ +hr = run_when_complete_nothrow(getFileOp.Get(), [](HRESULT hr, IStorageFile* file) -> HRESULT +{ + +}); +~~~ +*/ + +//! Run a fuction when an async operation completes. Use Microsoft::WRL::FtmBase for TAgility to make the completion handler agile and run on the async thread. +template +HRESULT run_when_complete_nothrow(_In_ ABI::Windows::Foundation::IAsyncAction* operation, TFunc&& func) WI_NOEXCEPT +{ + return details::RunWhenCompleteAction(operation, wistd::forward(func)); +} + +template::type> +HRESULT run_when_complete_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperation* operation, TFunc&& func) WI_NOEXCEPT +{ + return details::RunWhenComplete(operation, wistd::forward(func)); +} + +template::type> +HRESULT run_when_complete_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress* operation, TFunc&& func) WI_NOEXCEPT +{ + return details::RunWhenComplete(operation, wistd::forward(func)); +} + +template +HRESULT run_when_complete_nothrow(_In_ ABI::Windows::Foundation::IAsyncActionWithProgress* operation, TFunc&& func) WI_NOEXCEPT +{ + return details::RunWhenCompleteAction(operation, wistd::forward(func)); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +//! Run a fuction when an async operation completes. Use Microsoft::WRL::FtmBase for TAgility to make the completion handler agile and run on the async thread. +template +void run_when_complete(_In_ ABI::Windows::Foundation::IAsyncAction* operation, TFunc&& func) +{ + THROW_IF_FAILED((details::RunWhenCompleteAction(operation, wistd::forward(func)))); +} + +template::type> +void run_when_complete(_In_ ABI::Windows::Foundation::IAsyncOperation* operation, TFunc&& func) +{ + THROW_IF_FAILED((details::RunWhenComplete(operation, wistd::forward(func)))); +} + +template::type> +void run_when_complete(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress* operation, TFunc&& func) +{ + THROW_IF_FAILED((details::RunWhenComplete(operation, wistd::forward(func)))); +} + +template +void run_when_complete(_In_ ABI::Windows::Foundation::IAsyncActionWithProgress* operation, TFunc&& func) +{ + THROW_IF_FAILED((details::RunWhenCompleteAction(operation, wistd::forward(func)))); +} +#endif + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) +/** Wait for an asynchronous operation to complete (or be canceled). +Use to synchronously wait on async operations on background threads. +Do not call from UI threads or STA threads as reentrancy will result. +~~~ +ComPtr> op; +THROW_IF_FAILED(storageFileStatics->GetFileFromPathAsync(HStringReference(path).Get(), &op)); +auto file = wil::wait_for_completion(op.Get()); +~~~ +*/ +template +inline HRESULT wait_for_completion_nothrow(_In_ TAsync* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) WI_NOEXCEPT +{ + return details::WaitForCompletion(operation, flags, INFINITE, nullptr); +} + +// These forms return the result from the async operation + +template +HRESULT wait_for_completion_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperation* operation, + _Out_ typename wil::details::MapAsyncOpResultType::type* result, + COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) WI_NOEXCEPT +{ + return details::WaitForCompletion(operation, result, flags, INFINITE, nullptr); +} + +template +HRESULT wait_for_completion_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress* operation, + _Out_ typename wil::details::MapAsyncOpProgressResultType::type* result, + COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) WI_NOEXCEPT +{ + return details::WaitForCompletion(operation, result, flags, INFINITE, nullptr); +} + +// Same as above, but allows caller to specify a timeout value. +// On timeout, S_OK is returned, with timedOut set to true. + +template +inline HRESULT wait_for_completion_or_timeout_nothrow(_In_ TAsync* operation, + DWORD timeoutValue, _Out_ bool* timedOut, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) WI_NOEXCEPT +{ + return details::WaitForCompletion(operation, flags, timeoutValue, timedOut); +} + +template +HRESULT wait_for_completion_or_timeout_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperation* operation, + _Out_ typename wil::details::MapAsyncOpResultType::type* result, + DWORD timeoutValue, _Out_ bool* timedOut, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) WI_NOEXCEPT +{ + return details::WaitForCompletion(operation, result, flags, timeoutValue, timedOut); +} + +template +HRESULT wait_for_completion_or_timeout_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress* operation, + _Out_ typename wil::details::MapAsyncOpProgressResultType::type* result, + DWORD timeoutValue, _Out_ bool* timedOut, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) WI_NOEXCEPT +{ + return details::WaitForCompletion(operation, result, flags, timeoutValue, timedOut); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +//! Wait for an asynchronous operation to complete (or be canceled). +template +inline void wait_for_completion(_In_ TAsync* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) +{ + THROW_IF_FAILED(details::WaitForCompletion(operation, flags, INFINITE, nullptr)); +} + +template ::type>::type> +TReturn +wait_for_completion(_In_ ABI::Windows::Foundation::IAsyncOperation* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) +{ + TReturn result; + THROW_IF_FAILED(details::WaitForCompletion(operation, result.GetAddressOf(), flags, INFINITE, nullptr)); + return result; +} + +template ::type>::type> +TReturn +wait_for_completion(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS) +{ + TReturn result; + THROW_IF_FAILED(details::WaitForCompletion(operation, result.GetAddressOf(), flags, INFINITE, nullptr)); + return result; +} + +/** Similar to WaitForCompletion but this function encapsulates the creation of the async operation +making usage simpler. +~~~ +ComPtr launcher; // inited somewhere +auto result = call_and_wait_for_completion(launcher.Get(), &ILauncherStatics::LaunchUriAsync, uri.Get()); +~~~ +*/ +template +auto call_and_wait_for_completion(I* object, HRESULT(STDMETHODCALLTYPE I::*func)(P...), Args&&... args) +{ + Microsoft::WRL::ComPtr::type>::type>::type> op; + THROW_IF_FAILED((object->*func)(wistd::forward(args)..., &op)); + return wil::wait_for_completion(op.Get()); +} +#endif +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + +#pragma endregion + +#pragma region WinRT object construction +#ifdef WIL_ENABLE_EXCEPTIONS +//! Get a WinRT activation factory object, usually using a IXXXStatics interface. +template +com_ptr GetActivationFactory(PCWSTR runtimeClass) +{ + com_ptr result; + THROW_IF_FAILED(RoGetActivationFactory(Microsoft::WRL::Wrappers::HStringReference(runtimeClass).Get(), IID_PPV_ARGS(&result))); + return result; +} + +//! Get a WinRT object. +template +com_ptr ActivateInstance(PCWSTR runtimeClass) +{ + com_ptr result; + THROW_IF_FAILED(RoActivateInstance(Microsoft::WRL::Wrappers::HStringReference(runtimeClass).Get(), &result)); + return result.query(); +} +#endif +#pragma endregion + +#pragma region Async production helpers + +/// @cond +namespace details +{ + template + class SyncAsyncOp WrlFinal : public Microsoft::WRL::RuntimeClass, + Microsoft::WRL::AsyncBase>> + { + // typedef typename MapToSmartType::type TSmart; + using RuntimeClassT = typename SyncAsyncOp::RuntimeClassT; + InspectableClass(__super::z_get_rc_name_impl(), TrustLevel::BaseTrust); + public: + HRESULT RuntimeClassInitialize(const TResult& op) + { + m_result = op; + return S_OK; + } + + IFACEMETHODIMP put_Completed(ABI::Windows::Foundation::IAsyncOperationCompletedHandler* competed) override + { + competed->Invoke(this, ABI::Windows::Foundation::AsyncStatus::Completed); + return S_OK; + } + + IFACEMETHODIMP get_Completed(ABI::Windows::Foundation::IAsyncOperationCompletedHandler** competed) override + { + *competed = nullptr; + return S_OK; + } + + IFACEMETHODIMP GetResults(TResult* result) override + { + *result = m_result; + return S_OK; + } + + HRESULT OnStart() override { return S_OK; } + void OnClose() override { } + void OnCancel() override { } + private: + // needs to be MapToSmartType::type to hold non trial types + TResult m_result; + }; + + extern const __declspec(selectany) wchar_t SyncAsyncActionName[] = L"SyncActionAction"; + + class SyncAsyncActionOp WrlFinal : public Microsoft::WRL::RuntimeClass +#endif + >> + { + InspectableClass(InterfaceName_Windows_Foundation_IAsyncAction, TrustLevel::BaseTrust); + public: + IFACEMETHODIMP put_Completed(ABI::Windows::Foundation::IAsyncActionCompletedHandler* competed) override + { + competed->Invoke(this, ABI::Windows::Foundation::AsyncStatus::Completed); + return S_OK; + } + + IFACEMETHODIMP get_Completed(ABI::Windows::Foundation::IAsyncActionCompletedHandler** competed) override + { + *competed = nullptr; + return S_OK; + } + + IFACEMETHODIMP GetResults() override + { + return S_OK; + } + + HRESULT OnStart() override { return S_OK; } + void OnClose() override { } + void OnCancel() override { } + }; +} + +/// @endcond +//! Creates a WinRT async operation object that implements IAsyncOperation. Use mostly for testing and for mocking APIs. +template +HRESULT make_synchronous_async_operation_nothrow(ABI::Windows::Foundation::IAsyncOperation** result, const TResult& value) +{ + return Microsoft::WRL::MakeAndInitialize>(result, value); +} + +//! Creates a WinRT async operation object that implements IAsyncAction. Use mostly for testing and for mocking APIs. +inline HRESULT make_synchronous_async_action_nothrow(ABI::Windows::Foundation::IAsyncAction** result) +{ + return Microsoft::WRL::MakeAndInitialize(result); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +//! Creates a WinRT async operation object that implements IAsyncOperation. Use mostly for testing and for mocking APIs. +// TODO: map TRealResult and TSmartResult into SyncAsyncOp. +template ::type, typename TSmartResult = typename details::MapToSmartType::type> +void make_synchronous_async_operation(ABI::Windows::Foundation::IAsyncOperation** result, const TResult& value) +{ + THROW_IF_FAILED((Microsoft::WRL::MakeAndInitialize>(result, value))); +} + +//! Creates a WinRT async operation object that implements IAsyncAction. Use mostly for testing and for mocking APIs. +inline void make_synchronous_async_action(ABI::Windows::Foundation::IAsyncAction** result) +{ + THROW_IF_FAILED((Microsoft::WRL::MakeAndInitialize(result))); +} +#endif +#pragma endregion + +#pragma region EventRegistrationToken RAII wrapper + +// unique_winrt_event_token[_cx] is an RAII wrapper around EventRegistrationToken. When the unique_winrt_event_token[_cx] is +// destroyed, the event is automatically unregistered. Declare a wil::unique_winrt_event_token[_cx] at the scope the event +// should be registered for (often they are tied to object lifetime), where T is the type of the event sender +// wil::unique_winrt_event_token_cx m_token; +// +// Macros have been defined to register for handling the event and then returning an unique_winrt_event_token[_cx]. These +// macros simply hide the function references for adding and removing the event. +// C++/CX m_token = WI_MakeUniqueWinRtEventTokenCx(ExampleEventName, sender, handler); +// ABI m_token = WI_MakeUniqueWinRtEventToken(ExampleEventName, sender, handler, &m_token); // Exception and failfast +// ABI RETURN_IF_FAILED(WI_MakeUniqueWinRtEventTokenNoThrow(ExampleEventName, sender, handler, &m_token)); // No throw variant +// +// When the wrapper is destroyed, the handler will be unregistered. You can explicitly unregister the handler prior. +// m_token.reset(); +// +// You can release the EventRegistrationToken from being managed by the wrapper by calling .release() +// m_token.release(); // DANGER: no longer being managed +// +// If you just need the value of the EventRegistrationToken you can call .get() +// m_token.get(); +// +// See "onecore\shell\tests\wil\UniqueWinRTEventTokenTests.cpp" for more examples of usage in ABI and C++/CX. + +#ifdef __cplusplus_winrt +namespace details +{ + template struct remove_reference { typedef T type; }; + template struct remove_reference { typedef T type; }; +} + +template +class unique_winrt_event_token_cx +{ + using removal_func = void(T::*)(Windows::Foundation::EventRegistrationToken); + using static_removal_func = void(__cdecl *)(Windows::Foundation::EventRegistrationToken); + +public: + unique_winrt_event_token_cx() = default; + + unique_winrt_event_token_cx(Windows::Foundation::EventRegistrationToken token, T^ sender, removal_func removalFunction) WI_NOEXCEPT : + m_token(token), + m_weakSender(sender), + m_removalFunction(removalFunction) + {} + + unique_winrt_event_token_cx(Windows::Foundation::EventRegistrationToken token, static_removal_func removalFunction) WI_NOEXCEPT : + m_token(token), + m_staticRemovalFunction(removalFunction) + {} + + unique_winrt_event_token_cx(const unique_winrt_event_token_cx&) = delete; + unique_winrt_event_token_cx& operator=(const unique_winrt_event_token_cx&) = delete; + + unique_winrt_event_token_cx(unique_winrt_event_token_cx&& other) WI_NOEXCEPT : + m_token(other.m_token), + m_weakSender(wistd::move(other.m_weakSender)), + m_removalFunction(other.m_removalFunction), + m_staticRemovalFunction(other.m_staticRemovalFunction) + { + other.m_token = {}; + other.m_weakSender = nullptr; + other.m_removalFunction = nullptr; + other.m_staticRemovalFunction = nullptr; + } + + unique_winrt_event_token_cx& operator=(unique_winrt_event_token_cx&& other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + reset(); + + wistd::swap_wil(m_token, other.m_token); + wistd::swap_wil(m_weakSender, other.m_weakSender); + wistd::swap_wil(m_removalFunction, other.m_removalFunction); + wistd::swap_wil(m_staticRemovalFunction, other.m_staticRemovalFunction); + } + + return *this; + } + + ~unique_winrt_event_token_cx() WI_NOEXCEPT + { + reset(); + } + + explicit operator bool() const WI_NOEXCEPT + { + return (m_token.Value != 0); + } + + Windows::Foundation::EventRegistrationToken get() const WI_NOEXCEPT + { + return m_token; + } + + void reset() noexcept + { + if (m_token.Value != 0) + { + if (m_staticRemovalFunction) + { + (*m_staticRemovalFunction)(m_token); + } + else + { + auto resolvedSender = [&]() + { + try + { + return m_weakSender.Resolve(); + } + catch (...) + { + // Ignore RPC or other failures that are unavoidable in some cases + // matching wil::unique_winrt_event_token and winrt::event_revoker + return static_cast(nullptr); + } + }(); + if (resolvedSender) + { + (resolvedSender->*m_removalFunction)(m_token); + } + } + release(); + } + } + + // Stops the wrapper from managing resource and returns the EventRegistrationToken. + Windows::Foundation::EventRegistrationToken release() WI_NOEXCEPT + { + auto token = m_token; + m_token = {}; + m_weakSender = nullptr; + m_removalFunction = nullptr; + m_staticRemovalFunction = nullptr; + return token; + } + +private: + Windows::Foundation::EventRegistrationToken m_token = {}; + Platform::WeakReference m_weakSender; + removal_func m_removalFunction = nullptr; + static_removal_func m_staticRemovalFunction = nullptr; +}; + +#endif + +template +class unique_winrt_event_token +{ + using removal_func = HRESULT(__stdcall T::*)(::EventRegistrationToken); + +public: + unique_winrt_event_token() = default; + + unique_winrt_event_token(::EventRegistrationToken token, T* sender, removal_func removalFunction) WI_NOEXCEPT : + m_token(token), + m_removalFunction(removalFunction) + { + m_weakSender = wil::com_weak_query_failfast(sender); + } + + unique_winrt_event_token(const unique_winrt_event_token&) = delete; + unique_winrt_event_token& operator=(const unique_winrt_event_token&) = delete; + + unique_winrt_event_token(unique_winrt_event_token&& other) WI_NOEXCEPT : + m_token(other.m_token), + m_weakSender(wistd::move(other.m_weakSender)), + m_removalFunction(other.m_removalFunction) + { + other.m_token = {}; + other.m_removalFunction = nullptr; + } + + unique_winrt_event_token& operator=(unique_winrt_event_token&& other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + reset(); + + wistd::swap_wil(m_token, other.m_token); + wistd::swap_wil(m_weakSender, other.m_weakSender); + wistd::swap_wil(m_removalFunction, other.m_removalFunction); + } + + return *this; + } + + ~unique_winrt_event_token() WI_NOEXCEPT + { + reset(); + } + + explicit operator bool() const WI_NOEXCEPT + { + return (m_token.value != 0); + } + + ::EventRegistrationToken get() const WI_NOEXCEPT + { + return m_token; + } + + void reset() WI_NOEXCEPT + { + if (m_token.value != 0) + { + // If T cannot be QI'ed from the weak object then T is not a COM interface. + auto resolvedSender = m_weakSender.try_query(); + if (resolvedSender) + { + FAIL_FAST_IF_FAILED((resolvedSender.get()->*m_removalFunction)(m_token)); + } + release(); + } + } + + // Stops the wrapper from managing resource and returns the EventRegistrationToken. + ::EventRegistrationToken release() WI_NOEXCEPT + { + auto token = m_token; + m_token = {}; + m_weakSender = nullptr; + m_removalFunction = nullptr; + return token; + } + +private: + ::EventRegistrationToken m_token = {}; + wil::com_weak_ref_failfast m_weakSender; + removal_func m_removalFunction = nullptr; +}; + +namespace details +{ +#ifdef __cplusplus_winrt + + // Handles registration of the event handler to the subscribing object and then wraps the EventRegistrationToken in unique_winrt_event_token. + // Not intended to be directly called. Use the WI_MakeUniqueWinRtEventTokenCx macro to abstract away specifying the functions that handle addition and removal. + template + inline wil::unique_winrt_event_token_cx make_unique_winrt_event_token_cx(T^ sender, addition_func additionFunc, removal_func removalFunc, handler^ h) + { + auto rawToken = (sender->*additionFunc)(h); + wil::unique_winrt_event_token_cx temp(rawToken, sender, removalFunc); + return temp; + } + + template + inline wil::unique_winrt_event_token_cx make_unique_winrt_static_event_token_cx(addition_func additionFunc, removal_func removalFunc, handler^ h) + { + auto rawToken = (*additionFunc)(h); + wil::unique_winrt_event_token_cx temp(rawToken, removalFunc); + return temp; + } + +#endif // __cplusplus_winrt + + // Handles registration of the event handler to the subscribing object and then wraps the EventRegistrationToken in unique_winrt_event_token. + // Not intended to be directly called. Use the WI_MakeUniqueWinRtEventToken macro to abstract away specifying the functions that handle addition and removal. + template + inline auto make_unique_winrt_event_token(T* sender, addition_func additionFunc, removal_func removalFunc, handler h, wil::unique_winrt_event_token* token_reference) + { + ::EventRegistrationToken rawToken; + err_policy::HResult((sender->*additionFunc)(h, &rawToken)); + *token_reference = wil::unique_winrt_event_token(rawToken, sender, removalFunc); + return err_policy::OK(); + } + + // Overload make function to allow for returning the constructed object when not using HRESULT based code. + template + inline typename wistd::enable_if::value, wil::unique_winrt_event_token>::type + make_unique_winrt_event_token(T* sender, addition_func additionFunc, removal_func removalFunc, handler h) + { + ::EventRegistrationToken rawToken; + err_policy::HResult((sender->*additionFunc)(h, &rawToken)); + return wil::unique_winrt_event_token(rawToken, sender, removalFunc); + } + +} // namespace details + +// Helper macros to abstract function names for event addition and removal. +#ifdef __cplusplus_winrt + +#define WI_MakeUniqueWinRtEventTokenCx(_event, _object, _handler) \ + wil::details::make_unique_winrt_event_token_cx( \ + _object, \ + &wil::details::remove_reference::type::##_event##::add, \ + &wil::details::remove_reference::type::##_event##::remove, \ + _handler) + +#define WI_MakeUniqueWinRtStaticEventTokenCx(_event, _baseType, _handler) \ + wil::details::make_unique_winrt_static_event_token_cx<_baseType>( \ + &##_baseType##::##_event##::add, \ + &##_baseType##::##_event##::remove, \ + _handler) + +#endif // __cplusplus_winrt + +#ifdef WIL_ENABLE_EXCEPTIONS + +#define WI_MakeUniqueWinRtEventToken(_event, _object, _handler) \ + wil::details::make_unique_winrt_event_token( \ + _object, \ + &wistd::remove_pointer::type::add_##_event, \ + &wistd::remove_pointer::type::remove_##_event, \ + _handler) + +#endif // WIL_ENABLE_EXCEPTIONS + +#define WI_MakeUniqueWinRtEventTokenNoThrow(_event, _object, _handler, _token_reference) \ + wil::details::make_unique_winrt_event_token( \ + _object, \ + &wistd::remove_pointer::type::add_##_event, \ + &wistd::remove_pointer::type::remove_##_event, \ + _handler, \ + _token_reference) + +#define WI_MakeUniqueWinRtEventTokenFailFast(_event, _object, _handler) \ + wil::details::make_unique_winrt_event_token( \ + _object, \ + &wistd::remove_pointer::type::add_##_event, \ + &wistd::remove_pointer::type::remove_##_event, \ + _handler) + +#pragma endregion // EventRegistrationToken RAII wrapper + +} // namespace wil + +#if (NTDDI_VERSION >= NTDDI_WINBLUE) + +template <> +struct ABI::Windows::Foundation::IAsyncOperation : + ABI::Windows::Foundation::IAsyncOperation_impl +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperation"; + } +}; + +template +struct ABI::Windows::Foundation::IAsyncOperationWithProgress : + ABI::Windows::Foundation::IAsyncOperationWithProgress_impl +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperationWithProgress"; + } +}; + +template +struct ABI::Windows::Foundation::IAsyncOperation*> : + ABI::Windows::Foundation::IAsyncOperation_impl*> +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperation*>"; + } +}; + +template +struct ABI::Windows::Foundation::IAsyncOperationWithProgress*, P> : + ABI::Windows::Foundation::IAsyncOperationWithProgress_impl*, P> +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperationWithProgress*,P>"; + } +}; + +template +struct ABI::Windows::Foundation::IAsyncOperation*> : + ABI::Windows::Foundation::IAsyncOperation_impl*> +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperation*>"; + } +}; + +template +struct ABI::Windows::Foundation::IAsyncOperationWithProgress*, Z> : + ABI::Windows::Foundation::IAsyncOperationWithProgress_impl*, Z> +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperationWithProgress*,Z>"; + } +}; + +template <> +struct ABI::Windows::Foundation::IAsyncOperationCompletedHandler : + ABI::Windows::Foundation::IAsyncOperationCompletedHandler_impl +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperationCompletedHandler"; + } +}; + +template +struct ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler : + ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler_impl +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperationWithProgressCompletedHandler"; + } +}; + +template +struct ABI::Windows::Foundation::IAsyncOperationCompletedHandler*> : + ABI::Windows::Foundation::IAsyncOperationCompletedHandler_impl*> +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperationCompletedHandler*>"; + } +}; + +template +struct ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler*, P> : + ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler_impl*, P> +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperationWithProgressCompletedHandler*,P>"; + } +}; + +template +struct ABI::Windows::Foundation::IAsyncOperationCompletedHandler*> : + ABI::Windows::Foundation::IAsyncOperationCompletedHandler_impl*> +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperationCompletedHandler*>"; + } +}; + +template +struct ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler*, Z> : + ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler_impl*, Z> +{ + static const wchar_t* z_get_rc_name_impl() + { + return L"IAsyncOperationWithProgressCompletedHandler*,Z>"; + } +}; +#endif // NTDDI_VERSION >= NTDDI_WINBLUE + +#if !defined(MIDL_NS_PREFIX) && !defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) +// Internal .idl files use the namespace without the ABI prefix. Macro out ABI for that case +#pragma pop_macro("ABI") +#endif + +#if __WI_HAS_STD_LESS + +namespace std +{ + //! Specialization of `std::less` for `Microsoft::WRL::Wrappers::HString` that uses `hstring_less` for the + //! comparison function object. + template <> + struct less : + public wil::hstring_less + { + }; + + //! Specialization of `std::less` for `wil::unique_hstring` that uses `hstring_less` for the comparison function + //! object. + template <> + struct less : + public wil::hstring_less + { + }; +} + +#endif + +#endif // __WIL_WINRT_INCLUDED diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wistd_config.h b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wistd_config.h new file mode 100644 index 0000000..6d04a1c --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wistd_config.h @@ -0,0 +1,548 @@ +// -*- C++ -*- +//===--------------------------- __config ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// STL common functionality +// +// Some aspects of STL are core language concepts that should be used from all C++ code, regardless +// of whether exceptions are enabled in the component. Common library code that expects to be used +// from exception-free components want these concepts, but including STL headers directly introduces +// friction as it requires components not using STL to declare their STL version. Doing so creates +// ambiguity around whether STL use is safe in a particular component and implicitly brings in +// a long list of headers (including ) which can create further ambiguity around throwing new +// support (some routines pulled in may expect it). Secondarily, pulling in these headers also has +// the potential to create naming conflicts or other implied dependencies. +// +// To promote the use of these core language concepts outside of STL-based binaries, this file is +// selectively pulling those concepts *directly* from corresponding STL headers. The corresponding +// "std::" namespace STL functions and types should be preferred over these in code that is bound to +// STL. The implementation and naming of all functions are taken directly from STL, instead using +// "wistd" (Windows Implementation std) as the namespace. +// +// Routines in this namespace should always be considered a reflection of the *current* STL implementation +// of those routines. Updates from STL should be taken, but no "bugs" should be fixed here. +// +// New, exception-based code should not use this namespace, but instead should prefer the std:: implementation. +// Only code that is not exception-based and libraries that expect to be utilized across both exception +// and non-exception based code should utilize this functionality. + +// This header mimics libc++'s '__config' header to the extent necessary to get the wistd::* definitions compiling. Note +// that this has a few key differences since libc++'s MSVC compatability is currently not functional and a bit behind + +#ifndef _WISTD_CONFIG_H_ +#define _WISTD_CONFIG_H_ + +// DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage +#include // For size_t and other necessary types + +/// @cond +#if defined(_MSC_VER) && !defined(__clang__) +# if !defined(__WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# define __WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER +# endif +#endif + +#ifndef __WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER +#pragma GCC system_header +#endif + +#ifdef __GNUC__ +# define __WI_GNUC_VER (__GNUC__ * 100 + __GNUC_MINOR__) +// The __WI_GNUC_VER_NEW macro better represents the new GCC versioning scheme +// introduced in GCC 5.0. +# define __WI_GNUC_VER_NEW (__WI_GNUC_VER * 10 + __GNUC_PATCHLEVEL__) +#else +# define __WI_GNUC_VER 0 +# define __WI_GNUC_VER_NEW 0 +#endif + +// _MSVC_LANG is the more accurate way to get the C++ version in MSVC +#if defined(_MSVC_LANG) && (_MSVC_LANG > __cplusplus) +#define __WI_CPLUSPLUS _MSVC_LANG +#else +#define __WI_CPLUSPLUS __cplusplus +#endif + +#ifndef __WI_LIBCPP_STD_VER +# if __WI_CPLUSPLUS <= 201103L +# define __WI_LIBCPP_STD_VER 11 +# elif __WI_CPLUSPLUS <= 201402L +# define __WI_LIBCPP_STD_VER 14 +# elif __WI_CPLUSPLUS <= 201703L +# define __WI_LIBCPP_STD_VER 17 +# else +# define __WI_LIBCPP_STD_VER 18 // current year, or date of c++2a ratification +# endif +#endif // __WI_LIBCPP_STD_VER + +#if __WI_CPLUSPLUS < 201103L +#define __WI_LIBCPP_CXX03_LANG +#endif + +#if defined(__ELF__) +# define __WI_LIBCPP_OBJECT_FORMAT_ELF 1 +#elif defined(__MACH__) +# define __WI_LIBCPP_OBJECT_FORMAT_MACHO 1 +#elif defined(_WIN32) +# define __WI_LIBCPP_OBJECT_FORMAT_COFF 1 +#elif defined(__wasm__) +# define __WI_LIBCPP_OBJECT_FORMAT_WASM 1 +#else +# error Unknown object file format +#endif + +#if defined(__clang__) +# define __WI_LIBCPP_COMPILER_CLANG +#elif defined(__GNUC__) +# define __WI_LIBCPP_COMPILER_GCC +#elif defined(_MSC_VER) +# define __WI_LIBCPP_COMPILER_MSVC +#elif defined(__IBMCPP__) +# define __WI_LIBCPP_COMPILER_IBM +#endif + +// NOTE: MSVC, which is what we primarily target, is severly underrepresented in libc++ and checks such as +// __has_feature(...) are always false for MSVC, even when the feature being tested _is_ present in MSVC. Therefore, we +// instead modify all checks to be __WI_HAS_FEATURE_IS_UNION, etc., which provides the correct value for MSVC and falls +// back to the __has_feature(...), etc. value otherwise. We intentionally leave '__has_feature', etc. undefined for MSVC +// so that we don't accidentally use the incorrect behavior +#ifndef __WI_LIBCPP_COMPILER_MSVC + +#ifndef __has_feature +#define __has_feature(__x) 0 +#endif + +// '__is_identifier' returns '0' if '__x' is a reserved identifier provided by +// the compiler and '1' otherwise. +#ifndef __is_identifier +#define __is_identifier(__x) 1 +#endif + +#ifndef __has_cpp_attribute +#define __has_cpp_attribute(__x) 0 +#endif + +#ifndef __has_attribute +#define __has_attribute(__x) 0 +#endif + +#ifndef __has_builtin +#define __has_builtin(__x) 0 +#endif + +#if __has_feature(cxx_alignas) +# define __WI_ALIGNAS_TYPE(x) alignas(x) +# define __WI_ALIGNAS(x) alignas(x) +#else +# define __WI_ALIGNAS_TYPE(x) __attribute__((__aligned__(__alignof(x)))) +# define __WI_ALIGNAS(x) __attribute__((__aligned__(x))) +#endif + +#if __has_feature(cxx_explicit_conversions) || defined(__IBMCPP__) || \ + (!defined(__WI_LIBCPP_CXX03_LANG) && defined(__GNUC__)) // All supported GCC versions +# define __WI_LIBCPP_EXPLICIT explicit +#else +# define __WI_LIBCPP_EXPLICIT +#endif + +#if __has_feature(cxx_attributes) +# define __WI_LIBCPP_NORETURN [[noreturn]] +#else +# define __WI_LIBCPP_NORETURN __attribute__ ((noreturn)) +#endif + +#define __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS +#define __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS + +// The __WI_LIBCPP_NODISCARD_ATTRIBUTE should only be used to define other +// NODISCARD macros to the correct attribute. +#if __has_cpp_attribute(nodiscard) +# define __WI_LIBCPP_NODISCARD_ATTRIBUTE [[nodiscard]] +#elif defined(__WI_LIBCPP_COMPILER_CLANG) && !defined(__WI_LIBCPP_CXX03_LANG) +# define __WI_LIBCPP_NODISCARD_ATTRIBUTE [[clang::warn_unused_result]] +#else +// We can't use GCC's [[gnu::warn_unused_result]] and +// __attribute__((warn_unused_result)), because GCC does not silence them via +// (void) cast. +# define __WI_LIBCPP_NODISCARD_ATTRIBUTE +#endif + +#define __WI_HAS_FEATURE_IS_UNION __has_feature(is_union) +#define __WI_HAS_FEATURE_IS_CLASS __has_feature(is_class) +#define __WI_HAS_FEATURE_IS_ENUM __has_feature(is_enum) +#define __WI_HAS_FEATURE_IS_CONVERTIBLE_TO __has_feature(is_convertible_to) +#define __WI_HAS_FEATURE_IS_EMPTY __has_feature(is_empty) +#define __WI_HAS_FEATURE_IS_POLYMORPHIC __has_feature(is_polymorphic) +#define __WI_HAS_FEATURE_HAS_VIRTUAL_DESTRUCTOR __has_feature(has_virtual_destructor) +#define __WI_HAS_FEATURE_REFERENCE_QUALIFIED_FUNCTIONS __has_feature(cxx_reference_qualified_functions) +#define __WI_HAS_FEATURE_IS_CONSTRUCTIBLE __has_feature(is_constructible) +#define __WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE __has_feature(is_trivially_constructible) +#define __WI_HAS_FEATURE_IS_TRIVIALLY_ASSIGNABLE __has_feature(is_trivially_assignable) +#define __WI_HAS_FEATURE_HAS_TRIVIAL_DESTRUCTOR __has_feature(has_trivial_destructor) +#define __WI_HAS_FEATURE_NOEXCEPT __has_feature(cxx_noexcept) +#define __WI_HAS_FEATURE_IS_POD __has_feature(is_pod) +#define __WI_HAS_FEATURE_IS_STANDARD_LAYOUT __has_feature(is_standard_layout) +#define __WI_HAS_FEATURE_IS_TRIVIALLY_COPYABLE __has_feature(is_trivially_copyable) +#define __WI_HAS_FEATURE_IS_TRIVIAL __has_feature(is_trivial) +#define __WI_HAS_FEATURE_HAS_TRIVIAL_CONSTRUCTOR __has_feature(has_trivial_constructor) || (__WI_GNUC_VER >= 403) +#define __WI_HAS_FEATURE_HAS_NOTHROW_CONSTRUCTOR __has_feature(has_nothrow_constructor) || (__WI_GNUC_VER >= 403) +#define __WI_HAS_FEATURE_HAS_NOTHROW_COPY __has_feature(has_nothrow_copy) || (__WI_GNUC_VER >= 403) +#define __WI_HAS_FEATURE_HAS_NOTHROW_ASSIGN __has_feature(has_nothrow_assign) || (__WI_GNUC_VER >= 403) + +#if !(__has_feature(cxx_noexcept)) +#define __WI_LIBCPP_HAS_NO_NOEXCEPT +#endif + +#if !__is_identifier(__has_unique_object_representations) || __WI_GNUC_VER >= 700 +#define __WI_LIBCPP_HAS_UNIQUE_OBJECT_REPRESENTATIONS +#endif + +#if !(__has_feature(cxx_variadic_templates)) +#define __WI_LIBCPP_HAS_NO_VARIADICS +#endif + +#if __has_feature(is_literal) || __WI_GNUC_VER >= 407 +#define __WI_LIBCPP_IS_LITERAL(T) __is_literal(T) +#endif + +#if __has_feature(underlying_type) || __WI_GNUC_VER >= 407 +#define __WI_LIBCPP_UNDERLYING_TYPE(T) __underlying_type(T) +#endif + +#if __has_feature(is_final) || __WI_GNUC_VER >= 407 +#define __WI_LIBCPP_HAS_IS_FINAL +#endif + +#if __has_feature(is_base_of) || defined(__GNUC__) && __WI_GNUC_VER >= 403 +#define __WI_LIBCPP_HAS_IS_BASE_OF +#endif + +#if __is_identifier(__is_aggregate) && (__WI_GNUC_VER_NEW < 7001) +#define __WI_LIBCPP_HAS_NO_IS_AGGREGATE +#endif + +#if !(__has_feature(cxx_rtti)) && !defined(__WI_LIBCPP_NO_RTTI) +#define __WI_LIBCPP_NO_RTTI +#endif + +#if !(__has_feature(cxx_variable_templates)) +#define __WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES +#endif + +#if !(__has_feature(cxx_relaxed_constexpr)) +#define __WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR +#endif + +#if !__has_builtin(__builtin_addressof) && _GNUC_VER < 700 +#define __WI_LIBCPP_HAS_NO_BUILTIN_ADDRESSOF +#endif + +#if __has_attribute(__no_sanitize__) && !defined(__WI_LIBCPP_COMPILER_GCC) +# define __WI_LIBCPP_NO_CFI __attribute__((__no_sanitize__("cfi"))) +#else +# define __WI_LIBCPP_NO_CFI +#endif + +#define __WI_LIBCPP_ALWAYS_INLINE __attribute__ ((__always_inline__)) + +#if __has_attribute(internal_linkage) +# define __WI_LIBCPP_INTERNAL_LINKAGE __attribute__ ((internal_linkage)) +#else +# define __WI_LIBCPP_INTERNAL_LINKAGE __WI_LIBCPP_ALWAYS_INLINE +#endif + +#else + +// NOTE: Much of the following assumes a decently recent version of MSVC. Past versions can be supported, but will need +// to be updated to contain the proper _MSC_VER check +#define __WI_ALIGNAS_TYPE(x) alignas(x) +#define __WI_ALIGNAS(x) alignas(x) +#define __alignof__ __alignof + +#define __WI_LIBCPP_EXPLICIT explicit +#define __WI_LIBCPP_NORETURN [[noreturn]] +#define __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS __pragma(warning(suppress:26495)) +#define __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS __pragma(warning(suppress:26439)) + + +#if __WI_LIBCPP_STD_VER > 14 +#define __WI_LIBCPP_NODISCARD_ATTRIBUTE [[nodiscard]] +#else +#define __WI_LIBCPP_NODISCARD_ATTRIBUTE _Check_return_ +#endif + +#define __WI_HAS_FEATURE_IS_UNION 1 +#define __WI_HAS_FEATURE_IS_CLASS 1 +#define __WI_HAS_FEATURE_IS_ENUM 1 +#define __WI_HAS_FEATURE_IS_CONVERTIBLE_TO 1 +#define __WI_HAS_FEATURE_IS_EMPTY 1 +#define __WI_HAS_FEATURE_IS_POLYMORPHIC 1 +#define __WI_HAS_FEATURE_HAS_VIRTUAL_DESTRUCTOR 1 +#define __WI_LIBCPP_HAS_UNIQUE_OBJECT_REPRESENTATIONS 1 +#define __WI_HAS_FEATURE_REFERENCE_QUALIFIED_FUNCTIONS 1 +#define __WI_HAS_FEATURE_IS_CONSTRUCTIBLE 1 +#define __WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE 1 +#define __WI_HAS_FEATURE_IS_TRIVIALLY_ASSIGNABLE 1 +#define __WI_HAS_FEATURE_HAS_TRIVIAL_DESTRUCTOR 1 +#define __WI_HAS_FEATURE_NOEXCEPT 1 +#define __WI_HAS_FEATURE_IS_POD 1 +#define __WI_HAS_FEATURE_IS_STANDARD_LAYOUT 1 +#define __WI_HAS_FEATURE_IS_TRIVIALLY_COPYABLE 1 +#define __WI_HAS_FEATURE_IS_TRIVIAL 1 +#define __WI_HAS_FEATURE_HAS_TRIVIAL_CONSTRUCTOR 1 +#define __WI_HAS_FEATURE_HAS_NOTHROW_CONSTRUCTOR 1 +#define __WI_HAS_FEATURE_HAS_NOTHROW_COPY 1 +#define __WI_HAS_FEATURE_HAS_NOTHROW_ASSIGN 1 +#define __WI_HAS_FEATURE_IS_DESTRUCTIBLE 1 + +#if !defined(_CPPRTTI) && !defined(__WI_LIBCPP_NO_RTTI) +#define __WI_LIBCPP_NO_RTTI +#endif + +#define __WI_LIBCPP_IS_LITERAL(T) __is_literal_type(T) +#define __WI_LIBCPP_UNDERLYING_TYPE(T) __underlying_type(T) +#define __WI_LIBCPP_HAS_IS_FINAL +#define __WI_LIBCPP_HAS_IS_BASE_OF + +#if __WI_LIBCPP_STD_VER < 14 +#define __WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES +#endif + +#define __WI_LIBCPP_HAS_NO_BUILTIN_ADDRESSOF +#define __WI_LIBCPP_NO_CFI + +#define __WI_LIBCPP_ALWAYS_INLINE __forceinline +#define __WI_LIBCPP_INTERNAL_LINKAGE + +#endif + +#ifndef _WIN32 + +#ifdef __LITTLE_ENDIAN__ +# if __LITTLE_ENDIAN__ +# define __WI_LIBCPP_LITTLE_ENDIAN +# endif // __LITTLE_ENDIAN__ +#endif // __LITTLE_ENDIAN__ + +#ifdef __BIG_ENDIAN__ +# if __BIG_ENDIAN__ +# define __WI_LIBCPP_BIG_ENDIAN +# endif // __BIG_ENDIAN__ +#endif // __BIG_ENDIAN__ + +#ifdef __BYTE_ORDER__ +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define __WI_LIBCPP_LITTLE_ENDIAN +# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define __WI_LIBCPP_BIG_ENDIAN +# endif // __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#endif // __BYTE_ORDER__ + +#if !defined(__WI_LIBCPP_LITTLE_ENDIAN) && !defined(__WI_LIBCPP_BIG_ENDIAN) +# include +# if __BYTE_ORDER == __LITTLE_ENDIAN +# define __WI_LIBCPP_LITTLE_ENDIAN +# elif __BYTE_ORDER == __BIG_ENDIAN +# define __WI_LIBCPP_BIG_ENDIAN +# else // __BYTE_ORDER == __BIG_ENDIAN +# error unable to determine endian +# endif +#endif // !defined(__WI_LIBCPP_LITTLE_ENDIAN) && !defined(__WI_LIBCPP_BIG_ENDIAN) + +#else // _WIN32 + +#define __WI_LIBCPP_LITTLE_ENDIAN + +#endif // _WIN32 + +#ifdef __WI_LIBCPP_HAS_NO_CONSTEXPR +# define __WI_LIBCPP_CONSTEXPR +#else +# define __WI_LIBCPP_CONSTEXPR constexpr +#endif + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR) +# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 constexpr +#else +# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 +#endif + +#if __WI_LIBCPP_STD_VER > 14 && !defined(__WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR) +# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX14 constexpr +#else +# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX14 +#endif + +#if __WI_LIBCPP_STD_VER > 17 && !defined(__WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR) +# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX17 constexpr +#else +# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX17 +#endif + +#if !defined(__WI_LIBCPP_DISABLE_NODISCARD_AFTER_CXX17) && \ + (__WI_LIBCPP_STD_VER > 17 || defined(__WI_LIBCPP_ENABLE_NODISCARD)) +# define __WI_LIBCPP_NODISCARD_AFTER_CXX17 __WI_LIBCPP_NODISCARD_ATTRIBUTE +#else +# define __WI_LIBCPP_NODISCARD_AFTER_CXX17 +#endif + +#if __WI_LIBCPP_STD_VER > 14 && defined(__cpp_inline_variables) && (__cpp_inline_variables >= 201606L) +# define __WI_LIBCPP_INLINE_VAR inline +#else +# define __WI_LIBCPP_INLINE_VAR +#endif + +#ifdef __WI_LIBCPP_CXX03_LANG +#define __WI_LIBCPP_HAS_NO_UNICODE_CHARS +#define __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES +#endif + +#ifndef __SIZEOF_INT128__ +#define __WI_LIBCPP_HAS_NO_INT128 +#endif + +#if !__WI_HAS_FEATURE_NOEXCEPT && !defined(__WI_LIBCPP_HAS_NO_NOEXCEPT) +#define __WI_LIBCPP_HAS_NO_NOEXCEPT +#endif + +#ifndef __WI_LIBCPP_HAS_NO_NOEXCEPT +# define WI_NOEXCEPT noexcept +# define __WI_NOEXCEPT_(x) noexcept(x) +#else +# define WI_NOEXCEPT throw() +# define __WI_NOEXCEPT_(x) +#endif + +#if defined(__WI_LIBCPP_OBJECT_FORMAT_COFF) +#define __WI_LIBCPP_HIDDEN +#define __WI_LIBCPP_TEMPLATE_VIS +#endif // defined(__WI_LIBCPP_OBJECT_FORMAT_COFF) + +#ifndef __WI_LIBCPP_HIDDEN +# if !defined(__WI_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS) +# define __WI_LIBCPP_HIDDEN __attribute__ ((__visibility__("hidden"))) +# else +# define __WI_LIBCPP_HIDDEN +# endif +#endif + +#ifndef __WI_LIBCPP_TEMPLATE_VIS +# if !defined(__WI_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS) && !defined(__WI_LIBCPP_COMPILER_MSVC) +# if __has_attribute(__type_visibility__) +# define __WI_LIBCPP_TEMPLATE_VIS __attribute__ ((__type_visibility__("default"))) +# else +# define __WI_LIBCPP_TEMPLATE_VIS __attribute__ ((__visibility__("default"))) +# endif +# else +# define __WI_LIBCPP_TEMPLATE_VIS +# endif +#endif + +#define __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_HIDDEN __WI_LIBCPP_INTERNAL_LINKAGE + +namespace wistd // ("Windows Implementation" std) +{ + typedef decltype(__nullptr) nullptr_t; + + template + struct __less + { + __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 + bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;} + + __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 + bool operator()(const _T1& __x, const _T2& __y) const {return __x < __y;} + + __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 + bool operator()(const _T2& __x, const _T1& __y) const {return __x < __y;} + + __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 + bool operator()(const _T2& __x, const _T2& __y) const {return __x < __y;} + }; + + template + struct __less<_T1, _T1> + { + __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 + bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;} + }; + + template + struct __less + { + __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 + bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;} + }; + + template + struct __less<_T1, const _T1> + { + __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 + bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;} + }; + + // These are added to wistd to enable use of min/max without having to use the windows.h min/max + // macros that some clients might not have access to. Note: the STL versions of these have debug + // checking for the less than operator and support for iterators that these implementations lack. + // Use the STL versions when you require use of those features. + + // min + + template + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 + const _Tp& + (min)(const _Tp& __a, const _Tp& __b, _Compare __comp) + { + return __comp(__b, __a) ? __b : __a; + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 + const _Tp& + (min)(const _Tp& __a, const _Tp& __b) + { + return (min)(__a, __b, __less<_Tp>()); + } + + // max + + template + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 + const _Tp& + (max)(const _Tp& __a, const _Tp& __b, _Compare __comp) + { + return __comp(__a, __b) ? __b : __a; + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 + const _Tp& + (max)(const _Tp& __a, const _Tp& __b) + { + return (max)(__a, __b, __less<_Tp>()); + } + + template + struct __WI_LIBCPP_TEMPLATE_VIS unary_function + { + typedef _Arg argument_type; + typedef _Result result_type; + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS binary_function + { + typedef _Arg1 first_argument_type; + typedef _Arg2 second_argument_type; + typedef _Result result_type; + }; +} +/// @endcond + +#endif // _WISTD_CONFIG_H_ diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wistd_functional.h b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wistd_functional.h new file mode 100644 index 0000000..836f621 --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wistd_functional.h @@ -0,0 +1,544 @@ +// -*- C++ -*- +//===------------------------ functional ----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// STL common functionality +// +// Some aspects of STL are core language concepts that should be used from all C++ code, regardless +// of whether exceptions are enabled in the component. Common library code that expects to be used +// from exception-free components want these concepts, but including STL headers directly introduces +// friction as it requires components not using STL to declare their STL version. Doing so creates +// ambiguity around whether STL use is safe in a particular component and implicitly brings in +// a long list of headers (including ) which can create further ambiguity around throwing new +// support (some routines pulled in may expect it). Secondarily, pulling in these headers also has +// the potential to create naming conflicts or other implied dependencies. +// +// To promote the use of these core language concepts outside of STL-based binaries, this file is +// selectively pulling those concepts *directly* from corresponding STL headers. The corresponding +// "std::" namespace STL functions and types should be preferred over these in code that is bound to +// STL. The implementation and naming of all functions are taken directly from STL, instead using +// "wistd" (Windows Implementation std) as the namespace. +// +// Routines in this namespace should always be considered a reflection of the *current* STL implementation +// of those routines. Updates from STL should be taken, but no "bugs" should be fixed here. +// +// New, exception-based code should not use this namespace, but instead should prefer the std:: implementation. +// Only code that is not exception-based and libraries that expect to be utilized across both exception +// and non-exception based code should utilize this functionality. + +#ifndef _WISTD_FUNCTIONAL_H_ +#define _WISTD_FUNCTIONAL_H_ + +// DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage +#include "wistd_memory.h" +#include // For __fastfail +#include // For placement new + +#if !defined(__WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +#pragma warning(push) +#pragma warning(disable: 4324) +#pragma warning(disable: 4800) + +/// @cond +namespace wistd // ("Windows Implementation" std) +{ + // wistd::function + // + // All of the code below is in direct support of wistd::function. This class is identical to std::function + // with the following exceptions: + // + // 1) It never allocates and is safe to use from exception-free code (custom allocators are not supported) + // 2) It's slightly bigger on the stack (64 bytes, rather than 24 for 32bit) + // 3) There is an explicit static-assert if a lambda becomes too large to hold in the internal buffer (rather than an allocation) + + template + struct __invoke_void_return_wrapper + { +#ifndef __WI_LIBCPP_CXX03_LANG + template + static _Ret __call(_Args&&... __args) { + return __invoke(wistd::forward<_Args>(__args)...); + } +#else + template + static _Ret __call(_Fn __f) { + return __invoke(__f); + } + + template + static _Ret __call(_Fn __f, _A0& __a0) { + return __invoke(__f, __a0); + } + + template + static _Ret __call(_Fn __f, _A0& __a0, _A1& __a1) { + return __invoke(__f, __a0, __a1); + } + + template + static _Ret __call(_Fn __f, _A0& __a0, _A1& __a1, _A2& __a2){ + return __invoke(__f, __a0, __a1, __a2); + } +#endif + }; + + template <> + struct __invoke_void_return_wrapper + { +#ifndef __WI_LIBCPP_CXX03_LANG + template + static void __call(_Args&&... __args) { + (void)__invoke(wistd::forward<_Args>(__args)...); + } +#else + template + static void __call(_Fn __f) { + __invoke(__f); + } + + template + static void __call(_Fn __f, _A0& __a0) { + __invoke(__f, __a0); + } + + template + static void __call(_Fn __f, _A0& __a0, _A1& __a1) { + __invoke(__f, __a0, __a1); + } + + template + static void __call(_Fn __f, _A0& __a0, _A1& __a1, _A2& __a2) { + __invoke(__f, __a0, __a1, __a2); + } +#endif + }; + + //////////////////////////////////////////////////////////////////////////////// + // FUNCTION + //============================================================================== + + // bad_function_call + + __WI_LIBCPP_NORETURN inline __WI_LIBCPP_INLINE_VISIBILITY + void __throw_bad_function_call() + { + __fastfail(7); // FAST_FAIL_FATAL_APP_EXIT + } + + template class __WI_LIBCPP_TEMPLATE_VIS function; // undefined + + namespace __function + { + + template + struct __maybe_derive_from_unary_function + { + }; + + template + struct __maybe_derive_from_unary_function<_Rp(_A1)> + : public unary_function<_A1, _Rp> + { + }; + + template + struct __maybe_derive_from_binary_function + { + }; + + template + struct __maybe_derive_from_binary_function<_Rp(_A1, _A2)> + : public binary_function<_A1, _A2, _Rp> + { + }; + + template + __WI_LIBCPP_INLINE_VISIBILITY + bool __not_null(_Fp const&) { return true; } + + template + __WI_LIBCPP_INLINE_VISIBILITY + bool __not_null(_Fp* __ptr) { return __ptr; } + + template + __WI_LIBCPP_INLINE_VISIBILITY + bool __not_null(_Ret _Class::*__ptr) { return __ptr; } + + template + __WI_LIBCPP_INLINE_VISIBILITY + bool __not_null(function<_Fp> const& __f) { return !!__f; } + + } // namespace __function + +#ifndef __WI_LIBCPP_CXX03_LANG + + namespace __function { + + template class __base; + + template + class __base<_Rp(_ArgTypes...)> + { + __base(const __base&); + __base& operator=(const __base&); + public: + __WI_LIBCPP_INLINE_VISIBILITY __base() {} + __WI_LIBCPP_INLINE_VISIBILITY virtual ~__base() {} + virtual void __clone(__base*) const = 0; + virtual void __move(__base*) = 0; + virtual void destroy() WI_NOEXCEPT = 0; + virtual _Rp operator()(_ArgTypes&& ...) = 0; + }; + + template class __func; + + template + class __func<_Fp, _Rp(_ArgTypes...)> + : public __base<_Rp(_ArgTypes...)> + { + _Fp __f_; + public: + __WI_LIBCPP_INLINE_VISIBILITY + explicit __func(_Fp&& __f) + : __f_(wistd::move(__f)) {} + + __WI_LIBCPP_INLINE_VISIBILITY + explicit __func(const _Fp& __f) + : __f_(__f) {} + + virtual void __clone(__base<_Rp(_ArgTypes...)>*) const; + virtual void __move(__base<_Rp(_ArgTypes...)>*); + virtual void destroy() WI_NOEXCEPT; + virtual _Rp operator()(_ArgTypes&& ... __arg); + }; + + template + void + __func<_Fp, _Rp(_ArgTypes...)>::__clone(__base<_Rp(_ArgTypes...)>* __p) const + { + ::new (__p) __func(__f_); + } + + template + void + __func<_Fp, _Rp(_ArgTypes...)>::__move(__base<_Rp(_ArgTypes...)>* __p) + { + ::new (__p) __func(wistd::move(__f_)); + } + + template + void + __func<_Fp, _Rp(_ArgTypes...)>::destroy() WI_NOEXCEPT + { + __f_.~_Fp(); + } + + template + _Rp + __func<_Fp, _Rp(_ArgTypes...)>::operator()(_ArgTypes&& ... __arg) + { + typedef __invoke_void_return_wrapper<_Rp> _Invoker; + return _Invoker::__call(__f_, wistd::forward<_ArgTypes>(__arg)...); + } + + } // __function + + template + class __WI_LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes...)> + : public __function::__maybe_derive_from_unary_function<_Rp(_ArgTypes...)>, + public __function::__maybe_derive_from_binary_function<_Rp(_ArgTypes...)> + { + // 'wistd::function' is most similar to 'inplace_function' in that it _only_ permits holding function objects + // that can fit within its internal buffer. Therefore, we expand this size to accommodate space for at least 12 + // pointers (__base vtable takes an additional one). + static constexpr size_t __buffer_size = 13 * sizeof(void*); + + typedef __function::__base<_Rp(_ArgTypes...)> __base; + __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS + typename aligned_storage<__buffer_size>::type __buf_; + __base* __f_; + + __WI_LIBCPP_NO_CFI static __base *__as_base(void *p) { + return reinterpret_cast<__base*>(p); + } + + template + struct __callable_imp + { + static const bool value = is_same::value || + is_convertible::type, + _Rp>::value; + }; + + template + struct __callable_imp<_Fp, false> + { + static const bool value = false; + }; + + template + struct __callable + { + static const bool value = __callable_imp<_Fp, __lazy_and< + integral_constant, function>::value>, + __invokable<_Fp&, _ArgTypes...> + >::value>::value; + }; + + template + using _EnableIfCallable = typename enable_if<__callable<_Fp>::value>::type; + public: + typedef _Rp result_type; + + // construct/copy/destroy: + __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS + function() WI_NOEXCEPT : __f_(0) {} + __WI_LIBCPP_INLINE_VISIBILITY + function(nullptr_t) WI_NOEXCEPT : __f_(0) {} + function(const function&); + function(function&&); + template> + function(_Fp); + + function& operator=(const function&); + function& operator=(function&&); + function& operator=(nullptr_t) WI_NOEXCEPT; + template> + function& operator=(_Fp&&); + + ~function(); + + // function modifiers: + void swap(function&); + + // function capacity: + __WI_LIBCPP_INLINE_VISIBILITY + __WI_LIBCPP_EXPLICIT operator bool() const WI_NOEXCEPT {return __f_;} + + // deleted overloads close possible hole in the type system + template + bool operator==(const function<_R2(_ArgTypes2...)>&) const = delete; + template + bool operator!=(const function<_R2(_ArgTypes2...)>&) const = delete; + public: + // function invocation: + _Rp operator()(_ArgTypes...) const; + + // NOTE: type_info is very compiler specific, and on top of that, we're operating in a namespace other than + // 'std' so all functions requiring RTTI have been removed + }; + + template + __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS + function<_Rp(_ArgTypes...)>::function(const function& __f) + { + if (__f.__f_ == 0) + __f_ = 0; + else + { + __f_ = __as_base(&__buf_); + __f.__f_->__clone(__f_); + } + } + + template + __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS + function<_Rp(_ArgTypes...)>::function(function&& __f) + { + if (__f.__f_ == 0) + __f_ = 0; + else + { + __f_ = __as_base(&__buf_); + __f.__f_->__move(__f_); + __f.__f_->destroy(); + __f.__f_ = 0; + } + } + + template + template + __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS + function<_Rp(_ArgTypes...)>::function(_Fp __f) + : __f_(0) + { + if (__function::__not_null(__f)) + { + typedef __function::__func<_Fp, _Rp(_ArgTypes...)> _FF; + static_assert(sizeof(_FF) <= sizeof(__buf_), + "The sizeof(wistd::function) has grown too large for the reserved buffer (12 pointers). Refactor to reduce size of the capture."); + __f_ = ::new((void*)&__buf_) _FF(wistd::move(__f)); + } + } + + template + function<_Rp(_ArgTypes...)>& + function<_Rp(_ArgTypes...)>::operator=(const function& __f) + { + *this = nullptr; + if (__f.__f_) + { + __f_ = __as_base(&__buf_); + __f.__f_->__clone(__f_); + } + return *this; + } + + template + function<_Rp(_ArgTypes...)>& + function<_Rp(_ArgTypes...)>::operator=(function&& __f) + { + *this = nullptr; + if (__f.__f_) + { + __f_ = __as_base(&__buf_); + __f.__f_->__move(__f_); + __f.__f_->destroy(); + __f.__f_ = 0; + } + return *this; + } + + template + function<_Rp(_ArgTypes...)>& + function<_Rp(_ArgTypes...)>::operator=(nullptr_t) WI_NOEXCEPT + { + __base* __t = __f_; + __f_ = 0; + if (__t) + __t->destroy(); + return *this; + } + + template + template + function<_Rp(_ArgTypes...)>& + function<_Rp(_ArgTypes...)>::operator=(_Fp&& __f) + { + *this = nullptr; + if (__function::__not_null(__f)) + { + typedef __function::__func::type, _Rp(_ArgTypes...)> _FF; + static_assert(sizeof(_FF) <= sizeof(__buf_), + "The sizeof(wistd::function) has grown too large for the reserved buffer (12 pointers). Refactor to reduce size of the capture."); + __f_ = ::new((void*)&__buf_) _FF(wistd::move(__f)); + } + + return *this; + } + + template + function<_Rp(_ArgTypes...)>::~function() + { + if (__f_) + __f_->destroy(); + } + + template + void + function<_Rp(_ArgTypes...)>::swap(function& __f) + { + if (wistd::addressof(__f) == this) + return; + if (__f_ && __f.__f_) + { + typename aligned_storage::type __tempbuf; + __base* __t = __as_base(&__tempbuf); + __f_->__move(__t); + __f_->destroy(); + __f_ = 0; + __f.__f_->__move(__as_base(&__buf_)); + __f.__f_->destroy(); + __f.__f_ = 0; + __f_ = __as_base(&__buf_); + __t->__move(__as_base(&__f.__buf_)); + __t->destroy(); + __f.__f_ = __as_base(&__f.__buf_); + } + else if (__f_) + { + __f_->__move(__as_base(&__f.__buf_)); + __f_->destroy(); + __f_ = 0; + __f.__f_ = __as_base(&__f.__buf_); + } + else if (__f.__f_) + { + __f.__f_->__move(__as_base(&__buf_)); + __f.__f_->destroy(); + __f.__f_ = 0; + __f_ = __as_base(&__buf_); + } + } + + template + _Rp + function<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __arg) const + { + if (__f_ == 0) + __throw_bad_function_call(); + return (*__f_)(wistd::forward<_ArgTypes>(__arg)...); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator==(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) WI_NOEXCEPT {return !__f;} + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator==(nullptr_t, const function<_Rp(_ArgTypes...)>& __f) WI_NOEXCEPT {return !__f;} + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator!=(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) WI_NOEXCEPT {return (bool)__f;} + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator!=(nullptr_t, const function<_Rp(_ArgTypes...)>& __f) WI_NOEXCEPT {return (bool)__f;} + + // Provide both 'swap_wil' and 'swap' since we now have two ADL scenarios that we need to work + template + inline __WI_LIBCPP_INLINE_VISIBILITY + void + swap(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y) + {return __x.swap(__y);} + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + void + swap_wil(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y) + {return __x.swap(__y);} + + // std::invoke + template + typename __invoke_of<_Fn, _Args...>::type + invoke(_Fn&& __f, _Args&&... __args) + __WI_NOEXCEPT_((__nothrow_invokable<_Fn, _Args...>::value)) + { + return wistd::__invoke(wistd::forward<_Fn>(__f), wistd::forward<_Args>(__args)...); + } + +#else // __WI_LIBCPP_CXX03_LANG + +#error wistd::function and wistd::invoke not implemented for pre-C++11 + +#endif +} +/// @endcond + +#pragma warning(pop) + +#endif // _WISTD_FUNCTIONAL_H_ diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wistd_memory.h b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wistd_memory.h new file mode 100644 index 0000000..4f578a9 --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wistd_memory.h @@ -0,0 +1,1038 @@ +// -*- C++ -*- +//===-------------------------- memory ------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// STL common functionality +// +// Some aspects of STL are core language concepts that should be used from all C++ code, regardless +// of whether exceptions are enabled in the component. Common library code that expects to be used +// from exception-free components want these concepts, but including STL headers directly introduces +// friction as it requires components not using STL to declare their STL version. Doing so creates +// ambiguity around whether STL use is safe in a particular component and implicitly brings in +// a long list of headers (including ) which can create further ambiguity around throwing new +// support (some routines pulled in may expect it). Secondarily, pulling in these headers also has +// the potential to create naming conflicts or other implied dependencies. +// +// To promote the use of these core language concepts outside of STL-based binaries, this file is +// selectively pulling those concepts *directly* from corresponding STL headers. The corresponding +// "std::" namespace STL functions and types should be preferred over these in code that is bound to +// STL. The implementation and naming of all functions are taken directly from STL, instead using +// "wistd" (Windows Implementation std) as the namespace. +// +// Routines in this namespace should always be considered a reflection of the *current* STL implementation +// of those routines. Updates from STL should be taken, but no "bugs" should be fixed here. +// +// New, exception-based code should not use this namespace, but instead should prefer the std:: implementation. +// Only code that is not exception-based and libraries that expect to be utilized across both exception +// and non-exception based code should utilize this functionality. + +#ifndef _WISTD_MEMORY_H_ +#define _WISTD_MEMORY_H_ + +// DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage +#include "wistd_type_traits.h" + +#if !defined(__WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +/// @cond +namespace wistd // ("Windows Implementation" std) +{ + // allocator_traits + + template + struct __has_pointer_type : false_type {}; + + template + struct __has_pointer_type<_Tp, + typename __void_t::type> : true_type {}; + + namespace __pointer_type_imp + { + + template ::value> + struct __pointer_type + { + typedef typename _Dp::pointer type; + }; + + template + struct __pointer_type<_Tp, _Dp, false> + { + typedef _Tp* type; + }; + + } // __pointer_type_imp + + template + struct __pointer_type + { + typedef typename __pointer_type_imp::__pointer_type<_Tp, typename remove_reference<_Dp>::type>::type type; + }; + + template ::value && !__libcpp_is_final<_Tp>::value> + struct __compressed_pair_elem { + typedef _Tp _ParamT; + typedef _Tp& reference; + typedef const _Tp& const_reference; + +#ifndef __WI_LIBCPP_CXX03_LANG + __WI_LIBCPP_INLINE_VISIBILITY constexpr __compressed_pair_elem() : __value_() {} + + template ::type>::value + >::type> + __WI_LIBCPP_INLINE_VISIBILITY + constexpr explicit + __compressed_pair_elem(_Up&& __u) + : __value_(wistd::forward<_Up>(__u)) + { + } + + // NOTE: Since we have not added 'tuple' to 'wistd', the 'piecewise' constructor has been removed +#else + __WI_LIBCPP_INLINE_VISIBILITY __compressed_pair_elem() : __value_() {} + __WI_LIBCPP_INLINE_VISIBILITY + __compressed_pair_elem(_ParamT __p) : __value_(wistd::forward<_ParamT>(__p)) {} +#endif + + __WI_LIBCPP_INLINE_VISIBILITY reference __get() WI_NOEXCEPT { return __value_; } + __WI_LIBCPP_INLINE_VISIBILITY + const_reference __get() const WI_NOEXCEPT { return __value_; } + + private: + _Tp __value_; + }; + + template + struct __compressed_pair_elem<_Tp, _Idx, true> : private _Tp { + typedef _Tp _ParamT; + typedef _Tp& reference; + typedef const _Tp& const_reference; + typedef _Tp __value_type; + +#ifndef __WI_LIBCPP_CXX03_LANG + __WI_LIBCPP_INLINE_VISIBILITY constexpr __compressed_pair_elem() = default; + + template ::type>::value + >::type> + __WI_LIBCPP_INLINE_VISIBILITY + constexpr explicit + __compressed_pair_elem(_Up&& __u) + : __value_type(wistd::forward<_Up>(__u)) + {} + + // NOTE: Since we have not added 'tuple' to 'wistd', the 'piecewise' constructor has been removed +#else + __WI_LIBCPP_INLINE_VISIBILITY __compressed_pair_elem() : __value_type() {} + __WI_LIBCPP_INLINE_VISIBILITY + __compressed_pair_elem(_ParamT __p) + : __value_type(wistd::forward<_ParamT>(__p)) {} +#endif + + __WI_LIBCPP_INLINE_VISIBILITY reference __get() WI_NOEXCEPT { return *this; } + __WI_LIBCPP_INLINE_VISIBILITY + const_reference __get() const WI_NOEXCEPT { return *this; } + }; + + // Tag used to construct the second element of the compressed pair. + struct __second_tag {}; + + template + class __compressed_pair : private __compressed_pair_elem<_T1, 0>, + private __compressed_pair_elem<_T2, 1> { + typedef __compressed_pair_elem<_T1, 0> _Base1; + typedef __compressed_pair_elem<_T2, 1> _Base2; + + // NOTE: This static assert should never fire because __compressed_pair + // is *almost never* used in a scenario where it's possible for T1 == T2. + // (The exception is wistd::function where it is possible that the function + // object and the allocator have the same type). + static_assert((!is_same<_T1, _T2>::value), + "__compressed_pair cannot be instantated when T1 and T2 are the same type; " + "The current implementation is NOT ABI-compatible with the previous " + "implementation for this configuration"); + + public: +#ifndef __WI_LIBCPP_CXX03_LANG + template , _Dummy>::value && + __dependent_type, _Dummy>::value + >::type + > + __WI_LIBCPP_INLINE_VISIBILITY + constexpr __compressed_pair() {} + + template ::type, + __compressed_pair>::value, + bool>::type = true> + __WI_LIBCPP_INLINE_VISIBILITY constexpr explicit + __compressed_pair(_Tp&& __t) + : _Base1(wistd::forward<_Tp>(__t)), _Base2() {} + + template + __WI_LIBCPP_INLINE_VISIBILITY constexpr + __compressed_pair(__second_tag, _Tp&& __t) + : _Base1(), _Base2(wistd::forward<_Tp>(__t)) {} + + template + __WI_LIBCPP_INLINE_VISIBILITY constexpr + __compressed_pair(_U1&& __t1, _U2&& __t2) + : _Base1(wistd::forward<_U1>(__t1)), _Base2(wistd::forward<_U2>(__t2)) {} + + // NOTE: Since we have not added 'tuple' to 'wistd', the 'piecewise' constructor has been removed +#else + __WI_LIBCPP_INLINE_VISIBILITY + __compressed_pair() {} + + __WI_LIBCPP_INLINE_VISIBILITY explicit + __compressed_pair(_T1 __t1) : _Base1(wistd::forward<_T1>(__t1)) {} + + __WI_LIBCPP_INLINE_VISIBILITY + __compressed_pair(__second_tag, _T2 __t2) + : _Base1(), _Base2(wistd::forward<_T2>(__t2)) {} + + __WI_LIBCPP_INLINE_VISIBILITY + __compressed_pair(_T1 __t1, _T2 __t2) + : _Base1(wistd::forward<_T1>(__t1)), _Base2(wistd::forward<_T2>(__t2)) {} +#endif + + __WI_LIBCPP_INLINE_VISIBILITY + typename _Base1::reference first() WI_NOEXCEPT { + return static_cast<_Base1&>(*this).__get(); + } + + __WI_LIBCPP_INLINE_VISIBILITY + typename _Base1::const_reference first() const WI_NOEXCEPT { + return static_cast<_Base1 const&>(*this).__get(); + } + + __WI_LIBCPP_INLINE_VISIBILITY + typename _Base2::reference second() WI_NOEXCEPT { + return static_cast<_Base2&>(*this).__get(); + } + + __WI_LIBCPP_INLINE_VISIBILITY + typename _Base2::const_reference second() const WI_NOEXCEPT { + return static_cast<_Base2 const&>(*this).__get(); + } + + __WI_LIBCPP_INLINE_VISIBILITY + void swap(__compressed_pair& __x) + __WI_NOEXCEPT_(__is_nothrow_swappable<_T1>::value && + __is_nothrow_swappable<_T2>::value) + { + using wistd::swap_wil; + swap_wil(first(), __x.first()); + swap_wil(second(), __x.second()); + } + }; + + // Provide both 'swap_wil' and 'swap' since we now have two ADL scenarios that we need to work + template + inline __WI_LIBCPP_INLINE_VISIBILITY + void swap(__compressed_pair<_T1, _T2>& __x, __compressed_pair<_T1, _T2>& __y) + __WI_NOEXCEPT_(__is_nothrow_swappable<_T1>::value && + __is_nothrow_swappable<_T2>::value) { + __x.swap(__y); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + void swap_wil(__compressed_pair<_T1, _T2>& __x, __compressed_pair<_T1, _T2>& __y) + __WI_NOEXCEPT_(__is_nothrow_swappable<_T1>::value && + __is_nothrow_swappable<_T2>::value) { + __x.swap(__y); + } + + // default_delete + + template + struct __WI_LIBCPP_TEMPLATE_VIS default_delete { + static_assert(!is_function<_Tp>::value, + "default_delete cannot be instantiated for function types"); +#ifndef __WI_LIBCPP_CXX03_LANG + __WI_LIBCPP_INLINE_VISIBILITY constexpr default_delete() WI_NOEXCEPT = default; +#else + __WI_LIBCPP_INLINE_VISIBILITY default_delete() {} +#endif + template + __WI_LIBCPP_INLINE_VISIBILITY + default_delete(const default_delete<_Up>&, + typename enable_if::value>::type* = + 0) WI_NOEXCEPT {} + + __WI_LIBCPP_INLINE_VISIBILITY void operator()(_Tp* __ptr) const WI_NOEXCEPT { + static_assert(sizeof(_Tp) > 0, + "default_delete can not delete incomplete type"); + static_assert(!is_void<_Tp>::value, + "default_delete can not delete incomplete type"); + delete __ptr; + } + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS default_delete<_Tp[]> { + private: + template + struct _EnableIfConvertible + : enable_if::value> {}; + + public: +#ifndef __WI_LIBCPP_CXX03_LANG + __WI_LIBCPP_INLINE_VISIBILITY constexpr default_delete() WI_NOEXCEPT = default; +#else + __WI_LIBCPP_INLINE_VISIBILITY default_delete() {} +#endif + + template + __WI_LIBCPP_INLINE_VISIBILITY + default_delete(const default_delete<_Up[]>&, + typename _EnableIfConvertible<_Up>::type* = 0) WI_NOEXCEPT {} + + template + __WI_LIBCPP_INLINE_VISIBILITY + typename _EnableIfConvertible<_Up>::type + operator()(_Up* __ptr) const WI_NOEXCEPT { + static_assert(sizeof(_Tp) > 0, + "default_delete can not delete incomplete type"); + static_assert(!is_void<_Tp>::value, + "default_delete can not delete void type"); + delete[] __ptr; + } + }; + + + +#ifndef __WI_LIBCPP_CXX03_LANG + template + struct __unique_ptr_deleter_sfinae { + static_assert(!is_reference<_Deleter>::value, "incorrect specialization"); + typedef const _Deleter& __lval_ref_type; + typedef _Deleter&& __good_rval_ref_type; + typedef true_type __enable_rval_overload; + }; + + template + struct __unique_ptr_deleter_sfinae<_Deleter const&> { + typedef const _Deleter& __lval_ref_type; + typedef const _Deleter&& __bad_rval_ref_type; + typedef false_type __enable_rval_overload; + }; + + template + struct __unique_ptr_deleter_sfinae<_Deleter&> { + typedef _Deleter& __lval_ref_type; + typedef _Deleter&& __bad_rval_ref_type; + typedef false_type __enable_rval_overload; + }; +#endif // !defined(__WI_LIBCPP_CXX03_LANG) + + template > + class __WI_LIBCPP_TEMPLATE_VIS unique_ptr { + public: + typedef _Tp element_type; + typedef _Dp deleter_type; + typedef typename __pointer_type<_Tp, deleter_type>::type pointer; + + static_assert(!is_rvalue_reference::value, + "the specified deleter type cannot be an rvalue reference"); + + private: + __compressed_pair __ptr_; + + struct __nat { int __for_bool_; }; + +#ifndef __WI_LIBCPP_CXX03_LANG + typedef __unique_ptr_deleter_sfinae<_Dp> _DeleterSFINAE; + + template + using _LValRefType = + typename __dependent_type<_DeleterSFINAE, _Dummy>::__lval_ref_type; + + template + using _GoodRValRefType = + typename __dependent_type<_DeleterSFINAE, _Dummy>::__good_rval_ref_type; + + template + using _BadRValRefType = + typename __dependent_type<_DeleterSFINAE, _Dummy>::__bad_rval_ref_type; + + template , _Dummy>::type> + using _EnableIfDeleterDefaultConstructible = + typename enable_if::value && + !is_pointer<_Deleter>::value>::type; + + template + using _EnableIfDeleterConstructible = + typename enable_if::value>::type; + + template + using _EnableIfMoveConvertible = typename enable_if< + is_convertible::value && + !is_array<_Up>::value + >::type; + + template + using _EnableIfDeleterConvertible = typename enable_if< + (is_reference<_Dp>::value && is_same<_Dp, _UDel>::value) || + (!is_reference<_Dp>::value && is_convertible<_UDel, _Dp>::value) + >::type; + + template + using _EnableIfDeleterAssignable = typename enable_if< + is_assignable<_Dp&, _UDel&&>::value + >::type; + + public: + template > + __WI_LIBCPP_INLINE_VISIBILITY + constexpr unique_ptr() WI_NOEXCEPT : __ptr_(pointer()) {} + + template > + __WI_LIBCPP_INLINE_VISIBILITY + constexpr unique_ptr(nullptr_t) WI_NOEXCEPT : __ptr_(pointer()) {} + + template > + __WI_LIBCPP_INLINE_VISIBILITY + explicit unique_ptr(pointer __p) WI_NOEXCEPT : __ptr_(__p) {} + + template >> + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(pointer __p, _LValRefType<_Dummy> __d) WI_NOEXCEPT + : __ptr_(__p, __d) {} + + template >> + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(pointer __p, _GoodRValRefType<_Dummy> __d) WI_NOEXCEPT + : __ptr_(__p, wistd::move(__d)) { + static_assert(!is_reference::value, + "rvalue deleter bound to reference"); + } + + template >> + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(pointer __p, _BadRValRefType<_Dummy> __d) = delete; + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(unique_ptr&& __u) WI_NOEXCEPT + : __ptr_(__u.release(), wistd::forward(__u.get_deleter())) { + } + + template , _Up>, + class = _EnableIfDeleterConvertible<_Ep> + > + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(unique_ptr<_Up, _Ep>&& __u) WI_NOEXCEPT + : __ptr_(__u.release(), wistd::forward<_Ep>(__u.get_deleter())) {} + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr& operator=(unique_ptr&& __u) WI_NOEXCEPT { + reset(__u.release()); + __ptr_.second() = wistd::forward(__u.get_deleter()); + return *this; + } + + template , _Up>, + class = _EnableIfDeleterAssignable<_Ep> + > + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr& operator=(unique_ptr<_Up, _Ep>&& __u) WI_NOEXCEPT { + reset(__u.release()); + __ptr_.second() = wistd::forward<_Ep>(__u.get_deleter()); + return *this; + } + +#else // __WI_LIBCPP_CXX03_LANG + private: + unique_ptr(unique_ptr&); + template unique_ptr(unique_ptr<_Up, _Ep>&); + + unique_ptr& operator=(unique_ptr&); + template unique_ptr& operator=(unique_ptr<_Up, _Ep>&); + + public: + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr() : __ptr_(pointer()) + { + static_assert(!is_pointer::value, + "unique_ptr constructed with null function pointer deleter"); + static_assert(is_default_constructible::value, + "unique_ptr::deleter_type is not default constructible"); + } + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(nullptr_t) : __ptr_(pointer()) + { + static_assert(!is_pointer::value, + "unique_ptr constructed with null function pointer deleter"); + } + __WI_LIBCPP_INLINE_VISIBILITY + explicit unique_ptr(pointer __p) + : __ptr_(wistd::move(__p)) { + static_assert(!is_pointer::value, + "unique_ptr constructed with null function pointer deleter"); + } + + __WI_LIBCPP_INLINE_VISIBILITY + operator __rv() { + return __rv(*this); + } + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(__rv __u) + : __ptr_(__u->release(), + wistd::forward(__u->get_deleter())) {} + + template + __WI_LIBCPP_INLINE_VISIBILITY + typename enable_if< + !is_array<_Up>::value && + is_convertible::pointer, + pointer>::value && + is_assignable::value, + unique_ptr&>::type + operator=(unique_ptr<_Up, _Ep> __u) { + reset(__u.release()); + __ptr_.second() = wistd::forward<_Ep>(__u.get_deleter()); + return *this; + } + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(pointer __p, deleter_type __d) + : __ptr_(wistd::move(__p), wistd::move(__d)) {} +#endif // __WI_LIBCPP_CXX03_LANG + + __WI_LIBCPP_INLINE_VISIBILITY + ~unique_ptr() { reset(); } + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr& operator=(nullptr_t) WI_NOEXCEPT { + reset(); + return *this; + } + + __WI_LIBCPP_INLINE_VISIBILITY + typename add_lvalue_reference<_Tp>::type + operator*() const { + return *__ptr_.first(); + } + __WI_LIBCPP_INLINE_VISIBILITY + pointer operator->() const WI_NOEXCEPT { + return __ptr_.first(); + } + __WI_LIBCPP_INLINE_VISIBILITY + pointer get() const WI_NOEXCEPT { + return __ptr_.first(); + } + __WI_LIBCPP_INLINE_VISIBILITY + deleter_type& get_deleter() WI_NOEXCEPT { + return __ptr_.second(); + } + __WI_LIBCPP_INLINE_VISIBILITY + const deleter_type& get_deleter() const WI_NOEXCEPT { + return __ptr_.second(); + } + __WI_LIBCPP_INLINE_VISIBILITY + __WI_LIBCPP_EXPLICIT operator bool() const WI_NOEXCEPT { + return __ptr_.first() != nullptr; + } + + __WI_LIBCPP_INLINE_VISIBILITY + pointer release() WI_NOEXCEPT { + pointer __t = __ptr_.first(); + __ptr_.first() = pointer(); + return __t; + } + + __WI_LIBCPP_INLINE_VISIBILITY + void reset(pointer __p = pointer()) WI_NOEXCEPT { + pointer __tmp = __ptr_.first(); + __ptr_.first() = __p; + if (__tmp) + __ptr_.second()(__tmp); + } + + __WI_LIBCPP_INLINE_VISIBILITY + void swap(unique_ptr& __u) WI_NOEXCEPT { + __ptr_.swap(__u.__ptr_); + } + }; + + + template + class __WI_LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp> { + public: + typedef _Tp element_type; + typedef _Dp deleter_type; + typedef typename __pointer_type<_Tp, deleter_type>::type pointer; + + private: + __compressed_pair __ptr_; + + template + struct _CheckArrayPointerConversion : is_same<_From, pointer> {}; + + template + struct _CheckArrayPointerConversion<_FromElem*> + : integral_constant::value || + (is_same::value && + is_convertible<_FromElem(*)[], element_type(*)[]>::value) + > + {}; + +#ifndef __WI_LIBCPP_CXX03_LANG + typedef __unique_ptr_deleter_sfinae<_Dp> _DeleterSFINAE; + + template + using _LValRefType = + typename __dependent_type<_DeleterSFINAE, _Dummy>::__lval_ref_type; + + template + using _GoodRValRefType = + typename __dependent_type<_DeleterSFINAE, _Dummy>::__good_rval_ref_type; + + template + using _BadRValRefType = + typename __dependent_type<_DeleterSFINAE, _Dummy>::__bad_rval_ref_type; + + template , _Dummy>::type> + using _EnableIfDeleterDefaultConstructible = + typename enable_if::value && + !is_pointer<_Deleter>::value>::type; + + template + using _EnableIfDeleterConstructible = + typename enable_if::value>::type; + + template + using _EnableIfPointerConvertible = typename enable_if< + _CheckArrayPointerConversion<_Pp>::value + >::type; + + template + using _EnableIfMoveConvertible = typename enable_if< + is_array<_Up>::value && + is_same::value && + is_same::value && + is_convertible<_ElemT(*)[], element_type(*)[]>::value + >::type; + + template + using _EnableIfDeleterConvertible = typename enable_if< + (is_reference<_Dp>::value && is_same<_Dp, _UDel>::value) || + (!is_reference<_Dp>::value && is_convertible<_UDel, _Dp>::value) + >::type; + + template + using _EnableIfDeleterAssignable = typename enable_if< + is_assignable<_Dp&, _UDel&&>::value + >::type; + + public: + template > + __WI_LIBCPP_INLINE_VISIBILITY + constexpr unique_ptr() WI_NOEXCEPT : __ptr_(pointer()) {} + + template > + __WI_LIBCPP_INLINE_VISIBILITY + constexpr unique_ptr(nullptr_t) WI_NOEXCEPT : __ptr_(pointer()) {} + + template , + class = _EnableIfPointerConvertible<_Pp>> + __WI_LIBCPP_INLINE_VISIBILITY + explicit unique_ptr(_Pp __p) WI_NOEXCEPT + : __ptr_(__p) {} + + template >, + class = _EnableIfPointerConvertible<_Pp>> + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(_Pp __p, _LValRefType<_Dummy> __d) WI_NOEXCEPT + : __ptr_(__p, __d) {} + + template >> + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(nullptr_t, _LValRefType<_Dummy> __d) WI_NOEXCEPT + : __ptr_(nullptr, __d) {} + + template >, + class = _EnableIfPointerConvertible<_Pp>> + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(_Pp __p, _GoodRValRefType<_Dummy> __d) WI_NOEXCEPT + : __ptr_(__p, wistd::move(__d)) { + static_assert(!is_reference::value, + "rvalue deleter bound to reference"); + } + + template >> + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(nullptr_t, _GoodRValRefType<_Dummy> __d) WI_NOEXCEPT + : __ptr_(nullptr, wistd::move(__d)) { + static_assert(!is_reference::value, + "rvalue deleter bound to reference"); + } + + template >, + class = _EnableIfPointerConvertible<_Pp>> + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(_Pp __p, _BadRValRefType<_Dummy> __d) = delete; + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(unique_ptr&& __u) WI_NOEXCEPT + : __ptr_(__u.release(), wistd::forward(__u.get_deleter())) { + } + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr& operator=(unique_ptr&& __u) WI_NOEXCEPT { + reset(__u.release()); + __ptr_.second() = wistd::forward(__u.get_deleter()); + return *this; + } + + template , _Up>, + class = _EnableIfDeleterConvertible<_Ep> + > + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(unique_ptr<_Up, _Ep>&& __u) WI_NOEXCEPT + : __ptr_(__u.release(), wistd::forward<_Ep>(__u.get_deleter())) { + } + + template , _Up>, + class = _EnableIfDeleterAssignable<_Ep> + > + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr& + operator=(unique_ptr<_Up, _Ep>&& __u) WI_NOEXCEPT { + reset(__u.release()); + __ptr_.second() = wistd::forward<_Ep>(__u.get_deleter()); + return *this; + } + +#else // __WI_LIBCPP_CXX03_LANG + private: + template explicit unique_ptr(_Up); + + unique_ptr(unique_ptr&); + template unique_ptr(unique_ptr<_Up>&); + + unique_ptr& operator=(unique_ptr&); + template unique_ptr& operator=(unique_ptr<_Up>&); + + template + unique_ptr(_Up __u, + typename conditional< + is_reference::value, deleter_type, + typename add_lvalue_reference::type>::type, + typename enable_if::value, + __nat>::type = __nat()); + public: + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr() : __ptr_(pointer()) { + static_assert(!is_pointer::value, + "unique_ptr constructed with null function pointer deleter"); + } + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(nullptr_t) : __ptr_(pointer()) { + static_assert(!is_pointer::value, + "unique_ptr constructed with null function pointer deleter"); + } + + __WI_LIBCPP_INLINE_VISIBILITY + explicit unique_ptr(pointer __p) : __ptr_(__p) { + static_assert(!is_pointer::value, + "unique_ptr constructed with null function pointer deleter"); + } + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(pointer __p, deleter_type __d) + : __ptr_(__p, wistd::forward(__d)) {} + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(nullptr_t, deleter_type __d) + : __ptr_(pointer(), wistd::forward(__d)) {} + + __WI_LIBCPP_INLINE_VISIBILITY + operator __rv() { + return __rv(*this); + } + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr(__rv __u) + : __ptr_(__u->release(), + wistd::forward(__u->get_deleter())) {} + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr& operator=(__rv __u) { + reset(__u->release()); + __ptr_.second() = wistd::forward(__u->get_deleter()); + return *this; + } + +#endif // __WI_LIBCPP_CXX03_LANG + + public: + __WI_LIBCPP_INLINE_VISIBILITY + ~unique_ptr() { reset(); } + + __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr& operator=(nullptr_t) WI_NOEXCEPT { + reset(); + return *this; + } + + __WI_LIBCPP_INLINE_VISIBILITY + typename add_lvalue_reference<_Tp>::type + operator[](size_t __i) const { + return __ptr_.first()[__i]; + } + __WI_LIBCPP_INLINE_VISIBILITY + pointer get() const WI_NOEXCEPT { + return __ptr_.first(); + } + + __WI_LIBCPP_INLINE_VISIBILITY + deleter_type& get_deleter() WI_NOEXCEPT { + return __ptr_.second(); + } + + __WI_LIBCPP_INLINE_VISIBILITY + const deleter_type& get_deleter() const WI_NOEXCEPT { + return __ptr_.second(); + } + __WI_LIBCPP_INLINE_VISIBILITY + __WI_LIBCPP_EXPLICIT operator bool() const WI_NOEXCEPT { + return __ptr_.first() != nullptr; + } + + __WI_LIBCPP_INLINE_VISIBILITY + pointer release() WI_NOEXCEPT { + pointer __t = __ptr_.first(); + __ptr_.first() = pointer(); + return __t; + } + + template + __WI_LIBCPP_INLINE_VISIBILITY + typename enable_if< + _CheckArrayPointerConversion<_Pp>::value + >::type + reset(_Pp __p) WI_NOEXCEPT { + pointer __tmp = __ptr_.first(); + __ptr_.first() = __p; + if (__tmp) + __ptr_.second()(__tmp); + } + + __WI_LIBCPP_INLINE_VISIBILITY + void reset(nullptr_t = nullptr) WI_NOEXCEPT { + pointer __tmp = __ptr_.first(); + __ptr_.first() = nullptr; + if (__tmp) + __ptr_.second()(__tmp); + } + + __WI_LIBCPP_INLINE_VISIBILITY + void swap(unique_ptr& __u) WI_NOEXCEPT { + __ptr_.swap(__u.__ptr_); + } + + }; + + // Provide both 'swap_wil' and 'swap' since we now have two ADL scenarios that we need to work + template + inline __WI_LIBCPP_INLINE_VISIBILITY + typename enable_if< + __is_swappable<_Dp>::value, + void + >::type + swap(unique_ptr<_Tp, _Dp>& __x, unique_ptr<_Tp, _Dp>& __y) WI_NOEXCEPT {__x.swap(__y);} + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + typename enable_if< + __is_swappable<_Dp>::value, + void + >::type + swap_wil(unique_ptr<_Tp, _Dp>& __x, unique_ptr<_Tp, _Dp>& __y) WI_NOEXCEPT {__x.swap(__y);} + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator==(const unique_ptr<_T1, _D1>& __x, const unique_ptr<_T2, _D2>& __y) {return __x.get() == __y.get();} + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator!=(const unique_ptr<_T1, _D1>& __x, const unique_ptr<_T2, _D2>& __y) {return !(__x == __y);} + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator< (const unique_ptr<_T1, _D1>& __x, const unique_ptr<_T2, _D2>& __y) + { + typedef typename unique_ptr<_T1, _D1>::pointer _P1; + typedef typename unique_ptr<_T2, _D2>::pointer _P2; + typedef typename common_type<_P1, _P2>::type _Vp; + return less<_Vp>()(__x.get(), __y.get()); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator> (const unique_ptr<_T1, _D1>& __x, const unique_ptr<_T2, _D2>& __y) {return __y < __x;} + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator<=(const unique_ptr<_T1, _D1>& __x, const unique_ptr<_T2, _D2>& __y) {return !(__y < __x);} + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator>=(const unique_ptr<_T1, _D1>& __x, const unique_ptr<_T2, _D2>& __y) {return !(__x < __y);} + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator==(const unique_ptr<_T1, _D1>& __x, nullptr_t) WI_NOEXCEPT + { + return !__x; + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator==(nullptr_t, const unique_ptr<_T1, _D1>& __x) WI_NOEXCEPT + { + return !__x; + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator!=(const unique_ptr<_T1, _D1>& __x, nullptr_t) WI_NOEXCEPT + { + return static_cast(__x); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator!=(nullptr_t, const unique_ptr<_T1, _D1>& __x) WI_NOEXCEPT + { + return static_cast(__x); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator<(const unique_ptr<_T1, _D1>& __x, nullptr_t) + { + typedef typename unique_ptr<_T1, _D1>::pointer _P1; + return less<_P1>()(__x.get(), nullptr); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator<(nullptr_t, const unique_ptr<_T1, _D1>& __x) + { + typedef typename unique_ptr<_T1, _D1>::pointer _P1; + return less<_P1>()(nullptr, __x.get()); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator>(const unique_ptr<_T1, _D1>& __x, nullptr_t) + { + return nullptr < __x; + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator>(nullptr_t, const unique_ptr<_T1, _D1>& __x) + { + return __x < nullptr; + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator<=(const unique_ptr<_T1, _D1>& __x, nullptr_t) + { + return !(nullptr < __x); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator<=(nullptr_t, const unique_ptr<_T1, _D1>& __x) + { + return !(__x < nullptr); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator>=(const unique_ptr<_T1, _D1>& __x, nullptr_t) + { + return !(__x < nullptr); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + bool + operator>=(nullptr_t, const unique_ptr<_T1, _D1>& __x) + { + return !(nullptr < __x); + } + +#ifdef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + unique_ptr<_Tp, _Dp> + move(unique_ptr<_Tp, _Dp>& __t) + { + return unique_ptr<_Tp, _Dp>(__rv >(__t)); + } + +#endif +} +/// @endcond + +#endif // _WISTD_MEMORY_H_ diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wistd_type_traits.h b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wistd_type_traits.h new file mode 100644 index 0000000..b3e09fc --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wistd_type_traits.h @@ -0,0 +1,4504 @@ +// -*- C++ -*- +//===------------------------ type_traits ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// STL common functionality +// +// Some aspects of STL are core language concepts that should be used from all C++ code, regardless +// of whether exceptions are enabled in the component. Common library code that expects to be used +// from exception-free components want these concepts, but including directly introduces +// friction as it requires components not using STL to declare their STL version. Doing so creates +// ambiguity around whether STL use is safe in a particular component and implicitly brings in +// a long list of headers (including ) which can create further ambiguity around throwing new +// support (some routines pulled in may expect it). Secondarily, pulling in these headers also has +// the potential to create naming conflicts or other implied dependencies. +// +// To promote the use of these core language concepts outside of STL-based binaries, this file is +// selectively pulling those concepts *directly* from corresponding STL headers. The corresponding +// "std::" namespace STL functions and types should be preferred over these in code that is bound to +// STL. The implementation and naming of all functions are taken directly from STL, instead using +// "wistd" (Windows Implementation std) as the namespace. +// +// Routines in this namespace should always be considered a reflection of the *current* STL implementation +// of those routines. Updates from STL should be taken, but no "bugs" should be fixed here. +// +// New, exception-based code should not use this namespace, but instead should prefer the std:: implementation. +// Only code that is not exception-based and libraries that expect to be utilized across both exception +// and non-exception based code should utilize this functionality. + +#ifndef _WISTD_TYPE_TRAITS_H_ +#define _WISTD_TYPE_TRAITS_H_ + +// DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage +#include "wistd_config.h" + +#if !defined(__WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +/// @cond +namespace wistd // ("Windows Implementation" std) +{ + template struct __WI_LIBCPP_TEMPLATE_VIS pair; + template class __WI_LIBCPP_TEMPLATE_VIS reference_wrapper; + template struct __WI_LIBCPP_TEMPLATE_VIS hash; + + template + struct __void_t { typedef void type; }; + + template + struct __identity { typedef _Tp type; }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS __dependent_type : public _Tp {}; + + template + struct __WI_LIBCPP_TEMPLATE_VIS conditional {typedef _If type;}; + template + struct __WI_LIBCPP_TEMPLATE_VIS conditional {typedef _Then type;}; + +#if __WI_LIBCPP_STD_VER > 11 + template using conditional_t = typename conditional<_Bp, _If, _Then>::type; +#endif + + template struct __WI_LIBCPP_TEMPLATE_VIS __lazy_enable_if {}; + template struct __WI_LIBCPP_TEMPLATE_VIS __lazy_enable_if {typedef typename _Tp::type type;}; + + template struct __WI_LIBCPP_TEMPLATE_VIS enable_if {}; + template struct __WI_LIBCPP_TEMPLATE_VIS enable_if {typedef _Tp type;}; + +#if __WI_LIBCPP_STD_VER > 11 + template using enable_if_t = typename enable_if<_Bp, _Tp>::type; +#endif + + // addressof +#ifndef __WI_LIBCPP_HAS_NO_BUILTIN_ADDRESSOF + + template + inline __WI_LIBCPP_CONSTEXPR_AFTER_CXX14 + __WI_LIBCPP_NO_CFI __WI_LIBCPP_INLINE_VISIBILITY + _Tp* + addressof(_Tp& __x) WI_NOEXCEPT + { + return __builtin_addressof(__x); + } + +#else + + template + inline __WI_LIBCPP_NO_CFI __WI_LIBCPP_INLINE_VISIBILITY + _Tp* + addressof(_Tp& __x) WI_NOEXCEPT + { + return reinterpret_cast<_Tp *>( + const_cast(&reinterpret_cast(__x))); + } + +#endif // __WI_LIBCPP_HAS_NO_BUILTIN_ADDRESSOF + +#if !defined(__WI_LIBCPP_CXX03_LANG) + template _Tp* addressof(const _Tp&&) WI_NOEXCEPT = delete; +#endif + + struct __two {char __lx[2];}; + + // helper class: + + template + struct __WI_LIBCPP_TEMPLATE_VIS integral_constant + { + static __WI_LIBCPP_CONSTEXPR const _Tp value = __v; + typedef _Tp value_type; + typedef integral_constant type; + __WI_LIBCPP_INLINE_VISIBILITY + __WI_LIBCPP_CONSTEXPR operator value_type() const WI_NOEXCEPT {return value;} +#if __WI_LIBCPP_STD_VER > 11 + __WI_LIBCPP_INLINE_VISIBILITY + constexpr value_type operator ()() const WI_NOEXCEPT {return value;} +#endif + }; + + template + __WI_LIBCPP_CONSTEXPR const _Tp integral_constant<_Tp, __v>::value; + +#if !defined(__WI_LIBCPP_CXX03_LANG) + template + using bool_constant = integral_constant; +#define __WI_LIBCPP_BOOL_CONSTANT(__b) bool_constant<(__b)> +#else +#define __WI_LIBCPP_BOOL_CONSTANT(__b) integral_constant +#endif + + typedef __WI_LIBCPP_BOOL_CONSTANT(true) true_type; + typedef __WI_LIBCPP_BOOL_CONSTANT(false) false_type; + +#if !defined(__WI_LIBCPP_CXX03_LANG) + + // __lazy_and + + template + struct __lazy_and_impl; + + template + struct __lazy_and_impl : false_type {}; + + template <> + struct __lazy_and_impl : true_type {}; + + template + struct __lazy_and_impl : integral_constant {}; + + template + struct __lazy_and_impl : __lazy_and_impl<_Hp::type::value, _Tp...> {}; + + template + struct __lazy_and : __lazy_and_impl<_P1::type::value, _Pr...> {}; + + // __lazy_or + + template + struct __lazy_or_impl; + + template + struct __lazy_or_impl : true_type {}; + + template <> + struct __lazy_or_impl : false_type {}; + + template + struct __lazy_or_impl + : __lazy_or_impl<_Hp::type::value, _Tp...> {}; + + template + struct __lazy_or : __lazy_or_impl<_P1::type::value, _Pr...> {}; + + // __lazy_not + + template + struct __lazy_not : integral_constant {}; + + // __and_ + template struct __and_; + template<> struct __and_<> : true_type {}; + + template struct __and_<_B0> : _B0 {}; + + template + struct __and_<_B0, _B1> : conditional<_B0::value, _B1, _B0>::type {}; + + template + struct __and_<_B0, _B1, _B2, _Bn...> + : conditional<_B0::value, __and_<_B1, _B2, _Bn...>, _B0>::type {}; + + // __or_ + template struct __or_; + template<> struct __or_<> : false_type {}; + + template struct __or_<_B0> : _B0 {}; + + template + struct __or_<_B0, _B1> : conditional<_B0::value, _B0, _B1>::type {}; + + template + struct __or_<_B0, _B1, _B2, _Bn...> + : conditional<_B0::value, _B0, __or_<_B1, _B2, _Bn...> >::type {}; + + // __not_ + template + struct __not_ : conditional<_Tp::value, false_type, true_type>::type {}; + +#endif // !defined(__WI_LIBCPP_CXX03_LANG) + + // is_const + + template struct __WI_LIBCPP_TEMPLATE_VIS is_const : public false_type {}; + template struct __WI_LIBCPP_TEMPLATE_VIS is_const<_Tp const> : public true_type {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_const_v + = is_const<_Tp>::value; +#endif + + // is_volatile + + template struct __WI_LIBCPP_TEMPLATE_VIS is_volatile : public false_type {}; + template struct __WI_LIBCPP_TEMPLATE_VIS is_volatile<_Tp volatile> : public true_type {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_volatile_v + = is_volatile<_Tp>::value; +#endif + + // remove_const + + template struct __WI_LIBCPP_TEMPLATE_VIS remove_const {typedef _Tp type;}; + template struct __WI_LIBCPP_TEMPLATE_VIS remove_const {typedef _Tp type;}; +#if __WI_LIBCPP_STD_VER > 11 + template using remove_const_t = typename remove_const<_Tp>::type; +#endif + + // remove_volatile + + template struct __WI_LIBCPP_TEMPLATE_VIS remove_volatile {typedef _Tp type;}; + template struct __WI_LIBCPP_TEMPLATE_VIS remove_volatile {typedef _Tp type;}; +#if __WI_LIBCPP_STD_VER > 11 + template using remove_volatile_t = typename remove_volatile<_Tp>::type; +#endif + + // remove_cv + + template struct __WI_LIBCPP_TEMPLATE_VIS remove_cv + {typedef typename remove_volatile::type>::type type;}; +#if __WI_LIBCPP_STD_VER > 11 + template using remove_cv_t = typename remove_cv<_Tp>::type; +#endif + + // is_void + + template struct __libcpp_is_void : public false_type {}; + template <> struct __libcpp_is_void : public true_type {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_void + : public __libcpp_is_void::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_void_v + = is_void<_Tp>::value; +#endif + + // __is_nullptr_t + + template struct __is_nullptr_t_impl : public false_type {}; + template <> struct __is_nullptr_t_impl : public true_type {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS __is_nullptr_t + : public __is_nullptr_t_impl::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 + template struct __WI_LIBCPP_TEMPLATE_VIS is_null_pointer + : public __is_nullptr_t_impl::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_null_pointer_v + = is_null_pointer<_Tp>::value; +#endif +#endif + + // is_integral + + template struct __libcpp_is_integral : public false_type {}; + template <> struct __libcpp_is_integral : public true_type {}; + template <> struct __libcpp_is_integral : public true_type {}; + template <> struct __libcpp_is_integral : public true_type {}; + template <> struct __libcpp_is_integral : public true_type {}; +#ifdef _MSC_VER + template <> struct __libcpp_is_integral<__wchar_t> : public true_type {}; +#else + template <> struct __libcpp_is_integral : public true_type {}; +#endif +#ifndef __WI_LIBCPP_HAS_NO_UNICODE_CHARS + template <> struct __libcpp_is_integral : public true_type {}; + template <> struct __libcpp_is_integral : public true_type {}; +#endif // __WI_LIBCPP_HAS_NO_UNICODE_CHARS + template <> struct __libcpp_is_integral : public true_type {}; + template <> struct __libcpp_is_integral : public true_type {}; + template <> struct __libcpp_is_integral : public true_type {}; + template <> struct __libcpp_is_integral : public true_type {}; + template <> struct __libcpp_is_integral : public true_type {}; + template <> struct __libcpp_is_integral : public true_type {}; + template <> struct __libcpp_is_integral : public true_type {}; + template <> struct __libcpp_is_integral : public true_type {}; +#ifndef __WI_LIBCPP_HAS_NO_INT128 + template <> struct __libcpp_is_integral<__int128_t> : public true_type {}; + template <> struct __libcpp_is_integral<__uint128_t> : public true_type {}; +#endif + + template struct __WI_LIBCPP_TEMPLATE_VIS is_integral + : public __libcpp_is_integral::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_integral_v + = is_integral<_Tp>::value; +#endif + + // is_floating_point + + template struct __libcpp_is_floating_point : public false_type {}; + template <> struct __libcpp_is_floating_point : public true_type {}; + template <> struct __libcpp_is_floating_point : public true_type {}; + template <> struct __libcpp_is_floating_point : public true_type {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_floating_point + : public __libcpp_is_floating_point::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_floating_point_v + = is_floating_point<_Tp>::value; +#endif + + // is_array + + template struct __WI_LIBCPP_TEMPLATE_VIS is_array + : public false_type {}; + template struct __WI_LIBCPP_TEMPLATE_VIS is_array<_Tp[]> + : public true_type {}; + template struct __WI_LIBCPP_TEMPLATE_VIS is_array<_Tp[_Np]> + : public true_type {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_array_v + = is_array<_Tp>::value; +#endif + + // is_pointer + + template struct __libcpp_is_pointer : public false_type {}; + template struct __libcpp_is_pointer<_Tp*> : public true_type {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_pointer + : public __libcpp_is_pointer::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_pointer_v + = is_pointer<_Tp>::value; +#endif + + // is_reference + + template struct __WI_LIBCPP_TEMPLATE_VIS is_lvalue_reference : public false_type {}; + template struct __WI_LIBCPP_TEMPLATE_VIS is_lvalue_reference<_Tp&> : public true_type {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_rvalue_reference : public false_type {}; +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + template struct __WI_LIBCPP_TEMPLATE_VIS is_rvalue_reference<_Tp&&> : public true_type {}; +#endif + + template struct __WI_LIBCPP_TEMPLATE_VIS is_reference : public false_type {}; + template struct __WI_LIBCPP_TEMPLATE_VIS is_reference<_Tp&> : public true_type {}; +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + template struct __WI_LIBCPP_TEMPLATE_VIS is_reference<_Tp&&> : public true_type {}; +#endif + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_reference_v + = is_reference<_Tp>::value; + + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_lvalue_reference_v + = is_lvalue_reference<_Tp>::value; + + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_rvalue_reference_v + = is_rvalue_reference<_Tp>::value; +#endif + // is_union + +#if __WI_HAS_FEATURE_IS_UNION || (__WI_GNUC_VER >= 403) + + template struct __WI_LIBCPP_TEMPLATE_VIS is_union + : public integral_constant {}; + +#else + + template struct __libcpp_union : public false_type {}; + template struct __WI_LIBCPP_TEMPLATE_VIS is_union + : public __libcpp_union::type> {}; + +#endif + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_union_v + = is_union<_Tp>::value; +#endif + + // is_class + +#if __WI_HAS_FEATURE_IS_CLASS || (__WI_GNUC_VER >= 403) + + template struct __WI_LIBCPP_TEMPLATE_VIS is_class + : public integral_constant {}; + +#else + + namespace __is_class_imp + { + template char __test(int _Tp::*); + template __two __test(...); + } + + template struct __WI_LIBCPP_TEMPLATE_VIS is_class + : public integral_constant(0)) == 1 && !is_union<_Tp>::value> {}; + +#endif + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_class_v + = is_class<_Tp>::value; +#endif + + // is_same + + template struct __WI_LIBCPP_TEMPLATE_VIS is_same : public false_type {}; + template struct __WI_LIBCPP_TEMPLATE_VIS is_same<_Tp, _Tp> : public true_type {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_same_v + = is_same<_Tp, _Up>::value; +#endif + + // is_function + + namespace __libcpp_is_function_imp + { + struct __dummy_type {}; + template char __test(_Tp*); + template char __test(__dummy_type); + template __two __test(...); + template _Tp& __source(int); + template __dummy_type __source(...); + } + + template ::value || + is_union<_Tp>::value || + is_void<_Tp>::value || + is_reference<_Tp>::value || + __is_nullptr_t<_Tp>::value > + struct __libcpp_is_function + : public integral_constant(__libcpp_is_function_imp::__source<_Tp>(0))) == 1> + {}; + template struct __libcpp_is_function<_Tp, true> : public false_type {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_function + : public __libcpp_is_function<_Tp> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_function_v + = is_function<_Tp>::value; +#endif + + // is_member_function_pointer + + // template struct __libcpp_is_member_function_pointer : public false_type {}; + // template struct __libcpp_is_member_function_pointer<_Tp _Up::*> : public is_function<_Tp> {}; + // + + template + struct __member_pointer_traits_imp + { // forward declaration; specializations later + }; + + + template struct __libcpp_is_member_function_pointer + : public false_type {}; + + template + struct __libcpp_is_member_function_pointer<_Ret _Class::*> + : public is_function<_Ret> {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_member_function_pointer + : public __libcpp_is_member_function_pointer::type>::type {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_member_function_pointer_v + = is_member_function_pointer<_Tp>::value; +#endif + + // is_member_pointer + + template struct __libcpp_is_member_pointer : public false_type {}; + template struct __libcpp_is_member_pointer<_Tp _Up::*> : public true_type {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_member_pointer + : public __libcpp_is_member_pointer::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_member_pointer_v + = is_member_pointer<_Tp>::value; +#endif + + // is_member_object_pointer + + template struct __WI_LIBCPP_TEMPLATE_VIS is_member_object_pointer + : public integral_constant::value && + !is_member_function_pointer<_Tp>::value> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_member_object_pointer_v + = is_member_object_pointer<_Tp>::value; +#endif + + // is_enum + +#if __WI_HAS_FEATURE_IS_ENUM || (__WI_GNUC_VER >= 403) + + template struct __WI_LIBCPP_TEMPLATE_VIS is_enum + : public integral_constant {}; + +#else + + template struct __WI_LIBCPP_TEMPLATE_VIS is_enum + : public integral_constant::value && + !is_integral<_Tp>::value && + !is_floating_point<_Tp>::value && + !is_array<_Tp>::value && + !is_pointer<_Tp>::value && + !is_reference<_Tp>::value && + !is_member_pointer<_Tp>::value && + !is_union<_Tp>::value && + !is_class<_Tp>::value && + !is_function<_Tp>::value > {}; + +#endif + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_enum_v + = is_enum<_Tp>::value; +#endif + + // is_arithmetic + + template struct __WI_LIBCPP_TEMPLATE_VIS is_arithmetic + : public integral_constant::value || + is_floating_point<_Tp>::value> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_arithmetic_v + = is_arithmetic<_Tp>::value; +#endif + + // is_fundamental + + template struct __WI_LIBCPP_TEMPLATE_VIS is_fundamental + : public integral_constant::value || + __is_nullptr_t<_Tp>::value || + is_arithmetic<_Tp>::value> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_fundamental_v + = is_fundamental<_Tp>::value; +#endif + + // is_scalar + + template struct __WI_LIBCPP_TEMPLATE_VIS is_scalar + : public integral_constant::value || + is_member_pointer<_Tp>::value || + is_pointer<_Tp>::value || + __is_nullptr_t<_Tp>::value || + is_enum<_Tp>::value > {}; + + template <> struct __WI_LIBCPP_TEMPLATE_VIS is_scalar : public true_type {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_scalar_v + = is_scalar<_Tp>::value; +#endif + + // is_object + + template struct __WI_LIBCPP_TEMPLATE_VIS is_object + : public integral_constant::value || + is_array<_Tp>::value || + is_union<_Tp>::value || + is_class<_Tp>::value > {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_object_v + = is_object<_Tp>::value; +#endif + + // is_compound + + template struct __WI_LIBCPP_TEMPLATE_VIS is_compound + : public integral_constant::value> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_compound_v + = is_compound<_Tp>::value; +#endif + + + // __is_referenceable [defns.referenceable] + + struct __is_referenceable_impl { + template static _Tp& __test(int); + template static __two __test(...); + }; + + template + struct __is_referenceable : integral_constant(0)), __two>::value> {}; + + + // add_const + + template ::value || + is_function<_Tp>::value || + is_const<_Tp>::value > + struct __add_const {typedef _Tp type;}; + + template + struct __add_const<_Tp, false> {typedef const _Tp type;}; + + template struct __WI_LIBCPP_TEMPLATE_VIS add_const + {typedef typename __add_const<_Tp>::type type;}; + +#if __WI_LIBCPP_STD_VER > 11 + template using add_const_t = typename add_const<_Tp>::type; +#endif + + // add_volatile + + template ::value || + is_function<_Tp>::value || + is_volatile<_Tp>::value > + struct __add_volatile {typedef _Tp type;}; + + template + struct __add_volatile<_Tp, false> {typedef volatile _Tp type;}; + + template struct __WI_LIBCPP_TEMPLATE_VIS add_volatile + {typedef typename __add_volatile<_Tp>::type type;}; + +#if __WI_LIBCPP_STD_VER > 11 + template using add_volatile_t = typename add_volatile<_Tp>::type; +#endif + + // add_cv + + template struct __WI_LIBCPP_TEMPLATE_VIS add_cv + {typedef typename add_const::type>::type type;}; + +#if __WI_LIBCPP_STD_VER > 11 + template using add_cv_t = typename add_cv<_Tp>::type; +#endif + + // remove_reference + + template struct __WI_LIBCPP_TEMPLATE_VIS remove_reference {typedef _Tp type;}; + template struct __WI_LIBCPP_TEMPLATE_VIS remove_reference<_Tp&> {typedef _Tp type;}; +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + template struct __WI_LIBCPP_TEMPLATE_VIS remove_reference<_Tp&&> {typedef _Tp type;}; +#endif + +#if __WI_LIBCPP_STD_VER > 11 + template using remove_reference_t = typename remove_reference<_Tp>::type; +#endif + + // add_lvalue_reference + + template ::value> struct __add_lvalue_reference_impl { typedef _Tp type; }; + template struct __add_lvalue_reference_impl<_Tp, true> { typedef _Tp& type; }; + + template struct __WI_LIBCPP_TEMPLATE_VIS add_lvalue_reference + {typedef typename __add_lvalue_reference_impl<_Tp>::type type;}; + +#if __WI_LIBCPP_STD_VER > 11 + template using add_lvalue_reference_t = typename add_lvalue_reference<_Tp>::type; +#endif + +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + + template ::value> struct __add_rvalue_reference_impl { typedef _Tp type; }; + template struct __add_rvalue_reference_impl<_Tp, true> { typedef _Tp&& type; }; + + template struct __WI_LIBCPP_TEMPLATE_VIS add_rvalue_reference + {typedef typename __add_rvalue_reference_impl<_Tp>::type type;}; + +#if __WI_LIBCPP_STD_VER > 11 + template using add_rvalue_reference_t = typename add_rvalue_reference<_Tp>::type; +#endif + +#endif // __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + + // MSVC has issues compiling some source code that uses the libc++ definition of 'declval' +#ifdef _MSC_VER + template + typename add_rvalue_reference<_Tp>::type declval() WI_NOEXCEPT; +#else + template _Tp&& __declval(int); + template _Tp __declval(long); + + template + decltype(__declval<_Tp>(0)) + declval() WI_NOEXCEPT; +#endif + +#else // __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + + template + typename add_lvalue_reference<_Tp>::type + declval(); + +#endif // __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + + // __uncvref + + template + struct __uncvref { + typedef typename remove_cv::type>::type type; + }; + + template + struct __unconstref { + typedef typename remove_const::type>::type type; + }; + +#ifndef __WI_LIBCPP_CXX03_LANG + template + using __uncvref_t = typename __uncvref<_Tp>::type; +#endif + + // __is_same_uncvref + + template + struct __is_same_uncvref : is_same::type, + typename __uncvref<_Up>::type> {}; + +#if __WI_LIBCPP_STD_VER > 17 + // remove_cvref - same as __uncvref + template + struct remove_cvref : public __uncvref<_Tp> {}; + + template using remove_cvref_t = typename remove_cvref<_Tp>::type; +#endif + + + struct __any + { + __any(...); + }; + + // remove_pointer + + template struct __WI_LIBCPP_TEMPLATE_VIS remove_pointer {typedef _Tp type;}; + template struct __WI_LIBCPP_TEMPLATE_VIS remove_pointer<_Tp*> {typedef _Tp type;}; + template struct __WI_LIBCPP_TEMPLATE_VIS remove_pointer<_Tp* const> {typedef _Tp type;}; + template struct __WI_LIBCPP_TEMPLATE_VIS remove_pointer<_Tp* volatile> {typedef _Tp type;}; + template struct __WI_LIBCPP_TEMPLATE_VIS remove_pointer<_Tp* const volatile> {typedef _Tp type;}; + +#if __WI_LIBCPP_STD_VER > 11 + template using remove_pointer_t = typename remove_pointer<_Tp>::type; +#endif + + // add_pointer + + template ::value || + is_same::type, void>::value> + struct __add_pointer_impl + {typedef typename remove_reference<_Tp>::type* type;}; + template struct __add_pointer_impl<_Tp, false> + {typedef _Tp type;}; + + template struct __WI_LIBCPP_TEMPLATE_VIS add_pointer + {typedef typename __add_pointer_impl<_Tp>::type type;}; + +#if __WI_LIBCPP_STD_VER > 11 + template using add_pointer_t = typename add_pointer<_Tp>::type; +#endif + + // type_identity +#if __WI_LIBCPP_STD_VER > 17 + template struct type_identity { typedef _Tp type; }; + template using type_identity_t = typename type_identity<_Tp>::type; +#endif + + // is_signed + + template ::value> + struct __libcpp_is_signed_impl : public __WI_LIBCPP_BOOL_CONSTANT(_Tp(-1) < _Tp(0)) {}; + + template + struct __libcpp_is_signed_impl<_Tp, false> : public true_type {}; // floating point + + template ::value> + struct __libcpp_is_signed : public __libcpp_is_signed_impl<_Tp> {}; + + template struct __libcpp_is_signed<_Tp, false> : public false_type {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_signed : public __libcpp_is_signed<_Tp> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_signed_v + = is_signed<_Tp>::value; +#endif + + // is_unsigned + + template ::value> + struct __libcpp_is_unsigned_impl : public __WI_LIBCPP_BOOL_CONSTANT(_Tp(0) < _Tp(-1)) {}; + + template + struct __libcpp_is_unsigned_impl<_Tp, false> : public false_type {}; // floating point + + template ::value> + struct __libcpp_is_unsigned : public __libcpp_is_unsigned_impl<_Tp> {}; + + template struct __libcpp_is_unsigned<_Tp, false> : public false_type {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_unsigned : public __libcpp_is_unsigned<_Tp> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_unsigned_v + = is_unsigned<_Tp>::value; +#endif + + // rank + + template struct __WI_LIBCPP_TEMPLATE_VIS rank + : public integral_constant {}; + template struct __WI_LIBCPP_TEMPLATE_VIS rank<_Tp[]> + : public integral_constant::value + 1> {}; + template struct __WI_LIBCPP_TEMPLATE_VIS rank<_Tp[_Np]> + : public integral_constant::value + 1> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR size_t rank_v + = rank<_Tp>::value; +#endif + + // extent + + template struct __WI_LIBCPP_TEMPLATE_VIS extent + : public integral_constant {}; + template struct __WI_LIBCPP_TEMPLATE_VIS extent<_Tp[], 0> + : public integral_constant {}; + template struct __WI_LIBCPP_TEMPLATE_VIS extent<_Tp[], _Ip> + : public integral_constant::value> {}; + template struct __WI_LIBCPP_TEMPLATE_VIS extent<_Tp[_Np], 0> + : public integral_constant {}; + template struct __WI_LIBCPP_TEMPLATE_VIS extent<_Tp[_Np], _Ip> + : public integral_constant::value> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR size_t extent_v + = extent<_Tp, _Ip>::value; +#endif + + // remove_extent + + template struct __WI_LIBCPP_TEMPLATE_VIS remove_extent + {typedef _Tp type;}; + template struct __WI_LIBCPP_TEMPLATE_VIS remove_extent<_Tp[]> + {typedef _Tp type;}; + template struct __WI_LIBCPP_TEMPLATE_VIS remove_extent<_Tp[_Np]> + {typedef _Tp type;}; + +#if __WI_LIBCPP_STD_VER > 11 + template using remove_extent_t = typename remove_extent<_Tp>::type; +#endif + + // remove_all_extents + + template struct __WI_LIBCPP_TEMPLATE_VIS remove_all_extents + {typedef _Tp type;}; + template struct __WI_LIBCPP_TEMPLATE_VIS remove_all_extents<_Tp[]> + {typedef typename remove_all_extents<_Tp>::type type;}; + template struct __WI_LIBCPP_TEMPLATE_VIS remove_all_extents<_Tp[_Np]> + {typedef typename remove_all_extents<_Tp>::type type;}; + +#if __WI_LIBCPP_STD_VER > 11 + template using remove_all_extents_t = typename remove_all_extents<_Tp>::type; +#endif + + // decay + + template + struct __decay { + typedef typename remove_cv<_Up>::type type; + }; + + template + struct __decay<_Up, true> { + public: + typedef typename conditional + < + is_array<_Up>::value, + typename remove_extent<_Up>::type*, + typename conditional + < + is_function<_Up>::value, + typename add_pointer<_Up>::type, + typename remove_cv<_Up>::type + >::type + >::type type; + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS decay + { + private: + typedef typename remove_reference<_Tp>::type _Up; + public: + typedef typename __decay<_Up, __is_referenceable<_Up>::value>::type type; + }; + +#if __WI_LIBCPP_STD_VER > 11 + template using decay_t = typename decay<_Tp>::type; +#endif + + // is_abstract + + template struct __WI_LIBCPP_TEMPLATE_VIS is_abstract + : public integral_constant {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_abstract_v + = is_abstract<_Tp>::value; +#endif + + // is_final + +#if defined(__WI_LIBCPP_HAS_IS_FINAL) + template struct __WI_LIBCPP_TEMPLATE_VIS + __libcpp_is_final : public integral_constant {}; +#else + template struct __WI_LIBCPP_TEMPLATE_VIS + __libcpp_is_final : public false_type {}; +#endif + +#if defined(__WI_LIBCPP_HAS_IS_FINAL) && __WI_LIBCPP_STD_VER > 11 + template struct __WI_LIBCPP_TEMPLATE_VIS + is_final : public integral_constant {}; +#endif + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_final_v + = is_final<_Tp>::value; +#endif + + // is_aggregate +#if __WI_LIBCPP_STD_VER > 14 && !defined(__WI_LIBCPP_HAS_NO_IS_AGGREGATE) + + template struct __WI_LIBCPP_TEMPLATE_VIS + is_aggregate : public integral_constant {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR constexpr bool is_aggregate_v + = is_aggregate<_Tp>::value; +#endif + +#endif // __WI_LIBCPP_STD_VER > 14 && !defined(__WI_LIBCPP_HAS_NO_IS_AGGREGATE) + + // is_base_of + +#ifdef __WI_LIBCPP_HAS_IS_BASE_OF + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_base_of + : public integral_constant {}; + +#else // __WI_LIBCPP_HAS_IS_BASE_OF + + namespace __is_base_of_imp + { + template + struct _Dst + { + _Dst(const volatile _Tp &); + }; + template + struct _Src + { + operator const volatile _Tp &(); + template operator const _Dst<_Up> &(); + }; + template struct __one { typedef char type; }; + template typename __one(declval<_Src<_Dp> >()))>::type __test(int); + template __two __test(...); + } + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_base_of + : public integral_constant::value && + sizeof(__is_base_of_imp::__test<_Bp, _Dp>(0)) == 2> {}; + +#endif // __WI_LIBCPP_HAS_IS_BASE_OF + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_base_of_v + = is_base_of<_Bp, _Dp>::value; +#endif + + // is_convertible + +#if __WI_HAS_FEATURE_IS_CONVERTIBLE_TO && !defined(__WI_LIBCPP_USE_IS_CONVERTIBLE_FALLBACK) + + template struct __WI_LIBCPP_TEMPLATE_VIS is_convertible + : public integral_constant::value> {}; + +#else // __WI_HAS_FEATURE_IS_CONVERTIBLE_TO + + namespace __is_convertible_imp + { + template void __test_convert(_Tp); + + template + struct __is_convertible_test : public false_type {}; + + template + struct __is_convertible_test<_From, _To, + decltype(__is_convertible_imp::__test_convert<_To>(declval<_From>()))> : public true_type + {}; + + template ::value, + bool _IsFunction = is_function<_Tp>::value, + bool _IsVoid = is_void<_Tp>::value> + struct __is_array_function_or_void {enum {value = 0};}; + template struct __is_array_function_or_void<_Tp, true, false, false> {enum {value = 1};}; + template struct __is_array_function_or_void<_Tp, false, true, false> {enum {value = 2};}; + template struct __is_array_function_or_void<_Tp, false, false, true> {enum {value = 3};}; + } + + template ::type>::value> + struct __is_convertible_check + { + static const size_t __v = 0; + }; + + template + struct __is_convertible_check<_Tp, 0> + { + static const size_t __v = sizeof(_Tp); + }; + + template ::value, + unsigned _T2_is_array_function_or_void = __is_convertible_imp::__is_array_function_or_void<_T2>::value> + struct __is_convertible + : public integral_constant::value +#if defined(__WI_LIBCPP_HAS_NO_RVALUE_REFERENCES) + && !(!is_function<_T1>::value && !is_reference<_T1>::value && is_reference<_T2>::value + && (!is_const::type>::value + || is_volatile::type>::value) + && (is_same::type, + typename remove_cv::type>::type>::value + || is_base_of::type, _T1>::value)) +#endif + > + {}; + + template struct __is_convertible<_T1, _T2, 0, 1> : public false_type {}; + template struct __is_convertible<_T1, _T2, 1, 1> : public false_type {}; + template struct __is_convertible<_T1, _T2, 2, 1> : public false_type {}; + template struct __is_convertible<_T1, _T2, 3, 1> : public false_type {}; + + template struct __is_convertible<_T1, _T2, 0, 2> : public false_type {}; + template struct __is_convertible<_T1, _T2, 1, 2> : public false_type {}; + template struct __is_convertible<_T1, _T2, 2, 2> : public false_type {}; + template struct __is_convertible<_T1, _T2, 3, 2> : public false_type {}; + + template struct __is_convertible<_T1, _T2, 0, 3> : public false_type {}; + template struct __is_convertible<_T1, _T2, 1, 3> : public false_type {}; + template struct __is_convertible<_T1, _T2, 2, 3> : public false_type {}; + template struct __is_convertible<_T1, _T2, 3, 3> : public true_type {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_convertible + : public __is_convertible<_T1, _T2> + { + static const size_t __complete_check1 = __is_convertible_check<_T1>::__v; + static const size_t __complete_check2 = __is_convertible_check<_T2>::__v; + }; + +#endif // __WI_HAS_FEATURE_IS_CONVERTIBLE_TO + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_convertible_v + = is_convertible<_From, _To>::value; +#endif + + // is_empty + +#if __WI_HAS_FEATURE_IS_EMPTY || (__WI_GNUC_VER >= 407) + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_empty + : public integral_constant {}; + +#else // __WI_HAS_FEATURE_IS_EMPTY + + template + struct __is_empty1 + : public _Tp + { + double __lx; + }; + + struct __is_empty2 + { + double __lx; + }; + + template ::value> + struct __libcpp_empty : public integral_constant) == sizeof(__is_empty2)> {}; + + template struct __libcpp_empty<_Tp, false> : public false_type {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_empty : public __libcpp_empty<_Tp> {}; + +#endif // __WI_HAS_FEATURE_IS_EMPTY + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_empty_v + = is_empty<_Tp>::value; +#endif + + // is_polymorphic + +#if __WI_HAS_FEATURE_IS_POLYMORPHIC + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_polymorphic + : public integral_constant {}; + +#else + + template char &__is_polymorphic_impl( + typename enable_if(declval<_Tp*>())) != 0, + int>::type); + template __two &__is_polymorphic_impl(...); + + template struct __WI_LIBCPP_TEMPLATE_VIS is_polymorphic + : public integral_constant(0)) == 1> {}; + +#endif // __WI_HAS_FEATURE_IS_POLYMORPHIC + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_polymorphic_v + = is_polymorphic<_Tp>::value; +#endif + + // has_virtual_destructor + +#if __WI_HAS_FEATURE_HAS_VIRTUAL_DESTRUCTOR || (__WI_GNUC_VER >= 403) + + template struct __WI_LIBCPP_TEMPLATE_VIS has_virtual_destructor + : public integral_constant {}; + +#else + + template struct __WI_LIBCPP_TEMPLATE_VIS has_virtual_destructor + : public false_type {}; + +#endif + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool has_virtual_destructor_v + = has_virtual_destructor<_Tp>::value; +#endif + + // has_unique_object_representations + +#if __WI_LIBCPP_STD_VER > 14 && defined(__WI_LIBCPP_HAS_UNIQUE_OBJECT_REPRESENTATIONS) + + template struct __WI_LIBCPP_TEMPLATE_VIS has_unique_object_representations + : public integral_constant>)> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool has_unique_object_representations_v + = has_unique_object_representations<_Tp>::value; +#endif + +#endif + + // alignment_of + + template struct __WI_LIBCPP_TEMPLATE_VIS alignment_of + : public integral_constant {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR size_t alignment_of_v + = alignment_of<_Tp>::value; +#endif + + // aligned_storage + + template + struct __type_list + { + typedef _Hp _Head; + typedef _Tp _Tail; + }; + + struct __nat + { +#ifndef __WI_LIBCPP_CXX03_LANG + __nat() = delete; + __nat(const __nat&) = delete; + __nat& operator=(const __nat&) = delete; + ~__nat() = delete; +#endif + }; + + template + struct __align_type + { + static const size_t value = alignment_of<_Tp>::value; + typedef _Tp type; + }; + + struct __struct_double {long double __lx;}; + struct __struct_double4 {double __lx[4];}; + + typedef + __type_list<__align_type, + __type_list<__align_type, + __type_list<__align_type, + __type_list<__align_type, + __type_list<__align_type, + __type_list<__align_type, + __type_list<__align_type, + __type_list<__align_type<__struct_double>, + __type_list<__align_type<__struct_double4>, + __type_list<__align_type, + __nat + > > > > > > > > > > __all_types; + + template struct __find_pod; + + template + struct __find_pod<__type_list<_Hp, __nat>, _Align> + { + typedef typename conditional< + _Align == _Hp::value, + typename _Hp::type, + void + >::type type; + }; + + template + struct __find_pod<__type_list<_Hp, _Tp>, _Align> + { + typedef typename conditional< + _Align == _Hp::value, + typename _Hp::type, + typename __find_pod<_Tp, _Align>::type + >::type type; + }; + + template + struct __has_pod_with_align : public integral_constant::type, void>::value> {}; + + template struct __find_max_align; + + template + struct __find_max_align<__type_list<_Hp, __nat>, _Len> : public integral_constant {}; + + template + struct __select_align + { + private: + static const size_t __min = _A2 < _A1 ? _A2 : _A1; + static const size_t __max = _A1 < _A2 ? _A2 : _A1; + public: + static const size_t value = _Len < __max ? __min : __max; + }; + + template + struct __find_max_align<__type_list<_Hp, _Tp>, _Len> + : public integral_constant::value>::value> {}; + + template ::value> + struct __aligned_storage + { + typedef typename __find_pod<__all_types, _Align>::type _Aligner; + static_assert(!is_void<_Aligner>::value, ""); + union type + { + _Aligner __align; + unsigned char __data[(_Len + _Align - 1)/_Align * _Align]; + }; + }; + +#define __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(n) \ + template \ + struct __aligned_storage<_Len, n, false>\ + {\ + struct __WI_ALIGNAS(n) type\ + {\ + unsigned char __lx[(_Len + n - 1)/n * n];\ + };\ + } + + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x1); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x2); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x4); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x8); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x10); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x20); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x40); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x80); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x100); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x200); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x400); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x800); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x1000); + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x2000); + // PE/COFF does not support alignment beyond 8192 (=0x2000) +#if !defined(__WI_LIBCPP_OBJECT_FORMAT_COFF) + __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION(0x4000); +#endif // !defined(__WI_LIBCPP_OBJECT_FORMAT_COFF) + +#undef __WI_CREATE_ALIGNED_STORAGE_SPECIALIZATION + + template ::value> + struct __WI_LIBCPP_TEMPLATE_VIS aligned_storage : public __aligned_storage<_Len, _Align> {}; + +#if __WI_LIBCPP_STD_VER > 11 + template ::value> + using aligned_storage_t = typename aligned_storage<_Len, _Align>::type; +#endif + +#ifndef __WI_LIBCPP_HAS_NO_VARIADICS + + // aligned_union + + template + struct __static_max; + + template + struct __static_max<_I0> + { + static const size_t value = _I0; + }; + + template + struct __static_max<_I0, _I1, _In...> + { + static const size_t value = _I0 >= _I1 ? __static_max<_I0, _In...>::value : + __static_max<_I1, _In...>::value; + }; + + template + struct aligned_union + { + static const size_t alignment_value = __static_max<__alignof__(_Type0), + __alignof__(_Types)...>::value; + static const size_t __len = __static_max<_Len, sizeof(_Type0), + sizeof(_Types)...>::value; + typedef typename aligned_storage<__len, alignment_value>::type type; + }; + +#if __WI_LIBCPP_STD_VER > 11 + template using aligned_union_t = typename aligned_union<_Len, _Types...>::type; +#endif + +#endif // __WI_LIBCPP_HAS_NO_VARIADICS + + template + struct __numeric_type + { + static void __test(...); + static float __test(float); + static double __test(char); + static double __test(int); + static double __test(unsigned); + static double __test(long); + static double __test(unsigned long); + static double __test(long long); + static double __test(unsigned long long); + static double __test(double); + static long double __test(long double); + + typedef decltype(__test(declval<_Tp>())) type; + static const bool value = !is_same::value; + }; + + template <> + struct __numeric_type + { + static const bool value = true; + }; + + // __promote + + template ::value && + __numeric_type<_A2>::value && + __numeric_type<_A3>::value> + class __promote_imp + { + public: + static const bool value = false; + }; + + template + class __promote_imp<_A1, _A2, _A3, true> + { + private: + typedef typename __promote_imp<_A1>::type __type1; + typedef typename __promote_imp<_A2>::type __type2; + typedef typename __promote_imp<_A3>::type __type3; + public: + typedef decltype(__type1() + __type2() + __type3()) type; + static const bool value = true; + }; + + template + class __promote_imp<_A1, _A2, void, true> + { + private: + typedef typename __promote_imp<_A1>::type __type1; + typedef typename __promote_imp<_A2>::type __type2; + public: + typedef decltype(__type1() + __type2()) type; + static const bool value = true; + }; + + template + class __promote_imp<_A1, void, void, true> + { + public: + typedef typename __numeric_type<_A1>::type type; + static const bool value = true; + }; + + template + class __promote : public __promote_imp<_A1, _A2, _A3> {}; + + // make_signed / make_unsigned + + typedef + __type_list +#endif + > > > > > __signed_types; + + typedef + __type_list +#endif + > > > > > __unsigned_types; + + template struct __find_first; + + template + struct __find_first<__type_list<_Hp, _Tp>, _Size, true> + { + typedef _Hp type; + }; + + template + struct __find_first<__type_list<_Hp, _Tp>, _Size, false> + { + typedef typename __find_first<_Tp, _Size>::type type; + }; + + template ::type>::value, + bool = is_volatile::type>::value> + struct __apply_cv + { + typedef _Up type; + }; + + template + struct __apply_cv<_Tp, _Up, true, false> + { + typedef const _Up type; + }; + + template + struct __apply_cv<_Tp, _Up, false, true> + { + typedef volatile _Up type; + }; + + template + struct __apply_cv<_Tp, _Up, true, true> + { + typedef const volatile _Up type; + }; + + template + struct __apply_cv<_Tp&, _Up, false, false> + { + typedef _Up& type; + }; + + template + struct __apply_cv<_Tp&, _Up, true, false> + { + typedef const _Up& type; + }; + + template + struct __apply_cv<_Tp&, _Up, false, true> + { + typedef volatile _Up& type; + }; + + template + struct __apply_cv<_Tp&, _Up, true, true> + { + typedef const volatile _Up& type; + }; + + template ::value || is_enum<_Tp>::value> + struct __make_signed {}; + + template + struct __make_signed<_Tp, true> + { + typedef typename __find_first<__signed_types, sizeof(_Tp)>::type type; + }; + + template <> struct __make_signed {}; + template <> struct __make_signed< signed short, true> {typedef short type;}; + template <> struct __make_signed {typedef short type;}; + template <> struct __make_signed< signed int, true> {typedef int type;}; + template <> struct __make_signed {typedef int type;}; + template <> struct __make_signed< signed long, true> {typedef long type;}; + template <> struct __make_signed {typedef long type;}; + template <> struct __make_signed< signed long long, true> {typedef long long type;}; + template <> struct __make_signed {typedef long long type;}; +#ifndef __WI_LIBCPP_HAS_NO_INT128 + template <> struct __make_signed<__int128_t, true> {typedef __int128_t type;}; + template <> struct __make_signed<__uint128_t, true> {typedef __int128_t type;}; +#endif + + template + struct __WI_LIBCPP_TEMPLATE_VIS make_signed + { + typedef typename __apply_cv<_Tp, typename __make_signed::type>::type>::type type; + }; + +#if __WI_LIBCPP_STD_VER > 11 + template using make_signed_t = typename make_signed<_Tp>::type; +#endif + + template ::value || is_enum<_Tp>::value> + struct __make_unsigned {}; + + template + struct __make_unsigned<_Tp, true> + { + typedef typename __find_first<__unsigned_types, sizeof(_Tp)>::type type; + }; + + template <> struct __make_unsigned {}; + template <> struct __make_unsigned< signed short, true> {typedef unsigned short type;}; + template <> struct __make_unsigned {typedef unsigned short type;}; + template <> struct __make_unsigned< signed int, true> {typedef unsigned int type;}; + template <> struct __make_unsigned {typedef unsigned int type;}; + template <> struct __make_unsigned< signed long, true> {typedef unsigned long type;}; + template <> struct __make_unsigned {typedef unsigned long type;}; + template <> struct __make_unsigned< signed long long, true> {typedef unsigned long long type;}; + template <> struct __make_unsigned {typedef unsigned long long type;}; +#ifndef __WI_LIBCPP_HAS_NO_INT128 + template <> struct __make_unsigned<__int128_t, true> {typedef __uint128_t type;}; + template <> struct __make_unsigned<__uint128_t, true> {typedef __uint128_t type;}; +#endif + + template + struct __WI_LIBCPP_TEMPLATE_VIS make_unsigned + { + typedef typename __apply_cv<_Tp, typename __make_unsigned::type>::type>::type type; + }; + +#if __WI_LIBCPP_STD_VER > 11 + template using make_unsigned_t = typename make_unsigned<_Tp>::type; +#endif + +#ifdef __WI_LIBCPP_HAS_NO_VARIADICS + + template + struct __WI_LIBCPP_TEMPLATE_VIS common_type + { + public: + typedef typename common_type::type, _Vp>::type type; + }; + + template <> + struct __WI_LIBCPP_TEMPLATE_VIS common_type + { + public: + typedef void type; + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS common_type<_Tp, void, void> + { + public: + typedef typename common_type<_Tp, _Tp>::type type; + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS common_type<_Tp, _Up, void> + { + typedef typename decay() : declval<_Up>() + )>::type type; + }; + +#else // __WI_LIBCPP_HAS_NO_VARIADICS + + // bullet 1 - sizeof...(Tp) == 0 + + template + struct __WI_LIBCPP_TEMPLATE_VIS common_type {}; + + // bullet 2 - sizeof...(Tp) == 1 + + template + struct __WI_LIBCPP_TEMPLATE_VIS common_type<_Tp> + : public common_type<_Tp, _Tp> {}; + + // bullet 3 - sizeof...(Tp) == 2 + + template + struct __common_type2_imp {}; + + template + struct __common_type2_imp<_Tp, _Up, + typename __void_t() : declval<_Up>() + )>::type> + { + typedef typename decay() : declval<_Up>() + )>::type type; + }; + + template ::type, + class _DUp = typename decay<_Up>::type> + using __common_type2 = + typename conditional< + is_same<_Tp, _DTp>::value && is_same<_Up, _DUp>::value, + __common_type2_imp<_Tp, _Up>, + common_type<_DTp, _DUp> + >::type; + + template + struct __WI_LIBCPP_TEMPLATE_VIS common_type<_Tp, _Up> + : __common_type2<_Tp, _Up> {}; + + // bullet 4 - sizeof...(Tp) > 2 + + template struct __common_types; + + template + struct __common_type_impl {}; + + template + struct __common_type_impl< + __common_types<_Tp, _Up>, + typename __void_t::type>::type> + { + typedef typename common_type<_Tp, _Up>::type type; + }; + + template + struct __common_type_impl<__common_types<_Tp, _Up, _Vp...>, + typename __void_t::type>::type> + : __common_type_impl< + __common_types::type, _Vp...> > + { + + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS common_type<_Tp, _Up, _Vp...> + : __common_type_impl<__common_types<_Tp, _Up, _Vp...> > {}; + +#if __WI_LIBCPP_STD_VER > 11 + template using common_type_t = typename common_type<_Tp...>::type; +#endif + +#endif // __WI_LIBCPP_HAS_NO_VARIADICS + + // is_assignable + + template struct __select_2nd { typedef _Tp type; }; + + template + typename __select_2nd() = declval<_Arg>())), true_type>::type + __is_assignable_test(int); + + template + false_type __is_assignable_test(...); + + + template ::value || is_void<_Arg>::value> + struct __is_assignable_imp + : public decltype((__is_assignable_test<_Tp, _Arg>(0))) {}; + + template + struct __is_assignable_imp<_Tp, _Arg, true> + : public false_type + { + }; + + template + struct is_assignable + : public __is_assignable_imp<_Tp, _Arg> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_assignable_v + = is_assignable<_Tp, _Arg>::value; +#endif + + // is_copy_assignable + + template struct __WI_LIBCPP_TEMPLATE_VIS is_copy_assignable + : public is_assignable::type, + typename add_lvalue_reference::type>::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_copy_assignable_v + = is_copy_assignable<_Tp>::value; +#endif + + // is_move_assignable + + template struct __WI_LIBCPP_TEMPLATE_VIS is_move_assignable +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + : public is_assignable::type, + typename add_rvalue_reference<_Tp>::type> {}; +#else + : public is_copy_assignable<_Tp> {}; +#endif + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_move_assignable_v + = is_move_assignable<_Tp>::value; +#endif + + // is_destructible + +#if __WI_HAS_FEATURE_IS_DESTRUCTIBLE + + template + struct is_destructible + : public integral_constant {}; + +#else + + // if it's a reference, return true + // if it's a function, return false + // if it's void, return false + // if it's an array of unknown bound, return false + // Otherwise, return "std::declval<_Up&>().~_Up()" is well-formed + // where _Up is remove_all_extents<_Tp>::type + + template + struct __is_destructible_apply { typedef int type; }; + + template + struct __is_destructor_wellformed { + template + static char __test ( + typename __is_destructible_apply().~_Tp1())>::type + ); + + template + static __two __test (...); + + static const bool value = sizeof(__test<_Tp>(12)) == sizeof(char); + }; + + template + struct __destructible_imp; + + template + struct __destructible_imp<_Tp, false> + : public integral_constant::type>::value> {}; + + template + struct __destructible_imp<_Tp, true> + : public true_type {}; + + template + struct __destructible_false; + + template + struct __destructible_false<_Tp, false> : public __destructible_imp<_Tp, is_reference<_Tp>::value> {}; + + template + struct __destructible_false<_Tp, true> : public false_type {}; + + template + struct is_destructible + : public __destructible_false<_Tp, is_function<_Tp>::value> {}; + + template + struct is_destructible<_Tp[]> + : public false_type {}; + + template <> + struct is_destructible + : public false_type {}; + +#endif // __WI_HAS_FEATURE_IS_DESTRUCTIBLE + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_destructible_v + = is_destructible<_Tp>::value; +#endif + + // move + +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + + template + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + typename remove_reference<_Tp>::type&& + move(_Tp&& __t) WI_NOEXCEPT + { + typedef typename remove_reference<_Tp>::type _Up; + return static_cast<_Up&&>(__t); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + _Tp&& + forward(typename remove_reference<_Tp>::type& __t) WI_NOEXCEPT + { + return static_cast<_Tp&&>(__t); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + _Tp&& + forward(typename remove_reference<_Tp>::type&& __t) WI_NOEXCEPT + { + static_assert(!is_lvalue_reference<_Tp>::value, + "can not forward an rvalue as an lvalue"); + return static_cast<_Tp&&>(__t); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX17 + _T1 exchange(_T1& __obj, _T2 && __new_value) + { + _T1 __old_value = wistd::move(__obj); + __obj = wistd::forward<_T2>(__new_value); + return __old_value; + } + +#else // __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + _Tp& + move(_Tp& __t) + { + return __t; + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + const _Tp& + move(const _Tp& __t) + { + return __t; + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + _Tp& + forward(typename remove_reference<_Tp>::type& __t) WI_NOEXCEPT + { + return __t; + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + _T1 exchange(_T1& __obj, const _T2& __new_value) + { + _T1 __old_value = __obj; + __obj = __new_value; + return __old_value; + } + + template + class __rv + { + typedef typename remove_reference<_Tp>::type _Trr; + _Trr& t_; + public: + __WI_LIBCPP_INLINE_VISIBILITY + _Trr* operator->() {return &t_;} + __WI_LIBCPP_INLINE_VISIBILITY + explicit __rv(_Trr& __t) : t_(__t) {} + }; + +#endif // __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + +#if __WI_LIBCPP_STD_VER > 11 + template +#else + template +#endif + struct __WI_LIBCPP_TEMPLATE_VIS less : binary_function<_Tp, _Tp, bool> + { + __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 __WI_LIBCPP_INLINE_VISIBILITY + bool operator()(const _Tp& __x, const _Tp& __y) const + {return __x < __y;} + }; + +#if __WI_LIBCPP_STD_VER > 11 + template <> + struct __WI_LIBCPP_TEMPLATE_VIS less + { + template + __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 __WI_LIBCPP_INLINE_VISIBILITY + auto operator()(_T1&& __t, _T2&& __u) const + __WI_NOEXCEPT_(noexcept(wistd::forward<_T1>(__t) < wistd::forward<_T2>(__u))) + -> decltype (wistd::forward<_T1>(__t) < wistd::forward<_T2>(__u)) + { return wistd::forward<_T1>(__t) < wistd::forward<_T2>(__u); } + typedef void is_transparent; + }; +#endif + +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + typename decay<_Tp>::type + __decay_copy(_Tp&& __t) + { + return wistd::forward<_Tp>(__t); + } + +#else + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + typename decay<_Tp>::type + __decay_copy(const _Tp& __t) + { + return wistd::forward<_Tp>(__t); + } + +#endif + +#ifndef __WI_LIBCPP_HAS_NO_VARIADICS + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...), true, false> + { + typedef _Class _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...), true, false> + { + typedef _Class _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...) const, true, false> + { + typedef _Class const _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...) const, true, false> + { + typedef _Class const _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...) volatile, true, false> + { + typedef _Class volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...) volatile, true, false> + { + typedef _Class volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...) const volatile, true, false> + { + typedef _Class const volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...) const volatile, true, false> + { + typedef _Class const volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + +#if __WI_HAS_FEATURE_REFERENCE_QUALIFIED_FUNCTIONS || \ + (defined(__WI_GNUC_VER) && __WI_GNUC_VER >= 409) + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...) &, true, false> + { + typedef _Class& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...) &, true, false> + { + typedef _Class& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...) const&, true, false> + { + typedef _Class const& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...) const&, true, false> + { + typedef _Class const& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...) volatile&, true, false> + { + typedef _Class volatile& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...) volatile&, true, false> + { + typedef _Class volatile& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...) const volatile&, true, false> + { + typedef _Class const volatile& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...) const volatile&, true, false> + { + typedef _Class const volatile& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...) &&, true, false> + { + typedef _Class&& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...) &&, true, false> + { + typedef _Class&& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...) const&&, true, false> + { + typedef _Class const&& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...) const&&, true, false> + { + typedef _Class const&& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...) volatile&&, true, false> + { + typedef _Class volatile&& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...) volatile&&, true, false> + { + typedef _Class volatile&& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param...) const volatile&&, true, false> + { + typedef _Class const volatile&& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_Param..., ...) const volatile&&, true, false> + { + typedef _Class const volatile&& _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_Param..., ...); + }; + +#endif // __WI_HAS_FEATURE_REFERENCE_QUALIFIED_FUNCTIONS || __WI_GNUC_VER >= 409 + +#else // __WI_LIBCPP_HAS_NO_VARIADICS + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(), true, false> + { + typedef _Class _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(...), true, false> + { + typedef _Class _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0), true, false> + { + typedef _Class _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, ...), true, false> + { + typedef _Class _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1), true, false> + { + typedef _Class _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, ...), true, false> + { + typedef _Class _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, _P2), true, false> + { + typedef _Class _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, _P2); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, _P2, ...), true, false> + { + typedef _Class _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, _P2, ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)() const, true, false> + { + typedef _Class const _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(...) const, true, false> + { + typedef _Class const _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0) const, true, false> + { + typedef _Class const _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, ...) const, true, false> + { + typedef _Class const _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1) const, true, false> + { + typedef _Class const _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, ...) const, true, false> + { + typedef _Class const _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, _P2) const, true, false> + { + typedef _Class const _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, _P2); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, _P2, ...) const, true, false> + { + typedef _Class const _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, _P2, ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)() volatile, true, false> + { + typedef _Class volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(...) volatile, true, false> + { + typedef _Class volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0) volatile, true, false> + { + typedef _Class volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, ...) volatile, true, false> + { + typedef _Class volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1) volatile, true, false> + { + typedef _Class volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, ...) volatile, true, false> + { + typedef _Class volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, _P2) volatile, true, false> + { + typedef _Class volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, _P2); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, _P2, ...) volatile, true, false> + { + typedef _Class volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, _P2, ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)() const volatile, true, false> + { + typedef _Class const volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(...) const volatile, true, false> + { + typedef _Class const volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0) const volatile, true, false> + { + typedef _Class const volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, ...) const volatile, true, false> + { + typedef _Class const volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1) const volatile, true, false> + { + typedef _Class const volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, ...) const volatile, true, false> + { + typedef _Class const volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, ...); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, _P2) const volatile, true, false> + { + typedef _Class const volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, _P2); + }; + + template + struct __member_pointer_traits_imp<_Rp (_Class::*)(_P0, _P1, _P2, ...) const volatile, true, false> + { + typedef _Class const volatile _ClassType; + typedef _Rp _ReturnType; + typedef _Rp (_FnType) (_P0, _P1, _P2, ...); + }; + +#endif // __WI_LIBCPP_HAS_NO_VARIADICS + + template + struct __member_pointer_traits_imp<_Rp _Class::*, false, true> + { + typedef _Class _ClassType; + typedef _Rp _ReturnType; + }; + + template + struct __member_pointer_traits + : public __member_pointer_traits_imp::type, + is_member_function_pointer<_Mp>::value, + is_member_object_pointer<_Mp>::value> + { + // typedef ... _ClassType; + // typedef ... _ReturnType; + // typedef ... _FnType; + }; + + + template + struct __member_pointer_class_type {}; + + template + struct __member_pointer_class_type<_Ret _ClassType::*> { + typedef _ClassType type; + }; + + // result_of + + template class result_of; + +#ifdef __WI_LIBCPP_HAS_NO_VARIADICS + + template + class __result_of + { + }; + + template + class __result_of<_Fn(), true, false> + { + public: + typedef decltype(declval<_Fn>()()) type; + }; + + template + class __result_of<_Fn(_A0), true, false> + { + public: + typedef decltype(declval<_Fn>()(declval<_A0>())) type; + }; + + template + class __result_of<_Fn(_A0, _A1), true, false> + { + public: + typedef decltype(declval<_Fn>()(declval<_A0>(), declval<_A1>())) type; + }; + + template + class __result_of<_Fn(_A0, _A1, _A2), true, false> + { + public: + typedef decltype(declval<_Fn>()(declval<_A0>(), declval<_A1>(), declval<_A2>())) type; + }; + + template + struct __result_of_mp; + + // member function pointer + + template + struct __result_of_mp<_Mp, _Tp, true> + : public __identity::_ReturnType> + { + }; + + // member data pointer + + template + struct __result_of_mdp; + + template + struct __result_of_mdp<_Rp _Class::*, _Tp, false> + { + typedef typename __apply_cv()), _Rp>::type& type; + }; + + template + struct __result_of_mdp<_Rp _Class::*, _Tp, true> + { + typedef typename __apply_cv<_Tp, _Rp>::type& type; + }; + + template + struct __result_of_mp<_Rp _Class::*, _Tp, false> + : public __result_of_mdp<_Rp _Class::*, _Tp, + is_base_of<_Class, typename remove_reference<_Tp>::type>::value> + { + }; + + + + template + class __result_of<_Fn(_Tp), false, true> // _Fn must be member pointer + : public __result_of_mp::type, + _Tp, + is_member_function_pointer::type>::value> + { + }; + + template + class __result_of<_Fn(_Tp, _A0), false, true> // _Fn must be member pointer + : public __result_of_mp::type, + _Tp, + is_member_function_pointer::type>::value> + { + }; + + template + class __result_of<_Fn(_Tp, _A0, _A1), false, true> // _Fn must be member pointer + : public __result_of_mp::type, + _Tp, + is_member_function_pointer::type>::value> + { + }; + + template + class __result_of<_Fn(_Tp, _A0, _A1, _A2), false, true> // _Fn must be member pointer + : public __result_of_mp::type, + _Tp, + is_member_function_pointer::type>::value> + { + }; + + // result_of + + template + class __WI_LIBCPP_TEMPLATE_VIS result_of<_Fn()> + : public __result_of<_Fn(), + is_class::type>::value || + is_function::type>::type>::value, + is_member_pointer::type>::value + > + { + }; + + template + class __WI_LIBCPP_TEMPLATE_VIS result_of<_Fn(_A0)> + : public __result_of<_Fn(_A0), + is_class::type>::value || + is_function::type>::type>::value, + is_member_pointer::type>::value + > + { + }; + + template + class __WI_LIBCPP_TEMPLATE_VIS result_of<_Fn(_A0, _A1)> + : public __result_of<_Fn(_A0, _A1), + is_class::type>::value || + is_function::type>::type>::value, + is_member_pointer::type>::value + > + { + }; + + template + class __WI_LIBCPP_TEMPLATE_VIS result_of<_Fn(_A0, _A1, _A2)> + : public __result_of<_Fn(_A0, _A1, _A2), + is_class::type>::value || + is_function::type>::type>::value, + is_member_pointer::type>::value + > + { + }; + +#endif // __WI_LIBCPP_HAS_NO_VARIADICS + + // template struct is_constructible; + + namespace __is_construct + { + struct __nat {}; + } + +#if !defined(__WI_LIBCPP_CXX03_LANG) && (!__WI_HAS_FEATURE_IS_CONSTRUCTIBLE || \ + defined(__WI_LIBCPP_TESTING_FALLBACK_IS_CONSTRUCTIBLE)) + + template + struct __libcpp_is_constructible; + + template + struct __is_invalid_base_to_derived_cast { + static_assert(is_reference<_To>::value, "Wrong specialization"); + using _RawFrom = __uncvref_t<_From>; + using _RawTo = __uncvref_t<_To>; + static const bool value = __lazy_and< + __lazy_not>, + is_base_of<_RawFrom, _RawTo>, + __lazy_not<__libcpp_is_constructible<_RawTo, _From>> + >::value; + }; + + template + struct __is_invalid_lvalue_to_rvalue_cast : false_type { + static_assert(is_reference<_To>::value, "Wrong specialization"); + }; + + template + struct __is_invalid_lvalue_to_rvalue_cast<_ToRef&&, _FromRef&> { + using _RawFrom = __uncvref_t<_FromRef>; + using _RawTo = __uncvref_t<_ToRef>; + static const bool value = __lazy_and< + __lazy_not>, + __lazy_or< + is_same<_RawFrom, _RawTo>, + is_base_of<_RawTo, _RawFrom>> + >::value; + }; + + struct __is_constructible_helper + { + template + static void __eat(_To); + + // This overload is needed to work around a Clang bug that disallows + // static_cast(e) for non-reference-compatible types. + // Example: static_cast(declval()); + // NOTE: The static_cast implementation below is required to support + // classes with explicit conversion operators. + template (declval<_From>()))> + static true_type __test_cast(int); + + template (declval<_From>()))> + static integral_constant::value && + !__is_invalid_lvalue_to_rvalue_cast<_To, _From>::value + > __test_cast(long); + + template + static false_type __test_cast(...); + + template ()...))> + static true_type __test_nary(int); + template + static false_type __test_nary(...); + + template ()))> + static is_destructible<_Tp> __test_unary(int); + template + static false_type __test_unary(...); + }; + + template ::value> + struct __is_default_constructible + : decltype(__is_constructible_helper::__test_nary<_Tp>(0)) + {}; + + template + struct __is_default_constructible<_Tp, true> : false_type {}; + + template + struct __is_default_constructible<_Tp[], false> : false_type {}; + + template + struct __is_default_constructible<_Tp[_Nx], false> + : __is_default_constructible::type> {}; + + template + struct __libcpp_is_constructible + { + static_assert(sizeof...(_Args) > 1, "Wrong specialization"); + typedef decltype(__is_constructible_helper::__test_nary<_Tp, _Args...>(0)) + type; + }; + + template + struct __libcpp_is_constructible<_Tp> : __is_default_constructible<_Tp> {}; + + template + struct __libcpp_is_constructible<_Tp, _A0> + : public decltype(__is_constructible_helper::__test_unary<_Tp, _A0>(0)) + {}; + + template + struct __libcpp_is_constructible<_Tp&, _A0> + : public decltype(__is_constructible_helper:: + __test_cast<_Tp&, _A0>(0)) + {}; + + template + struct __libcpp_is_constructible<_Tp&&, _A0> + : public decltype(__is_constructible_helper:: + __test_cast<_Tp&&, _A0>(0)) + {}; + +#endif + +#if __WI_HAS_FEATURE_IS_CONSTRUCTIBLE + template + struct __WI_LIBCPP_TEMPLATE_VIS is_constructible + : public integral_constant + {}; +#elif !defined(__WI_LIBCPP_CXX03_LANG) + template + struct __WI_LIBCPP_TEMPLATE_VIS is_constructible + : public __libcpp_is_constructible<_Tp, _Args...>::type {}; +#else + // template struct is_constructible0; + + // main is_constructible0 test + + template + decltype((_Tp(), true_type())) + __is_constructible0_test(_Tp&); + + false_type + __is_constructible0_test(__any); + + template + decltype((_Tp(declval<_A0>()), true_type())) + __is_constructible1_test(_Tp&, _A0&); + + template + false_type + __is_constructible1_test(__any, _A0&); + + template + decltype((_Tp(declval<_A0>(), declval<_A1>()), true_type())) + __is_constructible2_test(_Tp&, _A0&, _A1&); + + template + false_type + __is_constructible2_test(__any, _A0&, _A1&); + + template + decltype((_Tp(declval<_A0>(), declval<_A1>(), declval<_A2>()), true_type())) + __is_constructible3_test(_Tp&, _A0&, _A1&, _A2&); + + template + false_type + __is_constructible3_test(__any, _A0&, _A1&, _A2&); + + template + struct __is_constructible0_imp // false, _Tp is not a scalar + : public common_type + < + decltype(__is_constructible0_test(declval<_Tp&>())) + >::type + {}; + + template + struct __is_constructible1_imp // false, _Tp is not a scalar + : public common_type + < + decltype(__is_constructible1_test(declval<_Tp&>(), declval<_A0&>())) + >::type + {}; + + template + struct __is_constructible2_imp // false, _Tp is not a scalar + : public common_type + < + decltype(__is_constructible2_test(declval<_Tp&>(), declval<_A0>(), declval<_A1>())) + >::type + {}; + + template + struct __is_constructible3_imp // false, _Tp is not a scalar + : public common_type + < + decltype(__is_constructible3_test(declval<_Tp&>(), declval<_A0>(), declval<_A1>(), declval<_A2>())) + >::type + {}; + + // handle scalars and reference types + + // Scalars are default constructible, references are not + + template + struct __is_constructible0_imp + : public is_scalar<_Tp> + {}; + + template + struct __is_constructible1_imp + : public is_convertible<_A0, _Tp> + {}; + + template + struct __is_constructible2_imp + : public false_type + {}; + + template + struct __is_constructible3_imp + : public false_type + {}; + + // Treat scalars and reference types separately + + template + struct __is_constructible0_void_check + : public __is_constructible0_imp::value || is_reference<_Tp>::value, + _Tp> + {}; + + template + struct __is_constructible1_void_check + : public __is_constructible1_imp::value || is_reference<_Tp>::value, + _Tp, _A0> + {}; + + template + struct __is_constructible2_void_check + : public __is_constructible2_imp::value || is_reference<_Tp>::value, + _Tp, _A0, _A1> + {}; + + template + struct __is_constructible3_void_check + : public __is_constructible3_imp::value || is_reference<_Tp>::value, + _Tp, _A0, _A1, _A2> + {}; + + // If any of T or Args is void, is_constructible should be false + + template + struct __is_constructible0_void_check + : public false_type + {}; + + template + struct __is_constructible1_void_check + : public false_type + {}; + + template + struct __is_constructible2_void_check + : public false_type + {}; + + template + struct __is_constructible3_void_check + : public false_type + {}; + + // is_constructible entry point + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_constructible + : public __is_constructible3_void_check::value + || is_abstract<_Tp>::value + || is_function<_Tp>::value + || is_void<_A0>::value + || is_void<_A1>::value + || is_void<_A2>::value, + _Tp, _A0, _A1, _A2> + {}; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_constructible<_Tp, __is_construct::__nat, __is_construct::__nat> + : public __is_constructible0_void_check::value + || is_abstract<_Tp>::value + || is_function<_Tp>::value, + _Tp> + {}; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_constructible<_Tp, _A0, __is_construct::__nat> + : public __is_constructible1_void_check::value + || is_abstract<_Tp>::value + || is_function<_Tp>::value + || is_void<_A0>::value, + _Tp, _A0> + {}; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_constructible<_Tp, _A0, _A1, __is_construct::__nat> + : public __is_constructible2_void_check::value + || is_abstract<_Tp>::value + || is_function<_Tp>::value + || is_void<_A0>::value + || is_void<_A1>::value, + _Tp, _A0, _A1> + {}; + + // Array types are default constructible if their element type + // is default constructible + + template + struct __is_constructible0_imp + : public is_constructible::type> + {}; + + template + struct __is_constructible1_imp + : public false_type + {}; + + template + struct __is_constructible2_imp + : public false_type + {}; + + template + struct __is_constructible3_imp + : public false_type + {}; + + // Incomplete array types are not constructible + + template + struct __is_constructible0_imp + : public false_type + {}; + + template + struct __is_constructible1_imp + : public false_type + {}; + + template + struct __is_constructible2_imp + : public false_type + {}; + + template + struct __is_constructible3_imp + : public false_type + {}; + +#endif // __WI_HAS_FEATURE_IS_CONSTRUCTIBLE + + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) && !defined(__WI_LIBCPP_HAS_NO_VARIADICS) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_constructible_v + = is_constructible<_Tp, _Args...>::value; +#endif + + // is_default_constructible + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_default_constructible + : public is_constructible<_Tp> + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_default_constructible_v + = is_default_constructible<_Tp>::value; +#endif + + // is_copy_constructible + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_copy_constructible + : public is_constructible<_Tp, + typename add_lvalue_reference::type>::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_copy_constructible_v + = is_copy_constructible<_Tp>::value; +#endif + + // is_move_constructible + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_move_constructible +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + : public is_constructible<_Tp, typename add_rvalue_reference<_Tp>::type> +#else + : public is_copy_constructible<_Tp> +#endif + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_move_constructible_v + = is_move_constructible<_Tp>::value; +#endif + + // is_trivially_constructible + +#ifndef __WI_LIBCPP_HAS_NO_VARIADICS + +#if __WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE || __WI_GNUC_VER >= 501 + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible + : integral_constant + { + }; + +#else // !__WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible + : false_type + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp> +#if __WI_HAS_FEATURE_HAS_TRIVIAL_CONSTRUCTOR + : integral_constant +#else + : integral_constant::value> +#endif + { + }; + + template +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, _Tp&&> +#else + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, _Tp> +#endif + : integral_constant::value> + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, const _Tp&> + : integral_constant::value> + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, _Tp&> + : integral_constant::value> + { + }; + +#endif // !__WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE + +#else // __WI_LIBCPP_HAS_NO_VARIADICS + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible + : false_type + { + }; + +#if __WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE || __WI_GNUC_VER >= 501 + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, __is_construct::__nat, + __is_construct::__nat> + : integral_constant + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, _Tp, + __is_construct::__nat> + : integral_constant + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, const _Tp&, + __is_construct::__nat> + : integral_constant + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, _Tp&, + __is_construct::__nat> + : integral_constant + { + }; + +#else // !__WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, __is_construct::__nat, + __is_construct::__nat> + : integral_constant::value> + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, _Tp, + __is_construct::__nat> + : integral_constant::value> + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, const _Tp&, + __is_construct::__nat> + : integral_constant::value> + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_constructible<_Tp, _Tp&, + __is_construct::__nat> + : integral_constant::value> + { + }; + +#endif // !__WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE + +#endif // __WI_LIBCPP_HAS_NO_VARIADICS + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) && !defined(__WI_LIBCPP_HAS_NO_VARIADICS) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_trivially_constructible_v + = is_trivially_constructible<_Tp, _Args...>::value; +#endif + + // is_trivially_default_constructible + + template struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_default_constructible + : public is_trivially_constructible<_Tp> + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_trivially_default_constructible_v + = is_trivially_default_constructible<_Tp>::value; +#endif + + // is_trivially_copy_constructible + + template struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_copy_constructible + : public is_trivially_constructible<_Tp, typename add_lvalue_reference::type> + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_trivially_copy_constructible_v + = is_trivially_copy_constructible<_Tp>::value; +#endif + + // is_trivially_move_constructible + + template struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_move_constructible +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + : public is_trivially_constructible<_Tp, typename add_rvalue_reference<_Tp>::type> +#else + : public is_trivially_copy_constructible<_Tp> +#endif + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_trivially_move_constructible_v + = is_trivially_move_constructible<_Tp>::value; +#endif + + // is_trivially_assignable + +#if __WI_HAS_FEATURE_IS_TRIVIALLY_ASSIGNABLE || __WI_GNUC_VER >= 501 + + template + struct is_trivially_assignable + : integral_constant + { + }; + +#else // !__WI_HAS_FEATURE_IS_TRIVIALLY_ASSIGNABLE + + template + struct is_trivially_assignable + : public false_type {}; + + template + struct is_trivially_assignable<_Tp&, _Tp> + : integral_constant::value> {}; + + template + struct is_trivially_assignable<_Tp&, _Tp&> + : integral_constant::value> {}; + + template + struct is_trivially_assignable<_Tp&, const _Tp&> + : integral_constant::value> {}; + +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + + template + struct is_trivially_assignable<_Tp&, _Tp&&> + : integral_constant::value> {}; + +#endif // __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + +#endif // !__WI_HAS_FEATURE_IS_TRIVIALLY_ASSIGNABLE + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_trivially_assignable_v + = is_trivially_assignable<_Tp, _Arg>::value; +#endif + + // is_trivially_copy_assignable + + template struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_copy_assignable + : public is_trivially_assignable::type, + typename add_lvalue_reference::type>::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_trivially_copy_assignable_v + = is_trivially_copy_assignable<_Tp>::value; +#endif + + // is_trivially_move_assignable + + template struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_move_assignable + : public is_trivially_assignable::type, +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + typename add_rvalue_reference<_Tp>::type> +#else + typename add_lvalue_reference<_Tp>::type> +#endif + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_trivially_move_assignable_v + = is_trivially_move_assignable<_Tp>::value; +#endif + + // is_trivially_destructible + +#if __WI_HAS_FEATURE_HAS_TRIVIAL_DESTRUCTOR || (__WI_GNUC_VER >= 403) + + template struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_destructible + : public integral_constant::value && __has_trivial_destructor(_Tp)> {}; + +#else + + template struct __libcpp_trivial_destructor + : public integral_constant::value || + is_reference<_Tp>::value> {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_destructible + : public __libcpp_trivial_destructor::type> {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_destructible<_Tp[]> + : public false_type {}; + +#endif + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_trivially_destructible_v + = is_trivially_destructible<_Tp>::value; +#endif + + // is_nothrow_constructible + +#if 0 + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible + : public integral_constant + { + }; + +#else + +#ifndef __WI_LIBCPP_HAS_NO_VARIADICS + +#if !defined(__WI_LIBCPP_HAS_NO_NOEXCEPT) || (__WI_GNUC_VER >= 407 && __cplusplus >= 201103L) + + template struct __libcpp_is_nothrow_constructible; + + template + struct __libcpp_is_nothrow_constructible + : public integral_constant()...))> + { + }; + + template + void __implicit_conversion_to(_Tp) noexcept { } + + template + struct __libcpp_is_nothrow_constructible + : public integral_constant(declval<_Arg>()))> + { + }; + + template + struct __libcpp_is_nothrow_constructible + : public false_type + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible + : __libcpp_is_nothrow_constructible::value, is_reference<_Tp>::value, _Tp, _Args...> + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible<_Tp[_Ns]> + : __libcpp_is_nothrow_constructible::value, is_reference<_Tp>::value, _Tp> + { + }; + +#else // !defined(__WI_LIBCPP_HAS_NO_NOEXCEPT) + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible + : false_type + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible<_Tp> +#if __WI_HAS_FEATURE_HAS_NOTHROW_CONSTRUCTOR + : integral_constant +#else + : integral_constant::value> +#endif + { + }; + + template +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible<_Tp, _Tp&&> +#else + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible<_Tp, _Tp> +#endif +#if __WI_HAS_FEATURE_HAS_NOTHROW_COPY + : integral_constant +#else + : integral_constant::value> +#endif + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible<_Tp, const _Tp&> +#if __WI_HAS_FEATURE_HAS_NOTHROW_COPY + : integral_constant +#else + : integral_constant::value> +#endif + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible<_Tp, _Tp&> +#if __WI_HAS_FEATURE_HAS_NOTHROW_COPY + : integral_constant +#else + : integral_constant::value> +#endif + { + }; + +#endif // !defined(__WI_LIBCPP_HAS_NO_NOEXCEPT) + +#else // __WI_LIBCPP_HAS_NO_VARIADICS + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible + : false_type + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible<_Tp, __is_construct::__nat, + __is_construct::__nat> +#if __WI_HAS_FEATURE_HAS_NOTHROW_CONSTRUCTOR + : integral_constant +#else + : integral_constant::value> +#endif + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible<_Tp, _Tp, + __is_construct::__nat> +#if __WI_HAS_FEATURE_HAS_NOTHROW_COPY + : integral_constant +#else + : integral_constant::value> +#endif + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible<_Tp, const _Tp&, + __is_construct::__nat> +#if __WI_HAS_FEATURE_HAS_NOTHROW_COPY + : integral_constant +#else + : integral_constant::value> +#endif + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_constructible<_Tp, _Tp&, + __is_construct::__nat> +#if __WI_HAS_FEATURE_HAS_NOTHROW_COPY + : integral_constant +#else + : integral_constant::value> +#endif + { + }; + +#endif // __WI_LIBCPP_HAS_NO_VARIADICS +#endif // __has_feature(is_nothrow_constructible) + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) && !defined(__WI_LIBCPP_HAS_NO_VARIADICS) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_nothrow_constructible_v + = is_nothrow_constructible<_Tp, _Args...>::value; +#endif + + // is_nothrow_default_constructible + + template struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_default_constructible + : public is_nothrow_constructible<_Tp> + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_nothrow_default_constructible_v + = is_nothrow_default_constructible<_Tp>::value; +#endif + + // is_nothrow_copy_constructible + + template struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_copy_constructible + : public is_nothrow_constructible<_Tp, + typename add_lvalue_reference::type>::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_nothrow_copy_constructible_v + = is_nothrow_copy_constructible<_Tp>::value; +#endif + + // is_nothrow_move_constructible + + template struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_move_constructible +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + : public is_nothrow_constructible<_Tp, typename add_rvalue_reference<_Tp>::type> +#else + : public is_nothrow_copy_constructible<_Tp> +#endif + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_nothrow_move_constructible_v + = is_nothrow_move_constructible<_Tp>::value; +#endif + + // is_nothrow_assignable + +#if !defined(__WI_LIBCPP_HAS_NO_NOEXCEPT) || (__WI_GNUC_VER >= 407 && __cplusplus >= 201103L) + + template struct __libcpp_is_nothrow_assignable; + + template + struct __libcpp_is_nothrow_assignable + : public false_type + { + }; + + template + struct __libcpp_is_nothrow_assignable + : public integral_constant() = declval<_Arg>()) > + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_assignable + : public __libcpp_is_nothrow_assignable::value, _Tp, _Arg> + { + }; + +#else // !defined(__WI_LIBCPP_HAS_NO_NOEXCEPT) + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_assignable + : public false_type {}; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_assignable<_Tp&, _Tp> +#if __WI_HAS_FEATURE_HAS_NOTHROW_ASSIGN + : integral_constant {}; +#else + : integral_constant::value> {}; +#endif + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_assignable<_Tp&, _Tp&> +#if __WI_HAS_FEATURE_HAS_NOTHROW_ASSIGN + : integral_constant {}; +#else + : integral_constant::value> {}; +#endif + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_assignable<_Tp&, const _Tp&> +#if __WI_HAS_FEATURE_HAS_NOTHROW_ASSIGN + : integral_constant {}; +#else + : integral_constant::value> {}; +#endif + +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + + template + struct is_nothrow_assignable<_Tp&, _Tp&&> +#if __WI_HAS_FEATURE_HAS_NOTHROW_ASSIGN + : integral_constant {}; +#else + : integral_constant::value> {}; +#endif + +#endif // __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + +#endif // !defined(__WI_LIBCPP_HAS_NO_NOEXCEPT) + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_nothrow_assignable_v + = is_nothrow_assignable<_Tp, _Arg>::value; +#endif + + // is_nothrow_copy_assignable + + template struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_copy_assignable + : public is_nothrow_assignable::type, + typename add_lvalue_reference::type>::type> {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_nothrow_copy_assignable_v + = is_nothrow_copy_assignable<_Tp>::value; +#endif + + // is_nothrow_move_assignable + + template struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_move_assignable + : public is_nothrow_assignable::type, +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + typename add_rvalue_reference<_Tp>::type> +#else + typename add_lvalue_reference<_Tp>::type> +#endif + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_nothrow_move_assignable_v + = is_nothrow_move_assignable<_Tp>::value; +#endif + + // is_nothrow_destructible + +#if !defined(__WI_LIBCPP_HAS_NO_NOEXCEPT) || (__WI_GNUC_VER >= 407 && __cplusplus >= 201103L) + + template struct __libcpp_is_nothrow_destructible; + + template + struct __libcpp_is_nothrow_destructible + : public false_type + { + }; + + template + struct __libcpp_is_nothrow_destructible + : public integral_constant().~_Tp()) > + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_destructible + : public __libcpp_is_nothrow_destructible::value, _Tp> + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_destructible<_Tp[_Ns]> + : public is_nothrow_destructible<_Tp> + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_destructible<_Tp&> + : public true_type + { + }; + +#ifndef __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_destructible<_Tp&&> + : public true_type + { + }; + +#endif + +#else + + template struct __libcpp_nothrow_destructor + : public integral_constant::value || + is_reference<_Tp>::value> {}; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_destructible + : public __libcpp_nothrow_destructor::type> {}; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_destructible<_Tp[]> + : public false_type {}; + +#endif + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_nothrow_destructible_v + = is_nothrow_destructible<_Tp>::value; +#endif + + // is_pod + +#if __WI_HAS_FEATURE_IS_POD || (__WI_GNUC_VER >= 403) + + template struct __WI_LIBCPP_TEMPLATE_VIS is_pod + : public integral_constant {}; + +#else + + template struct __WI_LIBCPP_TEMPLATE_VIS is_pod + : public integral_constant::value && + is_trivially_copy_constructible<_Tp>::value && + is_trivially_copy_assignable<_Tp>::value && + is_trivially_destructible<_Tp>::value> {}; + +#endif + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_pod_v + = is_pod<_Tp>::value; +#endif + + // is_literal_type; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_literal_type +#ifdef __WI_LIBCPP_IS_LITERAL + : public integral_constant +#else + : integral_constant::type>::value || + is_reference::type>::value> +#endif + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_literal_type_v + = is_literal_type<_Tp>::value; +#endif + + // is_standard_layout; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_standard_layout +#if __WI_HAS_FEATURE_IS_STANDARD_LAYOUT || (__WI_GNUC_VER >= 407) + : public integral_constant +#else + : integral_constant::type>::value> +#endif + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_standard_layout_v + = is_standard_layout<_Tp>::value; +#endif + + // is_trivially_copyable; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_trivially_copyable +#if __WI_HAS_FEATURE_IS_TRIVIALLY_COPYABLE + : public integral_constant +#elif __WI_GNUC_VER >= 501 + : public integral_constant::value && __is_trivially_copyable(_Tp)> +#else + : integral_constant::type>::value> +#endif + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_trivially_copyable_v + = is_trivially_copyable<_Tp>::value; +#endif + + // is_trivial; + + template struct __WI_LIBCPP_TEMPLATE_VIS is_trivial +#if __WI_HAS_FEATURE_IS_TRIVIAL || __WI_GNUC_VER >= 407 + : public integral_constant +#else + : integral_constant::value && + is_trivially_default_constructible<_Tp>::value> +#endif + {}; + +#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES) + template + __WI_LIBCPP_INLINE_VAR __WI_LIBCPP_CONSTEXPR bool is_trivial_v + = is_trivial<_Tp>::value; +#endif + + template struct __is_reference_wrapper_impl : public false_type {}; + template struct __is_reference_wrapper_impl > : public true_type {}; + template struct __is_reference_wrapper + : public __is_reference_wrapper_impl::type> {}; + +#ifndef __WI_LIBCPP_CXX03_LANG + + template ::type, + class _DecayA0 = typename decay<_A0>::type, + class _ClassT = typename __member_pointer_class_type<_DecayFp>::type> + using __enable_if_bullet1 = typename enable_if + < + is_member_function_pointer<_DecayFp>::value + && is_base_of<_ClassT, _DecayA0>::value + >::type; + + template ::type, + class _DecayA0 = typename decay<_A0>::type> + using __enable_if_bullet2 = typename enable_if + < + is_member_function_pointer<_DecayFp>::value + && __is_reference_wrapper<_DecayA0>::value + >::type; + + template ::type, + class _DecayA0 = typename decay<_A0>::type, + class _ClassT = typename __member_pointer_class_type<_DecayFp>::type> + using __enable_if_bullet3 = typename enable_if + < + is_member_function_pointer<_DecayFp>::value + && !is_base_of<_ClassT, _DecayA0>::value + && !__is_reference_wrapper<_DecayA0>::value + >::type; + + template ::type, + class _DecayA0 = typename decay<_A0>::type, + class _ClassT = typename __member_pointer_class_type<_DecayFp>::type> + using __enable_if_bullet4 = typename enable_if + < + is_member_object_pointer<_DecayFp>::value + && is_base_of<_ClassT, _DecayA0>::value + >::type; + + template ::type, + class _DecayA0 = typename decay<_A0>::type> + using __enable_if_bullet5 = typename enable_if + < + is_member_object_pointer<_DecayFp>::value + && __is_reference_wrapper<_DecayA0>::value + >::type; + + template ::type, + class _DecayA0 = typename decay<_A0>::type, + class _ClassT = typename __member_pointer_class_type<_DecayFp>::type> + using __enable_if_bullet6 = typename enable_if + < + is_member_object_pointer<_DecayFp>::value + && !is_base_of<_ClassT, _DecayA0>::value + && !__is_reference_wrapper<_DecayA0>::value + >::type; + + // __invoke forward declarations + + // fall back - none of the bullets + +#define __WI_LIBCPP_INVOKE_RETURN(...) \ + __WI_NOEXCEPT_(__WI_NOEXCEPT_(__VA_ARGS__)) -> decltype(__VA_ARGS__) \ + { return __VA_ARGS__; } + + template + auto __invoke(__any, _Args&& ...__args) -> __nat; + + template + auto __invoke_constexpr(__any, _Args&& ...__args) -> __nat; + + // bullets 1, 2 and 3 + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + auto + __invoke(_Fp&& __f, _A0&& __a0, _Args&& ...__args) + __WI_LIBCPP_INVOKE_RETURN((wistd::forward<_A0>(__a0).*__f)(wistd::forward<_Args>(__args)...)) + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + __WI_LIBCPP_CONSTEXPR auto + __invoke_constexpr(_Fp&& __f, _A0&& __a0, _Args&& ...__args) + __WI_LIBCPP_INVOKE_RETURN((wistd::forward<_A0>(__a0).*__f)(wistd::forward<_Args>(__args)...)) + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + auto + __invoke(_Fp&& __f, _A0&& __a0, _Args&& ...__args) + __WI_LIBCPP_INVOKE_RETURN((__a0.get().*__f)(wistd::forward<_Args>(__args)...)) + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + __WI_LIBCPP_CONSTEXPR auto + __invoke_constexpr(_Fp&& __f, _A0&& __a0, _Args&& ...__args) + __WI_LIBCPP_INVOKE_RETURN((__a0.get().*__f)(wistd::forward<_Args>(__args)...)) + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + auto + __invoke(_Fp&& __f, _A0&& __a0, _Args&& ...__args) + __WI_LIBCPP_INVOKE_RETURN(((*wistd::forward<_A0>(__a0)).*__f)(wistd::forward<_Args>(__args)...)) + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + __WI_LIBCPP_CONSTEXPR auto + __invoke_constexpr(_Fp&& __f, _A0&& __a0, _Args&& ...__args) + __WI_LIBCPP_INVOKE_RETURN(((*wistd::forward<_A0>(__a0)).*__f)(wistd::forward<_Args>(__args)...)) + + // bullets 4, 5 and 6 + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + auto + __invoke(_Fp&& __f, _A0&& __a0) + __WI_LIBCPP_INVOKE_RETURN(wistd::forward<_A0>(__a0).*__f) + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + __WI_LIBCPP_CONSTEXPR auto + __invoke_constexpr(_Fp&& __f, _A0&& __a0) + __WI_LIBCPP_INVOKE_RETURN(wistd::forward<_A0>(__a0).*__f) + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + auto + __invoke(_Fp&& __f, _A0&& __a0) + __WI_LIBCPP_INVOKE_RETURN(__a0.get().*__f) + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + __WI_LIBCPP_CONSTEXPR auto + __invoke_constexpr(_Fp&& __f, _A0&& __a0) + __WI_LIBCPP_INVOKE_RETURN(__a0.get().*__f) + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + auto + __invoke(_Fp&& __f, _A0&& __a0) + __WI_LIBCPP_INVOKE_RETURN((*wistd::forward<_A0>(__a0)).*__f) + + template > + inline __WI_LIBCPP_INLINE_VISIBILITY + __WI_LIBCPP_CONSTEXPR auto + __invoke_constexpr(_Fp&& __f, _A0&& __a0) + __WI_LIBCPP_INVOKE_RETURN((*wistd::forward<_A0>(__a0)).*__f) + + // bullet 7 + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + auto + __invoke(_Fp&& __f, _Args&& ...__args) + __WI_LIBCPP_INVOKE_RETURN(wistd::forward<_Fp>(__f)(wistd::forward<_Args>(__args)...)) + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + __WI_LIBCPP_CONSTEXPR auto + __invoke_constexpr(_Fp&& __f, _Args&& ...__args) + __WI_LIBCPP_INVOKE_RETURN(wistd::forward<_Fp>(__f)(wistd::forward<_Args>(__args)...)) + +#undef __WI_LIBCPP_INVOKE_RETURN + + // __invokable + + template + struct __invokable_r + { + // FIXME: Check that _Ret, _Fp, and _Args... are all complete types, cv void, + // or incomplete array types as required by the standard. + using _Result = decltype( + __invoke(declval<_Fp>(), declval<_Args>()...)); + + using type = + typename conditional< + !is_same<_Result, __nat>::value, + typename conditional< + is_void<_Ret>::value, + true_type, + is_convertible<_Result, _Ret> + >::type, + false_type + >::type; + static const bool value = type::value; + }; + + template + using __invokable = __invokable_r; + + template + struct __nothrow_invokable_r_imp { + static const bool value = false; + }; + + template + struct __nothrow_invokable_r_imp + { + typedef __nothrow_invokable_r_imp _ThisT; + + template + static void __test_noexcept(_Tp) noexcept; + + static const bool value = noexcept(_ThisT::__test_noexcept<_Ret>( + __invoke(declval<_Fp>(), declval<_Args>()...))); + }; + + template + struct __nothrow_invokable_r_imp + { + static const bool value = noexcept( + __invoke(declval<_Fp>(), declval<_Args>()...)); + }; + + template + using __nothrow_invokable_r = + __nothrow_invokable_r_imp< + __invokable_r<_Ret, _Fp, _Args...>::value, + is_void<_Ret>::value, + _Ret, _Fp, _Args... + >; + + template + using __nothrow_invokable = + __nothrow_invokable_r_imp< + __invokable<_Fp, _Args...>::value, + true, void, _Fp, _Args... + >; + + template + struct __invoke_of + : public enable_if< + __invokable<_Fp, _Args...>::value, + typename __invokable_r::_Result> + { + }; + + // result_of + + template + class __WI_LIBCPP_TEMPLATE_VIS result_of<_Fp(_Args...)> + : public __invoke_of<_Fp, _Args...> + { + }; + +#if __WI_LIBCPP_STD_VER > 11 + template using result_of_t = typename result_of<_Tp>::type; +#endif + +#if __WI_LIBCPP_STD_VER > 14 + + // invoke_result + + template + struct __WI_LIBCPP_TEMPLATE_VIS invoke_result + : __invoke_of<_Fn, _Args...> + { + }; + + template + using invoke_result_t = typename invoke_result<_Fn, _Args...>::type; + + // is_invocable + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_invocable + : integral_constant::value> {}; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_invocable_r + : integral_constant::value> {}; + + template + __WI_LIBCPP_INLINE_VAR constexpr bool is_invocable_v + = is_invocable<_Fn, _Args...>::value; + + template + __WI_LIBCPP_INLINE_VAR constexpr bool is_invocable_r_v + = is_invocable_r<_Ret, _Fn, _Args...>::value; + + // is_nothrow_invocable + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_invocable + : integral_constant::value> {}; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_invocable_r + : integral_constant::value> {}; + + template + __WI_LIBCPP_INLINE_VAR constexpr bool is_nothrow_invocable_v + = is_nothrow_invocable<_Fn, _Args...>::value; + + template + __WI_LIBCPP_INLINE_VAR constexpr bool is_nothrow_invocable_r_v + = is_nothrow_invocable_r<_Ret, _Fn, _Args...>::value; + +#endif // __WI_LIBCPP_STD_VER > 14 + +#endif // !defined(__WI_LIBCPP_CXX03_LANG) + + template struct __is_swappable; + template struct __is_nothrow_swappable; + + template + inline __WI_LIBCPP_INLINE_VISIBILITY +#ifndef __WI_LIBCPP_CXX03_LANG + typename enable_if + < + is_move_constructible<_Tp>::value && + is_move_assignable<_Tp>::value + >::type +#else + void +#endif + swap_wil(_Tp& __x, _Tp& __y) __WI_NOEXCEPT_(is_nothrow_move_constructible<_Tp>::value && + is_nothrow_move_assignable<_Tp>::value) + { + _Tp __t(wistd::move(__x)); + __x = wistd::move(__y); + __y = wistd::move(__t); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + _ForwardIterator2 + swap_ranges_wil(_ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) + { + for(; __first1 != __last1; ++__first1, (void) ++__first2) + swap_wil(*__first1, *__first2); + return __first2; + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + typename enable_if< + __is_swappable<_Tp>::value + >::type + swap_wil(_Tp (&__a)[_Np], _Tp (&__b)[_Np]) __WI_NOEXCEPT_(__is_nothrow_swappable<_Tp>::value) + { + wistd::swap_ranges_wil(__a, __a + _Np, __b); + } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY + void + iter_swap_wil(_ForwardIterator1 __a, _ForwardIterator2 __b) + // __WI_NOEXCEPT_(__WI_NOEXCEPT_(swap_wil(*__a, *__b))) + __WI_NOEXCEPT_(__WI_NOEXCEPT_(swap_wil(*declval<_ForwardIterator1>(), + *declval<_ForwardIterator2>()))) + { + swap_wil(*__a, *__b); + } + + // __swappable + + namespace __detail + { + // ALL generic swap overloads MUST already have a declaration available at this point. + + template ::value && !is_void<_Up>::value> + struct __swappable_with + { + template + static decltype(swap_wil(declval<_LHS>(), declval<_RHS>())) + __test_swap(int); + template + static __nat __test_swap(long); + + // Extra parens are needed for the C++03 definition of decltype. + typedef decltype((__test_swap<_Tp, _Up>(0))) __swap1; + typedef decltype((__test_swap<_Up, _Tp>(0))) __swap2; + + static const bool value = !is_same<__swap1, __nat>::value + && !is_same<__swap2, __nat>::value; + }; + + template + struct __swappable_with<_Tp, _Up, false> : false_type {}; + + template ::value> + struct __nothrow_swappable_with { + static const bool value = +#ifndef __WI_LIBCPP_HAS_NO_NOEXCEPT + noexcept(swap_wil(declval<_Tp>(), declval<_Up>())) + && noexcept(swap_wil(declval<_Up>(), declval<_Tp>())); +#else + false; +#endif + }; + + template + struct __nothrow_swappable_with<_Tp, _Up, false> : false_type {}; + + } // __detail + + template + struct __is_swappable + : public integral_constant::value> + { + }; + + template + struct __is_nothrow_swappable + : public integral_constant::value> + { + }; + +#if __WI_LIBCPP_STD_VER > 14 + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_swappable_with + : public integral_constant::value> + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_swappable + : public conditional< + __is_referenceable<_Tp>::value, + is_swappable_with< + typename add_lvalue_reference<_Tp>::type, + typename add_lvalue_reference<_Tp>::type>, + false_type + >::type + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_swappable_with + : public integral_constant::value> + { + }; + + template + struct __WI_LIBCPP_TEMPLATE_VIS is_nothrow_swappable + : public conditional< + __is_referenceable<_Tp>::value, + is_nothrow_swappable_with< + typename add_lvalue_reference<_Tp>::type, + typename add_lvalue_reference<_Tp>::type>, + false_type + >::type + { + }; + + template + __WI_LIBCPP_INLINE_VAR constexpr bool is_swappable_with_v + = is_swappable_with<_Tp, _Up>::value; + + template + __WI_LIBCPP_INLINE_VAR constexpr bool is_swappable_v + = is_swappable<_Tp>::value; + + template + __WI_LIBCPP_INLINE_VAR constexpr bool is_nothrow_swappable_with_v + = is_nothrow_swappable_with<_Tp, _Up>::value; + + template + __WI_LIBCPP_INLINE_VAR constexpr bool is_nothrow_swappable_v + = is_nothrow_swappable<_Tp>::value; + +#endif // __WI_LIBCPP_STD_VER > 14 + +#ifdef __WI_LIBCPP_UNDERLYING_TYPE + + template + struct underlying_type + { + typedef __WI_LIBCPP_UNDERLYING_TYPE(_Tp) type; + }; + +#if __WI_LIBCPP_STD_VER > 11 + template using underlying_type_t = typename underlying_type<_Tp>::type; +#endif + +#else // __WI_LIBCPP_UNDERLYING_TYPE + + template + struct underlying_type + { + static_assert(_Support, "The underyling_type trait requires compiler " + "support. Either no such support exists or " + "libc++ does not know how to use it."); + }; + +#endif // __WI_LIBCPP_UNDERLYING_TYPE + + + template ::value> + struct __sfinae_underlying_type + { + typedef typename underlying_type<_Tp>::type type; + typedef decltype(((type)1) + 0) __promoted_type; + }; + + template + struct __sfinae_underlying_type<_Tp, false> {}; + + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + int __convert_to_integral(int __val) { return __val; } + + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + unsigned __convert_to_integral(unsigned __val) { return __val; } + + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + long __convert_to_integral(long __val) { return __val; } + + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + unsigned long __convert_to_integral(unsigned long __val) { return __val; } + + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + long long __convert_to_integral(long long __val) { return __val; } + + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + unsigned long long __convert_to_integral(unsigned long long __val) {return __val; } + + template + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + typename enable_if::value, long long>::type + __convert_to_integral(_Fp __val) { return __val; } + +#ifndef __WI_LIBCPP_HAS_NO_INT128 + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + __int128_t __convert_to_integral(__int128_t __val) { return __val; } + + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + __uint128_t __convert_to_integral(__uint128_t __val) { return __val; } +#endif + + template + inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR + typename __sfinae_underlying_type<_Tp>::__promoted_type + __convert_to_integral(_Tp __val) { return __val; } + +#ifndef __WI_LIBCPP_CXX03_LANG + + template + struct __has_operator_addressof_member_imp + { + template + static auto __test(int) + -> typename __select_2nd().operator&()), true_type>::type; + template + static auto __test(long) -> false_type; + + static const bool value = decltype(__test<_Tp>(0))::value; + }; + + template + struct __has_operator_addressof_free_imp + { + template + static auto __test(int) + -> typename __select_2nd())), true_type>::type; + template + static auto __test(long) -> false_type; + + static const bool value = decltype(__test<_Tp>(0))::value; + }; + + template + struct __has_operator_addressof + : public integral_constant::value + || __has_operator_addressof_free_imp<_Tp>::value> + {}; + +#endif // __WI_LIBCPP_CXX03_LANG + +#ifndef __WI_LIBCPP_CXX03_LANG + + template using void_t = void; + +# ifndef __WI_LIBCPP_HAS_NO_VARIADICS + template + struct conjunction : __and_<_Args...> {}; + template + __WI_LIBCPP_INLINE_VAR constexpr bool conjunction_v + = conjunction<_Args...>::value; + + template + struct disjunction : __or_<_Args...> {}; + template + __WI_LIBCPP_INLINE_VAR constexpr bool disjunction_v + = disjunction<_Args...>::value; + + template + struct negation : __not_<_Tp> {}; + template + __WI_LIBCPP_INLINE_VAR constexpr bool negation_v + = negation<_Tp>::value; +# endif // __WI_LIBCPP_HAS_NO_VARIADICS +#endif // __WI_LIBCPP_CXX03_LANG + + // These traits are used in __tree and __hash_table +#ifndef __WI_LIBCPP_CXX03_LANG + struct __extract_key_fail_tag {}; + struct __extract_key_self_tag {}; + struct __extract_key_first_tag {}; + + template ::type> + struct __can_extract_key + : conditional::value, __extract_key_self_tag, + __extract_key_fail_tag>::type {}; + + template + struct __can_extract_key<_Pair, _Key, pair<_First, _Second>> + : conditional::type, _Key>::value, + __extract_key_first_tag, __extract_key_fail_tag>::type {}; + + // __can_extract_map_key uses true_type/false_type instead of the tags. + // It returns true if _Key != _ContainerValueTy (the container is a map not a set) + // and _ValTy == _Key. + template ::type> + struct __can_extract_map_key + : integral_constant::value> {}; + + // This specialization returns __extract_key_fail_tag for non-map containers + // because _Key == _ContainerValueTy + template + struct __can_extract_map_key<_ValTy, _Key, _Key, _RawValTy> + : false_type {}; + +#endif + +#if __WI_LIBCPP_STD_VER > 17 + enum class endian + { + little = 0xDEAD, + big = 0xFACE, +#if defined(__WI_LIBCPP_LITTLE_ENDIAN) + native = little +#elif defined(__WI_LIBCPP_BIG_ENDIAN) + native = big +#else + native = 0xCAFE +#endif + }; +#endif +} +/// @endcond + +#endif // _WISTD_TYPE_TRAITS_H_ diff --git a/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wrl.h b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wrl.h new file mode 100644 index 0000000..a2a1e24 --- /dev/null +++ b/packages/Microsoft.Windows.ImplementationLibrary.1.0.210204.1/include/wil/wrl.h @@ -0,0 +1,84 @@ +//********************************************************* +// +// Copyright (c) Microsoft. All rights reserved. +// This code is licensed under the MIT License. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. +// +//********************************************************* +#ifndef __WIL_WRL_INCLUDED +#define __WIL_WRL_INCLUDED + +#include +#include "result.h" +#include "common.h" // wistd type_traits helpers + +namespace wil +{ + +#ifdef WIL_ENABLE_EXCEPTIONS +#pragma region Object construction helpers that throw exceptions + + /** Used to construct a RuntimeClass based object that uses 2 phase construction. + Construct a RuntimeClass based object that uses 2 phase construction (by implementing + RuntimeClassInitialize() and returning error codes for failures. + ~~~~ + // SomeClass uses 2 phase initialization by implementing RuntimeClassInitialize() + auto someClass = MakeAndInitializeOrThrow(L"input", true); + ~~~~ */ + + template + Microsoft::WRL::ComPtr MakeAndInitializeOrThrow(TArgs&&... args) + { + Microsoft::WRL::ComPtr obj; + THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize(&obj, Microsoft::WRL::Details::Forward(args)...)); + return obj; + } + + /** Used to construct an RuntimeClass based object that uses exceptions in its constructor (and does + not require 2 phase construction). + ~~~~ + // SomeClass uses exceptions for error handling in its constructor. + auto someClass = MakeOrThrow(L"input", true); + ~~~~ */ + + template + Microsoft::WRL::ComPtr MakeOrThrow(TArgs&&... args) + { + // This is how you can detect the presence of RuntimeClassInitialize() and find dangerous use. + // Unfortunately this produces false positives as all RuntimeClass derived classes have + // a RuntimeClassInitialize() method from their base class. + // static_assert(!std::is_member_function_pointer::value, + // "class has a RuntimeClassInitialize member, use MakeAndInitializeOrThrow instead"); + auto obj = Microsoft::WRL::Make(Microsoft::WRL::Details::Forward(args)...); + THROW_IF_NULL_ALLOC(obj.Get()); + return obj; + } +#pragma endregion + +#endif // WIL_ENABLE_EXCEPTIONS + + /** By default WRL Callback objects are not agile, use this to make an agile one. Replace use of Callback<> with MakeAgileCallback<>. + Will return null on failure, translate that into E_OUTOFMEMORY using XXX_IF_NULL_ALLOC() + from wil\result.h to test the result. */ + template + ::Microsoft::WRL::ComPtr MakeAgileCallbackNoThrow(Args&&... args) WI_NOEXCEPT + { + using namespace Microsoft::WRL; + return Callback, TDelegateInterface, FtmBase>>(wistd::forward(args)...); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + template + ::Microsoft::WRL::ComPtr MakeAgileCallback(Args&&... args) + { + auto result = MakeAgileCallbackNoThrow(wistd::forward(args)...); + THROW_IF_NULL_ALLOC(result); + return result; + } +#endif // WIL_ENABLE_EXCEPTIONS +} // namespace wil + +#endif // __WIL_WRL_INCLUDED