diff --git a/cmake/wrap_standalone.cmake b/cmake/wrap_standalone.cmake index 861c1a31..5aaf2d0d 100644 --- a/cmake/wrap_standalone.cmake +++ b/cmake/wrap_standalone.cmake @@ -11,6 +11,8 @@ function(target_add_standalone_wrapper) STATICALLY_LINKED_CLAP_ENTRY HOSTED_CLAP_NAME + WIN32_ICON + MACOS_EMBEDDED_CLAP_LOCATION ) cmake_parse_arguments(SA "" "${oneValueArgs}" "" ${ARGN} ) @@ -35,6 +37,10 @@ function(target_add_standalone_wrapper) set(SA_OUTPUT_NAME ${SA_TARGET}) endif() + if (NOT DEFINED SA_WIN32_ICON) + set(SA_WIN32_ICON "") + endif() + guarantee_rtaudio() guarantee_rtmidi() @@ -96,19 +102,50 @@ function(target_add_standalone_wrapper) MACOS_EMBEDDED_CLAP_LOCATION ${SA_MACOS_EMBEDDED_CLAP_LOCATION}) elseif(WIN32 AND (CMAKE_CXX_COMPILER_ID MATCHES "MSVC" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")) + if(NOT "${SA_WIN32_ICON}" STREQUAL "") + message(STATUS "Win32 icon found: ${SA_WIN32_ICON}") + file(WRITE "${CMAKE_BINARY_DIR}/standalone_win32.rc" "1 ICON \"standalone_win32.ico\"") + file(COPY_FILE ${SA_WIN32_ICON} "${CMAKE_BINARY_DIR}/standalone_win32.ico") + else() + message(STATUS "Win32 icon not found, using default") + endif() + set_target_properties(${SA_TARGET} PROPERTIES - WIN32_EXECUTABLE TRUE - ) + WIN32_EXECUTABLE TRUE + ) + + target_compile_definitions(${salib} PUBLIC + NOMINMAX + WIN32_LEAN_AND_MEAN + CLAP_WRAPPER_HAS_WIN32 + ) target_sources(${SA_TARGET} PRIVATE - ${CLAP_WRAPPER_CMAKE_CURRENT_SOURCE_DIR}/src/detail/standalone/windows/winutils.cpp - ${CLAP_WRAPPER_CMAKE_CURRENT_SOURCE_DIR}/src/detail/standalone/windows/win32.manifest - ) + "${CLAP_WRAPPER_CMAKE_CURRENT_SOURCE_DIR}/src/wrapasstandalone_win32.cpp" + "${CLAP_WRAPPER_CMAKE_CURRENT_SOURCE_DIR}/src/detail/standalone/windows/host_window.cpp" + "${CLAP_WRAPPER_CMAKE_CURRENT_SOURCE_DIR}/src/detail/standalone/windows/settings_window.cpp" + "${CLAP_WRAPPER_CMAKE_CURRENT_SOURCE_DIR}/src/detail/standalone/windows/helpers.cpp" + "${CLAP_WRAPPER_CMAKE_CURRENT_SOURCE_DIR}/src/detail/standalone/windows/standalone.manifest" + ) - target_compile_definitions(${salib} PUBLIC - CLAP_WRAPPER_HAS_WIN32 - WIN32_NAME="${SA_OUTPUT_NAME}" - ) + target_include_directories(${SA_TARGET} PRIVATE "${CLAP_WRAPPER_CMAKE_CURRENT_SOURCE_DIR}/libs/wil") + + if(NOT "${SA_WIN32_ICON}" STREQUAL "") + target_sources(${SA_TARGET} PRIVATE + "${CMAKE_BINARY_DIR}/standalone_win32.rc" + ) + endif() + + target_link_options( + ${SA_TARGET} + PRIVATE + $<$: + /entry:mainCRTStartup + > + $<$: + -Wl,/entry:mainCRTStartup + > + ) elseif(UNIX) target_sources(${SA_TARGET} PRIVATE @@ -140,7 +177,7 @@ function(target_add_standalone_wrapper) PLUGIN_INDEX=${SA_PLUGIN_INDEX} $<$:STATICALLY_LINKED_CLAP_ENTRY=1> $<$:HOSTED_CLAP_NAME="${SA_HOSTED_CLAP_NAME}"> - OUTPUT_NAME="${OUTPUT_NAME}" + OUTPUT_NAME="${SA_OUTPUT_NAME}" ) target_link_libraries(${SA_TARGET} PRIVATE diff --git a/libs/wil/README.md b/libs/wil/README.md new file mode 100644 index 00000000..eafd930f --- /dev/null +++ b/libs/wil/README.md @@ -0,0 +1 @@ +This is a copy of the [Windows Implementation Library](https://github.com/microsoft/wil) from GitHub. The license is in the header files in the "wil" directory. diff --git a/libs/wil/wil/Tracelogging.h b/libs/wil/wil/Tracelogging.h new file mode 100644 index 00000000..119a9c18 --- /dev/null +++ b/libs/wil/wil/Tracelogging.h @@ -0,0 +1,6702 @@ +#pragma once +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! Defines a series of macros and types that simplify authoring and consumption of tracelogging, telemetry, and activities. + +#ifndef __WIL_TRACELOGGING_H_INCLUDED +/// @cond +#define __WIL_TRACELOGGING_H_INCLUDED +/// @endcond + +#ifdef _KERNEL_MODE +#error This header is not supported in kernel-mode. +#endif + +// Note that we avoid pulling in STL's memory header from TraceLogging.h through Resource.h as we have +// TraceLogging customers who are still on older versions of STL (without std::shared_ptr<>). +/// @cond +#define RESOURCE_SUPPRESS_STL +/// @endcond +#ifndef __WIL_RESULT_INCLUDED +#include +#endif +#undef RESOURCE_SUPPRESS_STL +#include +#include +#include +#ifndef __WIL_TRACELOGGING_CONFIG_H +#include +#endif +#ifndef TRACELOGGING_SUPPRESS_NEW +#include +#endif + +#pragma warning(push) +#pragma warning(disable : 26135) // Missing locking annotation, Caller failing to hold lock + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmicrosoft-template-shadow" +#endif + +/// @cond +#ifndef __TRACELOGGING_TEST_HOOK_ERROR +#define __TRACELOGGING_TEST_HOOK_ERROR(failure) +#define __TRACELOGGING_TEST_HOOK_ACTIVITY_ERROR(failure) +#define __TRACELOGGING_TEST_HOOK_CALLCONTEXT_ERROR(pFailure, hr) +#define __TRACELOGGING_TEST_HOOK_ACTIVITY_START() +#define __TRACELOGGING_TEST_HOOK_ACTIVITY_STOP(pFailure, hr) +#define __TRACELOGGING_TEST_HOOK_SET_ENABLED false +#define __TRACELOGGING_TEST_HOOK_VERIFY_API_TELEMETRY(nameSpace, apiList, specializationList, countArray, numCounters) +#define __TRACELOGGING_TEST_HOOK_API_TELEMETRY_EVENT_DELAY_MS 5000 +#endif + +// For use only within wil/TraceLogging.h: +#define _wiltlg_STRINGIZE(x) _wiltlg_STRINGIZE_imp(x) +#define _wiltlg_STRINGIZE_imp(x) #x +#define _wiltlg_LSTRINGIZE(x) _wiltlg_LSTRINGIZE_imp1(x) +#define _wiltlg_LSTRINGIZE_imp1(x) _wiltlg_LSTRINGIZE_imp2(#x) +#define _wiltlg_LSTRINGIZE_imp2(s) L##s + +/* +Macro __TRACELOGGING_DEFINE_PROVIDER_STORAGE_LINK(name1, name2): +This macro defines a storage link association between two names for use by the +TlgReflector static analysis tool. +*/ +#define __TRACELOGGING_DEFINE_PROVIDER_STORAGE_LINK(name1, name2) \ + __annotation(L"_TlgProviderLink:|" _wiltlg_LSTRINGIZE(__LINE__) L"|Key|" _wiltlg_LSTRINGIZE(name1) L"=" _wiltlg_LSTRINGIZE(name2)) + +// Utility macro for writing relevant fields from a wil::FailureInfo structure into a TraceLoggingWrite +// statement. Most fields are relevant for telemetry or for simple ETW, but there are a few additional +// fields reported via ETW. + +#define __RESULT_TELEMETRY_COMMON_FAILURE_PARAMS(failure) \ + TraceLoggingUInt32((failure).hr, "hresult", "Failure error code"), \ + TraceLoggingString((failure).pszFile, "fileName", "Source code file name where the error occurred"), \ + TraceLoggingUInt32((failure).uLineNumber, "lineNumber", "Line number within the source code file where the error occurred"), \ + TraceLoggingString((failure).pszModule, "module", "Name of the binary where the error occurred"), \ + TraceLoggingUInt32( \ + static_cast((failure).type), \ + "failureType", \ + "Indicates what type of failure was observed (exception, returned error, logged error or fail fast"), \ + TraceLoggingWideString((failure).pszMessage, "message", "Custom message associated with the failure (if any)"), \ + TraceLoggingUInt32((failure).threadId, "threadId", "Identifier of the thread the error occurred on"), \ + TraceLoggingString((failure).pszCallContext, "callContext", "List of telemetry activities containing this error"), \ + TraceLoggingUInt32( \ + (failure).callContextOriginating.contextId, \ + "originatingContextId", \ + "Identifier for the oldest telemetry activity containing this error"), \ + TraceLoggingString( \ + (failure).callContextOriginating.contextName, \ + "originatingContextName", \ + "Name of the oldest telemetry activity containing this error"), \ + TraceLoggingWideString( \ + (failure).callContextOriginating.contextMessage, \ + "originatingContextMessage", \ + "Custom message associated with the oldest telemetry activity containing this error (if any)"), \ + TraceLoggingUInt32( \ + (failure).callContextCurrent.contextId, "currentContextId", "Identifier for the newest telemetry activity containing this error"), \ + TraceLoggingString( \ + (failure).callContextCurrent.contextName, "currentContextName", "Name of the newest telemetry activity containing this error"), \ + TraceLoggingWideString( \ + (failure).callContextCurrent.contextMessage, \ + "currentContextMessage", \ + "Custom message associated with the newest telemetry activity containing this error (if any)") + +#define __RESULT_TRACELOGGING_COMMON_FAILURE_PARAMS(failure) \ + __RESULT_TELEMETRY_COMMON_FAILURE_PARAMS(failure), \ + TraceLoggingUInt32(static_cast((failure).failureId), "failureId", "Identifier assigned to this failure"), \ + TraceLoggingUInt32( \ + static_cast((failure).cFailureCount), "failureCount", "Number of failures seen within the binary where the error occurred"), \ + TraceLoggingString((failure).pszFunction, "function", "Name of the function where the error occurred") + +// Activity Start Event (ALL) +#define __ACTIVITY_START_PARAMS() \ + TraceLoggingStruct(1, "wilActivity"), \ + TraceLoggingUInt32(::GetCurrentThreadId(), "threadId", "Identifier of the thread the activity was run on") + +// Activity Stop Event (SUCCESSFUL or those WITHOUT full failure info -- just hr) +// Also utilized for intermediate stop events (a successful call to 'Stop()' from a Split activity +#define __ACTIVITY_STOP_PARAMS(hr) \ + TraceLoggingStruct(2, "wilActivity"), TraceLoggingUInt32(hr, "hresult", "Failure error code"), \ + TraceLoggingUInt32(::GetCurrentThreadId(), "threadId", "Identifier of the thread the activity was run on") + +// Activity Stop Event (FAILED with full failure info) +#define __ACTIVITY_STOP_TELEMETRY_FAILURE_PARAMS(failure) \ + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingStruct(14, "wilActivity"), \ + __RESULT_TELEMETRY_COMMON_FAILURE_PARAMS(failure) +#define __ACTIVITY_STOP_TRACELOGGING_FAILURE_PARAMS(failure) \ + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingStruct(17, "wilActivity"), \ + __RESULT_TRACELOGGING_COMMON_FAILURE_PARAMS(failure) + +// "ActivityError" tagged event (all distinct FAILURES occurring within the outer activity scope) +#define __ACTIVITY_ERROR_TELEMETRY_FAILURE_PARAMS(failure) \ + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingStruct(14, "wilActivity"), \ + __RESULT_TELEMETRY_COMMON_FAILURE_PARAMS(failure) +#define __ACTIVITY_ERROR_TRACELOGGING_FAILURE_PARAMS(failure) \ + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingStruct(17, "wilActivity"), \ + __RESULT_TRACELOGGING_COMMON_FAILURE_PARAMS(failure) + +// "ActivityFailure" tagged event (only comes through on TELEMETRY for CallContext activities that have FAILED) +#define __ACTIVITY_FAILURE_TELEMETRY_FAILURE_PARAMS(failure) \ + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingStruct(14, "wilActivity"), \ + __RESULT_TELEMETRY_COMMON_FAILURE_PARAMS(failure) +#define __ACTIVITY_FAILURE_TELEMETRY_PARAMS(hr, contextName, contextMessage) \ + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingStruct(4, "wilActivity"), \ + TraceLoggingUInt32(hr, "hresult", "Failure error code"), \ + TraceLoggingUInt32(::GetCurrentThreadId(), "threadId", "Identifier of the thread the activity was run on"), \ + TraceLoggingString(contextName, "currentContextName", "Name of the activity containing this error"), \ + TraceLoggingWideString(contextMessage, "currentContextMessage", "Custom message for the activity containing this error (if any)") + +// "FallbackError" events (all FAILURE events happening outside of ANY activity context) +#define __RESULT_TELEMETRY_FAILURE_PARAMS(failure) \ + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingStruct(14, "wilResult"), \ + __RESULT_TELEMETRY_COMMON_FAILURE_PARAMS(failure) +#define __RESULT_TRACELOGGING_FAILURE_PARAMS(failure) \ + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), TraceLoggingStruct(17, "wilResult"), \ + __RESULT_TRACELOGGING_COMMON_FAILURE_PARAMS(failure) +/// @endcond + +namespace wil +{ +enum class ActivityOptions +{ + None = 0, + TelemetryOnFailure = 0x1, + TraceLoggingOnFailure = 0x2 +}; +DEFINE_ENUM_FLAG_OPERATORS(ActivityOptions) + +template +class ActivityBase; + +/// @cond +namespace details +{ + // Lazy static initialization helper for holding a singleton telemetry class to maintain + // the provider handle. + + template + class static_lazy + { + public: + void __cdecl cleanup() WI_NOEXCEPT + { + void* pVoid; + BOOL pending; + + // If object is being constructed on another thread, wait until construction completes. + // Need a memory barrier here (see get() and ~Completer below) so use the result that we + // get from InitOnceBeginInitialize(..., &pVoid, ...) + if (::InitOnceBeginInitialize(&m_initOnce, INIT_ONCE_CHECK_ONLY, &pending, &pVoid) && !pending) + { + static_cast(pVoid)->~T(); + } + } + + T* get(void(__cdecl* cleanupFunc)(void)) WI_NOEXCEPT + { + void* pVoid{}; + BOOL pending; + if (::InitOnceBeginInitialize(&m_initOnce, 0, &pending, &pVoid) && pending) + { + // Don't do anything non-trivial from DllMain, fail fast. + // Some 3rd party code in IE calls shell functions this way, so we can only enforce + // this in DEBUG. +#ifdef DEBUG + FAIL_FAST_IMMEDIATE_IF_IN_LOADER_CALLOUT(); +#endif + + Completer completer(this); + pVoid = &m_storage; + ::new (pVoid) T(); + atexit(cleanupFunc); // ignore failure (that's what the C runtime does, too) + completer.Succeed(); + } + return static_cast(pVoid); + } + + private: + INIT_ONCE m_initOnce; + alignas(T) BYTE m_storage[sizeof(T)]; + struct Completer + { + static_lazy* m_pSelf; + DWORD m_flags; + + explicit Completer(static_lazy* pSelf) WI_NOEXCEPT : m_pSelf(pSelf), m_flags(INIT_ONCE_INIT_FAILED) + { + } + void Succeed() WI_NOEXCEPT + { + m_flags = 0; + } + + ~Completer() WI_NOEXCEPT + { + if (m_flags == 0) + { + reinterpret_cast(&m_pSelf->m_storage)->Create(); + } + ::InitOnceComplete(&m_pSelf->m_initOnce, m_flags, &m_pSelf->m_storage); + } + }; + }; + + // This class serves as a simple RAII wrapper around CallContextInfo. It presumes that + // the contextName parameter is always a static string, but copies or allocates the + // contextMessage as needed. + + class StoredCallContextInfo : public wil::CallContextInfo + { + public: + StoredCallContextInfo() WI_NOEXCEPT + { + ::ZeroMemory(this, sizeof(*this)); + } + + StoredCallContextInfo(StoredCallContextInfo&& other) WI_NOEXCEPT : StoredCallContextInfo() + { + operator=(wistd::move(other)); + } + + StoredCallContextInfo& operator=(StoredCallContextInfo&& other) WI_NOEXCEPT + { + contextId = other.contextId; + contextName = other.contextName; + ClearMessage(); + contextMessage = other.contextMessage; + other.contextMessage = nullptr; + m_ownsMessage = other.m_ownsMessage; + other.m_ownsMessage = false; + return *this; + } + + StoredCallContextInfo(StoredCallContextInfo const& other) WI_NOEXCEPT : m_ownsMessage(false) + { + contextId = other.contextId; + contextName = other.contextName; + if (other.m_ownsMessage) + { + AssignMessage(other.contextMessage); + } + else + { + contextMessage = other.contextMessage; + } + } + + StoredCallContextInfo(_In_opt_ PCSTR staticContextName) WI_NOEXCEPT : m_ownsMessage(false) + { + contextId = 0; + contextName = staticContextName; + contextMessage = nullptr; + } + + StoredCallContextInfo(PCSTR staticContextName, _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + : StoredCallContextInfo(staticContextName) + { + SetMessage(formatString, argList); + } + + void SetMessage(_Printf_format_string_ PCSTR formatString, va_list argList) + { + wchar_t loggingMessage[2048]; + PrintLoggingMessage(loggingMessage, ARRAYSIZE(loggingMessage), formatString, argList); + ClearMessage(); + AssignMessage(loggingMessage); + } + + void SetMessage(_In_opt_ PCWSTR message) + { + ClearMessage(); + contextMessage = message; + } + + void SetMessageCopy(_In_opt_ PCWSTR message) + { + ClearMessage(); + if (message != nullptr) + { + AssignMessage(message); + } + } + + void ClearMessage() + { + if (m_ownsMessage) + { + WIL_FreeMemory(const_cast(contextMessage)); + m_ownsMessage = false; + } + contextMessage = nullptr; + } + + ~StoredCallContextInfo() + { + ClearMessage(); + } + + StoredCallContextInfo& operator=(StoredCallContextInfo const&) = delete; + + private: + void AssignMessage(PCWSTR message) + { + auto length = wcslen(message); + if (length > 0) + { + auto sizeBytes = (length + 1) * sizeof(wchar_t); + contextMessage = static_cast(WIL_AllocateMemory(sizeBytes)); + if (contextMessage != nullptr) + { + m_ownsMessage = true; + memcpy_s(const_cast(contextMessage), sizeBytes, message, sizeBytes); + } + } + } + + bool m_ownsMessage; + }; + + template + void SetRelatedActivityId(TActivity&) + { + } + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + template + void SetRelatedActivityId(wil::ActivityBase& activity) + { + GUID capturedRelatedId; + EventActivityIdControl(EVENT_ACTIVITY_CTRL_GET_ID, &capturedRelatedId); + activity.SetRelatedActivityId(capturedRelatedId); + } +#endif + + typedef wistd::integral_constant tag_start; + typedef wistd::integral_constant tag_start_cv; +} // namespace details +/// @endcond + +// This class acts as a simple RAII class returned by a call to ContinueOnCurrentThread() for an activity +// or by a call to WatchCurrentThread() on a provider. The result is meant to be a stack local variable +// whose scope controls the lifetime of an error watcher on the given thread. That error watcher re-directs +// errors occurrent within the object's lifetime to the associated provider or activity. + +class ActivityThreadWatcher +{ +public: + ActivityThreadWatcher() WI_NOEXCEPT : m_callbackHolder(nullptr, nullptr, false) + { + } + + ActivityThreadWatcher(_In_ details::IFailureCallback* pCallback, PCSTR staticContextName) WI_NOEXCEPT + : m_callContext(staticContextName), + m_callbackHolder(pCallback, &m_callContext) + { + } + + ActivityThreadWatcher( + _In_ details::IFailureCallback* pCallback, PCSTR staticContextName, _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + : ActivityThreadWatcher(pCallback, staticContextName) + { + m_callContext.SetMessage(formatString, argList); + } + + // Uses the supplied StoredCallContextInfo rather than producing one itself + ActivityThreadWatcher(_In_ details::IFailureCallback* pCallback, _In_ details::StoredCallContextInfo const& callContext) WI_NOEXCEPT + : m_callContext(callContext), + m_callbackHolder(pCallback, &m_callContext) + { + } + + ActivityThreadWatcher(ActivityThreadWatcher&& other) WI_NOEXCEPT : m_callContext(wistd::move(other.m_callContext)), + m_callbackHolder(wistd::move(other.m_callbackHolder)) + { + m_callbackHolder.SetCallContext(&m_callContext); + } + + ActivityThreadWatcher(ActivityThreadWatcher const&) = delete; + ActivityThreadWatcher& operator=(ActivityThreadWatcher const&) = delete; + + void SetMessage(_Printf_format_string_ PCSTR formatString, ...) + { + va_list argList; + va_start(argList, formatString); + m_callContext.SetMessage(formatString, argList); + va_end(argList); + } + + void SetMessage(_In_opt_ PCWSTR message) + { + m_callContext.SetMessage(message); + } + + void SetMessageCopy(_In_opt_ PCWSTR message) + { + m_callContext.SetMessageCopy(message); + } + +private: + details::StoredCallContextInfo m_callContext; + details::ThreadFailureCallbackHolder m_callbackHolder; +}; + +// This is the base-class implementation of a TraceLogging class. TraceLogging classes are defined with +// BEGIN_TRACELOGGING_CLASS and automatically derive from this class + +enum class ErrorReportingType +{ + None = 0, + Telemetry, + TraceLogging +}; + +class TraceLoggingProvider : public details::IFailureCallback +{ +public: + // Only one instance of each of these derived classes should be created + TraceLoggingProvider(_In_ TraceLoggingProvider const&) = delete; + TraceLoggingProvider& operator=(TraceLoggingProvider const&) = delete; + void* operator new(size_t) = delete; + void* operator new[](size_t) = delete; + +protected: + // This can be overridden to provide specific initialization code for any individual provider. + // It will be ran once when the single static singleton instance of this class is created. + virtual void Initialize() WI_NOEXCEPT + { + } + + // This method can be overridden by a provider to more tightly control what happens in the event + // of a failure in a CallContext activity, WatchCurrentThread() object, or attributed to a specific failure. + virtual void OnErrorReported(bool alreadyReported, FailureInfo const& failure) WI_NOEXCEPT + { + if (!alreadyReported && WI_IsFlagClear(failure.flags, FailureFlags::RequestSuppressTelemetry)) + { + if (m_errorReportingType == ErrorReportingType::Telemetry) + { + ReportTelemetryFailure(failure); + } + else if (m_errorReportingType == ErrorReportingType::TraceLogging) + { + ReportTraceLoggingFailure(failure); + } + } + } + +public: + WI_NODISCARD TraceLoggingHProvider Provider_() const WI_NOEXCEPT + { + return m_providerHandle; + } + +protected: + TraceLoggingProvider() WI_NOEXCEPT + { + } + + virtual ~TraceLoggingProvider() WI_NOEXCEPT + { + if (m_ownsProviderHandle) + { + TraceLoggingUnregister(m_providerHandle); + } + } + + WI_NODISCARD bool IsEnabled_( + UCHAR eventLevel /* WINEVENT_LEVEL_XXX, e.g. WINEVENT_LEVEL_VERBOSE */, + ULONGLONG eventKeywords /* MICROSOFT_KEYWORD_XXX */) const WI_NOEXCEPT + { + return ((m_providerHandle != nullptr) && TraceLoggingProviderEnabled(m_providerHandle, eventLevel, eventKeywords)) || + __TRACELOGGING_TEST_HOOK_SET_ENABLED; + } + + void SetErrorReportingType_(ErrorReportingType type) + { + m_errorReportingType = type; + } + + static bool WasAlreadyReportedToTelemetry(long failureId) WI_NOEXCEPT + { + static long volatile s_lastFailureSeen = -1; + auto wasSeen = (s_lastFailureSeen == failureId); + s_lastFailureSeen = failureId; + return wasSeen; + } + + void ReportTelemetryFailure(FailureInfo const& failure) WI_NOEXCEPT + { + __TRACELOGGING_TEST_HOOK_ERROR(failure); + TraceLoggingWrite( + m_providerHandle, + "FallbackError", + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + __RESULT_TELEMETRY_FAILURE_PARAMS(failure)); + } + + void ReportTraceLoggingFailure(FailureInfo const& failure) WI_NOEXCEPT + { + TraceLoggingWrite( + m_providerHandle, "FallbackError", TraceLoggingLevel(WINEVENT_LEVEL_ERROR), __RESULT_TRACELOGGING_FAILURE_PARAMS(failure)); + } + + // Helper function for TraceLoggingError. + // It prints out a trace message for debug purposes. The message does not go into the telemetry. + void ReportTraceLoggingError(_In_ _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + if (IsEnabled_(WINEVENT_LEVEL_ERROR, 0)) + { + wchar_t loggingMessage[2048]; + details::PrintLoggingMessage(loggingMessage, ARRAYSIZE(loggingMessage), formatString, argList); + TraceLoggingWrite( + m_providerHandle, + "TraceLoggingError", + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + TraceLoggingWideString(loggingMessage, "traceLoggingMessage")); + } + } + + // Helper function for TraceLoggingInfo. + // It prints out a trace message for debug purposes. The message does not go into the telemetry. + void ReportTraceLoggingMessage(_In_ _Printf_format_string_ PCSTR formatString, va_list argList) WI_NOEXCEPT + { + if (IsEnabled_(WINEVENT_LEVEL_VERBOSE, 0)) + { + wchar_t loggingMessage[2048]; + details::PrintLoggingMessage(loggingMessage, ARRAYSIZE(loggingMessage), formatString, argList); + TraceLoggingWrite( + m_providerHandle, + "TraceLoggingInfo", + TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE), + TraceLoggingWideString(loggingMessage, "traceLoggingMessage")); + } + } + + void Register(TraceLoggingHProvider const providerHandle, TLG_PENABLECALLBACK callback = nullptr) WI_NOEXCEPT + { + // taking over the lifetime and management of providerHandle + m_providerHandle = providerHandle; + m_ownsProviderHandle = true; + TraceLoggingRegisterEx(providerHandle, callback, nullptr); + InternalInitialize(); + } + + void AttachProvider(TraceLoggingHProvider const providerHandle) WI_NOEXCEPT + { + m_providerHandle = providerHandle; + m_ownsProviderHandle = false; + InternalInitialize(); + } + +private: + // IFailureCallback + bool NotifyFailure(FailureInfo const& failure) WI_NOEXCEPT override + { + if (!WasAlreadyReportedToTelemetry(failure.failureId)) + { + OnErrorReported(false, failure); + } + return true; + } + + void InternalInitialize() + { + m_errorReportingType = ErrorReportingType::Telemetry; + Initialize(); + } + + TraceLoggingHProvider m_providerHandle{}; + bool m_ownsProviderHandle{}; + ErrorReportingType m_errorReportingType{}; +}; + +template < + typename TraceLoggingType, + UINT64 keyword = 0, + UINT8 level = WINEVENT_LEVEL_VERBOSE, + typename TlgReflectorTag = _TlgReflectorTag_Param0IsProviderType> // helps TlgReflector understand that this is a wrapper type +class BasicActivity : public _TlgActivityBase, keyword, level> +{ + using BaseTy = _TlgActivityBase, keyword, level>; + friend BaseTy; + + void OnStarted() + { + } + + void OnStopped() + { + } + +public: + BasicActivity() + { + } + + BasicActivity(BasicActivity&& rhs) : BaseTy(wistd::move(rhs)) + { + } + + BasicActivity& operator=(BasicActivity&& rhs) + { + BaseTy::operator=(wistd::move(rhs)); + return *this; + } + + /* + Returns a handle to the TraceLogging provider associated with this activity. + */ + WI_NODISCARD TraceLoggingHProvider Provider() const + { + return TraceLoggingType::Provider(); + } + + /* + Sets the related (parent) activity. + May only be called once. If used, must be called before starting the activity. + */ + template + void SetRelatedActivity(_In_ const ActivityTy& relatedActivity) + { + this->SetRelatedId(*relatedActivity.Id()); + } + + /* + Sets the related (parent) activity. + May only be called once. If used, must be called before starting the activity. + */ + void SetRelatedActivityId(_In_ const GUID& relatedActivityId) + { + this->SetRelatedId(relatedActivityId); + } + + /* + Sets the related (parent) activity. + May only be called once. If used, must be called before starting the activity. + */ + void SetRelatedActivityId(_In_ const GUID* relatedActivityId) + { + __FAIL_FAST_IMMEDIATE_ASSERT__(relatedActivityId != NULL); + this->SetRelatedId(*relatedActivityId); + } +}; + +template < + typename TraceLoggingType, + UINT64 keyword = 0, + UINT8 level = WINEVENT_LEVEL_VERBOSE, + typename TlgReflectorTag = _TlgReflectorTag_Param0IsProviderType> // helps TlgReflector understand that this is a wrapper type +class BasicThreadActivity + : public _TlgActivityBase, keyword, level> +{ + using BaseTy = _TlgActivityBase, keyword, level>; + friend BaseTy; + + void OnStarted() + { + this->PushThreadActivityId(); + } + + void OnStopped() + { + this->PopThreadActivityId(); + } + +public: + BasicThreadActivity() + { + } + + BasicThreadActivity(BasicThreadActivity&& rhs) : BaseTy(wistd::move(rhs)) + { + } + + BasicThreadActivity& operator=(BasicThreadActivity&& rhs) + { + BaseTy::operator=(wistd::move(rhs)); + return *this; + } + + /* + Returns a handle to the TraceLogging provider associated with this activity. + */ + WI_NODISCARD TraceLoggingHProvider Provider() const + { + return TraceLoggingType::Provider(); + } +}; + +/// @cond +#define __WI_TraceLoggingWriteTagged(activity, name, ...) \ + __pragma(warning(push)) __pragma(warning(disable : 4127)) do \ + { \ + _tlgActivityDecl(activity) \ + TraceLoggingWriteActivity(TraceLoggingType::Provider(), (name), _tlgActivityRef(activity).Id(), NULL, ##__VA_ARGS__); \ + } \ + while (0) \ + __pragma(warning(pop)) /// @endcond + +// This is the ultimate base class implementation for all activities. Activity classes are defined with +// DEFINE_TRACELOGGING_ACTIVITY, DEFINE_CALLCONTEXT_ACTIVITY, DEFINE_TELEMETRY_ACTIVITY and others + +template +class ActivityBase : public details::IFailureCallback +{ +public: + typedef ActivityTraceLoggingType TraceLoggingType; + + static UINT64 const Keyword = keyword; + static UINT8 const Level = level; + static UINT64 const PrivacyTag = privacyTag; + + ActivityBase(PCSTR contextName, bool shouldWatchErrors = false) WI_NOEXCEPT + : m_activityData(contextName), + m_pActivityData(&m_activityData), + m_callbackHolder(this, m_activityData.GetCallContext(), shouldWatchErrors) + { + } + + ActivityBase(ActivityBase&& other, bool shouldWatchErrors) WI_NOEXCEPT + : m_activityData(wistd::move(other.m_activityData)), + m_sharedActivityData(wistd::move(other.m_sharedActivityData)), + m_callbackHolder(this, nullptr, shouldWatchErrors) + { + m_pActivityData = m_sharedActivityData ? m_sharedActivityData.get() : &m_activityData; + m_callbackHolder.SetCallContext(m_pActivityData->GetCallContext()); + other.m_pActivityData = &other.m_activityData; + if (other.m_callbackHolder.IsWatching()) + { + other.m_callbackHolder.StopWatching(); + } + } + + ActivityBase(ActivityBase&& other) WI_NOEXCEPT : ActivityBase(wistd::move(other), other.m_callbackHolder.IsWatching()) + { + } + + ActivityBase(ActivityBase const& other) WI_NOEXCEPT + : m_activityData(), + m_pActivityData(&m_activityData), + m_callbackHolder(this, nullptr, false) // false = do not automatically watch for failures + { + operator=(other); + } + + ActivityBase& operator=(ActivityBase&& other) WI_NOEXCEPT + { + m_activityData = wistd::move(other.m_activityData); + m_sharedActivityData = wistd::move(other.m_sharedActivityData); + m_pActivityData = m_sharedActivityData ? m_sharedActivityData.get() : &m_activityData; + m_callbackHolder.SetCallContext(m_pActivityData->GetCallContext()); + m_callbackHolder.SetWatching(other.m_callbackHolder.IsWatching()); + other.m_pActivityData = &other.m_activityData; + if (other.m_callbackHolder.IsWatching()) + { + other.m_callbackHolder.StopWatching(); + } + return *this; + } + + ActivityBase& operator=(ActivityBase const& other) WI_NOEXCEPT + { + if (m_callbackHolder.IsWatching()) + { + m_callbackHolder.StopWatching(); + } + + if (other.m_sharedActivityData) + { + m_pActivityData = other.m_pActivityData; + m_sharedActivityData = other.m_sharedActivityData; + } + else if (m_sharedActivityData.create(wistd::move(other.m_activityData))) + { + // Locking should not be required as the first copy should always take place on the owning + // thread... + m_pActivityData = m_sharedActivityData.get(); + other.m_sharedActivityData = m_sharedActivityData; + other.m_pActivityData = m_pActivityData; + other.m_callbackHolder.SetCallContext(m_pActivityData->GetCallContext()); + } + m_callbackHolder.SetCallContext(m_pActivityData->GetCallContext()); + return *this; + } + + // These calls all result in setting a message to associate with any failures that might occur while + // running the activity. For example, you could associate a filename with a call context activity + // so that the file name is only reported if the activity fails with the failure. + + void SetMessage(_In_ _Printf_format_string_ PCSTR formatString, ...) + { + va_list argList; + va_start(argList, formatString); + auto lock = LockExclusive(); + GetCallContext()->SetMessage(formatString, argList); + va_end(argList); + } + + void SetMessage(_In_opt_ PCWSTR message) + { + auto lock = LockExclusive(); + GetCallContext()->SetMessage(message); + } + + void SetMessageCopy(_In_opt_ PCWSTR message) + { + auto lock = LockExclusive(); + GetCallContext()->SetMessageCopy(message); + } + + // This call stops watching for errors on the thread that the activity was originally + // created on. Use it when moving the activity into a thread-agnostic class or moving + // an activity across threads. + + void IgnoreCurrentThread() WI_NOEXCEPT + { + if (m_callbackHolder.IsWatching()) + { + m_callbackHolder.StopWatching(); + } + } + + // Call this API to retrieve an RAII object to watch events on the current thread. The returned + // object should only be used on the stack. + + WI_NODISCARD ActivityThreadWatcher ContinueOnCurrentThread() WI_NOEXCEPT + { + if (IsRunning()) + { + return ActivityThreadWatcher(this, *m_pActivityData->GetCallContext()); + } + return ActivityThreadWatcher(); + } + + // This is the 'default' Stop routine that accepts an HRESULT and completes the activity... + + void Stop(HRESULT hr = S_OK) WI_NOEXCEPT + { + bool stopActivity; + HRESULT hrLocal; + { + auto lock = LockExclusive(); + stopActivity = m_pActivityData->SetStopResult(hr, &hrLocal); + } + if (stopActivity) + { + ReportStopActivity(hrLocal); + } + else + { + __WI_TraceLoggingWriteTagged( + *this, + "ActivityIntermediateStop", + TraceLoggingKeyword(Keyword), + TelemetryPrivacyDataTag(PDT_ProductAndServicePerformance), + __ACTIVITY_STOP_PARAMS(hr)); + } + IgnoreCurrentThread(); + } + + // IFailureCallback + + bool NotifyFailure(FailureInfo const& failure) WI_NOEXCEPT override + { + // We always report errors to the ETW stream, but we hold-back the telemetry keyword if we've already reported this error + // to this particular telemetry provider. + + __TRACELOGGING_TEST_HOOK_ACTIVITY_ERROR(failure); + + if (WI_IsFlagClear(failure.flags, FailureFlags::RequestSuppressTelemetry)) + { +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-value" +#endif +#pragma warning(push) +#pragma warning(disable : 6319) + if (false, WI_IsFlagSet(options, ActivityOptions::TelemetryOnFailure) && !WasAlreadyReportedToTelemetry(failure.failureId)) + { + __WI_TraceLoggingWriteTagged( + *this, + "ActivityError", + TraceLoggingKeyword(Keyword | MICROSOFT_KEYWORD_TELEMETRY), + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + __ACTIVITY_ERROR_TELEMETRY_FAILURE_PARAMS(failure)); + } + else if (false, WI_IsFlagSet(options, ActivityOptions::TraceLoggingOnFailure)) + { + __WI_TraceLoggingWriteTagged( + *this, + "ActivityError", + TraceLoggingKeyword(0), + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + __ACTIVITY_ERROR_TRACELOGGING_FAILURE_PARAMS(failure)); + } + else + { + __WI_TraceLoggingWriteTagged( + *this, + "ActivityError", + TraceLoggingKeyword(Keyword), + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + __ACTIVITY_ERROR_TRACELOGGING_FAILURE_PARAMS(failure)); + } +#pragma warning(pop) +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + } + + auto lock = LockExclusive(); + m_pActivityData->NotifyFailure(failure); + return true; + } + + // This is the base TraceLoggingActivity<> contract... we implement it so that this class + // can be used by all of the activity macros and we re-route the request as needed. + // + // The contract required by the TraceLogging Activity macros is: + // - activity.Keyword // compile-time constant + // - activity.Level // compile-time constant + // - activity.PrivacyTag // compile-time constant + // - activity.Provider() + // - activity.Id() + // - activity.zInternalRelatedId() + // - activity.zInternalStart() + // - activity.zInternalStop() + // In addition, for TlgReflector to work correctly, it must be possible for + // TlgReflector to statically map from typeof(activity) to hProvider. + + WI_NODISCARD GUID const* zInternalRelatedId() const WI_NOEXCEPT + { + return m_pActivityData->zInternalRelatedId(); + } + + void zInternalStart() WI_NOEXCEPT + { + auto lock = LockExclusive(); + m_pActivityData->zInternalStart(); + } + + void zInternalStop() WI_NOEXCEPT + { + auto lock = LockExclusive(); + m_pActivityData->zInternalStop(); + } + + static TraceLoggingHProvider Provider() WI_NOEXCEPT + { + return ActivityTraceLoggingType::Provider(); + } + + WI_NODISCARD GUID const* Id() const WI_NOEXCEPT + { + return m_pActivityData->Id(); + } + + WI_NODISCARD GUID const* providerGuid() const WI_NOEXCEPT + { + return m_pActivityData->providerGuid(); + } + + template + void SetRelatedActivity(OtherTy const& relatedActivity) WI_NOEXCEPT + { + auto lock = LockExclusive(); + m_pActivityData->SetRelatedActivityId(relatedActivity.Id()); + } + + void SetRelatedActivityId(_In_ const GUID& relatedActivityId) WI_NOEXCEPT + { + auto lock = LockExclusive(); + m_pActivityData->SetRelatedActivityId(&relatedActivityId); + } + + void SetRelatedActivityId(_In_ const GUID* relatedActivityId) WI_NOEXCEPT + { + auto lock = LockExclusive(); + m_pActivityData->SetRelatedActivityId(relatedActivityId); + } + + WI_NODISCARD inline bool IsRunning() const WI_NOEXCEPT + { + return m_pActivityData->NeedsStopped(); + } + +protected: + virtual void StopActivity() WI_NOEXCEPT = 0; + virtual bool WasAlreadyReportedToTelemetry(long failureId) WI_NOEXCEPT = 0; + + void EnsureWatchingCurrentThread() + { + if (!m_callbackHolder.IsWatching()) + { + m_callbackHolder.StartWatching(); + } + } + + void SetStopResult(HRESULT hr, _Out_opt_ HRESULT* phr = nullptr) WI_NOEXCEPT + { + auto lock = LockExclusive(); + m_pActivityData->SetStopResult(hr, phr); + } + + void IncrementExpectedStopCount() WI_NOEXCEPT + { + auto lock = LockExclusive(); + m_pActivityData->IncrementExpectedStopCount(); + } + + // Locking should not be required on these accessors as we only use this at reporting (which will only happen from + // the final stop) + + FailureInfo const* GetFailureInfo() WI_NOEXCEPT + { + return m_pActivityData->GetFailureInfo(); + } + + WI_NODISCARD inline HRESULT GetResult() const WI_NOEXCEPT + { + return m_pActivityData->GetResult(); + } + + WI_NODISCARD details::StoredCallContextInfo* GetCallContext() const WI_NOEXCEPT + { + return m_pActivityData->GetCallContext(); + } + + // Think of this routine as the destructor -- since we need to call virtual derived methods, we can't use it as + // a destructor without a pure virtual method call, so we have the derived class call it in its destructor... + + void Destroy() WI_NOEXCEPT + { + bool fStop = true; + if (m_sharedActivityData) + { + // The lock unifies the 'unique()' check and the 'reset()' of any non-unique activity so that we + // can positively identify the final release of the internal data + + auto lock = LockExclusive(); + if (!m_sharedActivityData.unique()) + { + fStop = false; + m_sharedActivityData.reset(); + } + } + + if (fStop && m_pActivityData->NeedsStopped()) + { + ReportStopActivity(m_pActivityData->SetUnhandledException()); + } + } + +private: + void ReportStopActivity(HRESULT hr) WI_NOEXCEPT + { + if (FAILED(hr) && + WI_AreAllFlagsClear(Keyword, (MICROSOFT_KEYWORD_TELEMETRY | MICROSOFT_KEYWORD_MEASURES | MICROSOFT_KEYWORD_CRITICAL_DATA)) && + WI_IsFlagSet(options, ActivityOptions::TelemetryOnFailure)) + { + wil::FailureInfo const* pFailure = GetFailureInfo(); + if (pFailure != nullptr) + { + __TRACELOGGING_TEST_HOOK_CALLCONTEXT_ERROR(pFailure, pFailure->hr); + auto& failure = *pFailure; + __WI_TraceLoggingWriteTagged( + *this, + "ActivityFailure", + TraceLoggingKeyword(Keyword | MICROSOFT_KEYWORD_TELEMETRY), + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + __ACTIVITY_FAILURE_TELEMETRY_FAILURE_PARAMS(failure)); + } + else + { + __TRACELOGGING_TEST_HOOK_CALLCONTEXT_ERROR(nullptr, hr); + __WI_TraceLoggingWriteTagged( + *this, + "ActivityFailure", + TraceLoggingKeyword(Keyword | MICROSOFT_KEYWORD_TELEMETRY), + TraceLoggingLevel(WINEVENT_LEVEL_ERROR), + __ACTIVITY_FAILURE_TELEMETRY_PARAMS( + hr, m_pActivityData->GetCallContext()->contextName, m_pActivityData->GetCallContext()->contextMessage)); + } + } + + StopActivity(); + } + + rwlock_release_exclusive_scope_exit LockExclusive() WI_NOEXCEPT + { + // We only need to lock when we're sharing.... + return (m_sharedActivityData ? m_sharedActivityData->LockExclusive() : rwlock_release_exclusive_scope_exit()); + } + + template + class ActivityData : public _TlgActivityBase, keyword, level> + { + using BaseTy = _TlgActivityBase, keyword, level>; + friend BaseTy; + void OnStarted() + { + } + void OnStopped() + { + } + + // SFINAE dispatching on presence of ActivityTraceLoggingTypeOther::CreateActivityId(_Out_ GUID& childActivityId, _In_opt_ const GUID* relatedActivityId) + template + auto CreateActivityIdByProviderType(int, _Out_ GUID& childActivityId) + -> decltype(ProviderType::CreateActivityId(childActivityId, this->GetRelatedId()), (void)0) + { + ProviderType::CreateActivityId(childActivityId, this->GetRelatedId()); + } + + template + auto CreateActivityIdByProviderType(long, _Out_ GUID& childActivityId) -> void + { + EventActivityIdControl(EVENT_ACTIVITY_CTRL_CREATE_ID, &childActivityId); + } + + void CreateActivityId(_Out_ GUID& childActivityId) + { + CreateActivityIdByProviderType(0, childActivityId); + } + + public: + ActivityData(_In_opt_ PCSTR contextName = nullptr) WI_NOEXCEPT : BaseTy(), + m_callContext(contextName), + m_result(S_OK), + m_stopCountExpected(1) + { + } + + ActivityData(ActivityData&& other) WI_NOEXCEPT : BaseTy(wistd::move(other)), + m_callContext(wistd::move(other.m_callContext)), + m_result(other.m_result), + m_failure(wistd::move(other.m_failure)), + m_stopCountExpected(other.m_stopCountExpected) + { + } + + ActivityData& operator=(ActivityData&& other) WI_NOEXCEPT + { + BaseTy::operator=(wistd::move(other)); + m_callContext = wistd::move(other.m_callContext); + m_result = other.m_result; + m_failure = wistd::move(other.m_failure); + m_stopCountExpected = other.m_stopCountExpected; + return *this; + } + + ActivityData(ActivityData const& other) = delete; + ActivityData& operator=(ActivityData const& other) = delete; + + // returns true if the event was reported to telemetry + void NotifyFailure(FailureInfo const& failure) WI_NOEXCEPT + { + if ((failure.hr != m_failure.GetFailureInfo().hr) && // don't replace with the same error (likely propagation up the stack) + ((failure.hr != m_result) || + SUCCEEDED(m_result))) // don't replace if we've already got the current explicitly supplied failure code + { + m_failure.SetFailureInfo(failure); + } + } + + rwlock_release_exclusive_scope_exit LockExclusive() WI_NOEXCEPT + { + return m_lock.lock_exclusive(); + } + + static TraceLoggingHProvider Provider() + { + return ActivityTraceLoggingTypeOther::Provider(); + } + + WI_NODISCARD bool NeedsStopped() const WI_NOEXCEPT + { + return BaseTy::IsStarted(); + } + + void SetRelatedActivityId(const GUID* relatedId) + { + this->SetRelatedId(*relatedId); + } + + bool SetStopResult(HRESULT hr, _Out_opt_ HRESULT* phr) WI_NOEXCEPT + { + // We must be expecting at least one Stop -- otherwise the caller is calling Stop() more times + // than it can (normally once, or +1 for each call to Split()) + __FAIL_FAST_IMMEDIATE_ASSERT__(m_stopCountExpected >= 1); + if (SUCCEEDED(m_result)) + { + m_result = hr; + } + if (phr != nullptr) + { + *phr = m_result; + } + return ((--m_stopCountExpected) == 0); + } + + HRESULT SetUnhandledException() WI_NOEXCEPT + { + HRESULT hr = m_failure.GetFailureInfo().hr; + SetStopResult(FAILED(hr) ? hr : HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION), &hr); + return hr; + } + + void IncrementExpectedStopCount() WI_NOEXCEPT + { + m_stopCountExpected++; + } + + WI_NODISCARD FailureInfo const* GetFailureInfo() const WI_NOEXCEPT + { + return (FAILED(m_result) && (m_result == m_failure.GetFailureInfo().hr)) ? &m_failure.GetFailureInfo() : nullptr; + } + + WI_NODISCARD inline HRESULT GetResult() const WI_NOEXCEPT + { + return m_result; + } + + details::StoredCallContextInfo* GetCallContext() WI_NOEXCEPT + { + return &m_callContext; + } + + private: + details::StoredCallContextInfo m_callContext; + HRESULT m_result; + StoredFailureInfo m_failure; + int m_stopCountExpected; + wil::srwlock m_lock; + }; + + mutable ActivityData m_activityData; + mutable ActivityData* m_pActivityData; + mutable details::shared_object> m_sharedActivityData; + mutable details::ThreadFailureCallbackHolder m_callbackHolder; +}; + +} // namespace wil + +// Internal MACRO implementation of Activities. +// Do NOT use these macros directly. +/// @cond +#define __WI_TraceLoggingWriteStart(activity, name, ...) \ + __pragma(warning(push)) __pragma(warning(disable : 4127)) do \ + { \ + _tlgActivityDecl(activity); \ + using _tlg_Activity_t = wistd::remove_reference_t; \ + static constexpr const UINT64 _tlgActivity_Keyword = _tlg_Activity_t::Keyword; \ + static constexpr const UINT8 _tlgActivity_Level = _tlg_Activity_t::Level; \ + static constexpr const UINT64 _tlgActivityPrivacyTag = _tlg_Activity_t::PrivacyTag; \ + static_assert( \ + _tlgActivity_Keyword == (_tlgActivity_Keyword _tlg_FOREACH(_tlgKeywordVal, ##__VA_ARGS__)), \ + "Do not use TraceLoggingKeyword in TraceLoggingWriteStart. Keywords for START events are " \ + "specified in the activity type, e.g. TraceLoggingActivity."); \ + static_assert( \ + _tlgActivity_Level == (_tlgActivity_Level _tlg_FOREACH(_tlgLevelVal, ##__VA_ARGS__)), \ + "Do not use TraceLoggingLevel in TraceLoggingWriteStart. The Level for START events is " \ + "specified in the activity type, e.g. TraceLoggingActivity."); \ + _tlgActivityRef(activity).zInternalStart(); \ + TraceLoggingWriteActivity( \ + TraceLoggingType::Provider(), \ + (name), \ + _tlgActivityRef(activity).Id(), \ + _tlgActivityRef(activity).zInternalRelatedId(), \ + TraceLoggingOpcode(1 /* WINEVENT_OPCODE_START */), \ + TraceLoggingKeyword(_tlgActivity_Keyword), \ + TraceLoggingLevel(_tlgActivity_Level), \ + TelemetryPrivacyDataTag(_tlgActivityPrivacyTag), \ + TraceLoggingDescription("~^" _wiltlg_LSTRINGIZE(activity) L"^~"), \ + ##__VA_ARGS__); \ + } \ + while (0) \ + __pragma(warning(pop)) + +#define __WRITE_ACTIVITY_START(EventId, ...) \ + __TRACELOGGING_TEST_HOOK_ACTIVITY_START(); \ + __WI_TraceLoggingWriteStart(*this, #EventId, __ACTIVITY_START_PARAMS(), ##__VA_ARGS__); \ + EnsureWatchingCurrentThread() + +#define __WI_TraceLoggingWriteStop(activity, name, ...) \ + __pragma(warning(push)) __pragma(warning(disable : 4127)) do \ + { \ + _tlgActivityDecl(activity); \ + using _tlg_Activity_t = wistd::remove_reference_t; \ + static constexpr const UINT64 _tlgActivity_Keyword = _tlg_Activity_t::Keyword; \ + static constexpr const UINT8 _tlgActivity_Level = _tlg_Activity_t::Level; \ + static constexpr const UINT64 _tlgActivityPrivacyTag = _tlg_Activity_t::PrivacyTag; \ + static_assert( \ + _tlgActivity_Keyword == (_tlgActivity_Keyword _tlg_FOREACH(_tlgKeywordVal, ##__VA_ARGS__)), \ + "Do not use TraceLoggingKeyword in TraceLoggingWriteStop. Keywords for STOP events are " \ + "specified in the activity type, e.g. TraceLoggingActivity."); \ + static_assert( \ + _tlgActivity_Level == (_tlgActivity_Level _tlg_FOREACH(_tlgLevelVal, ##__VA_ARGS__)), \ + "Do not use TraceLoggingLevel in TraceLoggingWriteStop. The Level for STOP events is " \ + "specified in the activity type, e.g. TraceLoggingActivity."); \ + _tlgActivityRef(activity).zInternalStop(); \ + TraceLoggingWriteActivity( \ + TraceLoggingType::Provider(), \ + (name), \ + _tlgActivityRef(activity).Id(), \ + NULL, \ + TraceLoggingOpcode(2 /* WINEVENT_OPCODE_STOP */), \ + TraceLoggingKeyword(_tlgActivity_Keyword), \ + TraceLoggingLevel(_tlgActivity_Level), \ + TelemetryPrivacyDataTag(_tlgActivityPrivacyTag), \ + TraceLoggingDescription("~^" _wiltlg_LSTRINGIZE(activity) L"^~"), \ + ##__VA_ARGS__); \ + } \ + while (0) \ + __pragma(warning(pop)) + +#define __WRITE_ACTIVITY_STOP(EventId, ...) \ + wil::FailureInfo const* pFailure = GetFailureInfo(); \ + if (pFailure != nullptr) \ + { \ + __TRACELOGGING_TEST_HOOK_ACTIVITY_STOP(pFailure, pFailure->hr); \ + auto& failure = *pFailure; \ + if (false, WI_IsAnyFlagSet(Keyword, (MICROSOFT_KEYWORD_TELEMETRY | MICROSOFT_KEYWORD_MEASURES | MICROSOFT_KEYWORD_CRITICAL_DATA))) \ + { \ + __WI_TraceLoggingWriteStop(*this, #EventId, __ACTIVITY_STOP_TELEMETRY_FAILURE_PARAMS(failure), ##__VA_ARGS__); \ + } \ + else \ + { \ + __WI_TraceLoggingWriteStop(*this, #EventId, __ACTIVITY_STOP_TRACELOGGING_FAILURE_PARAMS(failure), ##__VA_ARGS__); \ + } \ + } \ + else \ + { \ + __TRACELOGGING_TEST_HOOK_ACTIVITY_STOP(nullptr, GetResult()); \ + __WI_TraceLoggingWriteStop(*this, #EventId, __ACTIVITY_STOP_PARAMS(GetResult()), ##__VA_ARGS__); \ + } \ + IgnoreCurrentThread(); + +// optional params are: KeyWord, Level, PrivacyTags, Options +#define __BEGIN_TRACELOGGING_ACTIVITY_CLASS(ActivityClassName, ...) \ + class ActivityClassName final : public wil::ActivityBase \ + { \ + protected: \ + void StopActivity() WI_NOEXCEPT override \ + { \ + __WRITE_ACTIVITY_STOP(ActivityClassName); \ + } \ + bool WasAlreadyReportedToTelemetry(long failureId) WI_NOEXCEPT override \ + { \ + return TraceLoggingType::WasAlreadyReportedToTelemetry(failureId); \ + } \ +\ + public: \ + static bool IsEnabled() WI_NOEXCEPT \ + { \ + return TraceLoggingType::IsEnabled(); \ + } \ + ~ActivityClassName() WI_NOEXCEPT \ + { \ + ActivityBase::Destroy(); \ + } \ + ActivityClassName(ActivityClassName const& other) WI_NOEXCEPT : ActivityBase(other) \ + { \ + } \ + ActivityClassName(ActivityClassName&& other) WI_NOEXCEPT : ActivityBase(wistd::move(other)) \ + { \ + } \ + ActivityClassName(ActivityClassName&& other, bool shouldWatchErrors) WI_NOEXCEPT \ + : ActivityBase(wistd::move(other), shouldWatchErrors) \ + { \ + } \ + ActivityClassName& operator=(ActivityClassName const& other) WI_NOEXCEPT \ + { \ + ActivityBase::operator=(other); \ + return *this; \ + } \ + ActivityClassName& operator=(ActivityClassName&& other) WI_NOEXCEPT \ + { \ + auto localActivity(wistd::move(*this)); \ + ActivityBase::operator=(wistd::move(other)); \ + return *this; \ + } \ + WI_NODISCARD explicit operator bool() const WI_NOEXCEPT \ + { \ + return IsRunning(); \ + } \ + void StopWithResult(HRESULT hr) \ + { \ + ActivityBase::Stop(hr); \ + } \ + template \ + void StopWithResult(HRESULT hr, TArgs&&... args) \ + { \ + SetStopResult(hr); \ + Stop(wistd::forward(args)...); \ + } \ + void Stop(HRESULT hr = S_OK) WI_NOEXCEPT \ + { \ + ActivityBase::Stop(hr); \ + } \ + void StartActivity() WI_NOEXCEPT \ + { \ + __WRITE_ACTIVITY_START(ActivityClassName); \ + } \ + void StartRelatedActivity() WI_NOEXCEPT \ + { \ + wil::details::SetRelatedActivityId(*this); \ + StartActivity(); \ + } \ + void StartActivityWithCorrelationVector(PCSTR correlationVector) WI_NOEXCEPT \ + { \ + __WRITE_ACTIVITY_START(ActivityClassName, TraceLoggingString(correlationVector, "__TlgCV__")); \ + } \ + WI_NODISCARD ActivityClassName Split() WI_NOEXCEPT \ + { \ + __FAIL_FAST_IMMEDIATE_ASSERT__(IsRunning()); \ + IncrementExpectedStopCount(); \ + return ActivityClassName(*this); \ + } \ + WI_NODISCARD ActivityClassName TransferToCurrentThread() WI_NOEXCEPT \ + { \ + return ActivityClassName(wistd::move(*this), IsRunning()); \ + } \ + WI_NODISCARD ActivityClassName TransferToMember() WI_NOEXCEPT \ + { \ + return ActivityClassName(wistd::move(*this), false); \ + } + +#define __IMPLEMENT_ACTIVITY_CLASS(ActivityClassName) \ +private: \ + template \ + ActivityClassName(wil::details::tag_start, TArgs&&... args) WI_NOEXCEPT : ActivityBase(#ActivityClassName) \ + { \ + StartActivity(wistd::forward(args)...); \ + __TRACELOGGING_DEFINE_PROVIDER_STORAGE_LINK("this", ActivityClassName); \ + } \ + template \ + ActivityClassName(wil::details::tag_start_cv, _In_opt_ PCSTR correlationVector, TArgs&&... args) WI_NOEXCEPT \ + : ActivityBase(#ActivityClassName) \ + { \ + StartActivityWithCorrelationVector(correlationVector, wistd::forward(args)...); \ + __TRACELOGGING_DEFINE_PROVIDER_STORAGE_LINK("this", ActivityClassName); \ + } \ +\ +public: \ + ActivityClassName() WI_NOEXCEPT : ActivityBase(#ActivityClassName, false) \ + { \ + } \ + template \ + WI_NODISCARD static ActivityClassName Start(TArgs&&... args) \ + { \ + return ActivityClassName(wil::details::tag_start(), wistd::forward(args)...); \ + } \ + template \ + WI_NODISCARD static ActivityClassName StartWithCorrelationVector(_In_ PCSTR correlationVector, TArgs&&... args) \ + { \ + return ActivityClassName(wil::details::tag_start_cv(), correlationVector, wistd::forward(args)...); \ + } + +#define __IMPLEMENT_CALLCONTEXT_CLASS(ActivityClassName) \ +protected: \ + ActivityClassName(_In_opt_ void**, PCSTR contextName, _In_opt_ _Printf_format_string_ PCSTR formatString, _In_opt_ va_list argList) : \ + ActivityBase(contextName) \ + { \ + GetCallContext()->SetMessage(formatString, argList); \ + StartActivity(); \ + } \ + ActivityClassName(_In_opt_ void**, PCSTR contextName) : ActivityBase(contextName) \ + { \ + StartActivity(); \ + } \ +\ +public: \ + ActivityClassName(PCSTR contextName) : ActivityBase(contextName, false) \ + { \ + } \ + ActivityClassName(PCSTR contextName, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT : ActivityClassName(contextName) \ + { \ + va_list argList; \ + va_start(argList, formatString); \ + GetCallContext()->SetMessage(formatString, argList); \ + } \ + WI_NODISCARD static ActivityClassName Start(PCSTR contextName) WI_NOEXCEPT \ + { \ + return ActivityClassName(static_cast(__nullptr), contextName); \ + } \ + WI_NODISCARD static ActivityClassName Start(PCSTR contextName, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT \ + { \ + va_list argList; \ + va_start(argList, formatString); \ + return ActivityClassName(static_cast(__nullptr), contextName, formatString, argList); \ + } + +#define __END_TRACELOGGING_ACTIVITY_CLASS() \ + } \ + ; +/// @endcond + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TAGGED_TRACELOGGING_EVENT(EventId, ...) \ + void EventId() \ + { \ + __WI_TraceLoggingWriteTagged(*this, #EventId, _GENERIC_PARTB_FIELDS_ENABLED, ##__VA_ARGS__); \ + } +#else +#define DEFINE_TAGGED_TRACELOGGING_EVENT(EventId, ...) \ + void EventId() \ + { \ + __WI_TraceLoggingWriteTagged(*this, #EventId, ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TAGGED_TRACELOGGING_EVENT_CV(EventId, ...) \ + void EventId(PCSTR correlationVector) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, #EventId, _GENERIC_PARTB_FIELDS_ENABLED, TraceLoggingString(correlationVector, "__TlgCV__"), ##__VA_ARGS__); \ + } +#else +#define DEFINE_TAGGED_TRACELOGGING_EVENT_CV(EventId, ...) \ + void EventId(PCSTR correlationVector) \ + { \ + __WI_TraceLoggingWriteTagged(*this, #EventId, TraceLoggingString(correlationVector, "__TlgCV__"), ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM1(EventId, VarType1, varName1, ...) \ + template \ + void EventId(T1&& varName1) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM1(EventId, VarType1, varName1, ...) \ + template \ + void EventId(T1&& varName1) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, #EventId, TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM1_CV(EventId, VarType1, varName1, ...) \ + template \ + void EventId(T1&& varName1, PCSTR correlationVector) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM1_CV(EventId, VarType1, varName1, ...) \ + template \ + void EventId(T1&& varName1, PCSTR correlationVector) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM2(EventId, VarType1, varName1, VarType2, varName2, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM2(EventId, VarType1, varName1, VarType2, varName2, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM2_CV(EventId, VarType1, varName1, VarType2, varName2, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, PCSTR correlationVector) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM2_CV(EventId, VarType1, varName1, VarType2, varName2, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, PCSTR correlationVector) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM3(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, T3&& varName3) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM3(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, T3&& varName3) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM3_CV(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, T3&& varName3, PCSTR correlationVector) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM3_CV(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, T3&& varName3, PCSTR correlationVector) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM4( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM4( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM4_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, PCSTR correlationVector) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM4_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, PCSTR correlationVector) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM5( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM5( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM5_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, PCSTR correlationVector) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM5_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, PCSTR correlationVector) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM6( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM6( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM6_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, PCSTR correlationVector) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM6_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, PCSTR correlationVector) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM7( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM7( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM7_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7, PCSTR correlationVector) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM7_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7, PCSTR correlationVector) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM8( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, VarType8, varName8, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7, T8&& varName8) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + TraceLoggingValue(static_cast(wistd::forward(varName8)), _wiltlg_STRINGIZE(varName8)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM8( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, VarType8, varName8, ...) \ + template \ + void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7, T8&& varName8) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + TraceLoggingValue(static_cast(wistd::forward(varName8)), _wiltlg_STRINGIZE(varName8)), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM8_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, VarType8, varName8, ...) \ + template \ + void EventId( \ + T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7, T8&& varName8, PCSTR correlationVector) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + TraceLoggingValue(static_cast(wistd::forward(varName8)), _wiltlg_STRINGIZE(varName8)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM8_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, VarType8, varName8, ...) \ + template \ + void EventId( \ + T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7, T8&& varName8, PCSTR correlationVector) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + TraceLoggingValue(static_cast(wistd::forward(varName8)), _wiltlg_STRINGIZE(varName8)), \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM9( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + VarType9, \ + varName9, \ + ...) \ + template \ + void EventId( \ + T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7, T8&& varName8, T9&& varName9) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + TraceLoggingValue(static_cast(wistd::forward(varName8)), _wiltlg_STRINGIZE(varName8)), \ + TraceLoggingValue(static_cast(wistd::forward(varName9)), _wiltlg_STRINGIZE(varName9)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM9( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + VarType9, \ + varName9, \ + ...) \ + template \ + void EventId( \ + T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7, T8&& varName8, T9&& varName9) \ + { \ + __WI_TraceLoggingWriteTagged( \ + *this, \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + TraceLoggingValue(static_cast(wistd::forward(varName8)), _wiltlg_STRINGIZE(varName8)), \ + TraceLoggingValue(static_cast(wistd::forward(varName9)), _wiltlg_STRINGIZE(varName9)), \ + ##__VA_ARGS__); \ + } +#endif + +#define DEFINE_TAGGED_TRACELOGGING_EVENT_UINT32(EventId, varName, ...) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM1(EventId, UINT32, varName, ##__VA_ARGS__) +#define DEFINE_TAGGED_TRACELOGGING_EVENT_BOOL(EventId, varName, ...) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM1(EventId, bool, varName, ##__VA_ARGS__) +#define DEFINE_TAGGED_TRACELOGGING_EVENT_STRING(EventId, varName, ...) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM1(EventId, PCWSTR, varName, ##__VA_ARGS__) + +// Internal MACRO implementation of TraceLogging classes. +// Do NOT use these macros directly. +/// @cond +#define __IMPLEMENT_TRACELOGGING_CLASS_BASE(TraceLoggingClassName, TraceLoggingProviderOwnerClassName) \ +public: \ + typedef TraceLoggingProviderOwnerClassName TraceLoggingType; \ + static bool IsEnabled( \ + UCHAR eventLevel = 0 /* WINEVENT_LEVEL_XXX, e.g. WINEVENT_LEVEL_VERBOSE */, \ + ULONGLONG eventKeywords = 0 /* MICROSOFT_KEYWORD_XXX */) WI_NOEXCEPT \ + { \ + return Instance()->IsEnabled_(eventLevel, eventKeywords); \ + } \ + static TraceLoggingHProvider Provider() WI_NOEXCEPT \ + { \ + return static_cast(Instance())->Provider_(); \ + } \ + static void SetTelemetryEnabled(bool) WI_NOEXCEPT \ + { \ + } \ + static void SetErrorReportingType(wil::ErrorReportingType type) WI_NOEXCEPT \ + { \ + return Instance()->SetErrorReportingType_(type); \ + } \ + static void __stdcall FallbackTelemetryCallback(bool alreadyReported, wil::FailureInfo const& failure) WI_NOEXCEPT \ + { \ + return Instance()->OnErrorReported(alreadyReported, failure); \ + } \ + WI_NODISCARD static wil::ActivityThreadWatcher WatchCurrentThread(PCSTR contextName) WI_NOEXCEPT \ + { \ + return wil::ActivityThreadWatcher(Instance(), contextName); \ + } \ + WI_NODISCARD static wil::ActivityThreadWatcher WatchCurrentThread( \ + PCSTR contextName, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT \ + { \ + va_list argList; \ + va_start(argList, formatString); \ + return wil::ActivityThreadWatcher(Instance(), contextName, formatString, argList); \ + } \ + __BEGIN_TRACELOGGING_ACTIVITY_CLASS(CallContext, wil::ActivityOptions::TelemetryOnFailure) \ + __IMPLEMENT_CALLCONTEXT_CLASS(CallContext); \ + __END_TRACELOGGING_ACTIVITY_CLASS(); \ + static CallContext Start(PCSTR contextName) WI_NOEXCEPT \ + { \ + return CallContext(contextName, __nullptr, __nullptr); \ + } \ + static CallContext Start(PCSTR contextName, _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT \ + { \ + va_list argList; \ + va_start(argList, formatString); \ + return CallContext(contextName, formatString, argList); \ + } \ + static void TraceLoggingInfo(_Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT \ + { \ + va_list argList; \ + va_start(argList, formatString); \ + return Instance()->ReportTraceLoggingMessage(formatString, argList); \ + } \ + static void TraceLoggingError(_Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT \ + { \ + va_list argList; \ + va_start(argList, formatString); \ + return Instance()->ReportTraceLoggingError(formatString, argList); \ + } \ +\ +private: \ + TraceLoggingHProvider Provider_() const WI_NOEXCEPT = delete; \ + TraceLoggingClassName() WI_NOEXCEPT{}; \ +\ +protected: \ + static TraceLoggingClassName* Instance() WI_NOEXCEPT \ + { \ + static wil::details::static_lazy wrapper; \ + return wrapper.get([]() { \ + wrapper.cleanup(); \ + }); \ + } \ + friend class wil::details::static_lazy; + +#define __IMPLEMENT_TRACELOGGING_CLASS_WITH_GROUP(TraceLoggingClassName, ProviderName, ProviderId, TraceLoggingOption) \ + __IMPLEMENT_TRACELOGGING_CLASS_BASE(TraceLoggingClassName, TraceLoggingClassName) \ +private: \ + struct StaticHandle \ + { \ + TraceLoggingHProvider handle; \ + StaticHandle() WI_NOEXCEPT \ + { \ + TRACELOGGING_DEFINE_PROVIDER_STORAGE(__hInner, ProviderName, ProviderId, TraceLoggingOption); \ + _tlg_DefineProvider_annotation(TraceLoggingClassName, _Tlg##TraceLoggingClassName##Prov, 0, ProviderName); \ + handle = &__hInner; \ + } \ + } m_staticHandle; \ +\ +protected: \ + void Create() WI_NOEXCEPT \ + { \ + Register(m_staticHandle.handle); \ + } \ +\ +public: + +#define __IMPLEMENT_TRACELOGGING_CLASS(TraceLoggingClassName, ProviderName, ProviderId) \ + __IMPLEMENT_TRACELOGGING_CLASS_WITH_GROUP(TraceLoggingClassName, ProviderName, ProviderId, TraceLoggingOptionMicrosoftTelemetry()) + +#define __IMPLEMENT_TRACELOGGING_CLASS_WITH_GROUP_CB(TraceLoggingClassName, ProviderName, ProviderId, TraceLoggingOption) \ + __IMPLEMENT_TRACELOGGING_CLASS_BASE(TraceLoggingClassName, TraceLoggingClassName) \ +private: \ + struct StaticHandle \ + { \ + TraceLoggingHProvider handle; \ + StaticHandle() WI_NOEXCEPT \ + { \ + TRACELOGGING_DEFINE_PROVIDER_STORAGE(__hInner, ProviderName, ProviderId, TraceLoggingOption); \ + _tlg_DefineProvider_annotation(TraceLoggingClassName, _Tlg##TraceLoggingClassName##Prov, 0, ProviderName); \ + handle = &__hInner; \ + } \ + } m_staticHandle; \ + static VOID NTAPI Callback( \ + _In_ const GUID* SourceId, \ + ULONG ControlCode, \ + UCHAR Level, \ + ULONGLONG MatchAnyKeyword, \ + ULONGLONG MatchAllKeyword, \ + _In_opt_ EVENT_FILTER_DESCRIPTOR* FilterData, \ + void* CallbackContext); \ +\ +protected: \ + void Create() WI_NOEXCEPT \ + { \ + Register(m_staticHandle.handle, &TraceLoggingClassName::Callback); \ + } \ +\ +public: + +#define __IMPLEMENT_TRACELOGGING_CLASS_WITHOUT_TELEMETRY(TraceLoggingClassName, ProviderName, ProviderId) \ + __IMPLEMENT_TRACELOGGING_CLASS_BASE(TraceLoggingClassName, TraceLoggingClassName) \ +private: \ + struct StaticHandle \ + { \ + TraceLoggingHProvider handle; \ + StaticHandle() WI_NOEXCEPT \ + { \ + TRACELOGGING_DEFINE_PROVIDER_STORAGE(__hInner, ProviderName, ProviderId); \ + _tlg_DefineProvider_annotation(TraceLoggingClassName, _Tlg##TraceLoggingClassName##Prov, 0, ProviderName); \ + handle = &__hInner; \ + } \ + } m_staticHandle; \ +\ +protected: \ + void Create() WI_NOEXCEPT \ + { \ + Register(m_staticHandle.handle); \ + } \ +\ +public: +/// @endcond + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TRACELOGGING_EVENT(EventId, ...) \ + static void EventId() \ + { \ + TraceLoggingWrite(TraceLoggingType::Provider(), #EventId, _GENERIC_PARTB_FIELDS_ENABLED, ##__VA_ARGS__); \ + } +#else +#define DEFINE_TRACELOGGING_EVENT(EventId, ...) \ + static void EventId() \ + { \ + TraceLoggingWrite(TraceLoggingType::Provider(), #EventId, ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TRACELOGGING_EVENT_CV(EventId, ...) \ + static void EventId(PCSTR correlationVector) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), #EventId, _GENERIC_PARTB_FIELDS_ENABLED, TraceLoggingString(correlationVector, "__TlgCV__"), ##__VA_ARGS__); \ + } +#else +#define DEFINE_TRACELOGGING_EVENT_CV(EventId, ...) \ + static void EventId(PCSTR correlationVector) \ + { \ + TraceLoggingWrite(TraceLoggingType::Provider(), #EventId, TraceLoggingString(correlationVector, "__TlgCV__"), ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TRACELOGGING_EVENT_PARAM1(EventId, VarType1, varName1, ...) \ + template \ + static void EventId(T1&& varName1) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TRACELOGGING_EVENT_PARAM1(EventId, VarType1, varName1, ...) \ + template \ + static void EventId(T1&& varName1) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TRACELOGGING_EVENT_PARAM1_CV(EventId, VarType1, varName1, ...) \ + template \ + static void EventId(T1&& varName1, PCSTR correlationVector) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TRACELOGGING_EVENT_PARAM1_CV(EventId, VarType1, varName1, ...) \ + template \ + static void EventId(T1&& varName1, PCSTR correlationVector) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TRACELOGGING_EVENT_PARAM2(EventId, VarType1, varName1, VarType2, varName2, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TRACELOGGING_EVENT_PARAM2(EventId, VarType1, varName1, VarType2, varName2, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TRACELOGGING_EVENT_PARAM2_CV(EventId, VarType1, varName1, VarType2, varName2, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2, PCSTR correlationVector) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TRACELOGGING_EVENT_PARAM2_CV(EventId, VarType1, varName1, VarType2, varName2, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2, PCSTR correlationVector) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TRACELOGGING_EVENT_PARAM3(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2, T3&& varName3) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TRACELOGGING_EVENT_PARAM3(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2, T3&& varName3) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TRACELOGGING_EVENT_PARAM3_CV(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2, T3&& varName3, PCSTR correlationVector) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TRACELOGGING_EVENT_PARAM3_CV(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2, T3&& varName3, PCSTR correlationVector) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TRACELOGGING_EVENT_PARAM4(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TRACELOGGING_EVENT_PARAM4(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TRACELOGGING_EVENT_PARAM4_CV(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, PCSTR correlationVector) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TRACELOGGING_EVENT_PARAM4_CV(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, PCSTR correlationVector) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TRACELOGGING_EVENT_PARAM5( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TRACELOGGING_EVENT_PARAM5( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TRACELOGGING_EVENT_PARAM5_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, PCSTR correlationVector) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TRACELOGGING_EVENT_PARAM5_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, PCSTR correlationVector) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TRACELOGGING_EVENT_PARAM6( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TRACELOGGING_EVENT_PARAM6( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TRACELOGGING_EVENT_PARAM6_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, PCSTR correlationVector) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TRACELOGGING_EVENT_PARAM6_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, PCSTR correlationVector) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TRACELOGGING_EVENT_PARAM7( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TRACELOGGING_EVENT_PARAM7( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TRACELOGGING_EVENT_PARAM7_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, ...) \ + template \ + static void EventId( \ + T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7, PCSTR correlationVector) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TRACELOGGING_EVENT_PARAM7_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, ...) \ + template \ + static void EventId( \ + T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7, PCSTR correlationVector) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TRACELOGGING_EVENT_PARAM8( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, VarType8, varName8, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7, T8&& varName8) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + TraceLoggingValue(static_cast(wistd::forward(varName8)), _wiltlg_STRINGIZE(varName8)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TRACELOGGING_EVENT_PARAM8( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, VarType8, varName8, ...) \ + template \ + static void EventId(T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7, T8&& varName8) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + TraceLoggingValue(static_cast(wistd::forward(varName8)), _wiltlg_STRINGIZE(varName8)), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TRACELOGGING_EVENT_PARAM8_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, VarType8, varName8, ...) \ + template \ + static void EventId( \ + T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7, T8&& varName8, PCSTR correlationVector) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + TraceLoggingValue(static_cast(wistd::forward(varName8)), _wiltlg_STRINGIZE(varName8)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TRACELOGGING_EVENT_PARAM8_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, VarType8, varName8, ...) \ + template \ + static void EventId( \ + T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7, T8&& varName8, PCSTR correlationVector) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + TraceLoggingValue(static_cast(wistd::forward(varName8)), _wiltlg_STRINGIZE(varName8)), \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TRACELOGGING_EVENT_PARAM9( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + VarType9, \ + varName9, \ + ...) \ + template \ + static void EventId( \ + T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7, T8&& varName8, T9&& varName9) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + TraceLoggingValue(static_cast(wistd::forward(varName8)), _wiltlg_STRINGIZE(varName8)), \ + TraceLoggingValue(static_cast(wistd::forward(varName9)), _wiltlg_STRINGIZE(varName9)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TRACELOGGING_EVENT_PARAM9( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + VarType9, \ + varName9, \ + ...) \ + template \ + static void EventId( \ + T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7, T8&& varName8, T9&& varName9) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + TraceLoggingValue(static_cast(wistd::forward(varName8)), _wiltlg_STRINGIZE(varName8)), \ + TraceLoggingValue(static_cast(wistd::forward(varName9)), _wiltlg_STRINGIZE(varName9)), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TRACELOGGING_EVENT_PARAM9_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + VarType9, \ + varName9, \ + ...) \ + template \ + static void EventId( \ + T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7, T8&& varName8, T9&& varName9, PCSTR correlationVector) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + TraceLoggingValue(static_cast(wistd::forward(varName8)), _wiltlg_STRINGIZE(varName8)), \ + TraceLoggingValue(static_cast(wistd::forward(varName9)), _wiltlg_STRINGIZE(varName9)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TRACELOGGING_EVENT_PARAM9_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + VarType9, \ + varName9, \ + ...) \ + template \ + static void EventId( \ + T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7, T8&& varName8, T9&& varName9, PCSTR correlationVector) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + TraceLoggingValue(static_cast(wistd::forward(varName8)), _wiltlg_STRINGIZE(varName8)), \ + TraceLoggingValue(static_cast(wistd::forward(varName9)), _wiltlg_STRINGIZE(varName9)), \ + TraceLoggingString(correlationVector, "__TlgCV__"), \ + ##__VA_ARGS__); \ + } +#endif + +#ifdef _GENERIC_PARTB_FIELDS_ENABLED +#define DEFINE_TRACELOGGING_EVENT_PARAM10( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + VarType9, \ + varName9, \ + VarType10, \ + varName10, \ + ...) \ + template \ + static void EventId( \ + T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7, T8&& varName8, T9&& varName9, T10&& varName10) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + TraceLoggingValue(static_cast(wistd::forward(varName8)), _wiltlg_STRINGIZE(varName8)), \ + TraceLoggingValue(static_cast(wistd::forward(varName9)), _wiltlg_STRINGIZE(varName9)), \ + TraceLoggingValue(static_cast(wistd::forward(varName10)), _wiltlg_STRINGIZE(varName10)), \ + _GENERIC_PARTB_FIELDS_ENABLED, \ + ##__VA_ARGS__); \ + } +#else +#define DEFINE_TRACELOGGING_EVENT_PARAM10( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + VarType9, \ + varName9, \ + VarType10, \ + varName10, \ + ...) \ + template \ + static void EventId( \ + T1&& varName1, T2&& varName2, T3&& varName3, T4&& varName4, T5&& varName5, T6&& varName6, T7&& varName7, T8&& varName8, T9&& varName9, T10&& varName10) \ + { \ + TraceLoggingWrite( \ + TraceLoggingType::Provider(), \ + #EventId, \ + TraceLoggingValue(static_cast(wistd::forward(varName1)), _wiltlg_STRINGIZE(varName1)), \ + TraceLoggingValue(static_cast(wistd::forward(varName2)), _wiltlg_STRINGIZE(varName2)), \ + TraceLoggingValue(static_cast(wistd::forward(varName3)), _wiltlg_STRINGIZE(varName3)), \ + TraceLoggingValue(static_cast(wistd::forward(varName4)), _wiltlg_STRINGIZE(varName4)), \ + TraceLoggingValue(static_cast(wistd::forward(varName5)), _wiltlg_STRINGIZE(varName5)), \ + TraceLoggingValue(static_cast(wistd::forward(varName6)), _wiltlg_STRINGIZE(varName6)), \ + TraceLoggingValue(static_cast(wistd::forward(varName7)), _wiltlg_STRINGIZE(varName7)), \ + TraceLoggingValue(static_cast(wistd::forward(varName8)), _wiltlg_STRINGIZE(varName8)), \ + TraceLoggingValue(static_cast(wistd::forward(varName9)), _wiltlg_STRINGIZE(varName9)), \ + TraceLoggingValue(static_cast(wistd::forward(varName10)), _wiltlg_STRINGIZE(varName10)), \ + ##__VA_ARGS__); \ + } +#endif + +#define DEFINE_TRACELOGGING_EVENT_UINT32(EventId, varName, ...) \ + DEFINE_TRACELOGGING_EVENT_PARAM1(EventId, UINT32, varName, ##__VA_ARGS__) +#define DEFINE_TRACELOGGING_EVENT_BOOL(EventId, varName, ...) \ + DEFINE_TRACELOGGING_EVENT_PARAM1(EventId, bool, varName, ##__VA_ARGS__) +#define DEFINE_TRACELOGGING_EVENT_STRING(EventId, varName, ...) \ + DEFINE_TRACELOGGING_EVENT_PARAM1(EventId, PCWSTR, varName, ##__VA_ARGS__) + +// Declaring a pure TraceLogging class +// To declare a tracelogging class, declare your class derived from wil::TraceLoggingProvider, populate the uuid +// attribute of the class with the GUID of your provider, and then include the IMPLEMENT_TRACELOGGING_CLASS_WITH_MICROSOFT_TELEMETRY +// macro within your class. +// +// If you want to register a provider using a callback to log events, you can instead use the IMPLEMENT_TRACELOGGING_CLASS_WITH_MICROSOFT_TELEMETRY_AND_CALLBACK +// Additionally your tracelogging class will have to implement a static Callback method. See the declaration within __IMPLEMENT_TRACELOGGING_CLASS_WITH_GROUP_CB. +// +// If you don't need or use telemetry, you can instead use the IMPLEMENT_TRACELOGGING_CLASS_WITHOUT_TELEMETRY. +// This prevents telemetry from enabling your provider even if you're not using telemetry. + +#define IMPLEMENT_TRACELOGGING_CLASS_WITH_GROUP(TraceLoggingClassName, ProviderName, ProviderId, GroupName) \ + __IMPLEMENT_TRACELOGGING_CLASS_WITH_GROUP(TraceLoggingClassName, ProviderName, ProviderId, GroupName) + +#define IMPLEMENT_TRACELOGGING_CLASS_WITH_GROUP_CB(TraceLoggingClassName, ProviderName, ProviderId, GroupName) \ + __IMPLEMENT_TRACELOGGING_CLASS_WITH_GROUP_CB(TraceLoggingClassName, ProviderName, ProviderId, GroupName) + +#define IMPLEMENT_TRACELOGGING_CLASS_WITH_MICROSOFT_TELEMETRY(TraceLoggingClassName, ProviderName, ProviderId) \ + IMPLEMENT_TRACELOGGING_CLASS_WITH_GROUP(TraceLoggingClassName, ProviderName, ProviderId, TraceLoggingOptionMicrosoftTelemetry()) +#define IMPLEMENT_TRACELOGGING_CLASS_WITH_MICROSOFT_TELEMETRY_AND_CALLBACK(TraceLoggingClassName, ProviderName, ProviderId) \ + IMPLEMENT_TRACELOGGING_CLASS_WITH_GROUP_CB(TraceLoggingClassName, ProviderName, ProviderId, TraceLoggingOptionMicrosoftTelemetry()) +#define IMPLEMENT_TRACELOGGING_CLASS_WITH_WINDOWS_CORE_TELEMETRY(TraceLoggingClassName, ProviderName, ProviderId) \ + IMPLEMENT_TRACELOGGING_CLASS_WITH_GROUP(TraceLoggingClassName, ProviderName, ProviderId, TraceLoggingOptionWindowsCoreTelemetry()) +#define IMPLEMENT_TRACELOGGING_CLASS_WITHOUT_TELEMETRY(TraceLoggingClassName, ProviderName, ProviderId) \ + __IMPLEMENT_TRACELOGGING_CLASS_WITHOUT_TELEMETRY(TraceLoggingClassName, ProviderName, ProviderId) + +#ifndef WIL_HIDE_DEPRECATED_1612 +WIL_WARN_DEPRECATED_1612_PRAGMA("IMPLEMENT_TRACELOGGING_CLASS") +// DEPRECATED: Use IMPLEMENT_TRACELOGGING_CLASS_WITH_MICROSOFT_TELEMETRY +#define IMPLEMENT_TRACELOGGING_CLASS IMPLEMENT_TRACELOGGING_CLASS_WITH_MICROSOFT_TELEMETRY +#endif + +// [Optional] Externally using a Tracelogging class +// Use TraceLoggingProviderWrite to directly use the trace logging provider externally from the class in code. +// This is recommended only for simple TraceLogging events. Telemetry events and activities are better defined +// within your Tracelogging class using one of the macros below. + +#define TraceLoggingProviderWrite(TraceLoggingClassName, EventId, ...) \ + TraceLoggingWrite(TraceLoggingClassName::TraceLoggingType::Provider(), EventId, ##__VA_ARGS__) + +#define TraceLoggingProviderWriteTelemetry(TraceLoggingClassName, EventId, ...) \ + TraceLoggingWrite( \ + TraceLoggingClassName::TraceLoggingType::Provider(), EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), ##__VA_ARGS__) + +#define TraceLoggingProviderWriteMeasure(TraceLoggingClassName, EventId, ...) \ + TraceLoggingWrite( \ + TraceLoggingClassName::TraceLoggingType::Provider(), EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), ##__VA_ARGS__) + +#define TraceLoggingProviderWriteCriticalData(TraceLoggingClassName, EventId, ...) \ + TraceLoggingWrite( \ + TraceLoggingClassName::TraceLoggingType::Provider(), EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), ##__VA_ARGS__) + +// [Optional] Custom Events +// Use these macros to define a Custom Event for a Provider. Use the TraceLoggingClassWrite or TraceLoggingClassWriteTelemetry +// from within a custom event to issue the event. Methods will be a no-op (and not be called) if the provider is not +// enabled. + +#define TraceLoggingClassWrite(EventId, ...) TraceLoggingWrite(TraceLoggingType::Provider(), EventId, ##__VA_ARGS__) + +#define TraceLoggingClassWriteTelemetry(EventId, ...) \ + TraceLoggingWrite(TraceLoggingType::Provider(), EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), ##__VA_ARGS__) + +#define TraceLoggingClassWriteMeasure(EventId, ...) \ + TraceLoggingWrite(TraceLoggingType::Provider(), EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), ##__VA_ARGS__) + +#define TraceLoggingClassWriteCriticalData(EventId, ...) \ + TraceLoggingWrite(TraceLoggingType::Provider(), EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), ##__VA_ARGS__) + +#define DEFINE_EVENT_METHOD(MethodName) \ + template \ + static void MethodName(TArgs&&... args) WI_NOEXCEPT \ + { \ + if (IsEnabled()) \ + { \ + Instance()->MethodName##_(wistd::forward(args)...); \ + } \ + } \ + void MethodName##_ + +// [Optional] Simple Events +// Use these macros to define very simple telemetry events for a Provider. The events can +// be TELEMETRY events or TRACELOGGING events. + +// To comply with the General Data Protection Regulations (GDPR), all collected Asimov events must +// be tagged with a Privacy Data Type per release and per event instance. Starting with 19H1, +// events will not be uploaded unless they are tagged in code or in DataGrid. Tagging in code is +// preferred and only the "compliant" macro variants (which supply a privacy data type) are +// supported outside of the Windows codebase. +// +// [Microsoft Internal] See also: https://osgwiki.com/wiki/Privacy_Data_Type_-_Tagging_in_Code + +#ifndef DISABLE_NONCOMPLIANT_TELEMETRY + +#define DEFINE_TELEMETRY_EVENT(EventId) DEFINE_TRACELOGGING_EVENT(EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) + +#define DEFINE_TELEMETRY_EVENT_PARAM1(EventId, VarType1, varName1) \ + DEFINE_TRACELOGGING_EVENT_PARAM1(EventId, VarType1, varName1, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TELEMETRY_EVENT_PARAM2(EventId, VarType1, varName1, VarType2, varName2) \ + DEFINE_TRACELOGGING_EVENT_PARAM2(EventId, VarType1, varName1, VarType2, varName2, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TELEMETRY_EVENT_PARAM3(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TRACELOGGING_EVENT_PARAM3( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TELEMETRY_EVENT_PARAM4(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TRACELOGGING_EVENT_PARAM4( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TELEMETRY_EVENT_PARAM5( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TRACELOGGING_EVENT_PARAM5( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TELEMETRY_EVENT_PARAM6( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TRACELOGGING_EVENT_PARAM6( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TELEMETRY_EVENT_PARAM7( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TRACELOGGING_EVENT_PARAM7( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TELEMETRY_EVENT_PARAM8( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, VarType8, varName8) \ + DEFINE_TRACELOGGING_EVENT_PARAM8( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) + +#define DEFINE_TELEMETRY_EVENT_CV(EventId) DEFINE_TRACELOGGING_EVENT_CV(EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TELEMETRY_EVENT_PARAM1_CV(EventId, VarType1, varName1) \ + DEFINE_TRACELOGGING_EVENT_PARAM1_CV(EventId, VarType1, varName1, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TELEMETRY_EVENT_PARAM2_CV(EventId, VarType1, varName1, VarType2, varName2) \ + DEFINE_TRACELOGGING_EVENT_PARAM2_CV(EventId, VarType1, varName1, VarType2, varName2, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TELEMETRY_EVENT_PARAM3_CV(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TRACELOGGING_EVENT_PARAM3_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TELEMETRY_EVENT_PARAM4_CV(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TRACELOGGING_EVENT_PARAM4_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TELEMETRY_EVENT_PARAM5_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TRACELOGGING_EVENT_PARAM5_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TELEMETRY_EVENT_PARAM6_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TRACELOGGING_EVENT_PARAM6_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TELEMETRY_EVENT_PARAM7_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TRACELOGGING_EVENT_PARAM7_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TELEMETRY_EVENT_PARAM8_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, VarType8, varName8) \ + DEFINE_TRACELOGGING_EVENT_PARAM8_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) + +#define DEFINE_TELEMETRY_EVENT_UINT32(EventId, varName) DEFINE_TELEMETRY_EVENT_PARAM1(EventId, UINT32, varName) +#define DEFINE_TELEMETRY_EVENT_BOOL(EventId, varName) DEFINE_TELEMETRY_EVENT_PARAM1(EventId, bool, varName) +#define DEFINE_TELEMETRY_EVENT_STRING(EventId, varName) DEFINE_TELEMETRY_EVENT_PARAM1(EventId, PCWSTR, varName) + +#endif + +#define DEFINE_COMPLIANT_TELEMETRY_EVENT(EventId, PrivacyTag) \ + DEFINE_TRACELOGGING_EVENT(EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), TelemetryPrivacyDataTag(PrivacyTag)) + +#define DEFINE_COMPLIANT_TELEMETRY_EVENT_PARAM1(EventId, PrivacyTag, VarType1, varName1) \ + DEFINE_TRACELOGGING_EVENT_PARAM1( \ + EventId, VarType1, varName1, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_TELEMETRY_EVENT_PARAM2(EventId, PrivacyTag, VarType1, varName1, VarType2, varName2) \ + DEFINE_TRACELOGGING_EVENT_PARAM2( \ + EventId, VarType1, varName1, VarType2, varName2, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_TELEMETRY_EVENT_PARAM3(EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TRACELOGGING_EVENT_PARAM3( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_TELEMETRY_EVENT_PARAM4( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TRACELOGGING_EVENT_PARAM4( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_TELEMETRY_EVENT_PARAM5( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TRACELOGGING_EVENT_PARAM5( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_TELEMETRY_EVENT_PARAM6( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TRACELOGGING_EVENT_PARAM6( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_TELEMETRY_EVENT_PARAM7( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TRACELOGGING_EVENT_PARAM7( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_TELEMETRY_EVENT_PARAM8( \ + EventId, \ + PrivacyTag, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8) \ + DEFINE_TRACELOGGING_EVENT_PARAM8( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag)) + +#define DEFINE_COMPLIANT_TELEMETRY_EVENT_CV(EventId, PrivacyTag) \ + DEFINE_TRACELOGGING_EVENT_CV(EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_TELEMETRY_EVENT_PARAM1_CV(EventId, PrivacyTag, VarType1, varName1) \ + DEFINE_TRACELOGGING_EVENT_PARAM1_CV( \ + EventId, VarType1, varName1, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_TELEMETRY_EVENT_PARAM2_CV(EventId, PrivacyTag, VarType1, varName1, VarType2, varName2) \ + DEFINE_TRACELOGGING_EVENT_PARAM2_CV( \ + EventId, VarType1, varName1, VarType2, varName2, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_TELEMETRY_EVENT_PARAM3_CV(EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TRACELOGGING_EVENT_PARAM3_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_TELEMETRY_EVENT_PARAM4_CV( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TRACELOGGING_EVENT_PARAM4_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_TELEMETRY_EVENT_PARAM5_CV( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TRACELOGGING_EVENT_PARAM5_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_TELEMETRY_EVENT_PARAM6_CV( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TRACELOGGING_EVENT_PARAM6_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_TELEMETRY_EVENT_PARAM7_CV( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TRACELOGGING_EVENT_PARAM7_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_TELEMETRY_EVENT_PARAM8_CV( \ + EventId, \ + PrivacyTag, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8) \ + DEFINE_TRACELOGGING_EVENT_PARAM8_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag)) + +#define DEFINE_COMPLIANT_EVENTTAGGED_TELEMETRY_EVENT_CV(EventId, PrivacyTag, EventTag) \ + DEFINE_TRACELOGGING_EVENT_CV( \ + EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), TelemetryPrivacyDataTag(PrivacyTag), TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_TELEMETRY_EVENT_PARAM1_CV(EventId, PrivacyTag, EventTag, VarType1, varName1) \ + DEFINE_TRACELOGGING_EVENT_PARAM1_CV( \ + EventId, \ + VarType1, \ + varName1, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_TELEMETRY_EVENT_PARAM2_CV(EventId, PrivacyTag, EventTag, VarType1, varName1, VarType2, varName2) \ + DEFINE_TRACELOGGING_EVENT_PARAM2_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_TELEMETRY_EVENT_PARAM3_CV( \ + EventId, PrivacyTag, EventTag, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TRACELOGGING_EVENT_PARAM3_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_TELEMETRY_EVENT_PARAM4_CV( \ + EventId, PrivacyTag, EventTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TRACELOGGING_EVENT_PARAM4_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_TELEMETRY_EVENT_PARAM5_CV( \ + EventId, PrivacyTag, EventTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TRACELOGGING_EVENT_PARAM5_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_TELEMETRY_EVENT_PARAM6_CV( \ + EventId, PrivacyTag, EventTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TRACELOGGING_EVENT_PARAM6_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_TELEMETRY_EVENT_PARAM7_CV( \ + EventId, PrivacyTag, EventTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TRACELOGGING_EVENT_PARAM7_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_TELEMETRY_EVENT_PARAM8_CV( \ + EventId, \ + PrivacyTag, \ + EventTag, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8) \ + DEFINE_TRACELOGGING_EVENT_PARAM8_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) + +#define DEFINE_COMPLIANT_TELEMETRY_EVENT_UINT32(EventId, PrivacyTag, varName) \ + DEFINE_COMPLIANT_TELEMETRY_EVENT_PARAM1(EventId, PrivacyTag, UINT32, varName) +#define DEFINE_COMPLIANT_TELEMETRY_EVENT_BOOL(EventId, PrivacyTag, varName) \ + DEFINE_COMPLIANT_TELEMETRY_EVENT_PARAM1(EventId, PrivacyTag, bool, varName) +#define DEFINE_COMPLIANT_TELEMETRY_EVENT_STRING(EventId, PrivacyTag, varName) \ + DEFINE_COMPLIANT_TELEMETRY_EVENT_PARAM1(EventId, PrivacyTag, PCWSTR, varName) + +// [Optional] Simple Events +// Use these macros to define very simple measure events for a Provider. + +#ifndef DISABLE_NONCOMPLIANT_TELEMETRY + +#define DEFINE_MEASURES_EVENT(EventId) DEFINE_TRACELOGGING_EVENT(EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_MEASURES_EVENT_PARAM1(EventId, VarType1, varName1) \ + DEFINE_TRACELOGGING_EVENT_PARAM1(EventId, VarType1, varName1, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_MEASURES_EVENT_PARAM2(EventId, VarType1, varName1, VarType2, varName2) \ + DEFINE_TRACELOGGING_EVENT_PARAM2(EventId, VarType1, varName1, VarType2, varName2, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_MEASURES_EVENT_PARAM3(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TRACELOGGING_EVENT_PARAM3( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_MEASURES_EVENT_PARAM4(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TRACELOGGING_EVENT_PARAM4( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_MEASURES_EVENT_PARAM5( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TRACELOGGING_EVENT_PARAM5( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_MEASURES_EVENT_PARAM6( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TRACELOGGING_EVENT_PARAM6( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_MEASURES_EVENT_PARAM7( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TRACELOGGING_EVENT_PARAM7( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_MEASURES_EVENT_PARAM8( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, VarType8, varName8) \ + DEFINE_TRACELOGGING_EVENT_PARAM8( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) + +#define DEFINE_MEASURES_EVENT_CV(EventId) DEFINE_TRACELOGGING_EVENT_CV(EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_MEASURES_EVENT_PARAM1_CV(EventId, VarType1, varName1) \ + DEFINE_TRACELOGGING_EVENT_PARAM1_CV(EventId, VarType1, varName1, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_MEASURES_EVENT_PARAM2_CV(EventId, VarType1, varName1, VarType2, varName2) \ + DEFINE_TRACELOGGING_EVENT_PARAM2_CV(EventId, VarType1, varName1, VarType2, varName2, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_MEASURES_EVENT_PARAM3_CV(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TRACELOGGING_EVENT_PARAM3_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_MEASURES_EVENT_PARAM4_CV(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TRACELOGGING_EVENT_PARAM4_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_MEASURES_EVENT_PARAM5_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TRACELOGGING_EVENT_PARAM5_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_MEASURES_EVENT_PARAM6_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TRACELOGGING_EVENT_PARAM6_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_MEASURES_EVENT_PARAM7_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TRACELOGGING_EVENT_PARAM7_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_MEASURES_EVENT_PARAM8_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, VarType8, varName8) \ + DEFINE_TRACELOGGING_EVENT_PARAM8_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) + +#define DEFINE_MEASURES_EVENT_UINT32(EventId, varName) DEFINE_MEASURES_EVENT_PARAM1(EventId, UINT32, varName) +#define DEFINE_MEASURES_EVENT_BOOL(EventId, varName) DEFINE_MEASURES_EVENT_PARAM1(EventId, bool, varName) +#define DEFINE_MEASURES_EVENT_STRING(EventId, varName) DEFINE_MEASURES_EVENT_PARAM1(EventId, PCWSTR, varName) + +#endif + +#define DEFINE_COMPLIANT_MEASURES_EVENT(EventId, PrivacyTag) \ + DEFINE_TRACELOGGING_EVENT(EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_MEASURES_EVENT_PARAM1(EventId, PrivacyTag, VarType1, varName1) \ + DEFINE_TRACELOGGING_EVENT_PARAM1( \ + EventId, VarType1, varName1, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_MEASURES_EVENT_PARAM2(EventId, PrivacyTag, VarType1, varName1, VarType2, varName2) \ + DEFINE_TRACELOGGING_EVENT_PARAM2( \ + EventId, VarType1, varName1, VarType2, varName2, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_MEASURES_EVENT_PARAM3(EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TRACELOGGING_EVENT_PARAM3( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_MEASURES_EVENT_PARAM4( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TRACELOGGING_EVENT_PARAM4( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_MEASURES_EVENT_PARAM5( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TRACELOGGING_EVENT_PARAM5( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_MEASURES_EVENT_PARAM6( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TRACELOGGING_EVENT_PARAM6( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_MEASURES_EVENT_PARAM7( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TRACELOGGING_EVENT_PARAM7( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_MEASURES_EVENT_PARAM8( \ + EventId, \ + PrivacyTag, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8) \ + DEFINE_TRACELOGGING_EVENT_PARAM8( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_MEASURES_EVENT_PARAM9( \ + EventId, \ + PrivacyTag, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + VarType9, \ + varName9) \ + DEFINE_TRACELOGGING_EVENT_PARAM9( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + VarType9, \ + varName9, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_MEASURES_EVENT_PARAM10( \ + EventId, \ + PrivacyTag, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + VarType9, \ + varName9, \ + VarType10, \ + varName10) \ + DEFINE_TRACELOGGING_EVENT_PARAM10( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + VarType9, \ + varName9, \ + VarType10, \ + varName10, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag)) + +#define DEFINE_COMPLIANT_MEASURES_EVENT_CV(EventId, PrivacyTag) \ + DEFINE_TRACELOGGING_EVENT_CV(EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_MEASURES_EVENT_PARAM1_CV(EventId, PrivacyTag, VarType1, varName1) \ + DEFINE_TRACELOGGING_EVENT_PARAM1_CV( \ + EventId, VarType1, varName1, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_MEASURES_EVENT_PARAM2_CV(EventId, PrivacyTag, VarType1, varName1, VarType2, varName2) \ + DEFINE_TRACELOGGING_EVENT_PARAM2_CV( \ + EventId, VarType1, varName1, VarType2, varName2, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_MEASURES_EVENT_PARAM3_CV(EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TRACELOGGING_EVENT_PARAM3_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_MEASURES_EVENT_PARAM4_CV( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TRACELOGGING_EVENT_PARAM4_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_MEASURES_EVENT_PARAM5_CV( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TRACELOGGING_EVENT_PARAM5_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_MEASURES_EVENT_PARAM6_CV( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TRACELOGGING_EVENT_PARAM6_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_MEASURES_EVENT_PARAM7_CV( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TRACELOGGING_EVENT_PARAM7_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_MEASURES_EVENT_PARAM8_CV( \ + EventId, \ + PrivacyTag, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8) \ + DEFINE_TRACELOGGING_EVENT_PARAM8_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag)) + +#define DEFINE_COMPLIANT_EVENTTAGGED_MEASURES_EVENT_CV(EventId, PrivacyTag, EventTag) \ + DEFINE_TRACELOGGING_EVENT_CV( \ + EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PrivacyTag), TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_MEASURES_EVENT_PARAM1_CV(EventId, PrivacyTag, EventTag, VarType1, varName1) \ + DEFINE_TRACELOGGING_EVENT_PARAM1_CV( \ + EventId, VarType1, varName1, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PrivacyTag), TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_MEASURES_EVENT_PARAM2_CV(EventId, PrivacyTag, EventTag, VarType1, varName1, VarType2, varName2) \ + DEFINE_TRACELOGGING_EVENT_PARAM2_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_MEASURES_EVENT_PARAM3_CV( \ + EventId, PrivacyTag, EventTag, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TRACELOGGING_EVENT_PARAM3_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_MEASURES_EVENT_PARAM4_CV( \ + EventId, PrivacyTag, EventTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TRACELOGGING_EVENT_PARAM4_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_MEASURES_EVENT_PARAM5_CV( \ + EventId, PrivacyTag, EventTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TRACELOGGING_EVENT_PARAM5_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_MEASURES_EVENT_PARAM6_CV( \ + EventId, PrivacyTag, EventTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TRACELOGGING_EVENT_PARAM6_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_MEASURES_EVENT_PARAM7_CV( \ + EventId, PrivacyTag, EventTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TRACELOGGING_EVENT_PARAM7_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_MEASURES_EVENT_PARAM8_CV( \ + EventId, \ + PrivacyTag, \ + EventTag, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8) \ + DEFINE_TRACELOGGING_EVENT_PARAM8_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_MEASURES_EVENT_PARAM9_CV( \ + EventId, \ + PrivacyTag, \ + EventTag, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + VarType9, \ + varName9) \ + DEFINE_TRACELOGGING_EVENT_PARAM9_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + VarType9, \ + varName9, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) + +#define DEFINE_COMPLIANT_MEASURES_EVENT_UINT32(EventId, PrivacyTag, varName) \ + DEFINE_COMPLIANT_MEASURES_EVENT_PARAM1(EventId, PrivacyTag, UINT32, varName) +#define DEFINE_COMPLIANT_MEASURES_EVENT_BOOL(EventId, PrivacyTag, varName) \ + DEFINE_COMPLIANT_MEASURES_EVENT_PARAM1(EventId, PrivacyTag, bool, varName) +#define DEFINE_COMPLIANT_MEASURES_EVENT_STRING(EventId, PrivacyTag, varName) \ + DEFINE_COMPLIANT_MEASURES_EVENT_PARAM1(EventId, PrivacyTag, PCWSTR, varName) + +// [Optional] Simple Events +// Use these macros to define very simple critical data events for a Provider. + +#ifndef DISABLE_NONCOMPLIANT_TELEMETRY + +#define DEFINE_CRITICAL_DATA_EVENT(EventId) \ + DEFINE_TRACELOGGING_EVENT(EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_CRITICAL_DATA_EVENT_PARAM1(EventId, VarType1, varName1) \ + DEFINE_TRACELOGGING_EVENT_PARAM1(EventId, VarType1, varName1, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_CRITICAL_DATA_EVENT_PARAM2(EventId, VarType1, varName1, VarType2, varName2) \ + DEFINE_TRACELOGGING_EVENT_PARAM2(EventId, VarType1, varName1, VarType2, varName2, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_CRITICAL_DATA_EVENT_PARAM3(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TRACELOGGING_EVENT_PARAM3( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_CRITICAL_DATA_EVENT_PARAM4(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TRACELOGGING_EVENT_PARAM4( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_CRITICAL_DATA_EVENT_PARAM5( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TRACELOGGING_EVENT_PARAM5( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_CRITICAL_DATA_EVENT_PARAM6( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TRACELOGGING_EVENT_PARAM6( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_CRITICAL_DATA_EVENT_PARAM7( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TRACELOGGING_EVENT_PARAM7( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_CRITICAL_DATA_EVENT_PARAM8( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, VarType8, varName8) \ + DEFINE_TRACELOGGING_EVENT_PARAM8( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) + +#define DEFINE_CRITICAL_DATA_EVENT_CV(EventId) \ + DEFINE_TRACELOGGING_EVENT_CV(EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_CRITICAL_DATA_EVENT_PARAM1_CV(EventId, VarType1, varName1) \ + DEFINE_TRACELOGGING_EVENT_PARAM1_CV(EventId, VarType1, varName1, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_CRITICAL_DATA_EVENT_PARAM2_CV(EventId, VarType1, varName1, VarType2, varName2) \ + DEFINE_TRACELOGGING_EVENT_PARAM2_CV( \ + EventId, VarType1, varName1, VarType2, varName2, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_CRITICAL_DATA_EVENT_PARAM3_CV(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TRACELOGGING_EVENT_PARAM3_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_CRITICAL_DATA_EVENT_PARAM4_CV(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TRACELOGGING_EVENT_PARAM4_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_CRITICAL_DATA_EVENT_PARAM5_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TRACELOGGING_EVENT_PARAM5_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_CRITICAL_DATA_EVENT_PARAM6_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TRACELOGGING_EVENT_PARAM6_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_CRITICAL_DATA_EVENT_PARAM7_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TRACELOGGING_EVENT_PARAM7_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_CRITICAL_DATA_EVENT_PARAM8_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, VarType8, varName8) \ + DEFINE_TRACELOGGING_EVENT_PARAM8_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) + +#define DEFINE_CRITICAL_DATA_EVENT_UINT32(EventId, varName) DEFINE_CRITICAL_DATA_EVENT_PARAM1(EventId, UINT32, varName) +#define DEFINE_CRITICAL_DATA_EVENT_BOOL(EventId, varName) DEFINE_CRITICAL_DATA_EVENT_PARAM1(EventId, bool, varName) +#define DEFINE_CRITICAL_DATA_EVENT_STRING(EventId, varName) DEFINE_CRITICAL_DATA_EVENT_PARAM1(EventId, PCWSTR, varName) + +#endif + +#define DEFINE_COMPLIANT_CRITICAL_DATA_EVENT(EventId, PrivacyTag) \ + DEFINE_TRACELOGGING_EVENT(EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_PARAM1(EventId, PrivacyTag, VarType1, varName1) \ + DEFINE_TRACELOGGING_EVENT_PARAM1( \ + EventId, VarType1, varName1, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_PARAM2(EventId, PrivacyTag, VarType1, varName1, VarType2, varName2) \ + DEFINE_TRACELOGGING_EVENT_PARAM2( \ + EventId, VarType1, varName1, VarType2, varName2, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_PARAM3(EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TRACELOGGING_EVENT_PARAM3( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_PARAM4( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TRACELOGGING_EVENT_PARAM4( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_PARAM5( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TRACELOGGING_EVENT_PARAM5( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_PARAM6( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TRACELOGGING_EVENT_PARAM6( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_PARAM7( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TRACELOGGING_EVENT_PARAM7( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_PARAM8( \ + EventId, \ + PrivacyTag, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8) \ + DEFINE_TRACELOGGING_EVENT_PARAM8( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag)) + +#define DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_CV(EventId, PrivacyTag) \ + DEFINE_TRACELOGGING_EVENT_CV(EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_PARAM1_CV(EventId, PrivacyTag, VarType1, varName1) \ + DEFINE_TRACELOGGING_EVENT_PARAM1_CV( \ + EventId, VarType1, varName1, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_PARAM2_CV(EventId, PrivacyTag, VarType1, varName1, VarType2, varName2) \ + DEFINE_TRACELOGGING_EVENT_PARAM2_CV( \ + EventId, VarType1, varName1, VarType2, varName2, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_PARAM3_CV(EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TRACELOGGING_EVENT_PARAM3_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_PARAM4_CV( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TRACELOGGING_EVENT_PARAM4_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_PARAM5_CV( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TRACELOGGING_EVENT_PARAM5_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_PARAM6_CV( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TRACELOGGING_EVENT_PARAM6_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_PARAM7_CV( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TRACELOGGING_EVENT_PARAM7_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_PARAM8_CV( \ + EventId, \ + PrivacyTag, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8) \ + DEFINE_TRACELOGGING_EVENT_PARAM8_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag)) + +#define DEFINE_COMPLIANT_EVENTTAGGED_CRITICAL_DATA_EVENT_CV(EventId, PrivacyTag, EventTag) \ + DEFINE_TRACELOGGING_EVENT_CV( \ + EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), TelemetryPrivacyDataTag(PrivacyTag), TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_CRITICAL_DATA_EVENT_PARAM1_CV(EventId, PrivacyTag, EventTag, VarType1, varName1) \ + DEFINE_TRACELOGGING_EVENT_PARAM1_CV( \ + EventId, \ + VarType1, \ + varName1, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_CRITICAL_DATA_EVENT_PARAM2_CV(EventId, PrivacyTag, EventTag, VarType1, varName1, VarType2, varName2) \ + DEFINE_TRACELOGGING_EVENT_PARAM2_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_CRITICAL_DATA_EVENT_PARAM3_CV( \ + EventId, PrivacyTag, EventTag, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TRACELOGGING_EVENT_PARAM3_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_CRITICAL_DATA_EVENT_PARAM4_CV( \ + EventId, PrivacyTag, EventTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TRACELOGGING_EVENT_PARAM4_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_CRITICAL_DATA_EVENT_PARAM5_CV( \ + EventId, PrivacyTag, EventTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TRACELOGGING_EVENT_PARAM5_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_CRITICAL_DATA_EVENT_PARAM6_CV( \ + EventId, PrivacyTag, EventTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TRACELOGGING_EVENT_PARAM6_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_CRITICAL_DATA_EVENT_PARAM7_CV( \ + EventId, PrivacyTag, EventTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TRACELOGGING_EVENT_PARAM7_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_CRITICAL_DATA_EVENT_PARAM8_CV( \ + EventId, \ + PrivacyTag, \ + EventTag, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8) \ + DEFINE_TRACELOGGING_EVENT_PARAM8_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) +#define DEFINE_COMPLIANT_EVENTTAGGED_CRITICAL_DATA_EVENT_PARAM9_CV( \ + EventId, \ + PrivacyTag, \ + EventTag, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + VarType9, \ + varName9) \ + DEFINE_TRACELOGGING_EVENT_PARAM9_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + VarType9, \ + varName9, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag), \ + TraceLoggingEventTag(EventTag)) + +#define DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_UINT32(EventId, PrivacyTag, varName) \ + DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_PARAM1(EventId, PrivacyTag, UINT32, varName) +#define DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_BOOL(EventId, PrivacyTag, varName) \ + DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_PARAM1(EventId, PrivacyTag, bool, varName) +#define DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_STRING(EventId, PrivacyTag, varName) \ + DEFINE_COMPLIANT_CRITICAL_DATA_EVENT_PARAM1(EventId, PrivacyTag, PCWSTR, varName) + +// Custom Activities +// For these you pair the appropriate BEGIN and END macros to define your activity. Within the pair +// you can use the (TODO: LIST MACRO NAMES) macros to add behavior. + +// [optional] params are: Options, Keyword, Level, PrivacyTag +#define BEGIN_CUSTOM_ACTIVITY_CLASS(ActivityClassName, ...) \ + __BEGIN_TRACELOGGING_ACTIVITY_CLASS(ActivityClassName, ##__VA_ARGS__) \ + __IMPLEMENT_ACTIVITY_CLASS(ActivityClassName) + +// [optional] param is: Level, PrivacyTag +#ifndef DISABLE_NONCOMPLIANT_TELEMETRY +#define BEGIN_TRACELOGGING_ACTIVITY_CLASS(ActivityClassName) \ + __BEGIN_TRACELOGGING_ACTIVITY_CLASS(ActivityClassName) \ + __IMPLEMENT_ACTIVITY_CLASS(ActivityClassName) +#define BEGIN_TRACELOGGING_ACTIVITY_CLASS_WITH_LEVEL(ActivityClassName, Level) \ + __BEGIN_TRACELOGGING_ACTIVITY_CLASS(ActivityClassName, wil::ActivityOptions::None, 0, Level) \ + __IMPLEMENT_ACTIVITY_CLASS(ActivityClassName) +#endif +#define BEGIN_COMPLIANT_TRACELOGGING_ACTIVITY_CLASS(ActivityClassName, PrivacyTag) \ + __BEGIN_TRACELOGGING_ACTIVITY_CLASS(ActivityClassName, wil::ActivityOptions::None, 0, WINEVENT_LEVEL_VERBOSE, PrivacyTag) \ + __IMPLEMENT_ACTIVITY_CLASS(ActivityClassName) +#define BEGIN_COMPLIANT_TRACELOGGING_ACTIVITY_CLASS_WITH_LEVEL(ActivityClassName, PrivacyTag, Level) \ + __BEGIN_TRACELOGGING_ACTIVITY_CLASS(ActivityClassName, wil::ActivityOptions::None, 0, Level, PrivacyTag) \ + __IMPLEMENT_ACTIVITY_CLASS(ActivityClassName) + +// [optional] param is: Level +#ifndef DISABLE_NONCOMPLIANT_TELEMETRY +#define BEGIN_CALLCONTEXT_ACTIVITY_CLASS(ActivityClassName) \ + __BEGIN_TRACELOGGING_ACTIVITY_CLASS(ActivityClassName, wil::ActivityOptions::TelemetryOnFailure) \ + __IMPLEMENT_ACTIVITY_CLASS(ActivityClassName) +#define BEGIN_CALLCONTEXT_ACTIVITY_CLASS_WITH_LEVEL(ActivityClassName, Level) \ + __BEGIN_TRACELOGGING_ACTIVITY_CLASS(ActivityClassName, wil::ActivityOptions::TelemetryOnFailure, 0, Level) \ + __IMPLEMENT_ACTIVITY_CLASS(ActivityClassName) +#endif +#define BEGIN_COMPLIANT_CALLCONTEXT_ACTIVITY_CLASS(ActivityClassName, PrivacyTag) \ + __BEGIN_TRACELOGGING_ACTIVITY_CLASS(ActivityClassName, wil::ActivityOptions::TelemetryOnFailure, 0, WINEVENT_LEVEL_VERBOSE, PrivacyTag) \ + __IMPLEMENT_ACTIVITY_CLASS(ActivityClassName) +#define BEGIN_COMPLIANT_CALLCONTEXT_ACTIVITY_CLASS_WITH_LEVEL(ActivityClassName, PrivacyTag, Level) \ + __BEGIN_TRACELOGGING_ACTIVITY_CLASS(ActivityClassName, wil::ActivityOptions::TelemetryOnFailure, 0, Level, PrivacyTag) \ + __IMPLEMENT_ACTIVITY_CLASS(ActivityClassName) + +// [optional] param is: Level +#ifndef DISABLE_NONCOMPLIANT_TELEMETRY +#define BEGIN_TELEMETRY_ACTIVITY_CLASS(ActivityClassName) \ + __BEGIN_TRACELOGGING_ACTIVITY_CLASS(ActivityClassName, wil::ActivityOptions::TelemetryOnFailure, MICROSOFT_KEYWORD_TELEMETRY) \ + __IMPLEMENT_ACTIVITY_CLASS(ActivityClassName) +#define BEGIN_TELEMETRY_ACTIVITY_CLASS_WITH_LEVEL(ActivityClassName, Level) \ + __BEGIN_TRACELOGGING_ACTIVITY_CLASS(ActivityClassName, wil::ActivityOptions::TelemetryOnFailure, MICROSOFT_KEYWORD_TELEMETRY, Level) \ + __IMPLEMENT_ACTIVITY_CLASS(ActivityClassName) +#endif +#define BEGIN_COMPLIANT_TELEMETRY_ACTIVITY_CLASS(ActivityClassName, PrivacyTag) \ + __BEGIN_TRACELOGGING_ACTIVITY_CLASS( \ + ActivityClassName, wil::ActivityOptions::TelemetryOnFailure, MICROSOFT_KEYWORD_TELEMETRY, WINEVENT_LEVEL_VERBOSE, PrivacyTag) \ + __IMPLEMENT_ACTIVITY_CLASS(ActivityClassName) +#define BEGIN_COMPLIANT_TELEMETRY_ACTIVITY_CLASS_WITH_LEVEL(ActivityClassName, PrivacyTag, Level) \ + __BEGIN_TRACELOGGING_ACTIVITY_CLASS( \ + ActivityClassName, wil::ActivityOptions::TelemetryOnFailure, MICROSOFT_KEYWORD_TELEMETRY, Level, PrivacyTag) \ + __IMPLEMENT_ACTIVITY_CLASS(ActivityClassName) + +// [optional] param is: Level +#ifndef DISABLE_NONCOMPLIANT_TELEMETRY +#define BEGIN_MEASURES_ACTIVITY_CLASS(ActivityClassName) \ + __BEGIN_TRACELOGGING_ACTIVITY_CLASS(ActivityClassName, wil::ActivityOptions::TelemetryOnFailure, MICROSOFT_KEYWORD_MEASURES) \ + __IMPLEMENT_ACTIVITY_CLASS(ActivityClassName) +#define BEGIN_MEASURES_ACTIVITY_CLASS_WITH_LEVEL(ActivityClassName, Level) \ + __BEGIN_TRACELOGGING_ACTIVITY_CLASS(ActivityClassName, wil::ActivityOptions::TelemetryOnFailure, MICROSOFT_KEYWORD_MEASURES, Level) \ + __IMPLEMENT_ACTIVITY_CLASS(ActivityClassName) +#endif +#define BEGIN_COMPLIANT_MEASURES_ACTIVITY_CLASS(ActivityClassName, PrivacyTag) \ + __BEGIN_TRACELOGGING_ACTIVITY_CLASS( \ + ActivityClassName, wil::ActivityOptions::TelemetryOnFailure, MICROSOFT_KEYWORD_MEASURES, WINEVENT_LEVEL_VERBOSE, PrivacyTag) \ + __IMPLEMENT_ACTIVITY_CLASS(ActivityClassName) +#define BEGIN_COMPLIANT_MEASURES_ACTIVITY_CLASS_WITH_LEVEL(ActivityClassName, PrivacyTag, Level) \ + __BEGIN_TRACELOGGING_ACTIVITY_CLASS( \ + ActivityClassName, wil::ActivityOptions::TelemetryOnFailure, MICROSOFT_KEYWORD_MEASURES, Level, PrivacyTag) \ + __IMPLEMENT_ACTIVITY_CLASS(ActivityClassName) + +// [optional] param is: Level +#ifndef DISABLE_NONCOMPLIANT_TELEMETRY +#define BEGIN_CRITICAL_DATA_ACTIVITY_CLASS(ActivityClassName) \ + __BEGIN_TRACELOGGING_ACTIVITY_CLASS(ActivityClassName, wil::ActivityOptions::TelemetryOnFailure, MICROSOFT_KEYWORD_CRITICAL_DATA) \ + __IMPLEMENT_ACTIVITY_CLASS(ActivityClassName) +#define BEGIN_CRITICAL_DATA_ACTIVITY_CLASS_WITH_LEVEL(ActivityClassName, Level) \ + __BEGIN_TRACELOGGING_ACTIVITY_CLASS(ActivityClassName, wil::ActivityOptions::TelemetryOnFailure, MICROSOFT_KEYWORD_CRITICAL_DATA, Level) \ + __IMPLEMENT_ACTIVITY_CLASS(ActivityClassName) +#endif +#define BEGIN_COMPLIANT_CRITICAL_DATA_ACTIVITY_CLASS(ActivityClassName, PrivacyTag) \ + __BEGIN_TRACELOGGING_ACTIVITY_CLASS( \ + ActivityClassName, wil::ActivityOptions::TelemetryOnFailure, MICROSOFT_KEYWORD_CRITICAL_DATA, WINEVENT_LEVEL_VERBOSE, PrivacyTag) \ + __IMPLEMENT_ACTIVITY_CLASS(ActivityClassName) +#define BEGIN_COMPLIANT_CRITICAL_DATA_ACTIVITY_CLASS_WITH_LEVEL(ActivityClassName, PrivacyTag, Level) \ + __BEGIN_TRACELOGGING_ACTIVITY_CLASS( \ + ActivityClassName, wil::ActivityOptions::TelemetryOnFailure, MICROSOFT_KEYWORD_CRITICAL_DATA, Level, PrivacyTag) \ + __IMPLEMENT_ACTIVITY_CLASS(ActivityClassName) + +// Use to end ALL activity class definitions +#define END_ACTIVITY_CLASS() __END_TRACELOGGING_ACTIVITY_CLASS() + +// Simple Activities +// For these you just use the appropriate macro to define the KIND of activity you want and specify +// the name (for tracelogging you can give other options) + +// [optional] params are: Options, Keyword, Level +#ifndef DISABLE_NONCOMPLIANT_TELEMETRY +#define DEFINE_CUSTOM_ACTIVITY(ActivityClassName, ...) \ + BEGIN_CUSTOM_ACTIVITY_CLASS(ActivityClassName, ##__VA_ARGS__) \ + END_ACTIVITY_CLASS() + +#define DEFINE_TRACELOGGING_ACTIVITY(ActivityClassName) \ + BEGIN_TRACELOGGING_ACTIVITY_CLASS(ActivityClassName) \ + END_ACTIVITY_CLASS() +#define DEFINE_TRACELOGGING_ACTIVITY_WITH_LEVEL(ActivityClassName, Level) \ + BEGIN_TRACELOGGING_ACTIVITY_CLASS_WITH_LEVEL(ActivityClassName, Level) \ + END_ACTIVITY_CLASS() + +#define DEFINE_CALLCONTEXT_ACTIVITY(ActivityClassName) \ + BEGIN_CALLCONTEXT_ACTIVITY_CLASS(ActivityClassName) \ + END_ACTIVITY_CLASS() +#define DEFINE_CALLCONTEXT_ACTIVITY_WITH_LEVEL(ActivityClassName, Level) \ + BEGIN_CALLCONTEXT_ACTIVITY_CLASS_WITH_LEVEL(ActivityClassName, Level) \ + END_ACTIVITY_CLASS() + +#define DEFINE_TELEMETRY_ACTIVITY(ActivityClassName) \ + BEGIN_TELEMETRY_ACTIVITY_CLASS(ActivityClassName) \ + END_ACTIVITY_CLASS() +#define DEFINE_TELEMETRY_ACTIVITY_WITH_LEVEL(ActivityClassName, Level) \ + BEGIN_TELEMETRY_ACTIVITY_CLASS_WITH_LEVEL(ActivityClassName, Level) \ + END_ACTIVITY_CLASS() +#endif + +#define DEFINE_COMPLIANT_TELEMETRY_ACTIVITY(ActivityClassName, PrivacyTag) \ + BEGIN_COMPLIANT_TELEMETRY_ACTIVITY_CLASS(ActivityClassName, PrivacyTag) \ + END_ACTIVITY_CLASS() +#define DEFINE_COMPLIANT_TELEMETRY_ACTIVITY_WITH_LEVEL(ActivityClassName, PrivacyTag, Level) \ + BEGIN_COMPLIANT_TELEMETRY_ACTIVITY_CLASS_WITH_LEVEL(ActivityClassName, PrivacyTag, Level) \ + END_ACTIVITY_CLASS() + +#ifndef DISABLE_NONCOMPLIANT_TELEMETRY +#define DEFINE_MEASURES_ACTIVITY(ActivityClassName) \ + BEGIN_MEASURES_ACTIVITY_CLASS(ActivityClassName) \ + END_ACTIVITY_CLASS() +#define DEFINE_MEASURES_ACTIVITY_WITH_LEVEL(ActivityClassName, Level) \ + BEGIN_MEASURES_ACTIVITY_CLASS_WITH_LEVEL(ActivityClassName, Level) \ + END_ACTIVITY_CLASS() +#endif + +#define DEFINE_COMPLIANT_MEASURES_ACTIVITY(ActivityClassName, PrivacyTag) \ + BEGIN_COMPLIANT_MEASURES_ACTIVITY_CLASS(ActivityClassName, PrivacyTag) \ + END_ACTIVITY_CLASS() +#define DEFINE_COMPLIANT_MEASURES_ACTIVITY_WITH_LEVEL(ActivityClassName, PrivacyTag, Level) \ + BEGIN_COMPLIANT_MEASURES_ACTIVITY_CLASS_WITH_LEVEL(ActivityClassName, PrivacyTag, Level) \ + END_ACTIVITY_CLASS() + +#ifndef DISABLE_NONCOMPLIANT_TELEMETRY +#define DEFINE_CRITICAL_DATA_ACTIVITY(ActivityClassName) \ + BEGIN_CRITICAL_DATA_ACTIVITY_CLASS(ActivityClassName) \ + END_ACTIVITY_CLASS() +#define DEFINE_CRITICAL_DATA_ACTIVITY_WITH_LEVEL(ActivityClassName, Level) \ + BEGIN_CRITICAL_DATA_ACTIVITY_CLASS_WITH_LEVEL(ActivityClassName, Level) \ + END_ACTIVITY_CLASS() +#endif + +#define DEFINE_COMPLIANT_CRITICAL_DATA_ACTIVITY(ActivityClassName, PrivacyTag) \ + BEGIN_COMPLIANT_CRITICAL_DATA_ACTIVITY_CLASS(ActivityClassName, PrivacyTag) \ + END_ACTIVITY_CLASS() +#define DEFINE_COMPLIANT_CRITICAL_DATA_ACTIVITY_WITH_LEVEL(ActivityClassName, PrivacyTag, Level) \ + BEGIN_COMPLIANT_CRITICAL_DATA_ACTIVITY_CLASS_WITH_LEVEL(ActivityClassName, PrivacyTag, Level) \ + END_ACTIVITY_CLASS() + +// [Optional] Custom Start or Stop Events for Activities +// Use these macros to define custom start or custom stop methods for an activity. Any activity can +// have multiple start or stop methods. To add custom start or stop events, define a StartActivity instance +// method or a Stop instance method within the BEGIN/END pair of a custom activity. Within that function, use +// TraceLoggingClassWriteStart or TraceLoggingClassWriteStop. + +// Params: (EventId, ...) +#define TraceLoggingClassWriteStart __WRITE_ACTIVITY_START +#define TraceLoggingClassWriteStop __WRITE_ACTIVITY_STOP + +// [Optional] Custom Tagged Events for Activities +// Use these macros to define a Custom Tagged Event for a Custom Activity. Use the +// TraceLoggingClassWriteTagged or TraceLoggingClassWriteTaggedTelemetry macros from within a custom event +// to write the event. + +#define TraceLoggingClassWriteTagged(EventId, ...) __WI_TraceLoggingWriteTagged(*this, #EventId, ##__VA_ARGS__) + +#define TraceLoggingClassWriteTaggedTelemetry(EventId, ...) \ + __WI_TraceLoggingWriteTagged(*this, #EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), ##__VA_ARGS__) + +#define TraceLoggingClassWriteTaggedMeasure(EventId, ...) \ + __WI_TraceLoggingWriteTagged(*this, #EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), ##__VA_ARGS__) + +#define TraceLoggingClassWriteTaggedCriticalData(EventId, ...) \ + __WI_TraceLoggingWriteTagged(*this, #EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), ##__VA_ARGS__) + +// [Optional] Simple Tagged Events for Activities +// Use these methods to define very simple tagged events for a Custom Activity. + +#ifndef DISABLE_NONCOMPLIANT_TELEMETRY + +#define DEFINE_TAGGED_TELEMETRY_EVENT(EventId) \ + DEFINE_TAGGED_TRACELOGGING_EVENT(EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TAGGED_TELEMETRY_EVENT_PARAM1(EventId, VarType1, varName1) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM1(EventId, VarType1, varName1, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TAGGED_TELEMETRY_EVENT_PARAM2(EventId, VarType1, varName1, VarType2, varName2) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM2( \ + EventId, VarType1, varName1, VarType2, varName2, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TAGGED_TELEMETRY_EVENT_PARAM3(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM3( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TAGGED_TELEMETRY_EVENT_PARAM4(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM4( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TAGGED_TELEMETRY_EVENT_PARAM5( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM5( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TAGGED_TELEMETRY_EVENT_PARAM6( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM6( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TAGGED_TELEMETRY_EVENT_PARAM7( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM7( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TAGGED_TELEMETRY_EVENT_PARAM8( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, VarType8, varName8) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM8( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) + +#define DEFINE_TAGGED_TELEMETRY_EVENT_CV(EventId) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_CV(EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TAGGED_TELEMETRY_EVENT_PARAM1_CV(EventId, VarType1, varName1) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM1_CV(EventId, VarType1, varName1, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TAGGED_TELEMETRY_EVENT_PARAM2_CV(EventId, VarType1, varName1, VarType2, varName2) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM2_CV( \ + EventId, VarType1, varName1, VarType2, varName2, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TAGGED_TELEMETRY_EVENT_PARAM3_CV(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM3_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TAGGED_TELEMETRY_EVENT_PARAM4_CV(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM4_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TAGGED_TELEMETRY_EVENT_PARAM5_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM5_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TAGGED_TELEMETRY_EVENT_PARAM6_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM6_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TAGGED_TELEMETRY_EVENT_PARAM7_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM7_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) +#define DEFINE_TAGGED_TELEMETRY_EVENT_PARAM8_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, VarType8, varName8) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM8_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY)) + +#define DEFINE_TAGGED_TELEMETRY_EVENT_UINT32(EventId, varName) DEFINE_TAGGED_TELEMETRY_EVENT_PARAM1(EventId, UINT32, varName) +#define DEFINE_TAGGED_TELEMETRY_EVENT_BOOL(EventId, varName) DEFINE_TAGGED_TELEMETRY_EVENT_PARAM1(EventId, bool, varName) +#define DEFINE_TAGGED_TELEMETRY_EVENT_STRING(EventId, varName) DEFINE_TAGGED_TELEMETRY_EVENT_PARAM1(EventId, PCWSTR, varName) + +#endif + +#define DEFINE_TAGGED_COMPLIANT_TELEMETRY_EVENT(EventId, PrivacyTag) \ + DEFINE_TAGGED_TRACELOGGING_EVENT(EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_TELEMETRY_EVENT_PARAM1(EventId, PrivacyTag, VarType1, varName1) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM1( \ + EventId, VarType1, varName1, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_TELEMETRY_EVENT_PARAM2(EventId, PrivacyTag, VarType1, varName1, VarType2, varName2) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM2( \ + EventId, VarType1, varName1, VarType2, varName2, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_TELEMETRY_EVENT_PARAM3(EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM3( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_TELEMETRY_EVENT_PARAM4( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM4( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_TELEMETRY_EVENT_PARAM5( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM5( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_TELEMETRY_EVENT_PARAM6( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM6( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_TELEMETRY_EVENT_PARAM7( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM7( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_TELEMETRY_EVENT_PARAM8( \ + EventId, \ + PrivacyTag, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM8( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), \ + TelemetryPrivacyDataTag(PrivacyTag)) + +#define DEFINE_TAGGED_COMPLIANT_TELEMETRY_EVENT_UINT32(EventId, PrivacyTag, varName) \ + DEFINE_TAGGED_COMPLIANT_TELEMETRY_EVENT_PARAM1(EventId, PrivacyTag, UINT32, varName) +#define DEFINE_TAGGED_COMPLIANT_TELEMETRY_EVENT_BOOL(EventId, PrivacyTag, varName) \ + DEFINE_TAGGED_COMPLIANT_TELEMETRY_EVENT_PARAM1(EventId, PrivacyTag, bool, varName) +#define DEFINE_TAGGED_COMPLIANT_TELEMETRY_EVENT_STRING(EventId, PrivacyTag, varName) \ + DEFINE_TAGGED_COMPLIANT_TELEMETRY_EVENT_PARAM1(EventId, PrivacyTag, PCWSTR, varName) + +// [Optional] Simple Tagged Events for Activities +// Use these methods to define very simple tagged measures events for a Custom Activity. + +#ifndef DISABLE_NONCOMPLIANT_TELEMETRY + +#define DEFINE_TAGGED_MEASURES_EVENT(EventId) \ + DEFINE_TAGGED_TRACELOGGING_EVENT(EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_TAGGED_MEASURES_EVENT_PARAM1(EventId, VarType1, varName1) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM1(EventId, VarType1, varName1, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_TAGGED_MEASURES_EVENT_PARAM2(EventId, VarType1, varName1, VarType2, varName2) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM2(EventId, VarType1, varName1, VarType2, varName2, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_TAGGED_MEASURES_EVENT_PARAM3(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM3( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_TAGGED_MEASURES_EVENT_PARAM4(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM4( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_TAGGED_MEASURES_EVENT_PARAM5( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM5( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_TAGGED_MEASURES_EVENT_PARAM6( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM6( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_TAGGED_MEASURES_EVENT_PARAM7( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM7( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_TAGGED_MEASURES_EVENT_PARAM8( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, VarType8, varName8) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM8( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) + +#define DEFINE_TAGGED_MEASURES_EVENT_CV(EventId) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_CV(EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_TAGGED_MEASURES_EVENT_PARAM1_CV(EventId, VarType1, varName1) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM1_CV(EventId, VarType1, varName1, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_TAGGED_MEASURES_EVENT_PARAM2_CV(EventId, VarType1, varName1, VarType2, varName2) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM2_CV( \ + EventId, VarType1, varName1, VarType2, varName2, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_TAGGED_MEASURES_EVENT_PARAM3_CV(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM3_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_TAGGED_MEASURES_EVENT_PARAM4_CV(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM4_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_TAGGED_MEASURES_EVENT_PARAM5_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM5_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_TAGGED_MEASURES_EVENT_PARAM6_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM6_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_TAGGED_MEASURES_EVENT_PARAM7_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM7_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) +#define DEFINE_TAGGED_MEASURES_EVENT_PARAM8_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, VarType8, varName8) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM8_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)) + +#define DEFINE_TAGGED_MEASURES_EVENT_UINT32(EventId, varName) DEFINE_TAGGED_MEASURES_EVENT_PARAM1(EventId, UINT32, varName) +#define DEFINE_TAGGED_MEASURES_EVENT_BOOL(EventId, varName) DEFINE_TAGGED_MEASURES_EVENT_PARAM1(EventId, bool, varName) +#define DEFINE_TAGGED_MEASURES_EVENT_STRING(EventId, varName) DEFINE_TAGGED_MEASURES_EVENT_PARAM1(EventId, PCWSTR, varName) + +#endif + +#define DEFINE_TAGGED_COMPLIANT_MEASURES_EVENT(EventId, PrivacyTag) \ + DEFINE_TAGGED_TRACELOGGING_EVENT(EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_MEASURES_EVENT_PARAM1(EventId, PrivacyTag, VarType1, varName1) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM1( \ + EventId, VarType1, varName1, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_MEASURES_EVENT_PARAM2(EventId, PrivacyTag, VarType1, varName1, VarType2, varName2) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM2( \ + EventId, VarType1, varName1, VarType2, varName2, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_MEASURES_EVENT_PARAM3(EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM3( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_MEASURES_EVENT_PARAM4( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM4( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_MEASURES_EVENT_PARAM5( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM5( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_MEASURES_EVENT_PARAM6( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM6( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_MEASURES_EVENT_PARAM7( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM7( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_MEASURES_EVENT_PARAM8( \ + EventId, \ + PrivacyTag, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM8( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), \ + TelemetryPrivacyDataTag(PrivacyTag)) + +#define DEFINE_TAGGED_COMPLIANT_MEASURES_EVENT_UINT32(EventId, PrivacyTag, varName) \ + DEFINE_TAGGED_COMPLIANT_MEASURES_EVENT_PARAM1(EventId, PrivacyTag, UINT32, varName) +#define DEFINE_TAGGED_COMPLIANT_MEASURES_EVENT_BOOL(EventId, PrivacyTag, varName) \ + DEFINE_TAGGED_COMPLIANT_MEASURES_EVENT_PARAM1(EventId, PrivacyTag, bool, varName) +#define DEFINE_TAGGED_COMPLIANT_MEASURES_EVENT_STRING(EventId, PrivacyTag, varName) \ + DEFINE_TAGGED_COMPLIANT_MEASURES_EVENT_PARAM1(EventId, PrivacyTag, PCWSTR, varName) + +// [Optional] Simple Tagged Events for Activities +// Use these methods to define very simple tagged CRITICAL_DATA events for a Custom Activity. + +#ifndef DISABLE_NONCOMPLIANT_TELEMETRY + +#define DEFINE_TAGGED_CRITICAL_DATA_EVENT(EventId) \ + DEFINE_TAGGED_TRACELOGGING_EVENT(EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_TAGGED_CRITICAL_DATA_EVENT_PARAM1(EventId, VarType1, varName1) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM1(EventId, VarType1, varName1, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_TAGGED_CRITICAL_DATA_EVENT_PARAM2(EventId, VarType1, varName1, VarType2, varName2) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM2( \ + EventId, VarType1, varName1, VarType2, varName2, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_TAGGED_CRITICAL_DATA_EVENT_PARAM3(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM3( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_TAGGED_CRITICAL_DATA_EVENT_PARAM4(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM4( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_TAGGED_CRITICAL_DATA_EVENT_PARAM5( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM5( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_TAGGED_CRITICAL_DATA_EVENT_PARAM6( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM6( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_TAGGED_CRITICAL_DATA_EVENT_PARAM7( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM7( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_TAGGED_CRITICAL_DATA_EVENT_PARAM8( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, VarType8, varName8) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM8( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_TAGGED_CRITICAL_DATA_EVENT_PARAM9( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + VarType9, \ + varName9) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM9( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + VarType9, \ + varName9, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) + +#define DEFINE_TAGGED_CRITICAL_DATA_EVENT_CV(EventId) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_CV(EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_TAGGED_CRITICAL_DATA_EVENT_PARAM1_CV(EventId, VarType1, varName1) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM1_CV(EventId, VarType1, varName1, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_TAGGED_CRITICAL_DATA_EVENT_PARAM2_CV(EventId, VarType1, varName1, VarType2, varName2) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM2_CV( \ + EventId, VarType1, varName1, VarType2, varName2, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_TAGGED_CRITICAL_DATA_EVENT_PARAM3_CV(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM3_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_TAGGED_CRITICAL_DATA_EVENT_PARAM4_CV(EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM4_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_TAGGED_CRITICAL_DATA_EVENT_PARAM5_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM5_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_TAGGED_CRITICAL_DATA_EVENT_PARAM6_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM6_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_TAGGED_CRITICAL_DATA_EVENT_PARAM7_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM7_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) +#define DEFINE_TAGGED_CRITICAL_DATA_EVENT_PARAM8_CV( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7, VarType8, varName8) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM8_CV( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)) + +#define DEFINE_TAGGED_CRITICAL_DATA_EVENT_UINT32(EventId, varName) \ + DEFINE_TAGGED_CRITICAL_DATA_EVENT_PARAM1(EventId, UINT32, varName) +#define DEFINE_TAGGED_CRITICAL_DATA_EVENT_BOOL(EventId, varName) DEFINE_TAGGED_CRITICAL_DATA_EVENT_PARAM1(EventId, bool, varName) +#define DEFINE_TAGGED_CRITICAL_DATA_EVENT_STRING(EventId, varName) \ + DEFINE_TAGGED_CRITICAL_DATA_EVENT_PARAM1(EventId, PCWSTR, varName) + +#endif + +#define DEFINE_TAGGED_COMPLIANT_CRITICAL_DATA_EVENT(EventId, PrivacyTag) \ + DEFINE_TAGGED_TRACELOGGING_EVENT(EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_CRITICAL_DATA_EVENT_PARAM1(EventId, PrivacyTag, VarType1, varName1) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM1( \ + EventId, VarType1, varName1, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_CRITICAL_DATA_EVENT_PARAM2(EventId, PrivacyTag, VarType1, varName1, VarType2, varName2) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM2( \ + EventId, VarType1, varName1, VarType2, varName2, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_CRITICAL_DATA_EVENT_PARAM3(EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM3( \ + EventId, VarType1, varName1, VarType2, varName2, VarType3, varName3, TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_CRITICAL_DATA_EVENT_PARAM4( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM4( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_CRITICAL_DATA_EVENT_PARAM5( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM5( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_CRITICAL_DATA_EVENT_PARAM6( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM6( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_CRITICAL_DATA_EVENT_PARAM7( \ + EventId, PrivacyTag, VarType1, varName1, VarType2, varName2, VarType3, varName3, VarType4, varName4, VarType5, varName5, VarType6, varName6, VarType7, varName7) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM7( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag)) +#define DEFINE_TAGGED_COMPLIANT_CRITICAL_DATA_EVENT_PARAM8( \ + EventId, \ + PrivacyTag, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8) \ + DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM8( \ + EventId, \ + VarType1, \ + varName1, \ + VarType2, \ + varName2, \ + VarType3, \ + varName3, \ + VarType4, \ + varName4, \ + VarType5, \ + varName5, \ + VarType6, \ + varName6, \ + VarType7, \ + varName7, \ + VarType8, \ + varName8, \ + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA), \ + TelemetryPrivacyDataTag(PrivacyTag)) + +#define DEFINE_TAGGED_COMPLIANT_CRITICAL_DATA_EVENT_UINT32(EventId, PrivacyTag, varName) \ + DEFINE_TAGGED_COMPLIANT_CRITICAL_DATA_EVENT_PARAM1(EventId, PrivacyTag, UINT32, varName) +#define DEFINE_TAGGED_COMPLIANT_CRITICAL_DATA_EVENT_BOOL(EventId, PrivacyTag, varName) \ + DEFINE_TAGGED_COMPLIANT_CRITICAL_DATA_EVENT_PARAM1(EventId, PrivacyTag, bool, varName) +#define DEFINE_TAGGED_COMPLIANT_CRITICAL_DATA_EVENT_STRING(EventId, PrivacyTag, varName) \ + DEFINE_TAGGED_COMPLIANT_CRITICAL_DATA_EVENT_PARAM1(EventId, PrivacyTag, PCWSTR, varName) + +// Thread Activities [deprecated] +// These are desktop only and are not recommended by the fundamentals team. These activities lag behind regular activities in +// their ability to use CallContext or to be cross-thread portable, so their usage should be limited. + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#define BEGIN_DEFINE_TRACELOGGING_THREAD_ACTIVITY_CLASS_WITH_KEYWORD_LEVEL(ActivityClassName, keyword, level) \ + class ActivityClassName final : public _TlgActivityBase \ + { \ + static const UINT64 PrivacyTag = 0; \ + friend class _TlgActivityBase; \ + void OnStarted() \ + { \ + PushThreadActivityId(); \ + } \ + void OnStopped() \ + { \ + PopThreadActivityId(); \ + } \ +\ + public: \ + ActivityClassName() : m_result(S_OK) \ + { \ + } \ +\ + private: \ + template \ + ActivityClassName(_In_ void**, TArgs&&... args) : m_result(S_OK) \ + { \ + StartActivity(wistd::forward(args)...); \ + } \ +\ + protected: \ + void EnsureWatchingCurrentThread() \ + { \ + } \ + void IgnoreCurrentThread() \ + { \ + } \ + wil::FailureInfo const* GetFailureInfo() \ + { \ + return (FAILED(m_result) && (m_cache.GetFailure() != nullptr) && (m_result == m_cache.GetFailure()->hr)) \ + ? m_cache.GetFailure() \ + : nullptr; \ + } \ + HRESULT GetResult() \ + { \ + return m_result; \ + } \ +\ + public: \ + ~ActivityClassName() \ + { \ + Stop(HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION)); \ + } \ + ActivityClassName(ActivityClassName&&) = default; \ + WI_NODISCARD TraceLoggingHProvider Provider() const \ + { \ + return TraceLoggingType::Provider(); \ + } \ + void Stop(HRESULT hr = S_OK) \ + { \ + if (IsStarted()) \ + { \ + m_result = hr; \ + TRACELOGGING_WRITE_ACTIVITY_STOP(ActivityClassName); \ + } \ + } \ + template \ + void StopWithResult(HRESULT hr, TArgs&&... args) \ + { \ + m_result = hr; \ + Stop(wistd::forward(args)...); \ + } \ + template \ + static ActivityClassName Start(TArgs&&... args) \ + { \ + return ActivityClassName(static_cast(__nullptr), wistd::forward(args)...); \ + } \ + void StartActivity() \ + { \ + TRACELOGGING_WRITE_ACTIVITY_START(ActivityClassName); \ + } + +#define BEGIN_DEFINE_TRACELOGGING_THREAD_ACTIVITY_CLASS_WITH_KEYWORD(ActivityClassName, keyword) \ + BEGIN_DEFINE_TRACELOGGING_THREAD_ACTIVITY_CLASS_WITH_KEYWORD_LEVEL(ActivityClassName, keyword, WINEVENT_LEVEL_VERBOSE) + +#define BEGIN_DEFINE_TRACELOGGING_THREAD_ACTIVITY_CLASS_WITH_LEVEL(ActivityClassName, level) \ + BEGIN_DEFINE_TRACELOGGING_THREAD_ACTIVITY_CLASS_WITH_KEYWORD_LEVEL(ActivityClassName, 0, level) + +#define BEGIN_DEFINE_TRACELOGGING_THREAD_ACTIVITY_CLASS(ActivityClassName) \ + BEGIN_DEFINE_TRACELOGGING_THREAD_ACTIVITY_CLASS_WITH_KEYWORD_LEVEL(ActivityClassName, 0, WINEVENT_LEVEL_VERBOSE) + +#define END_DEFINE_TRACELOGGING_THREAD_ACTIVITY_CLASS() \ +private: \ + HRESULT m_result; \ + wil::ThreadFailureCache m_cache; \ + } \ + ; + +#define BEGIN_DEFINE_TELEMETRY_THREAD_ACTIVITY_CLASS(ActivityClassName) \ + BEGIN_DEFINE_TRACELOGGING_THREAD_ACTIVITY_CLASS_WITH_KEYWORD(ActivityClassName, MICROSOFT_KEYWORD_TELEMETRY) + +#define END_DEFINE_TELEMETRY_THREAD_ACTIVITY_CLASS() END_DEFINE_TRACELOGGING_THREAD_ACTIVITY_CLASS() + +#define DEFINE_TRACELOGGING_THREAD_ACTIVITY_WITH_KEYWORD_LEVEL(ActivityClassName, keyword, level) \ + BEGIN_DEFINE_TRACELOGGING_THREAD_ACTIVITY_CLASS_WITH_KEYWORD_LEVEL(ActivityClassName, keyword, level) \ + END_DEFINE_TRACELOGGING_THREAD_ACTIVITY_CLASS() + +#define DEFINE_TRACELOGGING_THREAD_ACTIVITY_WITH_KEYWORD(ActivityClassName, keyword) \ + BEGIN_DEFINE_TRACELOGGING_THREAD_ACTIVITY_CLASS_WITH_KEYWORD(ActivityClassName, keyword) \ + END_DEFINE_TRACELOGGING_THREAD_ACTIVITY_CLASS() + +#define DEFINE_TRACELOGGING_THREAD_ACTIVITY_WITH_LEVEL(ActivityClassName, level) \ + BEGIN_DEFINE_TRACELOGGING_THREAD_ACTIVITY_CLASS_WITH_LEVEL(ActivityClassName, level) \ + END_DEFINE_TRACELOGGING_THREAD_ACTIVITY_CLASS() + +#define DEFINE_TRACELOGGING_THREAD_ACTIVITY(ActivityClassName) \ + BEGIN_DEFINE_TRACELOGGING_THREAD_ACTIVITY_CLASS(ActivityClassName) \ + END_DEFINE_TRACELOGGING_THREAD_ACTIVITY_CLASS() + +#define DEFINE_TELEMETRY_THREAD_ACTIVITY(ActivityClassName) \ + BEGIN_DEFINE_TELEMETRY_THREAD_ACTIVITY_CLASS(ActivityClassName) \ + END_DEFINE_TELEMETRY_THREAD_ACTIVITY_CLASS() + +#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) */ + +// [deprecated] +// DO NOT USE these concepts +// These should be removed post RI/FI cycle... + +#define DEFINE_TRACELOGGING_METHOD DEFINE_EVENT_METHOD +#define BEGIN_DEFINE_TELEMETRY_ACTIVITY_CLASS BEGIN_TELEMETRY_ACTIVITY_CLASS +#define END_DEFINE_TELEMETRY_ACTIVITY_CLASS END_ACTIVITY_CLASS +#define BEGIN_DEFINE_TRACELOGGING_ACTIVITY_CLASS BEGIN_TRACELOGGING_ACTIVITY_CLASS +#define END_DEFINE_TRACELOGGING_ACTIVITY_CLASS END_ACTIVITY_CLASS +#define TELEMETRY_WRITE_ACTIVITY_START TraceLoggingClassWriteStart +#define TRACELOGGING_WRITE_ACTIVITY_START TraceLoggingClassWriteStart +#define TELEMETRY_WRITE_ACTIVITY_STOP TraceLoggingClassWriteStop +#define TRACELOGGING_WRITE_ACTIVITY_STOP TraceLoggingClassWriteStop +#define WRITE_TRACELOGGING_EVENT TraceLoggingClassWrite +#define WRITE_TELEMETRY_EVENT TraceLoggingClassWriteTelemetry +#define TRACELOGGING_WRITE_TAGGED_EVENT TraceLoggingClassWriteTagged +#define TELEMETRY_WRITE_TAGGED_EVENT TraceLoggingClassWriteTaggedTelemetry + +/// @cond +// [deprecated] +// DO NOT USE these concepts +// These should be removed post RI/FI cycle... +#define __DEFINE_EVENT DEFINE_TRACELOGGING_EVENT +#define __DEFINE_EVENT_PARAM1 DEFINE_TRACELOGGING_EVENT_PARAM1 +#define __DEFINE_EVENT_PARAM2 DEFINE_TRACELOGGING_EVENT_PARAM2 +#define __DEFINE_EVENT_PARAM3 DEFINE_TRACELOGGING_EVENT_PARAM3 +#define __DEFINE_EVENT_PARAM4 DEFINE_TRACELOGGING_EVENT_PARAM4 +#define __DEFINE_EVENT_PARAM5 DEFINE_TRACELOGGING_EVENT_PARAM5 +#define __DEFINE_EVENT_PARAM6 DEFINE_TRACELOGGING_EVENT_PARAM6 +#define __DEFINE_EVENT_PARAM7 DEFINE_TRACELOGGING_EVENT_PARAM7 +#define __DEFINE_EVENT_UINT32 DEFINE_TRACELOGGING_EVENT_UINT32 +#define __DEFINE_EVENT_BOOL DEFINE_TRACELOGGING_EVENT_BOOL +#define __DEFINE_EVENT_STRING DEFINE_TRACELOGGING_EVENT_STRING + +// [deprecated] +// DO NOT USE these concepts +// These should be removed post RI/FI cycle... +#define __DEFINE_TAGGED_EVENT DEFINE_TAGGED_TRACELOGGING_EVENT +#define __DEFINE_TAGGED_EVENT_PARAM1 DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM1 +#define __DEFINE_TAGGED_EVENT_PARAM2 DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM2 +#define __DEFINE_TAGGED_EVENT_PARAM3 DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM3 +#define __DEFINE_TAGGED_EVENT_PARAM4 DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM4 +#define __DEFINE_TAGGED_EVENT_PARAM5 DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM5 +#define __DEFINE_TAGGED_EVENT_PARAM6 DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM6 +#define __DEFINE_TAGGED_EVENT_PARAM7 DEFINE_TAGGED_TRACELOGGING_EVENT_PARAM7 +#define __DEFINE_TAGGED_EVENT_UINT32 DEFINE_TAGGED_TRACELOGGING_EVENT_UINT32 +#define __DEFINE_TAGGED_EVENT_BOOL DEFINE_TAGGED_TRACELOGGING_EVENT_BOOL +#define __DEFINE_TAGGED_EVENT_STRING DEFINE_TAGGED_TRACELOGGING_EVENT_STRING +/// @endcond + +template +class ActivityErrorTracer +{ +public: + ActivityErrorTracer(T const&) + { + } +}; + +using TelemetryBase = wil::TraceLoggingProvider; + +#define TRACELOGGING_WRITE_EVENT(TraceLoggingClassName, EventId, ...) \ + TraceLoggingWrite(TraceLoggingClassName::TraceLoggingType::Provider(), EventId, ##__VA_ARGS__) + +#define TELEMETRY_WRITE_EVENT(EventId, ...) \ + TraceLoggingWrite(TraceLoggingType::Provider(), EventId, TraceLoggingKeyword(MICROSOFT_KEYWORD_TELEMETRY), ##__VA_ARGS__) + +#define DEFINE_TAGGED_EVENT_METHOD(MethodName) \ +public: \ + void MethodName + +#define DEFINE_ACTIVITY_START(...) void StartActivity(__VA_ARGS__) + +#define DEFINE_ACTIVITY_STOP(...) void Stop(__VA_ARGS__) + +#define DECLARE_TRACELOGGING_CLASS(TraceLoggingClassName, ProviderName, ProviderId) \ + class TraceLoggingClassName : public wil::TraceLoggingProvider \ + { \ + IMPLEMENT_TRACELOGGING_CLASS_WITH_MICROSOFT_TELEMETRY(TraceLoggingClassName, ProviderName, ProviderId); \ + }; + +#define IMPLEMENT_TELEMETRY_CLASS(TelemetryClassName, TraceLoggingClassName) \ + __IMPLEMENT_TRACELOGGING_CLASS_BASE(TelemetryClassName, TraceLoggingClassName) \ +protected: \ + void Create() \ + { \ + AttachProvider(TraceLoggingClassName::Provider()); \ + __TRACELOGGING_DEFINE_PROVIDER_STORAGE_LINK(TelemetryClassName, TraceLoggingClassName); \ + } \ +\ +public: + +namespace wil +{ +/// @cond +namespace details +{ +#ifdef WIL_API_TELEMETRY_SUSPEND_HANDLER +#pragma detect_mismatch("ODR_violation_WIL_API_TELEMETRY_SUSPEND_HANDLER_mismatch", "1") +#else +#pragma detect_mismatch("ODR_violation_WIL_API_TELEMETRY_SUSPEND_HANDLER_mismatch", "0") +#endif + + class ApiTelemetryLogger : public wil::TraceLoggingProvider + { + // {fb7fcbc6-7156-5a5b-eabd-0be47b14f453} + IMPLEMENT_TRACELOGGING_CLASS_WITH_MICROSOFT_TELEMETRY( + ApiTelemetryLogger, + "Microsoft.Windows.ApiTelemetry", + (0xfb7fcbc6, 0x7156, 0x5a5b, 0xea, 0xbd, 0x0b, 0xe4, 0x7b, 0x14, 0xf4, 0x53)); + + public: + // Used to store of list of APIs (with namespace, class, custom and call count data per API). + // This is public so that it can be unit tested. + class ApiDataList + { + public: + struct ApiData + { + PCWSTR className = nullptr; + PCWSTR apiName = nullptr; + PCSTR specialization = nullptr; + volatile long* counterReference = nullptr; + wistd::unique_ptr next; + + ApiData(PCWSTR className_, PCWSTR apiName_, PCSTR specialization_, volatile long* counterReference_) : + className(className_), apiName(apiName_), specialization(specialization_), counterReference(counterReference_) + { + } + }; + + // Inserts a new Api call counter into the list, keeping the list sorted by className + void Insert(PCWSTR className, PCWSTR apiName, _In_opt_ PCSTR specialization, volatile long* counterReference) + { + wistd::unique_ptr newApiData(new (std::nothrow) ApiData(className, apiName, specialization, counterReference)); + if (newApiData) + { + auto lock = m_lock.lock_exclusive(); + + // Insert the new ApiData, keeping the list sorted by className. + wistd::unique_ptr* currentNode = &m_root; + while (*currentNode) + { + wistd::unique_ptr& node = *currentNode; + if (wcscmp(className, node->className) <= 0) + { + break; + } + currentNode = &(node->next); + } + newApiData->next.reset(currentNode->release()); + currentNode->reset(newApiData.release()); + } + } + + // For each distinct namespace, calls the provided flushCallback function. + // After returning, it will have deleted all ApiData elements, and zeroed the *counterReference stored in each ApiData. + void Flush(wistd::function flushCallback) + { + wistd::unique_ptr root; + if (m_root) + { + auto lock = m_lock.lock_exclusive(); + root.swap(m_root); + } + + while (root) + { + // First find the number of characters we need to allocate for each string, and the number of items in the counter array to allocate + size_t totalApiListLength = 1; // Init to 1 to account for null terminator + size_t totalSpecializationsLength = 1; // Init to 1 to account for null terminator + UINT16 numCounts = 0; + + ProcessSingleNamespace(&root, [&](wistd::unique_ptr& node) { + // Get the length needed for the class string + const wchar_t* strAfterNamespace = GetClassStringPointer(node->className); + size_t classStrLen = wcslen(strAfterNamespace ? strAfterNamespace : node->className); + + totalApiListLength += (classStrLen + wcslen(node->apiName) + 1); // We add 1 to account for the comma delimiter + if (node->specialization) + { + totalSpecializationsLength += strlen(node->specialization) + 1; // We add 1 to account for the comma delimiter + } + else + { + totalSpecializationsLength += 2; // '-' plus comma delimiter + } + numCounts++; + }); + + // Fill arrays with the API data, and then pass it to the callback function + wistd::unique_ptr apiList(new (std::nothrow) wchar_t[totalApiListLength]); + wistd::unique_ptr specializationList(new (std::nothrow) char[totalSpecializationsLength]); + wistd::unique_ptr countArray(new (std::nothrow) UINT32[numCounts]); + size_t nameSpaceLength = GetNameSpaceLength(root->className) + 1; + wistd::unique_ptr nameSpace(new (std::nothrow) wchar_t[nameSpaceLength]); + if (!apiList || !specializationList || !countArray || !nameSpace) + { + return; + } + + ZeroMemory(apiList.get(), totalApiListLength * sizeof(wchar_t)); + ZeroMemory(specializationList.get(), totalSpecializationsLength * sizeof(char)); + ZeroMemory(countArray.get(), numCounts * sizeof(UINT32)); + ZeroMemory(nameSpace.get(), nameSpaceLength * sizeof(wchar_t)); + + StringCchCopyNW(nameSpace.get(), STRSAFE_MAX_CCH, root->className, nameSpaceLength - 1); + + int countArrayIndex = 0; + + wistd::unique_ptr* lastNamespaceNode = ProcessSingleNamespace(&root, [&](wistd::unique_ptr& node) { + countArray[countArrayIndex] = static_cast(::InterlockedExchangeNoFence(node->counterReference, 0)); + + // Prepend the portion of the apiName group string that's after the '.'. So for example, if the + // className is "Windows.System.Launcher", then we prepend "Launcher." to the apiName string. + const wchar_t* strAfterNamespace = GetClassStringPointer(node->className); + if (strAfterNamespace) + { + FAIL_FAST_IF_FAILED(StringCchCatW(apiList.get(), totalApiListLength, strAfterNamespace + 1)); + FAIL_FAST_IF_FAILED(StringCchCatW(apiList.get(), totalApiListLength, L".")); + } + + FAIL_FAST_IF_FAILED(StringCchCatW(apiList.get(), totalApiListLength, node->apiName)); + if (node->specialization) + { + FAIL_FAST_IF( + strncat_s(specializationList.get(), totalSpecializationsLength, node->specialization, strlen(node->specialization)) != + 0); + } + else + { + FAIL_FAST_IF(strncat_s(specializationList.get(), totalSpecializationsLength, "-", 1) != 0); + } + + if (countArrayIndex != (numCounts - 1)) + { + FAIL_FAST_IF_FAILED(StringCchCatW(apiList.get(), totalApiListLength, L",")); + FAIL_FAST_IF(strncat_s(specializationList.get(), totalSpecializationsLength, ",", 1) != 0); + } + + countArrayIndex++; + }); + + // Call the callback function with the data we've collected for this namespace + flushCallback(nameSpace.get(), apiList.get(), specializationList.get(), countArray.get(), numCounts); + + if (*lastNamespaceNode) + { + root.swap((*lastNamespaceNode)->next); + } + else + { + root.reset(); + } + } + } + + private: + static wistd::unique_ptr* ProcessSingleNamespace( + wistd::unique_ptr* root, wistd::function&)> workerCallback) + { + wistd::unique_ptr* currentNode = root; + while (*currentNode) + { + wistd::unique_ptr& node = *currentNode; + + workerCallback(node); + + // Check if our next node would be a new namespace; if so, then break out + if (node->next && !IsSameNameSpace(node->className, node->next->className)) + { + break; + } + + currentNode = &(node->next); + } + + return currentNode; + } + + static bool IsSameNameSpace(PCWSTR namespaceClass1, PCWSTR namespaceClass2) + { + return (wcsncmp(namespaceClass1, namespaceClass2, GetNameSpaceLength(namespaceClass2) + 1) == 0); + } + + static size_t GetNameSpaceLength(PCWSTR nameSpaceClass) + { + const wchar_t* strAfterNamespace = GetClassStringPointer(nameSpaceClass); + return (strAfterNamespace ? (strAfterNamespace - nameSpaceClass) : wcslen(nameSpaceClass)); + } + + static const wchar_t* GetClassStringPointer(PCWSTR nameSpaceClass) + { + // Note: Usage of wcsrchr can cause build errors in some components, so we implement a way of getting the pointer + // to the 'class' portion of the string ourselves. + int retIndex = 0; + while (nameSpaceClass[retIndex] != '\0') + { + retIndex++; + } + while (retIndex > 0 && nameSpaceClass[retIndex] != '.') + { + retIndex--; + } + return (retIndex != 0 ? &(nameSpaceClass[retIndex]) : nullptr); + } + + wistd::unique_ptr m_root; + wil::srwlock m_lock; + }; + + public: + // Initializes an entry that holds the className.apiName, along with a counter for that className.apiName. + // The counterReference passed to this should later be passed to LogApiInfo. + // + // A separate entry will be created for each apiName that has a distinct specialization value. + // + // This function only needs to be called once for each API, although it doesn't hurt if it gets called more than once. + // + // The apiName, className, and specialization parameters should be compile time constants. specialization can be null. + DEFINE_EVENT_METHOD(InitApiData) + (PCWSTR className, PCWSTR apiName, _In_opt_ PCSTR specialization, volatile long* counterReference) + { + // TODO: Validate that apiName and className are a compile-time constants; validate that specialization is + // either compile-time constant or nullptr; validate that counterReference points to static variable. + // Can do this by making sure address is <= (GetModuleHandle() + DLL size). + m_apiDataList.Insert(className, apiName, specialization, counterReference); + } + + // Fires a telemetry event that contains the method call apiName that has been logged by the component, + // since the last FireEvent() call, or since the component was loaded. + DEFINE_EVENT_METHOD(FireEvent)() + { + m_apiDataList.Flush([](PCWSTR nameSpace, PCWSTR apiList, PCSTR specializationList, UINT32* countArray, UINT16 numCounters) { + if (::wil::details::IsDebuggerPresent()) + { + TraceLoggingWrite( + Provider(), + "ApiCallCountsWithDebuggerPresent", + TraceLoggingValue(nameSpace, "Namespace"), + TraceLoggingValue(apiList, "ApiDataList"), + TraceLoggingValue(specializationList, "CustomList"), + TraceLoggingUInt32Array(countArray, numCounters, "HitCounts"), + TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_CRITICAL_DATA)); + } + else + { + TraceLoggingWrite( + Provider(), + "ApiCallCounts", + TraceLoggingValue(nameSpace, "Namespace"), + TraceLoggingValue(apiList, "ApiDataList"), + TraceLoggingValue(specializationList, "CustomList"), + TraceLoggingUInt32Array(countArray, numCounters, "HitCounts"), + TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), + TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES)); + } + + __TRACELOGGING_TEST_HOOK_VERIFY_API_TELEMETRY(nameSpace, apiList, specializationList, countArray, numCounters); + }); + + if (m_fireEventDelay < c_fireEventDelayLimit) + { + // Double the exponential backoff timer, until it reaches the maximum + m_fireEventDelay *= 2; + if (m_fireEventDelay > c_fireEventDelayLimit) + { + m_fireEventDelay = c_fireEventDelayLimit; + } + } + + ScheduleFireEventCallback(); + } + + // Used to declare that the component will handle calling FireEvent() in its own suspend handler. + // This optimizes the frequency at which the event will be fired. + DEFINE_EVENT_METHOD(UsingOwnSuspendHandler)() + { + m_fireEventDelay = c_fireEventDelayLimit; + ScheduleFireEventCallback(); + } + + private: + void Initialize() WI_NOEXCEPT override + { +#ifdef WIL_API_TELEMETRY_SUSPEND_HANDLER + m_fireEventDelay = c_fireEventDelayLimit; + + PPSM_APPSTATE_REGISTRATION psmReg; + BOOLEAN quiesced; + PsmRegisterAppStateChangeNotification( + [](BOOLEAN quiesced, PVOID, HANDLE) { + if (quiesced) + { + FireEvent(); + } + }, + StateChangeCategoryApplication, + 0, + nullptr, + &quiesced, + &psmReg); +#else + m_fireEventDelay = __TRACELOGGING_TEST_HOOK_API_TELEMETRY_EVENT_DELAY_MS; +#endif + m_fireEventThreadPoolTimer.reset(::CreateThreadpoolTimer(&FireEventCallback, nullptr, nullptr)); + ScheduleFireEventCallback(); + } + + static void __stdcall FireEventCallback(PTP_CALLBACK_INSTANCE, PVOID, PTP_TIMER) + { + FireEvent(); + } + + ~ApiTelemetryLogger() WI_NOEXCEPT override + { + FireEvent(); + + // release handle to thread pool timer instead of its destructor being call, if process is being terminated and dll is + // not being unloaded dynamically destruction of threadpool timer is considered invalid during process termination + if (ProcessShutdownInProgress()) + { + m_fireEventThreadPoolTimer.release(); + } + } + + void ScheduleFireEventCallback() + { + // do not schedule thread pool timer callback, if process is being terminated and dll is not being unloaded dynamically + if (m_fireEventThreadPoolTimer && !ProcessShutdownInProgress()) + { + // Note this will override any pending scheduled callback + FILETIME dueTime{}; + *reinterpret_cast(&dueTime) = -static_cast(m_fireEventDelay) * 10000; + SetThreadpoolTimer(m_fireEventThreadPoolTimer.get(), &dueTime, 0, 0); + } + } + + ApiDataList m_apiDataList; + wil::unique_threadpool_timer m_fireEventThreadPoolTimer; + + // The timer used to determine when to fire the next telemetry event (when it's fired based on a timer). + UINT m_fireEventDelay{}; + DWORD const c_fireEventDelayLimit = 20 * 60 * 1000; // 20 minutes + }; +} // namespace details +/// @endcond +} // namespace wil + +// Insert WI_LOG_API_USE near the top of a WinRT method to log that a method was called. +// The parameter should be the method name, for example: +// - WI_LOG_API_USE(L"LaunchUriAsync"); +// +// To log that the WinRT method reached a certain line of code, pass an override string: +// - WI_LOG_API_USE(L"LaunchUriAsync", "PointA"); +// +// If the class name can't be obtained at runtime, or if instrumenting a non-WinRT API, use the below macro, +// and pass the fully qualified class name (in the case of WinRT), or a string identifying the group of the non-WinRT API: +// - WI_LOG_CLASS_API_USE(RuntimeClass_Windows_System_Launcher, L"LaunchUriAsync"); +// +// Note: If the component can have a suspend handler, the following line should be added before including TraceLogging.h: +// - #define WIL_API_TELEMETRY_SUSPEND_HANDLER +// This will optimize the component's ability to upload telemetry, as it will upload on suspend. It will also disable +// frequent telemetry upload early in process execution. +// +// Alternatively, a component can call wil::details:ApiTelemetryLogger::FireEvent() from it's own suspend handler. +// If this is done, then in DLLMain it should also call wil::details::ApiTelemetryLogger::UsingOwnSuspendHandler(). +// +// Note: In your DLLMain method, please also add following code snippet +// +// wil::details::g_processShutdownInProgress = (lpReserved == nullptr); +// +// Adding this code snippet ensures that during process termination, thread pool timer +// destructor or SetThreadPoolTimer methods are not called, because they are invalid to call +// when dll is not getting dynamically unloaded. Skipping this code block will result in a continuable +// exception being thrown if process is getting terminated and dll in which ApiTelemetryLogger is not getting dynamically +// unloaded. For more details about lpReserved parameter, please refer to MSDN. + +/// @cond +#define __WI_LOG_CLASS_API_USE3(className, apiName, specialization) \ + do \ + { \ + static volatile long __wil_apiCallCounter = 0; \ + if (1 == ::InterlockedIncrementNoFence(&__wil_apiCallCounter)) \ + { \ + ::wil::details::ApiTelemetryLogger::InitApiData(className, apiName, specialization, &__wil_apiCallCounter); \ + } \ + } while (0, 0) +#define __WI_LOG_CLASS_API_USE2(className, apiName) __WI_LOG_CLASS_API_USE3(className, apiName, nullptr) +#define __WI_LOG_API_USE2(apiName, specialization) __WI_LOG_CLASS_API_USE3(InternalGetRuntimeClassName(), apiName, specialization) +#define __WI_LOG_API_USE1(apiName) __WI_LOG_CLASS_API_USE3(InternalGetRuntimeClassName(), apiName, nullptr) +/// @endcond + +#define WI_LOG_CLASS_API_USE(...) WI_MACRO_DISPATCH(__WI_LOG_CLASS_API_USE, ##__VA_ARGS__) + +#define WI_LOG_API_USE(...) WI_MACRO_DISPATCH(__WI_LOG_API_USE, ##__VA_ARGS__) + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#pragma warning(pop) +#endif // __WIL_TRACELOGGING_H_INCLUDED diff --git a/libs/wil/wil/com.h b/libs/wil/wil/com.h new file mode 100644 index 00000000..7438c0a8 --- /dev/null +++ b/libs/wil/wil/com.h @@ -0,0 +1,3330 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! Smart pointers and other thin usability pattern wrappers over COM patterns. +#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 + +#if __has_include() +#include +#endif +#if __has_include() +#include +#endif + +// Forward declaration within WIL (see https://msdn.microsoft.com/en-us/library/br244983.aspx) +/// @cond +namespace Microsoft +{ +namespace WRL +{ + template + class ComPtr; +} +} // namespace Microsoft +/// @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)> + { + }; + + using tag_com_query = wistd::integral_constant; + using tag_try_com_query = wistd::integral_constant; + using tag_com_copy = wistd::integral_constant; + using tag_try_com_copy = wistd::integral_constant; + + 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 + { + using type = default_query_policy; + }; + + 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), &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 + { + using type = weak_query_policy; + }; + +#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 + { + using type = agile_query_policy; + }; +#endif + + template + using query_policy_t = typename query_policy_helper::type>::type; + +} // namespace 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: + using element_type_reference = typename wistd::add_lvalue_reference::type; + using query_policy = details::query_policy_t; + +public: + //! The function return result (HRESULT or void) for the given err_policy (see @ref page_errors). + using result = typename err_policy::result; + //! The template type `T` being held by the com_ptr_t. + using element_type = T; + //! A pointer to the template type `T` being held by the com_ptr_t (what `get()` returns). + using pointer = T*; + + //! @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 = 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) + WI_NODISCARD const pointer* addressof() const WI_NOEXCEPT + { + return &m_ptr; + } + + //! Returns 'true' if the pointer is assigned (NOT nullptr) + WI_NODISCARD explicit operator bool() const WI_NOEXCEPT + { + return (m_ptr != nullptr); + } + + //! Returns the pointer + WI_NODISCARD pointer get() const WI_NOEXCEPT + { + return m_ptr; + } + + //! Allows direct calls against the pointer (AV on internal nullptr) + WI_NODISCARD pointer operator->() const WI_NOEXCEPT + { + return m_ptr; + } + + //! Dereferences the pointer (AV on internal nullptr) + WI_NODISCARD 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 + WI_NODISCARD 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 + WI_NODISCARD 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 + WI_NODISCARD 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 + WI_NODISCARD 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(); +} + +// clang-format off +#ifdef __cplusplus_winrt + +template +inline IInspectable* com_raw_ptr(T^ ptr) +{ + return reinterpret_cast(static_cast<::Platform::Object^>(ptr)); +} + +#endif +// clang-format on +/// @endcond + +#ifdef WIL_ENABLE_EXCEPTIONS +//! Constructs a `com_ptr` from a raw pointer. +//! This avoids having to restate the interface in pre-C++20. +//! Starting in C++20, you can write `wil::com_ptr(p)` directly. +//! ~~~ +//! void example(ILongNamedThing* thing) +//! { +//! callback([thing = wil::make_com_ptr(thing)] { /* do something */ }); +//! } +//! ~~~ +template +com_ptr make_com_ptr(T* p) +{ + return p; +} +#endif + +//! Constructs a `com_ptr_nothrow` from a raw pointer. +//! This avoids having to restate the interface in pre-C++20. +//! Starting in C++20, you can write `wil::com_ptr_nothrow(p)` directly. +//! ~~~ +//! void example(ILongNamedThing* thing) +//! { +//! callback([thing = wil::make_com_ptr_nothrow(thing)] { /* do something */ }); +//! } +//! ~~~ +template +com_ptr_nothrow make_com_ptr_nothrow(T* p) +{ + return p; +} + +//! Constructs a `com_ptr_failfast` from a raw pointer. +//! This avoids having to restate the interface in pre-C++20. +//! Starting in C++20, you can write `wil::com_ptr_failfast(p)` directly. +//! ~~~ +//! void example(ILongNamedThing* thing) +//! { +//! callback([thing = wil::make_com_ptr_failfast(thing)] { /* do something */ }); +//! } +//! ~~~ +template +com_ptr_failfast make_com_ptr_failfast(T* p) +{ + return p; +} + +//! @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; +} + +#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; +} +//! @} + +//! @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; +} +//! @} + +// clang-format off +#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 +// clang-format on + +//***************************************************************************** +// 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; +} + +#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; + } +} // namespace details + +#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; +} + +#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); +} + +#if __cpp_lib_apply && __has_include() +/// @cond +namespace details +{ + template + auto CoCreateInstanceEx(REFCLSID clsid, CLSCTX clsCtx) noexcept + { + MULTI_QI multiQis[sizeof...(Results)]{}; + const IID* iids[sizeof...(Results)]{&__uuidof(Results)...}; + + static_assert(sizeof...(Results) > 0); + + for (auto i = 0U; i < sizeof...(Results); ++i) + { + multiQis[i].pIID = iids[i]; + } + + const auto hr = CoCreateInstanceEx(clsid, nullptr, clsCtx, nullptr, ARRAYSIZE(multiQis), multiQis); + + std::tuple...> resultTuple; + + std::apply( + [i = 0, &multiQis](auto&... a) mutable { + (a.attach(reinterpret_cast::type::pointer>(multiQis[i++].pItf)), ...); + }, + resultTuple); + return std::tuple(hr, std::move(resultTuple)); + } + + template + auto com_multi_query(IUnknown* obj) + { + MULTI_QI multiQis[sizeof...(Results)]{}; + const IID* iids[sizeof...(Results)]{&__uuidof(Results)...}; + + static_assert(sizeof...(Results) > 0); + + for (auto i = 0U; i < sizeof...(Results); ++i) + { + multiQis[i].pIID = iids[i]; + } + + std::tuple...> resultTuple{}; + + wil::com_ptr_nothrow multiQi; + auto hr = obj->QueryInterface(IID_PPV_ARGS(&multiQi)); + if (SUCCEEDED(hr)) + { + hr = multiQi->QueryMultipleInterfaces(ARRAYSIZE(multiQis), multiQis); + std::apply( + [i = 0, &multiQis](auto&... a) mutable { + (a.attach(reinterpret_cast::type::pointer>(multiQis[i++].pItf)), ...); + }, + resultTuple); + } + return std::tuple{hr, std::move(resultTuple)}; + } +} // namespace details +/// @endcond + +#ifdef WIL_ENABLE_EXCEPTIONS +// CoCreateInstanceEx can be used to improve performance by requesting multiple interfaces +// from an object at create time. This is most useful for out of process (OOP) servers, saving +// and RPC per extra interface requested. +template +auto CoCreateInstanceEx(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER) +{ + auto [error, result] = details::CoCreateInstanceEx(clsid, clsCtx); + THROW_IF_FAILED(error); + THROW_HR_IF(E_NOINTERFACE, error == CO_S_NOTALLINTERFACES); + return result; +} + +template +auto TryCoCreateInstanceEx(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER) +{ + auto [error, result] = details::CoCreateInstanceEx(clsid, clsCtx); + return result; +} +#endif + +// Returns [error, result] where result is a tuple with each of the requested interfaces. +template +auto CoCreateInstanceExNoThrow(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER) noexcept +{ + auto [error, result] = details::CoCreateInstanceEx(clsid, clsCtx); + if (SUCCEEDED(error) && (error == CO_S_NOTALLINTERFACES)) + { + return std::tuple{E_NOINTERFACE, {}}; + } + return std::tuple{error, result}; +} + +template +auto TryCoCreateInstanceExNoThrow(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER) noexcept +{ + auto [error, result] = details::CoCreateInstanceEx(clsid, clsCtx); + return result; +} + +template +auto CoCreateInstanceExFailFast(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER) noexcept +{ + auto [error, result] = details::CoCreateInstanceEx(clsid, clsCtx); + FAIL_FAST_IF_FAILED(error); + FAIL_FAST_HR_IF(E_NOINTERFACE, error == CO_S_NOTALLINTERFACES); + return result; +} + +template +auto TryCoCreateInstanceExFailFast(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER) noexcept +{ + auto [error, result] = details::CoCreateInstanceEx(clsid, clsCtx); + return result; +} + +#ifdef WIL_ENABLE_EXCEPTIONS +template +auto com_multi_query(IUnknown* obj) +{ + auto [error, result] = details::com_multi_query(obj); + THROW_IF_FAILED(error); + THROW_HR_IF(E_NOINTERFACE, error == S_FALSE); + return result; +} + +template +auto try_com_multi_query(IUnknown* obj) +{ + auto [error, result] = details::com_multi_query(obj); + return result; +} +#endif + +#endif // __cpp_lib_apply && __has_include() + +#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. +@code +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); +} +@endcode +@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 receives 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 receives 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, +}; + +#if defined(__WIL_OBJBASE_H_) || defined(WIL_DOXYGEN) + +/** 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. +@code +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! +} +@endcode +@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` +@param options Controls behavior when reading a zero-length string from a stream +*/ +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. +@code +IStream* target = // ... +RETURN_IF_FAILED(wil::stream_write_string_nothrow(target, L"Waffles", 3)); +// Produces wchar_t[] { 0x3, L'W', L'a', L'f' }; +@endcode +@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. +@code +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' }; +@endcode +@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. +@code +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); +} +@endcode +@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); +} + +#if defined(__WIL_OBJBASE_H_) || defined(WIL_DOXYGEN) + +/** 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. +@code +IStream* source = // ... +wil::unique_cotaskmem_string content = wil::stream_read_string(source); +if (wcscmp(content.get(), L"waffles") == 0) +{ + // Waffles! +} +@endcode +@param source The stream from which to read a string +@param options Controls the behavior when reading a zero-length string +@return An non-null string (but possibly zero length) 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 `toWriteCch` is zero +@param toWriteCch 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. +@code +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)); + } +} +@endcode +*/ +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 + @code + // 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(); + @endcode + */ + 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 + WI_NODISCARD 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 + @code + // 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 }; + } + @endcode + */ + 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__) || defined(WIL_DOXYGEN) +/// @cond +namespace details +{ + inline void __stdcall SetSiteNull(IObjectWithSite* objWithSite) + { + objWithSite->SetSite(nullptr); // break the cycle + } +} // namespace 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__ + +// if C++17 or greater +#if WIL_HAS_CXX_17 +#ifdef WIL_ENABLE_EXCEPTIONS +/// @cond +namespace details +{ + template + struct com_enumerator_next_traits; + + template + struct com_enumerator_next_traits + { + using Interface = Itf; + using Result = T; + }; + + template + struct com_enumerator_next_traits + { + using Interface = Itf; + using Result = T; + }; + + template + struct has_next + { + template + static auto test(int) -> decltype(wistd::declval()->Next(0, nullptr, nullptr), wistd::true_type{}); + + template + static auto test(...) -> wistd::false_type; + + static constexpr bool value = decltype(test(0))::value; + }; + + template + constexpr bool has_next_v = has_next::value; + + template + struct You_must_specify_Smart_Output_type_explicitly + { + // If you get this error, you must specify a smart pointer type to receive the enumerated objects. + // We deduce the enumerator's output type (the type of the second parameter to the Next method). + // If that type is a COM pointer type (IFoo*), then we use wil::com_ptr. Otherwise, you must explicitly + // specify a smart-object type to receive the enumerated objects as it is not obvious how to handle disposing + // of an enumerated object. + // For example, if you have an enumerator that enumerates BSTRs, you must specify wil::unique_bstr as the + // smart pointer type to receive the enumerated BSTRs. + // auto it = wil::com_iterator(pEnumBStr); + static_assert( + wistd::is_same_v, + "Couldn't deduce a smart pointer type for the enumerator's output. You must explicitly specify a smart-object type to receive the enumerated objects."); + }; + + template + struct com_enumerator_traits + { + using Result = typename com_enumerator_next_traits::Result; + + // If the result is a COM pointer type (IFoo*), then we use wil::com_ptr. + // Otherwise, you must explicitly specify a smart output type. + using smart_result = wistd::conditional_t< + wistd::is_pointer_v && wistd::is_base_of_v<::IUnknown, wistd::remove_pointer_t>, + wil::com_ptr>, + You_must_specify_Smart_Output_type_explicitly>; + }; +} // namespace details +/// @endcond + +template +struct com_iterator +{ + using TActualStoredType = + wistd::conditional_t, typename wil::details::com_enumerator_traits::smart_result, TStoredType>; + + wil::com_ptr m_enum{}; + TActualStoredType m_currentValue{}; + + using smart_result = TActualStoredType; + com_iterator(com_iterator&&) = default; + com_iterator(com_iterator const&) = default; + com_iterator& operator=(com_iterator&&) = default; + com_iterator& operator=(com_iterator const&) = default; + + com_iterator(IEnumType* enumPtr) : m_enum(enumPtr) + { + FetchNext(); + } + + auto operator->() + { + return wistd::addressof(m_currentValue); + } + + auto& operator*() + { + return m_currentValue; + } + + const auto& operator*() const + { + return m_currentValue; + } + + com_iterator& operator++() + { + // If we're already at the end, don't try to advance. Otherwise, use Next to advance. + if (m_enum) + { + FetchNext(); + } + + return *this; + } + + bool operator!=(com_iterator const& other) const + { + return !(*this == other); + } + + bool operator==(com_iterator const& other) const + { + return (m_enum.get() == other.m_enum.get()); + } + +private: + void FetchNext() + { + if (m_enum) + { + // we cannot say m_currentValue = {} because com_ptr has 2 operator= overloads: one for T* and one for nullptr_t + m_currentValue = TActualStoredType{}; + auto hr = m_enum->Next(1, &m_currentValue, nullptr); + if (hr == S_FALSE) + { + m_enum = nullptr; + } + else + { + THROW_IF_FAILED_MSG(hr, "Failed to get next"); + } + } + } +}; + +// CTAD for com_iterator + +template +com_iterator(IEnumType*) -> com_iterator; + +template , int> = 0> +WI_NODISCARD auto make_range(IEnumXxx* enumPtr) +{ + using TActualStoredType = + wistd::conditional_t, typename wil::details::com_enumerator_traits::smart_result, TStoredType>; + + struct iterator_range + { + + static_assert(!wistd::is_same_v, "You must specify a type to receive the enumerated objects."); + + // the stored type must be constructible from the output type of the enumerator + static_assert( + wistd::is_constructible_v::Result>, + "The type you specified cannot be converted to the enumerator's output type."); + + using enumerator_type = com_iterator; + + IEnumXxx* m_enumerator{}; + iterator_range(IEnumXxx* enumPtr) : m_enumerator(enumPtr) + { + } + + WI_NODISCARD auto begin() + { + return enumerator_type(m_enumerator); + } + + WI_NODISCARD constexpr auto end() const noexcept + { + return enumerator_type(nullptr); + } + }; + + return iterator_range(enumPtr); +} + +#endif // WIL_HAS_CXX_17 +#endif // WIL_ENABLE_EXCEPTIONS + +} // namespace wil + +#endif diff --git a/libs/wil/wil/com_apartment_variable.h b/libs/wil/wil/com_apartment_variable.h new file mode 100644 index 00000000..8103150e --- /dev/null +++ b/libs/wil/wil/com_apartment_variable.h @@ -0,0 +1,489 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! Smart pointers and other thin usability pattern wrappers over COM patterns. +#ifndef __WIL_COM_APARTMENT_VARIABLE_INCLUDED +#define __WIL_COM_APARTMENT_VARIABLE_INCLUDED + +#include +#include +#include +#include +#include +#include + +#include "com.h" +#include "cppwinrt.h" +#include "result_macros.h" +#include "win32_helpers.h" + +#ifndef WIL_ENABLE_EXCEPTIONS +#error This header requires exceptions +#endif + +namespace wil +{ +// Determine if apartment variables are supported in the current process context. +// Prior to build 22365, the APIs needed to create apartment variables (e.g. RoGetApartmentIdentifier) +// failed for unpackaged processes. For MS people, see http://task.ms/31861017 for details. +// APIs needed to implement apartment variables did not work in non-packaged processes. +inline bool are_apartment_variables_supported() +{ + unsigned long long apartmentId{}; + return RoGetApartmentIdentifier(&apartmentId) != HRESULT_FROM_WIN32(ERROR_API_UNAVAILABLE); +} + +// COM will implicitly rundown the apartment registration when it invokes a handler +// and blocks calling unregister when executing the callback. So be careful to release() +// this when callback is invoked to avoid a double free of the cookie. +using unique_apartment_shutdown_registration = + unique_any; + +struct apartment_variable_platform +{ + static unsigned long long GetApartmentId() + { + unsigned long long apartmentId{}; + FAIL_FAST_IF_FAILED(RoGetApartmentIdentifier(&apartmentId)); + return apartmentId; + } + + static auto RegisterForApartmentShutdown(IApartmentShutdown* observer) + { + unsigned long long id{}; + shutdown_type cookie; + THROW_IF_FAILED(RoRegisterForApartmentShutdown(observer, &id, cookie.put())); + return cookie; + } + + static void UnRegisterForApartmentShutdown(APARTMENT_SHUTDOWN_REGISTRATION_COOKIE cookie) + { + FAIL_FAST_IF_FAILED(RoUnregisterForApartmentShutdown(cookie)); + } + + static auto CoInitializeEx(DWORD coinitFlags = 0 /*COINIT_MULTITHREADED*/) + { + return wil::CoInitializeEx(coinitFlags); + } + + // disable the test hook + inline static constexpr unsigned long AsyncRundownDelayForTestingRaces = INFINITE; + + using shutdown_type = wil::unique_apartment_shutdown_registration; +}; + +enum class apartment_variable_leak_action +{ + fail_fast, + ignore +}; + +// "pins" the current module in memory by incrementing the module reference count and leaking that. +inline void ensure_module_stays_loaded() +{ + static INIT_ONCE s_initLeakModule{}; // avoiding magic statics + wil::init_once_failfast(s_initLeakModule, []() { + HMODULE result{}; + FAIL_FAST_IF(!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN, L"", &result)); + return S_OK; + }); +} + +/// @cond +namespace details +{ + // For the address of data, you can detect global variables by the ability to resolve the module from the address. + inline bool IsGlobalVariable(const void* moduleAddress) noexcept + { + wil::unique_hmodule moduleHandle; + return GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast(moduleAddress), &moduleHandle) != FALSE; + } + + struct any_maker_base + { + std::any (*adapter)(void*); + void* inner; + + WI_NODISCARD std::any operator()() const + { + return adapter(inner); + } + }; + + template + struct any_maker : any_maker_base + { + any_maker() + { + adapter = [](auto) -> std::any { + return T{}; + }; + } + + any_maker(T (*maker)()) + { + adapter = [](auto maker) -> std::any { + return reinterpret_cast(maker)(); + }; + inner = reinterpret_cast(maker); + } + + template + any_maker(F&& f) + { + adapter = [](auto maker) -> std::any { + return reinterpret_cast(maker)[0](); + }; + inner = std::addressof(f); + } + }; + + template + struct apartment_variable_base + { + inline static winrt::slim_mutex s_lock; + + struct apartment_variable_storage + { + apartment_variable_storage(apartment_variable_storage&& other) noexcept = default; + apartment_variable_storage(const apartment_variable_storage& other) = delete; + + apartment_variable_storage(typename test_hook::shutdown_type&& cookie_) : cookie(std::move(cookie_)) + { + } + + winrt::apartment_context context; + typename test_hook::shutdown_type cookie; + // Variables are stored using the address of the apartment_variable_base<> as the key. + std::unordered_map*, std::any> variables; + }; + + // Apartment id -> variables storage. + inline static wil::object_without_destructor_on_shutdown> s_apartmentStorage; + + constexpr apartment_variable_base() = default; + ~apartment_variable_base() + { + // Global variables (object with static storage duration) + // are run down when the process is shutting down or when the + // dll is unloaded. At these points it is not possible to start + // an async operation and the work performed is not needed, + // the apartments with variable have been run down already. + const auto isGlobal = details::IsGlobalVariable(this); + if (!isGlobal) + { + clear_all_apartments_async(); + } + + if constexpr (leak_action == apartment_variable_leak_action::fail_fast) + { + if (isGlobal && !ProcessShutdownInProgress()) + { + // If you hit this fail fast it means the storage in s_apartmentStorage will be leaked. + // For apartment variables used in .exes, this is expected and + // this fail fast should be disabled using + // wil::apartment_variable + // + // For DLLs, if this is expected, disable this fail fast using + // wil::apartment_variable + // + // Use of apartment variables in DLLs only loaded by COM will never hit this case + // as COM will unload DLLs before apartments are rundown, + // providing the opportunity to empty s_apartmentStorage. + // + // But DLLs loaded and unloaded to call DLL entry points (outside of COM) may + // create variable storage that can't be cleaned up as the DLL lifetime is + // shorter that the COM lifetime. In these cases either + // 1) accept the leaks and disable the fail fast as describe above + // 2) disable module unloading by calling wil::ensure_module_stays_loaded + // 3) CoCreate an object from this DLL to make COM aware of the DLL + FAIL_FAST_IF(!s_apartmentStorage.get().empty()); + } + } + } + + // non-copyable, non-assignable + apartment_variable_base(apartment_variable_base const&) = delete; + void operator=(apartment_variable_base const&) = delete; + + // get current value or throw if no value has been set + std::any& get_existing() + { + auto any = get_if(); + if (!any) + { + THROW_HR(E_NOT_SET); + } + + return *any; + } + + static apartment_variable_storage* get_current_apartment_variable_storage() + { + auto storage = s_apartmentStorage.get().find(test_hook::GetApartmentId()); + if (storage != s_apartmentStorage.get().end()) + { + return &storage->second; + } + return nullptr; + } + + apartment_variable_storage* ensure_current_apartment_variables() + { + auto variables = get_current_apartment_variable_storage(); + if (variables) + { + return variables; + } + + struct ApartmentObserver : public winrt::implements + { + void STDMETHODCALLTYPE OnUninitialize(unsigned long long apartmentId) noexcept override + { + // This code runs at apartment rundown so be careful to avoid deadlocks by + // extracting the variables under the lock then release them outside. + auto variables = [apartmentId]() { + auto lock = winrt::slim_lock_guard(s_lock); + return s_apartmentStorage.get().extract(apartmentId); + }(); + WI_ASSERT(variables.key() == apartmentId); + // The system implicitly releases the shutdown observer + // after invoking the callback and does not allow calling unregister + // in the callback. So release the reference to the registration. + variables.mapped().cookie.release(); + } + }; + auto shutdownRegistration = test_hook::RegisterForApartmentShutdown(winrt::make().get()); + return &s_apartmentStorage.get() + .insert({test_hook::GetApartmentId(), apartment_variable_storage(std::move(shutdownRegistration))}) + .first->second; + } + + // get current value or custom-construct one on demand + template + std::any& get_or_create(any_maker&& creator) + { + apartment_variable_storage* variable_storage = nullptr; + + { // scope for lock + auto lock = winrt::slim_lock_guard(s_lock); + variable_storage = ensure_current_apartment_variables(); + + auto variable = variable_storage->variables.find(this); + if (variable != variable_storage->variables.end()) + { + return variable->second; + } + } // drop the lock + + // create the object outside the lock to avoid reentrant deadlock + auto value = creator(); + + auto insert_lock = winrt::slim_lock_guard(s_lock); + // The insertion may fail if creator() recursively caused itself to be created, + // in which case we return the existing object and the falsely-created one is discarded. + return variable_storage->variables.insert({this, std::move(value)}).first->second; + } + + // get pointer to current value or nullptr if no value has been set + std::any* get_if() + { + auto lock = winrt::slim_lock_guard(s_lock); + + if (auto variable_storage = get_current_apartment_variable_storage()) + { + auto variable = variable_storage->variables.find(this); + if (variable != variable_storage->variables.end()) + { + return &(variable->second); + } + } + return nullptr; + } + + // replace or create the current value, fail fasts if the value is not already stored + void set(std::any value) + { + // release value, with the swapped value, outside of the lock + { + auto lock = winrt::slim_lock_guard(s_lock); + auto storage = s_apartmentStorage.get().find(test_hook::GetApartmentId()); + FAIL_FAST_IF(storage == s_apartmentStorage.get().end()); + auto& variable_storage = storage->second; + auto variable = variable_storage.variables.find(this); + FAIL_FAST_IF(variable == variable_storage.variables.end()); + variable->second.swap(value); + } + } + + // remove any current value + void clear() + { + auto lock = winrt::slim_lock_guard(s_lock); + if (auto variable_storage = get_current_apartment_variable_storage()) + { + variable_storage->variables.erase(this); + if (variable_storage->variables.size() == 0) + { + s_apartmentStorage.get().erase(test_hook::GetApartmentId()); + } + } + } + + winrt::Windows::Foundation::IAsyncAction clear_all_apartments_async() + { + // gather all the apartments that hold objects we need to destruct + // (do not gather the objects themselves, because the apartment might + // destruct before we get around to it, and we should let the apartment + // destruct the object while it still can). + + std::vector contexts; + { // scope for lock + auto lock = winrt::slim_lock_guard(s_lock); + for (auto& [id, storage] : s_apartmentStorage.get()) + { + auto variable = storage.variables.find(this); + if (variable != storage.variables.end()) + { + contexts.push_back(storage.context); + } + } + } + + if (contexts.empty()) + { + co_return; + } + + wil::unique_mta_usage_cookie mta_reference; // need to extend the MTA due to async cleanup + FAIL_FAST_IF_FAILED(CoIncrementMTAUsage(mta_reference.put())); + + // From a background thread hop into each apartment to run down the object + // if it's still there. + co_await winrt::resume_background(); + + // This hook enables testing the case where execution of this method loses the race with + // apartment rundown by other means. + if constexpr (test_hook::AsyncRundownDelayForTestingRaces != INFINITE) + { + Sleep(test_hook::AsyncRundownDelayForTestingRaces); + } + + for (auto&& context : contexts) + { + try + { + co_await context; + clear(); + } + catch (winrt::hresult_error const& e) + { + // Ignore failure if apartment ran down before we could clean it up. + // The object already ran down as part of apartment cleanup. + if ((e.code() != RPC_E_SERVER_DIED_DNE) && (e.code() != RPC_E_DISCONNECTED)) + { + throw; + } + } + catch (...) + { + FAIL_FAST(); + } + } + } + + static const auto& storage() + { + return s_apartmentStorage.get(); + } + + static size_t current_apartment_variable_count() + { + auto lock = winrt::slim_lock_guard(s_lock); + if (auto variable_storage = get_current_apartment_variable_storage()) + { + return variable_storage->variables.size(); + } + return 0; + } + }; +} // namespace details +/// @endcond + +// Apartment variables enable storing COM objects safely in globals +// (objects with static storage duration) by creating a unique copy +// in each apartment and managing their lifetime based on apartment rundown +// notifications. +// They can also be used for automatic or dynamic storage duration but those +// cases are less common. +// This type is also useful for storing references to apartment affine objects. +// +// Note, that apartment variables hosted in a COM DLL need to integrate with +// the DllCanUnloadNow() function to include the ref counts contributed by +// C++ WinRT objects. This is automatic for DLLs that host C++ WinRT objects +// but WRL projects will need to be updated to call winrt::get_module_lock(). + +template +struct apartment_variable : details::apartment_variable_base +{ + using base = details::apartment_variable_base; + + constexpr apartment_variable() = default; + + // Get current value or throw if no value has been set. + T& get_existing() + { + return std::any_cast(base::get_existing()); + } + + // Get current value or default-construct one on demand. + T& get_or_create() + { + return std::any_cast(base::get_or_create(details::any_maker())); + } + + // Get current value or custom-construct one on demand. + template + T& get_or_create(F&& f) + { + return std::any_cast(base::get_or_create(details::any_maker(std::forward(f)))); + } + + // get pointer to current value or nullptr if no value has been set + T* get_if() + { + return std::any_cast(base::get_if()); + } + + // replace or create the current value, fail fasts if the value is not already stored + template + void set(V&& value) + { + return base::set(std::forward(value)); + } + + // Clear the value in the current apartment. + using base::clear; + + // Asynchronously clear the value in all apartments it is present in. + using base::clear_all_apartments_async; + + // For testing only. + // 1) To observe the state of the storage in the debugger assign this to + // a temporary variable (const&) and watch its contents. + // 2) Use this to test the implementation. + using base::storage; + // For testing only. The number of variables in the current apartment. + using base::current_apartment_variable_count; +}; +} // namespace wil + +#endif // __WIL_COM_APARTMENT_VARIABLE_INCLUDED diff --git a/libs/wil/wil/common.h b/libs/wil/wil/common.h new file mode 100644 index 00000000..30161999 --- /dev/null +++ b/libs/wil/wil/common.h @@ -0,0 +1,898 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! WIL Common Helpers: Provides broadly applicable, dependency-free pure C++ helpers, macros and type traits. +#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. +/// @cond +#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 +/// @endcond + +// 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. +/// @cond +#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 +/// @endcond + +/// @cond +#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 +/// @endcond + +#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 +// clang-format off +#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__ +// clang-format on +/// @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(__EXCEPTIONS)) && !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 or __EXCEPTIONS 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 + +/// @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 +/// @endcond + +#if WIL_EXCEPTION_MODE == 1 && !defined(WIL_ENABLE_EXCEPTIONS) +#error Must enable exceptions when WIL_EXCEPTION_MODE == 1 +#endif + +/// @cond +#ifndef WIL_ITERATOR_DEBUG_LEVEL +// NOTE: See the definition of 'RESULT_DEBUG' for commentary on the use of 'WIL_KERNEL_MODE' below +#if (DBG || defined(DEBUG) || defined(_DEBUG)) && (defined(WIL_KERNEL_MODE) || !defined(NDEBUG)) +#define WIL_ITERATOR_DEBUG_LEVEL 2 +#else +#define WIL_ITERATOR_DEBUG_LEVEL 0 +#endif +#endif + +#if (WIL_ITERATOR_DEBUG_LEVEL < 0) || (WIL_ITERATOR_DEBUG_LEVEL > 2) +#error Invalid value for 'WIL_ITERATOR_DEBUG_LEVEL'; valid values are 0-2 +#endif + +// To allow code with mis-matching iterator debug levels to link together without fear of ODR issues, we place iterators whose +// definitions differ based on the definition of WIL_ITERATOR_DEBUG_LEVEL in different namespaces +#if WIL_ITERATOR_DEBUG_LEVEL > 0 +#define __WI_ITR_NAMESPACE WI_PASTE(itr, WIL_ITERATOR_DEBUG_LEVEL) +#define __WI_ITR_NAMESPACE_BEGIN \ + inline namespace __WI_ITR_NAMESPACE \ + { +#define __WI_ITR_NAMESPACE_END } +#else +#define __WI_ITR_NAMESPACE +#define __WI_ITR_NAMESPACE_BEGIN +#define __WI_ITR_NAMESPACE_END +#endif +/// @endcond + +// 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 + +/**This define controls the degree of runtime checking for various iterator types defined by WIL. +This option roughly follows the behavior of the MSVC STL's `_ITERATOR_DEBUG_LEVEL` define, with similar available values. The +primary difference (besides being two disjoint values) is that `WIL_ITERATOR_DEBUG_LEVEL` will raise a failfast exception when a +check fails as opposed to the invalid parameter handler that the STL invokes. There are three definitions allowed: +0) This will disable all additional runtime checks for the various iterator types. This is the default when building as 'Release' +1) This enables checks only for unsafe iterator use. This includes things like attempting to increment an iterator past the end, + dereference an end iterator, dereference invalidated iterators, etc. +2) This enables all checks enabled by level 1 plus some additional checks to try and catch invalid iterator use. The specific + checks enabled by this level will vary between iterator types. This is the default when building as 'Debug' +*/ +#define WIL_ITERATOR_DEBUG_LEVEL 0 +#endif + +/// @cond +#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 +/// @endcond + +/// @cond +#define __R_ENABLE_IF_IS_CLASS(ptrType) wistd::enable_if_t::value, void*> = nullptr +#define __R_ENABLE_IF_IS_NOT_CLASS(ptrType) wistd::enable_if_t::value, void*> = nullptr +/// @endcond + +//! @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)) +//! @} // bitwise inspection macros + +//! @} // group bitwise + +#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_INITIALIZATION_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 `#IFDEF`s within functions would create ODR violations, whereas +doing it with global function pointers and header initialization allows a runtime determination. */ +#define WI_HEADER_INITIALIZATION_FUNCTION(name, fn) +#elif defined(_M_IX86) +#define WI_HEADER_INITIALIZATION_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_INITIALIZATION_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 + +// Keep the misspelled name for backward compatibility. +#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn) WI_HEADER_INITIALIZATION_FUNCTION(name, fn) + +/** 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_) + { + } + WI_NODISCARD T begin() const + { + return m_begin; + } + WI_NODISCARD T end() const + { + return m_end; + } + + private: + T m_begin; + T m_end; + }; +} // namespace details +/// @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; + }; +} // namespace details +/// @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) WI_NOEXCEPT +{ + return static_cast(val); +} + +template +__forceinline constexpr bool verify_bool(T /*val*/) WI_NOEXCEPT +{ + 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) WI_NOEXCEPT +{ + return val; +} + +template <> +_Post_satisfies_(return == (val != 0)) __forceinline constexpr bool verify_bool(int val) WI_NOEXCEPT +{ + return (val != 0); +} + +template <> +_Post_satisfies_(return == (val != 0)) __forceinline constexpr bool verify_bool(unsigned char val) WI_NOEXCEPT +{ + return (val != 0); +} + +/** 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) WI_NOEXCEPT +{ + // 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 hr The HRESULT returning expression +@return An HRESULT representing the evaluation of `val`. */ +template +_Post_satisfies_(return == hr) inline constexpr long verify_hresult(T hr) WI_NOEXCEPT +{ + // 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: +@code +#define STATUS_NOT_SUPPORTED 0x1 +@endcode +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: +@code +#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL) +@endcode +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: +@code +NT_RETURN_IF_FALSE(static_cast(STATUS_NOT_SUPPORTED), (dispatch->Version == HKE_V1_0)); +@endcode +@param status The NTSTATUS returning expression +@return An NTSTATUS representing the evaluation of `val`. */ +template +_Post_satisfies_(return == status) inline long verify_ntstatus(T status) WI_NOEXCEPT +{ + // 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; +} + +/** Verify that `error` is a Win32 error code. +Other types will generate an intentional compilation error. Note that this will accept any `long` value as that is +the underlying type used for WIN32 error codes, as well as any `DWORD` (`unsigned long`) value since this is the type +commonly used when manipulating Win32 error codes. +@param error The Win32 error code returning expression +@return An Win32 error code representing the evaluation of `error`. */ +template +_Post_satisfies_(return == error) inline T verify_win32(T error) WI_NOEXCEPT +{ + // Note: Win32 error code are defined as 'long' (#define ERROR_SUCCESS 0L), but are more frequently used as DWORD (unsigned + // long). This accept both types. + static_assert( + wistd::is_same::value || wistd::is_same::value, + "Wrong Type: Win32 error code (long / unsigned long) expected"); + return error; +} +/// @} // 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> + { + using type = unsigned char; + }; + + template <> + struct variable_size<2> + { + using type = unsigned short; + }; + + template <> + struct variable_size<4> + { + using type = unsigned long; + }; + + template <> + struct variable_size<8> + { + using type = unsigned long long; + }; + + template + struct variable_size_mapping + { + using type = typename variable_size::type; + }; +} // namespace 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; + +//! Declares a name that intentionally hides a name from an outer scope. +//! Use this to prevent accidental use of a parameter or lambda captured variable. +using hide_name = void(struct hidden_name); +} // namespace wil + +#pragma warning(pop) + +#endif // __cplusplus +#endif // __WIL_COMMON_INCLUDED diff --git a/libs/wil/wil/coroutine.h b/libs/wil/wil/coroutine.h new file mode 100644 index 00000000..6ce9a058 --- /dev/null +++ b/libs/wil/wil/coroutine.h @@ -0,0 +1,935 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! Types and helpers for using C++ coroutines. +#ifndef __WIL_COROUTINE_INCLUDED +#define __WIL_COROUTINE_INCLUDED + +/* + * A wil::task / com_task is a coroutine with the following characteristics: + * + * - T must be a copyable object, movable object, reference, or void. + * - The coroutine may be awaited at most once. The second await will crash. + * - The coroutine may be abandoned (allowed to destruct without co_await), + * in which case unobserved exceptions are fatal. + * - By default, wil::task resumes on an arbitrary thread. + * - By default, wil::com_task resumes in the same COM apartment. + * - task.resume_any_thread() allows resumption on any thread. + * - task.resume_same_apartment() forces resumption in the same COM apartment. + * + * The wil::task and wil::com_task are intended to supplement PPL and C++/WinRT, + * not to replace them. It provides coroutine implementations for scenarios that PPL + * and C++/WinRT do not support, but it does not support everything that PPL and + * C++/WinRT do. + * + * The implementation is optimized on the assumption that the coroutine is + * awaited only once, and that the coroutine is discarded after completion. + * To ensure proper usage, the task object is move-only, and + * co_await takes ownership of the task. See further discussion below. + * + * Comparison with PPL and C++/WinRT: + * + * | | PPL | C++/WinRT | wil::*task | + * |-----------------------------------------------------|-----------|-----------|---------------| + * | T can be non-constructible | No | Yes | Yes | + * | T can be void | Yes | Yes | Yes | + * | T can be reference | No | No | Yes | + * | T can be WinRT object | Yes | Yes | Yes | + * | T can be non-WinRT object | Yes | No | Yes | + * | T can be move-only | No | No | Yes | + * | Coroutine can be cancelled | Yes | Yes | No | + * | Coroutine can throw arbitrary exceptions | Yes | No | Yes | + * | Can co_await more than once | Yes | No | No | + * | Can have multiple clients waiting for completion | Yes | No | No | + * | co_await resumes in same COM context | Sometimes | Yes | You choose [1]| + * | Can force co_await to resume in same context | Yes | N/A | Yes [1] | + * | Can force co_await to resume in any thread | Yes | No | Yes | + * | Can change coroutine's resumption model | No | No | Yes | + * | Can wait synchronously | Yes | Yes | Yes [2] | + * | Can be consumed by non-C++ languages | No | Yes | No | + * | Implementation is small and efficient | No | Yes | Yes | + * | Can abandon coroutine (fail to co_await) | Yes | Yes | Yes | + * | Exception in abandoned coroutine | Crash | Ignored | Crash | + * | Coroutine starts automatically | Yes | Yes | Yes | + * | Coroutine starts synchronously | No | Yes | Yes | + * | Integrates with C++/WinRT coroutine callouts | No | Yes | No | + * + * [1] Resumption in the same COM apartment requires that you include COM headers. + * [2] Synchronous waiting requires that you include (usually via ). + * + * You can include the COM headers and/or synchapi.h headers, and then + * re-include this header file to activate the features dependent upon + * those headers. + * + * Examples: + * + * Implement a coroutine that returns a move-only non-WinRT type + * and which resumes on an arbitrary thread. + * + * wil::task GetNameAsync() + * { + * co_await resume_background(); // do work on BG thread + * wil::unique_cotaskmem_string name; + * THROW_IF_FAILED(GetNameSlow(&name)); + * co_return name; // awaiter will resume on arbitrary thread + * } + * + * Consumers: + * + * winrt::IAsyncAction UpdateNameAsync() + * { + * // wil::task resumes on an arbitrary thread. + * auto name = co_await GetNameAsync(); + * // could be on any thread now + * co_await SendNameAsync(name.get()); + * } + * + * winrt::IAsyncAction UpdateNameAsync() + * { + * // override default behavior of wil::task and + * // force it to resume in the same COM apartment. + * auto name = co_await GetNameAsync().resume_same_apartment(); + * // so we are still on the UI thread + * NameElement().Text(winrt::hstring(name.get())); + * } + * + * Conversely, a coroutine that returns a + * wil::com_task defaults to resuming in the same + * COM apartment, but you can allow it to resume on any thread + * by doing co_await GetNameAsync().resume_any_thread(). + * + * There is no harm in doing resume_same_apartment() / resume_any_thread() for a + * task that already defaults to resuming in that manner. In fact, awaiting the + * task directly is just a shorthand for awaiting the corresponding + * resume_whatever() method. + * + * Alternatively, you can just convert between wil::task and wil::com_task + * to change the default resumption context. + * + * co_await wil::com_task(GetNameAsync()); // now defaults to resume_same_apartment(); + * + * You can store the task in a variable, but since it is a move-only + * object, you will have to use std::move in order to transfer ownership out of + * an lvalue. + * + * winrt::IAsyncAction SomethingAsync() + * { + * wil::com_task task; + * switch (source) + * { + * // Some of these might return wil::task, + * // but assigning to a wil::com_task will make + * // the task resume in the same COM apartment. + * case widget: task = GetValueFromWidgetAsync(); break; + * case gadget: task = GetValueFromGadgetAsync(); break; + * case doodad: task = GetValueFromDoodadAsync(); break; + * default: FAIL_FAST(); // unknown source + * } + * auto value = co_await std::move(task); // **** need std::move + * DoSomethingWith(value); + * } + * + * You can wait synchronously by calling get(). The usual caveats + * about synchronous waits on STA threads apply. + * + * auto value = GetValueFromWidgetAsync().get(); + * + * auto task = GetValueFromWidgetAsync(); + * auto value = std::move(task).get(); // **** need std::move + */ + +// Detect which version of the coroutine standard we have. +/// @cond +#if defined(_RESUMABLE_FUNCTIONS_SUPPORTED) +#include +#define __WI_COROUTINE_NAMESPACE ::std::experimental +#elif defined(__cpp_impl_coroutine) +#include +#define __WI_COROUTINE_NAMESPACE ::std +#else +#error You must compile with C++20 coroutine support to use coroutine.h. +#endif +/// @endcond + +#include +#include +#include +#include +#include +#include + +namespace wil +{ +// There are three general categories of T that you can +// use with a task. We give them these names: +// +// T = void ("void category") +// T = some kind of reference ("reference category") +// T = non-void non-reference ("object category") +// +// Take care that the implementation supports all three categories. +// +// There is a sub-category of object category for move-only types. +// We designed our task to be co_awaitable only once, so that +// it can contain a move-only type. Any transfer of T as an +// object category must be done as an rvalue reference. +template +struct task; + +template +struct com_task; +} // namespace wil + +/// @cond +namespace wil::details::coro +{ +// task and com_task are convertible to each other. However, not +// all consumers of this header have COM enabled. Support for saving +// COM thread-local error information and restoring it on the resuming +// thread is enabled using these function pointers. If COM is not +// available then they are null and do not get called. If COM is +// enabled then they are filled in with valid pointers and get used. +__declspec(selectany) void*(__stdcall* g_pfnCaptureRestrictedErrorInformation)() WI_PFN_NOEXCEPT = nullptr; +__declspec(selectany) void(__stdcall* g_pfnRestoreRestrictedErrorInformation)(void* restricted_error) WI_PFN_NOEXCEPT = nullptr; +__declspec(selectany) void(__stdcall* g_pfnDestroyRestrictedErrorInformation)(void* restricted_error) WI_PFN_NOEXCEPT = nullptr; + +template +struct task_promise; + +// Unions may not contain references, C++/CX types, or void. +// To work around that, we put everything inside a result_wrapper +// struct, and put the struct in the union. For void, +// we create a special empty structure. +// +// get_value returns rvalue reference to T for object +// category, or just T itself for void and reference +// category. +// +// We take advantage of the reference collapsing rules +// so that T&& = T if T is reference category. + +template +struct result_wrapper +{ + T value; + T get_value() + { + return wistd::forward(value); + } +}; + +template <> +struct result_wrapper +{ + void get_value() + { + } +}; + +// The result_holder is basically a +// std::variant +// but with these extra quirks: +// * The only valid transition is monotype -> something-else. +// Consequently, it does not have valueless_by_exception. + +template +struct result_holder +{ + // The content of the result_holder + // depends on the result_status: + // + // empty: No active member. + // value: Active member is wrap. + // error: Active member is error. + enum class result_status + { + empty, + value, + error + }; + + result_status status{result_status::empty}; + union variant + { + variant() + { + } + ~variant() + { + } + result_wrapper wrap; + std::exception_ptr error; + } result; + + // The restricted error information is lit up when COM headers are + // included. If COM is not available then this will remain null. + // This error information is thread-local so we must save it on suspend + // and restore it on resume so that it propagates to the correct + // thread. It will then be available if the exception proves fatal. + // + // This object is non-copyable so we do not need to worry about + // supporting AddRef on the restricted error information. + void* restricted_error{nullptr}; + + // emplace_value will be called with + // + // * no parameters (void category) + // * The reference type T (reference category) + // * Some kind of reference to T (object category) + // + // Set the status after constructing the object. + // That way, if object construction throws an exception, + // the holder remains empty. + template + void emplace_value(Args&&... args) + { + WI_ASSERT(status == result_status::empty); + new (wistd::addressof(result.wrap)) result_wrapper{wistd::forward(args)...}; + status = result_status::value; + } + + void unhandled_exception() noexcept + { + if (g_pfnCaptureRestrictedErrorInformation) + { + WI_ASSERT(restricted_error == nullptr); + restricted_error = g_pfnCaptureRestrictedErrorInformation(); + } + + WI_ASSERT(status == result_status::empty); + new (wistd::addressof(result.error)) std::exception_ptr(std::current_exception()); + status = result_status::error; + } + + T get_value() + { + if (status == result_status::value) + { + return result.wrap.get_value(); + } + + WI_ASSERT(status == result_status::error); + if (restricted_error && g_pfnRestoreRestrictedErrorInformation) + { + g_pfnRestoreRestrictedErrorInformation(restricted_error); + } + std::rethrow_exception(wistd::exchange(result.error, {})); + } + + result_holder() = default; + result_holder(result_holder const&) = delete; + void operator=(result_holder const&) = delete; + + ~result_holder() noexcept(false) + { + if (restricted_error && g_pfnDestroyRestrictedErrorInformation) + { + g_pfnDestroyRestrictedErrorInformation(restricted_error); + restricted_error = nullptr; + } + + switch (status) + { + case result_status::value: + result.wrap.~result_wrapper(); + break; + case result_status::error: + // Rethrow unobserved exception. Delete this line to + // discard unobserved exceptions. + if (result.error) + std::rethrow_exception(result.error); + result.error.~exception_ptr(); + } + } +}; + +// Most of the work is done in the promise_base, +// It is a CRTP-like base class for task_promise and +// task_promise because the language forbids +// a single promise from containing both return_value and +// return_void methods (even if one of them is deleted by SFINAE). +template +struct promise_base +{ + // The coroutine state remains alive as long as the coroutine is + // still running (hasn't reached final_suspend) or the associated + // task has not yet abandoned the coroutine (either finished awaiting + // or destructed without awaiting). + // + // This saves an allocation, but does mean that the local + // frame of the coroutine will remain allocated (with the + // coroutine's imbound parameters still live) until all + // references are destroyed. To force the promise_base to be + // destroyed after co_await, we make the promise_base a + // move-only object and require co_await to be given an rvalue reference. + + // Special values for m_waiting. + static void* running_ptr() + { + return nullptr; + } + static void* completed_ptr() + { + return reinterpret_cast(1); + } + static void* abandoned_ptr() + { + return reinterpret_cast(2); + } + + // The awaiting coroutine is resumed by calling the + // m_resumer with the m_waiting. If the resumer is null, + // then the m_waiting is assumed to be the address of a + // coroutine_handle<>, which is resumed synchronously. + // Externalizing the resumer allows unused awaiters to be + // removed by the linker and removes a hard dependency on COM. + // Using nullptr to represent the default resumer avoids a + // CFG check. + + void(__stdcall* m_resumer)(void*); + std::atomic m_waiting{running_ptr()}; + result_holder m_holder; + + // Make it easier to access our CRTP derived class. + using Promise = task_promise; + auto as_promise() noexcept + { + return static_cast(this); + } + + // Make it easier to access the coroutine handle. + auto as_handle() noexcept + { + return __WI_COROUTINE_NAMESPACE::coroutine_handle::from_promise(*as_promise()); + } + + auto get_return_object() noexcept + { + // let the compiler construct the task / com_task from the promise. + return as_promise(); + } + + void destroy() + { + as_handle().destroy(); + } + + // The client lost interest in the coroutine, either because they are discarding + // the result without awaiting (risky!), or because they have finished awaiting. + // Discarding the result without awaiting is risky because any exception in the coroutine + // will be unobserved and result in a crash. If you want to disallow it, then + // raise an exception if waiting == running_ptr. + void abandon() + { + auto waiting = m_waiting.exchange(abandoned_ptr(), std::memory_order_acq_rel); + if (waiting != running_ptr()) + destroy(); + } + + __WI_COROUTINE_NAMESPACE::suspend_never initial_suspend() noexcept + { + return {}; + } + + template + void emplace_value(Args&&... args) + { + m_holder.emplace_value(wistd::forward(args)...); + } + + void unhandled_exception() noexcept + { + m_holder.unhandled_exception(); + } + + void resume_waiting_coroutine(void* waiting) const + { + if (m_resumer) + { + m_resumer(waiting); + } + else + { + __WI_COROUTINE_NAMESPACE::coroutine_handle<>::from_address(waiting).resume(); + } + } + + auto final_suspend() noexcept + { + struct awaiter : __WI_COROUTINE_NAMESPACE::suspend_always + { + promise_base& self; + void await_suspend(__WI_COROUTINE_NAMESPACE::coroutine_handle<>) const noexcept + { + // Need acquire so we can read from m_resumer. + // Need release so that the results are published in the case that nobody + // is awaiting right now, so that the eventual awaiter (possibly on another thread) + // can read the results. + auto waiting = self.m_waiting.exchange(completed_ptr(), std::memory_order_acq_rel); + if (waiting == abandoned_ptr()) + { + self.destroy(); + } + else if (waiting != running_ptr()) + { + WI_ASSERT(waiting != completed_ptr()); + self.resume_waiting_coroutine(waiting); + } + }; + }; + return awaiter{{}, *this}; + } + + // The remaining methods are used by the awaiters. + bool client_await_ready() + { + // Need acquire in case the coroutine has already completed, + // so we can read the results. This matches the release in + // the final_suspend's await_suspend. + auto waiting = m_waiting.load(std::memory_order_acquire); + WI_ASSERT((waiting == running_ptr()) || (waiting == completed_ptr())); + return waiting != running_ptr(); + } + + auto client_await_suspend(void* waiting, void(__stdcall* resumer)(void*)) + { + // "waiting" needs to be a pointer to an object. We reserve the first 16 + // pseudo-pointers as sentinels. + WI_ASSERT(reinterpret_cast(waiting) > 16); + + m_resumer = resumer; + + // Acquire to ensure that we can read the results of the return value, if the coroutine is completed. + // Release to ensure that our resumption state is published, if the coroutine is not completed. + auto previous = m_waiting.exchange(waiting, std::memory_order_acq_rel); + + // Suspend if the coroutine is still running. + // Otherwise, the coroutine is completed: Nobody will resume us, so we will have to resume ourselves. + WI_ASSERT((previous == running_ptr()) || (previous == completed_ptr())); + return previous == running_ptr(); + } + + T client_await_resume() + { + return m_holder.get_value(); + } +}; + +template +struct task_promise : promise_base +{ + template + void return_value(U&& value) + { + this->emplace_value(wistd::forward(value)); + } + + template + wistd::enable_if_t, Dummy> return_value(T const& value) + { + this->emplace_value(value); + } +}; + +template <> +struct task_promise : promise_base +{ + void return_void() + { + this->emplace_value(); + } +}; + +template +struct promise_deleter +{ + void operator()(promise_base* promise) const noexcept + { + promise->abandon(); + } +}; + +template +using promise_ptr = wistd::unique_ptr, promise_deleter>; + +template +struct agile_awaiter +{ + agile_awaiter(promise_ptr&& initial) : promise(wistd::move(initial)) + { + } + + promise_ptr promise; + + bool await_ready() + { + return promise->client_await_ready(); + } + + auto await_suspend(__WI_COROUTINE_NAMESPACE::coroutine_handle<> handle) + { + // Use the default resumer. + return promise->client_await_suspend(handle.address(), nullptr); + } + + T await_resume() + { + return promise->client_await_resume(); + } +}; + +template +struct task_base +{ + auto resume_any_thread() && noexcept + { + return agile_awaiter{wistd::move(promise)}; + } + + // You must #include before to enable apartment-aware awaiting. + auto resume_same_apartment() && noexcept; + + // Compiler error message metaprogramming: Tell people that they + // need to use std::move() if they try to co_await an lvalue. + struct cannot_await_lvalue_use_std_move + { + void await_ready() + { + } + }; + cannot_await_lvalue_use_std_move operator co_await() & = delete; + + // You must #include (usually via ) to enable synchronous waiting. + decltype(auto) get() &&; + +protected: + task_base(task_promise* initial = nullptr) noexcept : promise(initial) + { + } + + template + D& assign(D* self, task_base&& other) noexcept + { + static_cast(*this) = wistd::move(other); + return *self; + } + +private: + promise_ptr promise; + + static void __stdcall wake_by_address(void* completed); +}; +} // namespace wil::details::coro +/// @endcond + +namespace wil +{ +// Must write out both classes separately +// Cannot use deduction guides with alias template type prior to C++20. +template +struct task : details::coro::task_base +{ + using base = details::coro::task_base; + // Constructing from task_promise* cannot be explicit because get_return_object relies on implicit conversion. + task(details::coro::task_promise* initial = nullptr) noexcept : base(initial) + { + } + explicit task(base&& other) noexcept : base(wistd::move(other)) + { + } + task& operator=(base&& other) noexcept + { + return base::assign(this, wistd::move(other)); + } + + using base::operator co_await; + + auto operator co_await() && noexcept + { + return wistd::move(*this).resume_any_thread(); + } +}; + +template +struct com_task : details::coro::task_base +{ + using base = details::coro::task_base; + // Constructing from task_promise* cannot be explicit because get_return_object relies on implicit conversion. + com_task(details::coro::task_promise* initial = nullptr) noexcept : base(initial) + { + } + explicit com_task(base&& other) noexcept : base(wistd::move(other)) + { + } + com_task& operator=(base&& other) noexcept + { + return base::assign(this, wistd::move(other)); + } + + using base::operator co_await; + + auto operator co_await() && noexcept + { + // You must #include before to enable non-agile awaiting. + return wistd::move(*this).resume_same_apartment(); + } +}; + +template +task(com_task&&) -> task; +template +com_task(task&&) -> com_task; +} // namespace wil + +template +struct __WI_COROUTINE_NAMESPACE::coroutine_traits, Args...> +{ + using promise_type = wil::details::coro::task_promise; +}; + +template +struct __WI_COROUTINE_NAMESPACE::coroutine_traits, Args...> +{ + using promise_type = wil::details::coro::task_promise; +}; + +#endif // __WIL_COROUTINE_INCLUDED + +// Can re-include this header after including synchapi.h (usually via windows.h) to enable synchronous wait. +#if defined(_SYNCHAPI_H_) && !defined(__WIL_COROUTINE_SYNCHRONOUS_GET_INCLUDED) +#define __WIL_COROUTINE_SYNCHRONOUS_GET_INCLUDED + +namespace wil::details::coro +{ +template +decltype(auto) task_base::get() && +{ + if (!promise->client_await_ready()) + { + bool completed = false; + if (promise->client_await_suspend(&completed, wake_by_address)) + { + bool pending = false; + while (!completed) + { + WaitOnAddress(&completed, &pending, sizeof(pending), INFINITE); + } + } + } + return std::exchange(promise, {})->client_await_resume(); +} + +template +void __stdcall task_base::wake_by_address(void* completed) +{ + *reinterpret_cast(completed) = true; + WakeByAddressSingle(completed); +} +} // namespace wil::details::coro +#endif // __WIL_COROUTINE_SYNCHRONOUS_GET_INCLUDED + +// Can re-include this header after including COM header files to enable non-agile tasks. +#if defined(_COMBASEAPI_H_) && defined(_THREADPOOLAPISET_H_) && !defined(__WIL_COROUTINE_NON_AGILE_INCLUDED) +#define __WIL_COROUTINE_NON_AGILE_INCLUDED +#include +#include +#include + +namespace wil::details::coro +{ +inline void* __stdcall CaptureRestrictedErrorInformation() noexcept +{ + IRestrictedErrorInfo* restrictedError = nullptr; + (void)GetRestrictedErrorInfo(&restrictedError); + return restrictedError; // the returned object includes a strong reference +} + +inline void __stdcall RestoreRestrictedErrorInformation(_In_ void* restricted_error) noexcept +{ + (void)SetRestrictedErrorInfo(static_cast(restricted_error)); +} + +inline void __stdcall DestroyRestrictedErrorInformation(_In_ void* restricted_error) noexcept +{ + static_cast(restricted_error)->Release(); +} + +struct apartment_info +{ + APTTYPE aptType{}; + APTTYPEQUALIFIER aptTypeQualifier{}; + + void load() + { + if (FAILED(CoGetApartmentType(&aptType, &aptTypeQualifier))) + { + // If COM is not initialized, then act as if we are running + // on the implicit MTA. + aptType = APTTYPE_MTA; + aptTypeQualifier = APTTYPEQUALIFIER_IMPLICIT_MTA; + } + } +}; + +// apartment_resumer resumes a coroutine in a captured apartment. +struct apartment_resumer +{ + static auto as_self(void* p) + { + return reinterpret_cast(p); + } + + static bool is_sta() + { + apartment_info info; + info.load(); + switch (info.aptType) + { + case APTTYPE_STA: + case APTTYPE_MAINSTA: + return true; + case APTTYPE_NA: + return info.aptTypeQualifier == APTTYPEQUALIFIER_NA_ON_STA || info.aptTypeQualifier == APTTYPEQUALIFIER_NA_ON_MAINSTA; + default: + return false; + } + } + + static wil::com_ptr current_context() + { + wil::com_ptr context; + // This will fail if COM is not initialized. Treat as implicit MTA. + // Do not use IID_PPV_ARGS to avoid ambiguity between ::IUnknown and winrt::IUnknown. + CoGetObjectContext(__uuidof(IContextCallback), reinterpret_cast(&context)); + return context; + } + + __WI_COROUTINE_NAMESPACE::coroutine_handle<> waiter; + wil::com_ptr context{nullptr}; + apartment_info info{}; + HRESULT resume_result = S_OK; + + void capture_context(__WI_COROUTINE_NAMESPACE::coroutine_handle<> handle) + { + waiter = handle; + info.load(); + context = current_context(); + if (context == nullptr) + { + __debugbreak(); + } + } + + static void __stdcall resume_in_context(void* parameter) + { + auto self = as_self(parameter); + if (self->context == nullptr || self->context == current_context()) + { + self->context = nullptr; // removes the context cleanup from the resume path + self->waiter(); + } + else if (is_sta()) + { + submit_threadpool_callback(resume_context, self); + } + else + { + self->resume_context_sync(); + } + } + + static void submit_threadpool_callback(PTP_SIMPLE_CALLBACK callback, void* context) + { + THROW_IF_WIN32_BOOL_FALSE(TrySubmitThreadpoolCallback(callback, context, nullptr)); + } + + static void CALLBACK resume_context(PTP_CALLBACK_INSTANCE /*instance*/, void* parameter) + { + as_self(parameter)->resume_context_sync(); + } + + void resume_context_sync() + { + ComCallData data{}; + data.pUserDefined = this; + // The call to resume_apartment_callback will destruct the context. + // Capture into a local so we don't destruct it while it's in use. + // This also removes the context cleanup from the resume path. + auto local_context = wistd::move(context); + auto result = + local_context->ContextCallback(resume_apartment_callback, &data, IID_ICallbackWithNoReentrancyToApplicationSTA, 5, nullptr); + if (FAILED(result)) + { + // Unable to resume on the correct apartment. + // Resume on the wrong apartment, but tell the coroutine why. + resume_result = result; + waiter(); + } + } + + static HRESULT CALLBACK resume_apartment_callback(ComCallData* data) noexcept + { + as_self(data->pUserDefined)->waiter(); + return S_OK; + } + + void check() + { + THROW_IF_FAILED(resume_result); + } +}; + +// The COM awaiter captures the COM context when the co_await begins. +// When the co_await completes, it uses that COM context to resume execution. +// This follows the same algorithm employed by C++/WinRT, which has features like +// avoiding stack buildup and proper handling of the neutral apartment. +// It does, however, introduce fail-fast code paths if thread pool tasks cannot +// be submitted. (Those fail-fasts could be removed by preallocating the tasks, +// but that means paying an up-front cost for something that may never end up used, +// as well as introducing extra cleanup code in the fast-path.) +template +struct com_awaiter : agile_awaiter +{ + com_awaiter(promise_ptr&& initial) : agile_awaiter(wistd::move(initial)) + { + } + apartment_resumer resumer; + + auto await_suspend(__WI_COROUTINE_NAMESPACE::coroutine_handle<> handle) + { + resumer.capture_context(handle); + return this->promise->client_await_suspend(wistd::addressof(resumer), apartment_resumer::resume_in_context); + } + + decltype(auto) await_resume() + { + resumer.check(); + return agile_awaiter::await_resume(); + } +}; + +template +auto task_base::resume_same_apartment() && noexcept +{ + return com_awaiter{wistd::move(promise)}; +} +} // namespace wil::details::coro + +// This section is lit up when COM headers are available. Initialize the global function +// pointers such that error information can be saved and restored across thread boundaries. +WI_HEADER_INITIALIZATION_FUNCTION(CoroutineRestrictedErrorInitialize, [] { + ::wil::details::coro::g_pfnCaptureRestrictedErrorInformation = ::wil::details::coro::CaptureRestrictedErrorInformation; + ::wil::details::coro::g_pfnRestoreRestrictedErrorInformation = ::wil::details::coro::RestoreRestrictedErrorInformation; + ::wil::details::coro::g_pfnDestroyRestrictedErrorInformation = ::wil::details::coro::DestroyRestrictedErrorInformation; + return 1; +}) + +#endif // __WIL_COROUTINE_NON_AGILE_INCLUDED diff --git a/libs/wil/wil/cppwinrt.h b/libs/wil/wil/cppwinrt.h new file mode 100644 index 00000000..43570d74 --- /dev/null +++ b/libs/wil/wil/cppwinrt.h @@ -0,0 +1,499 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! WIL Error Handling Helpers: support for interoperability between WIL and C++/WinRT exception-to-HRESULT logic. +#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.221104.6" +inline constexpr int version_from_string(const char* versionString) +{ + int result = 0; + while ((*versionString >= '0') && (*versionString <= '9')) + { + result = result * 10 + (*versionString - '0'); + ++versionString; + } + + 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) +{ + int dotCount = 0; + while ((*versionString != '\0')) + { + if (*versionString == '.') + { + ++dotCount; + } + + ++versionString; + if (dotCount == 2) + { + return version_from_string(versionString); + } + } + + return 0; +} +} // namespace wil::details +/// @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 +/// @cond +#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 +/// @endcond + +#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 + +/// @cond +// 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; +/// @endcond + +/// @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; +} +} // namespace wil::details +/// @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::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_INITIALIZATION_FUNCTION(WilInitialize_CppWinRT, [] { + ::wil::WilInitialize_CppWinRT(); + return 1; + }); +#else + WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "1") +#endif +} // namespace details +/// @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)); +} + +inline auto str_raw_ptr(const winrt::hstring& str) noexcept +{ + return str.c_str(); +} + +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; +} + +// For obtaining an object from an interop method on the factory. Example: +// winrt::InputPane inputPane = wil::capture_interop(&IInputPaneInterop::GetForWindow, hwnd); +// If the method produces something different from the factory type: +// winrt::IAsyncAction action = wil::capture_interop(&IAccountsSettingsPaneInterop::ShowAddAccountForWindow, hwnd); +template +auto capture_interop(HRESULT (__stdcall Interface::*method)(InterfaceArgs...), Args&&... args) +{ + auto interop = winrt::get_activation_factory(); + return winrt::capture(interop, method, std::forward(args)...); +} + +// For obtaining an object from an interop method on an instance. Example: +// winrt::UserActivitySession session = wil::capture_interop(activity, &IUserActivityInterop::CreateSessionForWindow, hwnd); +template +auto capture_interop(winrt::Windows::Foundation::IUnknown const& o, HRESULT (__stdcall Interface::*method)(InterfaceArgs...), Args&&... args) +{ + return winrt::capture(o.as(), method, std::forward(args)...); +} + +/** Holds a reference to the host C++/WinRT module to prevent it from being unloaded. +Normally, this is done by being in an IAsyncOperation coroutine or by holding a strong +reference to a C++/WinRT object hosted in the same module, but if you have neither, +you will need to hold a reference explicitly. For the WRL equivalent, see wrl_module_reference. + +This can be used as a base, which permits EBO: +@code +struct NonWinrtObject : wil::winrt_module_reference +{ + int value; +}; + +// DLL will not be unloaded as long as NonWinrtObject is still alive. +auto p = std::make_unique(); +@endcode + +Or it can be used as a member (with [[no_unique_address]] to avoid +occupying any memory): +@code +struct NonWinrtObject +{ + int value; + + [[no_unique_address]] wil::winrt_module_reference module_ref; +}; + +// DLL will not be unloaded as long as NonWinrtObject is still alive. +auto p = std::make_unique(); +@endcode + +If using it to prevent the host DLL from unloading while a thread +or threadpool work item is still running, create the object before +starting the thread, and pass it to the thread. This avoids a race +condition where the host DLL could get unloaded before the thread starts. +@code +std::thread([module_ref = wil::winrt_module_reference()]() { do_background_work(); }); + +// Don't do this (race condition) +std::thread([]() { wil::winrt_module_reference module_ref; do_background_work(); }); // WRONG +@endcode + +Also useful in coroutines that neither capture DLL-hosted COM objects, nor are themselves +DLL-hosted COM objects. (If the coroutine returns IAsyncAction or captures a get_strong() +of its containing WinRT class, then the IAsyncAction or strong reference will itself keep +a strong reference to the host module.) +@code +winrt::fire_and_forget ContinueBackgroundWork() +{ + // prevent DLL from unloading while we are running on a background thread. + // Do this before switching to the background thread. + wil::winrt_module_reference module_ref; + + co_await winrt::resume_background(); + do_background_work(); +}; +@endcode +*/ +struct [[nodiscard]] winrt_module_reference +{ + winrt_module_reference() + { + ++winrt::get_module_lock(); + } + + winrt_module_reference(winrt_module_reference const&) : winrt_module_reference() + { + } + + ~winrt_module_reference() + { + --winrt::get_module_lock(); + } +}; + +/** Implements a C++/WinRT class where some interfaces are conditionally supported. +@code +// Assume the existence of a class "Version2" which says whether +// the IMyThing2 interface should be supported. +struct Version2 { static bool IsEnabled(); }; + +// Declare implementation class which conditionally supports IMyThing2. +struct MyThing : wil::winrt_conditionally_implements, + Version2, IMyThing2> +{ + // implementation goes here +}; + +@endcode + +If `Version2::IsEnabled()` returns `false`, then the `QueryInterface` +for `IMyThing2` will fail. + +Any interface not listed as conditional is assumed to be enabled unconditionally. + +You can add additional Version / Interface pairs to the template parameter list. +Interfaces may be conditionalized on at most one Version class. If you need a +complex conditional, create a new helper class. + +@code +// Helper class for testing two Versions. +struct Version2_or_greater { + static bool IsEnabled() { return Version2::IsEnabled() || Version3::IsEnabled(); } +}; + +// This implementation supports IMyThing2 if either Version2 or Version3 is enabled, +// and supports IMyThing3 only if Version3 is enabled. +struct MyThing : wil::winrt_conditionally_implements, +Version2_or_greater, IMyThing2, Version3, IMyThing3> +{ + // implementation goes here +}; +@endcode +*/ +template +struct winrt_conditionally_implements : Implements +{ + using Implements::Implements; + + void* find_interface(winrt::guid const& iid) const noexcept override + { + static_assert(sizeof...(Rest) % 2 == 0, "Extra template parameters should come in groups of two"); + if (is_enabled<0, std::tuple>(iid)) + { + return Implements::find_interface(iid); + } + return nullptr; + } + +private: + template + static bool is_enabled(winrt::guid const& iid) + { + if constexpr (index >= std::tuple_size_v) + { + return true; + } + else + { + check_no_duplicates<1, index + 1, Tuple>(); + return (iid == winrt::guid_of>()) ? std::tuple_element_t::IsEnabled() + : is_enabled(iid); + } + } + + template + static constexpr void check_no_duplicates() + { + if constexpr (index < upto) + { + static_assert( + !std::is_same_v, std::tuple_element_t>, + "Duplicate interfaces found in winrt_conditionally_implements"); + check_no_duplicates(); + } + } +}; +} // namespace wil + +#endif // __WIL_CPPWINRT_INCLUDED diff --git a/libs/wil/wil/cppwinrt_authoring.h b/libs/wil/wil/cppwinrt_authoring.h new file mode 100644 index 00000000..99819688 --- /dev/null +++ b/libs/wil/wil/cppwinrt_authoring.h @@ -0,0 +1,334 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! Helpers that make authoring C++/WinRT components easier. + +namespace wil +{ +#ifndef __WIL_CPPWINRT_AUTHORING_PROPERTIES_INCLUDED +/// @cond +#define __WIL_CPPWINRT_AUTHORING_PROPERTIES_INCLUDED +namespace details +{ + template + struct single_threaded_property_storage + { + T m_value{}; + single_threaded_property_storage() = default; + single_threaded_property_storage(const T& value) : m_value(value) + { + } + operator T&() + { + return m_value; + } + operator T const&() const + { + return m_value; + } + template + auto operator=(Q&& q) + { + m_value = wistd::forward(q); + return *this; + } + }; +} // namespace details +/// @endcond + +template +struct single_threaded_property + : std::conditional_t || std::is_final_v, wil::details::single_threaded_property_storage, T> +{ + single_threaded_property() = default; + template + single_threaded_property(TArgs&&... value) : base_type(std::forward(value)...) + { + } + + using base_type = + std::conditional_t || std::is_final_v, wil::details::single_threaded_property_storage, T>; + + T operator()() const + { + return *this; + } + + // This is the only setter exposed. We don't expose `operator()(Q&& q)`, + // since that is what C++/WinRT uses to implement public setters. Since + // single_threaded_property is intended for readonly properties, we + // don't want to expose that. + // + // To set the value of this property *internally* (within your + // implementation), use this `operator=`: + // + // MyProperty = 42; + // // MyProperty(42); // won't work + // + // For settable properties, use single_threaded_rw_property instead. + template + auto& operator=(Q&& q) + { + static_cast(*this) = std::forward(q); + return *this; + } +}; + +template +struct single_threaded_rw_property : single_threaded_property +{ + using base_type = single_threaded_property; + template + single_threaded_rw_property(TArgs&&... value) : base_type(std::forward(value)...) + { + } + + using base_type::operator(); + + // needed in lieu of deducing-this + template + auto& operator()(Q&& q) + { + return *this = std::forward(q); + } + + // needed in lieu of deducing-this + template + auto& operator=(Q&& q) + { + base_type::operator=(std::forward(q)); + return *this; + } +}; + +#endif // __WIL_CPPWINRT_AUTHORING_PROPERTIES_INCLUDED + +#if (!defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_FOUNDATION) && defined(WINRT_Windows_Foundation_H)) || \ + defined(WIL_DOXYGEN) // WinRT / XAML helpers +/// @cond +#define __WIL_CPPWINRT_AUTHORING_INCLUDED_FOUNDATION +namespace details +{ + template + struct event_base + { + winrt::event_token operator()(const T& handler) + { + return m_handler.add(handler); + } + + auto operator()(const winrt::event_token& token) noexcept + { + return m_handler.remove(token); + } + + template + auto invoke(TArgs&&... args) + { + return m_handler(std::forward(args)...); + } + + private: + winrt::event m_handler; + }; +} // namespace details +/// @endcond + +/** + * @brief A default event handler that maps to + * [Windows.Foundation.EventHandler](https://docs.microsoft.com/uwp/api/windows.foundation.eventhandler-1). + * @tparam T The event data type. + */ +template +struct untyped_event : wil::details::event_base> +{ +}; + +/** + * @brief A default event handler that maps to + * [Windows.Foundation.TypedEventHandler](https://docs.microsoft.com/uwp/api/windows.foundation.typedeventhandler-2). + * @tparam T The event data type. + * @details Usage example: + * @code + * // In IDL, this corresponds to: + * // event Windows.Foundation.TypedEventHandler OkClicked; + * wil::typed_event OkClicked; + * @endcode + */ +template +struct typed_event : wil::details::event_base> +{ +}; + +#endif // !defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_FOUNDATION) && defined(WINRT_Windows_Foundation_H) + +#if (!defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_XAML_DATA) && (defined(WINRT_Microsoft_UI_Xaml_Data_H) || defined(WINRT_Windows_UI_Xaml_Data_H))) || \ + defined(WIL_DOXYGEN) // INotifyPropertyChanged helpers +/// @cond +#define __WIL_CPPWINRT_AUTHORING_INCLUDED_XAML_DATA +namespace details +{ +#ifdef WINRT_Microsoft_UI_Xaml_Data_H + using Xaml_Data_PropertyChangedEventHandler = winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventHandler; + using Xaml_Data_PropertyChangedEventArgs = winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventArgs; +#elif defined(WINRT_Windows_UI_Xaml_Data_H) + using Xaml_Data_PropertyChangedEventHandler = winrt::Windows::UI::Xaml::Data::PropertyChangedEventHandler; + using Xaml_Data_PropertyChangedEventArgs = winrt::Windows::UI::Xaml::Data::PropertyChangedEventArgs; +#endif +} // namespace details +/// @endcond + +/** + * @brief Helper base class to inherit from to have a simple implementation of + * [INotifyPropertyChanged](https://docs.microsoft.com/uwp/api/windows.ui.xaml.data.inotifypropertychanged). + * @tparam T CRTP type + * @details When you declare your class, make this class a base class and pass your class as a template parameter: + * @code + * struct MyPage : MyPageT, wil::notify_property_changed_base + * { + * wil::single_threaded_notifying_property MyInt; + * MyPage() : INIT_NOTIFYING_PROPERTY(MyInt, 42) { } + * // or + * WIL_NOTIFYING_PROPERTY(int, MyInt, 42); + * }; + * @endcode + */ +template +struct notify_property_changed_base +{ + using Type = T; + auto PropertyChanged(Xaml_Data_PropertyChangedEventHandler const& value) + { + return m_propertyChanged.add(value); + } + + void PropertyChanged(winrt::event_token const& token) + { + m_propertyChanged.remove(token); + } + + Type& self() + { + return *static_cast(this); + } + + /** + * @brief Raises a property change notification event + * @param name The name of the property + * @return + * @details Usage example\n + * C++ + * @code + * void MyPage::DoSomething() + * { + * // modify MyInt + * // MyInt = ... + * + * // now send a notification to update the bound UI elements + * RaisePropertyChanged(L"MyInt"); + * } + * @endcode + */ + auto RaisePropertyChanged(std::wstring_view name) + { + return m_propertyChanged(self(), Xaml_Data_PropertyChangedEventArgs{name}); + } + +protected: + winrt::event m_propertyChanged; +}; + +/** + * @brief Implements a property type with notifications + * @tparam T the property type + * @details Use the INIT_NOTIFY_PROPERTY macro to initialize this property in your class constructor. This will set up the + * right property name, and bind it to the `notify_property_changed_base` implementation. + */ +template +struct single_threaded_notifying_property : single_threaded_rw_property +{ + using Type = T; + using base_type = single_threaded_rw_property; + using base_type::operator(); + + template + auto& operator()(Q&& q) + { + return *this = std::forward(q); + } + + template + auto& operator=(Q&& q) + { + if (q != this->operator()()) + { + static_cast(*this) = std::forward(q); + if (auto strong = m_sender.get(); (m_npc != nullptr) && (strong != nullptr)) + { + (*m_npc)(strong, Xaml_Data_PropertyChangedEventArgs{m_name}); + } + } + return *this; + } + + template + single_threaded_notifying_property( + winrt::event* npc, + const winrt::Windows::Foundation::IInspectable& sender, + std::wstring_view name, + TArgs&&... args) : + single_threaded_rw_property(std::forward(args)...), m_name(name), m_npc(npc), m_sender(sender) + { + } + + single_threaded_notifying_property(const single_threaded_notifying_property&) = default; + single_threaded_notifying_property(single_threaded_notifying_property&&) = default; + std::wstring_view Name() const noexcept + { + return m_name; + } + +private: + std::wstring_view m_name; + winrt::event* m_npc; + winrt::weak_ref m_sender; +}; + +/** + * @def WIL_NOTIFYING_PROPERTY + * @brief use this to stamp out a property that calls RaisePropertyChanged upon changing its value. This is a zero-storage + * alternative to wil::single_threaded_notifying_property. + * @details You can pass an initializer list for the initial property value in the variadic arguments to this macro. + */ +#define WIL_NOTIFYING_PROPERTY(type, name, ...) \ + type m_##name{__VA_ARGS__}; \ + auto name() const noexcept \ + { \ + return m_##name; \ + } \ + auto& name(type value) \ + { \ + if (m_##name != value) \ + { \ + m_##name = std::move(value); \ + RaisePropertyChanged(L"" #name); \ + } \ + return *this; \ + } + +/** + * @def INIT_NOTIFYING_PROPERTY + * @brief use this to initialize a wil::single_threaded_notifying_property in your class constructor. + */ +#define INIT_NOTIFYING_PROPERTY(NAME, VALUE) NAME(&m_propertyChanged, *this, L"" #NAME, VALUE) + +#endif // !defined(__WIL_CPPWINRT_AUTHORING_INCLUDED_XAML_DATA) && (defined(WINRT_Microsoft_UI_Xaml_Data_H) || defined(WINRT_Windows_UI_Xaml_Data_H)) +} // namespace wil diff --git a/libs/wil/wil/cppwinrt_helpers.h b/libs/wil/wil/cppwinrt_helpers.h new file mode 100644 index 00000000..6fdb1405 --- /dev/null +++ b/libs/wil/wil/cppwinrt_helpers.h @@ -0,0 +1,393 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! Helpers for common patterns and tasks when using C++/WinRT. + +#ifndef __WIL_CPPWINRT_HELPERS_DEFINED +#define __WIL_CPPWINRT_HELPERS_DEFINED + +/// @cond +namespace wil::details +{ +struct dispatcher_RunAsync +{ + template + static void Schedule(Dispatcher const& dispatcher, Args&&... args) + { + dispatcher.RunAsync(std::forward(args)...); + } +}; + +struct dispatcher_TryEnqueue +{ + template + static void Schedule(Dispatcher const& dispatcher, Args&&... args) + { + dispatcher.TryEnqueue(std::forward(args)...); + } +}; + +template +struct dispatcher_traits; +} // namespace wil::details + +#if defined(_RESUMABLE_FUNCTIONS_SUPPORTED) +#include +namespace wil::details +{ +template +using coroutine_handle = std::experimental::coroutine_handle; +} +#elif defined(__cpp_impl_coroutine) +#include +#if (__cpp_lib_coroutine >= 201902L) +namespace wil::details +{ +template +using coroutine_handle = std::coroutine_handle; +} +#endif // __cpp_lib_coroutine +#endif // __cpp_impl_coroutine +/// @endcond + +#if defined(_RESUMABLE_FUNCTIONS_SUPPORTED) || \ + (defined(__cpp_impl_coroutine) && defined(__cpp_lib_coroutine) && (__cpp_lib_coroutine >= 201902L)) +/// @cond +namespace wil::details +{ +struct dispatched_handler_state +{ + details::coroutine_handle<> handle{}; + bool orphaned = false; +}; + +struct dispatcher_handler +{ + dispatcher_handler(dispatched_handler_state* state) : m_state(state) + { + } + dispatcher_handler(dispatcher_handler&& other) noexcept : m_state(std::exchange(other.m_state, {})) + { + } + + ~dispatcher_handler() + { + if (m_state && m_state->handle) + { + m_state->orphaned = true; + Complete(); + } + } + void operator()() + { + Complete(); + } + + void Complete() + { + auto state = std::exchange(m_state, nullptr); + std::exchange(state->handle, {}).resume(); + } + + dispatched_handler_state* m_state; +}; +} // namespace wil::details +/// @endcond + +namespace wil +{ +//! Resumes coroutine execution on the thread associated with the dispatcher, or throws +//! an exception (from an arbitrary thread) if unable. Supported dispatchers are +//! Windows.System.DispatcherQueue, Microsoft.System.DispatcherQueue, +//! Microsoft.UI.Dispatching.DispatcherQueue, and Windows.UI.Core.CoreDispatcher, +//! but you must include the corresponding header before including +//! wil/cppwinrt_helpers.h. It is okay to include wil/cppwinrt_helpers.h multiple times: +//! support will be enabled for any winrt/Namespace.h headers that were included since +//! the previous inclusion of wil/cppwinrt_headers.h. +template +[[nodiscard]] auto resume_foreground( + Dispatcher const& dispatcher, + typename details::dispatcher_traits::Priority priority = details::dispatcher_traits::Priority::Normal) +{ + using Traits = details::dispatcher_traits; + using Priority = typename Traits::Priority; + using Handler = typename Traits::Handler; + + struct awaitable + { + awaitable(Dispatcher const& dispatcher, Priority priority) noexcept : m_dispatcher(dispatcher), m_priority(priority) + { + } + bool await_ready() const noexcept + { + return false; + } + + void await_suspend(details::coroutine_handle<> handle) + { + m_state.handle = handle; + Handler handler{details::dispatcher_handler(&m_state)}; + try + { + // The return value of Schedule is not reliable. Use the dispatcher_handler destructor + // to detect whether the work item failed to run. + Traits::Scheduler::Schedule(m_dispatcher, m_priority, handler); + } + catch (...) + { + m_state.handle = nullptr; // the exception will resume the coroutine, so the handler shouldn't do it + throw; + } + } + + void await_resume() const + { + if (m_state.orphaned) + { + throw winrt::hresult_error(static_cast(0x800701ab)); // HRESULT_FROM_WIN32(ERROR_NO_TASK_QUEUE) + } + } + + private: + Dispatcher const& m_dispatcher; + Priority const m_priority; + details::dispatched_handler_state m_state; + }; + return awaitable{dispatcher, priority}; +} +} // namespace wil +#endif // Coroutines are supported + +#endif // __WIL_CPPWINRT_HELPERS_DEFINED + +/// @cond +#if defined(WINRT_Windows_UI_Core_H) && !defined(__WIL_CPPWINRT_WINDOWS_UI_CORE_HELPERS) +#define __WIL_CPPWINRT_WINDOWS_UI_CORE_HELPERS +namespace wil::details +{ +template <> +struct dispatcher_traits +{ + using Priority = winrt::Windows::UI::Core::CoreDispatcherPriority; + using Handler = winrt::Windows::UI::Core::DispatchedHandler; + using Scheduler = dispatcher_RunAsync; +}; +} // namespace wil::details +#endif // __WIL_CPPWINRT_WINDOWS_UI_CORE_HELPERS + +#if defined(WINRT_Windows_System_H) && !defined(__WIL_CPPWINRT_WINDOWS_SYSTEM_HELPERS) +#define __WIL_CPPWINRT_WINDOWS_SYSTEM_HELPERS +namespace wil::details +{ +template <> +struct dispatcher_traits +{ + using Priority = winrt::Windows::System::DispatcherQueuePriority; + using Handler = winrt::Windows::System::DispatcherQueueHandler; + using Scheduler = dispatcher_TryEnqueue; +}; +} // namespace wil::details +#endif // __WIL_CPPWINRT_WINDOWS_SYSTEM_HELPERS + +#if defined(WINRT_Microsoft_System_H) && !defined(__WIL_CPPWINRT_MICROSOFT_SYSTEM_HELPERS) +#define __WIL_CPPWINRT_MICROSOFT_SYSTEM_HELPERS +namespace wil::details +{ +template <> +struct dispatcher_traits +{ + using Priority = winrt::Microsoft::System::DispatcherQueuePriority; + using Handler = winrt::Microsoft::System::DispatcherQueueHandler; + using Scheduler = dispatcher_TryEnqueue; +}; +} // namespace wil::details +#endif // __WIL_CPPWINRT_MICROSOFT_SYSTEM_HELPERS + +#if defined(WINRT_Microsoft_UI_Dispatching_H) && !defined(__WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS) +#define __WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS +namespace wil::details +{ +template <> +struct dispatcher_traits +{ + using Priority = winrt::Microsoft::UI::Dispatching::DispatcherQueuePriority; + using Handler = winrt::Microsoft::UI::Dispatching::DispatcherQueueHandler; + using Scheduler = dispatcher_TryEnqueue; +}; +} // namespace wil::details +#endif // __WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS +/// @endcond + +#if (defined(WINRT_Windows_Foundation_Collections_H) && !defined(__WIL_CPPWINRT_WINDOWS_FOUNDATION_COLLECTION_HELPERS)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL_CPPWINRT_WINDOWS_FOUNDATION_COLLECTION_HELPERS +/// @endcond +namespace wil +{ +/// @cond +namespace details +{ + template + struct is_winrt_vector_like + { + private: + template < + typename U, + typename = decltype(std::declval().GetMany(std::declval().Size(), winrt::array_view().GetAt(0))>{}))> + static constexpr bool get_value(int) + { + return true; + } + template + static constexpr bool get_value(...) + { + return false; + } + + public: + static constexpr bool value = get_value(0); + }; + + template + struct is_winrt_iterator_like + { + private: + template ().GetMany(winrt::array_view().Current())>{}))> + static constexpr bool get_value(int) + { + return true; + } + template + static constexpr bool get_value(...) + { + return false; + } + + public: + static constexpr bool value = get_value(0); + }; + + template + constexpr T empty() noexcept + { + if constexpr (std::is_base_of_v) + { + return nullptr; + } + else + { + return {}; + } + } +} // namespace details +/// @endcond + +/** Converts C++ / WinRT vectors, iterators, and iterables to std::vector by requesting the +collection's data in bulk. This can be more efficient in terms of IPC cost than iteratively +processing the collection. +@code +winrt::IVector collection = GetCollection(); +std::vector allData = wil::to_vector(collection); // read all data from collection +for (winrt::hstring const& item : allData) +{ + // use item +} +@endcode +Can be used for IVector, IVectorView, IIterable, IIterator, and any type or +interface that C++/WinRT projects those interfaces for (PropertySet, IMap, etc.) +Iterable-only types fetch content in units of 64. When used with an iterator, the returned +vector contains the iterator's current position and any others after it. +*/ +template +auto to_vector(TSrc const& src) +{ + if constexpr (details::is_winrt_vector_like::value) + { + using T = decltype(src.GetAt(0)); + std::vector result; + if (auto expected = src.Size()) + { + result.resize(expected + 1, details::empty()); + auto actual = src.GetMany(0, result); + if (actual > expected) + { + throw winrt::hresult_changed_state(); + } + result.resize(actual, details::empty()); + } + return result; + } + else if constexpr (details::is_winrt_iterator_like::value) + { + using T = decltype(src.Current()); + std::vector result; + constexpr uint32_t chunkSize = 64; + while (true) + { + auto const lastSize = result.size(); + result.resize(lastSize + chunkSize, details::empty()); + auto fetched = src.GetMany({result.data() + lastSize, result.data() + lastSize + chunkSize}); + if (fetched < chunkSize) + { + result.resize(lastSize + fetched, details::empty()); + break; + } + } + return result; + } + else + { + return to_vector(src.First()); + } +} +} // namespace wil +#endif + +#if (defined(WINRT_Windows_UI_H) && defined(_WINDOWS_UI_INTEROP_H_) && !defined(__WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS +/// @endcond +#if !defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) && !defined(MIDL_NS_PREFIX) +#pragma push_macro("ABI") +#undef ABI +#define ABI +#endif + +namespace wil +{ +#if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_WIN10_CU) +//! The following methods require that you include both +//! before including wil/cppwinrt_helpers.h, and that NTDDI_VERSION +//! is at least NTDDI_WIN10_CU. It is okay to include wil/cppwinrt_helpers.h multiple times: +//! support will be enabled for any headers that were included since the previous inclusion +//! of wil/cppwinrt_headers.h. +inline winrt::Windows::UI::WindowId GetWindowIdFromWindow(HWND hwnd) +{ + ABI::Windows::UI::WindowId abiWindowId; + winrt::check_hresult(::GetWindowIdFromWindow(hwnd, &abiWindowId)); + return winrt::Windows::UI::WindowId{abiWindowId.Value}; +} + +inline HWND GetWindowFromWindowId(winrt::Windows::UI::WindowId windowId) +{ + HWND hwnd; + winrt::check_hresult(::GetWindowFromWindowId({windowId.Value}, &hwnd)); + return hwnd; +} +#endif /*defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_WIN10_CU)*/ +} // namespace wil + +#if !defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) && !defined(MIDL_NS_PREFIX) +#pragma pop_macro("ABI") +#endif +#endif // __WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS diff --git a/libs/wil/wil/cppwinrt_notifiable_module_lock.h b/libs/wil/wil/cppwinrt_notifiable_module_lock.h new file mode 100644 index 00000000..658006b9 --- /dev/null +++ b/libs/wil/wil/cppwinrt_notifiable_module_lock.h @@ -0,0 +1,104 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! Utilities for implementing OOP COM server with cppwinrt only + +#ifndef __WIL_CPPWINRT_NOTIFIABLE_MODULE_LOCK_INCLUDED +#define __WIL_CPPWINRT_NOTIFIABLE_MODULE_LOCK_INCLUDED + +#ifdef WINRT_BASE_H +#error You must include this header before including any winrt header +#endif + +#ifndef WINRT_CUSTOM_MODULE_LOCK +#error You must define 'WINRT_CUSTOM_MODULE_LOCK' at the project level +#endif + +#include +#include + +namespace wil +{ +// Adopted from cppwinrt +struct notifiable_module_lock_base +{ + notifiable_module_lock_base() = default; + + notifiable_module_lock_base(uint32_t count) : m_count(count) + { + } + + uint32_t operator=(uint32_t count) noexcept + { + return m_count = count; + } + + uint32_t operator++() noexcept + { + return m_count.fetch_add(1, std::memory_order_relaxed) + 1; + } + + uint32_t operator--() noexcept + { + auto const remaining = m_count.fetch_sub(1, std::memory_order_release) - 1; + + if (remaining == 0) + { + std::atomic_thread_fence(std::memory_order_acquire); + notifier(); + } + else if (remaining < 0) + { + abort(); + } + + return remaining; + } + + operator uint32_t() const noexcept + { + return m_count; + } + + template + void set_notifier(Func&& func) + { + notifier = std::forward(func); + } + +private: + std::atomic m_count{0}; + std::function notifier{}; +}; + +struct notifiable_module_lock final : notifiable_module_lock_base +{ + static notifiable_module_lock& instance() noexcept + { + static notifiable_module_lock lock; + return lock; + } +}; +} // namespace wil + +#ifndef WIL_CPPWINRT_COM_SERVER_CUSTOM_MODULE_LOCK + +namespace winrt +{ +auto& get_module_lock() +{ + return wil::notifiable_module_lock::instance(); +} +} // namespace winrt + +#endif + +#endif \ No newline at end of file diff --git a/libs/wil/wil/cppwinrt_register_com_server.h b/libs/wil/wil/cppwinrt_register_com_server.h new file mode 100644 index 00000000..87593eb1 --- /dev/null +++ b/libs/wil/wil/cppwinrt_register_com_server.h @@ -0,0 +1,76 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! Utilities for making managing COM server easier + +#ifndef __WIL_CPPWINRT_REGISTER_COM_SERVER_INCLUDED +#define __WIL_CPPWINRT_REGISTER_COM_SERVER_INCLUDED + +#include +#include +#include +#include + +namespace wil::details +{ +template +struct CppWinRTClassFactory : winrt::implements, IClassFactory, winrt::no_module_lock> +{ + HRESULT __stdcall CreateInstance(IUnknown* outer, GUID const& iid, void** result) noexcept final + try + { + *result = nullptr; + + if (outer) + { + return CLASS_E_NOAGGREGATION; + } + return winrt::make_self().as(iid, result); + } + CATCH_RETURN() + + HRESULT __stdcall LockServer(BOOL) noexcept final + { + return S_OK; + } +}; + +template +void register_com_server(std::vector& registrations) +{ + DWORD registration{}; + winrt::check_hresult(CoRegisterClassObject( + winrt::guid_of(), winrt::make>().get(), CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, ®istration)); + // This emplace_back is no-throw as wil::register_com_server already reserves enough capacity + registrations.emplace_back(registration); + register_com_server(registrations); +} + +template <> +void register_com_server(std::vector&) +{ +} +} // namespace wil::details + +namespace wil +{ +template +[[nodiscard]] std::vector register_com_server() +{ + std::vector registrations; + registrations.reserve(sizeof...(Rest) + 1); + details::register_com_server(registrations); + // C++17 doesn't provide guaranteed copy elision, but the copy should be elided nonetheless. + return registrations; +} +} // namespace wil + +#endif \ No newline at end of file diff --git a/libs/wil/wil/cppwinrt_wrl.h b/libs/wil/wil/cppwinrt_wrl.h new file mode 100644 index 00000000..43d13b3c --- /dev/null +++ b/libs/wil/wil/cppwinrt_wrl.h @@ -0,0 +1,80 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! Provides interoperability between C++/WinRT types and the WRL Module system. +#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 +{ +/// @cond +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(); + } + } + }; +} // namespace details +/// @endcond + +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() +}; +} // namespace wil + +#define CoCreatableCppWinRtClass(className) \ + CoCreatableClassWithFactory(className, ::wil::wrl_factory_for_winrt_com_class) + +#endif // __WIL_CPPWINRT_WRL_INCLUDED diff --git a/libs/wil/wil/filesystem.h b/libs/wil/wil/filesystem.h new file mode 100644 index 00000000..85fbb207 --- /dev/null +++ b/libs/wil/wil/filesystem.h @@ -0,0 +1,1329 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! Helpers for interfacing with the Windows filesystem APIs. +#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 "wistd_type_traits.h" +#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 = nullptr; + 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 = nullptr; + 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 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)); + RETURN_IF_FAILED(CreateDirectoryDeepNoThrow(parent.get())); // recurs + } + if (::CreateDirectoryW(path, nullptr) == FALSE) + { + lastError = ::GetLastError(); + if (lastError != ERROR_ALREADY_EXISTS) + { + RETURN_WIN32(lastError); + } + } + } + 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); + +/// @cond +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)))); + } +} // namespace details +/// @endcond + +// 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, DWORD access = GENERIC_READ | /*DELETE*/ 0x00010000L, DWORD share = FILE_SHARE_READ) +{ + wil::unique_hfile result( + CreateFileW(path, access, share, 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, HANDLE deleteHandle = INVALID_HANDLE_VALUE) 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), + recursivelyDeletableDirectoryHandle.get())); + } + 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)) + { + if (deleteHandle != INVALID_HANDLE_VALUE) + { +#if (NTDDI_VERSION >= NTDDI_WIN10_RS1) + // DeleteFile and RemoveDirectory use POSIX delete, falling back to non-POSIX on most errors. Do the same here. + FILE_DISPOSITION_INFO_EX fileInfoEx{}; + fileInfoEx.Flags = FILE_DISPOSITION_FLAG_DELETE | FILE_DISPOSITION_FLAG_POSIX_SEMANTICS; + if (!SetFileInformationByHandle(deleteHandle, FileDispositionInfoEx, &fileInfoEx, sizeof(fileInfoEx))) + { + auto const err = ::GetLastError(); + // The real error we're looking for is STATUS_CANNOT_DELETE, but that's mapped to ERROR_ACCESS_DENIED. + if (err != ERROR_ACCESS_DENIED) + { +#endif + FILE_DISPOSITION_INFO fileInfo{}; + fileInfo.DeleteFile = TRUE; + RETURN_IF_WIN32_BOOL_FALSE(SetFileInformationByHandle(deleteHandle, FileDispositionInfo, &fileInfo, sizeof(fileInfo))); +#if (NTDDI_VERSION >= NTDDI_WIN10_RS1) + } + else + { + RETURN_WIN32(err); + } + } +#endif + } + else + { + 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. + WI_NODISCARD 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; + } + + WI_NODISCARD reference operator*() const WI_NOEXCEPT + { + return *current_; + } + WI_NODISCARD 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, // requires 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; +} // namespace details +/// @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) + { + BOOL cancelRes = CancelIoEx(m_folderHandle.get(), &m_overlapped); + + // no pending operation to cancel. Maybe StartIo returned + // an error? + if (!(cancelRes == FALSE && ::GetLastError() == ERROR_NOT_FOUND)) + { + DWORD bytesTransferredIgnored = 0; + GetOverlappedResult(m_folderHandle.get(), &m_overlapped, &bytesTransferredIgnored, TRUE); + } + } + + // 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; + unsigned 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; +} // namespace details +/// @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); + + if (result == ERROR_SUCCESS) + { + for (auto const& info : + create_next_entry_offset_iterator(reinterpret_cast(readerState->m_readBuffer))) + { + wchar_t relativeFileName[MAX_PATH]; + StringCchCopyNW( + relativeFileName, ARRAYSIZE(relativeFileName), info.FileName, info.FileNameLength / sizeof(info.FileName[0])); + + readerState->m_callback(static_cast(info.Action), relativeFileName); + } + } + else if (result == ERROR_NOTIFY_ENUM_DIR) + { + readerState->m_callback(FolderChangeEvent::ChangesLost, __nullptr); + } + else + { + // No need to requeue + return; + } + + // 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() +// 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, 64); + MAP_INFOCLASS_TO_STRUCT(FileRenameInfo, FILE_RENAME_INFO, false, 64); + 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, 64); + 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, 8192); + 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, 8192); + 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, 8192); + 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_maybenull_ 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 if ((lastError == ERROR_HANDLE_EOF) && (infoClass == FileStreamInfo)) + { + break; + } + else + { + RETURN_WIN32(lastError); + } + } + } + return S_OK; + } +} // namespace details +/// @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 WIL_ENABLE_EXCEPTIONS +/** 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; +} + +// Helpers to make the CreateFileW API easier to use. +// https://learn.microsoft.com/windows/win32/api/fileapi/nf-fileapi-createfilew + +struct file_and_error_result +{ + file_and_error_result(HANDLE file_handle, DWORD error) : file(file_handle), last_error(error) + { + } + + wil::unique_hfile file; + DWORD last_error{}; +}; + +/** Non-throwing open existing using OPEN_EXISTING, returns handle and error code. +~~~ +auto [handle, error] = wil::try_open_file(filePath.c_str()); +~~~ +*/ +inline file_and_error_result try_open_file( + PCWSTR path, + DWORD dwDesiredAccess = GENERIC_READ, + DWORD dwShareMode = FILE_SHARE_READ, + DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL, + bool inheritHandle = false) noexcept +{ + SECURITY_ATTRIBUTES secAttributes{sizeof(secAttributes)}; + secAttributes.bInheritHandle = inheritHandle; + auto handle = CreateFileW(path, dwDesiredAccess, dwShareMode, &secAttributes, OPEN_EXISTING, dwFlagsAndAttributes, nullptr); + return {handle, ::GetLastError()}; +} + +/** open existing using OPEN_EXISTING, throws on error. +~~~ +auto handle = wil::open_file(filePath.c_str()); +~~~ +*/ +inline wil::unique_hfile open_file( + PCWSTR path, + DWORD dwDesiredAccess = GENERIC_READ, + DWORD dwShareMode = FILE_SHARE_READ, + DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL, + bool inheritHandle = false) +{ + auto result = try_open_file(path, dwDesiredAccess, dwShareMode, dwFlagsAndAttributes, inheritHandle); + THROW_WIN32_IF(result.last_error, !result.file.is_valid()); + return wistd::move(result.file); +} + +/// @cond +namespace details +{ + template + file_and_error_result create_file( + PCWSTR path, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) noexcept + { + auto handle = CreateFileW( + path, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreateDisposition, dwFlagsAndAttributes, hTemplateFile); + return {handle, ::GetLastError()}; + } +} // namespace details +/// @endcond + +/** create using CREATE_NEW, returns handle and error code. +~~~ +auto [handle, error] = wil::try_create_new_file(filePath.c_str()); +~~~ +*/ +inline file_and_error_result try_create_new_file( + PCWSTR path, + DWORD dwDesiredAccess = GENERIC_READ | GENERIC_WRITE, + DWORD dwShareMode = FILE_SHARE_READ, + LPSECURITY_ATTRIBUTES lpSecurityAttributes = nullptr, + DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL, + HANDLE hTemplateFile = nullptr) noexcept +{ + return details::create_file(path, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwFlagsAndAttributes, hTemplateFile); +} + +/** create using OPEN_ALWAYS, returns handle and error code. +~~~ +auto [handle, error] = wil::try_open_or_create_file(filePath.c_str()); +~~~ +*/ +inline file_and_error_result try_open_or_create_file( + PCWSTR path, + DWORD dwDesiredAccess = GENERIC_READ | GENERIC_WRITE, + DWORD dwShareMode = FILE_SHARE_READ, + LPSECURITY_ATTRIBUTES lpSecurityAttributes = nullptr, + DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL, + HANDLE hTemplateFile = nullptr) noexcept +{ + return details::create_file(path, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwFlagsAndAttributes, hTemplateFile); +} + +/** create using CREATE_ALWAYS, returns handle and error code. +~~~ +auto [handle, error] = wil::try_open_or_truncate_existing_file(filePath.c_str()); +~~~ +*/ +inline file_and_error_result try_open_or_truncate_existing_file( + PCWSTR path, + DWORD dwDesiredAccess = GENERIC_READ | GENERIC_WRITE, + DWORD dwShareMode = FILE_SHARE_READ, + LPSECURITY_ATTRIBUTES lpSecurityAttributes = nullptr, + DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL, + HANDLE hTemplateFile = nullptr) noexcept +{ + return details::create_file(path, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwFlagsAndAttributes, hTemplateFile); +} + +/** create using TRUNCATE_EXISTING, returns handle and error code. +~~~ +auto [handle, error] = wil::try_truncate_existing_file(filePath.c_str()); +~~~ +*/ +inline file_and_error_result try_truncate_existing_file( + PCWSTR path, + DWORD dwDesiredAccess = GENERIC_READ | GENERIC_WRITE | GENERIC_WRITE, + DWORD dwShareMode = FILE_SHARE_READ, + LPSECURITY_ATTRIBUTES lpSecurityAttributes = nullptr, + DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL, + HANDLE hTemplateFile = nullptr) noexcept +{ + return details::create_file( + path, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwFlagsAndAttributes, hTemplateFile); +} + +/** create using CREATE_NEW, returns the file handle, throws on error. +~~~ +auto handle = wil::create_new_file(filePath.c_str()); +~~~ +*/ +inline wil::unique_hfile create_new_file( + PCWSTR path, + DWORD dwDesiredAccess = GENERIC_READ | GENERIC_WRITE, + DWORD dwShareMode = FILE_SHARE_READ, + LPSECURITY_ATTRIBUTES lpSecurityAttributes = nullptr, + DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL, + HANDLE hTemplateFile = nullptr) +{ + auto result = try_create_new_file(path, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwFlagsAndAttributes, hTemplateFile); + THROW_WIN32_IF(result.last_error, !result.file.is_valid()); + return wistd::move(result.file); +} + +/** create using OPEN_ALWAYS, returns the file handle, throws on error. +~~~ +auto handle = wil::open_or_create_file(filePath.c_str()); +~~~ +*/ +inline wil::unique_hfile open_or_create_file( + PCWSTR path, + DWORD dwDesiredAccess = GENERIC_READ | GENERIC_WRITE, + DWORD dwShareMode = FILE_SHARE_READ, + LPSECURITY_ATTRIBUTES lpSecurityAttributes = nullptr, + DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL, + HANDLE hTemplateFile = nullptr) +{ + auto result = try_open_or_create_file(path, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwFlagsAndAttributes, hTemplateFile); + THROW_WIN32_IF(result.last_error, !result.file.is_valid()); + return wistd::move(result.file); +} + +/** create using CREATE_ALWAYS, returns the file handle, throws on error. +~~~ +auto handle = wil::open_or_truncate_existing_file(filePath.c_str()); +~~~ +*/ +inline wil::unique_hfile open_or_truncate_existing_file( + PCWSTR path, + DWORD dwDesiredAccess = GENERIC_READ | GENERIC_WRITE, + DWORD dwShareMode = FILE_SHARE_READ, + LPSECURITY_ATTRIBUTES lpSecurityAttributes = nullptr, + DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL, + HANDLE hTemplateFile = nullptr) +{ + auto result = try_open_or_truncate_existing_file( + path, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwFlagsAndAttributes, hTemplateFile); + THROW_WIN32_IF(result.last_error, !result.file.is_valid()); + return wistd::move(result.file); +} + +/** create using TRUNCATE_EXISTING, returns the file handle, throws on error. +~~~ +auto handle = wil::truncate_existing_file(filePath.c_str()); +~~~ +*/ +inline wil::unique_hfile truncate_existing_file( + PCWSTR path, + DWORD dwDesiredAccess = GENERIC_READ | GENERIC_WRITE, + DWORD dwShareMode = FILE_SHARE_READ, + LPSECURITY_ATTRIBUTES lpSecurityAttributes = nullptr, + DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL, + HANDLE hTemplateFile = nullptr) +{ + auto result = + try_truncate_existing_file(path, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwFlagsAndAttributes, hTemplateFile); + THROW_WIN32_IF(result.last_error, !result.file.is_valid()); + return wistd::move(result.file); +} + +#endif // WIL_ENABLE_EXCEPTIONS +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && (_WIN32_WINNT >= _WIN32_WINNT_WIN7) +} // namespace wil + +#ifndef WIL_NO_FILE_TYPE_OPERATORS +inline bool operator==(const FILE_ID_128& left, const FILE_ID_128& right) +{ + return memcmp(&left, &right, sizeof(left)) == 0; +} + +inline bool operator!=(const FILE_ID_128& left, const FILE_ID_128& right) +{ + return !operator==(left, right); +} +#endif + +#endif // __WIL_FILESYSTEM_INCLUDED diff --git a/libs/wil/wil/nt_result_macros.h b/libs/wil/wil/nt_result_macros.h new file mode 100644 index 00000000..9fc52a82 --- /dev/null +++ b/libs/wil/wil/nt_result_macros.h @@ -0,0 +1,233 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! WIL Error Handling Helpers: supporting file defining a family of macros and functions designed to uniformly handle errors +//! across return codes, fail fast, exceptions and logging for NTSTATUS values. +#ifndef __WIL_NT_RESULTMACROS_INCLUDED +#define __WIL_NT_RESULTMACROS_INCLUDED + +#include "result_macros.h" + +// Helpers for return macros +/// @cond +#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; \ + } \ + __WI_SUPPRESS_4127_E 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, __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)); \ + } \ + return __status; \ + } \ + __WI_SUPPRESS_4127_E while ((void)0, 0) +/// @endcond + +//***************************************************************************** +// 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) __WI_CHECK_MSG_FMT(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)); +} + +/// @cond +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 + } // namespace __R_NS_NAME + + 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); + } +} // namespace details +/// @endcond +} // namespace wil + +#endif // __WIL_NT_RESULTMACROS_INCLUDED diff --git a/libs/wil/wil/registry.h b/libs/wil/wil/registry.h new file mode 100644 index 00000000..27fff0cc --- /dev/null +++ b/libs/wil/wil/registry.h @@ -0,0 +1,3323 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! Helpers for reading and writing values to/from the registry. +#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 "registry_helpers.h" +#include "resource.h" + +// wil registry does not require the use of the STL or C++ exceptions (see _nothrow functions) +// wil registry natively supports std::vector and std::wstring when preferring those types +// wil registry uses the __WIL_WINREG_STL define to enable support for wil::shared_* types (defined in resource.h) + +namespace wil +{ +//! Functions and classes that support reading and writing values to/from the registry. +namespace reg +{ +#if defined(WIL_ENABLE_EXCEPTIONS) + /** + * @brief Opens a new HKEY to the specified path - see RegOpenKeyExW + * @param key An open or well-known registry key + * @param subKey The name of the registry subkey to be opened. + * If `nullptr`, then `key` is used without modification. + * @param access The requested access desired for the opened key + * @return A wil::unique_hkey containing the resulting opened HKEY + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline ::wil::unique_hkey open_unique_key(HKEY key, _In_opt_ PCWSTR subKey, ::wil::reg::key_access access = ::wil::reg::key_access::read) + { + const reg_view_details::reg_view regview{key}; + ::wil::unique_hkey return_value; + regview.open_key(subKey, &return_value, access); + return return_value; + } + + /** + * @brief Creates a new HKEY to the specified path - see RegCreateKeyExW + * @param key An open or well-known registry key + * @param subKey The name of a subkey that this function opens or creates. + * Note: this cannot be null (see the above referenced API documentation) + * @param access The requested access desired for the opened key + * @return A wil::unique_hkey or wil::shared_hkey containing the resulting opened HKEY + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline ::wil::unique_hkey create_unique_key(HKEY key, PCWSTR subKey, ::wil::reg::key_access access = ::wil::reg::key_access::read) + { + const reg_view_details::reg_view regview{key}; + ::wil::unique_hkey return_value; + regview.create_key(subKey, &return_value, access); + return return_value; + } + +#if defined(__WIL_WINREG_STL) || defined(WIL_DOXYGEN) + /** + * @brief Opens a new HKEY to the specified path - see RegOpenKeyExW + * @param key An open or well-known registry key + * @param subKey The name of the registry subkey to be opened. + * If `nullptr`, then `key` is used without modification. + * @param access The requested access desired for the opened key + * @return A wil::shared_hkey containing the resulting opened HKEY + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline ::wil::shared_hkey open_shared_key(HKEY key, _In_opt_ PCWSTR subKey, ::wil::reg::key_access access = ::wil::reg::key_access::read) + { + const reg_view_details::reg_view regview{key}; + ::wil::shared_hkey return_value; + regview.open_key(subKey, &return_value, access); + return return_value; + } + + /** + * @brief Creates a new HKEY to the specified path - see RegCreateKeyExW + * @param key An open or well-known registry key + * @param subKey The name of a subkey that this function opens or creates. + * Note: this cannot be null (see the above referenced API documentation) + * @param access The requested access desired for the opened key + * @return A wil::shared_hkey or wil::shared_hkey containing the resulting opened HKEY + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline ::wil::shared_hkey create_shared_key(HKEY key, PCWSTR subKey, ::wil::reg::key_access access = ::wil::reg::key_access::read) + { + const reg_view_details::reg_view regview{key}; + ::wil::shared_hkey return_value; + regview.create_key(subKey, &return_value, access); + return return_value; + } +#endif // #if defined(__WIL_WINREG_STL) +#endif // #if defined(WIL_ENABLE_EXCEPTIONS) + + /** + * @brief Opens a new HKEY to the specified path - see RegOpenKeyExW + * @param key An open or well-known registry key + * @param subKey The name of the registry subkey to be opened. + * If `nullptr`, then `key` is used without modification. + * @param[out] hkey A reference to a wil::unique_hkey to receive the opened HKEY + * @param access The requested access desired for the opened key + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT open_unique_key_nothrow( + HKEY key, _In_opt_ PCWSTR subKey, ::wil::unique_hkey& hkey, ::wil::reg::key_access access = ::wil::reg::key_access::read) WI_NOEXCEPT + { + const reg_view_details::reg_view_nothrow regview{key}; + return regview.open_key(subKey, hkey.put(), access); + } + + /** + * @brief Creates a new HKEY to the specified path - see RegCreateKeyExW + * @param key An open or well-known registry key + * @param subKey The name of a subkey that this function opens or creates. + * Note: this cannot be null (see the above referenced API documentation) + * @param[out] hkey A reference to a wil::unique_hkey to receive the opened HKEY + * @param access The requested access desired for the opened key + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT create_unique_key_nothrow( + HKEY key, PCWSTR subKey, ::wil::unique_hkey& hkey, ::wil::reg::key_access access = ::wil::reg::key_access::read) WI_NOEXCEPT + { + const reg_view_details::reg_view_nothrow regview{key}; + return regview.create_key(subKey, hkey.put(), access); + } + +#if defined(__WIL_WINREG_STL) || defined(WIL_DOXYGEN) + /** + * @brief Opens a new HKEY to the specified path - see RegOpenKeyExW + * @param key An open or well-known registry key + * @param subKey The name of the registry subkey to be opened. + * If `nullptr`, then `key` is used without modification. + * @param[out] hkey A reference to a wil::shared_hkey to receive the opened HKEY + * @param access The requested access desired for the opened key + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT open_shared_key_nothrow( + HKEY key, _In_opt_ PCWSTR subKey, ::wil::shared_hkey& hkey, ::wil::reg::key_access access = ::wil::reg::key_access::read) WI_NOEXCEPT + { + const reg_view_details::reg_view_nothrow regview{key}; + return regview.open_key(subKey, hkey.put(), access); + } + + /** + * @brief Creates a new HKEY to the specified path - see RegCreateKeyExW + * @param key An open or well-known registry key + * @param subKey The name of a subkey that this function opens or creates. + * Note: this cannot be null (see the above referenced API documentation) + * @param[out] hkey A reference to a wil::shared_hkey to receive the opened HKEY + * @param access The requested access desired for the opened key + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT create_shared_key_nothrow( + HKEY key, PCWSTR subKey, ::wil::shared_hkey& hkey, ::wil::reg::key_access access = ::wil::reg::key_access::read) WI_NOEXCEPT + { + const reg_view_details::reg_view_nothrow regview{key}; + return regview.create_key(subKey, hkey.put(), access); + } +#endif // #define __WIL_WINREG_STL + + // + // wil::key_iterator and wil::value_iterator objects enable enumerating registry keys and values. + // + // Examples of usage when std::wstring is included: + // + // for (const auto& key_data : wil::make_range(wil::reg::key_iterator{hkey}, wil::reg::key_iterator{})) + // { + // key_data.name; // the std::wstring of the enumerated key + // } + // + // for (const auto& value_data : wil::make_range(wil::reg::value_iterator{hkey}, wil::reg::value_iterator{})) + // { + // value_data.name; // the std::wstring of the enumerated value + // value_data.type; // the REG_ type of the enumerated value + // } + // + // When std::wstring is not included, wil::unique_process_heap_string can be used instead: + // + // for (const auto& key_data : wil::make_range(wil::reg::key_heap_string_iterator{hkey}, wil::reg::key_heap_string_iterator{})) + // { + // key_data.name.get(); // the PCWSTR of the enumerated key + // } + // + // for (const auto& value_data : wil::make_range(wil::reg::value_heap_string_iterator{hkey}, wil::reg::value_heap_string_iterator{})) + // { + // value_data.name.get(); // the PCWSTR of the enumerated value + // value_data.type; // the REG_ type of the enumerated value + // } + // + // When not using exceptions, can manually walk the iterator using wil::unique_process_heap_string: + // + // auto iterate_keys = wil::reg::key_heap_string_nothrow_iterator{hkey}; + // for (const auto& key_data : wil::make_range(iterate_keys, wil::reg::key_heap_string_nothrow_iterator{})) + // { + // key_data.name.get(); // the PCWSTR of the enumerated key + // } + // if (FAILED(iterate_keys.last_error())) + // { + // // the HRESULT last_error() returns the registry error that prevented enumeration + // } + // + // auto iterate_values = wil::reg::value_heap_string_nothrow_iterator{hkey}; + // for (const auto& value_data : wil::make_range(iterate_values, wil::reg::value_heap_string_nothrow_iterator{})) + // { + // value_data.name.get(); // the PCWSTR of the enumerated value + // value_data.type; // the REG_ type of the enumerated value + // } + // if (FAILED(iterate_values.last_error())) + // { + // // the HRESULT last_error() returns the registry error that prevented enumeration + // } + // +#if defined(WIL_ENABLE_EXCEPTIONS) + +#if defined(_STRING_) || defined(WIL_DOXYGEN) + using key_iterator = ::wil::reg::iterator_t<::wil::reg::key_iterator_data<::std::wstring>>; + using value_iterator = ::wil::reg::iterator_t<::wil::reg::value_iterator_data<::std::wstring>>; +#endif + +#if defined(__WIL_OLEAUTO_H_) || defined(WIL_DOXYGEN) + using key_bstr_iterator = ::wil::reg::iterator_t<::wil::reg::key_iterator_data<::wil::unique_bstr>>; + using value_bstr_iterator = ::wil::reg::iterator_t<::wil::reg::value_iterator_data<::wil::unique_bstr>>; +#endif // #if defined(__WIL_OLEAUTO_H_) + + using key_heap_string_iterator = ::wil::reg::iterator_t<::wil::reg::key_iterator_data<::wil::unique_process_heap_string>>; + using value_heap_string_iterator = ::wil::reg::iterator_t<::wil::reg::value_iterator_data<::wil::unique_process_heap_string>>; + +#endif // #if defined(WIL_ENABLE_EXCEPTIONS) + + // no-throw versions of applicable registry iterators +#if defined(__WIL_OLEAUTO_H_) || defined(WIL_DOXYGEN) + using key_bstr_nothrow_iterator = ::wil::reg::iterator_nothrow_t<::wil::reg::key_iterator_data<::wil::unique_bstr>>; + using value_bstr_nothrow_iterator = ::wil::reg::iterator_nothrow_t<::wil::reg::value_iterator_data<::wil::unique_bstr>>; +#endif // #if defined(__WIL_OLEAUTO_H_) + + using key_heap_string_nothrow_iterator = + ::wil::reg::iterator_nothrow_t<::wil::reg::key_iterator_data<::wil::unique_process_heap_string>>; + using value_heap_string_nothrow_iterator = + ::wil::reg::iterator_nothrow_t<::wil::reg::value_iterator_data<::wil::unique_process_heap_string>>; + + /** + * @brief Queries for number of sub-keys + * @param key The HKEY to query for number of sub-keys + * @param[out] numSubKeys A pointer to a DWORD to receive the returned count + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_child_key_count_nothrow(HKEY key, _Out_ DWORD* numSubKeys) WI_NOEXCEPT + { + RETURN_IF_WIN32_ERROR(RegQueryInfoKeyW( + key, + nullptr, // null class + nullptr, // null class character count, + nullptr, // null reserved + numSubKeys, + nullptr, // null max subkey length + nullptr, // null max class length + nullptr, // null value count + nullptr, // null max value name length + nullptr, // null max value length + nullptr, // null security descriptor + nullptr)); // null last write filetime + return S_OK; + } + + inline HRESULT get_child_key_count_nothrow(HKEY key, _Out_ uint32_t* numSubKeys) WI_NOEXCEPT + { + DWORD subKeys{}; + RETURN_IF_FAILED(::wil::reg::get_child_key_count_nothrow(key, &subKeys)); + *numSubKeys = subKeys; + return S_OK; + } + + /** + * @brief Queries for number of values + * @param key The HKEY to query for number of values + * @param[out] numSubValues A pointer to a DWORD to receive the returned count + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_child_value_count_nothrow(HKEY key, _Out_ DWORD* numSubValues) WI_NOEXCEPT + { + RETURN_IF_WIN32_ERROR(RegQueryInfoKeyW( + key, + nullptr, // null class + nullptr, // null class char count, + nullptr, // null reserved + nullptr, // null subkey count + nullptr, // null max subkey length + nullptr, // null max class length + numSubValues, + nullptr, // null max value name length + nullptr, // null max value length + nullptr, // null security descriptor + nullptr)); // null last write filetime + return S_OK; + } + + inline HRESULT get_child_value_count_nothrow(HKEY key, _Out_ uint32_t* numSubValues) WI_NOEXCEPT + { + DWORD subValues{}; + RETURN_IF_FAILED(::wil::reg::get_child_value_count_nothrow(key, &subValues)); + *numSubValues = subValues; + return S_OK; + } + + /** + * @brief Queries for the filetime when the registry key was last written + * @param key The HKEY to query for number of values + * @param[out] lastModified A pointer to a FILETIME to receive the last write time + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline HRESULT get_last_write_filetime_nothrow(HKEY key, _Out_ FILETIME* lastModified) WI_NOEXCEPT + { + RETURN_IF_WIN32_ERROR(RegQueryInfoKeyW( + key, + nullptr, // null class + nullptr, // null class char count, + nullptr, // null reserved + nullptr, // null subkey count + nullptr, // null max subkey length + nullptr, // null max class length + nullptr, // null value count + nullptr, // null max value name length + nullptr, // null max value length + nullptr, // null security descriptor + lastModified)); + return S_OK; + } + +#if defined(WIL_ENABLE_EXCEPTIONS) + /** + * @brief Queries for number of sub-keys + * @param key The HKEY to query for number of sub-keys + * @return The queried number of sub-keys if succeeded + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline uint32_t get_child_key_count(HKEY key) + { + uint32_t numSubKeys{}; + THROW_IF_FAILED(::wil::reg::get_child_key_count_nothrow(key, &numSubKeys)); + return numSubKeys; + } + + /** + * @brief Queries for number of values + * @param key The HKEY to query for number of values + * @return The queried number of value if succeeded + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline uint32_t get_child_value_count(HKEY key) + { + uint32_t numSubValues{}; + THROW_IF_FAILED(::wil::reg::get_child_value_count_nothrow(key, &numSubValues)); + return numSubValues; + } + + /** + * @brief Queries for the filetime when the registry key was last written + * @param key The HKEY to query for number of values + * @return The queried filetime if succeeded + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline FILETIME get_last_write_filetime(HKEY key) + { + FILETIME lastModified{}; + THROW_IF_FAILED(::wil::reg::get_last_write_filetime_nothrow(key, &lastModified)); + return lastModified; + } +#endif // #if defined(WIL_ENABLE_EXCEPTIONS) + +#if defined(WIL_ENABLE_EXCEPTIONS) + // + // template + // void set_value(...) + // + // - Writes a value to a specified key and subkey, deducing the type from the given data + // - Throws a std::exception on failure (including wil::ResultException) + // + // Examples of usage (the template type does not need to be explicitly specified) + // wil::reg::set_value(key, L"subkey", L"dword_value_name", 0); // writes a REG_DWORD + // wil::reg::set_value(key, L"subkey", L"qword_value_name", 0ull); // writes a REG_QWORD + // wil::reg::set_value(key, L"subkey", L"string_value_name", L"hello"); // writes a REG_SZ + // + // A subkey is not required if the key is opened where this should write the value: + // wil::reg::set_value(key, L"dword_value_name", 0); // writes a REG_DWORD + // wil::reg::set_value(key, L"qword_value_name", 0ull); // writes a REG_QWORD + // wil::reg::set_value(key, L"string_value_name", L"hello"); // writes a REG_SZ + // + // Example usage writing a vector of wstrings to a REG_MULTI_SZ + // std::vector data { L"string1", L"string2", L"string3" }; + // wil::reg::set_value(key, L"multi_string_value_name", data); + // wil::reg::set_value(key, L"multi_string_value_name", data); + // + // Example of usage writing directly to a registry value from a raw byte vector + // - notice the registry type is required, not implied + // std::vector data { 0x00, 0xff, 0xee, 0xdd, 0xcc }; + // wil::reg::set_value_binary(key, L"binary_value_name", REG_BINARY, data); + // wil::reg::set_value_binary(key, L"binary_value_name", REG_BINARY, data); + // + + /** + * @brief Writes a value to a specified key and subkey, deducing the type from the given data. + * @tparam T The type of the data being set (the registry value type is deduced from T). + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The data (of type T) to write to the specified registry value + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + template + void set_value(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, const T& data) + { + const reg_view_details::reg_view regview{key}; + regview.set_value(subkey, value_name, data); + } + + /** + * @brief Writes a value under a specified key, the registry type based off the templated type passed as data + * @tparam T The type of the data being set (the registry value type is deduced from T). + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The data (of type T) to write to the specified registry value + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + template + void set_value(HKEY key, _In_opt_ PCWSTR value_name, const T& data) + { + ::wil::reg::set_value(key, nullptr, value_name, data); + } + + /** + * @brief Writes a null-terminated string value under a specified key + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The null-terminated string to write to the specified registry value + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline void set_value(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, PCWSTR data) + { + const reg_view_details::reg_view regview{key}; + regview.set_value(subkey, value_name, data); + } + + /** + * @brief Writes a null-terminated string value under a specified key + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The null-terminated string to write to the specified registry value + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline void set_value(HKEY key, _In_opt_ PCWSTR value_name, PCWSTR data) + { + ::wil::reg::set_value(key, nullptr, value_name, data); + } + + /** + * @brief Writes a REG_DWORD value from a uint32_t + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The 32-bit value to write to the specified registry value + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline void set_value_dword(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, uint32_t data) + { + ::wil::reg::set_value(key, subkey, value_name, data); + } + + /** + * @brief Writes a REG_DWORD value from a uint32_t + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The 32-bit value to write to the specified registry value + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline void set_value_dword(HKEY key, _In_opt_ PCWSTR value_name, uint32_t data) + { + ::wil::reg::set_value(key, nullptr, value_name, data); + } + + /** + * @brief Writes a REG_QWORD value from a uint64_t + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The 64-bit value to write to the specified registry value + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline void set_value_qword(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, uint64_t data) + { + ::wil::reg::set_value(key, subkey, value_name, data); + } + + /** + * @brief Writes a REG_QWORD value from a uint64_t + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The 64-bit value to write to the specified registry value + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline void set_value_qword(HKEY key, _In_opt_ PCWSTR value_name, uint64_t data) + { + ::wil::reg::set_value(key, nullptr, value_name, data); + } + + /** + * @brief Writes a REG_SZ value from a null-terminated string + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The null-terminated string value to write to the specified registry value + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline void set_value_string(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, PCWSTR data) + { + ::wil::reg::set_value(key, subkey, value_name, data); + } + + /** + * @brief Writes a REG_SZ value from a null-terminated string + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The null-terminated string value to write to the specified registry value + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline void set_value_string(HKEY key, _In_opt_ PCWSTR value_name, PCWSTR data) + { + ::wil::reg::set_value(key, nullptr, value_name, data); + } + + /** + * @brief Writes a REG_EXPAND_SZ value from a null-terminated string + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The null-terminated, unexpanded string value to write to the specified registry value. For example, `%PATH%`. + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline void set_value_expanded_string(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, PCWSTR data) + { + const reg_view_details::reg_view regview{key}; + regview.set_value(subkey, value_name, data, REG_EXPAND_SZ); + } + + /** + * @brief Writes a REG_EXPAND_SZ value from a null-terminated string + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The null-terminated, unexpanded string value to write to the specified registry value. For example, `%PATH%`. + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline void set_value_expanded_string(HKEY key, _In_opt_ PCWSTR value_name, PCWSTR data) + { + ::wil::reg::set_value_expanded_string(key, nullptr, value_name, data); + } + +#if (defined(_VECTOR_) && defined(_STRING_)) || defined(WIL_DOXYGEN) + /** + * @brief The generic set_value template function to write a REG_MULTI_SZ value from a std::vector + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data A std::vector to write to the specified registry value. + * Each string will be marshaled to a contiguous null-terminator-delimited multi-sz string + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline void set_value(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, const ::std::vector<::std::wstring>& data) + { + const auto multiStringWcharVector(reg_view_details::get_multistring_from_wstrings(::std::begin(data), ::std::end(data))); + const reg_view_details::reg_view regview{key}; + regview.set_value(subkey, value_name, multiStringWcharVector, REG_MULTI_SZ); + } + + /** + * @brief The generic set_value template function to write a REG_MULTI_SZ value from a std::vector + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data A std::vector to write to the specified registry value. + * Each string will be marshaled to a contiguous null-terminator-delimited multi-sz string. + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline void set_value(HKEY key, _In_opt_ PCWSTR value_name, const ::std::vector<::std::wstring>& data) + { + ::wil::reg::set_value(key, nullptr, value_name, data); + } + + /** + * @brief Writes a REG_MULTI_SZ value from a std::vector + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data A std::vector to write to the specified registry value. + * Each string will be marshaled to a contiguous null-terminator-delimited multi-sz string + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline void set_value_multistring(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, const ::std::vector<::std::wstring>& data) + { + ::wil::reg::set_value(key, subkey, value_name, data); + } + + /** + * @brief Writes a REG_MULTI_SZ value from a std::vector + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data A std::vector to write to the specified registry value. + * Each string will be marshaled to a contiguous null-terminator-delimited multi-sz string. + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline void set_value_multistring(HKEY key, _In_opt_ PCWSTR value_name, const ::std::vector<::std::wstring>& data) + { + ::wil::reg::set_value(key, nullptr, value_name, data); + } +#endif // #if defined(_VECTOR_) && defined(_STRING_) + +#if defined(_VECTOR_) || defined(WIL_DOXYGEN) + /** + * @brief Writes a registry value of the specified type from a `std::vector`/`std::vector` + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param type The registry type for the specified registry value - see RegSetKeyValueW + * @param data A `std::vector`/`std::vector` to write to the specified registry value. + * The vector contents will be directly marshaled to the specified value. + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline void set_value_binary(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, uint32_t type, const ::std::vector& data) + { + const reg_view_details::reg_view regview{key}; + regview.set_value(subkey, value_name, data, type); + } + + /** + * @brief Writes a registry value of the specified type from a `std::vector`/`std::vector` + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param type The registry type for the specified registry value - see RegSetKeyValueW + * @param data A `std::vector`/`std::vector` to write to the specified registry value. + * The vector contents will be directly marshaled to the specified value. + * @exception std::exception (including wil::ResultException) will be thrown on all failures + */ + inline void set_value_binary(HKEY key, _In_opt_ PCWSTR value_name, uint32_t type, const ::std::vector& data) + { + ::wil::reg::set_value_binary(key, nullptr, value_name, type, data); + } +#endif // #if defined(_VECTOR_) +#endif // #if defined(WIL_ENABLE_EXCEPTIONS) + + // + // template + // HRESULT set_value_nothrow(...) + // + // - Writes a value under a specified key + // - The type of registry value is determined by the template type T of data given + // - Returns an HRESULT error code indicating success or failure (does not throw C++ exceptions) + // + // Examples of usage (the template type does not need to be explicitly specified) + // hr = wil::reg::set_value_nothrow(key, L"subkey", L"dword_value_name", 0); // writes a REG_DWORD + // hr = wil::reg::set_value_nothrow(key, L"subkey", L"qword_value_name", 0ull); // writes a REG_QWORD + // hr = wil::reg::set_value_nothrow(key, L"subkey", L"string_value_name", L"hello"); // writes a REG_SZ + // + // A subkey is not required if the key is opened where this should write the value: + // hr = wil::reg::set_value_nothrow(key, L"dword_value_name", 0); // writes a REG_DWORD + // hr = wil::reg::set_value_nothrow(key, L"qword_value_name", 0ull); // writes a REG_QWORD + // hr = wil::reg::set_value_nothrow(key, L"string_value_name", L"hello"); // writes a REG_SZ + // + // Example of usage writing a REG_MULTI_SZ + // std::vector multisz_data { L"string1", L"string2", L"string3" }; + // hr = wil::reg::set_value_nothrow(key, L"multi_string_value_name", multisz_data); + // + // Values can be written directly from a vector of bytes - the registry type must be specified; e.g.: + // std::vector data { 0x00, 0xff, 0xee, 0xdd, 0xcc }; + // hr = wil::reg::set_value_binary_nothrow(key, L"binary_value_name", REG_BINARY, data); + // + /** + * @brief Writes a value to a specified key and subkey, deducing the type from the given data. + * @tparam T The type of the data being set (the registry value type is deduced from T). + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The data (of type T) to write to the specified registry value + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + template + HRESULT set_value_nothrow(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, const T& data) WI_NOEXCEPT + { + const reg_view_details::reg_view_nothrow regview{key}; + return regview.set_value(subkey, value_name, data); + } + + /** + * @brief Writes a value under a specified key, the registry type based off the templated type passed as data + * @tparam T The type of the data being set (the registry value type is deduced from T). + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The data (of type T) to write to the specified registry value + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + template + HRESULT set_value_nothrow(HKEY key, _In_opt_ PCWSTR value_name, const T& data) WI_NOEXCEPT + { + return ::wil::reg::set_value_nothrow(key, nullptr, value_name, data); + } + + /** + * @brief Writes a null-terminated string value under a specified key + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The null-terminated string to write to the specified registry value + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT set_value_nothrow(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, PCWSTR data) WI_NOEXCEPT + { + const reg_view_details::reg_view_nothrow regview{key}; + return regview.set_value(subkey, value_name, data); + } + + /** + * @brief Writes a null-terminated string value under a specified key + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The null-terminated string to write to the specified registry value + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT set_value_nothrow(HKEY key, _In_opt_ PCWSTR value_name, PCWSTR data) WI_NOEXCEPT + { + return ::wil::reg::set_value_nothrow(key, nullptr, value_name, data); + } + + /** + * @brief Writes a REG_DWORD value from a uint32_t + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The 32-bit value to write to the specified registry value + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT set_value_dword_nothrow(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, uint32_t data) WI_NOEXCEPT + { + return ::wil::reg::set_value_nothrow(key, subkey, value_name, data); + } + + /** + * @brief Writes a REG_DWORD value from a uint32_t + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The 32-bit value to write to the specified registry value + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT set_value_dword_nothrow(HKEY key, _In_opt_ PCWSTR value_name, uint32_t data) WI_NOEXCEPT + { + return ::wil::reg::set_value_nothrow(key, nullptr, value_name, data); + } + + /** + * @brief Writes a REG_QWORD value from a uint64_t + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The 64-bit value to write to the specified registry value + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT set_value_qword_nothrow(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, uint64_t data) WI_NOEXCEPT + { + return ::wil::reg::set_value_nothrow(key, subkey, value_name, data); + } + + /** + * @brief Writes a REG_QWORD value from a uint64_t + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The 64-bit value to write to the specified registry value + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT set_value_qword_nothrow(HKEY key, _In_opt_ PCWSTR value_name, uint64_t data) WI_NOEXCEPT + { + return ::wil::reg::set_value_nothrow(key, nullptr, value_name, data); + } + + /** + * @brief Writes a REG_SZ value from a null-terminated string + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The null-terminated string value to write to the specified registry value + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT set_value_string_nothrow(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, PCWSTR data) WI_NOEXCEPT + { + return ::wil::reg::set_value_nothrow(key, subkey, value_name, data); + } + + /** + * @brief Writes a REG_SZ value from a null-terminated string + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The null-terminated string value to write to the specified registry value + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT set_value_string_nothrow(HKEY key, _In_opt_ PCWSTR value_name, PCWSTR data) WI_NOEXCEPT + { + return ::wil::reg::set_value_nothrow(key, nullptr, value_name, data); + } + + /** + * @brief Writes a REG_EXPAND_SZ value from a null-terminated string + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The null-terminated string value to write to the specified registry value + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT set_value_expanded_string_nothrow(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, PCWSTR data) WI_NOEXCEPT + { + const reg_view_details::reg_view_nothrow regview{key}; + return regview.set_value(subkey, value_name, data, REG_EXPAND_SZ); + } + + /** + * @brief Writes a REG_EXPAND_SZ value from a null-terminated string + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param data The null-terminated string value to write to the specified registry value + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT set_value_expanded_string_nothrow(HKEY key, _In_opt_ PCWSTR value_name, PCWSTR data) WI_NOEXCEPT + { + return ::wil::reg::set_value_expanded_string_nothrow(key, nullptr, value_name, data); + } + +#if defined(__WIL_OBJBASE_H_) || defined(WIL_DOXYGEN) + /** + * @brief Writes raw bytes into a registry value under a specified key of the specified type + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param type The registry type for the specified registry value to write to - see RegSetValue + * @param value A ::wil::unique_cotaskmem_array_ptr holding the bytes to write into the specified registry value + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT set_value_binary_nothrow( + HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, uint32_t type, const ::wil::unique_cotaskmem_array_ptr& value) WI_NOEXCEPT + { + const reg_view_details::reg_view_nothrow regview{key}; + RETURN_IF_FAILED(regview.set_value<::wil::unique_cotaskmem_array_ptr>(subkey, value_name, value, type)); + return S_OK; + } + + /** + * @brief Writes raw bytes into a registry value under a specified key of the specified type + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to write to the unnamed default registry value. + * @param type The registry type for the specified registry value to write to - see RegSetValue + * @param value A ::wil::unique_cotaskmem_array_ptr holding the bytes to write into the specified registry value + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT set_value_binary_nothrow( + HKEY key, _In_opt_ PCWSTR value_name, uint32_t type, const ::wil::unique_cotaskmem_array_ptr& value) WI_NOEXCEPT + { + return ::wil::reg::set_value_binary_nothrow(key, nullptr, value_name, type, value); + } +#endif + +#if defined(WIL_ENABLE_EXCEPTIONS) + // + // template + // T get_value(...) + // + // - Reads a value under a specified key. + // - Requires a type T to be specified. + // - Throws a std::exception on failure (including wil::ResultException), including registry value not found. + // If you don't want an exception when the value does not exist, use try_get_value(...) + // + // Examples of usage (ensure the code handles a possible std::exception that will be thrown on all errors) + // uint32_t dword_value = wil::reg::get_value(key, L"subkey", L"dword_value_name"); + // uint64_t qword_value = wil::reg::get_value(key, L"subkey", L"qword_value_name); + // std::wstring string_value = wil::reg::get_value(key, L"subkey", L"string_value_name"); + // + // A subkey is not required if the key is opened where this should write the value: + // uint32_t dword_value = wil::reg::get_value(key, L"dword_value_name"); + // uint64_t qword_value = wil::reg::get_value(key, L"qword_value_name); + // std::wstring string_value = wil::reg::get_value(key, L"string_value_name"); + // + // The template type does not need to be specified if using functions written for a targeted type + // uint32_t dword_value = wil::reg::get_value_dword(key, L"dword_value_name"); + // uint64_t qword_value = wil::reg::get_value_qword(key, L"qword_value_name"); + // std::wstring string_value = wil::reg::get_value_string(key, L"string_value_name"); + // + // Values with REG_EXPAND_SZ can be read into each of the string types; e.g.: + // std::wstring expanded_string_value = wil::reg::get_value_expanded_string(key, L"string_value_name_with_environment_variables"); + // + // Values can be read directly into a vector of bytes - the registry type must be specified; e.g.: + // std::vector data = wil::reg::get_value_binary(key, L"binary_value_name", REG_BINARY); + // + // Multi-string values can be read into a vector; e.g.: + // std::vector multi_string_value = wil::reg::get_value_multistring(key, L"multi_string_value_name"); + // for (const auto& sub_string_value : multi_string_value) + // { + // // can read each string parsed from the multi-string + // PCWSTR string_value = sub_string_value.c_str(); + // } + // + // Reading REG_SZ and REG_EXPAND_SZ types are done through the below templated get_value_string and get_value_expanded_string functions + // Where the template type is the type to receive the string value + // The default template type is std::wstring, available if the caller has included the STL header + // + // Reading a bstr can be stored in a wil::shared_bstr or wil::unique_bstr - wil::shared_bstr has a c'tor taking a wil::unique_bstr + // wil::unique_bstr unique_value { wil::reg::get_value_string<::wil::unique_bstr>(key, L"string_value_name") }; + // wil::shared_bstr shared_value { wil::reg::get_value_string<::wil::shared_bstr>(key, L"string_value_name") }; + // + // Reading a cotaskmem string can be stored in a wil::unique_cotaskmem_string or wil::shared_cotaskmem_string + // wil::unique_cotaskmem_string unique_value { wil::reg::get_value_string(key, L"string_value_name") }; + // wil::shared_cotaskmem_string shared_value { wil::reg::get_value_string(key, L"string_value_name") }; + // + // Blocking get_value_string template types that are not already specialized - this gives a much friendlier compiler error message + template + T get_value_string(HKEY /*key*/, _In_opt_ PCWSTR /*subkey*/, _In_opt_ PCWSTR /*value_name*/) + { + static_assert(sizeof(T) != sizeof(T), "Unsupported type for get_value_string"); + } + + template + T get_value_string(HKEY /*key*/, _In_opt_ PCWSTR /*value_name*/) + { + static_assert(sizeof(T) != sizeof(T), "Unsupported type for get_value_string"); + } + + template + T get_value_expanded_string(HKEY /*key*/, _In_opt_ PCWSTR /*subkey*/, _In_opt_ PCWSTR /*value_name*/) + { + static_assert(sizeof(T) != sizeof(T), "Unsupported type for get_value_expanded_string"); + } + + template + T get_value_expanded_string(HKEY /*key*/, _In_opt_ PCWSTR /*value_name*/) + { + static_assert(sizeof(T) != sizeof(T), "Unsupported type for get_value_expanded_string"); + } + + /** + * @brief Reads a value from a specified key and subkey, deducing registry type from the type parameter T. + * @tparam T The type to read (the registry value type is deduced from T) + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value of the template type T + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + template + T get_value(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + T return_value{}; + const reg_view_details::reg_view regview{key}; + regview.get_value(subkey, value_name, return_value); + return return_value; + } + + /** + * @brief Reads a value under a specified key, deducing registry type from the type parameter T. + * @tparam T The type to read (the registry value type is deduced from T) + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value of the template type T + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + template + T get_value(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value(key, nullptr, value_name); + } + + /** + * @brief Reads a REG_DWORD value, returning a uint32_t + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The uint32_t value read from the registry + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + inline uint32_t get_value_dword(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value(key, subkey, value_name); + } + + /** + * @brief Reads a REG_DWORD value, returning a uint32_t + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The uint32_t value read from the registry + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + inline uint32_t get_value_dword(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value(key, nullptr, value_name); + } + + /** + * @brief Reads a REG_QWORD value, returning a uint64_t + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The uint64_t value read from the registry + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + inline uint64_t get_value_qword(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value(key, subkey, value_name); + } + + /** + * @brief Reads a REG_QWORD value, returning a uint64_t + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The uint64_t value read from the registry + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + inline uint64_t get_value_qword(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value(key, nullptr, value_name); + } + +#if defined(_STRING_) || defined(WIL_DOXYGEN) + /** + * @brief Reads a REG_SZ value, returning a std::wstring + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A std::wstring created from the string value read from the registry + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + inline ::std::wstring get_value_string(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value<::std::wstring>(key, subkey, value_name); + } + + /** + * @brief Reads a REG_SZ value, returning a std::wstring + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A std::wstring created from the string value read from the registry + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + inline ::std::wstring get_value_string(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value<::std::wstring>(key, nullptr, value_name); + } + + /** + * @brief Reads a REG_SZ value, returning a std::wstring + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A std::wstring created from the string value read from the registry + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + template <> + inline ::std::wstring get_value_string<::std::wstring>(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value<::std::wstring>(key, subkey, value_name); + } + + /** + * @brief Reads a REG_SZ value, returning a std::wstring + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A std::wstring created from the string value read from the registry + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + template <> + inline ::std::wstring get_value_string<::std::wstring>(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value<::std::wstring>(key, nullptr, value_name); + } + + /** + * @brief Reads a REG_EXPAND_SZ value, returning a std::wstring + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A std::wstring created from the string value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + inline ::std::wstring get_value_expanded_string(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + ::std::wstring value; + const reg_view_details::reg_view regview{key}; + regview.get_value(subkey, value_name, value, REG_EXPAND_SZ); + return value; + } + + /** + * @brief Reads a REG_EXPAND_SZ value, returning a std::wstring + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A std::wstring created from the string value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + inline ::std::wstring get_value_expanded_string(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value_expanded_string(key, nullptr, value_name); + } + + /** + * @brief Reads a REG_EXPAND_SZ value, returning a std::wstring + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A std::wstring created from the string value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + template <> + inline ::std::wstring get_value_expanded_string<::std::wstring>(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value_expanded_string(key, subkey, value_name); + } + + /** + * @brief Reads a REG_EXPAND_SZ value, returning a std::wstring + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A std::wstring created from the string value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + template <> + inline ::std::wstring get_value_expanded_string<::std::wstring>(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value_expanded_string(key, nullptr, value_name); + } +#endif // #if defined(_STRING_) + +#if defined(__WIL_OLEAUTO_H_) || defined(WIL_DOXYGEN) + /** + * @brief Reads a REG_SZ value, returning a wil::unique_bstr + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A wil::unique_bstr created from the string value read from the registry + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + template <> + inline ::wil::unique_bstr get_value_string<::wil::unique_bstr>(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value<::wil::unique_bstr>(key, subkey, value_name); + } + + /** + * @brief Reads a REG_SZ value, returning a wil::unique_bstr + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A wil::unique_bstr created from the string value read from the registry + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + template <> + inline ::wil::unique_bstr get_value_string<::wil::unique_bstr>(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value<::wil::unique_bstr>(key, nullptr, value_name); + } + + /** + * @brief Reads a REG_EXPAND_SZ value, returning a wil::unique_bstr + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A wil::unique_bstr created from the string value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + template <> + inline ::wil::unique_bstr get_value_expanded_string<::wil::unique_bstr>(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + ::wil::unique_bstr value; + const reg_view_details::reg_view regview{key}; + regview.get_value(subkey, value_name, value, REG_EXPAND_SZ); + return value; + } + + /** + * @brief Reads a REG_EXPAND_SZ value, returning a wil::unique_bstr + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A wil::unique_bstr created from the string value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + template <> + inline ::wil::unique_bstr get_value_expanded_string<::wil::unique_bstr>(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value_expanded_string<::wil::unique_bstr>(key, nullptr, value_name); + } + +#if defined(__WIL_OLEAUTO_H_STL) || defined(WIL_DOXYGEN) + /** + * @brief Reads a REG_SZ value, returning a wil::shared_bstr + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A wil::shared_bstr created from the string value read from the registry + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + template <> + inline ::wil::shared_bstr get_value_string<::wil::shared_bstr>(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value<::wil::shared_bstr>(key, subkey, value_name); + } + + /** + * @brief Reads a REG_SZ value, returning a wil::shared_bstr + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A wil::shared_bstr created from the string value read from the registry + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + template <> + inline ::wil::shared_bstr get_value_string<::wil::shared_bstr>(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value<::wil::shared_bstr>(key, nullptr, value_name); + } + + /** + * @brief Reads a REG_EXPAND_SZ value, returning a wil::unique_bstr + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A wil::shared_bstr created from the string value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + template <> + inline ::wil::shared_bstr get_value_expanded_string<::wil::shared_bstr>(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + ::wil::shared_bstr value; + const reg_view_details::reg_view regview{key}; + regview.get_value(subkey, value_name, value, REG_EXPAND_SZ); + return value; + } + + /** + * @brief Reads a REG_EXPAND_SZ value, returning a wil::shared_bstr + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A wil::shared_bstr created from the string value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + template <> + inline ::wil::shared_bstr get_value_expanded_string<::wil::shared_bstr>(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value_expanded_string<::wil::shared_bstr>(key, nullptr, value_name); + } +#endif // #if defined(__WIL_OLEAUTO_H_STL) +#endif // #if defined(__WIL_OLEAUTO_H_) + +#if defined(__WIL_OBJBASE_H_) || defined(WIL_DOXYGEN) + /** + * @brief Reads a REG_SZ value, returning a wil::unique_cotaskmem_string + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A wil::unique_cotaskmem_string created from the string value read from the registry + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + template <> + inline ::wil::unique_cotaskmem_string get_value_string<::wil::unique_cotaskmem_string>(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value<::wil::unique_cotaskmem_string>(key, subkey, value_name); + } + + /** + * @brief Reads a REG_SZ value, returning a wil::unique_cotaskmem_string + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A wil::unique_cotaskmem_string created from the string value read from the registry + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + template <> + inline ::wil::unique_cotaskmem_string get_value_string<::wil::unique_cotaskmem_string>(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value<::wil::unique_cotaskmem_string>(key, nullptr, value_name); + } + + /** + * @brief Reads a REG_EXPAND_SZ value, returning a wil::unique_cotaskmem_string + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A wil::unique_cotaskmem_string created from the string value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + template <> + inline ::wil::unique_cotaskmem_string get_value_expanded_string<::wil::unique_cotaskmem_string>( + HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + ::wil::unique_cotaskmem_string value; + const reg_view_details::reg_view regview{key}; + regview.get_value(subkey, value_name, value, REG_EXPAND_SZ); + return value; + } + + /** + * @brief Reads a REG_EXPAND_SZ value, returning a wil::unique_cotaskmem_string + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A wil::unique_cotaskmem_string created from the string value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + template <> + inline ::wil::unique_cotaskmem_string get_value_expanded_string<::wil::unique_cotaskmem_string>(HKEY key, _In_opt_ PCWSTR value_name) + { + return wil::reg::get_value_expanded_string<::wil::unique_cotaskmem_string>(key, nullptr, value_name); + } + +#if defined(__WIL_OBJBASE_H_STL) || defined(WIL_DOXYGEN) + /** + * @brief Reads a REG_SZ value, returning a wil::shared_cotaskmem_string + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A wil::shared_cotaskmem_string created from the string value read from the registry + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + template <> + inline ::wil::shared_cotaskmem_string get_value_string<::wil::shared_cotaskmem_string>(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value<::wil::shared_cotaskmem_string>(key, subkey, value_name); + } + + /** + * @brief Reads a REG_SZ value, returning a wil::shared_cotaskmem_string + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A wil::shared_cotaskmem_string created from the string value read from the registry + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + template <> + inline ::wil::shared_cotaskmem_string get_value_string<::wil::shared_cotaskmem_string>(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value<::wil::shared_cotaskmem_string>(key, nullptr, value_name); + } + + /** + * @brief Reads a REG_EXPAND_SZ value, returning a wil::shared_cotaskmem_string + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A wil::shared_cotaskmem_string created from the string value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + template <> + inline ::wil::shared_cotaskmem_string get_value_expanded_string<::wil::shared_cotaskmem_string>( + HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + ::wil::shared_cotaskmem_string value; + const reg_view_details::reg_view regview{key}; + regview.get_value(subkey, value_name, value, REG_EXPAND_SZ); + return value; + } + + /** + * @brief Reads a REG_EXPAND_SZ value, returning a wil::shared_cotaskmem_string + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A wil::shared_cotaskmem_string created from the string value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + template <> + inline ::wil::shared_cotaskmem_string get_value_expanded_string<::wil::shared_cotaskmem_string>(HKEY key, _In_opt_ PCWSTR value_name) + { + return wil::reg::get_value_expanded_string<::wil::shared_cotaskmem_string>(key, nullptr, value_name); + } +#endif // #if defined(__WIL_OBJBASE_H_STL) +#endif // defined(__WIL_OBJBASE_H_) + +#if defined(_VECTOR_) || defined(WIL_DOXYGEN) + /** + * @brief Reads a registry value of the specified type, returning a std::vector + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @param type The registry type for the specified registry value to read from - see RegGetValueW + * @return A std::vector containing the bytes of the specified registry value + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + inline ::std::vector get_value_binary(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, uint32_t type) + { + ::std::vector return_value{}; + const reg_view_details::reg_view regview{key}; + regview.get_value(subkey, value_name, return_value, type); + return return_value; + } + + /** + * @brief Reads a registry value of the specified type, returning a std::vector + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @param type The registry type for the specified registry value to read from - see RegGetValueW + * @return A std::vector containing the bytes of the specified registry value + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + */ + inline ::std::vector get_value_binary(HKEY key, _In_opt_ PCWSTR value_name, uint32_t type) + { + return ::wil::reg::get_value_binary(key, nullptr, value_name, type); + } +#endif // #if defined(_VECTOR_) + +#if (defined(_VECTOR_) && defined(_STRING_)) || defined(WIL_DOXYGEN) + /** + * @brief Reads a REG_MULTI_SZ value, returning a std::vector + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A vector of strings read from the REG_MULTI_SZ. Note: embedded nulls will be read as empty strings. See remarks. + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + * + * @remark Note that will return empty strings for embedded nulls - it won't stop at the first double-null character + * e.g. a REG_MULTI_SZ of L"string1\0\0string2\0\0string3\0\0" + * returns a vector of size 5: L"string1", empty-string, L"string2", empty-string, L"string3" + */ + template <> + inline ::std::vector<::std::wstring> get_value<::std::vector<::std::wstring>>(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + ::std::vector<::std::wstring> return_value; + ::std::vector rawData{::wil::reg::get_value_binary(key, subkey, value_name, REG_MULTI_SZ)}; + if (!rawData.empty()) + { + auto* const begin = reinterpret_cast(rawData.data()); + auto* const end = begin + rawData.size() / sizeof(wchar_t); + return_value = ::wil::reg::reg_view_details::get_wstring_vector_from_multistring(begin, end); + } + + return return_value; + } + + /** + * @brief Reads a REG_MULTI_SZ value, returning a std::vector + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A vector of strings read from the REG_MULTI_SZ. Note: embedded nulls will be read as empty strings. See remarks. + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + * + * @remark Note that will return empty strings for embedded nulls - it won't stop at the first double-null character + * e.g. a REG_MULTI_SZ of L"string1\0\0string2\0\0string3\0\0" + * returns a vector of size 5: L"string1", empty-string, L"string2", empty-string, L"string3" + */ + template <> + inline ::std::vector<::std::wstring> get_value<::std::vector<::std::wstring>>(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value<::std::vector<::std::wstring>>(key, nullptr, value_name); + } + + /** + * @brief Reads a REG_MULTI_SZ value, returning a std::vector + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A vector of strings read from the REG_MULTI_SZ. Note: embedded nulls will be read as empty strings. See remarks. + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + * + * @remark Note that will return empty strings for embedded nulls - it won't stop at the first double-null character + * e.g. a REG_MULTI_SZ of L"string1\0\0string2\0\0string3\0\0" + * returns a vector of size 5: L"string1", empty-string, L"string2", empty-string, L"string3" + */ + inline ::std::vector<::std::wstring> get_value_multistring(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value<::std::vector<::std::wstring>>(key, subkey, value_name); + } + + /** + * @brief Reads a REG_MULTI_SZ value, returning a std::vector + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return A vector of strings read from the REG_MULTI_SZ. Note: embedded nulls will be read as empty strings. See remarks. + * @exception std::exception (including wil::ResultException) will be thrown on all failures, including value not found + * + * @remark Note that will return empty strings for embedded nulls - it won't stop at the first double-null character + * e.g. a REG_MULTI_SZ of L"string1\0\0string2\0\0string3\0\0" + * returns a vector of size 5: L"string1", empty-string, L"string2", empty-string, L"string3" + */ + inline ::std::vector<::std::wstring> get_value_multistring(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::get_value<::std::vector<::std::wstring>>(key, nullptr, value_name); + } +#endif // #if defined(_VECTOR_) && defined(_STRING_) + +#if (defined(_OPTIONAL_) && defined(__cpp_lib_optional)) || defined(WIL_DOXYGEN) + // + // template + // void try_get_value(...) + // + // - Reads a value under a specified key and subkey, deducing registry type from the type parameter T. + // - throws a std::exception on failure (including wil::ResultException), except if the registry value was not found + // returns a std::nullopt if the registry value is not found + // + // Examples using the returned std::optional + // - Caller should ensure the code handles a possible std::exception that will be thrown on all errors except value not found + // + // std::optional opt_dword_value = wil::reg::try_get_value(key, L"dword_value_name"); + // if (opt_dword_value.has_value()) + // { + // // opt_dword_value.value() returns the uint32_t read from the registry + // } + // else + // { + // // the registry value did not exist + // } + // // if the caller wants to apply a default value of 0, they can call value_or() + // uint32_t opt_dword_value = wil::reg::try_get_value(key, L"dword_value_name").value_or(0); + // + // Examples using the returned std::optional + // std::optional opt_string_value = wil::reg::try_get_value_string(key, L"string_value_name"); + // if (opt_string_value.has_value()) + // { + // // opt_string_value.value() returns the std::wstring read from the registry + // // the below avoids copying the std::wstring as value() here returns a std::wstring& + // PCWSTR string_value = opt_string_value.value().c_str(); + // } + // else + // { + // // the registry value did not exist + // } + // + // // if the caller wants to apply a default value of L"default", they can call value_or() + // // note that std::optional only attempts to construct a std::wstring for L"default" if the std::optional is empty (std::nullopt) + // // thus only allocating a new std::wstring for the default value when it's needed + // std::optional opt_string_value = wil::reg::try_get_value_string(key, L"string_value_name").value_or(L"default"); + // + // Examples of usage: + // std::optional opt_dword_value = wil::reg::try_get_value(key, L"subkey", L"dword_value_name"); + // std::optional opt_qword_value = wil::reg::try_get_value(key, L"subkey", L"qword_value_name); + // std::optional opt_string_value = wil::reg::try_get_value(key, L"subkey", L"string_value_name"); + // + // A subkey is not required if the key is opened where this should write the value; e.g. + // std::optional opt_dword_value = wil::reg::try_get_value(key, L"dword_value_name"); + // std::optional opt_qword_value = wil::reg::try_get_value(key, L"qword_value_name); + // std::optional opt_string_value = wil::reg::try_get_value(key, L"string_value_name"); + // + // The template type does not need to be specified if using functions written for a targeted type; e.g. + // std::optional opt_dword_value = wil::reg::try_get_value_dword(key, L"dword_value_name"); + // std::optional opt_qword_value = wil::reg::try_get_value_qword(key, L"qword_value_name"); + // std::optional opt_string_value = wil::reg::try_get_value_string(key, L"string_value_name"); + // + // Values with REG_EXPAND_SZ can be read into each of the string types; e.g.: + // std::optional opt_expanded_string_value = wil::reg::try_get_value_expanded_string(key, L"string_value_name_with_environment_variables"); + // + // Values can be read directly into a vector of bytes - the registry type must be specified; e.g.: + // std::optional> opt_data = wil::reg::try_get_value_binary(key, L"binary_value_name", REG_BINARY); + // + // Multi-string values can be read into a std::vector; e.g.: + // std::optional<::std::vector<::std::wstring>> try_get_value_multistring(key, L"multi_string_value_name"); + // See the definition of try_get_value_multistring before for usage guidance + // + // Reading REG_SZ and REG_EXPAND_SZ types are done through the below templated try_get_value_string and try_get_value_expanded_string functions + // Where the template type is the type to receive the string value + // The default template type is std::wstring, available if the caller has included the STL header + // + // Reading a bstr is returned in a std::optional - because wil::unique_bstr cannot be copied and thus is difficult to work with a std::optional + // std::optional shared_value { wil::reg::try_get_value_string<::wil::shared_bstr>(key, L"string_value_name") }; + // + // Reading a cotaskmem string is returned in a std::optional - because wil::unique_cotaskmem_string cannot be copied and thus is difficult to work with a std::optional + // std::optional opt_shared_value { wil::reg::try_get_value_string(key, L"string_value_name") }; + // + // Blocking try_get_value_string template types that are not already specialized - this gives a much friendlier compiler error message + template + ::std::optional try_get_value_string(HKEY /*key*/, _In_opt_ PCWSTR /*subkey*/, _In_opt_ PCWSTR /*value_name*/) + { + static_assert(sizeof(T) != sizeof(T), "Unsupported type for try_get_value_string"); + } + + template + ::std::optional try_get_value_string(HKEY /*key*/, _In_opt_ PCWSTR /*value_name*/) + { + static_assert(sizeof(T) != sizeof(T), "Unsupported type for try_get_value_string"); + } + + template + ::std::optional try_get_value_expanded_string(HKEY /*key*/, _In_opt_ PCWSTR /*subkey*/, _In_opt_ PCWSTR /*value_name*/) + { + static_assert(sizeof(T) != sizeof(T), "Unsupported type for try_get_value_expanded_string"); + } + + template + ::std::optional try_get_value_expanded_string(HKEY /*key*/, _In_opt_ PCWSTR /*value_name*/) + { + static_assert(sizeof(T) != sizeof(T), "Unsupported type for try_get_value_expanded_string"); + } + + /** + * @brief Attempts to read a value under a specified key and subkey, returning in a std::optional, deducing registry type from + * the type parameter T. + * @tparam T The type to read, which will be placed into a std::optional (the registry value type is deduced from T) + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value (of type T) read from the registry value, in a std::optional. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + template + ::std::optional try_get_value(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { +#if defined(__WIL_OLEAUTO_H_) + // not allowing unique types with try_get_value: wil::unique_bstr cannot be copied and thus is difficult to work with a std::optional + static_assert(!wistd::is_same_v, "try_get with wil::unique_bstr is disabled"); +#endif // #if defined(__WIL_OLEAUTO_H_) +#if defined(__WIL_OBJBASE_H_) + // not allowing unique types with try_get_value: wil::unique_cotaskmem_string cannot be copied and thus is difficult to work with a std::optional + static_assert(!wistd::is_same_v, "try_get with wil::unique_cotaskmem_string is disabled"); +#endif // #if defined(__WIL_OBJBASE_H_) + + const reg_view_details::reg_view regview{key}; + return regview.try_get_value(subkey, value_name); + } + + /** + * @brief Attempts to read a value under a specified key, returning the value in a std::optional, deducing registry type from + * the type parameter T. + * @tparam T The type to read, which will be placed into a std::optional (the registry value type is deduced from T) + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value (of type T) read from the registry value, in a std::optional. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + template + ::std::optional try_get_value(HKEY key, _In_opt_ PCWSTR value_name) + { +#if defined(__WIL_OLEAUTO_H_) + // not allowing unique types with try_get_value: wil::unique_bstr cannot be copied and thus is difficult to work with a std::optional + static_assert(!wistd::is_same_v, "try_get with wil::unique_bstr is disabled"); +#endif // #if defined(__WIL_OLEAUTO_H_) +#if defined(__WIL_OBJBASE_H_) + // not allowing unique types with try_get_value: wil::unique_cotaskmem_string cannot be copied and thus is difficult to work with a std::optional + static_assert(!wistd::is_same_v, "try_get with wil::unique_cotaskmem_string is disabled"); +#endif // #if defined(__WIL_OBJBASE_H_) + + return ::wil::reg::try_get_value(key, nullptr, value_name); + } + + /** + * @brief Attempts to read a REG_DWORD value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value, in a std::optional. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + inline ::std::optional try_get_value_dword(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::try_get_value(key, subkey, value_name); + } + + /** + * @brief Attempts to read a REG_DWORD value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value, in a std::optional. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + inline ::std::optional try_get_value_dword(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::try_get_value(key, nullptr, value_name); + } + + /** + * @brief Attempts to read a REG_QWORD value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value, in a std::optional. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + inline ::std::optional try_get_value_qword(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::try_get_value(key, subkey, value_name); + } + + /** + * @brief Attempts to read a REG_QWORD value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value, in a std::optional. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + inline ::std::optional try_get_value_qword(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::try_get_value(key, nullptr, value_name); + } + +#if defined(_VECTOR_) || defined(WIL_DOXYGEN) + /** + * @brief Attempts to read a value under a specified key requiring the specified type, returning the raw bytes in a + * std::optional + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @param type The registry type for the specified registry value to read from - see RegGetValueW + * @return The raw bytes read from the registry value stored in a std::optional>. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + inline ::std::optional<::std::vector> try_get_value_binary(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, uint32_t type) + { + const reg_view_details::reg_view regview{key}; + return regview.try_get_value<::std::vector>(subkey, value_name, type); + } + + /** + * @brief Attempts to read a value under a specified key requiring the specified type, returning the raw bytes in a + * std::optional + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @param type The registry type for the specified registry value to read from - see RegGetValueW + * @return The raw bytes read from the registry value stored in a std::optional>. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + inline ::std::optional<::std::vector> try_get_value_binary(HKEY key, _In_opt_ PCWSTR value_name, uint32_t type) + { + return ::wil::reg::try_get_value_binary(key, nullptr, value_name, type); + } +#endif // #if defined(_VECTOR_) + +#if defined(_STRING_) || defined(WIL_DOXYGEN) + /** + * @brief Attempts to read a REG_SZ value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value, in a std::optional. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + inline ::std::optional<::std::wstring> try_get_value_string(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + const reg_view_details::reg_view regview{key}; + return regview.try_get_value<::std::wstring>(subkey, value_name); + } + + /** + * @brief Attempts to read a REG_SZ value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value, in a std::optional. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + inline ::std::optional<::std::wstring> try_get_value_string(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::try_get_value_string(key, nullptr, value_name); + } + + /** + * @brief Attempts to read a REG_SZ value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value, in a std::optional. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + template <> + inline ::std::optional<::std::wstring> try_get_value_string<::std::wstring>(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::try_get_value_string(key, subkey, value_name); + } + + /** + * @brief Attempts to read a REG_SZ value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value, in a std::optional. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + template <> + inline ::std::optional<::std::wstring> try_get_value_string<::std::wstring>(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::try_get_value_string(key, nullptr, value_name); + } + + /** + * @brief Attempts to read a REG_EXPAND_SZ value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value of the template type std::optional, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + inline ::std::optional<::std::wstring> try_get_value_expanded_string(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + const reg_view_details::reg_view regview{key}; + return regview.try_get_value<::std::wstring>(subkey, value_name, REG_EXPAND_SZ); + } + + /** + * @brief Attempts to read a REG_EXPAND_SZ value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value of the template type std::optional, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + inline ::std::optional<::std::wstring> try_get_value_expanded_string(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::try_get_value_expanded_string(key, nullptr, value_name); + } + + /** + * @brief Attempts to read a REG_EXPAND_SZ value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value of the template type std::optional, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + template <> + inline ::std::optional<::std::wstring> try_get_value_expanded_string<::std::wstring>(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::try_get_value_expanded_string(key, subkey, value_name); + } + + /** + * @brief Attempts to read a REG_EXPAND_SZ value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value of the template type std::optional, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + template <> + inline ::std::optional<::std::wstring> try_get_value_expanded_string<::std::wstring>(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::try_get_value_expanded_string(key, nullptr, value_name); + } +#endif // #if defined(_STRING_) + +#if defined(__WIL_OLEAUTO_H_STL) || defined(WIL_DOXYGEN) + /** + * @brief Attempts to read a REG_SZ value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value of the template type std::optional. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + template <> + inline ::std::optional<::wil::shared_bstr> try_get_value_string<::wil::shared_bstr>(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + const reg_view_details::reg_view regview{key}; + return regview.try_get_value<::wil::shared_bstr>(subkey, value_name); + } + + /** + * @brief Attempts to read a REG_SZ value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value of the template type std::optional. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + template <> + inline ::std::optional<::wil::shared_bstr> try_get_value_string<::wil::shared_bstr>(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::try_get_value_string<::wil::shared_bstr>(key, nullptr, value_name); + } + + /** + * @brief Attempts to read a REG_EXPAND_SZ value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value of the template type std::optional, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + template <> + inline ::std::optional<::wil::shared_bstr> try_get_value_expanded_string<::wil::shared_bstr>( + HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + const reg_view_details::reg_view regview{key}; + return regview.try_get_value<::wil::shared_bstr>(subkey, value_name, REG_EXPAND_SZ); + } + + /** + * @brief Attempts to read a REG_EXPAND_SZ value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value of the template type std::optional, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + template <> + inline ::std::optional<::wil::shared_bstr> try_get_value_expanded_string<::wil::shared_bstr>(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::try_get_value_expanded_string<::wil::shared_bstr>(key, nullptr, value_name); + } +#endif // #if defined(__WIL_OLEAUTO_H_STL) + +#if defined(__WIL_OBJBASE_H_STL) || defined(WIL_DOXYGEN) + /** + * @brief Attempts to read a REG_SZ value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value of the template type std::optional. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + template <> + inline ::std::optional<::wil::shared_cotaskmem_string> try_get_value_string<::wil::shared_cotaskmem_string>( + HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + const reg_view_details::reg_view regview{key}; + return regview.try_get_value<::wil::shared_cotaskmem_string>(subkey, value_name); + } + + /** + * @brief Attempts to read a REG_SZ value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value of the template type std::optional + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + template <> + inline ::std::optional<::wil::shared_cotaskmem_string> try_get_value_string<::wil::shared_cotaskmem_string>(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::try_get_value_string<::wil::shared_cotaskmem_string>(key, nullptr, value_name); + } + + /** + * @brief Attempts to read a REG_EXPAND_SZ value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value of the template type std::optional<:wil::shared_cotaskmem_string>, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + template <> + inline ::std::optional<::wil::shared_cotaskmem_string> try_get_value_expanded_string<::wil::shared_cotaskmem_string>( + HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + const reg_view_details::reg_view regview{key}; + return regview.try_get_value<::wil::shared_cotaskmem_string>(subkey, value_name, REG_EXPAND_SZ); + } + + /** + * @brief Attempts to read a REG_EXPAND_SZ value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value of the template type std::optional, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + template <> + inline ::std::optional<::wil::shared_cotaskmem_string> try_get_value_expanded_string<::wil::shared_cotaskmem_string>( + HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::try_get_value_expanded_string<::wil::shared_cotaskmem_string>(key, nullptr, value_name); + } +#endif // defined(__WIL_OBJBASE_H_STL) + +#if (defined(_VECTOR_) && defined(_STRING_)) || defined(WIL_DOXYGEN) + /** + * @brief Attempts to read a REG_MULTI_SZ value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value marshaled to a std::optional>. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + template <> + inline ::std::optional<::std::vector<::std::wstring>> try_get_value<::std::vector<::std::wstring>>( + HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + ::std::vector<::std::wstring> value; + const auto hr = ::wil::ResultFromException([&] { + value = ::wil::reg::get_value_multistring(key, subkey, value_name); + }); + if (SUCCEEDED(hr)) + { + return value; + } + + if (!::wil::reg::is_registry_not_found(hr)) + { + THROW_HR(HRESULT_FROM_WIN32(hr)); + } + + return ::std::nullopt; + } + + /** + * @brief Attempts to read a REG_MULTI_SZ value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value marshaled to a std::optional>. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + template <> + inline ::std::optional<::std::vector<::std::wstring>> try_get_value<::std::vector<::std::wstring>>(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::try_get_value<::std::vector<::std::wstring>>(key, nullptr, value_name); + } + + /** + * @brief Attempts to read a REG_MULTI_SZ value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value marshaled to a std::optional>. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + inline ::std::optional<::std::vector<::std::wstring>> try_get_value_multistring(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::try_get_value<::std::vector<::std::wstring>>(key, subkey, value_name); + } + + /** + * @brief Attempts to read a REG_MULTI_SZ value under a specified key, returning the value in a std::optional + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be updated. + * Can be nullptr to read from the unnamed default registry value. + * @return The value read from the registry value marshaled to a std::optional>. + * Returns std::nullopt if the value does not exist. + * @exception std::exception (including wil::ResultException) will be thrown on failures except value not found + */ + inline ::std::optional<::std::vector<::std::wstring>> try_get_value_multistring(HKEY key, _In_opt_ PCWSTR value_name) + { + return ::wil::reg::try_get_value<::std::vector<::std::wstring>>(key, nullptr, value_name); + } +#endif // #if defined (_VECTOR_) && defined (_STRING_) +#endif // #if defined (_OPTIONAL_) && defined(__cpp_lib_optional) +#endif // #if defined(WIL_ENABLE_EXCEPTIONS) + + // + // template + // HRESULT get_value_nothrow(...) + // + // - Reads a value from under a specified key + // - The required type of registry value being read from is determined by the template type T + // - Returns an HRESULT error code indicating success or failure (does not throw C++ exceptions) + // + // Examples of usage (the template type does not need to be explicitly specified) + // uint32_t dword_value{}; + // hr = wil::reg::get_value_nothrow(key, L"subkey", L"dword_value_name", &dword_value); // reads a REG_DWORD + // uint64_t qword_value{}; + // hr = wil::reg::get_value_nothrow(key, L"subkey", L"qword_value_name", &qword_value); // reads a REG_QWORD + // wil::unique_bstr string_value{}; + // hr = wil::reg::get_value_nothrow(key, L"subkey", L"string_value_name", string_value); // reads a REG_SZ + // + // A subkey is not required if the key is opened where this should write the value: + // hr = wil::reg::get_value_nothrow(key, L"dword_value_name", &dword_value); // reads a REG_DWORD + // hr = wil::reg::get_value_nothrow(key, L"qword_value_name", &qword_value); // reads a REG_QWORD + // hr = wil::reg::get_value_nothrow(key, L"string_value_name", string_value); // reads a REG_SZ + // + // Can also specify the registry type in the function name: + // hr = wil::reg::get_value_dword_nothrow(key, L"dword_value_name", &dword_value); // reads a REG_DWORD + // hr = wil::reg::get_value_qword_nothrow(key, L"qword_value_name", &qword_value); // reads a REG_QWORD + // hr = wil::reg::get_value_string_nothrow(key, L"string_value_name", string_value); // reads a REG_SZ + // + // Example storing directly into a WCHAR array - note will return the required number of bytes if the supplied array is too small + // WCHAR string_value[100]{}; + // uint32_t requiredBytes{}; + // hr = wil::reg::get_value_string_nothrow(key, L"string_value_name", string_value, &requiredBytes); + // + // Example of usage writing a REG_MULTI_SZ + // wil::unique_cotaskmem_array_ptr string_values{}; + // hr = wil::reg::get_value_multistring_nothrow(key, L"multi_string_value_name", string_values); + // + // Values can be written directly from a vector of bytes - the registry type must be specified; e.g.: + // wil::unique_cotaskmem_array_ptr raw_value{}; + // hr = wil::reg::get_value_binary_nothrow(key, L"binary_value_name", REG_BINARY, raw_value); + // + // Reading REG_SZ and REG_EXPAND_SZ types are done through the below templated get_value_string_nothrow and get_value_expanded_string_nothrow functions + // Where the template type is the type to receive the string value + // The default template type is std::wstring, available if the caller has included the STL header + // + // Example storing a string in a wil::unique_bstr, wil::shared_bstr, wil::unique_cotaskmem_string, or wil::shared_cotaskmem_string + /// - These string types are passed by reference, not by pointer, because the wil types overload the & operator + // + // wil::unique_bstr bstr_value{}; + // hr = wil::reg::get_value_nothrow(key, L"string_value_name", bstr_value); + // // or can specify explicitly reading a string into a wil::unique_bstr type + // hr = wil::reg::get_value_string_nothrow(key, L"string_value_name", bstr_value); + // + // wil::shared_bstr shared_bstr_value{}; + // hr = wil::reg::get_value_nothrow(key, L"string_value_name", shared_bstr_value); + // // or can specify explicitly reading a string into a wil::shared_bstr type + // hr = wil::reg::get_value_string_nothrow(key, L"string_value_name", shared_bstr_value); + // + // wil::unique_cotaskmem_string string_value{}; + // hr = wil::reg::get_value_nothrow(key, L"string_value_name", string_value); + // // or can specify explicitly reading a string into a wil::unique_cotaskmem_string type + // hr = wil::reg::get_value_string_nothrow(key, L"string_value_name", string_value); + // + // wil::shared_cotaskmem_string shared_string_value{}; + // hr = wil::reg::get_value_nothrow(key, L"string_value_name", shared_string_value); + // // or can specify explicitly reading a string into a wil::shared_cotaskmem_string type + // hr = wil::reg::get_value_string_nothrow(key, L"string_value_name", shared_string_value); + // + + /** + * @brief Reads a value under a specified key, the registry type based off the templated type passed as data + * @tparam T The type of the data being set (the registry value type is deduced from T). + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A pointer-to-T receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + template >* = nullptr> + HRESULT get_value_nothrow(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, _Out_ T* return_value) WI_NOEXCEPT + { + const reg_view_details::reg_view_nothrow regview{key}; + return regview.get_value(subkey, value_name, *return_value); + } + + /** + * @brief Reads a value under a specified key, the registry type based off the templated type passed as data + * @tparam T The type of the data being set (the registry value type is deduced from T). + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A pointer-to-T receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + template >* = nullptr> + HRESULT get_value_nothrow(HKEY key, _In_opt_ PCWSTR value_name, _Out_ T* return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_nothrow(key, nullptr, value_name, return_value); + } + + /** + * @brief Reads a REG_SZ value under a specified key + * @tparam Length The length of the WCHAR array passed as an OUT parameter + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A WCHAR array receiving the value read from the registry. + * Will write to the WCHAR array the string value read from the registry, guaranteeing null-termination + * @param[out] requiredBytes An optional pointer to a unsigned 32-bit value to receive the required bytes of the string in the + * registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + template || wistd::is_same_v>* = nullptr> + HRESULT get_value_string_nothrow( + HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, WCHAR (&return_value)[Length], _Out_opt_ DwordType* requiredBytes) WI_NOEXCEPT + { + const reg_view_details::reg_view_nothrow regview{key}; + return regview.get_value_char_array(subkey, value_name, return_value, REG_SZ, requiredBytes); + } + + /** + * @brief Reads a REG_SZ value under a specified key + * @tparam Length The length of the WCHAR array passed as an OUT parameter + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A WCHAR array receiving the value read from the registry. + * Will write to the WCHAR array the string value read from the registry, guaranteeing null-termination + * @param[out] requiredBytes An optional pointer to an unsigned 32-bit value to receive the required bytes of the string to be + * read + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + template || wistd::is_same_v>* = nullptr> + HRESULT get_value_string_nothrow(HKEY key, _In_opt_ PCWSTR value_name, WCHAR (&return_value)[Length], _Out_opt_ DwordType* requiredBytes) WI_NOEXCEPT + { + return ::wil::reg::get_value_string_nothrow(key, nullptr, value_name, return_value, requiredBytes); + } + + /** + * @brief Reads a REG_SZ value under a specified key + * @tparam Length The length of the WCHAR array passed as an OUT parameter + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A WCHAR array receiving the value read from the registry. + * Will write to the WCHAR array the string value read from the registry, guaranteeing null-termination + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + template + HRESULT get_value_string_nothrow(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, WCHAR (&return_value)[Length]) WI_NOEXCEPT + { + constexpr uint32_t* null_out_param = nullptr; + const reg_view_details::reg_view_nothrow regview{key}; + return regview.get_value_char_array(subkey, value_name, return_value, REG_SZ, null_out_param); + } + + /** + * @brief Reads a REG_SZ value under a specified key + * @tparam Length The length of the WCHAR array passed as an OUT parameter + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A WCHAR array receiving the value read from the registry. + * Will write to the WCHAR array the string value read from the registry, guaranteeing null-termination + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + template + HRESULT get_value_string_nothrow(HKEY key, _In_opt_ PCWSTR value_name, WCHAR (&return_value)[Length]) WI_NOEXCEPT + { + return ::wil::reg::get_value_string_nothrow(key, nullptr, value_name, return_value); + } + + /** + * @brief Reads a REG_SZ value under a specified key + * @tparam Length The length of the WCHAR array passed as an OUT parameter + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A WCHAR array receiving the value read from the registry. + * Will write to the WCHAR array the string value read from the registry, guaranteeing null-termination + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + template + HRESULT get_value_nothrow(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, WCHAR (&return_value)[Length]) WI_NOEXCEPT + { + return ::wil::reg::get_value_string_nothrow(key, subkey, value_name, return_value); + } + + /** + * @brief Reads a REG_SZ value under a specified key + * @tparam Length The length of the WCHAR array passed as an OUT parameter + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A WCHAR array receiving the value read from the registry. + * Will write to the WCHAR array the string value read from the registry, guaranteeing null-termination + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + template + HRESULT get_value_nothrow(HKEY key, _In_opt_ PCWSTR value_name, WCHAR (&return_value)[Length]) WI_NOEXCEPT + { + return ::wil::reg::get_value_string_nothrow(key, nullptr, value_name, return_value); + } + + /** + * @brief Reads a REG_DWORD value under a specified key + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A pointer to an unsigned 32-bit value receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + template || wistd::is_same_v>* = nullptr> + HRESULT get_value_dword_nothrow(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, _Out_ DwordType* return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_nothrow(key, subkey, value_name, return_value); + } + + /** + * @brief Reads a REG_DWORD value under a specified key + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A pointer to an unsigned 32-bit value receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + template || wistd::is_same_v>* = nullptr> + HRESULT get_value_dword_nothrow(HKEY key, _In_opt_ PCWSTR value_name, _Out_ DwordType* return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_nothrow(key, nullptr, value_name, return_value); + } + + /** + * @brief Reads a REG_QWORD value under a specified key + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A uint64_t receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + template || wistd::is_same_v>* = nullptr> + HRESULT get_value_qword_nothrow(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, _Out_ QwordType* return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_nothrow(key, subkey, value_name, return_value); + } + + /** + * @brief Reads a REG_QWORD value under a specified key + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A uint64_t receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + template || wistd::is_same_v>* = nullptr> + HRESULT get_value_qword_nothrow(HKEY key, _In_opt_ PCWSTR value_name, _Out_ QwordType* return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_nothrow(key, nullptr, value_name, return_value); + } + +#if defined(__WIL_OLEAUTO_H_) || defined(WIL_DOXYGEN) + /** + * @brief Reads a REG_SZ value under a specified key + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::unique_bstr receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_nothrow(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, ::wil::unique_bstr& return_value) WI_NOEXCEPT + { + const reg_view_details::reg_view_nothrow regview{key}; + return regview.get_value(subkey, value_name, return_value); + } + + /** + * @brief Reads a REG_SZ value under a specified key + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::unique_bstr receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_nothrow(HKEY key, _In_opt_ PCWSTR value_name, ::wil::unique_bstr& return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_nothrow(key, nullptr, value_name, return_value); + } + + /** + * @brief Reads a REG_SZ value under a specified key + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::unique_bstr receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_string_nothrow(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, ::wil::unique_bstr& return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_nothrow(key, subkey, value_name, return_value.addressof()); + } + + /** + * @brief Reads a REG_SZ value under a specified key + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::unique_bstr receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_string_nothrow(HKEY key, _In_opt_ PCWSTR value_name, ::wil::unique_bstr& return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_string_nothrow(key, nullptr, value_name, return_value); + } + +#if defined(__WIL_OLEAUTO_H_STL) || defined(WIL_DOXYGEN) + /** + * @brief Reads a REG_SZ value under a specified key + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::shared_bstr receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_nothrow(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, ::wil::shared_bstr& return_value) WI_NOEXCEPT + { + const reg_view_details::reg_view_nothrow regview{key}; + return regview.get_value(subkey, value_name, return_value); + } + + /** + * @brief Reads a REG_SZ value under a specified key + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::shared_bstr receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_nothrow(HKEY key, _In_opt_ PCWSTR value_name, ::wil::shared_bstr& return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_nothrow(key, nullptr, value_name, return_value); + } + + /** + * @brief Reads a REG_SZ value under a specified key + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::shared_bstr receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_string_nothrow(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, ::wil::shared_bstr& return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_nothrow(key, subkey, value_name, return_value.addressof()); + } + + /** + * @brief Reads a REG_SZ value under a specified key + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::shared_bstr receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_string_nothrow(HKEY key, _In_opt_ PCWSTR value_name, ::wil::shared_bstr& return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_string_nothrow(key, nullptr, value_name, return_value); + } +#endif // #if defined(__WIL_OLEAUTO_H_STL) +#endif // #if defined(__WIL_OLEAUTO_H_) + +#if defined(__WIL_OBJBASE_H_) || defined(WIL_DOXYGEN) + /** + * @brief Reads a REG_SZ value under a specified key + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::unique_cotaskmem_string receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_nothrow(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, ::wil::unique_cotaskmem_string& return_value) WI_NOEXCEPT + { + return_value.reset(); + const reg_view_details::reg_view_nothrow regview{key}; + return regview.get_value(subkey, value_name, return_value); + } + + /** + * @brief Reads a REG_SZ value under a specified key + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::unique_cotaskmem_string receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_nothrow(HKEY key, _In_opt_ PCWSTR value_name, ::wil::unique_cotaskmem_string& return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_nothrow(key, nullptr, value_name, return_value); + } + + /** + * @brief Reads a REG_SZ value under a specified key + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::unique_cotaskmem_string receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_string_nothrow( + HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, ::wil::unique_cotaskmem_string& return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_nothrow(key, subkey, value_name, return_value); + } + + /** + * @brief Reads a REG_SZ value under a specified key + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::unique_cotaskmem_string receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_string_nothrow(HKEY key, _In_opt_ PCWSTR value_name, ::wil::unique_cotaskmem_string& return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_nothrow(key, nullptr, value_name, return_value); + } + +#if defined(__WIL_OBJBASE_H_STL) || defined(WIL_DOXYGEN) + /** + * @brief Reads a REG_SZ value under a specified key + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::shared_cotaskmem_string receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_nothrow(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, ::wil::shared_cotaskmem_string& return_value) WI_NOEXCEPT + { + return_value.reset(); + const reg_view_details::reg_view_nothrow regview{key}; + return regview.get_value(subkey, value_name, return_value); + } + + /** + * @brief Reads a REG_SZ value under a specified key + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::shared_cotaskmem_string receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_nothrow(HKEY key, _In_opt_ PCWSTR value_name, ::wil::shared_cotaskmem_string& return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_nothrow(key, nullptr, value_name, return_value); + } + + /** + * @brief Reads a REG_SZ value under a specified key + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::shared_cotaskmem_string receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_string_nothrow( + HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, ::wil::shared_cotaskmem_string& return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_nothrow(key, subkey, value_name, return_value); + } + + /** + * @brief Reads a REG_SZ value under a specified key + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::shared_cotaskmem_string receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_string_nothrow(HKEY key, _In_opt_ PCWSTR value_name, ::wil::shared_cotaskmem_string& return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_nothrow(key, nullptr, value_name, return_value); + } +#endif // #if defined(__WIL_OBJBASE_H_STL) +#endif // defined(__WIL_OBJBASE_H_) + +#if defined(__WIL_OBJBASE_H_) || defined(WIL_DOXYGEN) + /** + * @brief Reads the raw bytes from a registry value under a specified key of the specified type + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param type The registry type for the specified registry value to read from - see RegGetValueW + * @param[out] return_value A ::wil::unique_cotaskmem_array_ptr receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_binary_nothrow( + HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, uint32_t type, ::wil::unique_cotaskmem_array_ptr& return_value) WI_NOEXCEPT + { + // zero the vector if it already had a buffer + for (auto& byte_value : return_value) + { + byte_value = 0x00; + } + const reg_view_details::reg_view_nothrow regview{key}; + RETURN_IF_FAILED(regview.get_value<::wil::unique_cotaskmem_array_ptr>(subkey, value_name, return_value, type)); + return S_OK; + } + + /** + * @brief Reads the raw bytes from a registry value under a specified key of the specified type + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param type The registry type for the specified registry value to read from - see RegGetValueW + * @param[out] return_value A ::wil::unique_cotaskmem_array_ptr receiving the value read from the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_binary_nothrow( + HKEY key, _In_opt_ PCWSTR value_name, uint32_t type, ::wil::unique_cotaskmem_array_ptr& return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_binary_nothrow(key, nullptr, value_name, type, return_value); + } +#endif // #if defined(__WIL_OBJBASE_H_) + + /** + * @brief Reads a REG_EXPAND_SZ value under a specified key + * @tparam Length The length of the WCHAR array passed as an OUT parameter + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A WCHAR array receiving the value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW. + * Will write to the WCHAR array the string value read from the registry, guaranteeing null-termination + * @param[out] requiredBytes An optional pointer to a uint32_t to receive the required bytes of the string to be read + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + template || wistd::is_same_v>* = nullptr> + HRESULT get_value_expanded_string_nothrow( + HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, WCHAR (&return_value)[Length], _Out_opt_ DwordType* requiredBytes) WI_NOEXCEPT + { + const reg_view_details::reg_view_nothrow regview{key}; + return regview.get_value_char_array(subkey, value_name, return_value, REG_EXPAND_SZ, requiredBytes); + } + + /** + * @brief Reads a REG_EXPAND_SZ value under a specified key + * @tparam Length The length of the WCHAR array passed as an OUT parameter + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A WCHAR array receiving the value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW. + * Will write to the WCHAR array the string value read from the registry, guaranteeing null-termination + * @param[out] requiredBytes An optional pointer to a uint32_t to receive the required bytes of the string to be read + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + template || wistd::is_same_v>* = nullptr> + HRESULT get_value_expanded_string_nothrow( + HKEY key, _In_opt_ PCWSTR value_name, WCHAR (&return_value)[Length], _Out_opt_ DwordType* requiredBytes) WI_NOEXCEPT + { + return ::wil::reg::get_value_expanded_string_nothrow(key, nullptr, value_name, return_value, requiredBytes); + } + + /** + * @brief Reads a REG_EXPAND_SZ value under a specified key + * @tparam Length The length of the WCHAR array passed as an OUT parameter + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A WCHAR array receiving the value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW. + * Will write to the WCHAR array the string value read from the registry, guaranteeing null-termination + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + template + HRESULT get_value_expanded_string_nothrow(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, WCHAR (&return_value)[Length]) WI_NOEXCEPT + { + constexpr uint32_t* null_out_param = nullptr; + const reg_view_details::reg_view_nothrow regview{key}; + return regview.get_value_char_array(subkey, value_name, return_value, REG_EXPAND_SZ, null_out_param); + } + + /** + * @brief Reads a REG_EXPAND_SZ value under a specified key + * @tparam Length The length of the WCHAR array passed as an OUT parameter + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A WCHAR array receiving the value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW. + * Will write to the WCHAR array the string value read from the registry, guaranteeing null-termination + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + template + HRESULT get_value_expanded_string_nothrow(HKEY key, _In_opt_ PCWSTR value_name, WCHAR (&return_value)[Length]) WI_NOEXCEPT + { + return ::wil::reg::get_value_expanded_string_nothrow(key, nullptr, value_name, return_value); + } + +#if defined(__WIL_OLEAUTO_H_) || defined(WIL_DOXYGEN) + /** + * @brief Reads a REG_EXPAND_SZ value under a specified key + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::unique_bstr receiving the value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW. + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_expanded_string_nothrow(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, ::wil::unique_bstr& return_value) WI_NOEXCEPT + { + const reg_view_details::reg_view_nothrow regview{key}; + return regview.get_value<::wil::unique_bstr>(subkey, value_name, return_value, REG_EXPAND_SZ); + } + + /** + * @brief Reads a REG_EXPAND_SZ value under a specified key + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::unique_bstr receiving the value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW. + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_expanded_string_nothrow(HKEY key, _In_opt_ PCWSTR value_name, ::wil::unique_bstr& return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_expanded_string_nothrow(key, nullptr, value_name, return_value); + } + +#if defined(__WIL_OLEAUTO_H_STL) || defined(WIL_DOXYGEN) + /** + * @brief Reads a REG_EXPAND_SZ value under a specified key + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::shared_bstr receiving the value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW. + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_expanded_string_nothrow(HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, ::wil::shared_bstr& return_value) WI_NOEXCEPT + { + const reg_view_details::reg_view_nothrow regview{key}; + return regview.get_value<::wil::shared_bstr>(subkey, value_name, return_value, REG_EXPAND_SZ); + } + + /** + * @brief Reads a REG_EXPAND_SZ value under a specified key + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::shared_bstr receiving the value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW. + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_expanded_string_nothrow(HKEY key, _In_opt_ PCWSTR value_name, ::wil::shared_bstr& return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_expanded_string_nothrow(key, nullptr, value_name, return_value); + } +#endif // #if defined(__WIL_OLEAUTO_H_STL) +#endif // #if defined(__WIL_OLEAUTO_H_) + +#if defined(__WIL_OBJBASE_H_) || defined(WIL_DOXYGEN) + /** + * @brief Reads a REG_EXPAND_SZ value under a specified key + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::unique_cotaskmem_string receiving the value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW. + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_expanded_string_nothrow( + HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, ::wil::unique_cotaskmem_string& return_value) WI_NOEXCEPT + { + const reg_view_details::reg_view_nothrow regview{key}; + return regview.get_value<::wil::unique_cotaskmem_string>(subkey, value_name, return_value, REG_EXPAND_SZ); + } + + /** + * @brief Reads a REG_EXPAND_SZ value under a specified key + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::unique_cotaskmem_string receiving the value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW. + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_expanded_string_nothrow(HKEY key, _In_opt_ PCWSTR value_name, ::wil::unique_cotaskmem_string& return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_expanded_string_nothrow(key, nullptr, value_name, return_value); + } + +#if defined(__WIL_OBJBASE_H_STL) || defined(WIL_DOXYGEN) + /** + * @brief Reads a REG_EXPAND_SZ value under a specified key + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::shared_cotaskmem_string receiving the value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW. + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_expanded_string_nothrow( + HKEY key, _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, ::wil::shared_cotaskmem_string& return_value) WI_NOEXCEPT + { + const reg_view_details::reg_view_nothrow regview{key}; + return regview.get_value<::wil::shared_cotaskmem_string>(subkey, value_name, return_value, REG_EXPAND_SZ); + } + + /** + * @brief Reads a REG_EXPAND_SZ value under a specified key + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A wil::shared_cotaskmem_string receiving the value read from the registry, + * with environment variables expanded, as though passed through ExpandEnvironmentStringsW. + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_expanded_string_nothrow(HKEY key, _In_opt_ PCWSTR value_name, ::wil::shared_cotaskmem_string& return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_expanded_string_nothrow(key, nullptr, value_name, return_value); + } +#endif // #if defined(__WIL_OBJBASE_H_STL) +#endif // defined(__WIL_OBJBASE_H_) + +#if defined(__WIL_OBJBASE_H_) || defined(WIL_DOXYGEN) + /** + * @brief Reads a REG_MULTI_SZ value under a specified key + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A ::wil::unique_cotaskmem_array_ptr<::wil::unique_cotaskmem_string> receiving the value read from + * the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_nothrow( + HKEY key, + _In_opt_ PCWSTR subkey, + _In_opt_ PCWSTR value_name, + ::wil::unique_cotaskmem_array_ptr<::wil::unique_cotaskmem_string>& return_value) WI_NOEXCEPT + { + ::wil::unique_cotaskmem_array_ptr rawData; + RETURN_IF_FAILED(::wil::reg::get_value_binary_nothrow(key, subkey, value_name, REG_MULTI_SZ, rawData)); + if (!rawData.empty()) + { + auto* const begin = reinterpret_cast(rawData.data()); + auto* const end = begin + rawData.size() / sizeof(wchar_t); + ::wil::reg::reg_view_details::get_cotaskmemstring_array_from_multistring_nothrow(begin, end, return_value); + } + return S_OK; + } + + /** + * @brief Reads a REG_MULTI_SZ value under a specified key + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A ::wil::unique_cotaskmem_array_ptr<::wil::unique_cotaskmem_string> receiving the value read from + * the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_nothrow( + HKEY key, _In_opt_ PCWSTR value_name, ::wil::unique_cotaskmem_array_ptr<::wil::unique_cotaskmem_string>& return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_nothrow(key, nullptr, value_name, return_value); + } + + /** + * @brief Reads a REG_MULTI_SZ value under a specified key + * @param key An open or well-known registry key + * @param subkey The name of the subkey to append to `key`. + * If `nullptr`, then `key` is used without modification. + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A ::wil::unique_cotaskmem_array_ptr<::wil::unique_cotaskmem_string> receiving the value read from + * the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_multistring_nothrow( + HKEY key, + _In_opt_ PCWSTR subkey, + _In_opt_ PCWSTR value_name, + ::wil::unique_cotaskmem_array_ptr<::wil::unique_cotaskmem_string>& return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_nothrow(key, subkey, value_name, return_value); + } + + /** + * @brief Reads a REG_MULTI_SZ value under a specified key + * @param key An open or well-known registry key + * @param value_name The name of the registry value whose data is to be read. + * Can be nullptr to read from the unnamed default registry value. + * @param[out] return_value A ::wil::unique_cotaskmem_array_ptr<::wil::unique_cotaskmem_string> receiving the value read from + * the registry + * @return HRESULT error code indicating success or failure (does not throw C++ exceptions) + */ + inline HRESULT get_value_multistring_nothrow( + HKEY key, _In_opt_ PCWSTR value_name, ::wil::unique_cotaskmem_array_ptr<::wil::unique_cotaskmem_string>& return_value) WI_NOEXCEPT + { + return ::wil::reg::get_value_nothrow(key, nullptr, value_name, return_value); + } +#endif // #if defined(__WIL_OBJBASE_H_) +} // namespace reg + +// 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 ref-count can be increased from a non zero value, + // false it was zero implying 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; +} // namespace details +/// @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 + const 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/libs/wil/wil/registry_helpers.h b/libs/wil/wil/registry_helpers.h new file mode 100644 index 00000000..9ebf2645 --- /dev/null +++ b/libs/wil/wil/registry_helpers.h @@ -0,0 +1,1951 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! Helpers for iterating over keys and values in the registry. +#ifndef __WIL_REGISTRY_HELPERS_INCLUDED +#define __WIL_REGISTRY_HELPERS_INCLUDED + +#if defined(_STRING_) || defined(_VECTOR_) || (defined(__cpp_lib_optional) && defined(_OPTIONAL_)) || defined(WIL_DOXYGEN) +#include +#include +#endif + +#include +#include +#include "resource.h" + +#ifdef _KERNEL_MODE +#error This header is not supported in kernel-mode. +#endif + +namespace wil +{ +namespace reg +{ + /** + * @brief Helper function to translate registry return values if the value was not found + * @param hr HRESULT to test from registry APIs + * @return boolean if the HRESULT indicates the registry value was not found + */ + constexpr bool is_registry_not_found(HRESULT hr) WI_NOEXCEPT + { + return (hr == __HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || (hr == __HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)); + } + + /** + * @brief Helper function to translate registry return values if the buffer was too small + * @param hr HRESULT to test from registry APIs + * @return boolean if the HRESULT indicates the buffer was too small for the value being read + */ + constexpr bool is_registry_buffer_too_small(HRESULT hr) WI_NOEXCEPT + { + return hr == __HRESULT_FROM_WIN32(ERROR_MORE_DATA); + } + + // Access rights for opening registry keys. See https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-key-security-and-access-rights. + enum class key_access + { + // Open key for reading. + read, + + // Open key for reading and writing. Equivalent to KEY_ALL_ACCESS. + readwrite, + }; + + /// @cond + namespace reg_view_details + { + constexpr DWORD get_value_flags_from_value_type(DWORD type) WI_NOEXCEPT + { + switch (type) + { + case REG_DWORD: + return RRF_RT_REG_DWORD; + case REG_QWORD: + return RRF_RT_REG_QWORD; + case REG_SZ: + return RRF_RT_REG_SZ | RRF_RT_REG_EXPAND_SZ | RRF_NOEXPAND; + case REG_EXPAND_SZ: + return RRF_RT_REG_SZ | RRF_RT_REG_EXPAND_SZ; + case REG_MULTI_SZ: + return RRF_RT_REG_MULTI_SZ; + case REG_BINARY: + return RRF_RT_REG_BINARY; + // the caller can directly specify their own flags if they need to + default: + return type; + } + } + + constexpr DWORD get_access_flags(key_access access) WI_NOEXCEPT + { + switch (access) + { + case key_access::read: + return KEY_READ; + case key_access::readwrite: + return KEY_ALL_ACCESS; + } + FAIL_FAST(); + RESULT_NORETURN_RESULT(0); + } + + /** + * @brief A utility function that walks a contiguous wchar_t container looking for strings within a multi-string + * @tparam InputIt An iterator type that reference a container that holds wchar_t characters to translate into individual + * strings + * @tparam Fn A callback function to be called each time a string is found - given the [begin, end] iterators referencing + * the found string + * @param first An iterator referencing to the beginning of the target container (like a std::begin iterator) + * @param last An iterator referencing one-past-the-end of the target container (like a std::end iterator) + * @param func A callback function to be called each time a string is found - given the [begin, end] iterators referencing + * the found string + */ + template + void walk_multistring(const InputIt& first, const InputIt& last, Fn func) + { + auto current = first; + const auto end_iterator = last; + const auto last_null = (end_iterator - 1); + while (current != end_iterator) + { + // hand rolling ::std::find(current, end_iterator, L'\0'); + // as this may be called when isn't available + auto next = current; + while (next != end_iterator && *next != L'\0') + { + ++next; + } + + if (next != end_iterator) + { + // don't add an empty string for the final 2nd-null-terminator + if (next != last_null) + { + // call the function provided with the [begin, end] pair referencing a string found + func(current, next); + } + current = next + 1; + } + else + { + current = next; + } + } + } + +#if defined(_VECTOR_) && defined(_STRING_) && defined(WIL_ENABLE_EXCEPTIONS) + /** + * @brief A translation function taking iterators referencing std::wstring objects and returns a corresponding + * std::vector to be written to a MULTI_SZ registry value. The translation follows the rules for how + * MULTI_SZ registry values should be formatted, notably how null characters should be embedded within the returned + * vector + * @tparam InputIt An iterator type that references a container that holds std::wstring objects to translate into a + * wchar_t buffer + * @param first An iterator referencing to the beginning of the target container (like a std::begin iterator) + * @param last An iterator referencing one-past-the-end of the target container (like a std::end iterator) + * @return A std::vector with the raw wchar_t buffer of bytes prepared to write to a MULTI_SZ registry value + */ + template + ::std::vector get_multistring_from_wstrings(const InputIt& first, const InputIt& last) + { + ::std::vector multistring; + + if (first == last) + { + multistring.push_back(L'\0'); + multistring.push_back(L'\0'); + return multistring; + } + + for (const auto& wstr : ::wil::make_range(first, last)) + { + multistring.insert(multistring.end(), ::std::begin(wstr), ::std::end(wstr)); + multistring.push_back(L'\0'); + } + + // double-null-terminate the last string + multistring.push_back(L'\0'); + return multistring; + } + + /** + * @brief A translation function taking iterators referencing wchar_t characters and returns extracted individual + * std::wstring objects. The translation follows the rules for how MULTI_SZ registry value can be formatted, + * notably with embedded null characters. Note that this conversion avoids returning empty std::wstring objects + * even though the input may contain contiguous null wchar_t values + * @tparam InputIt An iterator type that reference a container that holds wchar_t characters to translate into individual + * strings + * @param first An iterator referencing to the beginning of the target container (like a std::begin iterator) + * @param last An iterator referencing one-past-the-end of the target container (like a std::end iterator) + * @return A std::vector of the extracted strings from the input container of wchar_t characters + */ + template + ::std::vector<::std::wstring> get_wstring_vector_from_multistring(const InputIt& first, const InputIt& last) + { + if (last - first < 3) + { + // it doesn't have the required 2 terminating null characters - return an empty string + return ::std::vector<::std::wstring>(1); + } + + ::std::vector<::std::wstring> strings; + walk_multistring(first, last, [&](const InputIt& string_first, const InputIt& string_last) { + strings.emplace_back(string_first, string_last); + }); + return strings; + } +#endif // #if defined(_VECTOR_) && defined(_STRING_) && defined(WIL_ENABLE_EXCEPTIONS) + +#if defined(__WIL_OBJBASE_H_) + template + void get_multistring_bytearray_from_strings_nothrow(const PCWSTR data[C], ::wil::unique_cotaskmem_array_ptr& multistring) WI_NOEXCEPT + { + constexpr uint8_t nullTermination[2]{0x00, 0x00}; + + size_t total_array_length_bytes = 0; + for (size_t i = 0; i < C; ++i) + { + total_array_length_bytes += wcslen(data[i]) * sizeof(wchar_t); + total_array_length_bytes += sizeof(wchar_t); // plus one for the null-terminator + } + total_array_length_bytes += sizeof(wchar_t); // plus one for the ending double-null-terminator + + *multistring.addressof() = static_cast(::CoTaskMemAlloc(total_array_length_bytes)); + if (!multistring.get()) + { + multistring.reset(); + return; + } + *multistring.size_address() = total_array_length_bytes; + + size_t array_offset = 0; + for (size_t i = 0; i < C; ++i) + { + const auto string_length_bytes = wcslen(data[i]) * sizeof(wchar_t); + memcpy(multistring.get() + array_offset, data[i], string_length_bytes); + array_offset += string_length_bytes; + + static_assert(sizeof(nullTermination) == sizeof(wchar_t), "null terminator must be a wchar"); + memcpy(multistring.get() + array_offset, nullTermination, sizeof(nullTermination)); + array_offset += sizeof(nullTermination); + } + + // double-null-terminate the last string + memcpy(multistring.get() + array_offset, nullTermination, sizeof(nullTermination)); + } + + /** + * @brief A translation function taking iterators referencing wchar_t characters and returns extracted individual + * wil::unique_cotaskmem_string objects. The translation follows the rules for how MULTI_SZ registry value can be + * formatted, notably with embedded null characters. Note that this conversion avoids returning empty + * wil::unique_cotaskmem_string objects even though the input may contain contiguous null wchar_t values + * @tparam InputIt An iterator type that reference a container that holds wchar_t characters to translate into individual + * strings + * @param first An iterator referencing to the beginning of the target container (like a std::begin iterator) + * @param last An iterator referencing one-past-the-end of the target container (like a std::end iterator) + * @param cotaskmem_array The [out] wil::unique_cotaskmem_array_ptr to contain the array of + * strings. A wil::unique_cotaskmem_array_ptr of the extracted strings from the + * input container of wchar_t characters. An empty wil::unique_cotaskmem_array_ptr should be translated as out-of + * memory as there should always be at least one wil::unique_cotaskmem_string + */ + template + void get_cotaskmemstring_array_from_multistring_nothrow( + const InputIt& first, const InputIt& last, ::wil::unique_cotaskmem_array_ptr<::wil::unique_cotaskmem_string>& cotaskmem_array) WI_NOEXCEPT + { + if (last - first < 3) + { + // it doesn't have the required 2 terminating null characters - return an empty string + *cotaskmem_array.addressof() = static_cast(::CoTaskMemAlloc(sizeof(PWSTR) * 1)); + if (cotaskmem_array) + { + auto new_string = ::wil::make_cotaskmem_string_nothrow(L""); + if (new_string) + { + *cotaskmem_array.size_address() = 1; + cotaskmem_array[0] = new_string.release(); + } + else + { + // oom will return an empty array + cotaskmem_array.reset(); + } + } + else + { + // oom will return an empty array + cotaskmem_array.reset(); + } + return; + } + + // we must first count the # of strings for the array + size_t arraySize = 0; + walk_multistring(first, last, [&](const InputIt&, const InputIt&) { + ++arraySize; + }); + + // allocate the array size necessary to hold all the unique_cotaskmem_strings + *cotaskmem_array.addressof() = static_cast(::CoTaskMemAlloc(sizeof(PWSTR) * arraySize)); + if (!cotaskmem_array) + { + // oom will return an empty array + cotaskmem_array.reset(); + return; + } + + *cotaskmem_array.size_address() = arraySize; + ZeroMemory(cotaskmem_array.data(), sizeof(PWSTR) * arraySize); + + size_t arrayOffset = 0; + walk_multistring(first, last, [&](const InputIt& string_first, const InputIt& string_last) { + FAIL_FAST_IF(arrayOffset >= arraySize); + const auto stringSize = string_last - string_first; + auto new_string = ::wil::make_cotaskmem_string_nothrow(&(*string_first), stringSize); + if (!new_string) + { + // oom will return an empty array + cotaskmem_array.reset(); + return; + } + cotaskmem_array[arrayOffset] = new_string.release(); + ++arrayOffset; + }); + } +#endif // #if defined(__WIL_OBJBASE_H_) + + namespace reg_value_type_info + { + // supports_prepare_buffer is used to determine if the input buffer to read a registry value should be prepared + // before the first call to the registry read API + template + constexpr bool supports_prepare_buffer() WI_NOEXCEPT + { + return false; + } + template + HRESULT prepare_buffer(T&) WI_NOEXCEPT + { + // no-op in the default case + return S_OK; + } + + // supports_resize_buffer_bytes is used to determine if the input buffer to read a registry value can be resized + // for those cases if the error from the registry read API indicates it needs a larger buffer + template + constexpr bool supports_resize_buffer_bytes() WI_NOEXCEPT + { + return false; + } + template + constexpr HRESULT resize_buffer_bytes(T&, DWORD) WI_NOEXCEPT + { + return E_NOTIMPL; + } + + // supports_trim_buffer is used to determine if the input buffer to read a registry value must be trimmed + // after the registry read API has successfully written into the supplied buffer + // note that currently only std::wstring requires this as it cannot have embedded nulls + template + constexpr bool supports_trim_buffer() WI_NOEXCEPT + { + return false; + } + // if called for a type that does not support trimming, will return a zero-length + template + constexpr size_t trim_buffer(T&) WI_NOEXCEPT + { + return 0; + } + + constexpr void* get_buffer(const int32_t& value) WI_NOEXCEPT + { + return const_cast(&value); + } + + constexpr DWORD get_buffer_size_bytes(int32_t) WI_NOEXCEPT + { + return static_cast(sizeof(int32_t)); + } + + constexpr void* get_buffer(const uint32_t& value) WI_NOEXCEPT + { + return const_cast(&value); + } + + constexpr DWORD get_buffer_size_bytes(uint32_t) WI_NOEXCEPT + { + return static_cast(sizeof(uint32_t)); + } + + constexpr void* get_buffer(const long& value) WI_NOEXCEPT + { + return const_cast(&value); + } + + constexpr DWORD get_buffer_size_bytes(long) WI_NOEXCEPT + { + return static_cast(sizeof(long)); + } + + constexpr void* get_buffer(const unsigned long& value) WI_NOEXCEPT + { + return const_cast(&value); + } + + constexpr DWORD get_buffer_size_bytes(unsigned long) WI_NOEXCEPT + { + return static_cast(sizeof(unsigned long)); + } + + constexpr void* get_buffer(const int64_t& value) WI_NOEXCEPT + { + return const_cast(&value); + } + + constexpr DWORD get_buffer_size_bytes(int64_t) WI_NOEXCEPT + { + return static_cast(sizeof(int64_t)); + } + + constexpr void* get_buffer(const uint64_t& value) WI_NOEXCEPT + { + return const_cast(&value); + } + + constexpr DWORD get_buffer_size_bytes(uint64_t) WI_NOEXCEPT + { + return static_cast(sizeof(uint64_t)); + } + + constexpr void* get_buffer(PCWSTR value) WI_NOEXCEPT + { + return const_cast(value); + } + + inline DWORD get_buffer_size_bytes(PCWSTR value) WI_NOEXCEPT + { + if (!value) + { + return 0; + } + // including the last null buffer space in the returned buffer-size-bytes + // as the registry API we call guarantees null termination + return static_cast((::wcslen(value) + 1) * sizeof(wchar_t)); + } + +#if defined(_VECTOR_) && defined(WIL_ENABLE_EXCEPTIONS) + inline void* get_buffer(const ::std::vector& buffer) WI_NOEXCEPT + { + return const_cast(buffer.data()); + } + + inline DWORD get_buffer_size_bytes(const ::std::vector& value) WI_NOEXCEPT + { + return static_cast(value.size()); + } + + template <> + constexpr bool supports_prepare_buffer<::std::vector>() WI_NOEXCEPT + { + return true; + } + inline HRESULT prepare_buffer(::std::vector& value) WI_NOEXCEPT + try + { + // resize the initial vector to at least 1 byte + // this is needed so we can detect when the registry value exists + // but the value has zero-bytes + if (value.empty()) + { + value.resize(1); + } + // zero out the buffer if pre-allocated + for (auto& string_char : value) + { + string_char = 0x00; + } + return S_OK; + } + CATCH_RETURN(); + + template <> + constexpr bool supports_resize_buffer_bytes<::std::vector>() WI_NOEXCEPT + { + return true; + } + inline HRESULT resize_buffer_bytes(::std::vector& buffer, DWORD byteSize) WI_NOEXCEPT + try + { + buffer.resize(byteSize); + return S_OK; + } + CATCH_RETURN(); + + // std::vector does not implement resize_buffer_bytes + // because these support functions are only needed for set_value + // from the return of get_multistring_from_wstrings + inline void* get_buffer(const ::std::vector& value) WI_NOEXCEPT + { + return const_cast(value.data()); + } + + inline DWORD get_buffer_size_bytes(const ::std::vector& value) WI_NOEXCEPT + { + return static_cast(value.size()) * sizeof(wchar_t); + } + + template <> + constexpr bool supports_prepare_buffer<::std::vector>() WI_NOEXCEPT + { + return true; + } + inline HRESULT prepare_buffer(::std::vector& value) WI_NOEXCEPT + { + // zero out the buffer if pre-allocated + for (auto& string_char : value) + { + string_char = L'\0'; + } + return S_OK; + } +#endif // #if defined(_VECTOR_) && defined(WIL_ENABLE_EXCEPTIONS) + +#if defined(_STRING_) && defined(WIL_ENABLE_EXCEPTIONS) + inline void* get_buffer(const ::std::wstring& string) WI_NOEXCEPT + { + return const_cast(string.data()); + } + + inline DWORD get_buffer_size_bytes(const ::std::wstring& string) WI_NOEXCEPT + { + // including the last null buffer space in the returned buffer-size-bytes + // as the registry API we call guarantees null termination + return static_cast((string.size() + 1) * sizeof(wchar_t)); + } + + template <> + constexpr bool supports_prepare_buffer<::std::wstring>() WI_NOEXCEPT + { + return true; + } + inline HRESULT prepare_buffer(::std::wstring& string) WI_NOEXCEPT + { + // zero out the buffer if pre-allocated + for (auto& string_char : string) + { + string_char = L'\0'; + } + return S_OK; + } + + template <> + constexpr bool supports_resize_buffer_bytes<::std::wstring>() WI_NOEXCEPT + { + return true; + } + inline HRESULT resize_buffer_bytes(::std::wstring& string, DWORD byteSize) WI_NOEXCEPT + try + { + string.resize(byteSize / sizeof(wchar_t)); + return S_OK; + } + CATCH_RETURN(); + + template <> + constexpr bool supports_trim_buffer<::std::wstring>() WI_NOEXCEPT + { + return true; + } + inline size_t trim_buffer(::std::wstring& buffer) WI_NOEXCEPT + { + // remove any embedded null characters + const auto offset = buffer.find_first_of(L'\0'); + if (offset != ::std::wstring::npos) + { + buffer.resize(offset); + } + return buffer.size(); + } +#endif // #if defined(_STRING_) && defined(WIL_ENABLE_EXCEPTIONS) + +#if defined(__WIL_OLEAUTO_H_) + inline void* get_buffer(const BSTR& value) WI_NOEXCEPT + { + return value; + } + + inline DWORD get_buffer_size_bytes(const BSTR& value) WI_NOEXCEPT + { + auto length = ::SysStringLen(value); + if (length > 0) + { + // SysStringLen does not count the null-terminator + // including the last null buffer space in the returned buffer-size-bytes + // as the registry API we call guarantees null termination + length += 1; + } + return length * sizeof(WCHAR); + } + + template <> + constexpr bool supports_prepare_buffer() WI_NOEXCEPT + { + return true; + } + inline HRESULT prepare_buffer(const BSTR& value) WI_NOEXCEPT + { + if (value) + { + // zero out the buffer if pre-allocated + for (auto& string_char : ::wil::make_range(value, get_buffer_size_bytes(value) / sizeof(WCHAR))) + { + string_char = L'\0'; + } + } + return S_OK; + } + + template <> + constexpr bool supports_resize_buffer_bytes() WI_NOEXCEPT + { + return true; + } + // transferringOwnership is only set to false if this is a 'shallow' copy of the BSTR + // and the caller maintained ownership of the original BSTR. + inline HRESULT resize_buffer_bytes(BSTR& string, DWORD byteSize, bool transferringOwnership = true) WI_NOEXCEPT + { + // copy the original BSTR to copy from later if needed + const BSTR original_string = string; + const DWORD original_string_length = string == nullptr ? 0 : ::SysStringLen(string); + + // SysStringLen doesn't count the null-terminator, but our buffer size does + const bool original_string_length_too_small = (original_string_length + 1) < byteSize / sizeof(WCHAR); + if (original_string_length_too_small) + { + // pass a null BSTR value because SysAllocStringLen will copy the contents of the original BSTR, + // but in this case it's not long enough to copy the new length to be allocated + string = nullptr; + } + + // convert bytes to length (number of WCHAR's) + DWORD length_to_alloc = byteSize / sizeof(WCHAR); + // SysAllocStringLen adds a null, so subtract a wchar_t from the input length + length_to_alloc = length_to_alloc > 0 ? length_to_alloc - 1 : length_to_alloc; + const BSTR new_bstr{::SysAllocStringLen(string, length_to_alloc)}; + RETURN_IF_NULL_ALLOC(new_bstr); + + // copy back the original BSTR if it was too small for SysAllocStringLen + // also assuming that both lengths are greater than zero + const DWORD sourceLengthToCopy = original_string_length < length_to_alloc ? original_string_length : length_to_alloc; + if (sourceLengthToCopy > 0 && original_string_length_too_small) + { + ::memcpy_s(new_bstr, length_to_alloc * sizeof(WCHAR), original_string, sourceLengthToCopy * sizeof(WCHAR)); + } + + // if not transferring ownership, the caller will still own the original BSTR + if (transferringOwnership) + { + ::SysFreeString(string); + } + + string = new_bstr; + return S_OK; + } + + inline void* get_buffer(const ::wil::unique_bstr& value) WI_NOEXCEPT + { + return value.get(); + } + + inline DWORD get_buffer_size_bytes(const ::wil::unique_bstr& value) WI_NOEXCEPT + { + return get_buffer_size_bytes(value.get()); + } + + template <> + constexpr bool supports_prepare_buffer<::wil::unique_bstr>() WI_NOEXCEPT + { + return true; + } + inline HRESULT prepare_buffer(const ::wil::unique_bstr& value) WI_NOEXCEPT + { + if (value) + { + // zero out the buffer if pre-allocated + for (auto& string_char : ::wil::make_range(value.get(), get_buffer_size_bytes(value) / sizeof(WCHAR))) + { + string_char = L'\0'; + } + } + return S_OK; + } + + template <> + constexpr bool supports_resize_buffer_bytes<::wil::unique_bstr>() WI_NOEXCEPT + { + return true; + } + inline HRESULT resize_buffer_bytes(::wil::unique_bstr& string, DWORD byteSize) WI_NOEXCEPT + { + BSTR temp_bstr = string.get(); + + // not transferring ownership of the BSTR within 'string' to resize_buffer_bytes() + // resize_buffer_bytes() will overwrite temp_bstr with a newly-allocated BSTR + constexpr bool transferringOwnership = false; + RETURN_IF_FAILED(resize_buffer_bytes(temp_bstr, byteSize, transferringOwnership)); + + // if succeeded in creating a new BSTR, move ownership of the new BSTR into string + string.reset(temp_bstr); + return S_OK; + } +#endif // #if defined(__WIL_OLEAUTO_H_) + +#if defined(__WIL_OLEAUTO_H_STL) + inline void* get_buffer(const ::wil::shared_bstr& value) WI_NOEXCEPT + { + return value.get(); + } + + inline DWORD get_buffer_size_bytes(const ::wil::shared_bstr& value) WI_NOEXCEPT + { + return get_buffer_size_bytes(value.get()); + } + + template <> + constexpr bool supports_prepare_buffer<::wil::shared_bstr>() WI_NOEXCEPT + { + return true; + } + inline HRESULT prepare_buffer(const ::wil::shared_bstr& value) WI_NOEXCEPT + { + if (value) + { + // zero out the buffer if pre-allocated + for (auto& string_char : ::wil::make_range(value.get(), get_buffer_size_bytes(value) / sizeof(WCHAR))) + { + string_char = L'\0'; + } + } + return S_OK; + } + + template <> + constexpr bool supports_resize_buffer_bytes<::wil::shared_bstr>() WI_NOEXCEPT + { + return true; + } + inline HRESULT resize_buffer_bytes(::wil::shared_bstr& string, DWORD byteSize) WI_NOEXCEPT + { + BSTR temp_bstr = string.get(); + + // not transferring ownership of the BSTR within 'string' to resize_buffer_bytes() + // resize_buffer_bytes() will overwrite temp_bstr with a newly-allocated BSTR + constexpr bool transferringOwnership = false; + RETURN_IF_FAILED(resize_buffer_bytes(temp_bstr, byteSize, transferringOwnership)); + + // if succeeded in creating a new BSTR, move ownership of the new BSTR into string + string.reset(temp_bstr); + return S_OK; + } +#endif // #if defined(__WIL_OLEAUTO_H_STL) + +#if defined(__WIL_OBJBASE_H_) + inline void* get_buffer(const ::wil::unique_cotaskmem_string& value) WI_NOEXCEPT + { + return value.get(); + } + + constexpr DWORD get_buffer_size_bytes(const ::wil::unique_cotaskmem_string&) WI_NOEXCEPT + { + // wil::unique_cotaskmem_string does not intrinsically track its internal buffer size + // thus the caller must track the buffer size it requested to be allocated + return 0; + } + + template <> + constexpr bool supports_resize_buffer_bytes<::wil::unique_cotaskmem_string>() WI_NOEXCEPT + { + return true; + } + inline HRESULT resize_buffer_bytes(::wil::unique_cotaskmem_string& string, DWORD byteSize) WI_NOEXCEPT + { + // convert bytes to length (number of WCHAR's) + size_t length = byteSize / sizeof(wchar_t); + // ::wil::make_unique_string_nothrow adds one to the length when it allocates, so subtracting 1 from the input length + length = length > 0 ? length - 1 : length; + auto new_string = ::wil::make_unique_string_nothrow<::wil::unique_cotaskmem_string>(string.get(), length); + RETURN_IF_NULL_ALLOC(new_string.get()); + + string = ::wistd::move(new_string); + return S_OK; + } + + inline void* get_buffer(const ::wil::unique_cotaskmem_array_ptr& value) WI_NOEXCEPT + { + return value.get(); + } + + inline DWORD get_buffer_size_bytes(const ::wil::unique_cotaskmem_array_ptr& value) WI_NOEXCEPT + { + return static_cast(value.size()); + } + + template <> + constexpr bool supports_resize_buffer_bytes<::wil::unique_cotaskmem_array_ptr>() WI_NOEXCEPT + { + return true; + } + inline HRESULT resize_buffer_bytes(::wil::unique_cotaskmem_array_ptr& arrayValue, DWORD byteSize) WI_NOEXCEPT + { + ::wil::unique_cotaskmem_array_ptr tempValue; + *tempValue.addressof() = static_cast(::CoTaskMemAlloc(byteSize)); + RETURN_IF_NULL_ALLOC(tempValue.get()); + *tempValue.size_address() = byteSize; + + const auto bytesToCopy = arrayValue.size() < byteSize ? arrayValue.size() : byteSize; + CopyMemory(tempValue.get(), arrayValue.get(), bytesToCopy); + + arrayValue = ::wistd::move(tempValue); + return S_OK; + } +#endif // #if defined(__WIL_OBJBASE_H_) + +#if defined(__WIL_OBJBASE_H_STL) + inline void* get_buffer(const ::wil::shared_cotaskmem_string& value) WI_NOEXCEPT + { + return value.get(); + } + + constexpr DWORD get_buffer_size_bytes(const ::wil::shared_cotaskmem_string&) WI_NOEXCEPT + { + // wil::shared_cotaskmem_string does not intrinsically track its internal buffer size + // thus the caller must track the buffer size it requested to be allocated + return 0; + } + + template <> + constexpr bool supports_resize_buffer_bytes<::wil::shared_cotaskmem_string>() WI_NOEXCEPT + { + return true; + } + inline HRESULT resize_buffer_bytes(::wil::shared_cotaskmem_string& string, DWORD byteSize) WI_NOEXCEPT + { + // convert bytes to length (number of WCHAR's) + size_t length = byteSize / sizeof(WCHAR); + // ::wil::make_unique_string_nothrow adds one to the length when it allocates, so subtracting 1 from the input length + length = length > 0 ? length - 1 : length; + auto new_string = ::wil::make_unique_string_nothrow<::wil::unique_cotaskmem_string>(string.get(), length); + RETURN_IF_NULL_ALLOC(new_string.get()); + + string = ::wistd::move(new_string); + return S_OK; + } +#endif // #if defined(__WIL_OBJBASE_H_STL) + + inline void* get_buffer(const ::wil::unique_process_heap_string& value) WI_NOEXCEPT + { + return value.get(); + } + + constexpr DWORD get_buffer_size_bytes(const ::wil::unique_process_heap_string&) WI_NOEXCEPT + { + // wil::unique_process_heap_string does not intrinsically track its internal buffer size + // thus the caller must track the buffer size it requested to be allocated + return 0; + } + + template <> + constexpr bool supports_resize_buffer_bytes<::wil::unique_process_heap_string>() WI_NOEXCEPT + { + return true; + } + inline HRESULT resize_buffer_bytes(::wil::unique_process_heap_string& string, DWORD byteSize) WI_NOEXCEPT + { + // convert bytes to length (number of WCHAR's) + size_t length = byteSize / sizeof(wchar_t); + // ::wil::make_unique_string_nothrow adds one to the length when it allocates, so subtracting 1 from the input length + length = length > 0 ? length - 1 : length; + auto new_string = ::wil::make_unique_string_nothrow<::wil::unique_process_heap_string>(string.get(), length); + RETURN_IF_NULL_ALLOC(new_string.get()); + + string = ::wistd::move(new_string); + return S_OK; + } + + // constexpr expressions to determining the get* and set* registry value types + // for all supported types T to read/write values + template + DWORD get_value_type() WI_NOEXCEPT + { + static_assert(sizeof(T) != sizeof(T), "Unsupported type for get_value_type"); + } + + template + DWORD set_value_type() WI_NOEXCEPT + { + static_assert(sizeof(T) != sizeof(T), "Unsupported type for set_value_type"); + } + + template <> + constexpr DWORD get_value_type() WI_NOEXCEPT + { + return get_value_flags_from_value_type(REG_DWORD); + } + template <> + constexpr DWORD set_value_type() WI_NOEXCEPT + { + return REG_DWORD; + } + + template <> + constexpr DWORD get_value_type() WI_NOEXCEPT + { + return get_value_flags_from_value_type(REG_DWORD); + } + template <> + constexpr DWORD set_value_type() WI_NOEXCEPT + { + return REG_DWORD; + } + + template <> + constexpr DWORD get_value_type() WI_NOEXCEPT + { + return get_value_flags_from_value_type(REG_DWORD); + } + template <> + constexpr DWORD set_value_type() WI_NOEXCEPT + { + return REG_DWORD; + } + + template <> + constexpr DWORD get_value_type() WI_NOEXCEPT + { + return get_value_flags_from_value_type(REG_DWORD); + } + template <> + constexpr DWORD set_value_type() WI_NOEXCEPT + { + return REG_DWORD; + } + + template <> + constexpr DWORD get_value_type() WI_NOEXCEPT + { + return get_value_flags_from_value_type(REG_QWORD); + } + template <> + constexpr DWORD set_value_type() WI_NOEXCEPT + { + return REG_QWORD; + } + + template <> + constexpr DWORD get_value_type() WI_NOEXCEPT + { + return get_value_flags_from_value_type(REG_QWORD); + } + template <> + constexpr DWORD set_value_type() WI_NOEXCEPT + { + return REG_QWORD; + } + + template <> + constexpr DWORD get_value_type() WI_NOEXCEPT + { + return get_value_flags_from_value_type(REG_SZ); + } + template <> + constexpr DWORD set_value_type() WI_NOEXCEPT + { + return REG_SZ; + } + +#if defined(_STRING_) && defined(WIL_ENABLE_EXCEPTIONS) + template <> + constexpr DWORD get_value_type<::std::wstring>() WI_NOEXCEPT + { + return get_value_flags_from_value_type(REG_SZ); + } + + template <> + constexpr DWORD set_value_type() WI_NOEXCEPT + { + return REG_SZ; + } +#endif // #if defined(_STRING_) && defined(WIL_ENABLE_EXCEPTIONS) + +#if defined(__WIL_OLEAUTO_H_) + template <> + constexpr DWORD get_value_type() WI_NOEXCEPT + { + return get_value_flags_from_value_type(REG_SZ); + } + template <> + constexpr DWORD get_value_type<::wil::unique_bstr>() WI_NOEXCEPT + { + return get_value_flags_from_value_type(REG_SZ); + } + + template <> + constexpr DWORD set_value_type() WI_NOEXCEPT + { + return REG_SZ; + } + + template <> + constexpr DWORD set_value_type() WI_NOEXCEPT + { + return REG_SZ; + } +#endif // #if defined(__WIL_OLEAUTO_H_) + +#if defined(__WIL_OLEAUTO_H_STL) + + template <> + constexpr DWORD get_value_type<::wil::shared_bstr>() WI_NOEXCEPT + { + return get_value_flags_from_value_type(REG_SZ); + } + + template <> + constexpr DWORD set_value_type() WI_NOEXCEPT + { + return REG_SZ; + } +#endif // #if defined(__WIL_OLEAUTO_H_STL) + +#if defined(__WIL_OBJBASE_H_) + template <> + constexpr DWORD get_value_type<::wil::unique_cotaskmem_string>() WI_NOEXCEPT + { + return get_value_flags_from_value_type(REG_SZ); + } + + template <> + constexpr DWORD set_value_type() WI_NOEXCEPT + { + return REG_SZ; + } +#endif // defined(__WIL_OBJBASE_H_) + +#if defined(__WIL_OBJBASE_H_STL) + template <> + constexpr DWORD get_value_type<::wil::shared_cotaskmem_string>() WI_NOEXCEPT + { + return get_value_flags_from_value_type(REG_SZ); + } + + template <> + constexpr DWORD set_value_type() WI_NOEXCEPT + { + return REG_SZ; + } +#endif // #if defined(__WIL_OBJBASE_H_STL) + } // namespace reg_value_type_info + + template + class reg_view_t + { + public: + explicit reg_view_t(HKEY key) WI_NOEXCEPT : m_key(key) + { + } + ~reg_view_t() WI_NOEXCEPT = default; + reg_view_t(const reg_view_t&) = delete; + reg_view_t& operator=(const reg_view_t&) = delete; + reg_view_t(reg_view_t&&) = delete; + reg_view_t& operator=(reg_view_t&&) = delete; + + typename err_policy::result open_key( + _In_opt_ _In_opt_ PCWSTR subKey, _Out_ HKEY* hkey, ::wil::reg::key_access access = ::wil::reg::key_access::read) const + { + constexpr DWORD zero_options{0}; + return err_policy::HResult( + HRESULT_FROM_WIN32(::RegOpenKeyExW(m_key, subKey, zero_options, get_access_flags(access), hkey))); + } + + typename err_policy::result create_key(PCWSTR subKey, _Out_ HKEY* hkey, ::wil::reg::key_access access = ::wil::reg::key_access::read) const + { + *hkey = nullptr; + + constexpr DWORD zero_reserved{0}; + constexpr PWSTR null_class{nullptr}; + constexpr DWORD zero_options{0}; + constexpr SECURITY_ATTRIBUTES* null_security_attributes{nullptr}; + DWORD disposition{0}; + return err_policy::HResult(HRESULT_FROM_WIN32(::RegCreateKeyExW( + m_key, subKey, zero_reserved, null_class, zero_options, get_access_flags(access), null_security_attributes, hkey, &disposition))); + } + + typename err_policy::result delete_tree(_In_opt_ PCWSTR sub_key) const + { + auto hr = HRESULT_FROM_WIN32(::RegDeleteTreeW(m_key, sub_key)); + if (::wil::reg::is_registry_not_found(hr)) + { + hr = S_OK; + } + return err_policy::HResult(hr); + } + + typename err_policy::result delete_value(_In_opt_ PCWSTR value_name) const + { + return err_policy::HResult(HRESULT_FROM_WIN32(::RegDeleteValueW(m_key, value_name))); + } + + template + typename err_policy::result get_value( + _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, R& return_value, DWORD type = reg_value_type_info::get_value_type()) const + { + return get_value_with_type(subkey, value_name, return_value, type); + } + + // typename D supports unsigned 32-bit values; i.e. allows the caller to pass a DWORD* as well as uint32_t* + template || wistd::is_same_v>* = nullptr> + typename err_policy::result get_value_char_array( + _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, WCHAR (&return_value)[Length], DWORD type, _Out_opt_ DwordType* requiredBytes) const + { + constexpr DwordType zero_value{0ul}; + ::wil::assign_to_opt_param(requiredBytes, zero_value); + DWORD data_size_bytes{Length * sizeof(WCHAR)}; + const auto hr = HRESULT_FROM_WIN32(::RegGetValueW( + m_key, subkey, value_name, ::wil::reg::reg_view_details::get_value_flags_from_value_type(type), nullptr, return_value, &data_size_bytes)); + if (SUCCEEDED(hr) || ::wil::reg::is_registry_buffer_too_small(hr)) + { + const DwordType updated_value{data_size_bytes}; + ::wil::assign_to_opt_param(requiredBytes, updated_value); + } + return err_policy::HResult(hr); + } + +#if defined(_OPTIONAL_) && defined(__cpp_lib_optional) + // intended for err_exception_policy as err_returncode_policy will not get an error code + template + ::std::optional try_get_value( + _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, DWORD type = reg_value_type_info::get_value_type()) const + { + R value{}; + const auto hr = get_value_with_type(subkey, value_name, value, type); + if (SUCCEEDED(hr)) + { + return ::std::optional(::wistd::move(value)); + } + + if (::wil::reg::is_registry_not_found(hr)) + { + return ::std::nullopt; + } + + // throw if exception policy + err_policy::HResult(hr); + return ::std::nullopt; + } +#endif // #if defined (_OPTIONAL_) && defined(__cpp_lib_optional) + + template + typename err_policy::result set_value( + _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, const R& value, DWORD type = reg_value_type_info::set_value_type()) const + { + return set_value_with_type(subkey, value_name, value, type); + } + + private: + const HKEY m_key{}; + + template + typename err_policy::result set_value_with_type(_In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, const R& value, DWORD type) const + { + return err_policy::HResult(HRESULT_FROM_WIN32(::RegSetKeyValueW( + m_key, + subkey, + value_name, + type, + static_cast(reg_value_type_info::get_buffer(value)), + reg_value_type_info::get_buffer_size_bytes(value)))); + } + + template + typename get_value_with_type_policy::result get_value_with_type( + _In_opt_ PCWSTR subkey, _In_opt_ PCWSTR value_name, R& return_value, DWORD type = reg_value_type_info::get_value_type()) const + { + if +#if defined(__cpp_if_constexpr) + constexpr +#endif + (reg_value_type_info::supports_prepare_buffer()) + + { + const auto prepare_buffer_hr = reg_value_type_info::prepare_buffer(return_value); + if (FAILED(prepare_buffer_hr)) + { + return get_value_with_type_policy::HResult(prepare_buffer_hr); + } + } + + // get_buffer_size_bytes should include the null terminator when used for strings. + DWORD bytes_allocated{reg_value_type_info::get_buffer_size_bytes(return_value)}; + HRESULT get_value_hresult = S_OK; + for (;;) + { + constexpr DWORD* null_type{nullptr}; + DWORD data_size_bytes{bytes_allocated}; + get_value_hresult = HRESULT_FROM_WIN32(::RegGetValueW( + m_key, + subkey, + value_name, + get_value_flags_from_value_type(type), + null_type, + reg_value_type_info::get_buffer(return_value), + &data_size_bytes)); + + // some return types we can grow as needed - e.g. when writing to a std::wstring + // only compile and resize_buffer for those types that support dynamically growing the buffer + if +#if defined(__cpp_if_constexpr) + constexpr +#endif + (reg_value_type_info::supports_resize_buffer_bytes()) + { + // Attempt to grow the buffer with the data_size_bytes returned from GetRegValueW + // GetRegValueW will indicate the caller allocate the returned number of bytes in one of two cases: + // 1. returns ERROR_MORE_DATA + // 2. returns ERROR_SUCCESS when we gave it a nullptr for the out buffer + const bool shouldReallocate = + (::wil::reg::is_registry_buffer_too_small(get_value_hresult)) || + (SUCCEEDED(get_value_hresult) && (reg_value_type_info::get_buffer(return_value) == nullptr) && + (data_size_bytes > 0)); + if (shouldReallocate) + { + // verify if resize_buffer succeeded allocation + const auto resize_buffer_hr = reg_value_type_info::resize_buffer_bytes(return_value, data_size_bytes); + if (FAILED(resize_buffer_hr)) + { + // if resize fails, return this error back to the caller + return get_value_with_type_policy::HResult(resize_buffer_hr); + } + + // if it resize succeeds, continue the for loop to try again + bytes_allocated = data_size_bytes; + continue; + } + + // if the RegGetValueW call succeeded with a non-null [out] param, + // and the type supports resize_buffer_bytes + // and the bytes we allocated don't match data_size_bytes returned from RegGetValueW + // resize the buffer to match what RegGetValueW returned + if (SUCCEEDED(get_value_hresult)) + { + const auto current_byte_size = reg_value_type_info::get_buffer_size_bytes(return_value); + if (current_byte_size != data_size_bytes) + { + // verify if resize_buffer_bytes succeeded allocation + const auto resize_buffer_hr = reg_value_type_info::resize_buffer_bytes(return_value, data_size_bytes); + if (FAILED(resize_buffer_hr)) + { + // if resize fails, return this error back to the caller + return get_value_with_type_policy::HResult(resize_buffer_hr); + } + } + } + } + + // we don't need to reallocate and retry the call to RegGetValueW so breaking out of the loop + break; + } + + // some types (generally string types) require trimming its internal buffer after RegGetValueW successfully wrote into its buffer + if +#if defined(__cpp_if_constexpr) + constexpr +#endif + (reg_value_type_info::supports_trim_buffer()) + + { + if (SUCCEEDED(get_value_hresult)) + { + reg_value_type_info::trim_buffer(return_value); + } + } + + return get_value_with_type_policy::HResult(get_value_hresult); + } + }; + + using reg_view_nothrow = ::wil::reg::reg_view_details::reg_view_t<::wil::err_returncode_policy>; +#if defined(WIL_ENABLE_EXCEPTIONS) + using reg_view = ::wil::reg::reg_view_details::reg_view_t<::wil::err_exception_policy>; +#endif // #if defined(WIL_ENABLE_EXCEPTIONS) + } // namespace reg_view_details + /// @endcond + + /// @cond + namespace reg_iterator_details + { + constexpr uint32_t iterator_end_offset = 0xffffffff; + constexpr size_t iterator_default_buffer_length = 32; + constexpr size_t iterator_max_keyname_length = 255; + constexpr size_t iterator_max_valuename_length = 16383; + + // function overloads to allow *_enumerator objects to be constructed from all 3 types of HKEY representatives + inline HKEY get_hkey(HKEY h) WI_NOEXCEPT + { + return h; + } + inline HKEY get_hkey(const ::wil::unique_hkey& h) WI_NOEXCEPT + { + return h.get(); + } +#if defined(__WIL_WINREG_STL) + inline HKEY get_hkey(const ::wil::shared_hkey& h) WI_NOEXCEPT + { + return h.get(); + } +#endif // #if defined(__WIL_WINREG_STL) + +#if defined(WIL_ENABLE_EXCEPTIONS) && defined(_STRING_) + // overloads for some of the below string functions - specific for std::wstring + // these overloads must be declared before the template functions below, as some of those template functions + // reference these overload functions + inline void clear_name(::std::wstring& name, size_t) WI_NOEXCEPT + { + name.assign(name.size(), L'\0'); + } + inline ::std::wstring copy_name(const ::std::wstring& str, size_t length) WI_NOEXCEPT + { + try + { + // guarantee that the copied string has the specified internal length + // i.e., the same length assumptions hold when the string is copied + ::std::wstring tempString(length, L'0'); + tempString.assign(str); + return tempString; + } + catch (...) + { + return {}; + } + } + inline bool can_derive_length(const ::std::wstring& name) WI_NOEXCEPT + { + return !name.empty(); + } +#endif // #if defined(WIL_ENABLE_EXCEPTIONS) && defined(_STRING_) + + // string manipulation functions needed for iterator functions + template + PWSTR address_of_name(const T& name) WI_NOEXCEPT + { + return static_cast(::wil::reg::reg_view_details::reg_value_type_info::get_buffer(name)); + } + + template + bool can_derive_length(const T& name) WI_NOEXCEPT + { + return static_cast(address_of_name(name)); + } + + template + bool compare_name(const T& name, PCWSTR comparand) WI_NOEXCEPT + { + if (!can_derive_length(name) || !comparand) + { + return false; + } + return 0 == wcscmp(address_of_name(name), comparand); + } + + template + void clear_name(const T& name, size_t length) WI_NOEXCEPT + { + if (can_derive_length(name) && length > 0) + { + memset(address_of_name(name), 0, length * sizeof(wchar_t)); + } + } + + // failure returns zero + template + size_t resize_name_cch(T& name, size_t current_length, size_t new_length) WI_NOEXCEPT + { + if (new_length > current_length) + { + if (FAILED(::wil::reg::reg_view_details::reg_value_type_info::resize_buffer_bytes( + name, static_cast(new_length * sizeof(wchar_t))))) + { + return 0; + } + current_length = new_length; + // fall through to clear the newly allocated buffer + // and return the new length + } + + // continue to use the existing buffer since the requested length is less than or equals to the current length + clear_name(name, current_length); + return current_length; + } + + template + T copy_name(const T& name, size_t length) WI_NOEXCEPT + { + if (!can_derive_length(name)) + { + return {}; + } + return ::wil::make_unique_string_nothrow(address_of_name(name), length); + } + +#if defined(__WIL_OLEAUTO_H_) + inline ::wil::unique_bstr copy_name(const ::wil::unique_bstr& name, size_t length) WI_NOEXCEPT + { + if (!can_derive_length(name)) + { + return {}; + } + + // SysAllocStringLen adds a null, so subtract a wchar_t from the input length + length = length > 0 ? length - 1 : length; + return ::wil::unique_bstr{::SysAllocStringLen(name.get(), static_cast(length))}; + } +#endif // #if defined(__WIL_OLEAUTO_H_) + } // namespace reg_iterator_details + /// @endcond + + // forward declaration to allow friend-ing the template iterator class +#if defined(WIL_ENABLE_EXCEPTIONS) + template + class iterator_t; +#endif + template + class iterator_nothrow_t; + + // all methods must be noexcept - to be usable with any iterator type (throwing or non-throwing) + template + class key_iterator_data + { + public: + T name{}; + + key_iterator_data(HKEY key = nullptr) WI_NOEXCEPT : m_hkey{key} + { + } + ~key_iterator_data() WI_NOEXCEPT = default; + + key_iterator_data(const key_iterator_data& rhs) WI_NOEXCEPT + { + // might return null/empty string on failure + name = ::wil::reg::reg_iterator_details::copy_name(rhs.name, rhs.m_name_length); + m_hkey = rhs.m_hkey; + m_index = rhs.m_index; + m_name_length = ::wil::reg::reg_iterator_details::can_derive_length(name) ? rhs.m_name_length : 0; + } + key_iterator_data& operator=(const key_iterator_data& rhs) WI_NOEXCEPT + { + if (&rhs != this) + { + key_iterator_data temp(rhs); + *this = ::wistd::move(temp); + } + return *this; + } + + key_iterator_data(key_iterator_data&& rhs) WI_NOEXCEPT : name{wistd::move(rhs.name)}, + m_hkey{wistd::move(rhs.m_hkey)}, + m_index{wistd::move(rhs.m_index)}, + m_name_length{wistd::move(rhs.m_name_length)} + { + // once name is moved, we must reset m_name_length as name is now empty + rhs.m_name_length = 0; + } + key_iterator_data& operator=(key_iterator_data&& rhs) WI_NOEXCEPT + { + if (&rhs != this) + { + name = ::wistd::move(rhs.name); + m_hkey = ::wistd::move(rhs.m_hkey); + m_index = ::wistd::move(rhs.m_index); + m_name_length = ::wistd::move(rhs.m_name_length); + // once name is moved, we must reset m_name_length as name is now empty + rhs.m_name_length = 0; + } + return *this; + } + + // Case-sensitive comparison + bool operator==(PCWSTR comparand) const WI_NOEXCEPT + { + return ::wil::reg::reg_iterator_details::compare_name(name, comparand); + } + + private: +#if defined(WIL_ENABLE_EXCEPTIONS) + friend class ::wil::reg::iterator_t; +#endif + friend class ::wil::reg::iterator_nothrow_t; + + bool at_end() const WI_NOEXCEPT + { + return m_index == ::wil::reg::reg_iterator_details::iterator_end_offset; + } + + void make_end_iterator() WI_NOEXCEPT + { + ::wil::reg::reg_iterator_details::clear_name(name, m_name_length); + m_index = ::wil::reg::reg_iterator_details::iterator_end_offset; + } + + bool resize(size_t new_length) WI_NOEXCEPT + { + // if resize fails, will return 0 + m_name_length = ::wil::reg::reg_iterator_details::resize_name_cch(name, m_name_length, new_length); + return m_name_length > 0; + } + + HRESULT enumerate_current_index() WI_NOEXCEPT + { + FAIL_FAST_IF(at_end()); + + auto string_length = static_cast(m_name_length) > 0 + ? static_cast(m_name_length) + : static_cast(::wil::reg::reg_iterator_details::iterator_default_buffer_length); + for (;;) + { + if (!resize(string_length)) + { + RETURN_HR(E_OUTOFMEMORY); + } + + const auto error = ::RegEnumKeyExW( + m_hkey, // hKey + m_index, // dwIndex + string_length == 0 ? nullptr : ::wil::reg::reg_iterator_details::address_of_name(name), // lpName + &string_length, // lpcchName + nullptr, // lpReserved + nullptr, // lpClass + nullptr, // lpcchClass + nullptr); // lpftLastWriteTime + + if (error == ERROR_SUCCESS) + { + // string_length returned from RegEnumKeyExW does not include the null terminator + ++string_length; + // if the string type supports resize, we must resize it to the returned string size + // to ensure continuity of the string type with the actual string size + if (::wil::reg::reg_view_details::reg_value_type_info::supports_resize_buffer_bytes()) + { + RETURN_IF_FAILED(::wil::reg::reg_view_details::reg_value_type_info::resize_buffer_bytes( + name, string_length * sizeof(WCHAR))); + m_name_length = string_length; + } + if (::wil::reg::reg_view_details::reg_value_type_info::supports_trim_buffer()) + { + m_name_length = ::wil::reg::reg_view_details::reg_value_type_info::trim_buffer(name); + } + break; + } + if (error == ERROR_NO_MORE_ITEMS) + { + make_end_iterator(); + break; + } + if (error == ERROR_MORE_DATA) + { + // if our default wasn't big enough, resize to either half max or max length and try again + if (string_length < reg_iterator_details::iterator_max_keyname_length / 2) + { + string_length = reg_iterator_details::iterator_max_keyname_length / 2; + } + else if (string_length < reg_iterator_details::iterator_max_keyname_length + 1) + { + string_length = reg_iterator_details::iterator_max_keyname_length + 1; // plus one for null + } + else + { + // if we're already at max length, we can't grow anymore, so we'll just return the error + break; + } + continue; + } + // any other error will fail + RETURN_WIN32(error); + } + return S_OK; + } + + HKEY m_hkey{}; + uint32_t m_index = ::wil::reg::reg_iterator_details::iterator_end_offset; + size_t m_name_length{}; + }; + + // all methods must be noexcept - to be usable with any iterator type (throwing or non-throwing) + template + class value_iterator_data + { + public: + T name{}; + DWORD type = REG_NONE; + + value_iterator_data(HKEY key = nullptr) WI_NOEXCEPT : m_hkey{key} + { + } + ~value_iterator_data() WI_NOEXCEPT = default; + + value_iterator_data(const value_iterator_data& rhs) WI_NOEXCEPT + { + // might return null/empty string on failure + name = ::wil::reg::reg_iterator_details::copy_name(rhs.name, rhs.m_name_length); + type = rhs.type; + m_hkey = rhs.m_hkey; + m_index = rhs.m_index; + m_name_length = ::wil::reg::reg_iterator_details::can_derive_length(name) ? rhs.m_name_length : 0; + } + value_iterator_data& operator=(const value_iterator_data& rhs) WI_NOEXCEPT + { + if (&rhs != this) + { + value_iterator_data temp(rhs); + *this = ::wistd::move(temp); + } + return *this; + } + + value_iterator_data(value_iterator_data&&) WI_NOEXCEPT = default; + value_iterator_data& operator=(value_iterator_data&& rhs) WI_NOEXCEPT = default; + + bool at_end() const WI_NOEXCEPT + { + return m_index == ::wil::reg::reg_iterator_details::iterator_end_offset; + } + + private: +#if defined(WIL_ENABLE_EXCEPTIONS) + friend class ::wil::reg::iterator_t; +#endif + friend class ::wil::reg::iterator_nothrow_t; + + void make_end_iterator() WI_NOEXCEPT + { + ::wil::reg::reg_iterator_details::clear_name(name, m_name_length); + m_index = ::wil::reg::reg_iterator_details::iterator_end_offset; + } + + bool resize(size_t new_length) + { + // if resize fails, will return 0 + m_name_length = ::wil::reg::reg_iterator_details::resize_name_cch(name, m_name_length, new_length); + return m_name_length > 0; + } + + HRESULT enumerate_current_index() WI_NOEXCEPT + { + FAIL_FAST_IF(at_end()); + + auto string_length = static_cast(m_name_length) > 0 + ? static_cast(m_name_length) + : static_cast(::wil::reg::reg_iterator_details::iterator_default_buffer_length); + for (;;) + { + if (!resize(string_length)) + { + RETURN_HR(E_OUTOFMEMORY); + } + + const auto error = ::RegEnumValueW( + m_hkey, // hKey + m_index, // dwIndex + string_length == 0 ? nullptr : ::wil::reg::reg_iterator_details::address_of_name(name), // lpValueName + &string_length, // lpcchValueName + nullptr, // lpReserved + &type, // lpType + nullptr, // lpData + nullptr); // lpcbData + + if (error == ERROR_SUCCESS) + { + // string_length returned from RegEnumValueW does not include the null terminator + ++string_length; + // if the string type supports resize, we must resize it to the returned string size + // to ensure continuity of the string type with the actual string size + if (::wil::reg::reg_view_details::reg_value_type_info::supports_resize_buffer_bytes()) + { + RETURN_IF_FAILED(::wil::reg::reg_view_details::reg_value_type_info::resize_buffer_bytes( + name, string_length * sizeof(WCHAR))); + m_name_length = string_length; + } + if (::wil::reg::reg_view_details::reg_value_type_info::supports_trim_buffer()) + { + m_name_length = ::wil::reg::reg_view_details::reg_value_type_info::trim_buffer(name); + } + break; + } + if (error == ERROR_NO_MORE_ITEMS) + { + make_end_iterator(); + break; + } + if (error == ERROR_MORE_DATA) + { + if (string_length == ::wil::reg::reg_iterator_details::iterator_max_valuename_length + 1) + { + // this is the max size, so we can't grow anymore, so we'll just return the error + break; + } + + // resize and try again - growing exponentially up to the max + string_length *= 2; + if (string_length > ::wil::reg::reg_iterator_details::iterator_max_valuename_length + 1) + { + string_length = ::wil::reg::reg_iterator_details::iterator_max_valuename_length + 1; + } + continue; + } + + // any other error will fail + RETURN_WIN32(error); + } + return S_OK; + } + + HKEY m_hkey{}; + uint32_t m_index = ::wil::reg::reg_iterator_details::iterator_end_offset; + size_t m_name_length{}; + }; + +#if defined(WIL_ENABLE_EXCEPTIONS) + template + class iterator_t + { + public: + // defining iterator_traits allows STL functions to be used with this iterator class. + // Notice this is a forward_iterator + // - does not support random-access (e.g. vector::iterator) + // - does not support bidirectional access (e.g. list::iterator) +#if defined(_ITERATOR_) || defined(WIL_DOXYGEN) + using iterator_category = ::std::forward_iterator_tag; +#endif + using value_type = T; + using difference_type = size_t; + using distance_type = size_t; + using pointer = T*; + using reference = T&; + + iterator_t() WI_NOEXCEPT = default; + ~iterator_t() WI_NOEXCEPT = default; + + iterator_t(HKEY hkey) : m_data(hkey) + { + if (hkey != nullptr) + { + m_data.m_index = 0; + THROW_IF_FAILED(m_data.enumerate_current_index()); + } + } + + iterator_t(const iterator_t&) = default; + iterator_t& operator=(const iterator_t&) = default; + iterator_t(iterator_t&&) WI_NOEXCEPT = default; + iterator_t& operator=(iterator_t&&) WI_NOEXCEPT = default; + + // operator support + const T& operator*() const + { + FAIL_FAST_IF(m_data.at_end()); + return m_data; + } + const T& operator*() + { + FAIL_FAST_IF(m_data.at_end()); + return m_data; + } + const T* operator->() const + { + FAIL_FAST_IF(m_data.at_end()); + return &m_data; + } + const T* operator->() + { + FAIL_FAST_IF(m_data.at_end()); + return &m_data; + } + + bool operator==(const iterator_t& rhs) const WI_NOEXCEPT + { + if (m_data.at_end() || rhs.m_data.at_end()) + { + // if either is not initialized (or end), both must not be initialized (or end) to be equal + return m_data.m_index == rhs.m_data.m_index; + } + return m_data.m_hkey == rhs.m_data.m_hkey && m_data.m_index == rhs.m_data.m_index; + } + + bool operator!=(const iterator_t& rhs) const WI_NOEXCEPT + { + return !(*this == rhs); + } + + // pre-increment + iterator_t& operator++() + { + this->operator+=(1); + return *this; + } + const iterator_t& operator++() const + { + this->operator+=(1); + return *this; + } + + // increment by integer + iterator_t& operator+=(size_t offset) + { + uint32_t newIndex = m_data.m_index + static_cast(offset); + if (newIndex < m_data.m_index) + { + // fail on integer overflow + THROW_HR(E_INVALIDARG); + } + if (newIndex == ::wil::reg::reg_iterator_details::iterator_end_offset) + { + // fail if this creates an end iterator + THROW_HR(E_INVALIDARG); + } + + // iterate by the integer offset + for (size_t count = 0; count < offset; ++count) + { + ++m_data.m_index; + THROW_IF_FAILED(m_data.enumerate_current_index()); + } + return *this; + } + + // not supporting post-increment - which would require copy-construction + iterator_t operator++(int) = delete; + + private: + // container based on the class template type + T m_data{}; + }; +#endif + + template + class iterator_nothrow_t + { + public: + iterator_nothrow_t() WI_NOEXCEPT = default; + ~iterator_nothrow_t() WI_NOEXCEPT = default; + + iterator_nothrow_t(HKEY hkey) WI_NOEXCEPT : m_data(hkey) + { + if (hkey != nullptr) + { + m_data.m_index = 0; + m_last_error = m_data.enumerate_current_index(); + } + } + + iterator_nothrow_t(const iterator_nothrow_t&) WI_NOEXCEPT = default; + iterator_nothrow_t& operator=(const iterator_nothrow_t&) WI_NOEXCEPT = default; + iterator_nothrow_t(iterator_nothrow_t&&) WI_NOEXCEPT = default; + iterator_nothrow_t& operator=(iterator_nothrow_t&&) WI_NOEXCEPT = default; + + bool at_end() const WI_NOEXCEPT + { + return m_data.at_end(); + } + + HRESULT last_error() const WI_NOEXCEPT + { + return m_last_error; + } + + HRESULT move_next() WI_NOEXCEPT + { + const auto newIndex = m_data.m_index + 1; + if (newIndex < m_data.m_index) + { + // fail on integer overflow + m_last_error = E_INVALIDARG; + } + else if (newIndex == ::wil::reg::reg_iterator_details::iterator_end_offset) + { + // fail if this creates an end iterator + m_last_error = E_INVALIDARG; + } + else + { + m_data.m_index = newIndex; + m_last_error = m_data.enumerate_current_index(); + } + + if (FAILED(m_last_error)) + { + // on failure, set the iterator to an end iterator + m_data.make_end_iterator(); + } + + return m_last_error; + } + + // operator support + const T& operator*() const WI_NOEXCEPT + { + return m_data; + } + const T& operator*() WI_NOEXCEPT + { + return m_data; + } + const T* operator->() const WI_NOEXCEPT + { + return &m_data; + } + const T* operator->() WI_NOEXCEPT + { + return &m_data; + } + bool operator==(const iterator_nothrow_t& rhs) const WI_NOEXCEPT + { + if (m_data.at_end() || rhs.m_data.at_end()) + { + // if either is not initialized (or end), both must not be initialized (or end) to be equal + return m_data.m_index == rhs.m_data.m_index; + } + return m_data.m_hkey == rhs.m_data.m_hkey && m_data.m_index == rhs.m_data.m_index; + } + + bool operator!=(const iterator_nothrow_t& rhs) const WI_NOEXCEPT + { + return !(*this == rhs); + } + + iterator_nothrow_t& operator++() WI_NOEXCEPT + { + move_next(); + return *this; + } + const iterator_nothrow_t& operator++() const WI_NOEXCEPT + { + move_next(); + return *this; + } + + private: + // container based on the class template type + T m_data{}; + HRESULT m_last_error{}; + }; + +} // namespace reg +} // namespace wil +#endif // __WIL_REGISTRY_HELPERS_INCLUDED \ No newline at end of file diff --git a/libs/wil/wil/resource.h b/libs/wil/wil/resource.h new file mode 100644 index 00000000..1295212b --- /dev/null +++ b/libs/wil/wil/resource.h @@ -0,0 +1,7654 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! WIL Resource Wrappers (RAII): Provides a family of smart pointer patterns and resource wrappers to enable customers to +//! consistently use RAII in all code. + +#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 + +/// @cond +// 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 0xffffffffffffffffULL // UINT64_MAX +#else /* _WIN64 */ +#define __WI_SIZE_MAX 0xffffffffUL // UINT32_MAX +#endif /* _WIN64 */ +/// @endcond + +// Forward declaration +/// @cond +namespace Microsoft +{ +namespace WRL +{ + template + class ComPtr; +} +} // namespace Microsoft +/// @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 +//! inadvertently 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 = false; + DWORD m_error = 0; + +public: + last_error_context() WI_NOEXCEPT : last_error_context(::GetLastError()) + { + } + + explicit last_error_context(DWORD error) WI_NOEXCEPT : m_error(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; + } + + WI_NODISCARD auto value() const WI_NOEXCEPT + { + return m_error; + } +#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 + struct close_invoke_helper + { + __forceinline static void close(pointer_storage_t value) WI_NOEXCEPT + { + wistd::invoke(close_fn, value); + } + inline static void close_reset(pointer_storage_t value) WI_NOEXCEPT + { + auto preserveError = last_error_context(); + wistd::invoke(close_fn, value); + } + }; + + template + struct close_invoke_helper + { + __forceinline static void close(pointer_storage_t value) WI_NOEXCEPT + { + close_fn(value); + } + inline static void close_reset(pointer_storage_t value) WI_NOEXCEPT + { + auto preserveError = last_error_context(); + close_fn(value); + } + }; + + template + using close_invoker = + close_invoke_helper ? wistd::is_function_v> : false, close_fn_t, close_fn, pointer_storage_t>; + + template < + typename pointer_t, // The handle type + typename close_fn_t, // The handle close function type + close_fn_t close_fn, // * and function pointer + typename pointer_access_t = pointer_access_all, // all, noaddress or none to control pointer method access + typename pointer_storage_t = pointer_t, // The type used to store the handle (usually the same as the handle itself) + typename invalid_t = pointer_t, // The invalid handle value type + invalid_t invalid = invalid_t{}, // * and its value (default ZERO value) + typename pointer_invalid_t = wistd::nullptr_t> // nullptr_t if the invalid handle value is compatible with nullptr, otherwise pointer + struct resource_policy : close_invoker + { + 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); + } + }; + + // 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; + + public: + 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); + } + } + + WI_NODISCARD 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(); + } + + WI_NODISCARD 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; + } + + protected: + void replace(unique_storage&& other) WI_NOEXCEPT + { + reset(other.m_ptr); + other.m_ptr = policy::invalid_value(); + } + + private: + pointer_storage m_ptr; + }; + + // Determines if it is safe to deallocate a resource without destructing the object + template + struct needs_destruction + { + template + static auto invoke(int) -> wistd::bool_constant= 0>; // Always true, but SFINAE's if incomplete type + template + static auto invoke(float) -> wistd::false_type; + + // A type needs destruction if: + // 1. It is a complete type, + // 2. It can be destructed, and + // 3. It's not trivially destructible + // Note that we need the "complete type" check because some places use an undefined struct as a type-safe + // resource type (e.g. 'typedef struct tagSERIALIZEDPROPSTORAGE SERIALIZEDPROPSTORAGE') + static constexpr const bool value = wistd::conjunction_v< + decltype(invoke>(0)), + wistd::is_destructible>, + wistd::negation>>>; + }; + + template + constexpr bool needs_destruction_v = needs_destruction::value; + + // A pass-through type that statically asserts that the specified type does not need destruction. Useful when + // specifying template arguments for various unique_* types where the destructor won't run + template + struct ensure_trivially_destructible + { + // NOTE: Temporary opt-out for existing code that uses these types incorrectly +#ifndef WIL_DISABLE_UNIQUE_PTR_DESTRUCTOR_CHECKS + // If this static_assert fires, it means you've used a type that is not trivially destructible as a template + // argument to a wil::unique* type that will not invoke that object's destructor + static_assert(!needs_destruction_v, "Resource type has a non-trivial destructor, but is used in a context where its destructor will not be run"); +#endif + using type = T; + }; + + template + using ensure_trivially_destructible_t = typename ensure_trivially_destructible::type; +} // namespace 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) + __WI_NOEXCEPT_((wistd::is_nothrow_constructible_v)) : + 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); + } + + WI_NODISCARD 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(); + } + + WI_NODISCARD 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 < + typename pointer, // The handle type + typename close_fn_t, // The handle close function type + close_fn_t close_fn, // * and function pointer + typename pointer_access = details::pointer_access_all, // all, noaddress or none to control pointer method access + typename pointer_storage = pointer, // The type used to store the handle (usually the same as the handle itself) + typename invalid_t = pointer, // The invalid handle value type + invalid_t invalid = invalid_t{}, // * and its value (default ZERO value) + typename pointer_invalid = wistd::nullptr_t> // 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 + WI_NODISCARD 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 + WI_NODISCARD 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 +} // namespace details +/// @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 eliminate 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(); + } +} // namespace details +/// @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) WI_NOEXCEPT : 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) WI_NOEXCEPT : 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; + }; +} // namespace 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 requires, 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). Submit pull +requests to [GitHub](https://github.com/microsoft/wil/). 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_default_init 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 +{ + using closer = details::close_invoker; + +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 + { + closer::close(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 + { + closer::close_reset(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 + { + closer::close(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 + { + static_assert( + !details::needs_destruction_v, + "Resource type has a non-trivial destructor, but is used in a context where its destructor will not be run"); + } +}; + +/** 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 intended 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 pull requests +to [GitHub](https://github.com/microsoft/wil/). 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; + } + + WI_NODISCARD iterator begin() WI_NOEXCEPT + { + return (iterator(m_ptr)); + } + + WI_NODISCARD const_iterator begin() const WI_NOEXCEPT + { + return (const_iterator(m_ptr)); + } + + WI_NODISCARD iterator end() WI_NOEXCEPT + { + return (iterator(m_ptr + m_size)); + } + + WI_NODISCARD const_iterator end() const WI_NOEXCEPT + { + return (const_iterator(m_ptr + m_size)); + } + + WI_NODISCARD const_iterator cbegin() const WI_NOEXCEPT + { + return (begin()); + } + + WI_NODISCARD const_iterator cend() const WI_NOEXCEPT + { + return (end()); + } + + WI_NODISCARD size_type size() const WI_NOEXCEPT + { + return (m_size); + } + + WI_NODISCARD bool empty() const WI_NOEXCEPT + { + return (size() == size_type{}); + } + + WI_NODISCARD reference operator[](size_type position) + { + WI_ASSERT(position < m_size); + _Analysis_assume_(position < m_size); + return (m_ptr[position]); + } + + WI_NODISCARD const_reference operator[](size_type position) const + { + WI_ASSERT(position < m_size); + _Analysis_assume_(position < m_size); + return (m_ptr[position]); + } + + WI_NODISCARD reference front() + { + WI_ASSERT(!empty()); + return (m_ptr[0]); + } + + WI_NODISCARD const_reference front() const + { + WI_ASSERT(!empty()); + return (m_ptr[0]); + } + + WI_NODISCARD reference back() + { + WI_ASSERT(!empty()); + return (m_ptr[m_size - 1]); + } + + WI_NODISCARD const_reference back() const + { + WI_ASSERT(!empty()); + return (m_ptr[m_size - 1]); + } + + WI_NODISCARD ValueType* data() WI_NOEXCEPT + { + return (m_ptr); + } + + WI_NODISCARD const ValueType* data() const WI_NOEXCEPT + { + return (m_ptr); + } + + WI_NODISCARD pointer get() const WI_NOEXCEPT + { + return m_ptr; + } + + WI_NODISCARD 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) WI_NOEXCEPT : 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 + { + close_invoker::close(&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; + }; +} // namespace details +/// @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. +@code +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; +} +@endcode +*/ +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 + WI_NODISCARD 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 + WI_NODISCARD 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) + { + details::close_invoker::close(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 + WI_NODISCARD 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) + { + 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 + WI_NODISCARD 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()); +} + +#if !defined(__WIL_MIN_KERNEL) && !defined(WIL_KERNEL_MODE) +/// @cond +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)); + } +} // namespace details +/// @endcond + +// 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)...); +} +#endif // !defined(__WIL_MIN_KERNEL) && !defined(WIL_KERNEL_MODE) + +#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; +} + +#if !defined(__WIL_MIN_KERNEL) && !defined(WIL_KERNEL_MODE) +/// @cond +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; + } +} // namespace details +/// @endcond + +// 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; +} +#endif // !defined(__WIL_MIN_KERNEL) && !defined(WIL_KERNEL_MODE) + +} // namespace wil +#endif // __WIL_RESOURCE + +// Hash deferral function for unique_any_t +#if ((defined(_UNORDERED_SET_) || defined(_UNORDERED_MAP_)) && !defined(__WIL_RESOURCE_UNIQUE_HASH)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_RESOURCE_UNIQUE_HASH +/// @endcond +namespace std +{ +template +struct hash> +{ + WI_NODISCARD size_t operator()(wil::unique_any_t const& val) const + { + return (hash::pointer>()(val.get())); + } +}; +} // namespace std +#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)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define WIL_RESOURCE_STL +/// @endcond +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; + + public: + 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) + { + } + + WI_NODISCARD 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 < + typename allow_t = typename policy::pointer_access, + typename wistd::enable_if::value, int>::type = 0> + WI_NODISCARD pointer get() const WI_NOEXCEPT + { + return (m_ptr ? m_ptr->get() : policy::invalid_value()); + } + + template < + typename allow_t = typename policy::pointer_access, + typename wistd::enable_if::value, int>::type = 0> + pointer_storage* addressof() + { + if (!m_ptr) + { + m_ptr = std::make_shared(); + } + return m_ptr->addressof(); + } + + WI_NODISCARD long int use_count() const WI_NOEXCEPT + { + return m_ptr.use_count(); + } + + protected: + void replace(shared_storage&& other) WI_NOEXCEPT + { + m_ptr = wistd::move(other.m_ptr); + } + + private: + template + friend class ::wil::weak_any; + + std::shared_ptr m_ptr; + }; +} // namespace details +/// @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) __WI_NOEXCEPT_((wistd::is_nothrow_constructible_v)) : + 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); + } + + WI_NODISCARD 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(); + } + + WI_NODISCARD 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); + } + + WI_NODISCARD bool expired() const WI_NOEXCEPT + { + return m_weakPtr.expired(); + } + + WI_NODISCARD 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)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL_RESOURCE_SHARED_HASH +/// @endcond +namespace std +{ +template +struct hash> +{ + WI_NODISCARD size_t operator()(wil::shared_any_t const& val) const + { + return (hash::pointer>()(val.get())); + } +}; +} // namespace std +#endif + +namespace wil +{ + +#if (defined(__NOTHROW_T_DEFINED) && !defined(__WIL__NOTHROW_T_DEFINED)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL__NOTHROW_T_DEFINED +/// @endcond +/** 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)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WINBASE_ +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_ 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; +} // namespace details +/// @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; +typedef unique_any_handle_null_only unique_boundary_descriptor; + +/// @cond +namespace details +{ + template + inline void __stdcall ClosePrivateNamespaceHelper(HANDLE h) WI_NOEXCEPT + { + ::ClosePrivateNamespace(h, flags); + } +} // namespace details +/// @endcond + +template +using unique_private_namespace = + unique_any_handle_null_only>; + +using unique_private_namespace_close = unique_private_namespace<>; +using unique_private_namespace_destroy = unique_private_namespace; +#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 + WI_NODISCARD 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 securityAttributes = nullptr, _Out_opt_ bool* alreadyExists = nullptr) + { + auto handle = ::CreateEventExW( + securityAttributes, + 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(alreadyExists, false); + return false; + } + assign_to_opt_param(alreadyExists, (::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 securityAttributes = nullptr, + _Out_opt_ bool* alreadyExists = nullptr) + { + return err_policy::LastErrorIfFalse(try_create(options, name, securityAttributes, alreadyExists)); + } + + // 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 + +#ifndef WIL_NO_SLIM_EVENT +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && \ + ((_WIN32_WINNT >= _WIN32_WINNT_WIN8) || (__WIL_RESOURCE_ENABLE_QUIRKS && (_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. + WI_NODISCARD bool is_signaled() const WI_NOEXCEPT + { + return !!ReadAcquire(&m_isSignaled); + } + + bool wait(DWORD timeoutMilliseconds) WI_NOEXCEPT + { + if (timeoutMilliseconds == 0) + { + return TryAcquireEvent(); + } + else if (timeoutMilliseconds == INFINITE) + { + return wait(); + } + + UINT64 startTime{}; + QueryUnbiasedInterruptTime(&startTime); + + UINT64 elapsedTimeMilliseconds = 0; + + while (!TryAcquireEvent()) + { + if (elapsedTimeMilliseconds >= timeoutMilliseconds) + { + return false; + } + + DWORD newTimeout = static_cast(timeoutMilliseconds - elapsedTimeMilliseconds); + + if (!WaitForSignal(newTimeout)) + { + return false; + } + + UINT64 currTime; + QueryUnbiasedInterruptTime(&currTime); + + elapsedTimeMilliseconds = (currTime - startTime) / static_cast(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 timeoutMilliseconds) WI_NOEXCEPT + { + LONG falseValue = FALSE; + BOOL waitResult = WaitOnAddress(&m_isSignaled, &falseValue, sizeof(m_isSignaled), timeoutMilliseconds); + __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_WIN8) +#endif // WIL_NO_SLIM_EVENT + +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 mutexAttributes = nullptr, + _Out_opt_ bool* alreadyExists = nullptr) + { + auto handle = ::CreateMutexExW(mutexAttributes, name, dwFlags, desiredAccess); + if (handle == nullptr) + { + assign_to_opt_param(alreadyExists, false); + return false; + } + assign_to_opt_param(alreadyExists, (::GetLastError() == ERROR_ALREADY_EXISTS)); + 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 mutexAttributes = nullptr, + _Out_opt_ bool* alreadyExists = nullptr) + { + return err_policy::LastErrorIfFalse(try_create(name, dwFlags, desiredAccess, mutexAttributes, alreadyExists)); + } + + // 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, + _Out_opt_ bool* alreadyExists = nullptr) + { + auto handle = ::CreateSemaphoreExW(pSemaphoreAttributes, lInitialCount, lMaximumCount, name, 0, desiredAccess); + if (handle == nullptr) + { + assign_to_opt_param(alreadyExists, false); + return false; + } + assign_to_opt_param(alreadyExists, (::GetLastError() == ERROR_ALREADY_EXISTS)); + 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, + _Out_opt_ bool* alreadyExists = nullptr) + { + return err_policy::LastErrorIfFalse( + try_create(lInitialCount, lMaximumCount, name, desiredAccess, pSemaphoreAttributes, alreadyExists)); + } + + // 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; + } + }; +} // namespace details +/// @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 +{ + if (length == static_cast(-1)) + { + // guard against invalid parameters (null source with -1 length) + FAIL_FAST_IF(!source); + 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_; + } + WI_NODISCARD operator void*() const WI_NOEXCEPT + { + return pointer; + } + static void Close(SecureZeroData data) WI_NOEXCEPT + { + ::SecureZeroMemory(data.pointer, data.sizeBytes); + } + }; +} // namespace details +/// @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); + } +} // namespace details +/// @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, process_heap_deleter>; + +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); + } + }; +} // namespace details +/// @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, virtualalloc_deleter>; + +/** 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, mapview_deleter>; + +#endif // __WIL_WINBASE_ + +#if (defined(__WIL_WINBASE_) && defined(__NOTHROW_T_DEFINED) && !defined(__WIL_WINBASE_NOTHROW_T_DEFINED)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WINBASE_NOTHROW_T_DEFINED +/// @endcond + +// 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; +} // namespace details +/// @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. + WI_NODISCARD 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)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WINBASE_STL +/// @endcond +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)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WINBASE_NOTHROW_T_DEFINED_STL +/// @endcond +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)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WINBASE_DESKTOP +namespace details +{ + inline void __stdcall DestroyPrivateObjectSecurity(_Pre_opt_valid_ _Frees_ptr_opt_ PSECURITY_DESCRIPTOR pObjectDescriptor) WI_NOEXCEPT + { + ::DestroyPrivateObjectSecurity(&pObjectDescriptor); + } +} // namespace details +/// @endcond + +using hlocal_deleter = function_deleter; + +template +using unique_hlocal_ptr = wistd::unique_ptr, hlocal_deleter>; + +template +using unique_hlocal_array_ptr = wil::unique_array_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. +@code +auto foo = wil::make_unique_hlocal_nothrow(); +if (foo) +{ +// initialize allocated Foo object as appropriate +} +@endcode +*/ +template +inline typename wistd::enable_if::value, unique_hlocal_ptr>::type make_unique_hlocal_nothrow(Args&&... args) +{ + 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. +@code +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 +} +} +@endcode +*/ +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; + 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. +@code +auto foo = wil::make_unique_hlocal_failfast(); +// initialize allocated Foo object as appropriate +@endcode +*/ +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. +@code +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 +} +@endcode +*/ +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. +@code +auto foo = wil::make_unique_hlocal(); +// initialize allocated Foo object as appropriate +@endcode +*/ +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. +@code +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 +} +@endcode +*/ +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 +} // namespace details +/// @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, hlocal_secure_deleter>; + +/** 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. +@code +auto foo = wil::make_unique_hlocal_secure_nothrow(); +if (foo) +{ +// initialize allocated Foo object as appropriate +} +@endcode +*/ +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. +@code +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 +} +} +@endcode +*/ +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. +@code +auto foo = wil::make_unique_hlocal_secure_failfast(); +// initialize allocated Foo object as appropriate +@endcode +*/ +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. +@code +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 +} +@endcode +*/ +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. +@code +auto foo = wil::make_unique_hlocal_secure(); +// initialize allocated Foo object as appropriate +@endcode +*/ +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. +@code +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 +} +@endcode +*/ +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, hglobal_deleter>; + +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); + } + }; +} // namespace details +/// @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_)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL__WINUSER_ +/// @endcond +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 +#if !defined(NOCLIPBOARD) +using unique_close_clipboard_call = unique_call; + +inline unique_close_clipboard_call open_clipboard(HWND hwnd) +{ + return unique_close_clipboard_call{OpenClipboard(hwnd) != FALSE}; +} +#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)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WINBASE_DESKTOP_STL +/// @endcond +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)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL__COMBASEAPI_H_ +/// @endcond +#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; + } + } +} // namespace details +/// @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(); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +WI_NODISCARD inline unique_coreverttoself_call CoImpersonateClient() +{ + THROW_IF_FAILED(::CoImpersonateClient()); + return unique_coreverttoself_call(); +} +#endif + +typedef unique_struct unique_multi_qi; +#endif // __WIL__COMBASEAPI_H_ +#if (defined(__WIL__COMBASEAPI_H_) && !defined(__WIL__COMBASEAPI_H__STL) && defined(WIL_RESOURCE_STL) && (NTDDI_VERSION >= NTDDI_WIN8)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL__COMBASEAPI_H__STL +/// @endcond +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)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL__COMBASEAPI_H_APP +/// @endcond +//! 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 {}; +} + +#ifdef WIL_ENABLE_EXCEPTIONS +WI_NODISCARD inline unique_couninitialize_call CoInitializeEx(DWORD coinitFlags = 0 /*COINIT_MULTITHREADED*/) +{ + THROW_IF_FAILED(::CoInitializeEx(nullptr, coinitFlags)); + return {}; +} +#endif +#endif // __WIL__COMBASEAPI_H_APP + +#if (defined(__ROAPI_H_) && !defined(__WIL__ROAPI_H_APP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) && (NTDDI_VERSION >= NTDDI_WIN8)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL__ROAPI_H_APP +/// @endcond + +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(); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +//! 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 +#endif // __WIL__ROAPI_H_APP + +#if (defined(__WINSTRING_H_) && !defined(__WIL__WINSTRING_H_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL__WINSTRING_H_ +/// @endcond +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 support 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; + } + + WI_NODISCARD wchar_t* buffer() + { + WI_ASSERT(m_charBuffer != nullptr); + return m_charBuffer; + } + WI_NODISCARD 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; + }; +} // namespace details +/// @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)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL__WINSTRING_H_STL +/// @endcond +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)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WINREG_ +/// @endcond +typedef unique_any unique_hkey; +#endif // __WIL_WINREG_ +#if (defined(__WIL_WINREG_) && !defined(__WIL_WINREG_STL) && defined(WIL_RESOURCE_STL)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WINREG_STL +/// @endcond +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)) || defined(WIL_DOXYGEN) +/// @cond +#define _WIL__propidl_h__ +/// @endcond +// if language extensions (/Za) disabled, PropVariantInit will not exist, PROPVARIANT has forward declaration only +#if defined(_MSC_EXTENSIONS) +using unique_prop_variant = + wil::unique_struct; +#endif +#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)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL_OLEAUTO_H_ +/// @endcond +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 + +inline wil::unique_variant make_variant_bstr_nothrow(PCWSTR source) WI_NOEXCEPT +{ + wil::unique_variant result{}; + V_UNION(result.addressof(), bstrVal) = ::SysAllocString(source); + if (V_UNION(result.addressof(), bstrVal) != nullptr) + { + V_VT(result.addressof()) = VT_BSTR; + } + return result; +} + +inline wil::unique_variant make_variant_bstr_failfast(PCWSTR source) WI_NOEXCEPT +{ + auto result{make_variant_bstr_nothrow(source)}; + FAIL_FAST_HR_IF(E_OUTOFMEMORY, V_VT(result.addressof()) == VT_EMPTY); + return result; +} + +#ifdef WIL_ENABLE_EXCEPTIONS +inline wil::unique_variant make_variant_bstr(PCWSTR source) +{ + auto result{make_variant_bstr_nothrow(source)}; + THROW_HR_IF(E_OUTOFMEMORY, V_VT(result.addressof()) == VT_EMPTY); + return result; +} +#endif // WIL_ENABLE_EXCEPTIONS + +#endif // __WIL_OLEAUTO_H_ +#if (defined(__WIL_OLEAUTO_H_) && !defined(__WIL_OLEAUTO_H_STL) && defined(WIL_RESOURCE_STL)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_OLEAUTO_H_STL +/// @endcond +typedef shared_any shared_bstr; +typedef weak_any weak_bstr; +#endif // __WIL_OLEAUTO_H_STL + +#if ((defined(_WININET_) || defined(_DUBINET_)) && !defined(__WIL_WININET_)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WININET_ +/// @endcond +typedef unique_any unique_hinternet; +#endif // __WIL_WININET_ +#if (defined(__WIL_WININET_) && !defined(__WIL_WININET_STL) && defined(WIL_RESOURCE_STL)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WININET_STL +/// @endcond +typedef shared_any shared_hinternet; +typedef weak_any weak_hinternet; +#endif // __WIL_WININET_STL + +#if (defined(_WINHTTPX_) && !defined(__WIL_WINHTTP_)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WINHTTP_ +/// @endcond +typedef unique_any unique_winhttp_hinternet; +#endif // __WIL_WINHTTP_ +#if (defined(__WIL_WINHTTP_) && !defined(__WIL_WINHTTP_STL) && defined(WIL_RESOURCE_STL)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WINHTTP_STL +/// @endcond +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)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WINSOCKAPI_ +/// @endcond +typedef unique_any unique_socket; +#endif // __WIL_WINSOCKAPI_ +#if (defined(__WIL_WINSOCKAPI_) && !defined(__WIL_WINSOCKAPI_STL) && defined(WIL_RESOURCE_STL)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WINSOCKAPI_STL +/// @endcond +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)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WINGDI_ +/// @endcond +struct window_dc +{ + HDC dc; + HWND hwnd; + window_dc(HDC dc_, HWND hwnd_ = nullptr) WI_NOEXCEPT + { + dc = dc_; + hwnd = hwnd_; + } + WI_NODISCARD 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; + } + WI_NODISCARD 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_; + } + WI_NODISCARD 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)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WINGDI_STL +/// @endcond +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)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WTSAPI +/// @endcond +template +using unique_wtsmem_ptr = + wistd::unique_ptr, function_deleter>; +#endif // __WIL_WTSAPI + +#if (defined(_INC_WTSAPI) && !defined(__WIL_WTSAPI_WTSVIRTUALCHANNELCLOSE)) || defined(WIL_DOXYGEN) +#define __WIL_WTSAPI_WTSVIRTUALCHANNELCLOSE +typedef unique_any_handle_null unique_channel_handle; +#endif // __WIL_WTSAPI_WTSVIRTUALCHANNELCLOSE + +#if (defined(_WINSCARD_H_) && !defined(__WIL_WINSCARD_H_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WINSCARD_H_ +/// @endcond +typedef unique_any unique_scardctx; +#endif // __WIL_WINSCARD_H_ +#if (defined(__WIL_WINSCARD_H_) && !defined(__WIL_WINSCARD_H_STL) && defined(WIL_RESOURCE_STL)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WINSCARD_H_STL +/// @endcond +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)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL__WINCRYPT_H__ +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); + } +} // namespace details +/// @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 parameter 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. + @return '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; +typedef unique_any unique_hcryptmsg; +#endif // __WIL__WINCRYPT_H__ +#if (defined(__WIL__WINCRYPT_H__) && !defined(__WIL__WINCRYPT_H__STL) && defined(WIL_RESOURCE_STL)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL__WINCRYPT_H__STL +/// @endcond +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 shared_any shared_hcryptmsg; + +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; +typedef weak_any weak_hcryptmsg; +#endif // __WIL__WINCRYPT_H__STL + +#if (defined(__NCRYPT_H__) && !defined(__WIL_NCRYPT_H__) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_NCRYPT_H__ +/// @endcond +using ncrypt_deleter = function_deleter; + +template +using unique_ncrypt_ptr = wistd::unique_ptr, ncrypt_deleter>; + +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)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_NCRYPT_H_STL +/// @endcond +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)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_BCRYPT_H__ +namespace details +{ + inline void __stdcall BCryptCloseAlgorithmProviderNoFlags(_Pre_opt_valid_ _Frees_ptr_opt_ BCRYPT_ALG_HANDLE hAlgorithm) WI_NOEXCEPT + { + if (hAlgorithm) + { + ::BCryptCloseAlgorithmProvider(hAlgorithm, 0); + } + } +} // namespace details +/// @endcond + +using bcrypt_deleter = function_deleter; + +template +using unique_bcrypt_ptr = wistd::unique_ptr, bcrypt_deleter>; + +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)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_BCRYPT_H_STL +/// @endcond +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)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL__RPCNDR_H__ +/// @endcond + +//! 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, midl_deleter>; + +//! 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 + +/// @cond +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 +} // namespace details +/// @endcond +#endif // __WIL__RPCNDR_H__ + +#if (defined(_OBJBASE_H_) && !defined(__WIL_OBJBASE_H_) && !defined(WIL_KERNEL_MODE)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_OBJBASE_H_ +/// @endcond +using cotaskmem_deleter = function_deleter; + +template +using unique_cotaskmem_ptr = wistd::unique_ptr, cotaskmem_deleter>; + +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. +@code +auto foo = wil::make_unique_cotaskmem_nothrow(); +if (foo) +{ +// initialize allocated Foo object as appropriate +} +@endcode +*/ +template +inline typename wistd::enable_if::value, unique_cotaskmem_ptr>::type make_unique_cotaskmem_nothrow(Args&&... args) +{ + 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. +@code +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 +} +} +@endcode +*/ +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; + 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. +@code +auto foo = wil::make_unique_cotaskmem_failfast(); +// initialize allocated Foo object as appropriate +@endcode +*/ +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. +@code +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 +} +@endcode +*/ +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. +@code +auto foo = wil::make_unique_cotaskmem(); +// initialize allocated Foo object as appropriate +@endcode +*/ +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. +@code +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 +} +@endcode +*/ +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 +} // namespace details +/// @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)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_OBJBASE_H_STL +/// @endcond +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)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL_OBJBASE_AND_WINBASE_H_ +/// @endcond + +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, cotaskmem_secure_deleter>; + +/** 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. +@code +auto foo = wil::make_unique_cotaskmem_secure_nothrow(); +if (foo) +{ +// initialize allocated Foo object as appropriate +} +@endcode +*/ +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. +@code +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 +} +} +@endcode +*/ +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. +@code +auto foo = wil::make_unique_cotaskmem_secure_failfast(); +// initialize allocated Foo object as appropriate +@endcode +*/ +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. +@code +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 +} +@endcode +*/ +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. +@code +auto foo = wil::make_unique_cotaskmem_secure(); +// initialize allocated Foo object as appropriate +@endcode +*/ +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. +@code +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 +} +@endcode +*/ +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)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL_OLE2_H_ +/// @endcond +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) + { + } + + WI_NODISCARD 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(); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +//! 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 +#endif // __WIL_OLE2_H_ + +#if (defined(_INC_COMMCTRL) && !defined(__WIL_INC_COMMCTRL) && !defined(WIL_KERNEL_MODE)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_INC_COMMCTRL +/// @endcond +typedef unique_any unique_himagelist; +#endif // __WIL_INC_COMMCTRL +#if (defined(__WIL_INC_COMMCTRL) && !defined(__WIL_INC_COMMCTRL_STL) && defined(WIL_RESOURCE_STL)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_INC_COMMCTRL_STL +/// @endcond +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)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_INC_UXTHEME +/// @endcond +typedef unique_any unique_htheme; +#endif // __WIL_INC_UXTHEME + +#pragma warning(push) +#pragma warning(disable : 4995) +#if (defined(_INC_USERENV) && !defined(__WIL_INC_USERENV) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) && !defined(WIL_KERNEL_MODE)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL_INC_USERENV +/// @endcond +typedef unique_any unique_environment_block; +#endif // __WIL_INC_USERENV +#pragma warning(pop) + +#if (defined(__WINEVT_H__) && !defined(__WIL_INC_EVT_HANDLE) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_PKG_EVENTLOGSERVICE) && !defined(WIL_KERNEL_MODE)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL_INC_EVT_HANDLE +/// @endcond +typedef unique_any unique_evt_handle; +#endif // __WIL_INC_EVT_HANDLE + +#if (defined(_WINSVC_) && !defined(__WIL_HANDLE_H_WINSVC) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && !defined(WIL_KERNEL_MODE)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL_HANDLE_H_WINSVC +/// @endcond +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)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_HANDLE_H_WINSVC_STL +/// @endcond +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)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL_INC_STDIO +/// @endcond +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)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL__INC_STDIO_STL +/// @endcond +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(_INC_LOCALE) && !defined(__WIL_INC_LOCALE) && !defined(WIL_KERNEL_MODE)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_INC_LOCALE +/// @endcond +typedef unique_any<_locale_t, decltype(&::_free_locale), ::_free_locale> unique_locale; +#endif // __WIL_INC_LOCALE +#if (defined(__WIL_INC_LOCALE) && !defined(__WIL__INC_LOCALE_STL) && defined(WIL_RESOURCE_STL)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL__INC_LOCALE_STL +/// @endcond +typedef shared_any shared_locale; +typedef weak_any weak_locale; +#endif // __WIL__INC_LOCALE_STL + +#if (defined(_NTLSA_) && !defined(__WIL_NTLSA_) && !defined(WIL_KERNEL_MODE)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_NTLSA_ +/// @endcond +typedef unique_any unique_hlsa; + +using lsa_freemem_deleter = function_deleter; + +template +using unique_lsamem_ptr = wistd::unique_ptr, lsa_freemem_deleter>; +#endif // _NTLSA_ +#if (defined(_NTLSA_) && !defined(__WIL_NTLSA_STL) && defined(WIL_RESOURCE_STL)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_NTLSA_STL +/// @endcond +typedef shared_any shared_hlsa; +typedef weak_any weak_hlsa; +#endif // _NTLSA_ + +#if (defined(_LSALOOKUP_) && !defined(__WIL_LSALOOKUP_)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_LSALOOKUP_ +/// @endcond +typedef unique_any unique_hlsalookup; + +using lsalookup_freemem_deleter = function_deleter; + +template +using unique_lsalookupmem_ptr = wistd::unique_ptr, lsalookup_freemem_deleter>; +#endif // _LSALOOKUP_ +#if (defined(_LSALOOKUP_) && !defined(__WIL_LSALOOKUP_STL) && defined(WIL_RESOURCE_STL)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_LSALOOKUP_STL +/// @endcond +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)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL_HANDLE_H_NTLSA_IFS_ +/// @endcond +using lsa_deleter = function_deleter; + +template +using unique_lsa_ptr = wistd::unique_ptr, lsa_deleter>; +#endif // __WIL_HANDLE_H_NTLSA_IFS_ + +#if (defined(__WERAPI_H__) && !defined(__WIL_WERAPI_H__) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WERAPI_H__ +/// @endcond +typedef unique_any unique_wer_report; +#endif + +#if (defined(__MIDLES_H__) && !defined(__WIL_MIDLES_H__) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_MIDLES_H__ +/// @endcond +typedef unique_any unique_rpc_pickle; +#endif +#if (defined(__WIL_MIDLES_H__) && !defined(__WIL_MIDLES_H_STL) && defined(WIL_RESOURCE_STL)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_MIDLES_H_STL +/// @endcond +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)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_RPCDCE_H__ +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); + } +} // namespace details +/// @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)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_RPCDCE_H_STL +/// @endcond +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)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WCMAPI_H_ +/// @endcond +using wcm_deleter = function_deleter; + +template +using unique_wcm_ptr = wistd::unique_ptr, wcm_deleter>; +#endif + +#if (defined(_NETIOAPI_H_) && defined(_WS2IPDEF_) && defined(MIB_INVALID_TEREDO_PORT_NUMBER) && !defined(__WIL_NETIOAPI_H_) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL_NETIOAPI_H_ +/// @endcond +typedef unique_any unique_mib_iftable; +#endif +#if (defined(__WIL_NETIOAPI_H_) && !defined(__WIL_NETIOAPI_H_STL) && defined(WIL_RESOURCE_STL)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_NETIOAPI_H_STL +/// @endcond +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)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WLAN_WLANAPI_H +/// @endcond +using wlan_deleter = function_deleter; + +template +using unique_wlan_ptr = wistd::unique_ptr, wlan_deleter>; + +/// @cond +namespace details +{ + inline void __stdcall CloseWlanHandle(_In_ HANDLE hClientHandle) + { + ::WlanCloseHandle(hClientHandle, nullptr); + } +} // namespace details +/// @endcond + +typedef unique_any unique_wlan_handle; +#endif +#if (defined(__WIL_WLAN_WLANAPI_H) && !defined(__WIL_WLAN_WLANAPI_H_STL) && defined(WIL_RESOURCE_STL)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WLAN_WLANAPI_H_STL +/// @endcond +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)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_HPOWERNOTIFY_DEF_H_ +/// @endcond +typedef unique_any unique_hpowernotify; +#endif + +#if (defined(__WIL_WINBASE_DESKTOP) && defined(SID_DEFINED) && !defined(__WIL_PSID_DEF_H_)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_PSID_DEF_H_ +/// @endcond +typedef unique_any unique_process_heap_psid; +typedef unique_any unique_any_psid; +#if defined(_OBJBASE_H_) || defined(WIL_DOXYGEN) +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)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL_PROCESSTHREADSAPI_H_DESK_SYS +namespace details +{ + inline void __stdcall CloseProcessInformation(_In_ PROCESS_INFORMATION* p) + { + if (p->hProcess) + { + CloseHandle(p->hProcess); + } + + if (p->hThread) + { + CloseHandle(p->hThread); + } + } +} // namespace details +/// @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)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL__PROCESSENV_ +/// @endcond +/** Manages lifecycle of an environment-strings block. +@code +wil::unique_environstrings_ptr env { ::GetEnvironmentStringsW() }; +const wchar_t *nextVar = env.get(); +while (nextVar && *nextVar) +{ + // consume 'nextVar' + nextVar += wcslen(nextVar) + 1; +} +@endcode +*/ +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)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_APPMODEL_H_ +/// @endcond +typedef unique_any unique_package_info_reference; +#if NTDDI_VERSION >= NTDDI_WIN10_CO +typedef unique_any unique_package_dependency_context; +#endif // NTDDI_VERSION >= NTDDI_WIN10_CO +#endif // __WIL_APPMODEL_H_ +#if (defined(__WIL_APPMODEL_H_) && !defined(__WIL_APPMODEL_H_STL) && defined(WIL_RESOURCE_STL)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_APPMODEL_H_STL +/// @endcond +typedef shared_any shared_package_info_reference; +typedef weak_any weak_package_info_reference; +#if NTDDI_VERSION >= NTDDI_WIN10_CO +typedef shared_any shared_package_dependency_context; +typedef weak_any weak_package_dependency_context; +#endif // NTDDI_VERSION >= NTDDI_WIN10_CO +#endif // __WIL_APPMODEL_H_STL + +#if (defined(MSIXDYNAMICDEPENDENCY_H) && !defined(__WIL_MSIXDYNAMICDEPENDENCY_H)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_MSIXDYNAMICDEPENDENCY_H +/// @endcond +typedef unique_any unique_mdd_package_dependency_context; +#endif // __WIL_MSIXDYNAMICDEPENDENCY_H +#if (defined(MSIXDYNAMICDEPENDENCY_H) && !defined(__WIL_MSIXDYNAMICDEPENDENCY_H_STL) && defined(WIL_RESOURCE_STL)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_MSIXDYNAMICDEPENDENCY_H_STL +/// @endcond +typedef shared_any shared_mdd_package_dependency_context; +typedef weak_any weak_mdd_package_dependency_context; +#endif // __WIL_MSIXDYNAMICDEPENDENCY_H_STL + +#if (defined(_APISETLIBLOADER_) && !defined(__WIL_APISETLIBLOADER_)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_APISETLIBLOADER_ +/// @endcond +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) +typedef unique_any unique_dll_directory_cookie; +#endif +#endif // __WIL_APISETLIBLOADER_ +#if (defined(_APISETLIBLOADER_) && !defined(__WIL_APISETLIBLOADER_STL) && defined(WIL_RESOURCE_STL)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_APISETLIBLOADER_STL +/// @endcond +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM) +typedef shared_any shared_dll_directory_cookie; +typedef weak_any weak_dll_directory_cookie; +#endif +#endif // __WIL_APISETLIBLOADER_STL + +#if (defined(WDFAPI) && !defined(__WIL_WDFAPI)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_WDFAPI +namespace details +{ + template + using wdf_object_resource_policy = resource_policy; +} +/// @endcond + +template +using unique_wdf_any = unique_any_t>>; + +using unique_wdf_object = unique_wdf_any; +using unique_wdf_queue = 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; + +#if defined(WIL_KERNEL_MODE) +using unique_wdf_device_init = unique_any; +#endif + +WI_NODISCARD inline _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); +} + +WI_NODISCARD inline _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; + +WI_NODISCARD inline _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); +} + +/// @cond +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()); + } + }; +} // namespace details +/// @endcond + +using unique_wdf_wait_lock = unique_any_t; +using unique_wdf_spin_lock = unique_any_t; + +//! unique_wdf_object_reference is a RAII type for managing WDF object references acquired using +//! the WdfObjectReference* family of APIs. The behavior of this class is exactly identical to +//! wil::unique_any but a few methods have some WDF-object-reference-specific enhancements. +//! +//! * The constructor takes not only a WDFOBJECT-compatible type or a wil::unique_wdf_any, but +//! optionally also a tag with which the reference was acquired. +//! * A get_tag() method is provided to retrieve the tag. +//! * reset() is similar to the constructor in that it also optionally takes a tag. +//! * release() optionally takes an out-param that returns the tag. +//! +//! These subtle differences make it impossible to reuse the wil::unique_any_t template for its implementation. +template +class unique_wdf_object_reference +{ +public: + unique_wdf_object_reference() WI_NOEXCEPT = default; + + //! Wrap a WDF object reference that has already been acquired into this RAII type. If you + //! want to acquire a new reference instead, use WI_WdfObjectReferenceIncrement. + explicit unique_wdf_object_reference(wdf_object_t wdfObject, void* tag = nullptr) WI_NOEXCEPT : m_wdfObject(wdfObject), m_tag(tag) + { + } + + //! This is similar to the constructor that takes a raw WDF handle but is enlightened to + //! take a const-ref to a wil::unique_wdf_any<> instead, obviating the need to call .get() + //! on it. As with the other constructor, the expectation is that the raw reference has + //! already been acquired and ownership is being transferred into this RAII object. + explicit unique_wdf_object_reference(const wil::unique_wdf_any& wdfObject, void* tag = nullptr) WI_NOEXCEPT + : unique_wdf_object_reference(wdfObject.get(), tag) + { + } + + unique_wdf_object_reference(const unique_wdf_object_reference&) = delete; + unique_wdf_object_reference& operator=(const unique_wdf_object_reference&) = delete; + + unique_wdf_object_reference(unique_wdf_object_reference&& other) : m_wdfObject(other.m_wdfObject), m_tag(other.m_tag) + { + other.m_wdfObject = WDF_NO_HANDLE; + other.m_tag = nullptr; + } + + unique_wdf_object_reference& operator=(unique_wdf_object_reference&& other) + { + if (this != wistd::addressof(other)) + { + reset(other.m_wdfObject, other.m_tag); + other.m_wdfObject = WDF_NO_HANDLE; + other.m_tag = nullptr; + } + + return *this; + } + + ~unique_wdf_object_reference() WI_NOEXCEPT + { + reset(); + } + + WI_NODISCARD explicit operator bool() const WI_NOEXCEPT + { + return m_wdfObject != WDF_NO_HANDLE; + } + + WI_NODISCARD wdf_object_t get() const WI_NOEXCEPT + { + return m_wdfObject; + } + + WI_NODISCARD void* get_tag() const WI_NOEXCEPT + { + return m_tag; + } + + //! Replaces the current instance (releasing it if it exists) with a new WDF object + //! reference that has already been acquired by the caller. + void reset(wdf_object_t wdfObject = WDF_NO_HANDLE, void* tag = nullptr) WI_NOEXCEPT + { + if (m_wdfObject != WDF_NO_HANDLE) + { + // 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(m_wdfObject, m_tag); + } + + m_wdfObject = wdfObject; + m_tag = tag; + } + + void reset(const wil::unique_wdf_any& wdfObject, void* tag = nullptr) WI_NOEXCEPT + { + reset(wdfObject.get(), tag); + } + + wdf_object_t release(_Outptr_opt_ void** tag = nullptr) WI_NOEXCEPT + { + const auto wdfObject = m_wdfObject; + wil::assign_to_opt_param(tag, m_tag); + m_wdfObject = WDF_NO_HANDLE; + m_tag = nullptr; + return wdfObject; + } + + void swap(unique_wdf_object_reference& other) WI_NOEXCEPT + { + wistd::swap_wil(m_wdfObject, other.m_wdfObject); + wistd::swap_wil(m_tag, other.m_tag); + } + + //! Drops the current reference if any, and returns a pointer to a WDF handle which can + //! receive a newly referenced WDF handle. The tag is assumed to be nullptr. If a different + //! tag needs to be used, a temporary variable will need to be used to receive the WDF + //! handle and a unique_wdf_object_reference will need to be constructed with it. + //! + //! The quintessential use-case for this method is WdfIoQueueFindRequest. + wdf_object_t* put() WI_NOEXCEPT + { + reset(); + return &m_wdfObject; + } + + wdf_object_t* operator&() WI_NOEXCEPT + { + return put(); + } + +private: + wdf_object_t m_wdfObject = WDF_NO_HANDLE; + void* m_tag = nullptr; +}; + +// Increment the ref-count on a WDF object and return 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 +WI_NODISCARD inline unique_wdf_object_reference wdf_object_reference_increment( + wdf_object_t 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{wdfObject, tag}; +} + +template +WI_NODISCARD inline unique_wdf_object_reference wdf_object_reference_increment( + const wil::unique_wdf_any& wdfObject, PVOID tag, LONG lineNumber, PCSTR fileName) WI_NOEXCEPT +{ + return wdf_object_reference_increment(wdfObject.get(), tag, lineNumber, fileName); +} + +// A macro so that we can capture __LINE__ and __FILE__. +#define WI_WdfObjectReferenceIncrement(wdfObject, tag) wil::wdf_object_reference_increment(wdfObject, tag, __LINE__, __FILE__) + +//! wdf_request_completer is a unique_any-like RAII class for managing completion of a +//! WDFREQUEST. On destruction or explicit reset() it completes the WDFREQUEST with parameters +//! (status, information, priority boost) previously set using methods on this class. +//! +//! This class does not use the unique_any_t template primarily because the release() and put() +//! methods need to return a WDFREQUEST/WDFREQUEST*, as opposed to the internal storage type. +class wdf_request_completer +{ +public: + explicit wdf_request_completer(WDFREQUEST wdfRequest = WDF_NO_HANDLE) WI_NOEXCEPT : m_wdfRequest(wdfRequest) + { + } + + wdf_request_completer(const wdf_request_completer&) = delete; + wdf_request_completer& operator=(const wdf_request_completer&) = delete; + + wdf_request_completer(wdf_request_completer&& other) WI_NOEXCEPT : m_wdfRequest(other.m_wdfRequest), + m_status(other.m_status), + m_information(other.m_information), +#if defined(WIL_KERNEL_MODE) + m_priorityBoost(other.m_priorityBoost), +#endif + m_completionFlags(other.m_completionFlags) + { + clear_state(other); + } + + wdf_request_completer& operator=(wdf_request_completer&& other) WI_NOEXCEPT + { + if (this != wistd::addressof(other)) + { + reset(); + m_wdfRequest = other.m_wdfRequest; + m_status = other.m_status; + m_information = other.m_information; +#if defined(WIL_KERNEL_MODE) + m_priorityBoost = other.m_priorityBoost; +#endif + m_completionFlags = other.m_completionFlags; + clear_state(other); + } + + return *this; + } + + ~wdf_request_completer() WI_NOEXCEPT + { + reset(); + } + + WI_NODISCARD WDFREQUEST get() const WI_NOEXCEPT + { + return m_wdfRequest; + } + + //! Set the NTSTATUS value with with the WDFREQUEST will be completed when the RAII object + //! goes out of scope or .reset() is called explicitly. Calling this method does *not* + //! complete the request right away. No effect if this object currently does not have + //! ownership of a WDFREQUEST. The expected usage pattern is that set_status() is called + //! only after ownership of a WDFREQUEST is transferred to this object. + void set_status(NTSTATUS status) WI_NOEXCEPT + { + // The contract is that this method has no effect if we currently do not have a + // m_wdfRequest. But that is enforced by discarding all state when a WDFREQUEST is + // attached, not by explicitly checking for that condition here. + + m_status = status; + } + + //! Set the IO_STATUS_BLOCK.Information value with which the WDFREQUEST will be completed. + //! Note that the Information value is not stored directly in the WDFREQUEST using + //! WdfRequestSetInformation. It is only used at the time of completion. No effect if this + //! object currently does not have ownership of a WDFREQUEST. The expected usage pattern is + //! that set_information() is called only after ownership of a WDFREQUEST is transferred to + //! this object. + void set_information(ULONG_PTR information) WI_NOEXCEPT + { + // The contract is that this method has no effect if we currently do not have a + // m_wdfRequest. But that is enforced by discarding all state when a WDFREQUEST is + // attached, not by explicitly checking for that condition here. + + m_completionFlags.informationSet = 1; + m_information = information; + } + +#if defined(WIL_KERNEL_MODE) + //! Set the priority boost with which the WDFREQUEST will be completed. If this method is + //! called, the WDFREQUEST will eventually be completed with + //! WdfRequestCompleteWithPriorityBoost. No effect if this object currently does not have + //! ownership of a WDFREQUEST. The expected usage pattern is that set_priority_boost() is + //! called only after ownership of a WDFREQUEST is transferred to this object. + void set_priority_boost(CCHAR priorityBoost) WI_NOEXCEPT + { + // The contract is that this method has no effect if we currently do not have a + // m_wdfRequest. But that is enforced by discarding all state when a WDFREQUEST is + // attached, not by explicitly checking for that condition here. + + m_completionFlags.priorityBoostSet = 1; + m_priorityBoost = priorityBoost; + } +#endif + + WI_NODISCARD explicit operator bool() const WI_NOEXCEPT + { + return m_wdfRequest != WDF_NO_HANDLE; + } + + WDFREQUEST* put() WI_NOEXCEPT + { + reset(); + return &m_wdfRequest; + } + + WDFREQUEST* operator&() WI_NOEXCEPT + { + return put(); + } + + //! Relinquishes completion responsibility for the WDFREQUEST. Note that any state + //! (information, priority boost, status) set on this object is lost. This design choice was + //! made because it is atypical to set an information or priority boost value upfront; they + //! are typically set at the point where the request is going to be completed. Hence a + //! use-case wherein release() is called will typically not have set an information or + //! priority boost. + WDFREQUEST release() WI_NOEXCEPT + { + const auto wdfRequest = m_wdfRequest; + clear_state(*this); + return wdfRequest; + } + + void swap(wdf_request_completer& other) WI_NOEXCEPT + { + wistd::swap_wil(m_wdfRequest, other.m_wdfRequest); + wistd::swap_wil(m_information, other.m_information); + wistd::swap_wil(m_status, other.m_status); +#if defined(WIL_KERNEL_MODE) + wistd::swap_wil(m_priorityBoost, other.m_priorityBoost); +#endif + wistd::swap_wil(m_completionFlags, other.m_completionFlags); + } + + void reset(WDFREQUEST newWdfRequest = WDF_NO_HANDLE) + { + if (m_wdfRequest != WDF_NO_HANDLE) + { + // We try to match the usage patterns that the driver would have typically used in the + // various scenarios. For instance, if the driver has set the information field, we'll + // call WdfRequestCompleteWithInformation instead of calling WdfRequestSetInformation + // followed by WdfRequestComplete. + +#if defined(WIL_KERNEL_MODE) + if (m_completionFlags.priorityBoostSet) + { + if (m_completionFlags.informationSet) + { + WdfRequestSetInformation(m_wdfRequest, m_information); + } + + WdfRequestCompleteWithPriorityBoost(m_wdfRequest, m_status, m_priorityBoost); + } + else +#endif + if (m_completionFlags.informationSet) + { + WdfRequestCompleteWithInformation(m_wdfRequest, m_status, m_information); + } + else + { + WdfRequestComplete(m_wdfRequest, m_status); + } + } + + // We call clear_state unconditionally just in case some parameters (status, + // information, etc.) were set prior to attaching a WDFREQUEST to this object. Those + // parameters are not considered relevant to the WDFREQUEST being attached to this + // object now. + clear_state(*this, newWdfRequest); + } + +private: + static void clear_state(wdf_request_completer& completer, WDFREQUEST newWdfRequest = WDF_NO_HANDLE) WI_NOEXCEPT + { + completer.m_wdfRequest = newWdfRequest; + completer.m_status = STATUS_UNSUCCESSFUL; + completer.m_information = 0; +#if defined(WIL_KERNEL_MODE) + completer.m_priorityBoost = 0; +#endif + completer.m_completionFlags = {}; + } + + // Members are ordered in decreasing size to minimize padding. + + WDFREQUEST m_wdfRequest = WDF_NO_HANDLE; + + // This will not be used unless m_completionFlags.informationSet is set. + ULONG_PTR m_information = 0; + + // There is no reasonably default NTSTATUS value. Callers are expected to explicitly set a + // status value at the point where it is decided that the request needs to be completed. + NTSTATUS m_status = STATUS_UNSUCCESSFUL; + +// UMDF does not support WdfRequestCompleteWithPriorityBoost. +#if defined(WIL_KERNEL_MODE) + // This will not be used unless m_completionFlags.priorityBoostSet is set. + CCHAR m_priorityBoost = 0; +#endif + + struct + { + UINT8 informationSet : 1; +#if defined(WIL_KERNEL_MODE) + UINT8 priorityBoostSet : 1; +#endif + } m_completionFlags = {}; +}; +#endif + +#if (WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) && defined(_CFGMGR32_H_) && (WINVER >= _WIN32_WINNT_WIN8) && !defined(__WIL_CFGMGR32_H_)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL_CFGMGR32_H_ +/// @endcond +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_)) || \ + defined(WIL_DOXYGEN) +/// @cond +#define __WIL_SWDEVICE_H_ +/// @endcond +typedef unique_any unique_hswdevice; +#endif + +#if defined(WIL_KERNEL_MODE) && (defined(_WDMDDK_) || defined(_NTDDK_)) && !defined(__WIL_RESOURCE_WDM) +#define __WIL_RESOURCE_WDM + +/// @cond +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. + WI_NODISCARD 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); + } +} // namespace details +/// @endcond + +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; +}; + +/// @cond +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. + WI_NODISCARD 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; + }; +} // namespace details +/// @endcond + +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; +}; + +/// @cond +namespace details +{ + _IRQL_requires_max_(APC_LEVEL) + inline void release_fast_mutex_with_critical_region(FAST_MUTEX* fastMutex) WI_NOEXCEPT + { + ::ExReleaseFastMutexUnsafe(fastMutex); + ::KeLeaveCriticalRegion(); + } +} // namespace details +/// @endcond + +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 {}; +} + +//! WDM version of EX_PUSH_LOCK is available starting with Windows 10 1809 +#if (NTDDI_VERSION >= NTDDI_WIN10_RS5) +/// @cond +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(); + } +} // namespace details +/// @endcond + +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; +}; +#endif + +/// @cond +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); + } + } + }; +} // namespace details +/// @endcond + +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)) || \ + defined(WIL_DOXYGEN) +/// @cond +#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); + } +} // namespace details +/// @endcond +typedef wil::unique_struct unique_wintrust_data; +#endif // __WIL_WINTRUST + +#if (defined(MSCAT_H) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && !defined(__WIL_MSCAT)) || defined(WIL_DOXYGEN) +/// @cond +#define __WIL_MSCAT +namespace details +{ + inline void __stdcall CryptCATAdminReleaseContextNoFlags(_Pre_opt_valid_ _Frees_ptr_opt_ HCATADMIN handle) WI_NOEXCEPT + { + CryptCATAdminReleaseContext(handle, 0); + } +} // namespace details +/// @endcond +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; + +/// @cond +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) + { + } + + WI_NODISCARD 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; + } + + WI_NODISCARD bool operator==(wistd::nullptr_t) const WI_NOEXCEPT + { + return m_r.m_hCatInfo == nullptr; + } + + WI_NODISCARD bool operator!=(wistd::nullptr_t) const WI_NOEXCEPT + { + return !(*this == nullptr); + } + + private: + crypt_catalog_enumerator& m_r; + }; + + struct iterator + { +#if defined(_XUTILITY_) || defined(WIL_DOXYGEN) + // 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; + + WI_NODISCARD bool operator==(const iterator& rhs) const WI_NOEXCEPT + { + if (rhs.m_r == m_r) + { + return true; + } + + return (*this == nullptr) && (rhs == nullptr); + } + + WI_NODISCARD bool operator!=(const iterator& rhs) const WI_NOEXCEPT + { + return !(rhs == *this); + } + + WI_NODISCARD bool operator==(wistd::nullptr_t) const WI_NOEXCEPT + { + return nullptr == m_r || nullptr == m_r->current(); + } + + WI_NODISCARD bool operator!=(wistd::nullptr_t) const WI_NOEXCEPT + { + return !(*this == nullptr); + } + + iterator& operator++() WI_NOEXCEPT + { + if (m_r != nullptr) + { + m_r->next(); + } + + return *this; + } + + WI_NODISCARD 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 + { + } + + WI_NODISCARD iterator begin() WI_NOEXCEPT + { + init(); + return iterator(this); + } + + WI_NODISCARD 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; + }; +} // namespace details +/// @endcond + +/** 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 + +#if !defined(__WIL_RESOURCE_LOCK_ENFORCEMENT) +/// @cond +#define __WIL_RESOURCE_LOCK_ENFORCEMENT +/// @endcond + +/// @cond +namespace details +{ + // Only those lock types specialized by lock_proof_traits will allow either a write_lock_required or + // read_lock_required to be constructed. The allows_exclusive value indicates if the type represents an exclusive, + // write-safe lock acquisition, or a shared, read-only lock acquisition. + template + struct lock_proof_traits + { + }; + + // Base for specializing lock_proof_traits where the lock type is shared + struct shared_lock_proof + { + static constexpr bool allows_shared = true; + }; + + // Base for specializing lock_proof_traits where the lock type is exclusive (super-set of shared_lock_proof) + struct exclusive_lock_proof : shared_lock_proof + { + static constexpr bool allows_exclusive = true; + }; +} // namespace details +/// @endcond + +/** +Functions that need an exclusive lock use can use write_lock_required as a parameter to enforce lock +safety at compile time. Similarly, read_lock_required may stand as a parameter where shared ownership +of a lock is required. These are empty structs that will never be used, other than passing them on to +another function that requires them. + +These types are implicitly convertible from various lock holding types, enabling callers to provide them as +proof of the lock that they hold. + +The following example is intentionally contrived to demonstrate multiple use cases: + - Methods that require only shared/read access + - Methods that require only exclusive write access + - Methods that pass their proof-of-lock to a helper +~~~ + class RemoteControl + { + public: + void VolumeUp(); + int GetVolume(); + private: + int GetCurrentVolume(wil::read_lock_required); + void AdjustVolume(int delta, wil::write_lock_required); + void SetNewVolume(int newVolume, wil::write_lock_required); + + int m_currentVolume = 0; + wil::srwlock m_lock; + }; + + void RemoteControl::VolumeUp() + { + auto writeLock = m_lock.lock_exclusive(); + AdjustVolume(1, writeLock); + } + + int RemoteControl::GetVolume() + { + auto readLock = m_lock.lock_shared(); + return GetCurrentVolume(readLock); + } + + int RemoteControl::GetCurrentVolume(wil::read_lock_required) + { + return m_currentVolume; + } + + void AdjustVolume(int delta, wil::write_lock_required lockProof) + { + const auto currentVolume = GetCurrentVolume(lockProof); + SetNewVolume(currentVolume + delta, lockProof); + } + + void RemoteControl::SetNewVolume(int newVolume, wil::write_lock_required) + { + m_currentVolume = newVolume; + } +~~~ + +In this design it is impossible to not meet the "lock must be held" precondition and the function parameter types +help you understand which one. + +Cases not handled: + - Functions that need the lock held, but fail to specify this fact by requiring a lock required parameter need + to be found via code inspection. + - Recursively taking a lock, when it is already held, is not avoided in this pattern + - Readers will learn to be suspicious of acquiring a lock in functions with lock required parameters. + - Designs with multiple locks, that must be careful to take them in the same order every time, are not helped + by this pattern. + - Locking the wrong object + - Use of a std::lock type that has not actually be secured yet (such as by std::try_to_lock or std::defer_lock) + - or use of a lock type that had been acquired but has since been released, reset, or otherwise unlocked + +These utility types are not fool-proof against all lock misuse, anti-patterns, or other complex yet valid +scenarios. However on the net, their usage in typical cases can assist in creating clearer, self-documenting +code that catches the common issues of forgetting to hold a lock or forgetting whether a lock is required to +call another method safely. +*/ +struct write_lock_required +{ + /** + Construct a new write_lock_required object for use as proof that an exclusive lock has been acquired. + */ + template + write_lock_required(const TLockProof&, wistd::enable_if_t::allows_exclusive, int> = 0) + { + } + + write_lock_required() = delete; // No default construction +}; + +/** +Stands as proof that a shared lock has been acquired. See write_lock_required for more information. +*/ +struct read_lock_required +{ + /** + Construct a new read_lock_required object for use as proof that a shared lock has been acquired. + */ + template + read_lock_required(const TLockProof&, wistd::enable_if_t::allows_shared, int> = 0) + { + } + + /** + Uses a prior write_lock_required object to construct a read_lock_required object as proof that at shared lock + has been acquired. (Exclusive locks held are presumed to suffice for proof of a read lock) + */ + read_lock_required(const write_lock_required&) + { + } + + read_lock_required() = delete; // No default construction +}; +#endif // __WIL_RESOURCE_LOCK_ENFORCEMENT + +#if defined(__WIL_WINBASE_) && !defined(__WIL__RESOURCE_LOCKPROOF_WINBASE) && defined(__WIL_RESOURCE_LOCK_ENFORCEMENT) +#define __WIL__RESOURCE_LOCKPROOF_WINBASE + +/// @cond +namespace details +{ + // Specializations for srwlock + template <> + struct lock_proof_traits : shared_lock_proof + { + }; + + template <> + struct lock_proof_traits : exclusive_lock_proof + { + }; + + // Specialization for critical_section + template <> + struct lock_proof_traits : exclusive_lock_proof + { + }; +} // namespace details +/// @endcond + +#endif //__WIL__RESOURCE_LOCKPROOF_WINBASE + +#if defined(_MUTEX_) && !defined(__WIL__RESOURCE_LOCKPROOF_MUTEX) && defined(__WIL_RESOURCE_LOCK_ENFORCEMENT) +#define __WIL__RESOURCE_LOCKPROOF_MUTEX + +/// @cond +namespace details +{ + template + struct lock_proof_traits> : exclusive_lock_proof + { + }; + + template + struct lock_proof_traits> : exclusive_lock_proof + { + }; +} // namespace details +/// @endcond + +#endif //__WIL__RESOURCE_LOCKPROOF_MUTEX + +#if defined(_SHARED_MUTEX_) && !defined(__WIL__RESOURCE_LOCKPROOF_SHAREDMUTEX) && defined(__WIL_RESOURCE_LOCK_ENFORCEMENT) +#define __WIL__RESOURCE_LOCKPROOF_SHAREDMUTEX + +/// @cond +namespace details +{ + template + struct lock_proof_traits> : shared_lock_proof + { + }; +} // namespace details +/// @endcond + +#endif //__WIL__RESOURCE_LOCKPROOF_SHAREDMUTEX + +} // namespace wil + +#pragma warning(pop) diff --git a/libs/wil/wil/result.h b/libs/wil/wil/result.h new file mode 100644 index 00000000..154a4d42 --- /dev/null +++ b/libs/wil/wil/result.h @@ -0,0 +1,1293 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! WIL Error Handling Helpers: a family of macros and functions designed to uniformly handle and report errors across return +//! codes, fail fast, exceptions and logging. +#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. + m_refCount = m_refCount - 1; + if (m_refCount == 0) + { + m_data.ProcessShutdown(); + } + } + else + { + auto lock = m_mutex.acquire(); + m_refCount = m_refCount - 1; + 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 = (*data)->m_refCount + 1; + } + 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, _Outptr_result_nullonfailure_ ProcessLocalStorageData** data) + { + *data = nullptr; + + const DWORD size = static_cast(sizeof(ProcessLocalStorageData)); + + unique_process_heap_ptr dataAlloc(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 = static_cast*>(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; +#pragma warning(push) +#pragma warning(disable : 6001) // https://github.com/microsoft/wil/issues/164 + pNode = pNode->pNext; +#pragma warning(pop) + 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 = 0xffffffff; // MAXDWORD + 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) const + { + ::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)); + } + + WI_NODISCARD bool GetLastError(_Inout_ wil::FailureInfo& info, unsigned int minSequenceId, HRESULT matchRequirement) const + { + 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]{}; + 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); + } + +} // namespace 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_opt_ 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; + } + + WI_NODISCARD bool IsWatching() const + { + 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) + { + // If the telemetry was requested to be suppressed, + // pretend like it has already been reported to the fallback callback + g_pfnTelemetryCallback(reportedTelemetry || WI_IsFlagSet(pFailure->flags, FailureFlags::RequestSuppressTelemetry), *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 override + { + 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; + } + } +} // namespace details +/// @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_INITIALIZATION_FUNCTION(InitializeResultHeader, [] { + g_pfnGetContextAndNotifyFailure = GetContextAndNotifyFailure; + ::wil::details_abi::g_pProcessLocalData = &g_processLocalData; + g_pThreadFailureCallbacks = &g_threadFailureCallbacks; + return 1; + }); +#endif +} // namespace details +/// @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 override + { + // 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; +}; + +} // namespace wil + +#pragma warning(pop) + +#endif diff --git a/libs/wil/wil/result_macros.h b/libs/wil/wil/result_macros.h new file mode 100644 index 00000000..59a208d0 --- /dev/null +++ b/libs/wil/wil/result_macros.h @@ -0,0 +1,7338 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! WIL Error Handling Helpers: supporting file defining a family of macros and functions designed to uniformly handle errors +//! across return codes, fail fast, exceptions and logging. +#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. For kernel-mode, we ignore NDEBUG because that gets set automatically +// for driver projects. We mimic the behavior of NT_ASSERT which checks only for DBG. +// RESULT_NO_DEBUG is provided as an opt-out mechanism. +#ifndef RESULT_DEBUG +#if (DBG || defined(DEBUG) || defined(_DEBUG)) && !defined(RESULT_NO_DEBUG) && (defined(WIL_KERNEL_MODE) || !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_FAIL(msg) __WI_ASSERT_FAIL_ANNOTATION(L"" msg), DbgRaiseAssertionFailure() +#define WI_ASSERT(condition) (__WI_ANALYSIS_ASSUME(condition), ((!(condition)) ? (WI_ASSERT_FAIL(#condition), 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_FAIL(msg) +#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 +/// @cond +#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 +/// @endcond + +//***************************************************************************** +// 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 +//***************************************************************************** +/// @cond +#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 +/// @endcond + +//***************************************************************************** +// 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 + +// Force the compiler to evaluate a call to 'wprintf' to verify the format string & args and produce warnings if there +// are any issues. The short-circuit 'and' will prevent the call and strings used from making it into the binary. +// Note that this requires using a string literal for the format string. If you don't, you'll get the following compiler +// error: error C2146: syntax error: missing ')' before identifier '...' +#if !defined(wprintf) && !defined(WIL_NO_MSG_FORMAT_CHECKS) +#define __WI_CHECK_MSG_FMT(fmt, ...) (0 && ::wprintf(L"" fmt, ##__VA_ARGS__)) ? nullptr : fmt, ##__VA_ARGS__ +#else +#define __WI_CHECK_MSG_FMT(fmt, ...) fmt, ##__VA_ARGS__ +#endif + +// 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, __WI_CHECK_MSG_FMT(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, __WI_CHECK_MSG_FMT(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, __WI_CHECK_MSG_FMT(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, __WI_CHECK_MSG_FMT(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) __WI_CHECK_MSG_FMT(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, __WI_CHECK_MSG_FMT(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, __WI_CHECK_MSG_FMT(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_SUPPRESS_TELEMETRY(hr, str) \ + __WI_SUPPRESS_4127_S do \ + { \ + const HRESULT __hr = (hr); \ + __R_FN(Return_HrSuppressTelemetry)(__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) + +/// @cond +#define __WI_OR_IS_EXPECTED_HRESULT(e) || (__hrRet == wil::verify_hresult(e)) +/// @endcond +#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) + +// Always logs failed HR, if expected, telemetry will be called with 'alreadyReported' +#define RETURN_IF_FAILED_SUPPRESS_TELEMETRY_IF_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_HR_FAIL_SUPPRESS_TELEMETRY(__hrRet, #hr); \ + } \ + __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), __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define LOG_LAST_ERROR_MSG(fmt, ...) __R_FN(Log_GetLastErrorMsg)(__R_INFO(nullptr) __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define LOG_WIN32_MSG(win32err, fmt, ...) \ + __R_FN(Log_Win32Msg)(__R_INFO(#win32err) win32err, __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define LOG_NTSTATUS_MSG(status, fmt, ...) \ + __R_FN(Log_NtStatusMsg)(__R_INFO(#status) status, __WI_CHECK_MSG_FMT(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), __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define LOG_IF_WIN32_BOOL_FALSE_MSG(win32BOOL, fmt, ...) \ + __R_FN(Log_IfWin32BoolFalseMsg)(__R_INFO(#win32BOOL) wil::verify_BOOL(win32BOOL), __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define LOG_IF_WIN32_ERROR_MSG(win32err, fmt, ...) \ + __R_FN(Log_IfWin32ErrorMsg)(__R_INFO(#win32err) win32err, __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define LOG_IF_NULL_ALLOC_MSG(ptr, fmt, ...) \ + __R_FN(Log_IfNullAllocMsg)(__R_INFO(#ptr) ptr, __WI_CHECK_MSG_FMT(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), __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define LOG_HR_IF_NULL_MSG(hr, ptr, fmt, ...) \ + __R_FN(Log_HrIfNullMsg)(__R_INFO(#ptr) wil::verify_hresult(hr), ptr, __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define LOG_LAST_ERROR_IF_MSG(condition, fmt, ...) \ + __R_FN(Log_GetLastErrorIfMsg)(__R_INFO(#condition) wil::verify_bool(condition), __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define LOG_LAST_ERROR_IF_NULL_MSG(ptr, fmt, ...) \ + __R_FN(Log_GetLastErrorIfNullMsg)(__R_INFO(#ptr) ptr, __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define LOG_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) \ + __R_FN(Log_IfNtStatusFailedMsg)(__R_INFO(#status) status, __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) + +/// @cond +#define __WI_COMMA_EXPECTED_HRESULT(e) , wil::verify_hresult(e) +/// @endcond +#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), __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define FAIL_FAST_LAST_ERROR_MSG(fmt, ...) \ + __RFF_FN(FailFast_GetLastErrorMsg)(__RFF_INFO(nullptr) __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define FAIL_FAST_WIN32_MSG(win32err, fmt, ...) \ + __RFF_FN(FailFast_Win32Msg)(__RFF_INFO(#win32err) win32err, __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define FAIL_FAST_NTSTATUS_MSG(status, fmt, ...) \ + __RFF_FN(FailFast_NtStatusMsg)(__RFF_INFO(#status) status, __WI_CHECK_MSG_FMT(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), __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define FAIL_FAST_IF_WIN32_BOOL_FALSE_MSG(win32BOOL, fmt, ...) \ + __RFF_FN(FailFast_IfWin32BoolFalseMsg) \ + (__RFF_INFO(#win32BOOL) wil::verify_BOOL(win32BOOL), __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define FAIL_FAST_IF_WIN32_ERROR_MSG(win32err, fmt, ...) \ + __RFF_FN(FailFast_IfWin32ErrorMsg)(__RFF_INFO(#win32err) win32err, __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define FAIL_FAST_IF_NULL_ALLOC_MSG(ptr, fmt, ...) \ + __RFF_FN(FailFast_IfNullAllocMsg)(__RFF_INFO(#ptr) ptr, __WI_CHECK_MSG_FMT(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), __WI_CHECK_MSG_FMT(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, __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define FAIL_FAST_LAST_ERROR_IF_MSG(condition, fmt, ...) \ + __RFF_FN(FailFast_GetLastErrorIfMsg) \ + (__RFF_INFO(#condition) wil::verify_bool(condition), __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define FAIL_FAST_LAST_ERROR_IF_NULL_MSG(ptr, fmt, ...) \ + __RFF_FN(FailFast_GetLastErrorIfNullMsg)(__RFF_INFO(#ptr) ptr, __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define FAIL_FAST_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) \ + __RFF_FN(FailFast_IfNtStatusFailedMsg)(__RFF_INFO(#status) status, __WI_CHECK_MSG_FMT(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) __WI_CHECK_MSG_FMT(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), __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define FAIL_FAST_IF_NULL_MSG(ptr, fmt, ...) \ + __RFF_FN(FailFast_IfNullMsg)(__RFF_INFO(#ptr) ptr, __WI_CHECK_MSG_FMT(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) + +// Like 'FAIL_FAST_IF', but raises an assertion failure first for easier debugging +#define WI_FAIL_FAST_ASSERT(condition) \ + do \ + { \ + if (!wil::verify_bool(condition)) \ + { \ + WI_ASSERT_FAIL(#condition); \ + __RFF_FN(FailFast_Unexpected)(__RFF_INFO_ONLY(#condition)); \ + } \ + } while (0, 0) +#define WI_FAIL_FAST_ASSERT_MSG(condition, msg) \ + do \ + { \ + if (!wil::verify_bool(condition)) \ + { \ + WI_ASSERT_FAIL(msg); \ + __RFF_FN(FailFast_UnexpectedMsg)(__RFF_INFO(#condition) __WI_CHECK_MSG_FMT(msg)); \ + } \ + } while (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_WIN32_IF(win32err, condition) \ + __R_FN(Throw_Win32If)(__R_INFO(#condition) wil::verify_win32(win32err), wil::verify_bool(condition)) +#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), __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define THROW_LAST_ERROR_MSG(fmt, ...) __R_FN(Throw_GetLastErrorMsg)(__R_INFO(nullptr) __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define THROW_WIN32_MSG(win32err, fmt, ...) \ + __R_FN(Throw_Win32Msg)(__R_INFO(#win32err) win32err, __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define THROW_EXCEPTION_MSG(exception, fmt, ...) \ + wil::details::ReportFailure_CustomExceptionMsg(__R_INFO(#exception) exception, __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define THROW_NTSTATUS_MSG(status, fmt, ...) \ + __R_FN(Throw_NtStatusMsg)(__R_INFO(#status) status, __WI_CHECK_MSG_FMT(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), __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define THROW_IF_WIN32_BOOL_FALSE_MSG(win32BOOL, fmt, ...) \ + __R_FN(Throw_IfWin32BoolFalseMsg)(__R_INFO(#win32BOOL) wil::verify_BOOL(win32BOOL), __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define THROW_IF_WIN32_ERROR_MSG(win32err, fmt, ...) \ + __R_FN(Throw_IfWin32ErrorMsg)(__R_INFO(#win32err) win32err, __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define THROW_IF_NULL_ALLOC_MSG(ptr, fmt, ...) \ + __R_FN(Throw_IfNullAllocMsg)(__R_INFO(#ptr) ptr, __WI_CHECK_MSG_FMT(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), __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define THROW_HR_IF_NULL_MSG(hr, ptr, fmt, ...) \ + __R_FN(Throw_HrIfNullMsg)(__R_INFO(#ptr) wil::verify_hresult(hr), ptr, __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define THROW_WIN32_IF_MSG(win32err, condition, fmt, ...) \ + __R_FN(Throw_Win32IfMsg) \ + (__R_INFO(#condition) wil::verify_win32(win32err), wil::verify_bool(condition), __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define THROW_LAST_ERROR_IF_MSG(condition, fmt, ...) \ + __R_FN(Throw_GetLastErrorIfMsg)(__R_INFO(#condition) wil::verify_bool(condition), __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define THROW_LAST_ERROR_IF_NULL_MSG(ptr, fmt, ...) \ + __R_FN(Throw_GetLastErrorIfNullMsg)(__R_INFO(#ptr) ptr, __WI_CHECK_MSG_FMT(fmt, ##__VA_ARGS__)) +#define THROW_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) \ + __R_FN(Throw_IfNtStatusFailedMsg)(__R_INFO(#status) status, __WI_CHECK_MSG_FMT(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) __WI_CHECK_MSG_FMT(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) __WI_CHECK_MSG_FMT(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) __WI_CHECK_MSG_FMT(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) __WI_CHECK_MSG_FMT(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 +//***************************************************************************** +/// @cond +#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 +/// @endcond + +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)); + } +} // namespace details +/// @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 behavior + 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]{}; + 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 + { + enum class Kind : unsigned int + { + HResult, + NtStatus + }; + + static ResultStatus FromResult(const HRESULT _hr) + { + return {_hr, wil::details::HrToNtStatus(_hr), Kind::HResult}; + } + static ResultStatus FromStatus(const NTSTATUS _status) + { + return {wil::details::NtStatusToHr(_status), _status, Kind::NtStatus}; + } + static ResultStatus FromFailureInfo(const FailureInfo& _failure) + { + return {_failure.hr, _failure.status, WI_IsFlagSet(_failure.flags, FailureFlags::NtStatus) ? Kind::NtStatus : Kind::HResult}; + } + HRESULT hr = S_OK; + NTSTATUS status = STATUS_SUCCESS; + Kind kind = Kind::NtStatus; + }; + + // 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_pfnNotifyFailure)(_Inout_ FailureInfo* pFailure) WI_PFN_NOEXCEPT = nullptr; + __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; + + // 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) WI_NOEXCEPT + { + const HANDLE processHeap = ::GetProcessHeap(); + const PVOID allocation = ::HeapAlloc(processHeap, flags, size); + + static bool fetchedRtlDisownModuleHeapAllocation = false; + static NTSTATUS(__stdcall * pfnRtlDisownModuleHeapAllocation)(HANDLE, PVOID) WI_PFN_NOEXCEPT = nullptr; + + if (pfnRtlDisownModuleHeapAllocation) + { + (void)pfnRtlDisownModuleHeapAllocation(processHeap, allocation); + } + else if (!fetchedRtlDisownModuleHeapAllocation) + { + if (auto ntdllModule = ::GetModuleHandleW(L"ntdll.dll")) + { + pfnRtlDisownModuleHeapAllocation = reinterpret_cast( + ::GetProcAddress(ntdllModule, "RtlDisownModuleHeapAllocation")); + } + fetchedRtlDisownModuleHeapAllocation = true; + + if (pfnRtlDisownModuleHeapAllocation) + { + (void)pfnRtlDisownModuleHeapAllocation(processHeap, 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 + { + using type = tag_return_other; + }; + + template <> + struct return_type + { + using type = tag_return_HRESULT; + }; + + template <> + struct return_type + { + using type = tag_return_void; + }; + + template <> + struct return_type + { + using type = tag_return_void; + }; + + 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 __R_NS_NAME + 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; + } // namespace __RFF_NS_NAME + + 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, + FailureFlags flags, + _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, + FailureFlags flags = FailureFlags::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, FailureFlags flags = FailureFlags::None); + 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); + } + + WI_NODISCARD 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); + } + + WI_NODISCARD size_t size() const WI_NOEXCEPT + { + return m_size; + } + + WI_NODISCARD explicit operator bool() const WI_NOEXCEPT + { + return (m_pCopy != nullptr); + } + + WI_NODISCARD 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&& param1) + { + RefAndObject* pObject = new (std::nothrow) RefAndObject(wistd::forward(param1)); + if (pObject == nullptr) + { + return false; + } + reset(); + m_pCopy = pObject; + return true; + } + + WI_NODISCARD object_t* get() const WI_NOEXCEPT + { + return (m_pCopy == nullptr) ? nullptr : &m_pCopy->m_object; + } + + WI_NODISCARD explicit operator bool() const WI_NOEXCEPT + { + return (m_pCopy != nullptr); + } + + WI_NODISCARD bool unique() const WI_NOEXCEPT + { + return ((m_pCopy != nullptr) && (m_pCopy->m_refCount == 1)); + } + + WI_NODISCARD 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&& param1) : 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) + __WI_PUSH_WARNINGS +#if __clang_major__ >= 13 + __WI_CLANG_DISABLE_WARNING(-Wunused-but-set-variable) // s_hrErrorLast used for debugging. We intentionally only assign to it +#endif + __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; + } + __WI_POP_WARNINGS + + 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). + // [MSFT internal] 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; + } + + inline __declspec(noinline) DWORD GetLastErrorFail() WI_NOEXCEPT + { + __R_FN_LOCALS_FULL_RA; + return GetLastErrorFail(__R_FN_CALL_FULL); + } + + _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). + + inline HRESULT WilStringLengthWorkerA( + _In_reads_or_z_(cchMax) 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_ + inline HRESULT StringCchLengthA( + _In_reads_or_z_(cchMax) 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 = S_OK; + if ((psz == nullptr) || (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) inline HRESULT + WilStringValidateDestA(_In_reads_opt_(cchDest) 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; + } + + inline HRESULT 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{}; + + // leave the last space for the null terminator + size_t cchMax = cchDest - 1; + size_t cchNewDestLength = 0; +#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, nullptr, 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)); // lgtm[cpp/incorrect-string-type-conversion] False positive - The query is misinterpreting a buffer (char *) with a MBS string, the cast to TString is expected. + 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 = (wistd::min)(pEnd, pStart + ((cchLen + 1) * sizeof((*ppszBufferString)[0]))); + __analysis_assume((pReturn >= pStart) && (pReturn <= pEnd)); + return pReturn; + } +} // namespace details +/// @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_INITIALIZATION_FUNCTION(WilInitialize_ResultMacros_DesktopOrSystem_SuppressPrivateApiUse, [] { + ::wil::WilInitialize_ResultMacros_DesktopOrSystem_SuppressPrivateApiUse(); + return 1; + }); +#endif +#endif +} // namespace details +/// @endcond +#else // !WINAPI_PARTITION_DESKTOP, !WINAPI_PARTITION_SYSTEM, explicitly assume these modules can direct link +namespace details +{ + WI_HEADER_INITIALIZATION_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: + manually_managed_shutdown_aware_object() = default; + manually_managed_shutdown_aware_object(manually_managed_shutdown_aware_object const&) = delete; + void operator=(manually_managed_shutdown_aware_object const&) = delete; + + 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(); + } + + shutdown_aware_object(shutdown_aware_object const&) = delete; + void operator=(shutdown_aware_object const&) = delete; + + //! 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(); + } + } + + object_without_destructor_on_shutdown(object_without_destructor_on_shutdown const&) = delete; + void operator=(object_without_destructor_on_shutdown const&) = delete; + + //! 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); + } + + WI_NODISCARD 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)) WI_NODISCARD 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)) WI_NODISCARD 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. + WI_NODISCARD 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. + WI_NODISCARD inline const char* __CLR_OR_THIS_CALL what() const WI_NOEXCEPT override + { +#if !defined(NONLS) && !defined(NOAPISET) + if (!m_what) + { + wchar_t message[2048]; + GetFailureLogString(message, ARRAYSIZE(message), m_failure.GetFailureInfo()); + + int len = WideCharToMultiByte(CP_ACP, 0, message, -1, nullptr, 0, nullptr, nullptr); + if (!m_what.create(len)) + { + // Allocation failed, return placeholder string. + return "WIL Exception"; + } + + WideCharToMultiByte(CP_ACP, 0, message, -1, static_cast(m_what.get()), len, nullptr, nullptr); + } + return static_cast(m_what.get()); +#else + 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()); +#endif + } + + // 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]{}; + 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]{}; + 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]{}; + 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]{}; + 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; + } + + // clang-format off +#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_INITIALIZATION_FUNCTION(InitializeWinRt, [] { + g_pfnResultFromCaughtException_WinRt = ResultFromCaughtException_WinRt; + g_pfnResultFromKnownExceptions_WinRt = ResultFromKnownExceptions_WinRt; + g_pfnThrowPlatformException = ThrowPlatformException; + return 1; + }); +#endif +#endif + // clang-format on + + 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_INITIALIZATION_FUNCTION(InitializeResultExceptions, [] { + g_pfnRunFunctorWithExceptionFilter = RunFunctorWithExceptionFilter; + g_pfnRethrow = Rethrow; + g_pfnThrowResultException = ThrowResultExceptionInternal; + g_pfnResultFromCaughtExceptionInternal = ResultFromCaughtExceptionInternal; + return 1; + }); +} +/// @endcond + +//! 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. +//! ~~~~ +//! 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. +//! ~~~~ +//! 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. +//! +//! This 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); +} + +/// @cond +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, + FailureFlags flags, + _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 = flags; + WI_SetFlagIf(failure->flags, FailureFlags::NtStatus, resultPair.kind == ResultStatus::Kind::NtStatus); + 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; + + // Process failure notification / adjustments + if (details::g_pfnNotifyFailure) + { + details::g_pfnNotifyFailure(failure); + } + + // 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 && WI_IsFlagClear(failure->flags, FailureFlags::RequestSuppressTelemetry)) + { + 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 && + WI_IsFlagClear(failure->flags, FailureFlags::RequestSuppressTelemetry); + + // 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 ((WI_IsFlagSet(failure->flags, FailureFlags::RequestDebugBreak) || 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 == nullptr) // 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, FailureFlags flags) + { + 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), + flags, + &failure); + + if (WI_IsFlagSet(failure.flags, FailureFlags::RequestFailFast)) + { + WilFailFast(failure); + } + } + + template + inline __declspec(noinline) void ReportFailure_Base( + __R_FN_PARAMS_FULL, const ResultStatus& resultPair, PCWSTR message, ReportFailureOptions options, FailureFlags flags) + { + ReportFailure_Return(__R_FN_CALL_FULL, resultPair, message, options, flags); + } + + 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), + FailureFlags::None, + &failure); + __WI_SUPPRESS_4127_S + if ((T == FailureType::FailFast) || WI_IsFlagSet(failure.flags, FailureFlags::RequestFailFast)) + { + 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, FailureFlags) + { + 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, FailureFlags) + { + 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, FailureFlags flags) + { + ReportFailure_Base( + __R_FN_CALL_FULL, ResultStatus::FromResult(hr), nullptr /*message*/, ReportFailureOptions::None /*options*/, flags); + } + + template <> + __declspec(noinline) inline RESULT_NORETURN + void ReportFailure_Hr(__R_FN_PARAMS_FULL, HRESULT hr, FailureFlags) + { + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); + } + + template <> + __declspec(noinline) inline RESULT_NORETURN + void ReportFailure_Hr(__R_FN_PARAMS_FULL, HRESULT hr, FailureFlags) + { + 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)); + ::SetLastError(err); + 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]{}; + 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]{}; + 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]{}; + 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); + ::SetLastError(err); + 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); + } + + // clang-format off +#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 + // clang-format on + + 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), + FailureFlags::None, + &failure); + + if (WI_IsFlagSet(failure.flags, FailureFlags::RequestFailFast)) + { + WilFailFast(failure); + } + + // push the failure info context into the custom exception class + SetFailureInfo(failure, exception); + + throw exception; + } + + template + __declspec(noinline) RESULT_NORETURN inline void ReportFailure_CustomException(__R_FN_PARAMS _In_ T exception) + { + __R_FN_LOCALS_RA; + ReportFailure_CustomExceptionHelper(exception, __R_FN_CALL_FULL); + } + + template + __declspec(noinline) RESULT_NORETURN + 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); + } + + __R_DIRECT_METHOD(void, Return_HrSuppressTelemetry)(__R_DIRECT_FN_PARAMS HRESULT hr) WI_NOEXCEPT + { + __R_FN_LOCALS; + const FailureFlags flags = FailureFlags::RequestSuppressTelemetry; + wil::details::ReportFailure_Hr(__R_DIRECT_FN_CALL hr, flags); + } + + _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_Win32If)(__R_CONDITIONAL_FN_PARAMS DWORD err, bool condition) + { + if (condition) + { + __R_CALL_INTERNAL_METHOD(_Throw_Win32)(__R_CONDITIONAL_FN_CALL err); + } + return condition; + } + + _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_Win32IfMsg) + (__R_CONDITIONAL_FN_PARAMS DWORD err, bool condition, _Printf_format_string_ PCSTR formatString, ...) + { + if (condition) + { + va_list argList; + va_start(argList, formatString); + __R_CALL_INTERNAL_NOINLINE_METHOD(_Throw_Win32Msg)(__R_CONDITIONAL_NOINLINE_FN_CALL err, formatString, argList); + } + return condition; + } + + _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 + + } // namespace __R_NS_NAME +} // namespace details +/// @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 +{ + using result = HRESULT; + + __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/libs/wil/wil/result_originate.h b/libs/wil/wil/result_originate.h new file mode 100644 index 00000000..5eaa7e7a --- /dev/null +++ b/libs/wil/wil/result_originate.h @@ -0,0 +1,131 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! WIL Error Handling Helpers: supporting file enabling the originating of errors to produce better crash dumps + +// 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 +#include "resource.h" +#include "com.h" +#include + +namespace wil +{ +/// @cond +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(), "RoOriginateErrorW")); + if (pfn != nullptr) + { + pfn(failure.hr, 0, failure.pszMessage); + } + } +#else // DESKTOP | SYSTEM + ::RoOriginateErrorW(failure.hr, 0, failure.pszMessage); +#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 +/// @endcond +} // namespace wil + +// Automatically call RoOriginateError upon error origination by including this file +WI_HEADER_INITIALIZATION_FUNCTION(ResultStowedExceptionInitialize, [] { + ::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions); + ::wil::SetFailfastWithContextCallback(::wil::details::FailfastWithContextCallback); + return 1; +}) + +#endif // __WIL_RESULT_ORIGINATE_INCLUDED diff --git a/libs/wil/wil/rpc_helpers.h b/libs/wil/wil/rpc_helpers.h new file mode 100644 index 00000000..eb1741d9 --- /dev/null +++ b/libs/wil/wil/rpc_helpers.h @@ -0,0 +1,219 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! Helpers for invoking RPC functions and translating structured exceptions to HRESULTs or C++ exceptions +#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); + } +} // namespace details +/// @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 +} + +/// @cond +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)); + } + }; +} // namespace details +/// @endcond + +/** 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. +@code +// 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(); +@endcode +*/ +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: +@code +wil::unique_midl_ptr state; +wil::invoke_rpc(GetKittenState, binding.get(), L"fluffy", state.put()); +// use 'state' +@endcode +*/ +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: +@code +GUID id = wil::invoke_rpc_result(GetKittenId, binding.get()); +// use 'id' +@endcode +*/ +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 +} // namespace wil + +#endif diff --git a/libs/wil/wil/safecast.h b/libs/wil/wil/safecast.h new file mode 100644 index 00000000..62af9c3e --- /dev/null +++ b/libs/wil/wil/safecast.h @@ -0,0 +1,379 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! Type independent wrappers around the various intsafe.h functions +#ifndef __WIL_SAFECAST_INCLUDED +#define __WIL_SAFECAST_INCLUDED + +#include "result_macros.h" +#include +#include "wistd_config.h" +#include "wistd_type_traits.h" + +namespace wil +{ +/// @cond +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 potentially 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 <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, char> = LongLongToChar; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, int> = LongLongToInt; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, long> = LongLongToLong; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, short> = LongLongToShort; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, signed char> = LongLongToInt8; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned __int64> = LongLongToULongLong; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned char> = LongLongToUChar; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned int> = LongLongToUInt; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned long> = LongLongToULong; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned short> = LongLongToUShort; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = IntToChar; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = IntToShort; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = IntToInt8; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = IntToULongLong; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = IntToUChar; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = IntToUInt; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = IntToULong; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = IntToUShort; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = LongToChar; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = LongToInt; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = LongToShort; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = LongToInt8; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = LongToULongLong; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = LongToUChar; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = LongToUInt; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = LongToULong; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = LongToUShort; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ShortToChar; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ShortToInt8; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ShortToULongLong; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ShortToUChar; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ShortToUInt; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ShortToULong; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ShortToUShort; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = Int8ToULongLong; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = Int8ToUChar; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = Int8ToUInt; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = Int8ToULong; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = Int8ToUShort; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ULongLongToLongLong; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ULongLongToChar; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ULongLongToInt; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ULongLongToLong; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ULongLongToShort; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ULongLongToInt8; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ULongLongToUChar; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ULongLongToUInt; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ULongLongToULong; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ULongLongToUShort; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = UInt8ToChar; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = UIntToInt8; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = UIntToChar; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = UIntToInt; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = UIntToLong; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = UIntToShort; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = UIntToInt8; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = UIntToUChar; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = UIntToUShort; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ULongToChar; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ULongToInt; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ULongToLong; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ULongToShort; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ULongToInt8; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ULongToUChar; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ULongToUInt; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = ULongToUShort; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = UShortToChar; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = UShortToShort; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = UShortToInt8; + template <> + __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion = UShortToUChar; +} // namespace details +/// @endcond + +// Unsafe conversion where failure results in fail fast. +template , 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 , 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 , 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 , 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 , 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 , 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 , 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 , 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 , 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 , 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 , 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 , 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 , 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 , 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; +} +} // namespace wil + +#endif // __WIL_SAFECAST_INCLUDED diff --git a/libs/wil/wil/stl.h b/libs/wil/wil/stl.h new file mode 100644 index 00000000..5baff8f6 --- /dev/null +++ b/libs/wil/wil/stl.h @@ -0,0 +1,233 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! Windows STL helpers: custom allocators for STL containers +#ifndef __WIL_STL_INCLUDED +#define __WIL_STL_INCLUDED + +#include "common.h" +#include "resource.h" +#include +#include +#include +#include +#if _HAS_CXX17 +#include +#endif + +/// @cond +#ifndef WI_STL_FAIL_FAST_IF +#define WI_STL_FAIL_FAST_IF FAIL_FAST_IF +#endif +/// @endcond + +#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 + { + using other = secure_allocator; + }; + + 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; + }; +} // namespace details +/// @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(); +} + +#if _HAS_CXX17 +/** + zstring_view. A zstring_view is identical to a std::string_view except it is always nul-terminated (unless empty). + * zstring_view can be used for storing string literals without "forgetting" the length or that it is nul-terminated. + * A zstring_view can be converted implicitly to a std::string_view because it is always safe to use a nul-terminated + string_view as a plain string view. + * A zstring_view can be constructed from a std::string because the data in std::string is nul-terminated. +*/ +template +class basic_zstring_view : public std::basic_string_view +{ + using size_type = typename std::basic_string_view::size_type; + +public: + constexpr basic_zstring_view() noexcept = default; + constexpr basic_zstring_view(const basic_zstring_view&) noexcept = default; + constexpr basic_zstring_view& operator=(const basic_zstring_view&) noexcept = default; + + constexpr basic_zstring_view(const TChar* pStringData, size_type stringLength) noexcept : + std::basic_string_view(pStringData, stringLength) + { + if (pStringData[stringLength] != 0) + { + WI_STL_FAIL_FAST_IF(true); + } + } + + template + constexpr basic_zstring_view(const TChar (&stringArray)[stringArrayLength]) noexcept : + std::basic_string_view(&stringArray[0], length_n(&stringArray[0], stringArrayLength)) + { + } + + // Construct from nul-terminated char ptr. To prevent this from overshadowing array construction, + // we disable this constructor if the value is an array (including string literal). + template ::value && !std::is_array::value>* = nullptr> + constexpr basic_zstring_view(TPtr&& pStr) noexcept : std::basic_string_view(std::forward(pStr)) + { + } + + constexpr basic_zstring_view(const std::basic_string& str) noexcept : + std::basic_string_view(&str[0], str.size()) + { + } + + // basic_string_view [] precondition won't let us read view[view.size()]; so we define our own. + WI_NODISCARD constexpr const TChar& operator[](size_type idx) const noexcept + { + WI_ASSERT(idx <= this->size() && this->data() != nullptr); + return this->data()[idx]; + } + + WI_NODISCARD constexpr const TChar* c_str() const noexcept + { + WI_ASSERT(this->data() == nullptr || this->data()[this->size()] == 0); + return this->data(); + } + +private: + // Bounds-checked version of char_traits::length, like strnlen. Requires that the input contains a null terminator. + static constexpr size_type length_n(_In_reads_opt_(buf_size) const TChar* str, size_type buf_size) noexcept + { + const std::basic_string_view view(str, buf_size); + auto pos = view.find_first_of(TChar()); + if (pos == view.npos) + { + WI_STL_FAIL_FAST_IF(true); + } + return pos; + } + + // The following basic_string_view methods must not be allowed because they break the nul-termination. + using std::basic_string_view::swap; + using std::basic_string_view::remove_suffix; +}; + +using zstring_view = basic_zstring_view; +using zwstring_view = basic_zstring_view; + +inline namespace literals +{ + constexpr zstring_view operator"" _zv(const char* str, std::size_t len) noexcept + { + return zstring_view(str, len); + } + + constexpr zwstring_view operator"" _zv(const wchar_t* str, std::size_t len) noexcept + { + return zwstring_view(str, len); + } +} // namespace literals + +#endif // _HAS_CXX17 + +} // namespace wil + +#endif // WIL_ENABLE_EXCEPTIONS + +#endif // __WIL_STL_INCLUDED diff --git a/libs/wil/wil/token_helpers.h b/libs/wil/wil/token_helpers.h new file mode 100644 index 00000000..468dc3a8 --- /dev/null +++ b/libs/wil/wil/token_helpers.h @@ -0,0 +1,712 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! Helpers for using tokens and impersonation +#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() +/// @cond +#ifndef SECURITY_WIN32 +#define SECURITY_WIN32 +#endif +/// @endcond +#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 constexpr TOKEN_INFORMATION_CLASS infoClass = TokenAccessInformation; + static constexpr bool FixedSize = false; + }; + template <> + struct MapTokenStructToInfoClass + { + static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenAppContainerSid; + static constexpr bool FixedSize = false; + }; + template <> + struct MapTokenStructToInfoClass + { + static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenDefaultDacl; + static constexpr bool FixedSize = false; + }; + template <> + struct MapTokenStructToInfoClass + { + static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenGroupsAndPrivileges; + static constexpr bool FixedSize = false; + }; + template <> + struct MapTokenStructToInfoClass + { + static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenIntegrityLevel; + static constexpr bool FixedSize = false; + }; + template <> + struct MapTokenStructToInfoClass + { + static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenOwner; + static constexpr bool FixedSize = false; + }; + template <> + struct MapTokenStructToInfoClass + { + static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenPrimaryGroup; + static constexpr bool FixedSize = false; + }; + template <> + struct MapTokenStructToInfoClass + { + static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenPrivileges; + static constexpr bool FixedSize = false; + }; + template <> + struct MapTokenStructToInfoClass + { + static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenUser; + static constexpr bool FixedSize = false; + }; + + // fixed size cases + template <> + struct MapTokenStructToInfoClass + { + static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenElevationType; + static constexpr bool FixedSize = true; + }; + template <> + struct MapTokenStructToInfoClass + { + static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenMandatoryPolicy; + static constexpr bool FixedSize = true; + }; + template <> + struct MapTokenStructToInfoClass + { + static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenOrigin; + static constexpr bool FixedSize = true; + }; + template <> + struct MapTokenStructToInfoClass + { + static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenSource; + static constexpr bool FixedSize = true; + }; + template <> + struct MapTokenStructToInfoClass + { + static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenStatistics; + static constexpr bool FixedSize = true; + }; + template <> + struct MapTokenStructToInfoClass + { + static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenType; + static constexpr bool FixedSize = true; + }; + template <> + struct MapTokenStructToInfoClass + { + static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenImpersonationLevel; + static constexpr bool FixedSize = true; + }; + template <> + struct MapTokenStructToInfoClass + { + static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenElevation; + static constexpr bool FixedSize = true; + }; + + struct token_info_deleter + { + template + void operator()(T* p) const + { + static_assert(wistd::is_trivially_destructible_v, "do not use with nontrivial types"); + ::operator delete(p); + } + }; +} // namespace details +/// @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, OpenThreadTokenAs::Self)); +~~~~ +@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 openAs Current to use current thread security context, or Self to use process security context. +*/ +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 wil::unique_tokeninfo_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. +@code +// Retrieve the TOKEN_USER structure for the current process +wil::unique_tokeninfo_ptr user; +RETURN_IF_FAILED(wil::get_token_information_nothrow(user, GetCurrentProcessToken())); +RETURN_IF_FAILED(ConsumeSid(user->User.Sid)); +@endcode +Not specifying the token handle is the same as specifying 'nullptr' and retrieves information about the effective token. +@code +wil::unique_tokeninfo_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)); +} +@endcode +@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 +using unique_tokeninfo_ptr = wistd::unique_ptr; + +template ::FixedSize>* = nullptr> +inline HRESULT get_token_information_nothrow(unique_tokeninfo_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))); + unique_tokeninfo_ptr tokenInfoClose{static_cast(::operator new(tokenInfoSize, std::nothrow))}; + RETURN_IF_NULL_ALLOC(tokenInfoClose); + RETURN_IF_WIN32_BOOL_FALSE(GetTokenInformation(tokenHandle, infoClass, tokenInfoClose.get(), tokenInfoSize, &tokenInfoSize)); + tokenInfo = wistd::move(tokenInfoClose); + + 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; +} + +/// @cond +namespace details +{ + template ::FixedSize>* = nullptr> + unique_tokeninfo_ptr GetTokenInfoWrap(HANDLE token = nullptr) + { + unique_tokeninfo_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; + } +} // namespace details +/// @endcond + +//! 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. +@code +auto user = wil::get_token_information(GetCurrentProcessToken()); +ConsumeSid(user->User.Sid); +@endcode +Pass 'nullptr' (or omit the parameter) as tokenHandle to retrieve information about the effective token. +@code +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) + { + // ... + } +} +@endcode +@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); + } + } +} // namespace details +/// @endcond + +using unique_token_reverter = + wil::unique_any; + +/** 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. +@param reverter An RAII object that, on success, will revert the impersonation when it goes out of scope. +*/ +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 + +/// @cond +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; + } + }; +} // namespace details +/// @endcond + +/** 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()" +@code +// 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)); +@endcode +*/ +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. +@param value The result of the operation; `true` if the token represents an app container, `false` otherwise. +*/ +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/libs/wil/wil/traceloggingconfig.h b/libs/wil/wil/traceloggingconfig.h new file mode 100644 index 00000000..2a5a5cd0 --- /dev/null +++ b/libs/wil/wil/traceloggingconfig.h @@ -0,0 +1,73 @@ +#pragma once +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! Various definitions for use in conjunction with TraceLogging APIs + +#ifndef __WIL_TRACELOGGING_CONFIG_H +/// @cond +#define __WIL_TRACELOGGING_CONFIG_H +/// @endcond + +// Configuration macro for use in TRACELOGGING_DEFINE_PROVIDER. The definition +// in this file configures the provider as a normal (non-telemetry) provider. +#define TraceLoggingOptionMicrosoftTelemetry() // Empty definition for TraceLoggingOptionMicrosoftTelemetry + +// Configuration macro for use in TRACELOGGING_DEFINE_PROVIDER. The definition +// in this file configures the provider as a normal (non-telemetry) provider. +#define TraceLoggingOptionWindowsCoreTelemetry() // Empty definition for TraceLoggingOptionWindowsCoreTelemetry + +// Event privacy tags. Use the PDT macro values for the tag parameter, e.g.: +// TraceLoggingWrite(..., +// TelemetryPrivacyDataTag(PDT_BrowsingHistory | PDT_ProductAndServiceUsage), +// ...); +#define TelemetryPrivacyDataTag(tag) TraceLoggingUInt64((tag), "PartA_PrivTags") +#define PDT_BrowsingHistory 0x0000000000000002u +#define PDT_DeviceConnectivityAndConfiguration 0x0000000000000800u +#define PDT_InkingTypingAndSpeechUtterance 0x0000000000020000u +#define PDT_ProductAndServicePerformance 0x0000000001000000u +#define PDT_ProductAndServiceUsage 0x0000000002000000u +#define PDT_SoftwareSetupAndInventory 0x0000000080000000u + +// Event categories specified via keywords, e.g.: +// TraceLoggingWrite(..., +// TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES), +// ...); +#define MICROSOFT_KEYWORD_CRITICAL_DATA 0x0000800000000000 // Bit 47 +#define MICROSOFT_KEYWORD_MEASURES 0x0000400000000000 // Bit 46 +#define MICROSOFT_KEYWORD_TELEMETRY 0x0000200000000000 // Bit 45 +#define MICROSOFT_KEYWORD_RESERVED_44 0x0000100000000000 // Bit 44 (reserved for future assignment) + +// Event categories specified via event tags, e.g.: +// TraceLoggingWrite(..., +// TraceLoggingEventTag(MICROSOFT_EVENTTAG_REALTIME_LATENCY), +// ...); +#define MICROSOFT_EVENTTAG_DROP_USER_IDS 0x00008000 +#define MICROSOFT_EVENTTAG_AGGREGATE 0x00010000 +#define MICROSOFT_EVENTTAG_DROP_PII_EXCEPT_IP 0x00020000 +#define MICROSOFT_EVENTTAG_COSTDEFERRED_LATENCY 0x00040000 +#define MICROSOFT_EVENTTAG_CORE_DATA 0x00080000 +#define MICROSOFT_EVENTTAG_INJECT_XTOKEN 0x00100000 +#define MICROSOFT_EVENTTAG_REALTIME_LATENCY 0x00200000 +#define MICROSOFT_EVENTTAG_NORMAL_LATENCY 0x00400000 +#define MICROSOFT_EVENTTAG_CRITICAL_PERSISTENCE 0x00800000 +#define MICROSOFT_EVENTTAG_NORMAL_PERSISTENCE 0x01000000 +#define MICROSOFT_EVENTTAG_DROP_PII 0x02000000 +#define MICROSOFT_EVENTTAG_HASH_PII 0x04000000 +#define MICROSOFT_EVENTTAG_MARK_PII 0x08000000 + +// Field categories specified via field tags, e.g.: +// TraceLoggingWrite(..., +// TraceLoggingString(szUser, "UserName", "User's name", MICROSOFT_FIELDTAG_HASH_PII), +// ...); +#define MICROSOFT_FIELDTAG_DROP_PII 0x04000000 +#define MICROSOFT_FIELDTAG_HASH_PII 0x08000000 +#endif // __WIL_TRACELOGGING_CONFIG_H \ No newline at end of file diff --git a/libs/wil/wil/win32_helpers.h b/libs/wil/wil/win32_helpers.h new file mode 100644 index 00000000..dee2e2f0 --- /dev/null +++ b/libs/wil/wil/win32_helpers.h @@ -0,0 +1,913 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! Various types and helpers for interfacing with various Win32 APIs +#ifndef __WIL_WIN32_HELPERS_INCLUDED +#define __WIL_WIN32_HELPERS_INCLUDED + +#include // FILETIME, HINSTANCE +#include // GetSystemTimeAsFileTime +#include // GetProcAddress +#include // GetModuleFileNameExW (macro), K32GetModuleFileNameExW +#include +#include + +// detect std::bit_cast +#ifdef __has_include +#if (__cplusplus >= 202002L || _MSVC_LANG >= 202002L) && __has_include() +#include +#endif +#endif + +/// @cond +#if __cpp_lib_bit_cast >= 201806L +#define __WI_CONSTEXPR_BIT_CAST constexpr +#else +#define __WI_CONSTEXPR_BIT_CAST inline +#endif +/// @endcond + +#include "result.h" +#include "resource.h" +#include "wistd_functional.h" +#include "wistd_type_traits.h" + +/// @cond +#if _HAS_CXX20 && defined(_STRING_VIEW_) && defined(_COMPARE_) +// If we're using c++20, then must be included to use the string ordinal functions +#define __WI_DEFINE_STRING_ORDINAL_FUNCTIONS +#elif !_HAS_CXX20 && defined(_STRING_VIEW_) +#define __WI_DEFINE_STRING_ORDINAL_FUNCTIONS +#endif +/// @endcond + +/// @cond +namespace wistd +{ +#if defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS) + +#if _HAS_CXX20 + +using weak_ordering = std::weak_ordering; + +#else // _HAS_CXX20 + +struct weak_ordering +{ + static const weak_ordering less; + static const weak_ordering equivalent; + static const weak_ordering greater; + + [[nodiscard]] friend constexpr bool operator==(const weak_ordering left, std::nullptr_t) noexcept + { + return left.m_value == 0; + } + + [[nodiscard]] friend constexpr bool operator!=(const weak_ordering left, std::nullptr_t) noexcept + { + return left.m_value != 0; + } + + [[nodiscard]] friend constexpr bool operator<(const weak_ordering left, std::nullptr_t) noexcept + { + return left.m_value < 0; + } + + [[nodiscard]] friend constexpr bool operator>(const weak_ordering left, std::nullptr_t) noexcept + { + return left.m_value > 0; + } + + [[nodiscard]] friend constexpr bool operator<=(const weak_ordering left, std::nullptr_t) noexcept + { + return left.m_value <= 0; + } + + [[nodiscard]] friend constexpr bool operator>=(const weak_ordering left, std::nullptr_t) noexcept + { + return left.m_value >= 0; + } + + [[nodiscard]] friend constexpr bool operator==(std::nullptr_t, const weak_ordering right) noexcept + { + return right == 0; + } + + [[nodiscard]] friend constexpr bool operator!=(std::nullptr_t, const weak_ordering right) noexcept + { + return right != 0; + } + + [[nodiscard]] friend constexpr bool operator<(std::nullptr_t, const weak_ordering right) noexcept + { + return right > 0; + } + + [[nodiscard]] friend constexpr bool operator>(std::nullptr_t, const weak_ordering right) noexcept + { + return right < 0; + } + + [[nodiscard]] friend constexpr bool operator<=(std::nullptr_t, const weak_ordering right) noexcept + { + return right >= 0; + } + + [[nodiscard]] friend constexpr bool operator>=(std::nullptr_t, const weak_ordering right) noexcept + { + return right <= 0; + } + + signed char m_value; +}; + +inline constexpr weak_ordering weak_ordering::less{static_cast(-1)}; +inline constexpr weak_ordering weak_ordering::equivalent{static_cast(0)}; +inline constexpr weak_ordering weak_ordering::greater{static_cast(1)}; + +#endif // !_HAS_CXX20 + +#endif // defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS) +} // namespace wistd +/// @endcond + +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. +constexpr size_t max_path_segment_length = 255; + +//! Character length not including the null, MAX_PATH (260) includes the null. +constexpr size_t 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. +constexpr size_t max_extended_path_length = 0x7FFF - 24; + +//! For {guid} string form. Includes space for the null terminator. +constexpr size_t guid_string_buffer_length = 39; + +//! For {guid} string form. Not including the null terminator. +constexpr size_t guid_string_length = 38; + +#pragma region String and identifier comparisons +// Using CompareStringOrdinal functions: +// +// Indentifiers require a locale-less (ordinal), and often case-insensitive, comparison (filenames, registry keys, XML node names, +// etc). DO NOT use locale-sensitive (lexical) comparisons for resource identifiers (e.g.wcs*() functions in the CRT). + +#if defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS) || defined(WIL_DOXYGEN) + +/// @cond +namespace details +{ + [[nodiscard]] inline int CompareStringOrdinal(std::wstring_view left, std::wstring_view right, bool caseInsensitive) WI_NOEXCEPT + { + // Casting from size_t (unsigned) to int (signed) should be safe from overrun to a negative, + // merely truncating the string. CompareStringOrdinal should be resilient to negatives. + return ::CompareStringOrdinal( + left.data(), static_cast(left.size()), right.data(), static_cast(right.size()), caseInsensitive); + } +} // namespace details +/// @endcond + +[[nodiscard]] inline wistd::weak_ordering compare_string_ordinal(std::wstring_view left, std::wstring_view right, bool caseInsensitive) WI_NOEXCEPT +{ + switch (wil::details::CompareStringOrdinal(left, right, caseInsensitive)) + { + case CSTR_LESS_THAN: + return wistd::weak_ordering::less; + case CSTR_GREATER_THAN: + return wistd::weak_ordering::greater; + default: + return wistd::weak_ordering::equivalent; + } +} + +#endif // defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS) + +#pragma endregion + +#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_duration + +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_) && (_WIN32_WINNT >= _WIN32_WINNT_WIN7)) || defined(WIL_DOXYGEN) + /// 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_ +} // namespace filetime +#pragma endregion + +#pragma region RECT helpers +template +constexpr auto rect_width(rect_type const& rect) +{ + return rect.right - rect.left; +} + +template +constexpr auto rect_height(rect_type const& rect) +{ + return rect.bottom - rect.top; +} + +template +constexpr auto rect_is_empty(rect_type const& rect) +{ + return (rect.left >= rect.right) || (rect.top >= rect.bottom); +} + +template +constexpr auto rect_contains_point(rect_type const& rect, point_type const& point) +{ + return (point.x >= rect.left) && (point.x < rect.right) && (point.y >= rect.top) && (point.y < rect.bottom); +} + +template +constexpr rect_type rect_from_size(length_type x, length_type y, length_type width, length_type height) +{ + rect_type rect; + rect.left = x; + rect.top = y; + rect.right = x + width; + rect.bottom = y + height; + return rect; +} +#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]{}; + 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 ? (static_cast(lengthToUse) + 1) : (static_cast(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 = static_cast(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 = static_cast(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; + }); +} + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES) +template +HRESULT GetWindowsDirectoryW(string_type& result) WI_NOEXCEPT +{ + return wil::AdaptFixedSizeToAllocatedResult( + result, [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT { + *valueLengthNeededWithNul = ::GetWindowsDirectoryW(value, static_cast(valueLength)); + RETURN_LAST_ERROR_IF(*valueLengthNeededWithNul == 0); + if (*valueLengthNeededWithNul < valueLength) + { + (*valueLengthNeededWithNul)++; // it fit, account for the null + } + return S_OK; + }); +} +#endif + +#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 = nullptr /* current process 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; +} + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES) +template +string_type GetWindowsDirectoryW() +{ + string_type result; + THROW_IF_FAILED((wil::GetWindowsDirectoryW(result))); + return result; +} +#endif + +template +string_type GetSystemDirectoryW() +{ + string_type result; + THROW_IF_FAILED((wil::GetSystemDirectoryW(result))); + return result; +} + +template +string_type QueryFullProcessImageNameW(HANDLE processHandle = GetCurrentProcess(), DWORD flags = 0) +{ + string_type result{}; + THROW_IF_FAILED((wil::QueryFullProcessImageNameW(processHandle, flags, result))); + return result; +} + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + +// Lookup a DWORD value under HKLM\...\Image File Execution Options\ +inline DWORD GetCurrentProcessExecutionOption(PCWSTR valueName, DWORD defaultValue = 0) +{ + auto filePath = wil::GetModuleFileNameW(); + if (auto lastSlash = wcsrchr(filePath.get(), L'\\')) + { + const auto fileName = lastSlash + 1; + auto keyPath = wil::str_concat( + LR"(SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\)", fileName); + DWORD value{}, sizeofValue = sizeof(value); + if (::RegGetValueW( + HKEY_LOCAL_MACHINE, + keyPath.get(), + valueName, +#ifdef RRF_SUBKEY_WOW6464KEY + RRF_RT_REG_DWORD | RRF_SUBKEY_WOW6464KEY, +#else + RRF_RT_REG_DWORD, +#endif + nullptr, + &value, + &sizeofValue) == ERROR_SUCCESS) + { + return value; + } + } + return defaultValue; +} + +// Waits for a debugger to attach to the current process based on registry configuration. +// +// Example: +// HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\explorer.exe +// WaitForDebuggerPresent=1 +// +// REG_DWORD value of +// missing or 0 -> don't break +// 1 -> wait for the debugger, continue execution once it is attached +// 2 -> wait for the debugger, break here once attached. +inline void WaitForDebuggerPresent(bool checkRegistryConfig = true) +{ + for (;;) + { + auto configValue = checkRegistryConfig ? GetCurrentProcessExecutionOption(L"WaitForDebuggerPresent") : 1; + if (configValue == 0) + { + return; // not configured, don't wait + } + + if (IsDebuggerPresent()) + { + if (configValue == 2) + { + DebugBreak(); // debugger attached, SHIFT+F11 to return to the caller + } + return; // debugger now attached, continue executing + } + Sleep(500); + } +} +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) + +#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); +} + +// GetModuleHandleExW was added to the app partition in version 22000 of the SDK +#if defined(NTDDI_WIN10_CO) ? WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES) \ + : WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES) +// Use this in threads that can outlive the object or API call that created them. +// Without this COM, or the API caller, can unload the DLL, resulting in a crash. +// It is very important that this be the first object created in the thread proc +// as when this runs down the thread exits and no destructors of objects created before +// it will run. +[[nodiscard]] inline auto get_module_reference_for_thread() noexcept +{ + HMODULE thisModule{}; + FAIL_FAST_IF(!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, L"", &thisModule)); + return wil::scope_exit([thisModule] { + FreeLibraryAndExitThread(thisModule, 0); + }); +} +#endif + +/// @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); + } + }; +} // namespace details +/// @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 +} // namespace wil + +// 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/libs/wil/wil/win32_result_macros.h b/libs/wil/wil/win32_result_macros.h new file mode 100644 index 00000000..32e60a3e --- /dev/null +++ b/libs/wil/wil/win32_result_macros.h @@ -0,0 +1,205 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! WIL Error Handling Helpers: supporting file defining a family of macros and functions designed to uniformly handle errors +//! across return codes, fail fast, exceptions and logging for Win32 error codes. +#ifndef __WIL_WIN32_RESULTMACROS_INCLUDED +#define __WIL_WIN32_RESULTMACROS_INCLUDED + +#include "result_macros.h" + +// Helpers for return macros +/// @cond +#define __WIN32_RETURN_WIN32(error, str) \ + __WI_SUPPRESS_4127_S do \ + { \ + const auto __error = (error); \ + if (FAILED_WIN32(__error)) \ + { \ + __R_FN(Return_Win32)(__R_INFO(str) __error); \ + } \ + return __error; \ + } \ + __WI_SUPPRESS_4127_E while ((void)0, 0) +#define __WIN32_RETURN_GLE_FAIL(str) return __R_FN(Win32_Return_GetLastError)(__R_INFO_ONLY(str)) + +FORCEINLINE long __WIN32_FROM_HRESULT(HRESULT hr) +{ + if (SUCCEEDED(hr)) + { + return ERROR_SUCCESS; + } + return HRESULT_FACILITY(hr) == FACILITY_WIN32 ? HRESULT_CODE(hr) : hr; +} +/// @endcond + +//***************************************************************************** +// Macros for returning failures as WIN32 error codes +//***************************************************************************** + +// Always returns a known result (WIN32 error code) - always logs failures +#define WIN32_RETURN_WIN32(error) __WIN32_RETURN_WIN32(wil::verify_win32(error), #error) +#define WIN32_RETURN_LAST_ERROR() __WIN32_RETURN_GLE_FAIL(nullptr) + +// Conditionally returns failures (WIN32 error code) - always logs failures +#define WIN32_RETURN_IF_WIN32_ERROR(error) \ + __WI_SUPPRESS_4127_S do \ + { \ + const auto __errorRet = wil::verify_win32(error); \ + if (FAILED_WIN32(__errorRet)) \ + { \ + __WIN32_RETURN_WIN32(__errorRet, #error); \ + } \ + } \ + __WI_SUPPRESS_4127_E while ((void)0, 0) +#define WIN32_RETURN_WIN32_IF(error, condition) \ + __WI_SUPPRESS_4127_S do \ + { \ + if (wil::verify_bool(condition)) \ + { \ + __WIN32_RETURN_WIN32(wil::verify_win32(error), #condition); \ + } \ + } \ + __WI_SUPPRESS_4127_E while ((void)0, 0) +#define WIN32_RETURN_WIN32_IF_NULL(error, ptr) \ + __WI_SUPPRESS_4127_S do \ + { \ + if ((ptr) == nullptr) \ + { \ + __WIN32_RETURN_WIN32(wil::verify_win32(error), #ptr); \ + } \ + } \ + __WI_SUPPRESS_4127_E while ((void)0, 0) +#define WIN32_RETURN_LAST_ERROR_IF(condition) \ + __WI_SUPPRESS_4127_S do \ + { \ + if (wil::verify_bool(condition)) \ + { \ + __WIN32_RETURN_GLE_FAIL(#condition); \ + } \ + } \ + __WI_SUPPRESS_4127_E while ((void)0, 0) +#define WIN32_RETURN_LAST_ERROR_IF_NULL(ptr) \ + __WI_SUPPRESS_4127_S do \ + { \ + if ((ptr) == nullptr) \ + { \ + __WIN32_RETURN_GLE_FAIL(#ptr); \ + } \ + } \ + __WI_SUPPRESS_4127_E while ((void)0, 0) + +// Conditionally returns failures (WIN32 error code) - use for failures that are expected in common use - failures are not logged - macros are only for control flow pattern +#define WIN32_RETURN_IF_WIN32_ERROR_EXPECTED(error) \ + __WI_SUPPRESS_4127_S do \ + { \ + const auto __errorRet = wil::verify_win32(error); \ + if (FAILED_WIN32(__errorRet)) \ + { \ + return __errorRet; \ + } \ + } \ + __WI_SUPPRESS_4127_E while ((void)0, 0) +#define WIN32_RETURN_WIN32_IF_EXPECTED(error, condition) \ + __WI_SUPPRESS_4127_S do \ + { \ + if (wil::verify_bool(condition)) \ + { \ + return wil::verify_win32(error); \ + } \ + } \ + __WI_SUPPRESS_4127_E while ((void)0, 0) +#define WIN32_RETURN_WIN32_IF_NULL_EXPECTED(error, ptr) \ + __WI_SUPPRESS_4127_S do \ + { \ + if ((ptr) == nullptr) \ + { \ + return wil::verify_win32(error); \ + } \ + } \ + __WI_SUPPRESS_4127_E while ((void)0, 0) +#define WIN32_RETURN_LAST_ERROR_IF_EXPECTED(condition) \ + __WI_SUPPRESS_4127_S do \ + { \ + if (wil::verify_bool(condition)) \ + { \ + return wil::verify_win32(wil::details::GetLastErrorFail()); \ + } \ + } \ + __WI_SUPPRESS_4127_E while ((void)0, 0) +#define WIN32_RETURN_LAST_ERROR_IF_NULL_EXPECTED(ptr) \ + __WI_SUPPRESS_4127_S do \ + { \ + if ((ptr) == nullptr) \ + { \ + return wil::verify_win32(wil::details::GetLastErrorFail()); \ + } \ + } \ + __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 WIN32_RETURN_CAUGHT_EXCEPTION() return __R_FN(Win32_Return_CaughtException)(__R_INFO_ONLY(nullptr)) + +// Use these macros in place of a catch block to handle exceptions +#define WIN32_CATCH_RETURN() \ + catch (...) \ + { \ + WIN32_RETURN_CAUGHT_EXCEPTION(); \ + } + +namespace wil +{ +//***************************************************************************** +// Public Helpers that catch -- mostly only enabled when exceptions are enabled +//***************************************************************************** + +// Win32ErrorFromCaughtException 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 a WIN32 error code. If an exception is of an unrecognized type +// the function will fail fast. +// +// try +// { +// // Code +// } +// catch (...) +// { +// status = wil::Win32ErrorFromCaughtException(); +// } +_Always_(_Post_satisfies_(return > 0)) __declspec(noinline) inline long Win32ErrorFromCaughtException() WI_NOEXCEPT +{ + return __WIN32_FROM_HRESULT(ResultFromCaughtException()); +} + +/// @cond +namespace details::__R_NS_NAME +{ +#ifdef WIL_ENABLE_EXCEPTIONS + __R_DIRECT_METHOD(long, Win32_Return_CaughtException)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __R_FN_LOCALS; + return __WIN32_FROM_HRESULT(wil::details::ReportFailure_CaughtException(__R_DIRECT_FN_CALL_ONLY)); + } +#endif + + __R_DIRECT_METHOD(long, Win32_Return_GetLastError)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT + { + __R_FN_LOCALS; + return __WIN32_FROM_HRESULT(wil::details::ReportFailure_GetLastErrorHr(__R_DIRECT_FN_CALL_ONLY)); + } +} // namespace details::__R_NS_NAME +/// @endcond +} // namespace wil + +#endif // __WIL_WIN32_RESULTMACROS_INCLUDED diff --git a/libs/wil/wil/windowing.h b/libs/wil/wil/windowing.h new file mode 100644 index 00000000..ca9da1fa --- /dev/null +++ b/libs/wil/wil/windowing.h @@ -0,0 +1,173 @@ +//********************************************************* +// +// 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_WINDOWING_INCLUDED +#define __WIL_WINDOWING_INCLUDED + +#include +#include + +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +namespace wil +{ +namespace details +{ + template + struct always_false : wistd::false_type + { + }; + + template + BOOL __stdcall EnumWindowsCallbackNoThrow(HWND hwnd, LPARAM lParam) + { + auto pCallback = reinterpret_cast(lParam); +#ifdef __cpp_if_constexpr + using result_t = decltype((*pCallback)(hwnd)); + if constexpr (wistd::is_void_v) + { + (*pCallback)(hwnd); + return TRUE; + } + else if constexpr (wistd::is_same_v) + { + // NB: this works for both HRESULT and NTSTATUS as both S_OK and ERROR_SUCCESS are 0 + return (S_OK == (*pCallback)(hwnd)) ? TRUE : FALSE; + } + else if constexpr (std::is_same_v) + { + return (*pCallback)(hwnd) ? TRUE : FALSE; + } + else + { + static_assert(details::always_false::value, "Callback must return void, bool, or HRESULT"); + } +#else + return (*pCallback)(hwnd); +#endif + } + + template + void DoEnumWindowsNoThrow(TEnumApi&& enumApi, TCallback&& callback) noexcept + { + enumApi(EnumWindowsCallbackNoThrow, reinterpret_cast(&callback)); + } + +#ifdef WIL_ENABLE_EXCEPTIONS + template + struct EnumWindowsCallbackData + { + std::exception_ptr exception; + TCallback* pCallback; + }; + + template + BOOL __stdcall EnumWindowsCallback(HWND hwnd, LPARAM lParam) + { + auto pCallbackData = reinterpret_cast*>(lParam); + try + { + auto pCallback = pCallbackData->pCallback; +#ifdef __cpp_if_constexpr + using result_t = decltype((*pCallback)(hwnd)); + if constexpr (std::is_void_v) + { + (*pCallback)(hwnd); + return TRUE; + } + else if constexpr (std::is_same_v) + { + // NB: this works for both HRESULT and NTSTATUS as both S_OK and ERROR_SUCCESS are 0 + return (S_OK == (*pCallback)(hwnd)) ? TRUE : FALSE; + } + else if constexpr (std::is_same_v) + { + return (*pCallback)(hwnd) ? TRUE : FALSE; + } + else + { + static_assert(details::always_false::value, "Callback must return void, bool, or HRESULT"); + } +#else + return (*pCallback)(hwnd); +#endif + } + catch (...) + { + pCallbackData->exception = std::current_exception(); + return FALSE; + } + }; + + template + void DoEnumWindows(TEnumApi&& enumApi, TCallback&& callback) + { + EnumWindowsCallbackData callbackData = {nullptr, &callback}; + enumApi(EnumWindowsCallback, reinterpret_cast(&callbackData)); + if (callbackData.exception) + { + std::rethrow_exception(callbackData.exception); + } + } +#endif +} // namespace details + +template +void for_each_window_nothrow(TCallback&& callback) noexcept +{ + details::DoEnumWindowsNoThrow(&EnumWindows, wistd::forward(callback)); +} + +template +void for_each_thread_window_nothrow(_In_ DWORD threadId, TCallback&& callback) noexcept +{ + auto boundEnumThreadWindows = [threadId](WNDENUMPROC enumproc, LPARAM lParam) noexcept -> BOOL { + return EnumThreadWindows(threadId, enumproc, lParam); + }; + details::DoEnumWindowsNoThrow(boundEnumThreadWindows, wistd::forward(callback)); +} + +template +void for_each_child_window_nothrow(_In_ HWND hwndParent, TCallback&& callback) noexcept +{ + auto boundEnumChildWindows = [hwndParent](WNDENUMPROC enumproc, LPARAM lParam) noexcept -> BOOL { + return EnumChildWindows(hwndParent, enumproc, lParam); + }; + details::DoEnumWindowsNoThrow(boundEnumChildWindows, wistd::forward(callback)); +} + +#ifdef WIL_ENABLE_EXCEPTIONS +template +void for_each_window(TCallback&& callback) +{ + details::DoEnumWindows(&EnumWindows, wistd::forward(callback)); +} + +template +void for_each_thread_window(_In_ DWORD threadId, TCallback&& callback) +{ + auto boundEnumThreadWindows = [threadId](WNDENUMPROC enumproc, LPARAM lParam) noexcept -> BOOL { + return EnumThreadWindows(threadId, enumproc, lParam); + }; + details::DoEnumWindows(boundEnumThreadWindows, wistd::forward(callback)); +} + +template +void for_each_child_window(_In_ HWND hwndParent, TCallback&& callback) +{ + auto boundEnumChildWindows = [hwndParent](WNDENUMPROC enumproc, LPARAM lParam) noexcept -> BOOL { + return EnumChildWindows(hwndParent, enumproc, lParam); + }; + details::DoEnumWindows(boundEnumChildWindows, wistd::forward(callback)); +} +#endif + +} // namespace wil +#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#endif // __WIL_WINDOWING_INCLUDED \ No newline at end of file diff --git a/libs/wil/wil/winrt.h b/libs/wil/wil/winrt.h new file mode 100644 index 00000000..ae5e59c4 --- /dev/null +++ b/libs/wil/wil/winrt.h @@ -0,0 +1,2520 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! Various types and functions for working with the Windows Runtime +#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 +#if defined(WIL_ENABLE_EXCEPTIONS) && defined(__has_include) +#if __has_include() +#define __WI_HAS_STD_VECTOR 1 +#include +#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. +/// @cond +#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 +/// @endcond + +namespace wil +{ +// time_t is the number of 1 - second intervals since January 1, 1970. +constexpr long long SecondsToStartOf1970 = 0x2b6109100; +constexpr long long 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_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< + wistd::conjunction_v< + wistd::is_constructible, + wistd::is_convertible().data()), const wchar_t*>, + wistd::is_same().size())>>, + const wchar_t*> + get_buffer(const StringT& str, UINT32* length) WI_NOEXCEPT + { + *length = static_cast(str.size()); + const wchar_t* ret = str.data(); + return ret ? ret : L""; + } + + 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; + } + }; +} // namespace details +/// @endcond + +//! Detects if one or more embedded null is present in an HSTRING. +inline bool HasEmbeddedNull(_In_opt_ HSTRING value) +{ + BOOL hasEmbeddedNull = FALSE; + (void)WindowsStringHasEmbeddedNull(value, &hasEmbeddedNull); + return hasEmbeddedNull != FALSE; +} + +/** TwoPhaseHStringConstructor help using the 2 phase constructor pattern for HSTRINGs. +@code +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() }; +@endcode + +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 Detach() 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; + + WI_NODISCARD 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 + WI_NODISCARD wchar_t* Get() const + { + return const_cast(m_maker.buffer()); + } + //! Used to validate range of buffer when populating. + WI_NODISCARD 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. + */ + WI_NODISCARD 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 + WI_NODISCARD 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 + WI_NODISCARD 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() = default; + type(T&& value) : m_value(wistd::forward(value)) + { + } + WI_NODISCARD operator T() const + { + return m_value; + } + type& operator=(T&& value) + { + m_value = wistd::forward(value); + return *this; + } + WI_NODISCARD T Get() const + { + return m_value; + } + + // Returning T&& to support move only types + // In case of absence of T::operator=(T&&) a call to T::operator=(const T&) will happen + T&& Get() + { + return wistd::move(m_value); + } + + WI_NODISCARD 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. +} // namespace details +/// @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. +@code +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 +} +@endcode +Standard algorithm example: +@code +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 +}); +@endcode +*/ +#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: +#if defined(_XUTILITY_) || defined(WIL_DOXYGEN) + // 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; + } + + WI_NODISCARD vector_iterator operator+(int n) const + { + vector_iterator ret(*this); + ret += n; + return ret; + } + + WI_NODISCARD vector_iterator operator-(int n) const + { + vector_iterator ret(*this); + ret -= n; + return ret; + } + + WI_NODISCARD ptrdiff_t operator-(const vector_iterator& other) const + { + return m_i - other.m_i; + } + + WI_NODISCARD bool operator==(const vector_iterator& other) const + { + return m_i == other.m_i; + } + + WI_NODISCARD bool operator!=(const vector_iterator& other) const + { + return m_i != other.m_i; + } + + WI_NODISCARD bool operator<(const vector_iterator& other) const + { + return m_i < other.m_i; + } + + WI_NODISCARD bool operator>(const vector_iterator& other) const + { + return m_i > other.m_i; + } + + WI_NODISCARD bool operator<=(const vector_iterator& other) const + { + return m_i <= other.m_i; + } + + WI_NODISCARD 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<> + +__WI_ITR_NAMESPACE_BEGIN + +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) WI_NOEXCEPT : 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: +#if defined(_XUTILITY_) || defined(WIL_DOXYGEN) + // 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) +#if WIL_ITERATOR_DEBUG_LEVEL > 0 + , + m_version(range->m_version) +#endif + { + } + + WI_NODISCARD reference operator*() const + { + return *this->operator->(); + } + + WI_NODISCARD pointer operator->() const + { +#if WIL_ITERATOR_DEBUG_LEVEL > 0 + WI_FAIL_FAST_ASSERT_MSG(m_version == m_range->m_version, "Dereferencing an out-of-date vector_iterator_nothrow"); + WI_FAIL_FAST_ASSERT_MSG(SUCCEEDED(*m_range->m_result), "Dereferencing a vector_iterator_nothrow in a failed state"); + WI_FAIL_FAST_ASSERT_MSG(m_i < m_range->m_size, "Dereferencing an 'end' iterator"); +#endif + return wistd::addressof(m_range->m_currentElement); + } + + vector_iterator_nothrow& operator++() + { + return *this += 1; + } + + vector_iterator_nothrow& operator--() + { + return *this += -1; + } + + 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) + { +#if WIL_ITERATOR_DEBUG_LEVEL == 2 + // This is _technically_ safe because we are not reading the out-of-date cached value, however having two active + // copies of iterators is generally a sign of problematic code with a bug waiting to happen + WI_FAIL_FAST_ASSERT_MSG( + m_version == m_range->m_version, "Incrementing/decrementing an out-of-date copy of a vector_iterator_nothrow"); +#endif + m_i += n; +#if WIL_ITERATOR_DEBUG_LEVEL > 0 + // NOTE: 'm_i' is unsigned, hence the single check for increment/decrement + WI_FAIL_FAST_ASSERT_MSG(m_i <= m_range->m_size, "Incrementing/decrementing a vector_iterator_nothrow out of range"); +#endif + m_range->get_at_current(m_i); +#if WIL_ITERATOR_DEBUG_LEVEL > 0 + m_version = m_range->m_version; +#endif + return *this; + } + + vector_iterator_nothrow& operator-=(int n) + { + return *this += -n; + } + + WI_NODISCARD bool operator==(vector_iterator_nothrow const& other) const + { + return FAILED(*m_range->m_result) || (m_i == other.m_i); + } + + WI_NODISCARD bool operator!=(vector_iterator_nothrow const& other) const + { + return !operator==(other); + } + + private: + vector_range_nothrow* m_range; + unsigned int m_i = 0; + +#if WIL_ITERATOR_DEBUG_LEVEL > 0 + // For checked iterator support; must match the version in 'm_range' + int m_version = 0; +#endif + }; + + vector_iterator_nothrow begin() + { + // NOTE: Calling 'begin()' more than once is explicitly allowed as a way to reset the range, however because state is + // shared between all iterators, this invalidates previously created iterators + 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()); + +#if WIL_ITERATOR_DEBUG_LEVEL > 0 + ++m_version; +#endif + } + } + +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; + +#if WIL_ITERATOR_DEBUG_LEVEL > 0 + // For checked iterator support + int m_version = 0; +#endif +}; + +__WI_ITR_NAMESPACE_END + +#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: +#if defined(_XUTILITY_) || defined(WIL_DOXYGEN) + 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) + { + if (this != wistd::addressof(other)) + { + m_iterator = other.m_iterator; + m_i = other.m_i; + err_policy::HResult(other.m_element.CopyTo(m_element.ReleaseAndGetAddressOf())); + } + return *this; + } + + WI_NODISCARD bool operator==(iterable_iterator const& other) const + { + return m_i == other.m_i; + } + + WI_NODISCARD 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 + +#if defined(__WI_HAS_STD_VECTOR) || defined(WIL_DOXYGEN) +/** Converts WinRT vectors to std::vector by requesting the collection's data in a single +operation. This can be more efficient in terms of IPC cost than iteratively processing it. +@code +ComPtr> values = GetValues(); +std::vector> allData = wil::to_vector(values); +for (ComPtr const& item : allData) +{ + // use item +} +@endcode +Can be used for ABI::Windows::Foundation::Collections::IVector and +ABI::Windows::Foundation::Collections::IVectorView +*/ +template +auto to_vector(VectorType* src) +{ + using TResult = typename details::MapVectorResultType::type; + using TSmart = typename details::MapToSmartType::type; + static_assert(sizeof(TResult) == sizeof(TSmart), "result and smart sizes are different"); + std::vector output; + UINT32 expected = 0; + THROW_IF_FAILED(src->get_Size(&expected)); + if (expected > 0) + { + output.resize(expected + 1); + UINT32 fetched = 0; + THROW_IF_FAILED(src->GetMany(0, static_cast(output.size()), reinterpret_cast(output.data()), &fetched)); + THROW_HR_IF(E_CHANGED_STATE, fetched > expected); + output.resize(fetched); + } + return output; +} +#endif + +#pragma region error code base IIterable<> + +__WI_ITR_NAMESPACE_BEGIN + +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) WI_NOEXCEPT : m_iterator(wistd::move(other.m_iterator)), + m_element(wistd::move(other.m_element)), + m_resultStorage(other.m_resultStorage) +#if WIL_ITERATOR_DEBUG_LEVEL > 0 + , + m_index(other.m_index) +#endif + { + 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: +#if defined(_XUTILITY_) || defined(WIL_DOXYGEN) + // 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) + { + } + + WI_NODISCARD bool operator==(iterable_iterator_nothrow const& other) const + { + return FAILED(*m_range->m_result) || (m_i == other.m_i); + } + + WI_NODISCARD bool operator!=(iterable_iterator_nothrow const& other) const + { + return !operator==(other); + } + + WI_NODISCARD reference operator*() const WI_NOEXCEPT + { + return *this->operator->(); + } + + WI_NODISCARD pointer operator->() const WI_NOEXCEPT + { +#if WIL_ITERATOR_DEBUG_LEVEL > 0 + WI_FAIL_FAST_ASSERT_MSG(SUCCEEDED(*m_range->m_result), "Dereferencing an iterable_iterator_nothrow in a failed state"); + WI_FAIL_FAST_ASSERT_MSG(m_i >= 0, "Dereferencing an 'end' iterator"); + WI_FAIL_FAST_ASSERT_MSG(m_i == m_range->m_index, "Dereferencing an out-of-date iterable_iterator_nothrow"); +#endif + return wistd::addressof(m_range->m_element); + } + + iterable_iterator_nothrow& operator++() + { +#if WIL_ITERATOR_DEBUG_LEVEL > 0 + // Failing this check is always bad because the iterator object we hold always advances forward + WI_FAIL_FAST_ASSERT_MSG(m_i >= 0, "Incrementing an end iterator"); + WI_FAIL_FAST_ASSERT_MSG(m_i == m_range->m_index, "Incrementing an out-of-date copy of an iterable_iterator_nothrow"); +#endif + 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; + } +#if WIL_ITERATOR_DEBUG_LEVEL > 0 + m_range->m_index = m_i; +#endif + return *this; + } + + iterable_iterator_nothrow operator++(int) + { + iterable_iterator_nothrow old(*this); + ++*this; + return old; + } + + private: + iterable_range_nothrow* m_range; + int m_i; + }; + + iterable_iterator_nothrow begin() + { +#if WIL_ITERATOR_DEBUG_LEVEL == 2 + // The IIterator we hold only advances forward; we can't reset it back to the beginning. Calling this method more than + // once will very likely lead to unexpected behavior. + WI_FAIL_FAST_ASSERT_MSG(m_index == 0, "Calling begin() on an already advanced iterable_range_nothrow"); +#endif + 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; + +#if WIL_ITERATOR_DEBUG_LEVEL > 0 + // For checked iterator support + int m_index = 0; +#endif +}; + +__WI_ITR_NAMESPACE_END + +#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 +} // namespace wil + +#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 +#pragma endregion + +#endif // WIL_ENABLE_EXCEPTIONS + +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; + } + + WI_NODISCARD HANDLE GetEvent() const + { + return m_completedEventHandle.get(); + } + + WI_NODISCARD 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) +} // namespace details +/// @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 function 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 function 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 < + typename TResult, + typename TProgress, + typename TReturn = typename wil::details::MapToSmartType::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< + ABI::Windows::Foundation::IAsyncOperation, + 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< + ABI::Windows::Foundation::IAsyncAction, + Microsoft::WRL::AsyncBase< + ABI::Windows::Foundation::IAsyncActionCompletedHandler, + Microsoft::WRL::Details::Nil, + Microsoft::WRL::AsyncResultType::SingleResult +#ifndef _WRL_DISABLE_CAUSALITY_ + , + Microsoft::WRL::AsyncCausalityOptions +#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 + { + } + }; +} // namespace details +/// @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. + +// clang-format off +#ifdef __cplusplus_winrt +namespace details +{ + template + struct remove_reference + { + typedef T type; + }; + template + struct remove_reference + { + typedef T type; + }; +} // namespace details + +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(); + } + + WI_NODISCARD explicit operator bool() const WI_NOEXCEPT + { + return (m_token.Value != 0); + } + + WI_NODISCARD 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 +// clang-format on + +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(); + } + + WI_NODISCARD explicit operator bool() const WI_NOEXCEPT + { + return (m_token.value != 0); + } + + WI_NODISCARD ::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; +}; + +/// @cond +namespace details +{ + // clang-format off +#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 + // clang-format on + + // 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 +/// @endcond + +// 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) +/// @cond +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>"; + } +}; +/// @endcond +#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 +{ +}; +} // namespace std + +#endif + +#endif // __WIL_WINRT_INCLUDED diff --git a/libs/wil/wil/wistd_config.h b/libs/wil/wil/wistd_config.h new file mode 100644 index 00000000..42dff16d --- /dev/null +++ b/libs/wil/wil/wistd_config.h @@ -0,0 +1,582 @@ +// -*- 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 compatibility 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 + +#if defined(__WI_LIBCPP_COMPILER_MSVC) +#define __WI_PUSH_WARNINGS __pragma(warning(push)) +#define __WI_POP_WARNINGS __pragma(warning(pop)) +#elif defined(__WI_LIBCPP_COMPILER_CLANG) +#define __WI_PUSH_WARNINGS __pragma(clang diagnostic push) +#define __WI_POP_WARNINGS __pragma(clang diagnostic pop) +#else +#define __WI_PUSH_WARNINGS +#define __WI_POP_WARNINGS +#endif + +#ifdef __WI_LIBCPP_COMPILER_MSVC +#define __WI_MSVC_DISABLE_WARNING(id) __pragma(warning(disable : id)) +#else +#define __WI_MSVC_DISABLE_WARNING(id) +#endif + +#ifdef __WI_LIBCPP_COMPILER_CLANG +#define __WI_CLANG_DISABLE_WARNING(warning) __pragma(clang diagnostic ignored #warning) +#else +#define __WI_CLANG_DISABLE_WARNING(warning) +#endif + +// NOTE: MSVC, which is what we primarily target, is severely 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) +{ +using nullptr_t = decltype(__nullptr); + +template +struct __less +{ + __WI_LIBCPP_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 bool operator()( + const _T1& __x, const _T1& __y) const + { + return __x < __y; + } + + __WI_LIBCPP_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 bool operator()( + const _T1& __x, const _T2& __y) const + { + return __x < __y; + } + + __WI_LIBCPP_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 bool operator()( + const _T2& __x, const _T1& __y) const + { + return __x < __y; + } + + __WI_LIBCPP_NODISCARD_ATTRIBUTE __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_NODISCARD_ATTRIBUTE __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_NODISCARD_ATTRIBUTE __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_NODISCARD_ATTRIBUTE __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 +{ + using argument_type = _Arg; + using result_type = _Result; +}; + +template +struct __WI_LIBCPP_TEMPLATE_VIS binary_function +{ + using first_argument_type = _Arg1; + using second_argument_type = _Arg2; + using result_type = _Result; +}; +} // namespace wistd +/// @endcond + +#endif // _WISTD_CONFIG_H_ diff --git a/libs/wil/wil/wistd_functional.h b/libs/wil/wil/wistd_functional.h new file mode 100644 index 00000000..533ce8b3 --- /dev/null +++ b/libs/wil/wil/wistd_functional.h @@ -0,0 +1,568 @@ +// -*- 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)...); + } + + // '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). + constexpr const size_t __buffer_size = 13 * sizeof(void*); + +} // namespace __function + +// NOTE: The extra 'alignas' here is to work around the x86 compiler bug mentioned in +// https://github.com/microsoft/STL/issues/1533 to force alignment on the stack +template +class __WI_LIBCPP_TEMPLATE_VIS __WI_ALIGNAS(typename aligned_storage<__function::__buffer_size>::type) function<_Rp(_ArgTypes...)> + : public __function::__maybe_derive_from_unary_function<_Rp(_ArgTypes...)>, + public __function::__maybe_derive_from_binary_function<_Rp(_ArgTypes...)> +{ + using __base = __function::__base<_Rp(_ArgTypes...)>; + __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS + typename aligned_storage<__function::__buffer_size>::type __buf_; + __base* __f_; + + __WI_LIBCPP_NO_CFI static __base* __as_base(void* p) + { + return static_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 constexpr bool value = false; + }; + + template + struct __callable + { + static const bool value = + __callable_imp<_Fp, __lazy_and, function>::value>, __invokable<_Fp&, _ArgTypes...>>::value>::value; + }; + + template + using _EnableIfCallable = typename enable_if<__callable<_Fp>::value>::type; + +public: + using result_type = _Rp; + + // 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_NODISCARD_ATTRIBUTE __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_ == nullptr) + __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_ == nullptr) + __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_(nullptr) +{ + 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 (static_cast(&__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 (static_cast(&__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_ == nullptr) + __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 +} // namespace wistd +/// @endcond + +#pragma warning(pop) + +#endif // _WISTD_FUNCTIONAL_H_ diff --git a/libs/wil/wil/wistd_memory.h b/libs/wil/wil/wistd_memory.h new file mode 100644 index 00000000..61e14c75 --- /dev/null +++ b/libs/wil/wil/wistd_memory.h @@ -0,0 +1,992 @@ +// -*- 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 + { + using type = typename _Dp::pointer; + }; + + template + struct __pointer_type<_Tp, _Dp, false> + { + using type = _Tp*; + }; + +} // namespace __pointer_type_imp + +template +struct __pointer_type +{ + using type = typename __pointer_type_imp::__pointer_type<_Tp, typename remove_reference<_Dp>::type>::type; +}; + +template ::value && !__libcpp_is_final<_Tp>::value> +struct __compressed_pair_elem +{ + using _ParamT = _Tp; + using reference = _Tp&; + using const_reference = const _Tp&; + +#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_NODISCARD_ATTRIBUTE __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 +{ + using _ParamT = _Tp; + using reference = _Tp&; + using const_reference = const _Tp&; + using __value_type = _Tp; + +#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_NODISCARD_ATTRIBUTE __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 __declspec(empty_bases) __compressed_pair : private __compressed_pair_elem<_T1, 0>, private __compressed_pair_elem<_T2, 1> +{ + using _Base1 = __compressed_pair_elem<_T1, 0>; + using _Base2 = __compressed_pair_elem<_T2, 1>; + + // 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 < + bool _Dummy = true, + class = typename enable_if<__dependent_type, _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_NODISCARD_ATTRIBUTE __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_NODISCARD_ATTRIBUTE __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* = nullptr) 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* = nullptr) 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"); + using __lval_ref_type = const _Deleter&; + using __good_rval_ref_type = _Deleter&&; + using __enable_rval_overload = true_type; +}; + +template +struct __unique_ptr_deleter_sfinae<_Deleter const&> +{ + using __lval_ref_type = const _Deleter&; + using __bad_rval_ref_type = const _Deleter&&; + using __enable_rval_overload = false_type; +}; + +template +struct __unique_ptr_deleter_sfinae<_Deleter&> +{ + using __lval_ref_type = _Deleter&; + using __bad_rval_ref_type = _Deleter&&; + using __enable_rval_overload = false_type; +}; +#endif // !defined(__WI_LIBCPP_CXX03_LANG) + +template > +class __WI_LIBCPP_TEMPLATE_VIS unique_ptr +{ +public: + using element_type = _Tp; + using deleter_type = _Dp; + using pointer = typename __pointer_type<_Tp, deleter_type>::type; + + 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 + using _DeleterSFINAE = __unique_ptr_deleter_sfinae<_Dp>; + + 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::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::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_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY typename add_lvalue_reference<_Tp>::type operator*() const + { + return *__ptr_.first(); + } + __WI_LIBCPP_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY pointer operator->() const WI_NOEXCEPT + { + return __ptr_.first(); + } + __WI_LIBCPP_NODISCARD_ATTRIBUTE __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_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY const deleter_type& get_deleter() const WI_NOEXCEPT + { + return __ptr_.second(); + } + __WI_LIBCPP_NODISCARD_ATTRIBUTE __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: + using element_type = _Tp; + using deleter_type = _Dp; + using pointer = typename __pointer_type<_Tp, deleter_type>::type; + +private: + __compressed_pair __ptr_; + + template + struct _CheckArrayPointerConversion : is_same<_From, pointer> + { + }; + + template + struct _CheckArrayPointerConversion<_FromElem*> + : integral_constant< + bool, + is_same<_FromElem*, pointer>::value || + (is_same::value && is_convertible<_FromElem (*)[], element_type (*)[]>::value)> + { + }; + +#ifndef __WI_LIBCPP_CXX03_LANG + using _DeleterSFINAE = __unique_ptr_deleter_sfinae<_Dp>; + + 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::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::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_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY typename add_lvalue_reference<_Tp>::type operator[](size_t __i) const + { + return __ptr_.first()[__i]; + } + __WI_LIBCPP_NODISCARD_ATTRIBUTE __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_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY const deleter_type& get_deleter() const WI_NOEXCEPT + { + return __ptr_.second(); + } + __WI_LIBCPP_NODISCARD_ATTRIBUTE __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 +} // namespace wistd +/// @endcond + +#endif // _WISTD_MEMORY_H_ diff --git a/libs/wil/wil/wistd_type_traits.h b/libs/wil/wil/wistd_type_traits.h new file mode 100644 index 00000000..40cba991 --- /dev/null +++ b/libs/wil/wil/wistd_type_traits.h @@ -0,0 +1,4969 @@ +// -*- 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_NODISCARD_ATTRIBUTE __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR operator value_type() const WI_NOEXCEPT + { + return value; + } +#if __WI_LIBCPP_STD_VER > 11 + __WI_LIBCPP_NODISCARD_ATTRIBUTE __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(...); +} // namespace __is_class_imp + +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(...); +} // namespace __libcpp_is_function_imp + +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< + bool, + !is_void<_Tp>::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::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(...); +} // namespace __is_base_of_imp + +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 + }; + }; +} // namespace __is_convertible_imp + +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 < + class _T1, + class _T2, + unsigned _T1_is_array_function_or_void = __is_convertible_imp::__is_array_function_or_void<_T1>::value, + unsigned _T2_is_array_function_or_void = __is_convertible_imp::__is_array_function_or_void<_T2>::value> +struct __is_convertible + : public integral_constant< + bool, + __is_convertible_imp::__is_convertible_test<_T1, _T2>::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< + signed char, + __type_list< + signed short, + __type_list< + signed int, + __type_list< + signed long, + __type_list< + signed long long, +#ifndef __WI_LIBCPP_HAS_NO_INT128 + __type_list< + __int128_t, +#endif + __nat +#ifndef __WI_LIBCPP_HAS_NO_INT128 + > +#endif + >>>>> + __signed_types; + +typedef __type_list< + unsigned char, + __type_list< + unsigned short, + __type_list< + unsigned int, + __type_list< + unsigned long, + __type_list< + unsigned long long, +#ifndef __WI_LIBCPP_HAS_NO_INT128 + __type_list< + __uint128_t, +#endif + __nat +#ifndef __WI_LIBCPP_HAS_NO_INT128 + > +#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 +{ + typedef short type; +}; +template <> +struct __make_signed +{ + typedef short type; +}; +template <> +struct __make_signed +{ + typedef int type; +}; +template <> +struct __make_signed +{ + typedef int type; +}; +template <> +struct __make_signed +{ + typedef long type; +}; +template <> +struct __make_signed +{ + typedef long type; +}; +template <> +struct __make_signed +{ + 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 +{ + typedef unsigned short type; +}; +template <> +struct __make_unsigned +{ + typedef unsigned short type; +}; +template <> +struct __make_unsigned +{ + typedef unsigned int type; +}; +template <> +struct __make_unsigned +{ + typedef unsigned int type; +}; +template <> +struct __make_unsigned +{ + typedef unsigned long type; +}; +template <> +struct __make_unsigned +{ + typedef unsigned long type; +}; +template <> +struct __make_unsigned +{ + 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::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_NODISCARD_ATTRIBUTE __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_NODISCARD_ATTRIBUTE __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 + { + }; +} // namespace __is_construct + +#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_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()))>::type +{ +}; + +template +struct __is_constructible1_imp // false, _Tp is not a scalar + : public common_type(), declval<_A0&>()))>::type +{ +}; + +template +struct __is_constructible2_imp // false, _Tp is not a scalar + : public common_type(), declval<_A0>(), declval<_A1>()))>::type +{ +}; + +template +struct __is_constructible3_imp // false, _Tp is not a scalar + : public common_type(), 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< + is_void<_Tp>::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< + typename add_lvalue_reference<_Tp>::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< + typename add_lvalue_reference<_Tp>::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< + bool, + is_trivially_default_constructible<_Tp>::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::value && is_base_of<_ClassT, _DecayA0>::value>::type; + +template ::type, class _DecayA0 = typename decay<_A0>::type> +using __enable_if_bullet2 = + typename enable_if::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::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::value && is_base_of<_ClassT, _DecayA0>::value>::type; + +template ::type, class _DecayA0 = typename decay<_A0>::type> +using __enable_if_bullet5 = + typename enable_if::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::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::value, typename conditional::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::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 + { + }; + +} // namespace __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::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::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 underlying_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 +} // namespace wistd +/// @endcond + +#endif // _WISTD_TYPE_TRAITS_H_ diff --git a/libs/wil/wil/wrl.h b/libs/wil/wil/wrl.h new file mode 100644 index 00000000..846ea675 --- /dev/null +++ b/libs/wil/wil/wrl.h @@ -0,0 +1,131 @@ +//********************************************************* +// +// 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. +// +//********************************************************* +//! @file +//! Windows Runtime Library Helpers: helpers for constructing RuntimeClass based objects and agile WRL Callback objects +#ifndef __WIL_WRL_INCLUDED +#define __WIL_WRL_INCLUDED + +#include +#include "result.h" +#include "common.h" // wistd type_traits helpers +#include // GetModuleHandleW + +/// @cond +EXTERN_C IMAGE_DOS_HEADER __ImageBase; +/// @endcond + +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. +@code + // SomeClass uses 2 phase initialization by implementing RuntimeClassInitialize() + auto someClass = MakeAndInitializeOrThrow(L"input", true); +@endcode +*/ +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). +@code + // SomeClass uses exceptions for error handling in its constructor. + auto someClass = MakeOrThrow(L"input", true); +@endcode +*/ +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 + +/** Holds a reference to the host WRL module to prevent it from being unloaded. +Normally, the reference is held implicitly because you are a member function +of a DLL-hosted COM object, or because you retain a strong reference +to some DLL-hosted COM object, but if those do not apply to you, then you +will need to hold a reference explicitly. For examples (and for the C++/WinRT +equivalent), see winrt_module_reference. +*/ +struct [[nodiscard]] wrl_module_reference +{ + wrl_module_reference() + { + if (auto modulePtr = ::Microsoft::WRL::GetModuleBase()) + { + modulePtr->IncrementObjectCount(); + } + else + { +#ifdef GET_MODULE_HANDLE_EX_FLAG_PIN + // If this assertion fails, then you are using wrl_module_reference + // from a DLL that does not host WRL objects, and the module reference + // has no effect. + WI_ASSERT(reinterpret_cast(&__ImageBase) == GetModuleHandleW(nullptr)); +#endif + } + } + + wrl_module_reference(wrl_module_reference const&) : wrl_module_reference() + { + } + + ~wrl_module_reference() + { + if (auto modulePtr = ::Microsoft::WRL::GetModuleBase()) + { + modulePtr->DecrementObjectCount(); + } + } +}; + +} // namespace wil + +#endif // __WIL_WRL_INCLUDED diff --git a/src/detail/standalone/entry.cpp b/src/detail/standalone/entry.cpp index 502e8bef..26adac52 100644 --- a/src/detail/standalone/entry.cpp +++ b/src/detail/standalone/entry.cpp @@ -59,6 +59,17 @@ std::shared_ptr mainCreatePlugin(const clap_plugin_entry *ee, cons if (pt.has_value()) { auto loadPath = *pt / plugin->_plugin->desc->id; + + try + { + LOG << "Trying to save default clap wrapper settings" << std::endl; + standaloneHost->saveStandaloneAndPluginSettings(loadPath, "defaults.clapwrapper"); + } + catch (const fs::filesystem_error &e) + { + // Oh well - whatcha gonna do? + } + try { if (fs::exists(loadPath / "settings.clapwrapper")) @@ -146,4 +157,4 @@ int mainFinish() } return 0; } -} // namespace freeaudio::clap_wrapper::standalone \ No newline at end of file +} // namespace freeaudio::clap_wrapper::standalone diff --git a/src/detail/standalone/standalone_host.cpp b/src/detail/standalone/standalone_host.cpp index ecfaac95..bd2ec778 100644 --- a/src/detail/standalone/standalone_host.cpp +++ b/src/detail/standalone/standalone_host.cpp @@ -121,6 +121,8 @@ void StandaloneHost::clapProcess(void *pOutput, const void *pInput, uint32_t fra process.in_events = &inputEvents; process.out_events = &outputEvents; process.frames_count = frameCount; + process.audio_inputs_count = numAudioInputs; + process.audio_outputs_count = numAudioOutputs; assert(frameCount < utilityBufferSize); if (frameCount >= utilityBufferSize) diff --git a/src/detail/standalone/standalone_host.h b/src/detail/standalone/standalone_host.h index 231163bb..2e104d6f 100644 --- a/src/detail/standalone/standalone_host.h +++ b/src/detail/standalone/standalone_host.h @@ -38,15 +38,6 @@ struct GtkGui; #endif #endif -#if WIN -#if CLAP_WRAPPER_HAS_WIN32 -namespace windows -{ -struct Win32Gui; -} -#endif -#endif - std::optional getStandaloneSettingsPath(); struct StandaloneHost : Clap::IHost @@ -185,12 +176,6 @@ struct StandaloneHost : Clap::IHost #if CLAP_WRAPPER_HAS_GTK3 freeaudio::clap_wrapper::standalone::linux_standalone::GtkGui *gtkGui{nullptr}; #endif -#endif - -#if WIN -#if CLAP_WRAPPER_HAS_WIN32 - freeaudio::clap_wrapper::standalone::windows::Win32Gui *win32Gui{nullptr}; -#endif #endif bool register_timer(uint32_t period_ms, clap_id *timer_id) override; diff --git a/src/detail/standalone/windows/helper.h b/src/detail/standalone/windows/helper.h deleted file mode 100644 index 4431be63..00000000 --- a/src/detail/standalone/windows/helper.h +++ /dev/null @@ -1,368 +0,0 @@ -#if CLAP_WRAPPER_HAS_WIN32 - -#ifdef UNICODE -#undef UNICODE -#endif - -#include -#include - -#include -#include - -#pragma comment(lib, "dwmapi") - -#define IDM_SETTINGS 1001 -#define IDM_SAVE_STATE 1002 -#define IDM_LOAD_STATE 1003 -#define IDM_RESET_STATE 1004 - -namespace freeaudio::clap_wrapper::standalone::windows -{ -struct Window -{ - Window(); - ~Window(); - - static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); - virtual int OnClose(HWND, UINT, WPARAM, LPARAM); - virtual int OnDestroy(HWND, UINT, WPARAM, LPARAM); - virtual int OnDpiChanged(HWND, UINT, WPARAM, LPARAM); - virtual int OnKeyDown(HWND, UINT, WPARAM, LPARAM); - virtual int OnWindowPosChanged(HWND, UINT, WPARAM, LPARAM); - - bool fullscreen(); - - HWND m_hwnd; - bool isConsoleAttached{false}; -}; - -struct Console -{ - Console(); - ~Console(); - FILE* f; -}; - -template -T* InstanceFromWndProc(HWND hwnd, UINT umsg, LPARAM lparam) -{ - T* pInstance; - - if (umsg == WM_NCCREATE) - { - LPCREATESTRUCT pCreateStruct{reinterpret_cast(lparam)}; - pInstance = reinterpret_cast(pCreateStruct->lpCreateParams); - ::SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(pInstance)); - pInstance->*m_hwnd = hwnd; - } - - else - pInstance = reinterpret_cast(::GetWindowLongPtr(hwnd, GWLP_USERDATA)); - - return pInstance; -} - -std::string narrow(std::wstring in) -{ - if (!in.empty()) - { - auto inSize{static_cast(in.size())}; - - auto outSize{::WideCharToMultiByte(CP_UTF8, WC_NO_BEST_FIT_CHARS | WC_ERR_INVALID_CHARS, in.data(), - inSize, nullptr, 0, nullptr, nullptr)}; - - if (outSize > 0) - { - std::string out; - out.resize(static_cast(outSize)); - - if (::WideCharToMultiByte(CP_UTF8, WC_NO_BEST_FIT_CHARS | WC_ERR_INVALID_CHARS, in.data(), inSize, - out.data(), outSize, nullptr, nullptr) > 0) - return out; - } - } - - return {}; -} - -std::wstring widen(std::string in) -{ - if (!in.empty()) - { - auto inSize{static_cast(in.size())}; - - auto outSize{::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, in.data(), inSize, nullptr, 0)}; - - if (outSize > 0) - { - std::wstring out; - out.resize(static_cast(outSize)); - - if (::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, in.data(), inSize, out.data(), outSize) > - 0) - return out; - } - } - - return {}; -} - -std::string randomize(std::string in) -{ - std::random_device rd; - std::mt19937 mt(rd()); - std::uniform_real_distribution dist(1.0, 10.0); - auto randomDouble{dist(mt)}; - auto randomNumber{std::to_string(randomDouble)}; - randomNumber.erase(remove(randomNumber.begin(), randomNumber.end(), '.'), randomNumber.end()); - - return (in + randomNumber); -} - -Window::Window() -{ - std::string clapName{WIN32_NAME}; - std::string randomName{randomize(clapName)}; - - WNDCLASSEX wcex{sizeof(WNDCLASSEX)}; - wcex.lpszClassName = randomName.c_str(); - wcex.lpszMenuName = randomName.c_str(); - wcex.lpfnWndProc = Window::WndProc; - wcex.style = 0; - wcex.cbClsExtra = 0; - wcex.cbWndExtra = 0; - wcex.hInstance = ::GetModuleHandle(nullptr); - wcex.hbrBackground = reinterpret_cast(::GetStockObject(BLACK_BRUSH)); - wcex.hCursor = reinterpret_cast(::LoadImage(nullptr, reinterpret_cast(IDC_ARROW), - IMAGE_CURSOR, 0, 0, LR_SHARED | LR_DEFAULTSIZE)); - wcex.hIcon = - reinterpret_cast(::LoadImage(nullptr, reinterpret_cast(IDI_APPLICATION), IMAGE_ICON, - 0, 0, LR_DEFAULTCOLOR | LR_DEFAULTSIZE | LR_SHARED)); - wcex.hIconSm = - reinterpret_cast(::LoadImage(nullptr, reinterpret_cast(IDI_APPLICATION), IMAGE_ICON, - 0, 0, LR_DEFAULTCOLOR | LR_DEFAULTSIZE | LR_SHARED)); - - ::RegisterClassEx(&wcex); - - ::CreateWindowEx(0, randomName.c_str(), clapName.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, - ::GetModuleHandle(nullptr), this); - - auto hMenu{::GetSystemMenu(m_hwnd, FALSE)}; - - MENUITEMINFO seperator{sizeof(MENUITEMINFO)}; - seperator.fMask = MIIM_FTYPE; - seperator.fType = MFT_SEPARATOR; - - MENUITEMINFO audioIn{sizeof(MENUITEMINFO)}; - audioIn.fMask = MIIM_STRING | MIIM_ID; - audioIn.wID = IDM_SETTINGS; - audioIn.dwTypeData = const_cast("Settings"); - - MENUITEMINFO saveState{sizeof(MENUITEMINFO)}; - saveState.fMask = MIIM_STRING | MIIM_ID; - saveState.wID = IDM_SAVE_STATE; - saveState.dwTypeData = const_cast("Save state..."); - - MENUITEMINFO loadState{sizeof(MENUITEMINFO)}; - loadState.fMask = MIIM_STRING | MIIM_ID; - loadState.wID = IDM_LOAD_STATE; - loadState.dwTypeData = const_cast("Load state..."); - - MENUITEMINFO resetState{sizeof(MENUITEMINFO)}; - resetState.fMask = MIIM_STRING | MIIM_ID; - resetState.wID = IDM_RESET_STATE; - resetState.dwTypeData = const_cast("Reset state..."); - - if (hMenu != INVALID_HANDLE_VALUE) - { - ::InsertMenuItem(hMenu, 1, TRUE, &seperator); - ::InsertMenuItem(hMenu, 2, TRUE, &audioIn); - ::InsertMenuItem(hMenu, 3, TRUE, &seperator); - ::InsertMenuItem(hMenu, 4, TRUE, &saveState); - ::InsertMenuItem(hMenu, 5, TRUE, &loadState); - ::InsertMenuItem(hMenu, 6, TRUE, &resetState); - ::InsertMenuItem(hMenu, 7, TRUE, &seperator); - } -} - -Window::~Window() -{ -} - -LRESULT CALLBACK Window::WndProc(HWND h, UINT m, WPARAM w, LPARAM l) -{ - Window* pWindow = InstanceFromWndProc(h, m, l); - - if (pWindow) - { - switch (m) - { - case WM_CLOSE: - return pWindow->OnClose(h, m, w, l); - case WM_DESTROY: - return pWindow->OnDestroy(h, m, w, l); - case WM_DPICHANGED: - return pWindow->OnDpiChanged(h, m, w, l); - case WM_KEYDOWN: - return pWindow->OnKeyDown(h, m, w, l); - case WM_WINDOWPOSCHANGED: - return pWindow->OnWindowPosChanged(h, m, w, l); - } - } - - return ::DefWindowProc(h, m, w, l); -} - -int Window::OnClose(HWND h, UINT m, WPARAM w, LPARAM l) -{ - ::DestroyWindow(h); - - return 0; -} - -int Window::OnDestroy(HWND h, UINT m, WPARAM w, LPARAM l) -{ - auto plugin{freeaudio::clap_wrapper::standalone::getMainPlugin()}; - - if (plugin && plugin->_ext._gui) - { - plugin->_ext._gui->hide(plugin->_plugin); - plugin->_ext._gui->destroy(plugin->_plugin); - } - - ::PostQuitMessage(0); - - return 0; -} - -int Window::OnDpiChanged(HWND h, UINT m, WPARAM w, LPARAM l) -{ - auto plugin{freeaudio::clap_wrapper::standalone::getMainPlugin()}; - auto ui{plugin->_ext._gui}; - auto p{plugin->_plugin}; - - auto dpi{::GetDpiForWindow(h)}; - auto scaleFactor{static_cast(dpi) / static_cast(USER_DEFAULT_SCREEN_DPI)}; - - ui->set_scale(p, scaleFactor); - - auto bounds{(RECT*)l}; - ::SetWindowPos(h, nullptr, bounds->left, bounds->top, (bounds->right - bounds->left), - (bounds->bottom - bounds->top), SWP_NOZORDER | SWP_NOACTIVATE); - - return 0; -} - -int Window::OnKeyDown(HWND h, UINT m, WPARAM w, LPARAM l) -{ - auto plugin{freeaudio::clap_wrapper::standalone::getMainPlugin()}; - auto ui{plugin->_ext._gui}; - auto p{plugin->_plugin}; - - switch (w) - { - case VK_F11: - { - if (ui->can_resize(p)) fullscreen(); - - break; - } - - default: - return 0; - } - - return 0; -} - -int Window::OnWindowPosChanged(HWND h, UINT m, WPARAM w, LPARAM l) -{ - auto plugin{freeaudio::clap_wrapper::standalone::getMainPlugin()}; - auto ui{plugin->_ext._gui}; - auto p{plugin->_plugin}; - - auto dpi{::GetDpiForWindow(h)}; - auto scaleFactor{static_cast(dpi) / static_cast(USER_DEFAULT_SCREEN_DPI)}; - - if (ui->can_resize(p)) - { - RECT r{0, 0, 0, 0}; - ::GetClientRect(h, &r); - uint32_t w = (r.right - r.left); - uint32_t h = (r.bottom - r.top); - ui->adjust_size(p, &w, &h); - ui->set_size(p, w, h); - } - -#ifdef _DEBUG - if (isConsoleAttached) - { - RECT wr{0, 0, 0, 0}; - ::GetWindowRect(h, &wr); - ::SetWindowPos(::GetConsoleWindow(), nullptr, wr.left, wr.bottom, (wr.right - wr.left), 200, - SWP_NOZORDER | SWP_ASYNCWINDOWPOS); - } -#endif - - return 0; -} - -bool Window::fullscreen() -{ - auto plugin{freeaudio::clap_wrapper::standalone::getMainPlugin()}; - auto ui{plugin->_ext._gui}; - auto p{plugin->_plugin}; - - static RECT pos; - - auto style{::GetWindowLongPtr(m_hwnd, GWL_STYLE)}; - - if (style & WS_OVERLAPPEDWINDOW) - { - MONITORINFO mi = {sizeof(mi)}; - ::GetWindowRect(m_hwnd, &pos); - if (::GetMonitorInfo(::MonitorFromWindow(m_hwnd, MONITOR_DEFAULTTONEAREST), &mi)) - { - ::SetWindowLongPtr(m_hwnd, GWL_STYLE, style & ~WS_OVERLAPPEDWINDOW); - ::SetWindowPos(m_hwnd, HWND_TOP, mi.rcMonitor.left, mi.rcMonitor.top, - mi.rcMonitor.right - mi.rcMonitor.left, mi.rcMonitor.bottom - mi.rcMonitor.top, - SWP_FRAMECHANGED); - } - - return true; - } - - else - { - ::SetWindowLongPtr(m_hwnd, GWL_STYLE, style | WS_OVERLAPPEDWINDOW); - ::SetWindowPos(m_hwnd, nullptr, pos.left, pos.top, (pos.right - pos.left), (pos.bottom - pos.top), - SWP_FRAMECHANGED); - - return false; - } -} - -Console::Console() -{ - ::AllocConsole(); - ::EnableMenuItem(::GetSystemMenu(::GetConsoleWindow(), FALSE), SC_CLOSE, - MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); - ::freopen_s(&f, "CONOUT$", "w", stdout); - ::freopen_s(&f, "CONOUT$", "w", stderr); - ::freopen_s(&f, "CONIN$", "r", stdin); - std::cout.clear(); - std::clog.clear(); - std::cerr.clear(); - std::cin.clear(); -} - -Console::~Console() -{ - ::fclose(f); - ::FreeConsole(); -} -} // namespace freeaudio::clap_wrapper::standalone::windows - -#endif diff --git a/src/detail/standalone/windows/helpers.cpp b/src/detail/standalone/windows/helpers.cpp new file mode 100644 index 00000000..66211abf --- /dev/null +++ b/src/detail/standalone/windows/helpers.cpp @@ -0,0 +1,200 @@ +#include "helpers.h" + +namespace freeaudio::clap_wrapper::standalone::windows::helpers::detail +{ +auto CALLBACK DefaultWindowProcedure::wndProc(::HWND hWnd, ::UINT uMsg, ::WPARAM wParam, + ::LPARAM lParam) -> ::LRESULT +{ + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); +} +} // namespace freeaudio::clap_wrapper::standalone::windows::helpers::detail + +namespace freeaudio::clap_wrapper::standalone::windows::helpers +{ +::HMODULE getInstance() +{ + ::HMODULE hInstance; + ::GetModuleHandleExW(0, nullptr, &hInstance); + + return hInstance; +} + +bool activateWindow(::HWND window) +{ + return ::ShowWindow(window, SW_NORMAL); +} + +bool showWindow(::HWND window) +{ + return ::ShowWindow(window, SW_SHOW); +} + +bool hideWindow(::HWND window) +{ + return ::ShowWindow(window, SW_HIDE); +} + +void centerWindow(::HWND window, int width, int height) +{ + ::MONITORINFO mi{sizeof(::MONITORINFO)}; + ::GetMonitorInfoW(::MonitorFromWindow(window, MONITOR_DEFAULTTONEAREST), &mi); + + auto monitorWidth{checkSafeSize(mi.rcWork.right - mi.rcWork.left)}; + auto monitorHeight{checkSafeSize(mi.rcWork.bottom - mi.rcWork.top)}; + + if (monitorWidth > width && monitorHeight > height) + { + auto x{(monitorWidth - width) / 2}; + auto y{(monitorHeight - height) / 2}; + ::SetWindowPos(window, nullptr, x, y, width, height, 0); + } +} + +bool closeWindow(::HWND window) +{ + return ::CloseWindow(window); +} + +bool checkWindowVisibility(::HWND window) +{ + return ::IsWindowVisible(window); +} + +unsigned int getCurrentDpi(::HWND window) +{ + return ::GetDpiForWindow(window); +} + +double getCurrentScale(::HWND window) +{ + return static_cast(::GetDpiForWindow(window)) / static_cast(USER_DEFAULT_SCREEN_DPI); +} + +void abort(unsigned int exitCode) +{ + ::ExitProcess(exitCode); +} + +void quit(unsigned int exitCode) +{ + ::PostQuitMessage(exitCode); +} + +int messageLoop() +{ + ::MSG msg{}; + int r{}; + + while ((r = ::GetMessageW(&msg, nullptr, 0, 0)) != 0) + { + if (r == -1) + { + return EXIT_FAILURE; + } + + else + { + ::TranslateMessage(&msg); + ::DispatchMessageW(&msg); + } + } + + return static_cast(msg.wParam); +} + +std::string toUTF8(std::wstring wstring) +{ + if (wstring.empty()) return {}; + + auto safeSize{checkSafeSize(wstring.length())}; + + auto length{::WideCharToMultiByte(CP_UTF8, WC_NO_BEST_FIT_CHARS | WC_ERR_INVALID_CHARS, wstring.data(), + safeSize, nullptr, 0, nullptr, nullptr)}; + + std::string utf8(length, 0); + + if (::WideCharToMultiByte(CP_UTF8, WC_NO_BEST_FIT_CHARS | WC_ERR_INVALID_CHARS, wstring.data(), + safeSize, utf8.data(), length, nullptr, nullptr) > 0) + return utf8; + + else + return {}; +} + +std::wstring toUTF16(std::string string) +{ + if (string.empty()) return {}; + + auto safeSize{checkSafeSize(string.length())}; + + auto length{::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, string.data(), safeSize, nullptr, 0)}; + + std::wstring utf16(length, 0); + + if (::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, string.data(), safeSize, utf16.data(), + length) > 0) + return utf16; + + else + return {}; +} + +void log(std::initializer_list args) +{ + std::string message; + + for (const auto& arg : args) + { + message.append(arg); + } + + ::OutputDebugStringW(toUTF16(message).c_str()); + ::OutputDebugStringW(L"\n"); +} + +void messageBox(std::initializer_list args) +{ + std::string message; + + for (auto arg : args) + { + message.append(arg); + } + + ::MessageBoxW(nullptr, toUTF16(message).c_str(), nullptr, MB_OK | MB_ICONASTERISK); +} + +void errorBox(std::initializer_list args) +{ + std::string message; + + for (auto arg : args) + { + message.append(arg); + } + + ::MessageBoxW(nullptr, toUTF16(message).c_str(), nullptr, MB_OK | MB_ICONHAND); +} + +::HBRUSH loadBrushFromSystem(int name) +{ + return static_cast<::HBRUSH>(::GetStockObject(name)); +} + +::HCURSOR loadCursorFromSystem(LPSTR name) +{ + return static_cast<::HCURSOR>( + ::LoadImageA(nullptr, name, IMAGE_CURSOR, 0, 0, LR_SHARED | LR_DEFAULTSIZE)); +} + +::HICON loadIconFromSystem(LPSTR name) +{ + return static_cast<::HICON>(::LoadImageA(nullptr, name, IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE)); +} + +::HICON loadIconFromResource() +{ + return static_cast<::HICON>( + ::LoadImageW(getInstance(), MAKEINTRESOURCEW(1), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE)); +} +} // namespace freeaudio::clap_wrapper::standalone::windows::helpers diff --git a/src/detail/standalone/windows/helpers.h b/src/detail/standalone/windows/helpers.h new file mode 100644 index 00000000..11c2ca47 --- /dev/null +++ b/src/detail/standalone/windows/helpers.h @@ -0,0 +1,124 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + +#include "detail/standalone/entry.h" + +namespace freeaudio::clap_wrapper::standalone::windows::helpers::detail +{ +struct DefaultWindowProcedure +{ + static auto CALLBACK wndProc(::HWND hWnd, ::UINT uMsg, ::WPARAM wParam, ::LPARAM lParam) -> ::LRESULT; +}; +} // namespace freeaudio::clap_wrapper::standalone::windows::helpers::detail + +namespace freeaudio::clap_wrapper::standalone::windows::helpers +{ +template +auto registerWindowClass(const wchar_t* name, T* self = nullptr) -> const wchar_t* +{ + ::WNDCLASSEXW windowClass{sizeof(::WNDCLASSEXW)}; + + auto hInstance{getInstance()}; + + if (!::GetClassInfoExW(hInstance, name, &windowClass)) + { + auto iconFromResource{loadIconFromResource()}; + + windowClass.lpszClassName = name; + windowClass.lpszMenuName = nullptr; + windowClass.lpfnWndProc = self ? self->wndProc : ::DefWindowProcA; + windowClass.style = 0; + windowClass.cbClsExtra = 0; + windowClass.cbWndExtra = sizeof(intptr_t); + windowClass.hInstance = hInstance; + windowClass.hbrBackground = loadBrushFromSystem(); + windowClass.hCursor = loadCursorFromSystem(); + windowClass.hIcon = iconFromResource ? iconFromResource : loadIconFromSystem(); + windowClass.hIconSm = iconFromResource ? iconFromResource : loadIconFromSystem(); + + auto atom{::RegisterClassExW(&windowClass)}; + + if (!atom) + { + helpers::errorBox({"Window registration failed"}); + helpers::abort(); + } + } + + return name; +} + +template +auto createWindow(const wchar_t* name = L"Window", T* self = nullptr) -> ::HWND +{ + registerWindowClass(name, self); + + return ::CreateWindowExW(0, name, name, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, getInstance(), + self); +} + +template +T* instanceFromWndProc(HWND hWnd, UINT uMsg, LPARAM lParam) +{ + T* self{nullptr}; + + if (uMsg == WM_NCCREATE) + { + auto lpCreateStruct{reinterpret_cast<::LPCREATESTRUCTW>(lParam)}; + self = static_cast(lpCreateStruct->lpCreateParams); + ::SetWindowLongPtrW(hWnd, 0, reinterpret_cast(self)); + self->m_hWnd.reset(hWnd); + } + + else + { + self = reinterpret_cast(::GetWindowLongPtrW(hWnd, 0)); + } + + return self; +} + +template +U checkSafeSize(T value) +{ + constexpr U max{std::numeric_limits::max()}; + + if (value > static_cast(max)) + { + throw std::overflow_error("Unsafe size"); + } + + return static_cast(value); +} + +::HMODULE getInstance(); +bool activateWindow(::HWND window); +bool showWindow(::HWND window); +bool hideWindow(::HWND window); +void centerWindow(::HWND window, int width, int height); +bool closeWindow(::HWND window); +bool checkWindowVisibility(::HWND window); +unsigned int getCurrentDpi(::HWND window); +double getCurrentScale(::HWND window); + +void abort(unsigned int exitCode = EXIT_FAILURE); +void quit(unsigned int exitCode = EXIT_SUCCESS); +int messageLoop(); +std::string toUTF8(std::wstring wstring); +std::wstring toUTF16(std::string string); +void log(std::initializer_list args); +void messageBox(std::initializer_list args); +void errorBox(std::initializer_list args); +::HBRUSH loadBrushFromSystem(int name = BLACK_BRUSH); +::HCURSOR loadCursorFromSystem(LPSTR name = IDC_ARROW); +::HICON loadIconFromSystem(LPSTR name = IDI_APPLICATION); +::HICON loadIconFromResource(); +} // namespace freeaudio::clap_wrapper::standalone::windows::helpers diff --git a/src/detail/standalone/windows/host_window.cpp b/src/detail/standalone/windows/host_window.cpp new file mode 100644 index 00000000..972ab21b --- /dev/null +++ b/src/detail/standalone/windows/host_window.cpp @@ -0,0 +1,351 @@ +#include +#include "detail/standalone/entry.h" +#include "host_window.h" +#include "helpers.h" + +#define IDM_SETTINGS 1001 +#define IDM_SAVE_STATE 1002 +#define IDM_LOAD_STATE 1003 +#define IDM_RESET_STATE 1004 + +namespace freeaudio::clap_wrapper::standalone::windows +{ +HostWindow::HostWindow(std::shared_ptr clapPlugin) + : m_clapPlugin{clapPlugin} + , m_plugin{m_clapPlugin->_plugin} + , m_pluginGui{m_clapPlugin->_ext._gui} + , m_pluginState{m_clapPlugin->_ext._state} +{ + if (!m_plugin) + { + helpers::errorBox({"Plugin is null"}); + helpers::abort(); + } + + if (!m_pluginGui) + { + helpers::errorBox({"Plugin GUI is null"}); + helpers::abort(); + } + + freeaudio::clap_wrapper::standalone::windows::helpers::createWindow( + helpers::toUTF16(OUTPUT_NAME).c_str(), this); + + if (!m_hWnd) + { + helpers::errorBox({"Host Window creation failed"}); + helpers::abort(); + } + + setupMenu(); + + setupStandaloneHost(); + + if (!checkApi()) + { + helpers::errorBox({"CLAP_WINDOW_API_WIN32 is not supported"}); + helpers::abort(); + } + + setupPlugin(); + + helpers::activateWindow(m_hWnd.get()); + + freeaudio::clap_wrapper::standalone::mainStartAudio(); +} + +void HostWindow::setupMenu() +{ + auto hMenu{::GetSystemMenu(m_hWnd.get(), FALSE)}; + + ::MENUITEMINFOW seperator{sizeof(::MENUITEMINFOW)}; + seperator.fMask = MIIM_FTYPE; + seperator.fType = MFT_SEPARATOR; + + ::MENUITEMINFOW audioIn{sizeof(::MENUITEMINFOW)}; + audioIn.fMask = MIIM_STRING | MIIM_ID; + audioIn.wID = IDM_SETTINGS; + audioIn.dwTypeData = const_cast(L"Audio/MIDI Settings"); + + ::MENUITEMINFOW saveState{sizeof(::MENUITEMINFOW)}; + saveState.fMask = MIIM_STRING | MIIM_ID; + saveState.wID = IDM_SAVE_STATE; + saveState.dwTypeData = const_cast(L"Save plugin state..."); + + ::MENUITEMINFOW loadState{sizeof(::MENUITEMINFOW)}; + loadState.fMask = MIIM_STRING | MIIM_ID; + loadState.wID = IDM_LOAD_STATE; + loadState.dwTypeData = const_cast(L"Load plugin state..."); + + ::MENUITEMINFOW resetState{sizeof(::MENUITEMINFOW)}; + resetState.fMask = MIIM_STRING | MIIM_ID; + resetState.wID = IDM_RESET_STATE; + resetState.dwTypeData = const_cast(L"Reset to default plugin state"); + + if (hMenu != INVALID_HANDLE_VALUE) + { + ::InsertMenuItemW(hMenu, 1, TRUE, &seperator); + ::InsertMenuItemW(hMenu, 2, TRUE, &audioIn); + ::InsertMenuItemW(hMenu, 3, TRUE, &seperator); + ::InsertMenuItemW(hMenu, 4, TRUE, &saveState); + ::InsertMenuItemW(hMenu, 5, TRUE, &loadState); + ::InsertMenuItemW(hMenu, 6, TRUE, &seperator); + ::InsertMenuItemW(hMenu, 7, TRUE, &resetState); + ::InsertMenuItemW(hMenu, 8, TRUE, &seperator); + } +} + +void HostWindow::setupStandaloneHost() +{ + freeaudio::clap_wrapper::standalone::getStandaloneHost()->onRequestResize = + [this](uint32_t width, uint32_t height) { return setWindowSize(width, height); }; +} + +bool HostWindow::checkApi() +{ + return m_pluginGui->is_api_supported(m_plugin, CLAP_WINDOW_API_WIN32, false); +} + +void HostWindow::setupPlugin() +{ + m_pluginGui->create(m_plugin, CLAP_WINDOW_API_WIN32, false); + + m_pluginGui->set_scale(m_plugin, helpers::getCurrentScale(m_hWnd.get())); + + uint32_t width{0}; + uint32_t height{0}; + + if (m_pluginGui->can_resize(m_plugin)) + { + // We can restore size here + // pluginGui->adjust_size(plugin, &previousWidth, &previousHeight); + // pluginGui->set_size(plugin, previousWidth, previousHeight); + } + else + { + // We can't resize, so disable WS_THICKFRAME and WS_MAXIMIZEBOX + ::SetWindowLongPtrW(m_hWnd.get(), GWL_STYLE, + ::GetWindowLongPtrW(m_hWnd.get(), GWL_STYLE) & ~WS_OVERLAPPEDWINDOW | + WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX); + } + + m_pluginGui->get_size(m_plugin, &width, &height); + + setWindowSize(width, height); + + clap_window clapWindow; + clapWindow.api = CLAP_WINDOW_API_WIN32; + clapWindow.win32 = static_cast(m_hWnd.get()); + + m_pluginGui->set_parent(m_plugin, &clapWindow); +} + +bool HostWindow::setWindowSize(uint32_t width, uint32_t height) +{ + ::RECT rect{0, 0, 0, 0}; + rect.right = width; + rect.bottom = height; + + ::AdjustWindowRectExForDpi( + &rect, ::GetWindowLongPtrW(m_hWnd.get(), GWL_STYLE), ::GetMenu(m_hWnd.get()) != nullptr, + ::GetWindowLongPtrW(m_hWnd.get(), GWL_EXSTYLE), helpers::getCurrentDpi(m_hWnd.get())); + + ::SetWindowPos(m_hWnd.get(), nullptr, 0, 0, (rect.right - rect.left), (rect.bottom - rect.top), + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE); + + return true; +} + +LRESULT CALLBACK HostWindow::wndProc(::HWND hWnd, ::UINT uMsg, ::WPARAM wParam, ::LPARAM lParam) +{ + auto self{helpers::instanceFromWndProc(hWnd, uMsg, lParam)}; + + if (self) + { + switch (uMsg) + { + case WM_SHOWWINDOW: + return self->onShowWindow(hWnd, uMsg, wParam, lParam); + case WM_DPICHANGED: + return self->onDpiChanged(hWnd, uMsg, wParam, lParam); + case WM_WINDOWPOSCHANGED: + return self->onWindowPosChanged(hWnd, uMsg, wParam, lParam); + case WM_SYSCOMMAND: + return self->onSysCommand(hWnd, uMsg, wParam, lParam); + case WM_DESTROY: + return self->onDestroy(hWnd, uMsg, wParam, lParam); + } + } + + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + +int HostWindow::onShowWindow(::HWND hWnd, ::UINT uMsg, ::WPARAM wParam, ::LPARAM lParam) +{ + if (wParam) + { + m_pluginGui->show(m_plugin); + } + else + { + m_pluginGui->hide(m_plugin); + } + + return 0; +} + +int HostWindow::onDpiChanged(::HWND hWnd, ::UINT uMsg, ::WPARAM wParam, ::LPARAM lParam) +{ + m_pluginGui->set_scale(m_plugin, helpers::getCurrentScale(hWnd)); + + return 0; +} + +int HostWindow::onWindowPosChanged(::HWND hWnd, ::UINT uMsg, ::WPARAM wParam, ::LPARAM lParam) +{ + if (m_pluginGui->can_resize(m_plugin)) + { + ::RECT rect{0, 0, 0, 0}; + ::GetClientRect(hWnd, &rect); + + uint32_t width = (rect.right - rect.left); + uint32_t height = (rect.bottom - rect.top); + + m_pluginGui->adjust_size(m_plugin, &width, &height); + m_pluginGui->set_size(m_plugin, width, height); + } + + return 0; +} + +int HostWindow::onSysCommand(::HWND hWnd, ::UINT uMsg, ::WPARAM wParam, ::LPARAM lParam) +{ + switch (wParam) + { + case IDM_SETTINGS: + { + // Disabled until settings window is finished + // helpers::checkWindowVisibility(m_settingsWindow.m_hWnd.get()) + // ? helpers::hideWindow(m_settingsWindow.m_hWnd.get()) + // : helpers::showWindow(m_settingsWindow.m_hWnd.get()); + + return 0; + } + + case IDM_SAVE_STATE: + { + auto coUninitialize{wil::CoInitializeEx(COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)}; + + auto fileSaveDialog{wil::CoCreateInstance(CLSID_FileSaveDialog)}; + + fileSaveDialog->SetDefaultExtension(m_fileTypes.at(0).pszName); + fileSaveDialog->SetFileTypes(m_fileTypes.size(), m_fileTypes.data()); + + fileSaveDialog->Show(m_hWnd.get()); + + wil::com_ptr shellItem; + auto hr{fileSaveDialog->GetResult(&shellItem)}; + + if (SUCCEEDED(hr)) + { + wil::unique_cotaskmem_string result; + shellItem->GetDisplayName(SIGDN_FILESYSPATH, &result); + + auto saveFile{std::filesystem::path(result.get())}; + + LOG << saveFile << std::endl; + + try + { + freeaudio::clap_wrapper::standalone::getStandaloneHost()->saveStandaloneAndPluginSettings( + saveFile.parent_path(), saveFile.filename()); + } + catch (const fs::filesystem_error& e) + { + helpers::errorBox({"Unable to save state: ", e.what()}); + } + } + + return 0; + } + + case IDM_LOAD_STATE: + { + auto coUninitialize{wil::CoInitializeEx(COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)}; + + auto fileOpenDialog{wil::CoCreateInstance(CLSID_FileOpenDialog)}; + + fileOpenDialog->SetDefaultExtension(m_fileTypes.at(0).pszName); + fileOpenDialog->SetFileTypes(m_fileTypes.size(), m_fileTypes.data()); + + fileOpenDialog->Show(m_hWnd.get()); + + wil::com_ptr shellItem; + auto hr{fileOpenDialog->GetResult(&shellItem)}; + + if (SUCCEEDED(hr)) + { + wil::unique_cotaskmem_string result; + shellItem->GetDisplayName(SIGDN_FILESYSPATH, &result); + + auto saveFile{std::filesystem::path(result.get())}; + + LOG << saveFile << std::endl; + + try + { + if (fs::exists(saveFile)) + { + freeaudio::clap_wrapper::standalone::getStandaloneHost()->tryLoadStandaloneAndPluginSettings( + saveFile.parent_path(), saveFile.filename()); + } + } + catch (const fs::filesystem_error& e) + { + helpers::errorBox({"Unable to load state: ", e.what()}); + } + } + + return 0; + } + + case IDM_RESET_STATE: + { + auto pt{freeaudio::clap_wrapper::standalone::getStandaloneSettingsPath()}; + + if (pt.has_value()) + { + auto loadPath{*pt / m_plugin->desc->id}; + + try + { + if (fs::exists(loadPath / "defaults.clapwrapper")) + { + freeaudio::clap_wrapper::standalone::getStandaloneHost()->tryLoadStandaloneAndPluginSettings( + loadPath, "defaults.clapwrapper"); + } + } + catch (const fs::filesystem_error& e) + { + // + } + } + + return 0; + } + } + + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + +int HostWindow::onDestroy(::HWND hWnd, ::UINT uMsg, ::WPARAM wParam, ::LPARAM lParam) +{ + m_pluginGui->destroy(m_plugin); + + freeaudio::clap_wrapper::standalone::mainFinish(); + + helpers::quit(); + + return 0; +} +} // namespace freeaudio::clap_wrapper::standalone::windows diff --git a/src/detail/standalone/windows/host_window.h b/src/detail/standalone/windows/host_window.h new file mode 100644 index 00000000..fdcd0830 --- /dev/null +++ b/src/detail/standalone/windows/host_window.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include + +#include "detail/standalone/standalone_host.h" +#include "settings_window.h" + +namespace freeaudio::clap_wrapper::standalone::windows +{ +struct HostWindow +{ + HostWindow(std::shared_ptr plugin); + + void setupMenu(); + void setupStandaloneHost(); + void setupPlugin(); + bool checkApi(); + + bool setWindowSize(uint32_t width, uint32_t height); + + static LRESULT CALLBACK wndProc(::HWND hWnd, ::UINT uMsg, ::WPARAM wParam, ::LPARAM lParam); + int onShowWindow(::HWND hWnd, ::UINT uMsg, ::WPARAM wParam, ::LPARAM lParam); + int onDpiChanged(::HWND hWnd, ::UINT uMsg, ::WPARAM wParam, ::LPARAM lParam); + int onWindowPosChanged(::HWND hWnd, ::UINT uMsg, ::WPARAM wParam, ::LPARAM lParam); + int onSysCommand(::HWND hWnd, ::UINT uMsg, ::WPARAM wParam, ::LPARAM lParam); + int onDestroy(::HWND hWnd, ::UINT uMsg, ::WPARAM wParam, ::LPARAM lParam); + + std::shared_ptr m_clapPlugin; + const clap_plugin_t* m_plugin; + const clap_plugin_gui_t* m_pluginGui; + const clap_plugin_state_t* m_pluginState; + freeaudio::clap_wrapper::standalone::StandaloneHost* m_standaloneHost; + wil::unique_hwnd m_hWnd; + SettingsWindow m_settingsWindow; + std::vector m_fileTypes{{L"clapwrapper", L"*.clapwrapper"}}; +}; +} // namespace freeaudio::clap_wrapper::standalone::windows diff --git a/src/detail/standalone/windows/settings_window.cpp b/src/detail/standalone/windows/settings_window.cpp new file mode 100644 index 00000000..4e8d0602 --- /dev/null +++ b/src/detail/standalone/windows/settings_window.cpp @@ -0,0 +1,72 @@ +#include + +#include "detail/standalone/standalone_details.h" +#include "settings_window.h" +#include "helpers.h" + +#define ID_COMBOBOX1 101 +#define ID_COMBOBOX2 102 +#define ID_COMBOBOX3 103 +#define ID_BUTTON1 104 +#define ID_BUTTON2 105 + +namespace freeaudio::clap_wrapper::standalone::windows +{ +SettingsWindow::SettingsWindow() +{ + freeaudio::clap_wrapper::standalone::windows::helpers::createWindow(L"Audio/MIDI Settings", this); + + if (!m_hWnd) + { + helpers::errorBox({"Settings Window creation failed"}); + helpers::abort(); + } + + int width{400}; + int height{360}; + + helpers::centerWindow(m_hWnd.get(), 400, 360); +} + +LRESULT CALLBACK SettingsWindow::wndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + auto self{helpers::instanceFromWndProc(hWnd, uMsg, lParam)}; + + if (self) + { + switch (uMsg) + { + case WM_CREATE: + return self->onCreate(hWnd, uMsg, wParam, lParam); + case WM_CLOSE: + return self->onClose(hWnd, uMsg, wParam, lParam); + } + } + + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + +int SettingsWindow::onCreate(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + CreateWindow("COMBOBOX", NULL, WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST, 10, 10, 150, 100, + m_hWnd.get(), (HMENU)ID_COMBOBOX1, NULL, NULL); + CreateWindow("COMBOBOX", NULL, WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST, 10, 50, 150, 100, + m_hWnd.get(), (HMENU)ID_COMBOBOX2, NULL, NULL); + CreateWindow("COMBOBOX", NULL, WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST, 10, 90, 150, 100, + m_hWnd.get(), (HMENU)ID_COMBOBOX3, NULL, NULL); + + CreateWindow("BUTTON", "Button 1", WS_CHILD | WS_VISIBLE, 10, 130, 80, 30, m_hWnd.get(), + (HMENU)ID_BUTTON1, NULL, NULL); + CreateWindow("BUTTON", "Button 2", WS_CHILD | WS_VISIBLE, 100, 130, 80, 30, m_hWnd.get(), + (HMENU)ID_BUTTON2, NULL, NULL); + + return 0; +} + +int SettingsWindow::onClose(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + helpers::hideWindow(hWnd); + + return 0; +} +} // namespace freeaudio::clap_wrapper::standalone::windows diff --git a/src/detail/standalone/windows/settings_window.h b/src/detail/standalone/windows/settings_window.h new file mode 100644 index 00000000..29dcccd8 --- /dev/null +++ b/src/detail/standalone/windows/settings_window.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +namespace freeaudio::clap_wrapper::standalone::windows +{ +struct SettingsWindow +{ + SettingsWindow(); + + static LRESULT CALLBACK wndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + int onCreate(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + int onClose(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + wil::unique_hwnd m_hWnd; +}; +} // namespace freeaudio::clap_wrapper::standalone::windows diff --git a/src/detail/standalone/windows/win32.manifest b/src/detail/standalone/windows/standalone.manifest similarity index 57% rename from src/detail/standalone/windows/win32.manifest rename to src/detail/standalone/windows/standalone.manifest index 376ec498..3e668fc6 100644 --- a/src/detail/standalone/windows/win32.manifest +++ b/src/detail/standalone/windows/standalone.manifest @@ -9,24 +9,12 @@ - - - - - - + + true/pm + PerMonitorV2 - true - - - UTF-8 - - - SegmentHeap diff --git a/src/detail/standalone/windows/winutils.cpp b/src/detail/standalone/windows/winutils.cpp deleted file mode 100644 index 47b1d5b4..00000000 --- a/src/detail/standalone/windows/winutils.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#if CLAP_WRAPPER_HAS_WIN32 - -#ifdef UNICODE -#undef UNICODE -#endif - -#include - -#include "../entry.h" -#include "../standalone_details.h" -#include "../../clap/fsutil.h" - -#include "winutils.h" -#include "helper.h" - -int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pCmdLine, int nCmdShow) -{ -#ifdef _DEBUG - freeaudio::clap_wrapper::standalone::windows::Console console; -#endif - - const clap_plugin_entry* entry{nullptr}; - -#ifdef STATICALLY_LINKED_CLAP_ENTRY - extern const clap_plugin_entry clap_entry; - entry = &clap_entry; -#else - std::string clapName{HOSTED_CLAP_NAME}; - - auto searchPaths{Clap::getValidCLAPSearchPaths()}; - - auto lib{Clap::Library()}; - - for (const auto& searchPath : searchPaths) - { - auto clapPath = searchPath / (clapName + ".clap"); - - if (fs::exists(clapPath) && !entry) - { - lib.load(clapPath); - entry = lib._pluginEntry; - } - } -#endif - - if (!entry) return 0; - - std::string pid{PLUGIN_ID}; - int pindex{PLUGIN_INDEX}; - - auto plugin{freeaudio::clap_wrapper::standalone::mainCreatePlugin(entry, pid, pindex, 1, __argv)}; - - freeaudio::clap_wrapper::standalone::mainStartAudio(); - - freeaudio::clap_wrapper::standalone::windows::Win32Gui win32Gui{}; - - auto sah{freeaudio::clap_wrapper::standalone::getStandaloneHost()}; - - sah->win32Gui = &win32Gui; - - win32Gui.plugin = plugin; - - if (plugin->_ext._gui) - { - auto ui{plugin->_ext._gui}; - auto p{plugin->_plugin}; - - ui->create(p, CLAP_WINDOW_API_WIN32, false); - - freeaudio::clap_wrapper::standalone::windows::Window window; - - auto dpi{::GetDpiForWindow(window.m_hwnd)}; - auto scaleFactor{static_cast(dpi) / static_cast(USER_DEFAULT_SCREEN_DPI)}; - ui->set_scale(p, scaleFactor); - - if (ui->can_resize(p)) - { - // We can check here if we had a previous size but we aren't saving state yet - } - - uint32_t w{0}; - uint32_t h{0}; - ui->get_size(p, &w, &h); - - RECT r{0, 0, 0, 0}; - r.right = w; - r.bottom = h; - ::AdjustWindowRectExForDpi(&r, WS_OVERLAPPEDWINDOW, 0, 0, dpi); - ::SetWindowPos(window.m_hwnd, nullptr, 0, 0, (r.right - r.left), (r.bottom - r.top), SWP_NOMOVE); - - if (!ui->can_resize(p)) - { - ::SetWindowLongPtr(window.m_hwnd, GWL_STYLE, - ::GetWindowLongPtr(window.m_hwnd, GWL_STYLE) & ~WS_OVERLAPPEDWINDOW | - WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX); - } - - clap_window win; - win.api = CLAP_WINDOW_API_WIN32; - win.win32 = (void*)window.m_hwnd; - ui->set_parent(p, &win); - - ui->show(p); - - ::ShowWindow(window.m_hwnd, SW_SHOWDEFAULT); - } - - MSG msg{}; - int r{0}; - - while ((r = ::GetMessage(&msg, nullptr, 0, 0)) != 0) - { - if (r == -1) - return 0; - - else - { - ::TranslateMessage(&msg); - ::DispatchMessage(&msg); - } - } - - win32Gui.plugin = nullptr; - - plugin = nullptr; - - freeaudio::clap_wrapper::standalone::mainFinish(); - - return 0; -} - -#endif diff --git a/src/detail/standalone/windows/winutils.h b/src/detail/standalone/windows/winutils.h deleted file mode 100644 index 7aa811c8..00000000 --- a/src/detail/standalone/windows/winutils.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include -#include "detail/standalone/standalone_host.h" - -namespace freeaudio::clap_wrapper::standalone::windows -{ -struct Win32Gui -{ - std::shared_ptr plugin; -}; -} // namespace freeaudio::clap_wrapper::standalone::windows diff --git a/src/wrapasstandalone_win32.cpp b/src/wrapasstandalone_win32.cpp new file mode 100644 index 00000000..e24b8254 --- /dev/null +++ b/src/wrapasstandalone_win32.cpp @@ -0,0 +1,42 @@ +#include "detail/standalone/standalone_details.h" +#include "detail/standalone/entry.h" +#include "detail/standalone/windows/host_window.h" +#include "detail/standalone/windows/helpers.h" + +int main(int argc, char* argv[]) +{ + const clap_plugin_entry* entry{nullptr}; +#ifdef STATICALLY_LINKED_CLAP_ENTRY + extern const clap_plugin_entry clap_entry; + entry = &clap_entry; +#else + auto clapName{std::string(HOSTED_CLAP_NAME)}; + LOG << "Loading " << clapName << std::endl; + + auto lib{Clap::Library()}; + + for (const auto& searchPaths : Clap::getValidCLAPSearchPaths()) + { + auto clapPath{searchPaths / (clapName + ".clap")}; + + if (fs::exists(clapPath) && !entry) + { + lib.load(clapPath); + entry = lib._pluginEntry; + } + } +#endif + + if (!entry) + { + freeaudio::clap_wrapper::standalone::windows::helpers::errorBox({"No entry as configured"}); + freeaudio::clap_wrapper::standalone::windows::helpers::abort(3); + } + + auto clapPlugin{ + freeaudio::clap_wrapper::standalone::mainCreatePlugin(entry, PLUGIN_ID, PLUGIN_INDEX, argc, argv)}; + + freeaudio::clap_wrapper::standalone::windows::HostWindow hostWindow{clapPlugin}; + + return freeaudio::clap_wrapper::standalone::windows::helpers::messageLoop(); +}